diff --git a/js/.idea/.name b/js/.idea/.name new file mode 100644 index 00000000000..09b0cd3eaf8 --- /dev/null +++ b/js/.idea/.name @@ -0,0 +1 @@ +k2js \ No newline at end of file diff --git a/js/.idea/ant.xml b/js/.idea/ant.xml new file mode 100644 index 00000000000..2581ca3fe84 --- /dev/null +++ b/js/.idea/ant.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/js/.idea/compiler.xml b/js/.idea/compiler.xml new file mode 100644 index 00000000000..00e9e1b79cb --- /dev/null +++ b/js/.idea/compiler.xml @@ -0,0 +1,23 @@ + + + + + + diff --git a/js/.idea/copyright/apache.xml b/js/.idea/copyright/apache.xml new file mode 100644 index 00000000000..647fcdb02ef --- /dev/null +++ b/js/.idea/copyright/apache.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/js/.idea/copyright/profiles_settings.xml b/js/.idea/copyright/profiles_settings.xml new file mode 100644 index 00000000000..ace2e994924 --- /dev/null +++ b/js/.idea/copyright/profiles_settings.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/js/.idea/dictionaries/Pavel_Talanov.xml b/js/.idea/dictionaries/Pavel_Talanov.xml new file mode 100644 index 00000000000..556d1745d0f --- /dev/null +++ b/js/.idea/dictionaries/Pavel_Talanov.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/js/.idea/encodings.xml b/js/.idea/encodings.xml new file mode 100644 index 00000000000..e206d70d859 --- /dev/null +++ b/js/.idea/encodings.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/js/.idea/inspectionProfiles/Project_Default.xml b/js/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 00000000000..a76f2aecef0 --- /dev/null +++ b/js/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/js/.idea/inspectionProfiles/profiles_settings.xml b/js/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 00000000000..3b312839bf2 --- /dev/null +++ b/js/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/js/.idea/libraries/guava_10_0_1.xml b/js/.idea/libraries/guava_10_0_1.xml new file mode 100644 index 00000000000..ef3fb899674 --- /dev/null +++ b/js/.idea/libraries/guava_10_0_1.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/js/.idea/misc.xml b/js/.idea/misc.xml new file mode 100644 index 00000000000..d10b9ab7f7b --- /dev/null +++ b/js/.idea/misc.xml @@ -0,0 +1,36 @@ + + + + + + + + + + http://www.w3.org/1999/xhtml + + + + + + + + diff --git a/js/.idea/modules.xml b/js/.idea/modules.xml new file mode 100644 index 00000000000..37bb0135fbe --- /dev/null +++ b/js/.idea/modules.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/js/.idea/scopes/scope_settings.xml b/js/.idea/scopes/scope_settings.xml new file mode 100644 index 00000000000..922003b8433 --- /dev/null +++ b/js/.idea/scopes/scope_settings.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/js/.idea/uiDesigner.xml b/js/.idea/uiDesigner.xml new file mode 100644 index 00000000000..3b000203088 --- /dev/null +++ b/js/.idea/uiDesigner.xml @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/js/.idea/vcs.xml b/js/.idea/vcs.xml new file mode 100644 index 00000000000..275077f8255 --- /dev/null +++ b/js/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/js/.idea/workspace.xml b/js/.idea/workspace.xml new file mode 100644 index 00000000000..bd428ae958b --- /dev/null +++ b/js/.idea/workspace.xml @@ -0,0 +1,1097 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + localhost + 5050 + + + + + + + 1329842523563 + 1329842523563 + + + 1329844972815 + 1329844972815 + + + 1329929708567 + 1329929708567 + + + 1329932665155 + 1329932665155 + + + 1329933709697 + 1329933709697 + + + 1329985061185 + 1329985061185 + + + 1329990410839 + 1329990410839 + + + 1329990620588 + 1329990620588 + + + 1330004324089 + 1330004324089 + + + 1330080013521 + 1330080013521 + + + 1330081675078 + 1330081675078 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + k2js:jar + + + + + + + + No facets are configured + + + + + + + + idea-full + + + + + + + + 1.6 + + + + + + + + cli + + + + + + + + guava-10.0.1 + + + + + + + + + diff --git a/js/ideaAction.patch b/js/ideaAction.patch new file mode 100644 index 00000000000..4f2eaa8fa34 --- /dev/null +++ b/js/ideaAction.patch @@ -0,0 +1,150 @@ +Index: compiler/frontend/src/org/jetbrains/jet/lang/resolve/AnnotationResolver.java +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +--- compiler/frontend/src/org/jetbrains/jet/lang/resolve/AnnotationResolver.java (revision 24a2ec5) ++++ compiler/frontend/src/org/jetbrains/jet/lang/resolve/AnnotationResolver.java (revision 4229e867af6d744d3b384dfc515cbf89caf363ae) +@@ -173,8 +173,8 @@ + List result = Lists.newArrayList(); + for (JetAnnotationEntry annotation : annotations) { + AnnotationDescriptor annotationDescriptor = new AnnotationDescriptor(); +- result.add(annotationDescriptor); +- trace.record(BindingContext.ANNOTATION, annotation, annotationDescriptor); ++ // result.add(annotationDescriptor); ++ // trace.record(BindingContext.ANNOTATION, annotation, annotationDescriptor); + } + return result; + } +Index: idea/src/META-INF/plugin.xml +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +--- idea/src/META-INF/plugin.xml (revision 24a2ec5) ++++ idea/src/META-INF/plugin.xml (revision 4229e867af6d744d3b384dfc515cbf89caf363ae) +@@ -1,9 +1,12 @@ + + Kotlin + Kotlin language support +- @snapshot@ ++ 1.0 + JetBrains + ++ ++ ++ + + + org.jetbrains.jet.plugin.compiler.JetCompilerManager +@@ -28,16 +31,18 @@ + + + +- +- +- +- + + + + ++ ++ ++ ++ ++ ++ + + + +Index: idea/src/org/jetbrains/jet/plugin/actions/TranslateToJsAction.java +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +--- idea/src/org/jetbrains/jet/plugin/actions/TranslateToJsAction.java (revision 4229e867af6d744d3b384dfc515cbf89caf363ae) ++++ idea/src/org/jetbrains/jet/plugin/actions/TranslateToJsAction.java (revision 4229e867af6d744d3b384dfc515cbf89caf363ae) +@@ -0,0 +1,79 @@ ++package org.jetbrains.jet.plugin.actions; ++ ++import com.intellij.notification.Notification; ++import com.intellij.notification.NotificationType; ++import com.intellij.notification.Notifications; ++import com.intellij.openapi.actionSystem.AnAction; ++import com.intellij.openapi.actionSystem.AnActionEvent; ++import com.intellij.openapi.actionSystem.LangDataKeys; ++import com.intellij.openapi.actionSystem.PlatformDataKeys; ++import com.intellij.openapi.application.ApplicationManager; ++import com.intellij.openapi.editor.Editor; ++import com.intellij.openapi.project.Project; ++import com.intellij.openapi.vfs.VirtualFile; ++import com.intellij.psi.PsiFile; ++import org.jetbrains.jet.lang.psi.JetFile; ++import org.jetbrains.k2js.facade.K2JSTranslator; ++ ++/** ++ * @author Pavel Talanov ++ */ ++public final class TranslateToJsAction extends AnAction { ++ ++ private static void notifyFailure(Throwable exception) { ++ Notifications.Bus.notify(new Notification("JsTranslator", "Translation failed.", ++ "Exception: " + exception.getMessage(), ++ NotificationType.ERROR)); ++ } ++ ++ private static void notifySuccess(String outputPath) { ++ Notifications.Bus.notify(new Notification("JsTranslator", "Translation successful.", ++ "Generated file: " + outputPath, ++ NotificationType.INFORMATION)); ++ } ++ ++ public void actionPerformed(AnActionEvent event) { ++ ++ final PsiFile psiFile = event.getData(LangDataKeys.PSI_FILE); ++ if (!(psiFile instanceof JetFile)) { ++ return; ++ } ++ ++ Runnable task = new Runnable() { ++ @Override ++ public void run() { ++ try { ++ Project project = psiFile.getProject(); ++ String outputPath = getOutputPath(psiFile); ++ K2JSTranslator.translateWithCallToMainAndSaveToFile((JetFile) psiFile, ++ outputPath, ++ project); ++ notifySuccess(outputPath); ++ } catch (Throwable e) { ++ e.printStackTrace(); ++ notifyFailure(e); ++ } ++ } ++ }; ++ ApplicationManager.getApplication().runWriteAction(task); ++ } ++ ++ private static String getOutputPath(PsiFile psiFile) { ++ VirtualFile virtualFile = psiFile.getVirtualFile(); ++ assert virtualFile != null : "Internal error: Psi file should correspond to actual virtual file"; ++ String originalFilePath = virtualFile.getPath(); ++ ++ //TODO: make platform independent ++ String pathToDir = originalFilePath.substring(0, originalFilePath.lastIndexOf("/") + 1); ++ String generatedFileName = ((JetFile) psiFile).getNamespaceHeader().getName() + ".js"; ++ return pathToDir + generatedFileName; ++ } ++ ++ ++ @Override ++ public void update(AnActionEvent e) { ++ Editor editor = e.getData(PlatformDataKeys.EDITOR); ++ PsiFile psiFile = e.getData(LangDataKeys.PSI_FILE); ++ e.getPresentation().setEnabled(editor != null && psiFile instanceof JetFile); ++ } ++} diff --git a/js/js.ast/AUTHORS b/js/js.ast/AUTHORS new file mode 100644 index 00000000000..fea9b245e9a --- /dev/null +++ b/js/js.ast/AUTHORS @@ -0,0 +1,8 @@ +# Below is a list of people and organizations that have contributed +# to the Dart project. Names should be added to the list like so: +# +# Name/Organization + +Google Inc. + +Ola Martin Bini diff --git a/js/js.ast/LICENSE b/js/js.ast/LICENSE new file mode 100644 index 00000000000..64ed1a49724 --- /dev/null +++ b/js/js.ast/LICENSE @@ -0,0 +1,48 @@ +This license applies to all parts of Dart that are not externally +maintained libraries. The external maintained libraries used by +Dart are: + +JSCRE - in runtime/third_party/jscre +Ant - in third_party/apache_ant +Google App Engine for Java SDK - in third_party/appengine-java-sdk +args4j - in third_party/args4j +Chrome SDK - in third_party/chromesdk +Eclipse - in third_party/eclipse +gsutil = in third_party/gsutil +Guava - in third_party/guava +hamcrest - in third_party/hamcrest +Httplib2 - in third_party/httplib2 +Jetty - in third_party/jetty +JSON - in third_party/json +JUnit - in third_party/junit +Ouath - in third_party/oauth2client +Python-scss - in third_party/pyscss +Rhino - in third_party/rhino + +The libraries may have their own licenses; we recommend you read them, +as their terms may differ from the terms below. + +Copyright 2011, the Dart project authors. All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/js/js.ast/PATENTS b/js/js.ast/PATENTS new file mode 100644 index 00000000000..69541968b91 --- /dev/null +++ b/js/js.ast/PATENTS @@ -0,0 +1,23 @@ +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Dart Project. + +Google hereby grants to you a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this +section) patent license to make, have made, use, offer to sell, sell, +import, transfer, and otherwise run, modify and propagate the contents +of this implementation of Dart, where such license applies only to +those patent claims, both currently owned by Google and acquired in +the future, licensable by Google that are necessarily infringed by +this implementation of Dart. This grant does not include claims that +would be infringed only as a consequence of further modification of +this implementation. If you or your agent or exclusive licensee +institute or order or agree to the institution of patent litigation +against any entity (including a cross-claim or counterclaim in a +lawsuit) alleging that this implementation of Dart or any code +incorporated within this implementation of Dart constitutes direct or +contributory patent infringement, or inducement of patent +infringement, then any patent rights granted to you under this License +for this implementation of Dart shall terminate as of the date such +litigation is filed. diff --git a/js/js.ast/js.ast.iml b/js/js.ast/js.ast.iml new file mode 100644 index 00000000000..73bb50aea75 --- /dev/null +++ b/js/js.ast/js.ast.iml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/js/js.ast/js.iml b/js/js.ast/js.iml new file mode 100644 index 00000000000..1e3480a2bfb --- /dev/null +++ b/js/js.ast/js.iml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/js/js.ast/src/com/google/dart/compiler/InternalCompilerException.java b/js/js.ast/src/com/google/dart/compiler/InternalCompilerException.java new file mode 100644 index 00000000000..5db265e920a --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/InternalCompilerException.java @@ -0,0 +1,15 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler; + +/** + * Exception thrown when the compiler encounters an unexpected internal error. + */ +public class InternalCompilerException extends RuntimeException { + + public InternalCompilerException(String message) { + super(message); + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/Source.java b/js/js.ast/src/com/google/dart/compiler/Source.java new file mode 100644 index 00000000000..13e6e16cbdd --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/Source.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; + +import java.io.IOException; +import java.io.Reader; +import java.net.URI; + +/** + * 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 java.util.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.ast/src/com/google/dart/compiler/backend/js/Cloner.java b/js/js.ast/src/com/google/dart/compiler/backend/js/Cloner.java new file mode 100644 index 00000000000..fa3447fe168 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/Cloner.java @@ -0,0 +1,232 @@ +// Copyright 2011, the Dart project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +package com.google.dart.compiler.backend.js; + +import com.google.dart.compiler.InternalCompilerException; +import com.google.dart.compiler.backend.js.ast.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +/** + * Implements actual cloning logic. We rely on the JsExpressions to provide + * traversal logic. The {@link #stack} field is used to accumulate + * already-cloned JsExpression instances. One gotcha that falls out of this is + * that argument lists are on the stack in reverse order, so lists should be + * constructed via inserts, rather than appends. + */ +public class Cloner extends JsVisitor { + protected final Stack stack = new Stack(); + private boolean successful = true; + + /** + * @param expression + * @return Return a clone of the expression tree + */ + public static JsExpression clone(JsExpression expression) { + Cloner c = new Cloner(); + c.accept(expression); + return c.getExpression(); + } + + @Override + public void endVisit(JsArrayAccess x, JsContext ctx) { + JsArrayAccess newExpression = new JsArrayAccess(); + newExpression.setIndexExpr(stack.pop()); + newExpression.setArrayExpr(stack.pop()); + stack.push(newExpression); + } + + @Override + public void endVisit(JsArrayLiteral x, JsContext ctx) { + JsArrayLiteral toReturn = new JsArrayLiteral(); + List expressions = toReturn.getExpressions(); + int size = x.getExpressions().size(); + while (size-- > 0) { + expressions.add(0, stack.pop()); + } + stack.push(toReturn); + } + + @Override + public void endVisit(JsBinaryOperation x, JsContext ctx) { + JsBinaryOperation toReturn = new JsBinaryOperation(x.getOperator()); + toReturn.setArg2(stack.pop()); + toReturn.setArg1(stack.pop()); + stack.push(toReturn); + } + + @Override + public void endVisit(JsBooleanLiteral x, JsContext ctx) { + stack.push(x); + } + + @Override + public void endVisit(JsConditional x, JsContext ctx) { + JsConditional toReturn = new JsConditional(); + toReturn.setElseExpression(stack.pop()); + toReturn.setThenExpression(stack.pop()); + toReturn.setTestExpression(stack.pop()); + stack.push(toReturn); + } + + /** + * The only functions that would get be visited are those being used as + * first-class objects. + */ + @Override + public void endVisit(JsFunction x, JsContext ctx) { + // Set a flag to indicate that we cannot continue, and push a null so + // we don't run out of elements on the stack. + successful = false; + stack.push(null); + } + + /** + * Cloning the invocation allows us to modify it without damaging other call + * sites. + */ + @Override + public void endVisit(JsInvocation x, JsContext ctx) { + JsInvocation toReturn = new JsInvocation(); + List params = toReturn.getArguments(); + int size = x.getArguments().size(); + while (size-- > 0) { + params.add(0, stack.pop()); + } + toReturn.setQualifier(stack.pop()); + stack.push(toReturn); + } + + /** + * Do a deep clone of a JsNameRef. Because JsNameRef chains are shared + * throughout the AST, you can't just go and change their qualifiers when + * re-writing an invocation. + */ + @Override + public void endVisit(JsNameRef x, JsContext ctx) { + JsNameRef toReturn; + JsName name = x.getName(); + if (name != null) { + toReturn = new JsNameRef(name); + } else { + toReturn = new JsNameRef(x.getIdent()); + } + + if (x.getQualifier() != null) { + toReturn.setQualifier(stack.pop()); + } + stack.push(toReturn); + } + + @Override + public void endVisit(JsNew x, JsContext ctx) { + int size = x.getArguments().size(); + List arguments = new ArrayList(size); + while (size-- > 0) { + arguments.add(0, stack.pop()); + } + JsNew toReturn = new JsNew(stack.pop()); + toReturn.getArguments().addAll(arguments); + stack.push(toReturn); + } + + @Override + public void endVisit(JsNullLiteral x, JsContext ctx) { + stack.push(x); + } + + @Override + public void endVisit(JsNumberLiteral x, JsContext ctx) { + stack.push(x); + } + + @Override + public void endVisit(JsObjectLiteral x, JsContext ctx) { + JsObjectLiteral toReturn = new JsObjectLiteral(); + List inits = toReturn.getPropertyInitializers(); + + int size = x.getPropertyInitializers().size(); + while (size-- > 0) { + /* + * JsPropertyInitializers are the only non-JsExpression objects that we + * care about, so we just go ahead and create the objects in the loop, + * rather than expecting it to be on the stack and having to perform + * narrowing casts at all stack.pop() invocations. + */ + JsPropertyInitializer newInit = new JsPropertyInitializer(); + newInit.setValueExpr(stack.pop()); + newInit.setLabelExpr(stack.pop()); + + inits.add(0, newInit); + } + stack.push(toReturn); + } + + @Override + public void endVisit(JsPostfixOperation x, JsContext ctx) { + JsPostfixOperation toReturn = new JsPostfixOperation(x.getOperator()); + toReturn.setArg(stack.pop()); + stack.push(toReturn); + } + + @Override + public void endVisit(JsPrefixOperation x, JsContext ctx) { + JsPrefixOperation toReturn = new JsPrefixOperation(x.getOperator()); + toReturn.setArg(stack.pop()); + stack.push(toReturn); + } + + @Override + public void endVisit(JsRegExp x, JsContext ctx) { + stack.push(x); + } + + @Override + public void endVisit(JsStringLiteral x, JsContext ctx) { + stack.push(x); + } + + @Override + public void endVisit(JsThisRef x, JsContext ctx) { + stack.push(new JsThisRef()); + } + + public JsExpression getExpression() { + return (successful && checkStack()) ? stack.peek() : null; + } + + private boolean checkStack() { + if (stack.size() > 1) { + throw new InternalCompilerException("Too many expressions on stack"); + } + + return stack.size() == 1; + } +} + diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/JsConstructExpressionVisitor.java b/js/js.ast/src/com/google/dart/compiler/backend/js/JsConstructExpressionVisitor.java new file mode 100644 index 00000000000..a15af6b44ed --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/JsConstructExpressionVisitor.java @@ -0,0 +1,104 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js; + +import com.google.dart.compiler.backend.js.ast.*; + +/** + * Searches for method invocations in constructor expressions that would not + * normally be surrounded by parentheses. + */ +public class JsConstructExpressionVisitor extends JsVisitor { + + public static boolean exec(JsExpression expression) { + if (JsPrecedenceVisitor.exec(expression) < JsPrecedenceVisitor.PRECEDENCE_NEW) { + return true; + } + JsConstructExpressionVisitor visitor = new JsConstructExpressionVisitor(); + visitor.accept(expression); + return visitor.containsInvocation; + } + + private boolean containsInvocation = false; + + private JsConstructExpressionVisitor() { + } + + /** + * We only look at the array expression since the index has its own scope. + */ + @Override + public boolean visit(JsArrayAccess x, JsContext ctx) { + accept(x.getArrayExpr()); + return false; + } + + /** + * Array literals have their own scoping. + */ + @Override + public boolean visit(JsArrayLiteral x, JsContext ctx) { + return false; + } + + /** + * Functions have their own scoping. + */ + @Override + public boolean visit(JsFunction x, JsContext ctx) { + return false; + } + + @Override + public boolean visit(JsInvocation x, JsContext ctx) { + containsInvocation = true; + return false; + } + + @Override + public boolean visit(JsNameRef x, JsContext ctx) { + if (!x.isLeaf()) { + accept(x.getQualifier()); + } + return false; + } + + /** + * New constructs bind to the nearest set of parentheses. + */ + @Override + public boolean visit(JsNew x, JsContext ctx) { + return false; + } + + /** + * Object literals have their own scope. + */ + @Override + public boolean visit(JsObjectLiteral x, JsContext ctx) { + return false; + } + + /** + * We only look at nodes that would not normally be surrounded by parentheses. + */ + @Override + protected T doAccept(T node) { + // Assign to Object to prevent 'inconvertible types' toJsAst errors due + // to http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6548436 + // reproducible in jdk1.6.0_02. + Object o = node; + if (o instanceof JsExpression) { + JsExpression expression = (JsExpression) o; + int precedence = JsPrecedenceVisitor.exec(expression); + // Only visit expressions that won't automatically be surrounded by + // parentheses + if (precedence < JsPrecedenceVisitor.PRECEDENCE_NEW) { + return node; + } + } + return super.doAccept(node); + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/JsFirstExpressionVisitor.java b/js/js.ast/src/com/google/dart/compiler/backend/js/JsFirstExpressionVisitor.java new file mode 100644 index 00000000000..0fdb8a5032b --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/JsFirstExpressionVisitor.java @@ -0,0 +1,119 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js; + +import com.google.dart.compiler.backend.js.ast.*; + +/** + * Determines if an expression statement needs to be surrounded by parentheses. + * + * The statement or the left-most expression needs to be surrounded by + * parentheses if the left-most expression is an object literal or a function + * object. Function declarations do not need parentheses. + * + * For example the following require parentheses:
+ *
    + *
  • { key : 'value'}
  • + *
  • { key : 'value'}.key
  • + *
  • function () {return 1;}()
  • + *
  • function () {return 1;}.prototype
  • + *
+ * + * The following do not require parentheses:
+ *
    + *
  • var x = { key : 'value'}
  • + *
  • "string" + { key : 'value'}.key
  • + *
  • function func() {}
  • + *
  • function() {}
  • + *
+ */ +public class JsFirstExpressionVisitor extends JsVisitor { + + public static boolean exec(JsExprStmt statement) { + JsFirstExpressionVisitor visitor = new JsFirstExpressionVisitor(); + JsExpression expression = statement.getExpression(); + // Pure function declarations do not need parentheses + if (expression instanceof JsFunction) { + return false; + } + visitor.accept(statement.getExpression()); + return visitor.needsParentheses; + } + + private boolean needsParentheses = false; + + private JsFirstExpressionVisitor() { + } + + @Override + public boolean visit(JsArrayAccess x, JsContext ctx) { + accept(x.getArrayExpr()); + return false; + } + + @Override + public boolean visit(JsArrayLiteral x, JsContext ctx) { + return false; + } + + @Override + public boolean visit(JsBinaryOperation x, JsContext ctx) { + accept(x.getArg1()); + return false; + } + + @Override + public boolean visit(JsConditional x, JsContext ctx) { + accept(x.getTestExpression()); + return false; + } + + @Override + public boolean visit(JsFunction x, JsContext ctx) { + needsParentheses = true; + return false; + } + + @Override + public boolean visit(JsInvocation x, JsContext ctx) { + accept(x.getQualifier()); + return false; + } + + @Override + public boolean visit(JsNameRef x, JsContext ctx) { + if (!x.isLeaf()) { + accept(x.getQualifier()); + } + return false; + } + + @Override + public boolean visit(JsNew x, JsContext ctx) { + return false; + } + + @Override + public boolean visit(JsObjectLiteral x, JsContext ctx) { + needsParentheses = true; + return false; + } + + @Override + public boolean visit(JsPostfixOperation x, JsContext ctx) { + accept(x.getArg()); + return false; + } + + @Override + public boolean visit(JsPrefixOperation x, JsContext ctx) { + return false; + } + + @Override + public boolean visit(JsRegExp x, JsContext ctx) { + return false; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/JsNamer.java b/js/js.ast/src/com/google/dart/compiler/backend/js/JsNamer.java new file mode 100644 index 00000000000..0c49ad05750 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/JsNamer.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; + +import com.google.dart.compiler.backend.js.ast.JsProgram; + +/** + * A namer runs through a program and renames the short names of JsNames. + * Namers must assign short names that don't clash and that are valid + * JS-identifiers. Nested JsScopes must not shadow JsNames from outer + * scopes. + * If a JsName is marked as non-obfuscatable then it must retain its short + * name. + */ +public interface JsNamer { + /** + * GlobalNames the shortNames of all JsNames of the program so that they are valid + * JS-identifiers and that there are no clashes and no shadowing. + */ + public void exec(JsProgram program); +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/JsNormalizer.java b/js/js.ast/src/com/google/dart/compiler/backend/js/JsNormalizer.java new file mode 100644 index 00000000000..04c5530cd42 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/JsNormalizer.java @@ -0,0 +1,103 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js; + +import com.google.dart.compiler.backend.js.ast.*; + +/** + * Fixes any semantic errors introduced by JS AST gen. + * + *
    + *
  • Creating clinit calls can put comma expressions as lvalues; the modifying + * operation must be moved inside the comma expression to the last argument.
  • + *
+ */ +public class JsNormalizer { + + /** + * Resolves any unresolved JsNameRefs. + */ + private static class JsNormalizing extends JsModVisitor { + + @Override + public void endVisit(JsBinaryOperation x, JsContext ctx) { + maybeShuffleModifyingBinary(x, ctx); + } + + @Override + public void endVisit(JsPostfixOperation x, JsContext ctx) { + maybeShuffleModifyingUnary(x, ctx); + } + + @Override + public void endVisit(JsPrefixOperation x, JsContext ctx) { + maybeShuffleModifyingUnary(x, ctx); + } + + /** + * Due to the way clinits are constructed, you can end up with a comma + * operation as the argument to a modifying operation, which is illegal. + * Juggle things to put the operator inside of the comma expression. + */ + private void maybeShuffleModifyingBinary(JsBinaryOperation x, JsContext ctx) { + JsBinaryOperator myOp = x.getOperator(); + JsExpression lhs = x.getArg1(); + + if (myOp.isAssignment() && (lhs instanceof JsBinaryOperation)) { + // Find the rightmost comma operation + JsBinaryOperation curLhs = (JsBinaryOperation) lhs; + assert (curLhs.getOperator() == JsBinaryOperator.COMMA); + while (curLhs.getArg2() instanceof JsBinaryOperation) { + curLhs = (JsBinaryOperation) curLhs.getArg2(); + assert (curLhs.getOperator() == JsBinaryOperator.COMMA); + } + // curLhs is now the rightmost comma operation; slide our operation in + x.setArg1(curLhs.getArg2()); + curLhs.setArg2(x); + // replace myself with the comma expression + ctx.replaceMe(lhs); + } + } + + /** + * Due to the way clinits are constructed, you can end up with a comma + * operation as the argument to a modifying operation, which is illegal. + * Juggle things to put the operator inside of the comma expression. + */ + private void maybeShuffleModifyingUnary(JsUnaryOperation x, JsContext ctx) { + JsUnaryOperator myOp = x.getOperator(); + JsExpression arg = x.getArg(); + if (myOp.isModifying() && (arg instanceof JsBinaryOperation)) { + // Find the rightmost comma operation + JsBinaryOperation curArg = (JsBinaryOperation) arg; + assert (curArg.getOperator() == JsBinaryOperator.COMMA); + while (curArg.getArg2() instanceof JsBinaryOperation) { + curArg = (JsBinaryOperation) curArg.getArg2(); + assert (curArg.getOperator() == JsBinaryOperator.COMMA); + } + // curArg is now the rightmost comma operation; slide our operation in + x.setArg(curArg.getArg2()); + curArg.setArg2(x); + // replace myself with the comma expression + ctx.replaceMe(arg); + } + } + } + + public static void exec(JsProgram program) { + new JsNormalizer(program).execImpl(); + } + + private final JsProgram program; + + private JsNormalizer(JsProgram program) { + this.program = program; + } + + private void execImpl() { + JsNormalizing normalizer = new JsNormalizing(); + normalizer.accept(program); + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/JsParserException.java b/js/js.ast/src/com/google/dart/compiler/backend/js/JsParserException.java new file mode 100644 index 00000000000..79b53c1d69e --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/JsParserException.java @@ -0,0 +1,92 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js; + +/** + * Indicates inability to parse JavaScript source. + */ +public class JsParserException extends Exception { + + /** + * Represents the location of a parser exception. + */ + public static class SourceDetail { + private final String fileName; + private final int line; + private final int lineOffset; + private final String lineSource; + + public SourceDetail(int line, String lineSource, int lineOffset, String fileName) { + this.line = line; + this.lineSource = lineSource; + this.lineOffset = lineOffset; + this.fileName = fileName; + } + + public String getFileName() { + return fileName; + } + + public int getLine() { + return line; + } + + public int getLineOffset() { + return lineOffset; + } + + public String getLineSource() { + return lineSource; + } + } + + private static String createMessageWithDetail(String msg, SourceDetail sourceDetail) { + if (sourceDetail == null) { + return msg; + } + StringBuffer sb = new StringBuffer(); + sb.append(sourceDetail.getFileName()); + sb.append('('); + sb.append(sourceDetail.getLine()); + sb.append(')'); + sb.append(": "); + sb.append(msg); + if (sourceDetail.getLineSource() != null) { + sb.append("\n> "); + sb.append(sourceDetail.getLineSource()); + sb.append("\n> "); + for (int i = 0, n = sourceDetail.getLineOffset(); i < n; ++i) { + sb.append('-'); + } + sb.append('^'); + } + return sb.toString(); + } + + private final SourceDetail sourceDetail; + + public JsParserException(String msg) { + this(msg, null); + } + + public JsParserException(String msg, int line, String lineSource, int lineOffset, String fileName) { + this(msg, new SourceDetail(line, lineSource, lineOffset, fileName)); + } + + public JsParserException(String msg, SourceDetail sourceDetail) { + super(createMessageWithDetail(msg, sourceDetail)); + this.sourceDetail = sourceDetail; + } + + /** + * Provides additional source detail in some cases. + * + * @return additional detail regarding the error, or null if no + * additional detail is available + */ + public SourceDetail getSourceDetail() { + return sourceDetail; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/JsPrecedenceVisitor.java b/js/js.ast/src/com/google/dart/compiler/backend/js/JsPrecedenceVisitor.java new file mode 100644 index 00000000000..c3a58b64e22 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/JsPrecedenceVisitor.java @@ -0,0 +1,274 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use 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; + + public static int exec(JsExpression expression) { + JsPrecedenceVisitor visitor = new JsPrecedenceVisitor(); + visitor.accept(expression); + if (visitor.answer < 0) { + throw new RuntimeException("Precedence must be >= 0!"); + } + return visitor.answer; + } + + private int answer = -1; + + private JsPrecedenceVisitor() { + } + + @Override + public boolean visit(JsArrayAccess x, JsContext ctx) { + answer = 16; + return false; + } + + @Override + public boolean visit(JsArrayLiteral x, JsContext ctx) { + answer = 17; // primary + return false; + } + + @Override + public boolean visit(JsBinaryOperation x, JsContext ctx) { + answer = x.getOperator().getPrecedence(); + return false; + } + + @Override + public boolean visit(JsBlock x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsBooleanLiteral x, JsContext ctx) { + answer = 17; // primary + return false; + } + + @Override + public boolean visit(JsBreak x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsCase x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsCatch x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsConditional x, JsContext ctx) { + answer = 3; + return false; + } + + @Override + public boolean visit(JsContinue x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsDebugger x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsDefault x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsDoWhile x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsEmpty x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsExprStmt x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsFor x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsForIn x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsFunction x, JsContext ctx) { + answer = 17; // primary + return false; + } + + @Override + public boolean visit(JsIf x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsInvocation x, JsContext ctx) { + answer = 16; + return false; + } + + @Override + public boolean visit(JsLabel x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsNameRef x, JsContext ctx) { + if (x.isLeaf()) { + answer = 17; // primary + } else { + answer = 16; // property access + } + return false; + } + + @Override + public boolean visit(JsNew x, JsContext ctx) { + answer = PRECEDENCE_NEW; + return false; + } + + @Override + public boolean visit(JsNullLiteral x, JsContext ctx) { + answer = 17; // primary + return false; + } + + @Override + public boolean visit(JsNumberLiteral x, JsContext ctx) { + answer = 17; // primary + return false; + } + + @Override + public boolean visit(JsObjectLiteral x, JsContext ctx) { + answer = 17; // primary + return false; + } + + @Override + public boolean visit(JsParameter x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsPostfixOperation x, JsContext ctx) { + answer = x.getOperator().getPrecedence(); + return false; + } + + @Override + public boolean visit(JsPrefixOperation x, JsContext ctx) { + answer = x.getOperator().getPrecedence(); + return false; + } + + @Override + public boolean visit(JsProgram x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsPropertyInitializer x, JsContext ctx) { + answer = 17; // primary + return false; + } + + @Override + public boolean visit(JsRegExp x, JsContext ctx) { + answer = 17; // primary + return false; + } + + @Override + public boolean visit(JsReturn x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsStringLiteral x, JsContext ctx) { + answer = 17; // primary + return false; + } + + @Override + public boolean visit(JsSwitch x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsThisRef x, JsContext ctx) { + answer = 17; // primary + return false; + } + + @Override + public boolean visit(JsThrow x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsTry x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsVars.JsVar x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsVars x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsWhile x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/JsPrettyNamer.java b/js/js.ast/src/com/google/dart/compiler/backend/js/JsPrettyNamer.java new file mode 100644 index 00000000000..2c61e01f6b3 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/JsPrettyNamer.java @@ -0,0 +1,122 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js; + +import com.google.dart.compiler.backend.js.ast.JsName; +import com.google.dart.compiler.backend.js.ast.JsProgram; +import com.google.dart.compiler.backend.js.ast.JsRootScope; +import com.google.dart.compiler.backend.js.ast.JsScope; + +import java.util.*; + +/** + * A namer that uses short, readable idents to maximize reability. + */ +public class JsPrettyNamer implements JsNamer { + + public JsPrettyNamer() { + this.program = null; + } + + @Override + public void exec(JsProgram program) { + new JsPrettyNamer(program).execImpl(); + } + + /** + * Communicates to a parent scope all the idents used by all child scopes. + */ + private Set childIdents = null; + + private final JsProgram program; + + /** + * A map containing the next integer to try as an identifier suffix for a + * given JsScope. + */ + private IdentityHashMap> startIdentForScope = + new IdentityHashMap>(); + + protected JsPrettyNamer(JsProgram program) { + this.program = program; + } + + private void execImpl() { + visit(program.getRootScope()); + } + + private boolean isLegal(JsScope scope, Set childIdents, String newIdent) { + if (JsReservedIdentifiers.isKeyword(newIdent)) { + return false; + } + if (childIdents.contains(newIdent)) { + // one of my children already claimed this ident + return false; + } + /* + * Never obfuscate a name into an identifier that conflicts with an existing + * unobfuscatable name! It's okay if it conflicts with an existing + * obfuscatable name; that name will get obfuscated out of the way. + */ + return (scope.findExistingUnobfuscatableName(newIdent) == null); + } + + private void visit(JsScope scope) { + HashMap startIdent = startIdentForScope.get(scope); + if (startIdent == null) { + startIdent = new HashMap(); + startIdentForScope.put(scope, startIdent); + } + + // Save off the childIdents which is currently being computed for my parent. + Set myChildIdents = childIdents; + + /* + * Visit my children first. Reset childIdents so that my children will get a + * clean slate: I do not communicate to my children. + */ + childIdents = new HashSet(); + List children = scope.getChildren(); + for (Iterator it = children.iterator(); it.hasNext();) { + visit(it.next()); + } + + JsRootScope rootScope = program.getRootScope(); + if (scope == rootScope) { + return; + } + + // Visit all my idents. + for (Iterator it = scope.getAllNames(); it.hasNext();) { + JsName name = it.next(); + if (!name.isObfuscatable()) { + // Unobfuscatable names become themselves. + name.setShortIdent(name.getIdent()); + continue; + } + + String newIdent = name.getShortIdent(); + if (!isLegal(scope, childIdents, newIdent)) { + String checkIdent; + + // Start searching using a suffix hint stored in the scope. + // We still do a search in case there is a collision with + // a user-provided identifier + Integer s = startIdent.get(newIdent); + int suffix = (s == null) ? 0 : s.intValue(); + do { + checkIdent = newIdent + "_" + suffix++; + } while (!isLegal(scope, childIdents, checkIdent)); + startIdent.put(newIdent, suffix); + name.setShortIdent(checkIdent); + } else { + // nothing to do; the short name is already good + } + childIdents.add(name.getShortIdent()); + } + myChildIdents.addAll(childIdents); + childIdents = myChildIdents; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/JsRequiresSemiVisitor.java b/js/js.ast/src/com/google/dart/compiler/backend/js/JsRequiresSemiVisitor.java new file mode 100644 index 00000000000..f4095c5c822 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/JsRequiresSemiVisitor.java @@ -0,0 +1,140 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use 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:
+ *
    + *
  • if (cond);
  • + *
  • while (cond);
  • + *
+ * + * The following do not require semicolons:
+ *
    + *
  • return 1
  • + *
  • do {} while(true)
  • + *
+ */ +public class JsRequiresSemiVisitor extends JsVisitor { + + public static boolean exec(JsStatement lastStatement) { + JsRequiresSemiVisitor visitor = new JsRequiresSemiVisitor(); + visitor.accept(lastStatement); + return visitor.needsSemicolon; + } + + private boolean needsSemicolon = false; + + private JsRequiresSemiVisitor() { + } + + @Override + public boolean visit(JsBlock x, JsContext ctx) { + return false; + } + + @Override + public boolean visit(JsBreak x, JsContext ctx) { + return false; + } + + @Override + public boolean visit(JsDebugger x, JsContext ctx) { + return false; + } + + @Override + public boolean visit(JsDoWhile x, JsContext ctx) { + return false; + } + + @Override + public boolean visit(JsEmpty x, JsContext ctx) { + return false; + } + + @Override + public boolean visit(JsExprStmt x, JsContext ctx) { + return false; + } + + @Override + public boolean visit(JsFor x, JsContext ctx) { + if (x.getBody() instanceof JsEmpty) { + needsSemicolon = true; + } + return false; + } + + @Override + public boolean visit(JsForIn x, JsContext ctx) { + if (x.getBody() instanceof JsEmpty) { + needsSemicolon = true; + } + return false; + } + + @Override + public boolean visit(JsIf x, JsContext ctx) { + JsStatement thenStmt = x.getThenStmt(); + JsStatement elseStmt = x.getElseStmt(); + JsStatement toCheck = thenStmt; + if (elseStmt != null) { + toCheck = elseStmt; + } + if (toCheck instanceof JsEmpty) { + needsSemicolon = true; + } else { + // Must recurse to determine last statement (possible if-else chain). + accept(toCheck); + } + return false; + } + + @Override + public boolean visit(JsLabel x, JsContext ctx) { + if (x.getStmt() instanceof JsEmpty) { + needsSemicolon = true; + } + return false; + } + + @Override + public boolean visit(JsReturn x, JsContext ctx) { + return false; + } + + @Override + public boolean visit(JsSwitch x, JsContext ctx) { + return false; + } + + @Override + public boolean visit(JsThrow x, JsContext ctx) { + return false; + } + + @Override + public boolean visit(JsTry x, JsContext ctx) { + return false; + } + + @Override + public boolean visit(JsVars x, JsContext ctx) { + return false; + } + + @Override + public boolean visit(JsWhile x, JsContext ctx) { + if (x.getBody() instanceof JsEmpty) { + needsSemicolon = true; + } + return false; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/JsReservedIdentifiers.java b/js/js.ast/src/com/google/dart/compiler/backend/js/JsReservedIdentifiers.java new file mode 100644 index 00000000000..56ae5428a3d --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/JsReservedIdentifiers.java @@ -0,0 +1,223 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js; + +import java.util.HashSet; +import java.util.Set; + +/** + * Determines whether or not a particular string is a JavaScript keyword or not. + */ +public class JsReservedIdentifiers { + + private static Set javaScriptKeywords; + private static Set reservedGlobalSymbols; + private static Set reservedPropertySymbols; + + static { + javaScriptKeywords = new HashSet(); + reservedGlobalSymbols = new HashSet(); + reservedPropertySymbols = new HashSet(); + initJavaScriptKeywords(); + initReservedGlobalSymbols(); + initReservedPropertySymbols(); + } + + public static boolean isKeyword(String s) { + return javaScriptKeywords.contains(s); + } + + private static void initJavaScriptKeywords() { + String[] keywords = new String[] { + // These are current keywords + "break", "delete", "function", "return", "typeof", "case", "do", "if", "switch", "var", + "catch", "else", "in", "this", "void", "continue", "false", "instanceof", "throw", + "while", "debugger", "finally", "new", "true", "with", "default", "for", + "null", "try", + + // These are future keywords + "abstract", "double", "goto", "native", "static", "boolean", "enum", "implements", + "package", "super", "byte", "export", "import", "private", "synchronized", "char", + "extends", "int", "protected", "throws", "class", "final", "interface", "public", + "transient", "const", "float", "long", "short", "volatile" + }; + + for (int i = 0; i < keywords.length; i++) { + javaScriptKeywords.add(keywords[i]); + } + } + + /** + * @return a set containing all known reserved global identifiers. This set must not be modified. + */ + public static Set getReservedGlobalSymbols() { + return reservedGlobalSymbols; + } + + /** + * Returns true if the string s can not be used as a global identifier. The check includes + * JavaScript keywords (as they must not be used either). + * @param s + * @return true if the given String must not be used as a global identifier. + */ + public static boolean isReservedGlobalSymbol(String s) { + return isKeyword(s) || reservedGlobalSymbols.contains(s); + } + + private static void initReservedGlobalSymbols() { + // Section references are from Ecma-262 + // (http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf) + String[] commonBuiltins = new String[] { + // 15.1.1 Value Properties of the Global Object + "NaN", "Infinity", "undefined", + + // 15.1.2 Function Properties of the Global Object + "eval", "parseInt", "parseFloat", "isNan", "isFinite", + + // 15.1.3 URI Handling Function Properties + "decodeURI", "decodeURIComponent", + "encodeURI", + "encodeURIComponent", + + // 15.1.4 Constructor Properties of the Global Object + "Object", "Function", "Array", "String", "Boolean", "Number", "Date", + "RegExp", "Error", "EvalError", "RangeError", "ReferenceError", + "SyntaxError", "TypeError", "URIError", + + // 15.1.5 Other Properties of the Global Object + "Math", + + // 10.1.6 Activation Object + "arguments", + + // B.2 Additional Properties (non-normative) + "escape", "unescape", + + // Window props (https://developer.mozilla.org/en/DOM/window) + "applicationCache", "closed", "Components", "content", "controllers", + "crypto", "defaultStatus", "dialogArguments", "directories", + "document", "frameElement", "frames", "fullScreen", "globalStorage", + "history", "innerHeight", "innerWidth", "length", + "location", "locationbar", "localStorage", "menubar", + "mozInnerScreenX", "mozInnerScreenY", "mozScreenPixelsPerCssPixel", + "name", "navigator", "opener", "outerHeight", "outerWidth", + "pageXOffset", "pageYOffset", "parent", "personalbar", "pkcs11", + "returnValue", "screen", "scrollbars", "scrollMaxX", "scrollMaxY", + "self", "sessionStorage", "sidebar", "status", "statusbar", "toolbar", + "top", "window", + + // Window methods (https://developer.mozilla.org/en/DOM/window) + "alert", "addEventListener", "atob", "back", "blur", "btoa", + "captureEvents", "clearInterval", "clearTimeout", "close", "confirm", + "disableExternalCapture", "dispatchEvent", "dump", + "enableExternalCapture", "escape", "find", "focus", "forward", + "GeckoActiveXObject", "getAttention", "getAttentionWithCycleCount", + "getComputedStyle", "getSelection", "home", "maximize", "minimize", + "moveBy", "moveTo", "open", "openDialog", "postMessage", "print", + "prompt", "QueryInterface", "releaseEvents", "removeEventListener", + "resizeBy", "resizeTo", "restore", "routeEvent", "scroll", "scrollBy", + "scrollByLines", "scrollByPages", "scrollTo", "setInterval", + "setResizeable", "setTimeout", "showModalDialog", "sizeToContent", + "stop", "uuescape", "updateCommands", "XPCNativeWrapper", + "XPCSafeJSOjbectWrapper", + + // Mozilla Window event handlers, same cite + "onabort", "onbeforeunload", "onchange", "onclick", "onclose", + "oncontextmenu", "ondragdrop", "onerror", "onfocus", "onhashchange", + "onkeydown", "onkeypress", "onkeyup", "onload", "onmousedown", + "onmousemove", "onmouseout", "onmouseover", "onmouseup", + "onmozorientation", "onpaint", "onreset", "onresize", "onscroll", + "onselect", "onsubmit", "onunload", + + // Safari Web Content Guide + // http://developer.apple.com/library/safari/#documentation/AppleApplications/Reference/SafariWebContent/SafariWebContent.pdf + // WebKit Window member data, from WebKit DOM Reference + // (http://developer.apple.com/safari/library/documentation/AppleApplications/Reference/WebKitDOMRef/DOMWindow_idl/Classes/DOMWindow/index.html) + // TODO(fredsa) Many, many more functions and member data to add + "ontouchcancel", "ontouchend", "ontouchmove", "ontouchstart", + "ongesturestart", "ongesturechange", "ongestureend", + + // extra window methods + "uneval", + + // keywords https://developer.mozilla.org/en/New_in_JavaScript_1.7, + // https://developer.mozilla.org/en/New_in_JavaScript_1.8.1 + "getPrototypeOf", "let", "yield", + + // "future reserved words" + "abstract", "int", "short", "boolean", "interface", "static", "byte", + "long", "char", "final", "native", "synchronized", "float", "package", + "throws", "goto", "private", "transient", "implements", "protected", + "volatile", "double", "public", + + // IE methods + // (http://msdn.microsoft.com/en-us/library/ms535873(VS.85).aspx#) + "attachEvent", "clientInformation", "clipboardData", "createPopup", + "dialogHeight", "dialogLeft", "dialogTop", "dialogWidth", + "onafterprint", "onbeforedeactivate", "onbeforeprint", + "oncontrolselect", "ondeactivate", "onhelp", "onresizeend", + + // Common browser-defined identifiers not defined in ECMAScript + "event", "external", "Debug", "Enumerator", "Global", "Image", + "ActiveXObject", "VBArray", "Components", + + // Functions commonly defined on Object + "toString", "getClass", "constructor", "prototype", "valueOf", + + // Client-side JavaScript identifiers, which are needed for linkers + // that don't ensure GWT's window != $wnd, document != $doc, etc. + // Taken from the Rhino book, pg 715 + "Anchor", "Applet", "Attr", "Canvas", "CanvasGradient", + "CanvasPattern", "CanvasRenderingContext2D", "CDATASection", + "CharacterData", "Comment", "CSS2Properties", "CSSRule", + "CSSStyleSheet", "Document", "DocumentFragment", "DocumentType", + "DOMException", "DOMImplementation", "DOMParser", "Element", "Event", + "ExternalInterface", "FlashPlayer", "Form", "Frame", "History", + "HTMLCollection", "HTMLDocument", "HTMLElement", "IFrame", "Image", + "Input", "JSObject", "KeyEvent", "Link", "Location", "MimeType", + "MouseEvent", "Navigator", "Node", "NodeList", "Option", "Plugin", + "ProcessingInstruction", "Range", "RangeException", "Screen", "Select", + "Table", "TableCell", "TableRow", "TableSelection", "Text", "TextArea", + "UIEvent", "Window", "XMLHttpRequest", "XMLSerializer", + "XPathException", "XPathResult", "XSLTProcessor", + + // These keywords trigger the loading of the java-plugin. For the + // next-generation plugin, this results in starting a new Java process. + "java", "Packages", "netscape", "sun", "JavaObject", "JavaClass", + "JavaArray", "JavaMember", + + // GWT-defined identifiers + "$wnd", "$doc", "$entry", "$moduleName", "$moduleBase", "$gwt_version", "$sessionId", + + // Identifiers used by JsStackEmulator; later set to obfuscatable + "$stack", "$stackDepth", "$location", + + // TODO: prove why this is necessary or remove it + "call" + }; + for (int i = 0; i < commonBuiltins.length; i++) { + reservedGlobalSymbols.add(commonBuiltins[i]); + } + } + + /** + * Returns true if the given string can not be used as property symbol. The check excludes + * keywords as JavaScript allow keywords as properties. + * @param s + * @return true if the given string must not be used as a property. + */ + public static boolean isReservedPropertySymbol(String s) { + return reservedPropertySymbols.contains(s); + } + + private static void initReservedPropertySymbols() { + // TODO(floitsch): fill in reserved property symbols. + reservedPropertySymbols.add("__PROTO__"); + reservedPropertySymbols.add("prototype"); + } + + private JsReservedIdentifiers() { + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/JsSourceGenerationVisitor.java b/js/js.ast/src/com/google/dart/compiler/backend/js/JsSourceGenerationVisitor.java new file mode 100644 index 00000000000..3a2689f65da --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/JsSourceGenerationVisitor.java @@ -0,0 +1,39 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js; + +import com.google.dart.compiler.util.TextOutput; +import com.google.dart.compiler.backend.js.ast.JsContext; +import com.google.dart.compiler.backend.js.ast.JsProgramFragment; +import com.google.dart.compiler.backend.js.ast.JsBlock; +import com.google.dart.compiler.backend.js.ast.JsProgram; + +/** + * Generates JavaScript source from an AST. + */ +public class JsSourceGenerationVisitor extends JsToStringGenerationVisitor { + + public JsSourceGenerationVisitor(TextOutput out) { + super(out); + } + + @Override + public boolean visit(JsProgram x, JsContext ctx) { + // Descend naturally. + return true; + } + + @Override + public boolean visit(JsProgramFragment x, JsContext ctx) { + // Descend naturally. + return true; + } + + @Override + public boolean visit(JsBlock x, JsContext ctx) { + printJsBlock(x, false, true); + return false; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/JsToStringGenerationVisitor.java b/js/js.ast/src/com/google/dart/compiler/backend/js/JsToStringGenerationVisitor.java new file mode 100644 index 00000000000..a078eca30ac --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/JsToStringGenerationVisitor.java @@ -0,0 +1,1331 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use 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.util.TextOutput; + +import java.util.*; +import java.util.regex.Pattern; + +//import com.google.debugging.sourcemap.FilePosition; + +/** + * Produces text output from a JavaScript AST. + */ +public class JsToStringGenerationVisitor extends JsVisitor { + + private static final char[] CHARS_BREAK = "break".toCharArray(); + private static final char[] CHARS_CASE = "case".toCharArray(); + private static final char[] CHARS_CATCH = "catch".toCharArray(); + private static final char[] CHARS_CONTINUE = "continue".toCharArray(); + private static final char[] CHARS_DEBUGGER = "debugger".toCharArray(); + private static final char[] CHARS_DEFAULT = "default".toCharArray(); + private static final char[] CHARS_DO = "do".toCharArray(); + private static final char[] CHARS_ELSE = "else".toCharArray(); + private static final char[] CHARS_FALSE = "false".toCharArray(); + private static final char[] CHARS_FINALLY = "finally".toCharArray(); + private static final char[] CHARS_FOR = "for".toCharArray(); + private static final char[] CHARS_FUNCTION = "function".toCharArray(); + private static final char[] CHARS_IF = "if".toCharArray(); + private static final char[] CHARS_IN = "in".toCharArray(); + private static final char[] CHARS_NEW = "new".toCharArray(); + private static final char[] CHARS_NULL = "null".toCharArray(); + private static final char[] CHARS_RETURN = "return".toCharArray(); + private static final char[] CHARS_SWITCH = "switch".toCharArray(); + private static final char[] CHARS_THIS = "this".toCharArray(); + private static final char[] CHARS_THROW = "throw".toCharArray(); + private static final char[] CHARS_TRUE = "true".toCharArray(); + private static final char[] CHARS_TRY = "try".toCharArray(); + private static final char[] CHARS_VAR = "var".toCharArray(); + private static final char[] CHARS_WHILE = "while".toCharArray(); + private static final char[] HEX_DIGITS = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + /** + * How many lines of code to print inside of a JsBlock when printing terse. + */ + private static final int JSBLOCK_LINES_TO_PRINT = 3; + + /** + * A variable name is valid if it contains only letters, numbers, _, $ and + * does not begin with a number. There are actually other valid variable + * names, such as ones that contain escaped Unicode characters, but we + * surround those names with quotes in property initializers to be safe. + */ + private static final Pattern VALID_NAME_PATTERN = Pattern.compile("[a-zA-Z_$][\\w$]*"); + + public static String javaScriptString(String value) { + return javaScriptString(value, false); + } + + /** + * Generate JavaScript code that evaluates to the supplied string. Adapted + * from + * . The difference is that we quote with either " or ' depending on + * which one is used less inside the string. + */ + public static String javaScriptString(String value, boolean forceDoubleQuote) { + char[] chars = value.toCharArray(); + final int n = chars.length; + int quoteCount = 0; + int aposCount = 0; + for (int i = 0; i < n; ++i) { + switch (chars[i]) { + case '"': + ++quoteCount; + break; + case '\'': + ++aposCount; + break; + } + } + + StringBuffer result = new StringBuffer(value.length() + 16); + + char quoteChar = (quoteCount < aposCount || forceDoubleQuote) ? '"' : '\''; + result.append(quoteChar); + + for (int i = 0; i < n; ++i) { + char c = chars[i]; + + if (' ' <= c && c <= '~' && c != quoteChar && c != '\\') { + // an ordinary print character (like C isprint()) + result.append(c); + continue; + } + + int escape = -1; + switch (c) { + case '\b': + escape = 'b'; + break; + case '\f': + escape = 'f'; + break; + case '\n': + escape = 'n'; + break; + case '\r': + escape = 'r'; + break; + case '\t': + escape = 't'; + break; + case '"': + escape = '"'; + break; // only reach here if == quoteChar + case '\'': + escape = '\''; + break; // only reach here if == quoteChar + case '\\': + escape = '\\'; + break; + } + + if (escape >= 0) { + // an \escaped sort of character + result.append('\\'); + result.append((char) escape); + } else { + /* + * Emit characters from 0 to 31 that don't have a single character + * escape sequence in octal where possible. This saves one or two + * characters compared to the hexadecimal format '\xXX'. + * + * These short octal sequences may only be used at the end of the string + * or where the following character is a non-digit. Otherwise, the + * following character would be incorrectly interpreted as belonging to + * the sequence. + */ + if (c < ' ' && (i == n - 1 || chars[i + 1] < '0' || chars[i + 1] > '9')) { + result.append('\\'); + if (c > 0x7) { + result.append((char) ('0' + (0x7 & (c >> 3)))); + } + result.append((char) ('0' + (0x7 & c))); + } else { + int hexSize; + if (c < 256) { + // 2-digit hex + result.append("\\x"); + hexSize = 2; + } else { + // Unicode. + result.append("\\u"); + hexSize = 4; + } + // append hexadecimal form of ch left-padded with 0 + for (int shift = (hexSize - 1) * 4; shift >= 0; shift -= 4) { + int digit = 0xf & (c >> shift); + result.append(HEX_DIGITS[digit]); + } + } + } + } + result.append(quoteChar); + escapeClosingTags(result); + String resultString = result.toString(); + return resultString; + } + + /** + * Escapes any closing XML tags embedded in str, which could + * potentially cause a parse failure in a browser, for example, embedding a + * closing <script> tag. + * + * @param str an unescaped literal; May be null + */ + private static void escapeClosingTags(StringBuffer str) { + if (str == null) { + return; + } + + int index = 0; + + while ((index = str.indexOf(" globalBlocks = new HashSet(); + private final TextOutput p; + private ArrayList statementEnds = new ArrayList(); + private ArrayList statementStarts = new ArrayList(); + private boolean buildMappings; + //private List mappings = Lists.newArrayList(); + + public JsToStringGenerationVisitor(TextOutput out) { + this.p = out; + } + + /** + * @param generate Whether to translate the source map. + */ + public void generateSourceMap(boolean generate) { + this.buildMappings = generate; + } + +// public void writeSourceMap(Appendable out, String name) throws IOException { +// GenerateSourceMap generator = new GenerateSourceMap(); +// for (SourceMapping m : mappings) { +// generator.addMapping(m.getNode(), m.getStart(), m.getEnd()); +// } +// generator.appendTo(out, name); +// } + + @Override + public void doTraverse(JsVisitable x, JsContext ctx) { + //SourceMapping m = null; + // TODO(johnlenz): filter out uninteresting node types + if (buildMappings) { + // m = new SourceMapping((HasSourceInfo) x, new FilePosition(p.getLine(), p.getColumn())); + // mappings.add(m); + } + + super.doTraverse(x, ctx); + + if (buildMappings) { + // m.setEnd(new FilePosition(p.getLine(), p.getColumn())); + } + } + + @Override + public boolean visit(JsArrayAccess x, JsContext ctx) { + JsExpression arrayExpr = x.getArrayExpr(); + _parenPush(x, arrayExpr, false); + accept(arrayExpr); + _parenPop(x, arrayExpr, false); + _lsquare(); + accept(x.getIndexExpr()); + _rsquare(); + return false; + } + + @Override + public boolean visit(JsArrayLiteral x, JsContext ctx) { + _lsquare(); + boolean sep = false; + for (Object element : x.getExpressions()) { + JsExpression arg = (JsExpression) element; + sep = _sepCommaOptSpace(sep); + _parenPushIfCommaExpr(arg); + accept(arg); + _parenPopIfCommaExpr(arg); + } + _rsquare(); + return false; + } + + @Override + public boolean visit(JsBinaryOperation x, JsContext ctx) { + JsBinaryOperator op = x.getOperator(); + JsExpression arg1 = x.getArg1(); + _parenPush(x, arg1, !op.isLeftAssociative()); + accept(arg1); + if (op.isKeyword()) { + _parenPopOrSpace(x, arg1, !op.isLeftAssociative()); + } else { + _parenPop(x, arg1, !op.isLeftAssociative()); + _spaceOpt(); + } + p.print(op.getSymbol()); + JsExpression arg2 = x.getArg2(); + if (_spaceCalc(op, arg2)) { + _parenPushOrSpace(x, arg2, op.isLeftAssociative()); + } else { + _spaceOpt(); + _parenPush(x, arg2, op.isLeftAssociative()); + } + accept(arg2); + _parenPop(x, arg2, op.isLeftAssociative()); + return false; + } + + @Override + public boolean visit(JsBlock x, JsContext ctx) { + printJsBlock(x, true, true); + return false; + } + + @Override + public boolean visit(JsBooleanLiteral x, JsContext ctx) { + if (x.getValue()) { + _true(); + } else { + _false(); + } + return false; + } + + @Override + public boolean visit(JsBreak x, JsContext ctx) { + _break(); + + JsNameRef label = x.getLabel(); + if (label != null) { + _space(); + _nameRef(label); + } + + return false; + } + + @Override + public boolean visit(JsCase x, JsContext ctx) { + _case(); + _space(); + accept(x.getCaseExpr()); + _colon(); + _newlineOpt(); + + indent(); + for (Object element : x.getStmts()) { + JsStatement stmt = (JsStatement) element; + needSemi = true; + accept(stmt); + if (needSemi) { + _semi(); + } + _newlineOpt(); + } + outdent(); + needSemi = false; + return false; + } + + @Override + public boolean visit(JsCatch x, JsContext ctx) { + _spaceOpt(); + _catch(); + _spaceOpt(); + _lparen(); + _nameDef(x.getParameter().getName()); + + // Optional catch condition. + // + JsExpression catchCond = x.getCondition(); + if (catchCond != null) { + _space(); + _if(); + _space(); + accept(catchCond); + } + + _rparen(); + _spaceOpt(); + accept(x.getBody()); + + return false; + } + + @Override + public boolean visit(JsConditional x, JsContext ctx) { + // Associativity: for the then and else branches, it is safe to insert + // another + // ternary expression, but if the test expression is a ternary, it should + // get parentheses around it. + { + JsExpression testExpression = x.getTestExpression(); + _parenPush(x, testExpression, true); + accept(testExpression); + _parenPop(x, testExpression, true); + } + _questionMark(); + { + JsExpression thenExpression = x.getThenExpression(); + _parenPush(x, thenExpression, false); + accept(thenExpression); + _parenPop(x, thenExpression, false); + } + _colon(); + { + JsExpression elseExpression = x.getElseExpression(); + _parenPush(x, elseExpression, false); + accept(elseExpression); + _parenPop(x, elseExpression, false); + } + return false; + } + + @Override + public boolean visit(JsContinue x, JsContext ctx) { + _continue(); + + JsNameRef label = x.getLabel(); + if (label != null) { + _space(); + _nameRef(label); + } + + return false; + } + + @Override + public boolean visit(JsDebugger x, JsContext ctx) { + _debugger(); + return false; + } + + @Override + public boolean visit(JsDefault x, JsContext ctx) { + _default(); + _colon(); + + indent(); + for (Object element : x.getStmts()) { + JsStatement stmt = (JsStatement) element; + needSemi = true; + accept(stmt); + if (needSemi) { + _semi(); + } + _newlineOpt(); + } + outdent(); + needSemi = false; + return false; + } + + @Override + public boolean visit(JsDoWhile x, JsContext ctx) { + _do(); + _nestedPush(x.getBody(), true); + accept(x.getBody()); + _nestedPop(x.getBody()); + if (needSemi) { + _semi(); + _newlineOpt(); + } else { + _spaceOpt(); + needSemi = true; + } + _while(); + _spaceOpt(); + _lparen(); + accept(x.getCondition()); + _rparen(); + return false; + } + + @Override + public boolean visit(JsEmpty x, JsContext ctx) { + return false; + } + + @Override + public boolean visit(JsExprStmt x, JsContext ctx) { + boolean surroundWithParentheses = JsFirstExpressionVisitor.exec(x); + if (surroundWithParentheses) { + _lparen(); + } + JsExpression expr = x.getExpression(); + accept(expr); + if (surroundWithParentheses) { + _rparen(); + } + return false; + } + + @Override + public boolean visit(JsFor x, JsContext ctx) { + _for(); + _spaceOpt(); + _lparen(); + + // The init expressions or var decl. + // + if (x.getInitExpr() != null) { + accept(x.getInitExpr()); + } else if (x.getInitVars() != null) { + accept(x.getInitVars()); + } + + _semi(); + + // The loop test. + // + if (x.getCondition() != null) { + _spaceOpt(); + accept(x.getCondition()); + } + + _semi(); + + // The incr expression. + // + if (x.getIncrExpr() != null) { + _spaceOpt(); + accept(x.getIncrExpr()); + } + + _rparen(); + _nestedPush(x.getBody(), false); + accept(x.getBody()); + _nestedPop(x.getBody()); + return false; + } + + @Override + public boolean visit(JsForIn x, JsContext ctx) { + _for(); + _spaceOpt(); + _lparen(); + + if (x.getIterVarName() != null) { + _var(); + _space(); + _nameDef(x.getIterVarName()); + + if (x.getIterExpr() != null) { + _spaceOpt(); + _assignment(); + _spaceOpt(); + accept(x.getIterExpr()); + } + } else { + // Just a name ref. + // + accept(x.getIterExpr()); + } + + _space(); + _in(); + _space(); + accept(x.getObjExpr()); + + _rparen(); + _nestedPush(x.getBody(), false); + accept(x.getBody()); + _nestedPop(x.getBody()); + return false; + } + + // function foo(a, b) { + // stmts... + // } + // + @Override + public boolean visit(JsFunction x, JsContext ctx) { + _function(); + + // Functions can be anonymous. + // + if (x.getName() != null) { + _space(); + _nameOf(x); + } + + _lparen(); + boolean sep = false; + for (Object element : x.getParameters()) { + JsParameter param = (JsParameter) element; + sep = _sepCommaOptSpace(sep); + accept(param); + } + _rparen(); + + accept(x.getBody()); + needSemi = true; + return false; + } + + @Override + public boolean visit(JsIf x, JsContext ctx) { + _if(); + _spaceOpt(); + _lparen(); + accept(x.getIfExpr()); + _rparen(); + JsStatement thenStmt = x.getThenStmt(); + _nestedPush(thenStmt, false); + accept(thenStmt); + _nestedPop(thenStmt); + JsStatement elseStmt = x.getElseStmt(); + if (elseStmt != null) { + if (needSemi) { + _semi(); + _newlineOpt(); + } else { + _spaceOpt(); + needSemi = true; + } + _else(); + boolean elseIf = elseStmt instanceof JsIf; + if (!elseIf) { + _nestedPush(elseStmt, true); + } else { + _space(); + } + accept(elseStmt); + if (!elseIf) { + _nestedPop(elseStmt); + } + } + return false; + } + + @Override + public boolean visit(JsInvocation x, JsContext ctx) { + JsExpression qualifier = x.getQualifier(); + _parenPush(x, qualifier, false); + accept(qualifier); + _parenPop(x, qualifier, false); + + _lparen(); + boolean sep = false; + for (Object element : x.getArguments()) { + JsExpression arg = (JsExpression) element; + sep = _sepCommaOptSpace(sep); + _parenPushIfCommaExpr(arg); + accept(arg); + _parenPopIfCommaExpr(arg); + } + _rparen(); + return false; + } + + @Override + public boolean visit(JsLabel x, JsContext ctx) { + _nameOf(x); + _colon(); + _spaceOpt(); + accept(x.getStmt()); + return false; + } + + @Override + public boolean visit(JsNameRef x, JsContext ctx) { + JsExpression q = x.getQualifier(); + if (q != null) { + _parenPush(x, q, false); + if (q instanceof JsNumberLiteral) { + /** + * Fix for Issue #3796. "42.foo" is not allowed, but "(42).foo" is. + */ + _lparen(); + } + accept(q); + if (q instanceof JsNumberLiteral) { + _rparen(); + } + _parenPop(x, q, false); + _dot(); + } + _nameRef(x); + return false; + } + + @Override + public boolean visit(JsNew x, JsContext ctx) { + _new(); + _space(); + + JsExpression ctorExpr = x.getConstructorExpression(); + boolean needsParens = JsConstructExpressionVisitor.exec(ctorExpr); + if (needsParens) { + _lparen(); + } + accept(ctorExpr); + if (needsParens) { + _rparen(); + } + + /* + * If a constructor call has no arguments, it may simply be replaced with + * "new Constructor" with no parentheses. + */ + List args = x.getArguments(); + if (args.size() > 0) { + _lparen(); + boolean sep = false; + for (JsExpression arg : args) { + sep = _sepCommaOptSpace(sep); + _parenPushIfCommaExpr(arg); + accept(arg); + _parenPopIfCommaExpr(arg); + } + _rparen(); + } + + return false; + } + + @Override + public boolean visit(JsNullLiteral x, JsContext ctx) { + _null(); + return false; + } + + @Override + public boolean visit(JsNumberLiteral x, JsContext ctx) { + double dvalue = x.getValue(); + long lvalue = (long) dvalue; + if (lvalue == dvalue) { + p.print(Long.toString(lvalue)); + } else { + p.print(Double.toString(dvalue)); + } + return false; + } + + @Override + public boolean visit(JsObjectLiteral x, JsContext ctx) { + _lbrace(); + boolean sep = false; + for (Object element : x.getPropertyInitializers()) { + sep = _sepCommaOptSpace(sep); + JsPropertyInitializer propInit = (JsPropertyInitializer) element; + printLabel : { + JsExpression labelExpr = propInit.getLabelExpr(); + // labels can be either string, integral, or decimal literals + if (labelExpr instanceof JsStringLiteral) { + String propName = ((JsStringLiteral) labelExpr).getValue(); + if (VALID_NAME_PATTERN.matcher(propName).matches() + && !JsReservedIdentifiers.isKeyword(propName)) { + p.print(propName); + break printLabel; + } + } + accept(labelExpr); + } + _colon(); + JsExpression valueExpr = propInit.getValueExpr(); + _parenPushIfCommaExpr(valueExpr); + accept(valueExpr); + _parenPopIfCommaExpr(valueExpr); + } + _rbrace(); + return false; + } + + @Override + public boolean visit(JsParameter x, JsContext ctx) { + _nameOf(x); + return false; + } + + @Override + public boolean visit(JsPostfixOperation x, JsContext ctx) { + JsUnaryOperator op = x.getOperator(); + JsExpression arg = x.getArg(); + // unary operators always associate correctly (I think) + _parenPush(x, arg, false); + accept(arg); + _parenPop(x, arg, false); + p.print(op.getSymbol()); + return false; + } + + @Override + public boolean visit(JsPrefixOperation x, JsContext ctx) { + JsUnaryOperator op = x.getOperator(); + p.print(op.getSymbol()); + JsExpression arg = x.getArg(); + if (_spaceCalc(op, arg)) { + _space(); + } + // unary operators always associate correctly (I think) + _parenPush(x, arg, false); + accept(arg); + _parenPop(x, arg, false); + return false; + } + + @Override + public boolean visit(JsProgram x, JsContext ctx) { + p.print(""); + return false; + } + + @Override + public boolean visit(JsProgramFragment x, JsContext ctx) { + p.print(""); + return false; + } + + @Override + public boolean visit(JsPropertyInitializer x, JsContext ctx) { + // Since there are separators, we actually print the property init + // in visit(JsObjectLiteral). + // + return false; + } + + @Override + public boolean visit(JsRegExp x, JsContext ctx) { + _slash(); + p.print(x.getPattern()); + _slash(); + String flags = x.getFlags(); + if (flags != null) { + p.print(flags); + } + return false; + } + + @Override + public boolean visit(JsReturn x, JsContext ctx) { + _return(); + JsExpression expr = x.getExpr(); + if (expr != null) { + _space(); + accept(expr); + } + return false; + } + + @Override + public boolean visit(JsStringLiteral x, JsContext ctx) { + printStringLiteral(x.getValue()); + return false; + } + + @Override + public boolean visit(JsSwitch x, JsContext ctx) { + _switch(); + _spaceOpt(); + _lparen(); + accept(x.getExpr()); + _rparen(); + _spaceOpt(); + _blockOpen(); + acceptList(x.getCases()); + _blockClose(); + return false; + } + + @Override + public boolean visit(JsThisRef x, JsContext ctx) { + _this(); + return false; + } + + @Override + public boolean visit(JsThrow x, JsContext ctx) { + _throw(); + _space(); + accept(x.getExpr()); + return false; + } + + @Override + public boolean visit(JsTry x, JsContext ctx) { + _try(); + _spaceOpt(); + accept(x.getTryBlock()); + + acceptList(x.getCatches()); + + JsBlock finallyBlock = x.getFinallyBlock(); + if (finallyBlock != null) { + _spaceOpt(); + _finally(); + _spaceOpt(); + accept(finallyBlock); + } + + return false; + } + + @Override + public boolean visit(JsVars.JsVar x, JsContext ctx) { + _nameOf(x); + JsExpression initExpr = x.getInitExpr(); + if (initExpr != null) { + _spaceOpt(); + _assignment(); + _spaceOpt(); + _parenPushIfCommaExpr(initExpr); + accept(initExpr); + _parenPopIfCommaExpr(initExpr); + } + return false; + } + + @Override + public boolean visit(JsVars x, JsContext ctx) { + _var(); + _space(); + boolean sep = false; + for (JsVars.JsVar var : x) { + sep = _sepCommaOptSpace(sep); + accept(var); + } + return false; + } + + @Override + public boolean visit(JsWhile x, JsContext ctx) { + _while(); + _spaceOpt(); + _lparen(); + accept(x.getCondition()); + _rparen(); + _nestedPush(x.getBody(), false); + accept(x.getBody()); + _nestedPop(x.getBody()); + return false; + } + + // CHECKSTYLE_NAMING_OFF + protected void _newline() { + p.newline(); + } + + protected void _newlineOpt() { + p.newlineOpt(); + } + + protected void printJsBlock(JsBlock x, boolean truncate, boolean finalNewline) { + boolean needBraces = !x.isGlobalBlock(); + + if (needBraces) { + // Open braces. + // + _blockOpen(); + } + + int count = 0; + for (Iterator iter = x.getStatements().iterator(); iter.hasNext(); ++count) { + boolean isGlobal = x.isGlobalBlock() || globalBlocks.contains(x); + + if (truncate && count > JSBLOCK_LINES_TO_PRINT) { + p.print("[...]"); + _newlineOpt(); + break; + } + JsStatement stmt = iter.next(); + needSemi = true; + boolean shouldRecordPositions = isGlobal && !(stmt instanceof JsBlock); + boolean stmtIsGlobalBlock = false; + if (isGlobal) { + if (stmt instanceof JsBlock) { + // A block inside a global block is still considered global + stmtIsGlobalBlock = true; + globalBlocks.add((JsBlock) stmt); + } + } + if (shouldRecordPositions) { + statementStarts.add(p.getPosition()); + } + accept(stmt); + if (stmtIsGlobalBlock) { + globalBlocks.remove(stmt); + } + if (needSemi) { + /* + * Special treatment of function decls: If they are the only item in a + * statement (i.e. not part of an assignment operation), just give them + * a newline instead of a semi. + */ + boolean functionStmt = + stmt instanceof JsExprStmt && ((JsExprStmt) stmt).getExpression() instanceof JsFunction; + /* + * Special treatment of the last statement in a block: only a few + * statements at the end of a block require semicolons. + */ + boolean lastStatement = !iter.hasNext() && needBraces && !JsRequiresSemiVisitor.exec(stmt); + if (functionStmt) { + if (lastStatement) { + _newlineOpt(); + } else { + _newline(); + } + } else { + if (lastStatement) { + _semiOpt(); + } else { + _semi(); + } + _newlineOpt(); + } + } + if (shouldRecordPositions) { + assert (statementStarts.size() == statementEnds.size() + 1); + statementEnds.add(p.getPosition()); + } + } + + if (needBraces) { + // _blockClose() modified + p.indentOut(); + p.print('}'); + if (finalNewline) { + _newlineOpt(); + } + } + needSemi = false; + } + + private void _assignment() { + p.print('='); + } + + private void _blockClose() { + p.indentOut(); + p.print('}'); + _newlineOpt(); + } + + private void _blockOpen() { + p.print('{'); + p.indentIn(); + _newlineOpt(); + } + + private void _break() { + p.print(CHARS_BREAK); + } + + private void _case() { + p.print(CHARS_CASE); + } + + private void _catch() { + p.print(CHARS_CATCH); + } + + private void _colon() { + p.print(':'); + } + + private void _continue() { + p.print(CHARS_CONTINUE); + } + + private void _debugger() { + p.print(CHARS_DEBUGGER); + } + + private void _default() { + p.print(CHARS_DEFAULT); + } + + private void _do() { + p.print(CHARS_DO); + } + + private void _dot() { + p.print('.'); + } + + private void _else() { + p.print(CHARS_ELSE); + } + + private void _false() { + p.print(CHARS_FALSE); + } + + private void _finally() { + p.print(CHARS_FINALLY); + } + + private void _for() { + p.print(CHARS_FOR); + } + + private void _function() { + p.print(CHARS_FUNCTION); + } + + private void _if() { + p.print(CHARS_IF); + } + + private void _in() { + p.print(CHARS_IN); + } + + private void _lbrace() { + p.print('{'); + } + + private void _lparen() { + p.print('('); + } + + private void _lsquare() { + p.print('['); + } + + private void _nameDef(JsName name) { + p.print(name.getShortIdent()); + } + + private void _nameOf(HasName hasName) { + _nameDef(hasName.getName()); + } + + private void _nameRef(JsNameRef nameRef) { + p.print(nameRef.getShortIdent()); + } + + private boolean _nestedPop(JsStatement statement) { + boolean pop = !(statement instanceof JsBlock); + if (pop) { + p.indentOut(); + } + return pop; + } + + private boolean _nestedPush(JsStatement statement, boolean needSpace) { + boolean push = !(statement instanceof JsBlock); + if (push) { + if (needSpace) { + _space(); + } + p.indentIn(); + _newlineOpt(); + } else { + _spaceOpt(); + } + return push; + } + + private void _new() { + p.print(CHARS_NEW); + } + + private void _null() { + p.print(CHARS_NULL); + } + + private boolean _parenCalc(JsExpression parent, JsExpression child, boolean wrongAssoc) { + int parentPrec = JsPrecedenceVisitor.exec(parent); + int childPrec = JsPrecedenceVisitor.exec(child); + return (parentPrec > childPrec || (parentPrec == childPrec && wrongAssoc)); + } + + private boolean _parenPop(JsExpression parent, JsExpression child, boolean wrongAssoc) { + boolean doPop = _parenCalc(parent, child, wrongAssoc); + if (doPop) { + _rparen(); + } + return doPop; + } + + private boolean _parenPopIfCommaExpr(JsExpression x) { + boolean doPop = + x instanceof JsBinaryOperation + && ((JsBinaryOperation) x).getOperator() == JsBinaryOperator.COMMA; + if (doPop) { + _rparen(); + } + return doPop; + } + + private boolean _parenPopOrSpace(JsExpression parent, JsExpression child, boolean wrongAssoc) { + boolean doPop = _parenCalc(parent, child, wrongAssoc); + if (doPop) { + _rparen(); + } else { + _space(); + } + return doPop; + } + + private boolean _parenPush(JsExpression parent, JsExpression child, boolean wrongAssoc) { + boolean doPush = _parenCalc(parent, child, wrongAssoc); + if (doPush) { + _lparen(); + } + return doPush; + } + + private boolean _parenPushIfCommaExpr(JsExpression x) { + boolean doPush = + x instanceof JsBinaryOperation + && ((JsBinaryOperation) x).getOperator() == JsBinaryOperator.COMMA; + if (doPush) { + _lparen(); + } + return doPush; + } + + private boolean _parenPushOrSpace(JsExpression parent, JsExpression child, boolean wrongAssoc) { + boolean doPush = _parenCalc(parent, child, wrongAssoc); + if (doPush) { + _lparen(); + } else { + _space(); + } + return doPush; + } + + private void _questionMark() { + p.print('?'); + } + + private void _rbrace() { + p.print('}'); + } + + private void _return() { + p.print(CHARS_RETURN); + } + + private void _rparen() { + p.print(')'); + } + + private void _rsquare() { + p.print(']'); + } + + private void _semi() { + p.print(';'); + } + + private void _semiOpt() { + p.printOpt(';'); + } + + private boolean _sepCommaOptSpace(boolean sep) { + if (sep) { + p.print(','); + _spaceOpt(); + } + return true; + } + + private void _slash() { + p.print('/'); + } + + private void _space() { + p.print(' '); + } + + /** + * Decide whether, if op is printed followed by arg, + * there needs to be a space between the operator and expression. + * + * @return true if a space needs to be printed + */ + private boolean _spaceCalc(JsOperator op, JsExpression arg) { + if (op.isKeyword()) { + return true; + } + if (arg instanceof JsBinaryOperation) { + JsBinaryOperation binary = (JsBinaryOperation) arg; + /* + * If the binary operation has a higher precedence than op, then it won't + * be parenthesized, so check the first argument of the binary operation. + */ + if (binary.getOperator().getPrecedence() > op.getPrecedence()) { + return _spaceCalc(op, binary.getArg1()); + } + return false; + } + if (arg instanceof JsPrefixOperation) { + JsOperator op2 = ((JsPrefixOperation) arg).getOperator(); + return (op == JsBinaryOperator.SUB || op == JsUnaryOperator.NEG) + && (op2 == JsUnaryOperator.DEC || op2 == JsUnaryOperator.NEG) + || (op == JsBinaryOperator.ADD && op2 == JsUnaryOperator.INC); + } + if (arg instanceof JsNumberLiteral) { + JsNumberLiteral literal = (JsNumberLiteral) arg; + return (op == JsBinaryOperator.SUB || op == JsUnaryOperator.NEG) && (literal.getValue() < 0); + } + return false; + } + + private void _spaceOpt() { + p.printOpt(' '); + } + + private void _switch() { + p.print(CHARS_SWITCH); + } + + private void _this() { + p.print(CHARS_THIS); + } + + private void _throw() { + p.print(CHARS_THROW); + } + + private void _true() { + p.print(CHARS_TRUE); + } + + private void _try() { + p.print(CHARS_TRY); + } + + private void _var() { + p.print(CHARS_VAR); + } + + private void _while() { + p.print(CHARS_WHILE); + } + + // CHECKSTYLE_NAMING_ON + + private void indent() { + p.indentIn(); + } + + private void outdent() { + p.indentOut(); + } + + private void printStringLiteral(String value) { + String resultString = javaScriptString(value); + p.print(resultString); + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/UncheckedJsParserException.java b/js/js.ast/src/com/google/dart/compiler/backend/js/UncheckedJsParserException.java new file mode 100644 index 00000000000..c2db8120537 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/UncheckedJsParserException.java @@ -0,0 +1,21 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js; + +/** + * An unchecked wrapper exception to interop with Rhino. + */ +class UncheckedJsParserException extends RuntimeException { + + private final JsParserException parserException; + + public UncheckedJsParserException(JsParserException parserException) { + this.parserException = parserException; + } + + public JsParserException getParserException() { + return parserException; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/CanBooleanEval.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/CanBooleanEval.java new file mode 100644 index 00000000000..8f4364d94f2 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/CanBooleanEval.java @@ -0,0 +1,15 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * An interface that describes the boolean evaluation of an expression. + */ +public interface CanBooleanEval { + + boolean isBooleanFalse(); + + boolean isBooleanTrue(); +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/HasArguments.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/HasArguments.java new file mode 100644 index 00000000000..5a47c567606 --- /dev/null +++ b/js/js.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 { + List getArguments(); +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/HasCondition.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/HasCondition.java new file mode 100644 index 00000000000..082f4e15eb3 --- /dev/null +++ b/js/js.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.ast/src/com/google/dart/compiler/backend/js/ast/HasName.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/HasName.java new file mode 100644 index 00000000000..8daed3769f4 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/HasName.java @@ -0,0 +1,12 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Implemented by JavaScript objects that have a name. + */ +public interface HasName { + JsName getName(); +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsArrayAccess.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsArrayAccess.java new file mode 100644 index 00000000000..46df5108086 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsArrayAccess.java @@ -0,0 +1,68 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Represents a javascript expression for array access. + */ +public final class JsArrayAccess extends JsExpression { + + private JsExpression arrayExpr; + private JsExpression indexExpr; + + public JsArrayAccess() { + super(); + } + + public JsArrayAccess(JsExpression arrayExpr, JsExpression indexExpr) { + this.arrayExpr = arrayExpr; + this.indexExpr = indexExpr; + } + + public JsExpression getArrayExpr() { + return arrayExpr; + } + + public JsExpression getIndexExpr() { + return indexExpr; + } + + @Override + public boolean hasSideEffects() { + return arrayExpr.hasSideEffects() || indexExpr.hasSideEffects(); + } + + @Override + public boolean isDefinitelyNotNull() { + return false; + } + + @Override + public boolean isDefinitelyNull() { + return false; + } + + public void setArrayExpr(JsExpression arrayExpr) { + this.arrayExpr = arrayExpr; + } + + public void setIndexExpr(JsExpression indexExpr) { + this.indexExpr = indexExpr; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + arrayExpr = v.accept(arrayExpr); + indexExpr = v.accept(indexExpr); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.ARRAY_ACCESS; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsArrayLiteral.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsArrayLiteral.java new file mode 100644 index 00000000000..8cb9f7e6d52 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsArrayLiteral.java @@ -0,0 +1,67 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +import java.util.ArrayList; +import java.util.List; + +/** + * Represents a JavaScript expression for array literals. + */ +public final class JsArrayLiteral extends JsLiteral { + + private final List exprs = new ArrayList(); + + public JsArrayLiteral() { + super(); + } + + public List getExpressions() { + return exprs; + } + + @Override + public boolean hasSideEffects() { + for (JsExpression expr : exprs) { + if (expr.hasSideEffects()) { + return true; + } + } + return false; + } + + @Override + public boolean isBooleanFalse() { + return false; + } + + @Override + public boolean isBooleanTrue() { + return true; + } + + @Override + public boolean isDefinitelyNotNull() { + return true; + } + + @Override + public boolean isDefinitelyNull() { + return false; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + v.acceptWithInsertRemove(exprs); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.ARRAY; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsBinaryOperation.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsBinaryOperation.java new file mode 100644 index 00000000000..b09d50c1d05 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsBinaryOperation.java @@ -0,0 +1,107 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Represents a JavaScript binary operation. + */ +public final class JsBinaryOperation extends JsExpression { + + private JsExpression arg1; + private JsExpression arg2; + private final JsBinaryOperator op; + + public JsBinaryOperation(JsBinaryOperator op) { + this(op, null, null); + } + + public JsBinaryOperation(JsBinaryOperator op, JsExpression arg1, JsExpression arg2) { + this.op = op; + this.arg1 = arg1; + this.arg2 = arg2; + } + + public JsExpression getArg1() { + return arg1; + } + + public JsExpression getArg2() { + return arg2; + } + + public JsBinaryOperator getOperator() { + return op; + } + + @Override + public boolean hasSideEffects() { + return op.isAssignment() || arg1.hasSideEffects() || arg2.hasSideEffects(); + } + + @Override + public boolean isDefinitelyNotNull() { + // Precarious coding, but none of these can have null results. + if (op.getPrecedence() > 5) { + return true; + } + if (op == JsBinaryOperator.OR) { + if (arg1 instanceof CanBooleanEval) { + if (((CanBooleanEval) arg1).isBooleanTrue()) { + assert arg1.isDefinitelyNotNull(); + return true; + } + } + } + // AND and OR can return nulls + if (op.isAssignment()) { + if (op == JsBinaryOperator.ASG) { + return arg2.isDefinitelyNotNull(); + } else { + // All other ASG's are math ops. + return true; + } + } + + if (op == JsBinaryOperator.COMMA) { + return arg2.isDefinitelyNotNull(); + } + + return false; + } + + @Override + public boolean isDefinitelyNull() { + if (op == JsBinaryOperator.AND) { + return arg1.isDefinitelyNull(); + } + return false; + } + + public void setArg1(JsExpression arg1) { + this.arg1 = arg1; + } + + public void setArg2(JsExpression arg2) { + this.arg2 = arg2; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + if (op.isAssignment()) { + arg1 = v.acceptLvalue(arg1); + } else { + arg1 = v.accept(arg1); + } + arg2 = v.accept(arg2); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.BINARY_OP; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsBinaryOperator.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsBinaryOperator.java new file mode 100644 index 00000000000..b54647f3a46 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsBinaryOperator.java @@ -0,0 +1,120 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Represents the operator in a JavaScript binary operation. + */ +public enum JsBinaryOperator implements JsOperator { + + /* + * Precedence indices from "JavaScript - The Definitive Guide" 4th Edition + * (page 57) + * + * + * Precedence 15 is for really important things that have their own AST + * classes. + * + * Precedence 14 is for unary operators. + */ + + MUL("*", 13, LEFT | INFIX), DIV("/", 13, LEFT | INFIX), MOD("%", 13, LEFT + | INFIX), + + ADD("+", 12, LEFT | INFIX), SUB("-", 12, LEFT | INFIX), + + SHL("<<", 11, LEFT | INFIX), SHR(">>", 11, LEFT | INFIX), SHRU(">>>", 11, + LEFT | INFIX), + + LT("<", 10, LEFT | INFIX), LTE("<=", 10, LEFT | INFIX), GT(">", 10, LEFT + | INFIX), GTE(">=", 10, LEFT | INFIX), INSTANCEOF("instanceof", 10, LEFT + | INFIX), INOP("in", 10, LEFT | INFIX), + + EQ("==", 9, LEFT | INFIX), NEQ("!=", 9, LEFT | INFIX), REF_EQ("===", 9, LEFT + | INFIX), REF_NEQ("!==", 9, LEFT | INFIX), + + BIT_AND("&", 8, LEFT | INFIX), + + BIT_XOR("^", 7, LEFT | INFIX), + + BIT_OR("|", 6, LEFT | INFIX), + + AND("&&", 5, LEFT | INFIX), + + OR("||", 4, LEFT | INFIX), + + // Precedence 3 is for the condition operator. + + // These assignment operators are right-associative. + ASG("=", 2, INFIX), ASG_ADD("+=", 2, INFIX), ASG_SUB("-=", 2, INFIX), ASG_MUL( + "*=", 2, INFIX), ASG_DIV("/=", 2, INFIX), ASG_MOD("%=", 2, INFIX), ASG_SHL( + "<<=", 2, INFIX), ASG_SHR(">>=", 2, INFIX), ASG_SHRU(">>>=", 2, INFIX), ASG_BIT_AND( + "&=", 2, INFIX), ASG_BIT_OR("|=", 2, INFIX), ASG_BIT_XOR("^=", 2, INFIX), + + COMMA(",", 1, LEFT | INFIX); + + private final int mask; + private final int precedence; + private final String symbol; + + private JsBinaryOperator(String symbol, int precedence, int mask) { + this.symbol = symbol; + this.precedence = precedence; + this.mask = mask; + } + + @Override + public int getPrecedence() { + return precedence; + } + + @Override + public String getSymbol() { + return symbol; + } + + public boolean isAssignment() { + /* + * Beware, flaky! Maybe I should have added Yet Another Field to + * BinaryOperator? + */ + return (getPrecedence() == ASG.getPrecedence()); + } + + @Override + public boolean isKeyword() { + return this == INSTANCEOF || this == INOP; + } + + @Override + public boolean isLeftAssociative() { + return (mask & LEFT) != 0; + } + + @Override + public boolean isPrecedenceLessThan(JsOperator other) { + return precedence < other.getPrecedence(); + } + + @Override + public boolean isValidInfix() { + return (mask & INFIX) != 0; + } + + @Override + public boolean isValidPostfix() { + return (mask & POSTFIX) != 0; + } + + @Override + public boolean isValidPrefix() { + return (mask & PREFIX) != 0; + } + + @Override + public String toString() { + return symbol; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsBlock.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsBlock.java new file mode 100644 index 00000000000..fca607c4eb4 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsBlock.java @@ -0,0 +1,60 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +import java.util.LinkedList; +import java.util.List; + +/** + * Represents a JavaScript block statement. + */ +public class JsBlock extends JsStatement { + + private final LinkedList stmts = new LinkedList(); + + public JsBlock() { + } + + public JsBlock(JsStatement stmt) { + stmts.add(stmt); + } + + public List getStatements() { + return stmts; + } + + public void addStatement(JsStatement statement) { + assert statement != null; + stmts.add(statement); + } + + public boolean isGlobalBlock() { + return false; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + v.acceptWithInsertRemove(stmts); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.BLOCK; + } + + /*Pavel Talanov*/ + public void addVarDeclaration(JsVars vars) { + stmts.offerFirst(vars); + } + + public void setStatements(List statements) { + assert this.stmts.isEmpty() : "Already contains statements."; + this.stmts.addAll(statements); + } + +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsBooleanLiteral.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsBooleanLiteral.java new file mode 100644 index 00000000000..12238ddbb02 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsBooleanLiteral.java @@ -0,0 +1,53 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Represents a JavaScript literal boolean expression. + */ +public final class JsBooleanLiteral extends JsValueLiteral { + + private final boolean value; + + // Should be interned by JsProgram + JsBooleanLiteral(boolean value) { + this.value = value; + } + + public boolean getValue() { + return value; + } + + @Override + public boolean isBooleanFalse() { + return value == false; + } + + @Override + public boolean isBooleanTrue() { + return value == true; + } + + @Override + public boolean isDefinitelyNotNull() { + return true; + } + + @Override + public boolean isDefinitelyNull() { + return false; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + v.visit(this, ctx); + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.BOOLEAN; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsBreak.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsBreak.java new file mode 100644 index 00000000000..363f294df78 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsBreak.java @@ -0,0 +1,46 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Represents the JavaScript break statement. + */ +public final class JsBreak extends JsStatement { + + private final JsNameRef label; + + public JsBreak() { + this(null); + } + + public JsBreak(JsNameRef label) { + super(); + this.label = label; + } + + public JsNameRef getLabel() { + return label; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + if (label != null) { + v.accept(label); + } + } + v.endVisit(this, ctx); + } + + @Override + public boolean unconditionalControlBreak() { + return true; + } + + @Override + public NodeKind getKind() { + return NodeKind.BREAK; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsCase.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsCase.java new file mode 100644 index 00000000000..43d2fc41230 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsCase.java @@ -0,0 +1,39 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Represents the JavaScript case statement. + */ +public final class JsCase extends JsSwitchMember { + + private JsExpression caseExpr; + + public JsCase() { + super(); + } + + public JsExpression getCaseExpr() { + return caseExpr; + } + + public void setCaseExpr(JsExpression caseExpr) { + this.caseExpr = caseExpr; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + caseExpr = v.accept(caseExpr); + v.acceptWithInsertRemove(stmts); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.CASE; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsCatch.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsCatch.java new file mode 100644 index 00000000000..f992545b944 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsCatch.java @@ -0,0 +1,66 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Represents a JavaScript catch clause. + */ +public class JsCatch extends JsNode implements HasCondition { + + protected final JsCatchScope scope; + private JsBlock body; + private JsExpression condition; + private JsParameter param; + + public JsCatch(JsScope parent, String ident) { + super(); + assert (parent != null); + scope = new JsCatchScope(parent, ident); + param = new JsParameter(scope.findExistingName(ident)); + } + + public JsBlock getBody() { + return body; + } + + @Override + public JsExpression getCondition() { + return condition; + } + + public JsParameter getParameter() { + return param; + } + + public JsScope getScope() { + return scope; + } + + public void setBody(JsBlock body) { + this.body = body; + } + + @Override + public void setCondition(JsExpression condition) { + this.condition = condition; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + param = v.accept(param); + if (condition != null) { + condition = v.accept(condition); + } + body = v.accept(body); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.CATCH; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsCatchScope.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsCatchScope.java new file mode 100644 index 00000000000..5e5a2f2f577 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsCatchScope.java @@ -0,0 +1,75 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * A special scope used only for catch blocks. It only holds a single symbol: + * the catch argument's name. + */ +public class JsCatchScope extends JsScope { + + private final JsName name; + + public JsCatchScope(JsScope parent, String ident) { + super(parent, "Catch scope"); + this.name = new JsName(this, ident, ident, ident); + } + + @Override + public JsName declareName(String ident) { + // Declare into parent scope! + return getParent().declareName(ident); + } + + @Override + public JsName declareName(String ident, String shortIdent) { + // Declare into parent scope! + return getParent().declareName(ident, shortIdent); + } + + @Override + public Iterator getAllNames() { + return new Iterator() { + private boolean didIterate = false; + + @Override + public boolean hasNext() { + return !didIterate; + } + + @Override + public JsName next() { + if (didIterate) { + throw new NoSuchElementException(); + } + didIterate = true; + return name; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + }; + } + + @Override + protected JsName doCreateName(String ident, String shortIdent, String originalName) { + throw new UnsupportedOperationException("Cannot create a name in a catch scope"); + } + + @Override + public JsName findExistingNameNoRecurse(String ident) { + if (name.getIdent().equals(ident)) { + return name; + } else { + return null; + } + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsConditional.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsConditional.java new file mode 100644 index 00000000000..b1171bac2c0 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsConditional.java @@ -0,0 +1,78 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Represents a JavaScript conditional expression. + */ +public final class JsConditional extends JsExpression { + + private JsExpression elseExpr; + private JsExpression testExpr; + private JsExpression thenExpr; + + public JsConditional() { + } + + public JsConditional(JsExpression testExpr, JsExpression thenExpr, JsExpression elseExpr) { + this.testExpr = testExpr; + this.thenExpr = thenExpr; + this.elseExpr = elseExpr; + } + + public JsExpression getElseExpression() { + return elseExpr; + } + + public JsExpression getTestExpression() { + return testExpr; + } + + public JsExpression getThenExpression() { + return thenExpr; + } + + @Override + public boolean hasSideEffects() { + return testExpr.hasSideEffects() || thenExpr.hasSideEffects() || elseExpr.hasSideEffects(); + } + + @Override + public boolean isDefinitelyNotNull() { + return thenExpr.isDefinitelyNotNull() && elseExpr.isDefinitelyNotNull(); + } + + @Override + public boolean isDefinitelyNull() { + return thenExpr.isDefinitelyNull() && elseExpr.isDefinitelyNull(); + } + + public void setElseExpression(JsExpression elseExpr) { + this.elseExpr = elseExpr; + } + + public void setTestExpression(JsExpression testExpr) { + this.testExpr = testExpr; + } + + public void setThenExpression(JsExpression thenExpr) { + this.thenExpr = thenExpr; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + testExpr = v.accept(testExpr); + thenExpr = v.accept(thenExpr); + elseExpr = v.accept(elseExpr); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.CONDITIONAL; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsContext.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsContext.java new file mode 100644 index 00000000000..ddb170052d3 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsContext.java @@ -0,0 +1,27 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * The context in which a JsNode visitation occurs. This represents the set of + * possible operations a JsVisitor subclass can perform on the currently visited + * node. + */ +public interface JsContext { + + boolean canInsert(); + + boolean canRemove(); + + void insertAfter(JsVisitable node); + + void insertBefore(JsVisitable node); + + boolean isLvalue(); + + void removeMe(); + + void replaceMe(JsVisitable node); +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsContinue.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsContinue.java new file mode 100644 index 00000000000..d761815fe76 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsContinue.java @@ -0,0 +1,46 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Represents the JavaScript continue statement. + */ +public final class JsContinue extends JsStatement { + + private final JsNameRef label; + + public JsContinue() { + this(null); + } + + public JsContinue(JsNameRef label) { + super(); + this.label = label; + } + + public JsNameRef getLabel() { + return label; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + if (label != null) { + v.accept(label); + } + } + v.endVisit(this, ctx); + } + + @Override + public boolean unconditionalControlBreak() { + return true; + } + + @Override + public NodeKind getKind() { + return NodeKind.CONTINUE; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsDebugger.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsDebugger.java new file mode 100644 index 00000000000..34cfae43f48 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsDebugger.java @@ -0,0 +1,25 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Represents a JavaScript debugger statement. + */ +public class JsDebugger extends JsStatement { + + public JsDebugger() { + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + v.visit(this, ctx); + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.DEBUGGER; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsDefault.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsDefault.java new file mode 100644 index 00000000000..a76987a8e5d --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsDefault.java @@ -0,0 +1,28 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Represents the default option in a JavaScript swtich statement. + */ +public final class JsDefault extends JsSwitchMember { + + public JsDefault() { + super(); + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + v.acceptWithInsertRemove(stmts); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.DEFAULT; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsDoWhile.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsDoWhile.java new file mode 100644 index 00000000000..1dd4a47e8d4 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsDoWhile.java @@ -0,0 +1,54 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Represents a JavaScript do..while statement. + */ +public class JsDoWhile extends JsStatement { + + private JsStatement body; + private JsExpression condition; + + public JsDoWhile() { + super(); + } + + public JsDoWhile(JsExpression condition, JsStatement body) { + super(); + this.condition = condition; + this.body = body; + } + + public JsStatement getBody() { + return body; + } + + public JsExpression getCondition() { + return condition; + } + + public void setBody(JsStatement body) { + this.body = body; + } + + public void setCondition(JsExpression condition) { + this.condition = condition; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + body = v.accept(body); + condition = v.accept(condition); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.DO; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsEmpty.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsEmpty.java new file mode 100644 index 00000000000..c2ac99e3ac0 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsEmpty.java @@ -0,0 +1,26 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Represents an empty statement in JavaScript. + */ +public class JsEmpty extends JsStatement { + + // Interned by JsProgram + JsEmpty() { + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + v.visit(this, ctx); + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.EMPTY; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsExprStmt.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsExprStmt.java new file mode 100644 index 00000000000..5b9fc1218c3 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsExprStmt.java @@ -0,0 +1,36 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Represents a JavaScript expression statement. + */ +public final class JsExprStmt extends JsStatement { + + private JsExpression expr; + + public JsExprStmt(JsExpression expr) { + super(); + this.expr = expr; + this.setSourceInfo(expr); + } + + public JsExpression getExpression() { + return expr; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + expr = v.accept(expr); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.EXPR_STMT; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsExpression.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsExpression.java new file mode 100644 index 00000000000..43545ec98dc --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsExpression.java @@ -0,0 +1,52 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +import com.google.dart.compiler.common.SourceInfo; + +/** + * An abstract base class for all JavaScript expressions. + */ +public abstract class JsExpression extends JsNode { + + protected JsExpression() { + } + + /** + * Determines whether the expression can cause side effects. + */ + public abstract boolean hasSideEffects(); + + /** + * True if the target expression is definitely not null. + */ + public abstract boolean isDefinitelyNotNull(); + + /** + * True if the target expression is definitely null. + */ + public abstract boolean isDefinitelyNull(); + + /** + * Determines whether or not this expression is a leaf, such as a + * {@link JsNameRef}, {@link JsBooleanLiteral}, and so on. Leaf expressions + * never need to be parenthesized. + */ + public boolean isLeaf() { + // Conservatively say that it isn't a leaf. + // Individual subclasses can speak for themselves if they are a leaf. + return false; + } + + public JsExprStmt makeStmt() { + return new JsExprStmt(this); + } + + @Override + public JsExpression setSourceRef(SourceInfo info) { + super.setSourceRef(info); + return this; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsFor.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsFor.java new file mode 100644 index 00000000000..b980d4be1a2 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsFor.java @@ -0,0 +1,105 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +import com.google.dart.compiler.common.SourceInfo; + +/** + * A for statement. If specified at all, the initializer part is + * either a declaration of one or more variables, in which case + * {@link #getInitVars()} is used, or an expression, in which case + * {@link #getInitExpr()} is used. In the latter case, the comma operator is + * often used to create a compound expression. + * + *

+ * Note that any of the parts of the for loop header can be + * null, although the body will never be null. + */ +public class JsFor extends JsStatement { + + private JsStatement body; + private JsExpression condition; + private JsExpression incrExpr; + private JsExpression initExpr; + private JsVars initVars; + + public JsFor() { + super(); + } + + public JsStatement getBody() { + return body; + } + + public JsExpression getCondition() { + return condition; + } + + public JsExpression getIncrExpr() { + return incrExpr; + } + + public JsExpression getInitExpr() { + return initExpr; + } + + public JsVars getInitVars() { + return initVars; + } + + public void setBody(JsStatement body) { + this.body = body; + } + + public void setCondition(JsExpression condition) { + this.condition = condition; + } + + public void setIncrExpr(JsExpression incrExpr) { + this.incrExpr = incrExpr; + } + + public void setInitExpr(JsExpression initExpr) { + this.initExpr = initExpr; + } + + public void setInitVars(JsVars initVars) { + this.initVars = initVars; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + assert (!(initExpr != null && initVars != null)); + + if (initExpr != null) { + initExpr = v.accept(initExpr); + } else if (initVars != null) { + initVars = v.accept(initVars); + } + + if (condition != null) { + condition = v.accept(condition); + } + + if (incrExpr != null) { + incrExpr = v.accept(incrExpr); + } + body = v.accept(body); + } + v.endVisit(this, ctx); + } + + @Override + public JsFor setSourceRef(SourceInfo info) { + super.setSourceRef(info); + return this; + } + + @Override + public NodeKind getKind() { + return NodeKind.FOR; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsForIn.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsForIn.java new file mode 100644 index 00000000000..646e349885c --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsForIn.java @@ -0,0 +1,71 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Represents a JavaScript for..in statement. + */ +public class JsForIn extends JsStatement { + + private JsStatement body; + private JsExpression iterExpr; + private JsExpression objExpr; + + // Optional: the name of a new iterator variable to introduce + private final JsName iterVarName; + + public JsForIn() { + this(null); + } + + public JsForIn(JsName iterVarName) { + this.iterVarName = iterVarName; + } + + public JsStatement getBody() { + return body; + } + + public JsExpression getIterExpr() { + return iterExpr; + } + + public JsName getIterVarName() { + return iterVarName; + } + + public JsExpression getObjExpr() { + return objExpr; + } + + public void setBody(JsStatement body) { + this.body = body; + } + + public void setIterExpr(JsExpression iterExpr) { + this.iterExpr = iterExpr; + } + + public void setObjExpr(JsExpression objExpr) { + this.objExpr = objExpr; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + if (iterExpr != null) { + iterExpr = v.acceptLvalue(iterExpr); + } + objExpr = v.accept(objExpr); + body = v.accept(body); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.FOR_IN; + } +} \ No newline at end of file diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsFunction.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsFunction.java new file mode 100644 index 00000000000..f2c7d258791 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsFunction.java @@ -0,0 +1,238 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +import com.google.dart.compiler.common.SourceInfo; + +import java.util.ArrayList; +import java.util.List; + +/** + * Represents a JavaScript function expression. + */ +public final class JsFunction extends JsLiteral implements HasName { + + private static void trace(String title, String code) { + System.out.println("---------------------------"); + System.out.println(title + ":"); + System.out.println("---------------------------"); + System.out.println(code); + } + + protected JsBlock body; + protected final List params = new ArrayList(); + protected final JsScope scope; + private boolean artificiallyRescued; + private boolean executeOnce; + private boolean fromDart; + private JsFunction impliedExecute; + private JsName name; + private boolean trace = false; + private boolean traceFirst = true; + private boolean hoisted = false; + private boolean constructor = false; + + /** + * Creates an anonymous function. + */ + public JsFunction(JsScope parent) { + this(parent, null, false); + } + + /** + * Creates a function that is not derived from Dart source. + */ + public JsFunction(JsScope parent, JsName name) { + this(parent, name, false); + } + + /** + * Creates a named function, possibly derived from Dart source. + */ + public JsFunction(JsScope parent, JsName name, boolean fromDart) { + assert (parent != null); + this.fromDart = fromDart; + setName(name); + String scopeName = (name == null) ? "" : name.getIdent(); + scopeName = "function " + scopeName; + this.scope = new JsScope(parent, scopeName); + } + + public JsBlock getBody() { + return body; + } + + /** + * If true, this indicates that only the first invocation of the function will + * have any effects. Subsequent invocations may be considered to be no-op + * calls whose return value is ignored. + */ + public boolean getExecuteOnce() { + return executeOnce; + } + + public JsFunction getImpliedExecute() { + return impliedExecute; + } + + @Override + public JsName getName() { + return name; + } + + public void setParameters(List params) { + this.params.clear(); + this.params.addAll(params); + } + + public List getParameters() { + return params; + } + + public JsScope getScope() { + return scope; + } + + @Override + public boolean hasSideEffects() { + // If there's a name, the name is assigned to. + return name != null; + } + + public boolean isArtificiallyRescued() { + return artificiallyRescued; + } + + @Override + public boolean isBooleanFalse() { + return false; + } + + @Override + public boolean isBooleanTrue() { + return true; + } + + @Override + public boolean isDefinitelyNotNull() { + return true; + } + + @Override + public boolean isDefinitelyNull() { + return false; + } + + public boolean isFromDart() { + return fromDart; + } + + public void setArtificiallyRescued(boolean rescued) { + this.artificiallyRescued = rescued; + } + + public void setBody(JsBlock body) { + this.body = body; + } + + public void setExecuteOnce(boolean executeOnce) { + this.executeOnce = executeOnce; + } + + public void setFromDart(boolean fromDart) { + this.fromDart = fromDart; + } + + public void setImpliedExecute(JsFunction impliedExecute) { + this.impliedExecute = impliedExecute; + } + + public void setName(JsName name) { + this.name = name; + if (name != null) { + if (isFromDart()) { + name.setStaticRef(this); + } + } + } + + public void setTrace() { + this.trace = true; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + String before = null; + if (trace && v instanceof JsModVisitor) { + before = this.toSource(); + if (traceFirst) { + traceFirst = false; + trace("SCRIPT INITIAL", before); + } + } + if (v.visit(this, ctx)) { + v.acceptWithInsertRemove(params); + body = v.accept(body); + } + v.endVisit(this, ctx); + if (trace && v instanceof JsModVisitor) { + String after = this.toSource(); + if (!after.equals(before)) { + String title = v.getClass().getSimpleName(); + trace(title, after); + } + } + } + + public void setHoisted() { + hoisted = true; + } + + /** + * Whether the function has been hoisted + */ + public boolean isHoisted() { + return hoisted; + } + + /** + * Rebase the function to a new scope. + * + * @param newScopeParent The scope to add the function to. + */ + public void rebaseScope(JsScope newScopeParent) { + this.scope.rebase(newScopeParent); + } + + @Override + public JsFunction setSourceRef(SourceInfo info) { + super.setSourceRef(info); + return this; + } + + public boolean isConstructor() { + return this.constructor; + } + + public boolean setIsConstructor(boolean constructor) { + return this.constructor = constructor; + } + + @Override + public NodeKind getKind() { + return NodeKind.FUNCTION; + } + + // Pavel Talanov + // dummy parameter to distinguish from other constructors + private JsFunction(Void dummy, JsScope functionScope) { + this.fromDart = false; + this.scope = functionScope; + } + + public static JsFunction getAnonymousFunctionWithScope(JsScope scope) { + return new JsFunction(null, scope); + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsGlobalBlock.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsGlobalBlock.java new file mode 100644 index 00000000000..9a14004e14c --- /dev/null +++ b/js/js.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; + +/** + * Represnts 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.ast/src/com/google/dart/compiler/backend/js/ast/JsIf.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsIf.java new file mode 100644 index 00000000000..478538235ab --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsIf.java @@ -0,0 +1,65 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Represents a JavaScript if statement. + */ +public final class JsIf extends JsStatement { + + private JsExpression ifExpr; + private JsStatement thenStmt; + private JsStatement elseStmt; + + public JsIf() { + } + + public JsIf(JsExpression ifExpr, JsStatement thenStmt, JsStatement elseStmt) { + this.ifExpr = ifExpr; + this.thenStmt = thenStmt; + this.elseStmt = elseStmt; + } + + public JsStatement getElseStmt() { + return elseStmt; + } + + public JsExpression getIfExpr() { + return ifExpr; + } + + public JsStatement getThenStmt() { + return thenStmt; + } + + public void setElseStmt(JsStatement elseStmt) { + this.elseStmt = elseStmt; + } + + public void setIfExpr(JsExpression ifExpr) { + this.ifExpr = ifExpr; + } + + public void setThenStmt(JsStatement thenStmt) { + this.thenStmt = thenStmt; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + ifExpr = v.accept(ifExpr); + thenStmt = v.accept(thenStmt); + if (elseStmt != null) { + elseStmt = v.accept(elseStmt); + } + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.IF; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsInvocation.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsInvocation.java new file mode 100644 index 00000000000..777c33730d0 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsInvocation.java @@ -0,0 +1,73 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Represents a JavaScript invocation. + */ +public final class JsInvocation extends JsExpression implements HasArguments { + + private final List args = new ArrayList(); + private JsExpression qualifier; + + public JsInvocation() { + } + + @Override + public List getArguments() { + return args; + } + + public JsExpression getQualifier() { + return qualifier; + } + + @Override + public boolean hasSideEffects() { + return true; + } + + @Override + public boolean isDefinitelyNotNull() { + return false; + } + + @Override + public boolean isDefinitelyNull() { + return false; + } + + public void setQualifier(JsExpression qualifier) { + this.qualifier = qualifier; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + qualifier = v.accept(qualifier); + v.acceptList(args); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.INVOKE; + } + + // Pavel Talanov + public void setArguments(List arguments) { + assert this.args.isEmpty() : "Arguments already set."; + this.args.addAll(arguments); + } + + public void setArguments(JsExpression... arguments) { + setArguments(Arrays.asList(arguments)); + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsLabel.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsLabel.java new file mode 100644 index 00000000000..7d026c3a6de --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsLabel.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; + +/** + * Represents a JavaScript label statement. + */ +public class JsLabel extends JsStatement implements HasName { + + private final JsName label; + + private JsStatement stmt; + + public JsLabel(JsName label) { + this.label = label; + } + + @Override + public JsName getName() { + return label; + } + + public JsStatement getStmt() { + return stmt; + } + + public void setStmt(JsStatement stmt) { + this.stmt = stmt; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + stmt = v.accept(stmt); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.LABEL; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsLiteral.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsLiteral.java new file mode 100644 index 00000000000..c87ae0c8af1 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsLiteral.java @@ -0,0 +1,14 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * A JavaScript string literal expression. + */ +public abstract class JsLiteral extends JsExpression implements CanBooleanEval { + + protected JsLiteral() { + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsModVisitor.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsModVisitor.java new file mode 100644 index 00000000000..ef6e75ce994 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsModVisitor.java @@ -0,0 +1,189 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +import com.google.dart.compiler.util.Hack; + +import java.util.List; + +/** + * A visitor for iterating through and modifying an AST. + */ +public class JsModVisitor extends JsVisitor { + + private class ListContext implements JsContext { + + private List collection; + private int index; + private boolean removed; + private boolean replaced; + + @Override + public boolean canInsert() { + return true; + } + + @Override + public boolean canRemove() { + return true; + } + + @Override + public void insertAfter(JsVisitable node) { + checkRemoved(); + collection.add(index + 1, Hack.cast(node)); + didChange = true; + } + + @Override + public void insertBefore(JsVisitable node) { + checkRemoved(); + collection.add(index++, Hack.cast(node)); + didChange = true; + } + + @Override + public boolean isLvalue() { + return false; + } + + @Override + public void removeMe() { + checkState(); + collection.remove(index--); + didChange = removed = true; + } + + @Override + public void replaceMe(JsVisitable node) { + checkState(); + checkReplacement(collection.get(index), node); + collection.set(index, Hack.cast(node)); + didChange = replaced = true; + } + + protected void traverse(List collection) { + this.collection = collection; + for (index = 0; index < collection.size(); ++index) { + removed = replaced = false; + doTraverse(collection.get(index), this); + } + } + + private void checkRemoved() { + if (removed) { + throw new RuntimeException("Node was already removed"); + } + } + + private void checkState() { + checkRemoved(); + if (replaced) { + throw new RuntimeException("Node was already replaced"); + } + } + } + + private class LvalueContext extends NodeContext { + @Override + public boolean isLvalue() { + return true; + } + } + + private class NodeContext implements JsContext { + private T node; + private boolean replaced; + + @Override + public boolean canInsert() { + return false; + } + + @Override + public boolean canRemove() { + return false; + } + + @Override + public void insertAfter(JsVisitable node) { + throw new UnsupportedOperationException(); + } + + @Override + public void insertBefore(JsVisitable node) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isLvalue() { + return false; + } + + @Override + public void removeMe() { + throw new UnsupportedOperationException(); + } + + @Override + public void replaceMe(JsVisitable node) { + if (replaced) { + throw new RuntimeException("Node was already replaced"); + } + checkReplacement(this.node, node); + this.node = Hack.cast(node); + didChange = replaced = true; + } + + protected T traverse(T node) { + this.node = node; + replaced = false; + doTraverse(node, this); + return this.node; + } + } + + protected static void checkReplacement(T origNode, T newNode) { + if (newNode == null) { + throw new RuntimeException("Cannot replace with null"); + } + if (newNode == origNode) { + throw new RuntimeException("The replacement is the same as the original"); + } + } + + protected boolean didChange = false; + + @Override + public boolean didChange() { + return didChange; + } + + @Override + protected T doAccept(T node) { + return new NodeContext().traverse(node); + } + + @Override + protected void doAcceptList(List collection) { + NodeContext ctx = new NodeContext(); + for (int i = 0, c = collection.size(); i < c; ++i) { + ctx.traverse(collection.get(i)); + if (ctx.replaced) { + collection.set(i, ctx.node); + } + } + } + + @Override + protected JsExpression doAcceptLvalue(JsExpression expr) { + return new LvalueContext().traverse(expr); + } + + @Override + protected void doAcceptWithInsertRemove(List collection) { + new ListContext().traverse(collection); + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsName.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsName.java new file mode 100644 index 00000000000..ccc1322338a --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsName.java @@ -0,0 +1,102 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +import java.io.Serializable; + +/** + * An abstract base class for named JavaScript objects. + */ +public class JsName implements Serializable { + private final JsScope enclosing; + private final String ident; + private boolean isObfuscatable; + private String shortIdent; + private String originalName; + + /** + * A back-reference to the JsNode that the JsName refers to. + */ + private JsNode staticRef; + + /** + * @param ident the unmangled ident to use for this name + */ +JsName(JsScope enclosing, String ident, String shortIdent, String originalName) { + this.enclosing = enclosing; + this.ident = ident; + this.shortIdent = shortIdent; + if (originalName != null) { + this.originalName = originalName; + } + this.isObfuscatable = true; + } + + public JsScope getEnclosing() { + return enclosing; + } + + public String getIdent() { + return ident; + } + + public String getShortIdent() { + return shortIdent; + } + + public String getOriginalName() { + return originalName; + } + + public JsNode getStaticRef() { + return staticRef; + } + + public boolean isObfuscatable() { + return isObfuscatable; + } + + public JsNameRef makeRef() { + return new JsNameRef(this); + } + + public void setObfuscatable(boolean isObfuscatable) { + this.isObfuscatable = isObfuscatable; + } + + public void setShortIdent(String shortIdent) { + this.shortIdent = shortIdent; + } + + /** + * Should never be called except on immutable stuff. + */ + public void setStaticRef(JsNode node) { + this.staticRef = node; + } + + @Override + public String toString() { + return ident; + } + + @Override + public 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.ast/src/com/google/dart/compiler/backend/js/ast/JsNameRef.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsNameRef.java new file mode 100644 index 00000000000..dc26c8fa61a --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsNameRef.java @@ -0,0 +1,113 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Represents a JavaScript expression that references a name. + */ +public final class JsNameRef extends JsExpression implements CanBooleanEval, HasName { + + private String ident; + private JsName name; + private JsExpression qualifier; + + public JsNameRef(JsName name) { + this.name = name; + } + + public JsNameRef(String ident) { + this.ident = ident; + } + + public String getIdent() { + return (name == null) ? ident : name.getIdent(); + } + + @Override + public JsName getName() { + return name; + } + + public JsExpression getQualifier() { + return qualifier; + } + + public String getShortIdent() { + return (name == null) ? ident : name.getShortIdent(); + } + + @Override + public boolean hasSideEffects() { + if (qualifier == null) { + return false; + } + if (!qualifier.isDefinitelyNotNull()) { + // Could trigger NPE. + return true; + } + return qualifier.hasSideEffects(); + } + + @Override + public boolean isBooleanFalse() { + return isDefinitelyNull(); + } + + @Override + public boolean isBooleanTrue() { + return false; + } + + @Override + public boolean isDefinitelyNotNull() { + // TODO: look for single-assignment of stuff from Java? + return false; + } + + @Override + public boolean isDefinitelyNull() { + if (name != null) { + return (name.getEnclosing().getProgram().getUndefinedLiteral().getName() == name); + } + return false; + } + + @Override + public boolean isLeaf() { + if (qualifier == null) { + return true; + } else { + return false; + } + } + + public boolean isResolved() { + return name != null; + } + + public void resolve(JsName name) { + this.name = name; + this.ident = null; + } + + public void setQualifier(JsExpression qualifier) { + this.qualifier = qualifier; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + if (qualifier != null) { + qualifier = v.accept(qualifier); + } + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.NAME_REF; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsNew.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsNew.java new file mode 100644 index 00000000000..68fc366684d --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsNew.java @@ -0,0 +1,66 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +import java.util.ArrayList; +import java.util.List; + +/** + * Represents the JavaScript new expression. + */ +public final class JsNew extends JsExpression implements HasArguments { + + private final List args = new ArrayList(); + private JsExpression ctorExpr; + + public JsNew(JsExpression ctorExpr) { + this.ctorExpr = ctorExpr; + } + + @Override + public List getArguments() { + return args; + } + + public void setArguments(List arguments) { + args.clear(); + args.addAll(arguments); + } + + public JsExpression getConstructorExpression() { + return ctorExpr; + } + + @Override + public boolean hasSideEffects() { + return true; + } + + @Override + public boolean isDefinitelyNotNull() { + // Sadly, in JS it can be! + // TODO: analysis could probably determine most instances cannot be null. + return false; + } + + @Override + public boolean isDefinitelyNull() { + return false; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + ctorExpr = v.accept(ctorExpr); + v.acceptList(args); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.NEW; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsNode.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsNode.java new file mode 100644 index 00000000000..0d4b7aaa714 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsNode.java @@ -0,0 +1,50 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.google.dart.compiler.backend.js.ast; + +import com.google.dart.compiler.backend.js.JsSourceGenerationVisitor; +import com.google.dart.compiler.backend.js.JsToStringGenerationVisitor; +import com.google.dart.compiler.common.AbstractNode; +import com.google.dart.compiler.common.SourceInfo; +import com.google.dart.compiler.util.DefaultTextOutput; + +/** + * Base class for all JS AST elements. + */ +public abstract class JsNode extends AbstractNode implements JsVisitable { + + protected JsNode() { + } + + // Causes source generation to delegate to the one visitor + public final String toSource() { + DefaultTextOutput out = new DefaultTextOutput(false); + JsSourceGenerationVisitor v = new JsSourceGenerationVisitor(out); + v.accept(this); + return out.toString(); + } + + // Causes source generation to delegate to the one visitor + @Override + public final String toString() { + DefaultTextOutput out = new DefaultTextOutput(false); + JsToStringGenerationVisitor v = new JsToStringGenerationVisitor(out); + v.accept(this); + return out.toString(); + } + + @Override + public SourceInfo getSourceInfo() { + return this; + } + + public JsNode setSourceRef(SourceInfo info) { + if (info != null) { + this.setSourceInfo(info); + } + return this; + } + + public abstract NodeKind getKind(); +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsNullLiteral.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsNullLiteral.java new file mode 100644 index 00000000000..506b2e3f330 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsNullLiteral.java @@ -0,0 +1,46 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * A JavaScript null literal. + */ +public final class JsNullLiteral extends JsValueLiteral { + + // Should only be instantiated in JsProgram + JsNullLiteral() { + } + + @Override + public boolean isBooleanFalse() { + return true; + } + + @Override + public boolean isBooleanTrue() { + return false; + } + + @Override + public boolean isDefinitelyNotNull() { + return false; + } + + @Override + public boolean isDefinitelyNull() { + return true; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + v.visit(this, ctx); + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.NULL; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsNumberLiteral.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsNumberLiteral.java new file mode 100644 index 00000000000..726c6b0ed27 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsNumberLiteral.java @@ -0,0 +1,53 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Represents a JavaScript literal decimal expression. + */ +public final class JsNumberLiteral extends JsValueLiteral { + + private final double value; + + // Should be interned by JsProgram + JsNumberLiteral(double value) { + this.value = value; + } + + public double getValue() { + return value; + } + + @Override + public boolean isBooleanFalse() { + return value == 0.0; + } + + @Override + public boolean isBooleanTrue() { + return value != 0.0; + } + + @Override + public boolean isDefinitelyNotNull() { + return true; + } + + @Override + public boolean isDefinitelyNull() { + return false; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + v.visit(this, ctx); + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.NUMBER; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsObjectLiteral.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsObjectLiteral.java new file mode 100644 index 00000000000..749ed473c21 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsObjectLiteral.java @@ -0,0 +1,72 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +import java.util.ArrayList; +import java.util.List; + +/** + * A JavaScript object literal. + */ +public final class JsObjectLiteral extends JsLiteral { + + private final List props; + + public JsObjectLiteral() { + props = new ArrayList(); + } + + public JsObjectLiteral(List props) { + assert props != null; + this.props = props; + } + + public List getPropertyInitializers() { + return props; + } + + @Override + public boolean hasSideEffects() { + for (JsPropertyInitializer prop : props) { + if (prop.hasSideEffects()) { + return true; + } + } + return false; + } + + @Override + public boolean isBooleanFalse() { + return false; + } + + @Override + public boolean isBooleanTrue() { + return true; + } + + @Override + public boolean isDefinitelyNotNull() { + return true; + } + + @Override + public boolean isDefinitelyNull() { + return false; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + v.acceptWithInsertRemove(props); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.OBJECT; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsOperator.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsOperator.java new file mode 100644 index 00000000000..e4440cbe9ab --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsOperator.java @@ -0,0 +1,32 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * A JavaScript operator. + */ +public interface JsOperator { + + int INFIX = 0x02; + int LEFT = 0x01; + int POSTFIX = 0x04; + int PREFIX = 0x08; + + int getPrecedence(); + + String getSymbol(); + + boolean isKeyword(); + + boolean isLeftAssociative(); + + boolean isPrecedenceLessThan(JsOperator other); + + boolean isValidInfix(); + + boolean isValidPostfix(); + + boolean isValidPrefix(); +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsParameter.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsParameter.java new file mode 100644 index 00000000000..17581ce4084 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsParameter.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; + +/** + * A JavaScript parameter. + */ +public final class JsParameter extends JsNode implements HasName { + + private final JsName name; + + public JsParameter(JsName name) { + this.name = name; + name.setStaticRef(this); + } + + @Override + public JsName getName() { + return name; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + v.visit(this, ctx); + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.PARAMETER; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsPostfixOperation.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsPostfixOperation.java new file mode 100644 index 00000000000..a33536f6c0d --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsPostfixOperation.java @@ -0,0 +1,42 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * A JavaScript postfix operation. + */ +public final class JsPostfixOperation extends JsUnaryOperation { + + public JsPostfixOperation(JsUnaryOperator op) { + this(op, null); + } + + public JsPostfixOperation(JsUnaryOperator op, JsExpression arg) { + super(op, arg); + } + + @Override + public boolean isDefinitelyNotNull() { + return true; + } + + @Override + public boolean isDefinitelyNull() { + return false; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + super.traverse(v, ctx); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.POSTFIX_OP; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsPrefixOperation.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsPrefixOperation.java new file mode 100644 index 00000000000..8ce12c7bacd --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsPrefixOperation.java @@ -0,0 +1,69 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * A JavaScript prefix operation. + */ +public final class JsPrefixOperation extends JsUnaryOperation implements CanBooleanEval { + + public JsPrefixOperation(JsUnaryOperator op) { + this(op, null); + } + + public JsPrefixOperation(JsUnaryOperator op, JsExpression arg) { + super(op, arg); + } + + @Override + public boolean isBooleanFalse() { + if (getOperator() == JsUnaryOperator.VOID) { + return true; + } + if (getOperator() == JsUnaryOperator.NOT && getArg() instanceof CanBooleanEval) { + CanBooleanEval eval = (CanBooleanEval) getArg(); + return eval.isBooleanTrue(); + } + return false; + } + + @Override + public boolean isBooleanTrue() { + if (getOperator() == JsUnaryOperator.NOT && getArg() instanceof CanBooleanEval) { + CanBooleanEval eval = (CanBooleanEval) getArg(); + return eval.isBooleanFalse(); + } + if (getOperator() == JsUnaryOperator.TYPEOF) { + return true; + } + return false; + } + + @Override + public boolean isDefinitelyNotNull() { + if (getOperator() == JsUnaryOperator.TYPEOF) { + return true; + } + return getOperator() != JsUnaryOperator.VOID; + } + + @Override + public boolean isDefinitelyNull() { + return getOperator() == JsUnaryOperator.VOID; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + super.traverse(v, ctx); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.PREFIX_OP; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsProgram.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsProgram.java new file mode 100644 index 00000000000..c8e4310ba9b --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsProgram.java @@ -0,0 +1,174 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +import java.util.HashMap; +import java.util.Map; + +/** + * A JavaScript program. + */ +public final class JsProgram extends JsNode { + + private final JsStatement debuggerStmt; + private final JsEmpty emptyStmt; + private final JsBooleanLiteral falseLiteral; + private JsProgramFragment[] fragments; + private final Map indexedFunctions = new HashMap(); + private final JsNullLiteral nullLiteral; + private final Map numberLiteralMap = + new HashMap(); + private final JsScope objectScope; + private final JsRootScope rootScope; + private final Map stringLiteralMap = + new HashMap(); + private final JsScope topScope; + private final JsBooleanLiteral trueLiteral; + + /** + * Constructs a JavaScript program object. + */ + public JsProgram(String unitId) { + rootScope = new JsRootScope(this); + topScope = new JsScope(rootScope, "Global", unitId); + objectScope = new JsScope(rootScope, "Object"); + setFragmentCount(1); + + debuggerStmt = new JsDebugger(); + emptyStmt = new JsEmpty(); + falseLiteral = new JsBooleanLiteral(false); + nullLiteral = new JsNullLiteral(); + trueLiteral = new JsBooleanLiteral(true); + } + + public JsBooleanLiteral getBooleanLiteral(boolean truth) { + if (truth) { + return getTrueLiteral(); + } + return getFalseLiteral(); + } + + /** + * Gets the {@link JsStatement} to use whenever parsed source include a + * debugger statement. + */ + public JsStatement getDebuggerStmt() { + return debuggerStmt; + } + + public JsEmpty getEmptyStmt() { + return emptyStmt; + } + + public JsBooleanLiteral getFalseLiteral() { + return falseLiteral; + } + + public JsBlock getFragmentBlock(int fragment) { + if (fragment < 0 || fragment >= fragments.length) { + throw new IllegalArgumentException("Invalid fragment: " + fragment); + } + return fragments[fragment].getGlobalBlock(); + } + + public int getFragmentCount() { + return this.fragments.length; + } + + /** + * Gets the one and only global block. + */ + public JsBlock getGlobalBlock() { + return getFragmentBlock(0); + } + + public JsFunction getIndexedFunction(String name) { + return indexedFunctions.get(name); + } + + public JsNullLiteral getNullLiteral() { + return nullLiteral; + } + + public JsNumberLiteral getNumberLiteral(double value) { + JsNumberLiteral lit = numberLiteralMap.get(value); + if (lit == null) { + lit = new JsNumberLiteral(value); + numberLiteralMap.put(value, lit); + } + + return lit; + } + + public JsScope getObjectScope() { + return objectScope; + } + + /** + * Gets the quasi-mythical root scope. This is not the same as the top scope; + * all unresolvable identifiers wind up here, because they are considered + * external to the program. + */ + public JsRootScope getRootScope() { + return rootScope; + } + + /** + * Gets the top level scope. This is the scope of all the statements in the + * main program. + */ + public JsScope getScope() { + return topScope; + } + + /** + * Creates or retrieves a JsStringLiteral from an interned object pool. + */ + public JsStringLiteral getStringLiteral(String value) { + JsStringLiteral lit = stringLiteralMap.get(value); + if (lit == null) { + lit = new JsStringLiteral(value); + stringLiteralMap.put(value, lit); + } + return lit; + } + + public JsBooleanLiteral getTrueLiteral() { + return trueLiteral; + } + + public JsNameRef getUndefinedLiteral() { + return new JsNameRef("$Dart$Null"); + } + + public void setFragmentCount(int fragments) { + this.fragments = new JsProgramFragment[fragments]; + for (int i = 0; i < fragments; i++) { + this.fragments[i] = new JsProgramFragment(); + } + } + + public void setIndexedFunctions(Map indexedFunctions) { + this.indexedFunctions.clear(); + this.indexedFunctions.putAll(indexedFunctions); + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + for (JsProgramFragment fragment : fragments) { + v.accept(fragment); + } + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.PROGRAM; + } + + +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsProgramFragment.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsProgramFragment.java new file mode 100644 index 00000000000..3c6859e2150 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsProgramFragment.java @@ -0,0 +1,34 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * One independently loadable fragment of a {@link JsProgram}. + */ +public class JsProgramFragment extends JsNode { + + private final JsGlobalBlock globalBlock; + + public JsProgramFragment() { + this.globalBlock = new JsGlobalBlock(); + } + + public JsBlock getGlobalBlock() { + return globalBlock; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + v.accept(globalBlock); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.PROGRAM_FRAGMENT; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsPropertyInitializer.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsPropertyInitializer.java new file mode 100644 index 00000000000..d68d8d01af4 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsPropertyInitializer.java @@ -0,0 +1,56 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Used in object literals to specify property values by name. + */ +public class JsPropertyInitializer extends JsNode { + + private JsExpression labelExpr; + private JsExpression valueExpr; + + public JsPropertyInitializer() { + } + + public JsPropertyInitializer(JsExpression labelExpr, JsExpression valueExpr) { + this.labelExpr = labelExpr; + this.valueExpr = valueExpr; + } + + public JsExpression getLabelExpr() { + return labelExpr; + } + + public JsExpression getValueExpr() { + return valueExpr; + } + + public boolean hasSideEffects() { + return labelExpr.hasSideEffects() || valueExpr.hasSideEffects(); + } + + public void setLabelExpr(JsExpression labelExpr) { + this.labelExpr = labelExpr; + } + + public void setValueExpr(JsExpression valueExpr) { + this.valueExpr = valueExpr; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + labelExpr = v.accept(labelExpr); + valueExpr = v.accept(valueExpr); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.PROPERTY_INIT; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsRegExp.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsRegExp.java new file mode 100644 index 00000000000..158c7a5e092 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsRegExp.java @@ -0,0 +1,64 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * A JavaScript regular expression. + */ +public final class JsRegExp extends JsValueLiteral { + + private String flags; + private String pattern; + + public JsRegExp() { + } + + public String getFlags() { + return flags; + } + + public String getPattern() { + return pattern; + } + + @Override + public boolean isBooleanFalse() { + return false; + } + + @Override + public boolean isBooleanTrue() { + return true; + } + + @Override + public boolean isDefinitelyNotNull() { + return true; + } + + @Override + public boolean isDefinitelyNull() { + return false; + } + + public void setFlags(String suffix) { + this.flags = suffix; + } + + public void setPattern(String re) { + this.pattern = re; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + v.visit(this, ctx); + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.REGEXP; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsReturn.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsReturn.java new file mode 100644 index 00000000000..16d90e4b353 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsReturn.java @@ -0,0 +1,48 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * A JavaScript return statement. + */ +public final class JsReturn extends JsStatement { + + private JsExpression expr; + + public JsReturn() { + } + + public JsReturn(JsExpression expr) { + this.expr = expr; + } + + public JsExpression getExpr() { + return expr; + } + + public void setExpr(JsExpression expr) { + this.expr = expr; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + if (expr != null) { + expr = v.accept(expr); + } + } + v.endVisit(this, ctx); + } + + @Override + public boolean unconditionalControlBreak() { + return true; + } + + @Override + public NodeKind getKind() { + return NodeKind.RETURN; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsRootScope.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsRootScope.java new file mode 100644 index 00000000000..33b71f26222 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsRootScope.java @@ -0,0 +1,50 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +import com.google.dart.compiler.backend.js.JsReservedIdentifiers; + +/** + * The root scope is the parent of every scope. All identifiers in this scope + * are not obfuscatable. This scope is prefilled with reserved global + * JavaScript symbols. + */ +public final class JsRootScope extends JsScope { + + private final JsProgram program; + + public JsRootScope(JsProgram program) { + super("Root"); + this.program = program; + } + + @Override + public JsProgram getProgram() { + return program; + } + + @Override + protected JsName doCreateName(String ident, String shortIdent, String originalName) { + JsName name = super.doCreateName(ident, shortIdent, originalName); + name.setObfuscatable(false); + return name; + } + + @Override + public JsName findExistingNameNoRecurse(String ident) { + JsName name = super.findExistingNameNoRecurse(ident); + if (name == null) { + if (JsReservedIdentifiers.getReservedGlobalSymbols().contains(ident)) { + /* + * Lazily add JsNames for reserved identifiers. Since a JsName for a reserved global symbol + * must report a legitimate enclosing scope, we can't simply have a shared set of symbol + * names. + */ + name = doCreateName(ident, ident, ident); + } + } + return name; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsScope.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsScope.java new file mode 100644 index 00000000000..0a4053960b8 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsScope.java @@ -0,0 +1,308 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +import com.google.dart.compiler.util.Lists; +import com.google.dart.compiler.util.Maps; + +import java.io.Serializable; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * A scope is a factory for creating and allocating + * {@link 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 implements Serializable { + + private List children = Collections.emptyList(); + private final String description; + private Map names = Collections.emptyMap(); + private JsScope parent; + protected int tempIndex = 0; + private final String scopeId; + + /* + * Create a scope with parent. + */ + public JsScope(JsScope parent, String description) { + this(parent, description, null); + } + + /** + * Create a scope with parent. + */ + public JsScope(JsScope parent, String description, String scopeId) { + assert (parent != null); + this.scopeId = scopeId; + this.description = description; + this.parent = parent; + parent.children = Lists.add(parent.children, this); + } + + /** + * Rebase the function to a new scope. + * + * @param newParent The scope to add the function to. + */ + public void rebase(JsScope newParent) { + detachFromParent(); + parent = newParent; + parent.children = Lists.add(parent.children, this); + } + + /** + * Rebase the function's children to a new scope. + * + * @param newParent + */ + public void rebaseChildScopes(JsScope newParent) { + if (newParent == this) { + return; + } + parent.children = Lists.addAll(parent.children, children); + for (JsScope child : children) { + child.parent = newParent; + } + children = Collections.emptyList(); + } + + /** + * Subclasses can detach and become parentless. + */ + protected void detachFromParent() { + JsScope oldParent = parent; + + oldParent.children = Lists.remove( + parent.children, oldParent.children.indexOf(this)); + + parent = null; + } + + /** + * Subclasses can be parentless. + */ + protected JsScope(String description) { + this.description = description; + this.parent = null; + this.scopeId = null; + } + + /** + * Gets a name object associated with the specified ident in this scope, + * creating it if necessary.
+ * If the JsName does not exist yet, a new JsName is created. The ident, + * short name, and original name of the newly created JsName are equal to + * the given ident. + * + * @param ident An identifier that is unique within this scope. + */ + public JsName declareName(String ident) { + JsName name = findExistingNameNoRecurse(ident); + if (name != null) { + return name; + } + return doCreateName(ident, ident, ident); + } + + /** + * Creates a new variable with an unique ident in this scope. + * The generated JsName is guaranteed to have an identifier (but not short + * name) that does not clash with any existing variables in the scope. + * Future declarations of variables might however clash with the temporary + * (unless they use this function). + */ + public JsName declareFreshName(String shortName) { + String ident = shortName; + int counter = 0; + while (findExistingNameNoRecurse(ident) != null) { + ident = shortName + "_" + counter++; + } + return doCreateName(ident, shortName, shortName); + } + + String getNextTempName() { + // TODO(ngeoffray): Decide on a convention for temporary variables + // introduced by the compiler. + return "tmp$" + (scopeId != null ? scopeId + "$" : "") + tempIndex++; + } + + /** + * Creates a temporary variable with an unique name in this scope. + * The generated temporary is guaranteed to have an identifier (but not short + * name) that does not clash with any existing variables in the scope. + * Future declarations of variables might however clash with the temporary. + */ + public JsName declareTemporary() { + return declareFreshName(getNextTempName()); + } + + /** + * Gets a name object associated with the specified ident in this scope, + * creating it if necessary.
+ * If the JsName does not exist yet, a new JsName is created with the given + * ident, short name and original name. + * + * @param ident An identifier that is unique within this scope. + * @param shortIdent A "pretty" name that does not have to be unique. + * @throws IllegalArgumentException if ident already exists in this scope but + * the requested short name does not match the existing short name. + */ + public JsName declareName(String ident, String shortIdent) { + return declareName(ident, shortIdent, ident); + } + + /** + * Gets a name object associated with the specified ident in this scope, + * creating it if necessary.
+ * If the JsName does not exist yet, a new JsName is created. The original + * name stored in the JsName is equal to the (unmangled) specified originalName. + * + * @param ident An identifier that is unique within this scope. + * @param shortIdent A "pretty" name that does not have to be unique. + * @param originalName The original name in the source. + * @throws IllegalArgumentException if ident already exists in this scope but + * the requested short name does not match the existing short name, + * or the original name does not match the existing original name. + */ + public JsName declareName(String ident, String shortIdent, String originalName) { + JsName name = findExistingNameNoRecurse(ident); + if (name != null) { + if (!name.getShortIdent().equals(shortIdent) + || !nullableEquals(name.getOriginalName(), originalName)) { + throw new IllegalArgumentException("Requested short name " + shortIdent + + " conflicts with preexisting short name " + name.getShortIdent() + " for identifier " + + ident); + } + return name; + } + return doCreateName(ident, shortIdent, originalName); + } + + boolean nullableEquals(String s1, String s2) { + return (s1 == null) ? (s2 == null) : s1.equals(s2); + } + + /** + * Attempts to find the name object for the specified ident, searching in this + * scope, and if not found, in the parent scopes. + * + * @return null if the identifier has no associated name + */ + public final JsName findExistingName(String ident) { + JsName name = findExistingNameNoRecurse(ident); + if (name == null && parent != null) { + return parent.findExistingName(ident); + } + return name; + } + + /** + * Attempts to find an unobfuscatable name object for the specified ident, + * searching in this scope, and if not found, in the parent scopes. + * + * @return null if the identifier has no associated name + */ + public final JsName findExistingUnobfuscatableName(String ident) { + JsName name = findExistingNameNoRecurse(ident); + if (name != null && name.isObfuscatable()) { + name = null; + } + if (name == null && parent != null) { + return parent.findExistingUnobfuscatableName(ident); + } + return name; + } + + /** + * Returns an iterator for all the names defined by this scope. + */ + public Iterator getAllNames() { + return names.values().iterator(); + } + + /** + * Returns a list of this scope's child scopes. + */ + public final List getChildren() { + return children; + } + + /** + * Returns the parent scope of this scope, or null if this is the + * root scope. + */ + public final JsScope getParent() { + return parent; + } + + /** + * Returns the associated program. + */ + public JsProgram getProgram() { + assert (parent != null) : "Subclasses must override getProgram() if they do not set a parent"; + return parent.getProgram(); + } + + @Override + public final String toString() { + if (parent != null) { + return description + "->" + parent; + } else { + return description; + } + } + + /** + * Creates a new name in this scope. + */ + protected JsName doCreateName(String ident, String shortIdent, String originalName) { + JsName name = new JsName(this, ident, shortIdent, originalName); + names = Maps.putOrdered(names, ident, name); + return name; + } + + /** + * Attempts to find the name object for the specified ident, searching in this + * scope only. + * + * @return null if the identifier has no associated name + */ + public JsName findExistingNameNoRecurse(String ident) { + return names.get(ident); + } + + /** + * + * Checks whether this scope owns this name. + */ + public boolean ownsName(JsName name) { + return names.containsValue(name); + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsStatement.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsStatement.java new file mode 100644 index 00000000000..82d37ca1185 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsStatement.java @@ -0,0 +1,22 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Abstract base class for JavaScript statement objects. + */ +public abstract class JsStatement extends JsNode { + + protected JsStatement() { + } + + /** + * Returns true if this statement definitely causes an abrupt change in flow + * control. + */ + public boolean unconditionalControlBreak() { + return false; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsStringLiteral.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsStringLiteral.java new file mode 100644 index 00000000000..77e60c9b89a --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsStringLiteral.java @@ -0,0 +1,53 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * A JavaScript string literal expression. + */ +public final class JsStringLiteral extends JsValueLiteral { + + private final String value; + + // These only get created by JsProgram so that they can be interned. + JsStringLiteral(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + @Override + public boolean isBooleanFalse() { + return value.length() == 0; + } + + @Override + public boolean isBooleanTrue() { + return value.length() != 0; + } + + @Override + public boolean isDefinitelyNotNull() { + return true; + } + + @Override + public boolean isDefinitelyNull() { + return false; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + v.visit(this, ctx); + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.STRING; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsSwitch.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsSwitch.java new file mode 100644 index 00000000000..5a49624af42 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsSwitch.java @@ -0,0 +1,47 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +import java.util.ArrayList; +import java.util.List; + +/** + * A JavaScript switch statement. + */ +public class JsSwitch extends JsStatement { + + private final List cases = new ArrayList(); + private JsExpression expr; + + public JsSwitch() { + super(); + } + + public List getCases() { + return cases; + } + + public JsExpression getExpr() { + return expr; + } + + public void setExpr(JsExpression expr) { + this.expr = expr; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + expr = v.accept(expr); + v.acceptWithInsertRemove(cases); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.SWITCH; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsSwitchMember.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsSwitchMember.java new file mode 100644 index 00000000000..0a748f60078 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsSwitchMember.java @@ -0,0 +1,24 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +import java.util.ArrayList; +import java.util.List; + +/** + * A member/case in a JavaScript switch object. + */ +public abstract class JsSwitchMember extends JsNode { + + protected final List stmts = new ArrayList(); + + protected JsSwitchMember() { + super(); + } + + public List getStmts() { + return stmts; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsThisRef.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsThisRef.java new file mode 100644 index 00000000000..9adb6459488 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsThisRef.java @@ -0,0 +1,51 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * A JavaScript this reference. + */ +public final class JsThisRef extends JsValueLiteral { + + public JsThisRef() { + super(); + } + + @Override + public boolean isBooleanFalse() { + return false; + } + + @Override + public boolean isBooleanTrue() { + return true; + } + + @Override + public boolean isDefinitelyNotNull() { + /* + * You'd think that you could get a null this via function.call/apply, but + * in fact you can't: they just make this be the window object instead. So + * it really can't ever be null. + */ + return true; + } + + @Override + public boolean isDefinitelyNull() { + return false; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + v.visit(this, ctx); + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.THIS; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsThrow.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsThrow.java new file mode 100644 index 00000000000..4616418fb6b --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsThrow.java @@ -0,0 +1,48 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * A JavaScript throw statement. + */ +public class JsThrow extends JsStatement { + + private JsExpression expr; + + public JsThrow() { + super(); + } + + public JsThrow(JsExpression expr) { + super(); + this.expr = expr; + } + + public JsExpression getExpr() { + return expr; + } + + public void setExpr(JsExpression expr) { + this.expr = expr; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + expr = v.accept(expr); + } + v.endVisit(this, ctx); + } + + @Override + public boolean unconditionalControlBreak() { + return true; + } + + @Override + public NodeKind getKind() { + return NodeKind.THROW; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsTry.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsTry.java new file mode 100644 index 00000000000..61357bcd406 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsTry.java @@ -0,0 +1,59 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +import java.util.ArrayList; +import java.util.List; + +/** + * A JavaScript try statement. + */ +public class JsTry extends JsStatement { + + private final List catches = new ArrayList(); + private JsBlock finallyBlock; + private JsBlock tryBlock; + + public JsTry() { + super(); + } + + public List getCatches() { + return catches; + } + + public JsBlock getFinallyBlock() { + return finallyBlock; + } + + public JsBlock getTryBlock() { + return tryBlock; + } + + public void setFinallyBlock(JsBlock block) { + this.finallyBlock = block; + } + + public void setTryBlock(JsBlock block) { + tryBlock = block; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + tryBlock = v.accept(tryBlock); + v.acceptWithInsertRemove(catches); + if (finallyBlock != null) { + finallyBlock = v.accept(finallyBlock); + } + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.TRY; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsUnaryOperation.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsUnaryOperation.java new file mode 100644 index 00000000000..00f103f35c9 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsUnaryOperation.java @@ -0,0 +1,53 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * A JavaScript prefix or postfix operation. + */ +public abstract class JsUnaryOperation extends JsExpression { + + private JsExpression arg; + + private final JsUnaryOperator op; + + public JsUnaryOperation(JsUnaryOperator op) { + this(op, null); + } + + public JsUnaryOperation(JsUnaryOperator op, JsExpression arg) { + super(); + this.op = op; + this.arg = arg; + } + + public JsExpression getArg() { + return arg; + } + + public JsUnaryOperator getOperator() { + return op; + } + + @Override + public final boolean hasSideEffects() { + return op.isModifying() || arg.hasSideEffects(); + } + + public void setArg(JsExpression arg) { + this.arg = arg; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (op.isModifying()) { + // The delete operator is practically like an assignment of undefined, so + // for practical purposes we're treating it as an lvalue. + arg = v.acceptLvalue(arg); + } else { + arg = v.accept(arg); + } + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsUnaryOperator.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsUnaryOperator.java new file mode 100644 index 00000000000..2b854d8313e --- /dev/null +++ b/js/js.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.ast/src/com/google/dart/compiler/backend/js/ast/JsValueLiteral.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsValueLiteral.java new file mode 100644 index 00000000000..f23ab5dbce4 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsValueLiteral.java @@ -0,0 +1,24 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * A JavaScript string literal expression. + */ +public abstract class JsValueLiteral extends JsLiteral { + + protected JsValueLiteral() { + } + + @Override + public final boolean hasSideEffects() { + return false; + } + + @Override + public final boolean isLeaf() { + return true; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsVars.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsVars.java new file mode 100644 index 00000000000..aaf629259aa --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsVars.java @@ -0,0 +1,109 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +import com.google.dart.compiler.common.SourceInfo; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * A JavaScript var statement. + */ +public class JsVars extends JsStatement implements Iterable { + + /** + * A var declared using the JavaScript var statement. + */ + public static class JsVar extends JsNode implements HasName { + + private final JsName name; + private JsExpression initExpr; + + public JsVar(JsName name) { + this.name = name; + } + + public JsVar(JsName name, JsExpression initExpr) { + this.name = name; + this.initExpr = initExpr; + } + + public JsExpression getInitExpr() { + return initExpr; + } + + @Override + public JsName getName() { + return name; + } + + public void setInitExpr(JsExpression initExpr) { + this.initExpr = initExpr; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + if (initExpr != null) { + initExpr = v.accept(initExpr); + } + } + v.endVisit(this, ctx); + } + + @Override + public JsVar setSourceRef(SourceInfo info) { + super.setSourceRef(info); + return this; + } + + @Override + public NodeKind getKind() { + return NodeKind.VAR; + } + } + + private final List vars = new ArrayList(); + + public JsVars() { + } + + public void add(JsVar var) { + vars.add(var); + } + + public int getNumVars() { + return vars.size(); + } + + public void insert(JsVar var) { + vars.add(var); + } + + public boolean isEmpty() { + return vars.isEmpty(); + } + + // Iterator returns JsVar objects + @Override + public Iterator iterator() { + return vars.iterator(); + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + v.acceptWithInsertRemove(vars); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.VARS; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsVisitable.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsVisitable.java new file mode 100644 index 00000000000..626648af8ae --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsVisitable.java @@ -0,0 +1,19 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Abstracts the idea that a class can be traversed. + */ +public interface JsVisitable { + + /** + * Causes this object to have the visitor visit itself and its children. + * + * @param visitor the visitor that should traverse this node + * @param ctx the context of an existing traversal + */ + void traverse(JsVisitor visitor, JsContext ctx); +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsVisitor.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsVisitor.java new file mode 100644 index 00000000000..5b9cd983d08 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsVisitor.java @@ -0,0 +1,430 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use 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 nodes that will visit child nodes. + */ +@SuppressWarnings("unused") +public class JsVisitor { + + protected static final JsContext LVALUE_CONTEXT = new JsContext() { + + @Override + public boolean canInsert() { + return false; + } + + @Override + public boolean canRemove() { + return false; + } + + @Override + public void insertAfter(JsVisitable node) { + throw new UnsupportedOperationException(); + } + + @Override + public void insertBefore(JsVisitable node) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isLvalue() { + return true; + } + + @Override + public void removeMe() { + throw new UnsupportedOperationException(); + } + + @Override + public void replaceMe(JsVisitable node) { + throw new UnsupportedOperationException(); + } + }; + + protected static final JsContext UNMODIFIABLE_CONTEXT = new JsContext() { + + @Override + public boolean canInsert() { + return false; + } + + @Override + public boolean canRemove() { + return false; + } + + @Override + public void insertAfter(JsVisitable node) { + throw new UnsupportedOperationException(); + } + + @Override + public void insertBefore(JsVisitable node) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isLvalue() { + return false; + } + + @Override + public void removeMe() { + throw new UnsupportedOperationException(); + } + + @Override + public void replaceMe(JsVisitable node) { + throw new UnsupportedOperationException(); + } + }; + + public final T accept(T node) { + return this.doAccept(node); + } + + public final void acceptList(List collection) { + doAcceptList(collection); + } + + public JsExpression acceptLvalue(JsExpression expr) { + return doAcceptLvalue(expr); + } + + public final void acceptWithInsertRemove(List collection) { + doAcceptWithInsertRemove(collection); + } + + public boolean didChange() { + throw new UnsupportedOperationException(); + } + + public void endVisit(JsArrayAccess x, JsContext ctx) { + } + + public void endVisit(JsArrayLiteral x, JsContext ctx) { + } + + public void endVisit(JsBinaryOperation x, JsContext ctx) { + } + + public void endVisit(JsBlock x, JsContext ctx) { + } + + public void endVisit(JsBooleanLiteral x, JsContext ctx) { + } + + public void endVisit(JsBreak x, JsContext ctx) { + } + + public void endVisit(JsCase x, JsContext ctx) { + } + + public void endVisit(JsCatch x, JsContext ctx) { + } + + public void endVisit(JsConditional x, JsContext ctx) { + } + + public void endVisit(JsContinue x, JsContext ctx) { + } + + public void endVisit(JsDebugger x, JsContext ctx) { + } + + public void endVisit(JsDefault x, JsContext ctx) { + } + + public void endVisit(JsDoWhile x, JsContext ctx) { + } + + public void endVisit(JsEmpty x, JsContext ctx) { + } + + public void endVisit(JsExprStmt x, JsContext ctx) { + } + + public void endVisit(JsFor x, JsContext ctx) { + } + + public void endVisit(JsForIn x, JsContext ctx) { + } + + public void endVisit(JsFunction x, JsContext ctx) { + } + + public void endVisit(JsIf x, JsContext ctx) { + } + + public void endVisit(JsInvocation x, JsContext ctx) { + } + + public void endVisit(JsLabel x, JsContext ctx) { + } + + public void endVisit(JsNameRef x, JsContext ctx) { + } + + public void endVisit(JsNew x, JsContext ctx) { + } + + public void endVisit(JsNullLiteral x, JsContext ctx) { + } + + public void endVisit(JsNumberLiteral x, JsContext ctx) { + } + + public void endVisit(JsObjectLiteral x, JsContext ctx) { + } + + public void endVisit(JsParameter x, JsContext ctx) { + } + + public void endVisit(JsPostfixOperation x, JsContext ctx) { + } + + public void endVisit(JsPrefixOperation x, JsContext ctx) { + } + + public void endVisit(JsProgram x, JsContext ctx) { + } + + public void endVisit(JsProgramFragment x, JsContext ctx) { + } + + public void endVisit(JsPropertyInitializer x, JsContext ctx) { + } + + public void endVisit(JsRegExp x, JsContext ctx) { + } + + public void endVisit(JsReturn x, JsContext ctx) { + } + + public void endVisit(JsStringLiteral x, JsContext ctx) { + } + + public void endVisit(JsSwitch x, JsContext ctx) { + } + + public void endVisit(JsThisRef x, JsContext ctx) { + } + + public void endVisit(JsThrow x, JsContext ctx) { + } + + public void endVisit(JsTry x, JsContext ctx) { + } + + public void endVisit(JsVars.JsVar x, JsContext ctx) { + } + + public void endVisit(JsVars x, JsContext ctx) { + } + + public void endVisit(JsWhile x, JsContext ctx) { + } + + public boolean visit(JsArrayAccess x, JsContext ctx) { + return true; + } + + public boolean visit(JsArrayLiteral x, JsContext ctx) { + return true; + } + + public boolean visit(JsBinaryOperation x, JsContext ctx) { + return true; + } + + public boolean visit(JsBlock x, JsContext ctx) { + return true; + } + + public boolean visit(JsBooleanLiteral x, JsContext ctx) { + return true; + } + + public boolean visit(JsBreak x, JsContext ctx) { + return true; + } + + public boolean visit(JsCase x, JsContext ctx) { + return true; + } + + public boolean visit(JsCatch x, JsContext ctx) { + return true; + } + + public boolean visit(JsConditional x, JsContext ctx) { + return true; + } + + public boolean visit(JsContinue x, JsContext ctx) { + return true; + } + + public boolean visit(JsDebugger x, JsContext ctx) { + return true; + } + + public boolean visit(JsDefault x, JsContext ctx) { + return true; + } + + public boolean visit(JsDoWhile x, JsContext ctx) { + return true; + } + + public boolean visit(JsEmpty x, JsContext ctx) { + return true; + } + + public boolean visit(JsExprStmt x, JsContext ctx) { + return true; + } + + public boolean visit(JsFor x, JsContext ctx) { + return true; + } + + public boolean visit(JsForIn x, JsContext ctx) { + return true; + } + + public boolean visit(JsFunction x, JsContext ctx) { + return true; + } + + public boolean visit(JsIf x, JsContext ctx) { + return true; + } + + public boolean visit(JsInvocation x, JsContext ctx) { + return true; + } + + public boolean visit(JsLabel x, JsContext ctx) { + return true; + } + + public boolean visit(JsNameRef x, JsContext ctx) { + return true; + } + + public boolean visit(JsNew x, JsContext ctx) { + return true; + } + + public boolean visit(JsNullLiteral x, JsContext ctx) { + return true; + } + + public boolean visit(JsNumberLiteral x, JsContext ctx) { + return true; + } + + public boolean visit(JsObjectLiteral x, JsContext ctx) { + return true; + } + + public boolean visit(JsParameter x, JsContext ctx) { + return true; + } + + public boolean visit(JsPostfixOperation x, JsContext ctx) { + return true; + } + + public boolean visit(JsPrefixOperation x, JsContext ctx) { + return true; + } + + public boolean visit(JsProgram x, JsContext ctx) { + return true; + } + + public boolean visit(JsProgramFragment x, JsContext ctx) { + return true; + } + + public boolean visit(JsPropertyInitializer x, JsContext ctx) { + return true; + } + + public boolean visit(JsRegExp x, JsContext ctx) { + return true; + } + + public boolean visit(JsReturn x, JsContext ctx) { + return true; + } + + public boolean visit(JsStringLiteral x, JsContext ctx) { + return true; + } + + public boolean visit(JsSwitch x, JsContext ctx) { + return true; + } + + public boolean visit(JsThisRef x, JsContext ctx) { + return true; + } + + public boolean visit(JsThrow x, JsContext ctx) { + return true; + } + + public boolean visit(JsTry x, JsContext ctx) { + return true; + } + + public boolean visit(JsVars.JsVar x, JsContext ctx) { + return true; + } + + public boolean visit(JsVars x, JsContext ctx) { + return true; + } + + public boolean visit(JsWhile x, JsContext ctx) { + return true; + } + + protected T doAccept(T node) { + doTraverse(node, UNMODIFIABLE_CONTEXT); + return node; + } + + protected void doAcceptList(List collection) { + for (T node : collection) { + doTraverse(node, UNMODIFIABLE_CONTEXT); + } + } + + protected JsExpression doAcceptLvalue(JsExpression expr) { + doTraverse(expr, LVALUE_CONTEXT); + return expr; + } + + protected void doAcceptWithInsertRemove(List collection) { + for (T node : collection) { + doTraverse(node, UNMODIFIABLE_CONTEXT); + } + } + + protected void doTraverse(JsVisitable node, JsContext ctx) { + node.traverse(this, ctx); + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsWhile.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsWhile.java new file mode 100644 index 00000000000..c40203bc1f1 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/JsWhile.java @@ -0,0 +1,52 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * A JavaScript while statement. + */ +public class JsWhile extends JsStatement { + + private JsStatement body; + private JsExpression condition; + + public JsWhile() { + } + + public JsWhile(JsExpression condition, JsStatement body) { + this.condition = condition; + this.body = body; + } + + public JsStatement getBody() { + return body; + } + + public JsExpression getCondition() { + return condition; + } + + public void setBody(JsStatement body) { + this.body = body; + } + + public void setCondition(JsExpression condition) { + this.condition = condition; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + condition = v.accept(condition); + body = v.accept(body); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.WHILE; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/backend/js/ast/NodeKind.java b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/NodeKind.java new file mode 100644 index 00000000000..614e4bb3bd9 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/backend/js/ast/NodeKind.java @@ -0,0 +1,50 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +public enum NodeKind { + ARRAY_ACCESS, + ARRAY, + BINARY_OP, + BLOCK, + BOOLEAN, + BREAK, + CASE, + CATCH, + CONDITIONAL, + CONTINUE, + DEBUGGER, + DEFAULT, + DO, + EMPTY, + EXPR_STMT, + FOR, + FOR_IN, + FUNCTION, + IF, + INVOKE, + LABEL, + NAME_REF, + NEW, + NULL, + NUMBER, + OBJECT, + PARAMETER, + POSTFIX_OP, + PREFIX_OP, + PROGRAM, + PROGRAM_FRAGMENT, + PROPERTY_INIT, + REGEXP, + RETURN, + STRING, + SWITCH, + THIS, + THROW, + TRY, + VARS, + VAR, + WHILE +} diff --git a/js/js.ast/src/com/google/dart/compiler/common/AbstractNode.java b/js/js.ast/src/com/google/dart/compiler/common/AbstractNode.java new file mode 100644 index 00000000000..5b70197dd13 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/common/AbstractNode.java @@ -0,0 +1,81 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.common; + +import com.google.dart.compiler.Source; + +/** + * Abstract base class for nodes that carry source information. + */ +public class AbstractNode implements SourceInfo, HasSourceInfo { + + // TODO(johnlenz): All this source location data is wasteful. + // Move it into a com.google.dart.compiler.backend.common object, that can be shared between the ASTs + // or something. + protected Source source = null; + protected int sourceLine = -1; + protected int sourceColumn = -1; + protected int sourceStart = -1; + protected int sourceLength = -1; + + @Override + public Source getSource() { + return source; + } + + @Override + public int getSourceLine() { + return sourceLine; + } + + @Override + public int getSourceColumn() { + return sourceColumn; + } + + @Override + public int getSourceStart() { + return sourceStart; + } + + @Override + public int getSourceLength() { + return sourceLength; + } + + @Override + public SourceInfo getSourceInfo() { + return this; + } + + @Override + public void setSourceInfo(SourceInfo info) { + source = info.getSource(); + sourceStart = info.getSourceStart(); + sourceLength = info.getSourceLength(); + sourceLine = info.getSourceLine(); + sourceColumn = info.getSourceColumn(); + } + + @Override + public final void setSourceLocation( + Source source, int line, int column, int startPosition, int length) { + assert (startPosition != -1 && length >= 0 + || startPosition == -1 && length == 0); + this.source = source; + this.sourceLine = line; + this.sourceColumn = column; + this.sourceStart = startPosition; + this.sourceLength = length; + } + + public final void setSourceRange(int startPosition, int length) { + assert (startPosition != -1 && length >= 0 + || startPosition == -1 && length == 0); + this.sourceStart = startPosition; + this.sourceLength = length; + } + +} diff --git a/js/js.ast/src/com/google/dart/compiler/common/HasSourceInfo.java b/js/js.ast/src/com/google/dart/compiler/common/HasSourceInfo.java new file mode 100644 index 00000000000..bda3da94996 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/common/HasSourceInfo.java @@ -0,0 +1,55 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.common; + +import com.google.dart.compiler.Source; + +/** + * Abstract view of a class that has source info. + */ +public interface HasSourceInfo { + + /** + * Return the source info associated with this object. + */ + SourceInfo getSourceInfo(); + + /** + * Set the source info associated with this object. May only be called once. + * @param info + */ + void setSourceInfo(SourceInfo info); + + /** + * Sets the source range of the original source file where the source fragment + * corresponding to this node was found. + * + *

+ * Each node in the subtree (other than the contrived nodes) carries source + * range(s) information relating back to positions in the given source (the + * given source itself is not remembered with the AST). The source range + * usually begins at the first character of the first token corresponding to + * the node; leading whitespace and comments are not included. The + * source range usually extends through the last character of the last token + * corresponding to the node; trailing whitespace and comments are not + * included. There are a handful of exceptions (including the various body + * declarations). Source ranges nest properly: the source range for a child is + * always within the source range of its parent, and the source ranges of + * sibling nodes never overlap. + * + * @param source the associated source + * @param line the 1-based line index, or -1, if no source + * location is available + * @param column the 1-based column index, or -1, if no source + * location is available + * @param startPosition a 0-based character index, or -1, if no + * source location is available + * @param length a (possibly 0) length, or -1, if no source + * location is available + * @see SourceInfo#getSourceStart() + * @see SourceInfo#getSourceLength() + */ + void setSourceLocation(Source source, int line, int column, int startPosition, int length); +} diff --git a/js/js.ast/src/com/google/dart/compiler/common/Name.java b/js/js.ast/src/com/google/dart/compiler/common/Name.java new file mode 100644 index 00000000000..4697b491d96 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/common/Name.java @@ -0,0 +1,121 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.common; + +import java.io.*; +import java.nio.charset.Charset; + +/** + * Instead of Strings, we use GlobalNames to abstract the underlying representation. + * + *

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

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

+ * The parser supplies useful well-defined source ranges to the nodes it creates. + * + * @return a (possibly 0) length, or 0 + * if no source source position information is recorded for this node + * @see #getSourceStart() + * @see HasSourceInfo#setSourceLocation(com.google.dart.compiler.Source, int, int, int, int) + */ + int getSourceLength(); +} diff --git a/js/js.ast/src/com/google/dart/compiler/util/AbstractTextOutput.java b/js/js.ast/src/com/google/dart/compiler/util/AbstractTextOutput.java new file mode 100644 index 00000000000..9c8d2b1cd16 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/util/AbstractTextOutput.java @@ -0,0 +1,132 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.util; + +import java.io.PrintWriter; +import java.util.Arrays; + +/** + * An abstract base type to build TextOutput implementations. + */ +public abstract class AbstractTextOutput implements TextOutput { + private final boolean compact; + private int identLevel = 0; + private int indentGranularity = 2; + private char[][] indents = new char[][] {new char[0]}; + private boolean justNewlined; + private PrintWriter out; + private int position = 0; + private int line = 0; + private int column = 0; + + protected AbstractTextOutput(boolean compact) { + this.compact = compact; + } + + public int getPosition() { + return position; + } + + public int getLine() { + return line; + } + + public int getColumn() { + return column; + } + + public void indentIn() { + ++identLevel; + if (identLevel >= indents.length) { + // Cache a new level of indentation string. + // + char[] newIndentLevel = new char[identLevel * indentGranularity]; + Arrays.fill(newIndentLevel, ' '); + char[][] newIndents = new char[indents.length + 1][]; + System.arraycopy(indents, 0, newIndents, 0, indents.length); + newIndents[identLevel] = newIndentLevel; + indents = newIndents; + } + } + + public void indentOut() { + --identLevel; + } + + public void newline() { + out.print('\n'); + position++; + line++; + column = 0; + justNewlined = true; + } + + public void newlineOpt() { + if (!compact) { + newline(); + } + } + + public void print(char c) { + maybeIndent(); + out.print(c); + position++; + column++; + justNewlined = false; + } + + public void print(char[] s) { + maybeIndent(); + printAndCount(s); + justNewlined = false; + } + + public void print(String s) { + maybeIndent(); + printAndCount(s.toCharArray()); + justNewlined = false; + } + + // Why don't the "Opt" methods update "justNewLined"? + public void printOpt(char c) { + if (!compact) { + maybeIndent(); + out.print(c); + position += 1; + column++; + } + } + + public void printOpt(char[] s) { + if (!compact) { + maybeIndent(); + printAndCount(s); + } + } + + public void printOpt(String s) { + if (!compact) { + maybeIndent(); + printAndCount(s.toCharArray()); + } + } + + protected void setPrintWriter(PrintWriter out) { + this.out = out; + } + + private void maybeIndent() { + if (justNewlined && !compact) { + printAndCount(indents[identLevel]); + justNewlined = false; + } + } + + private void printAndCount(char[] chars) { + position += chars.length; + column += chars.length; + out.print(chars); + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/util/AstUtil.java b/js/js.ast/src/com/google/dart/compiler/util/AstUtil.java new file mode 100644 index 00000000000..c635d696c54 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/util/AstUtil.java @@ -0,0 +1,404 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.util; + +import com.google.dart.compiler.InternalCompilerException; +import com.google.dart.compiler.backend.js.ast.*; +import com.google.dart.compiler.common.SourceInfo; +import com.sun.istack.internal.NotNull; + +import java.util.List; + +/** + * @author johnlenz@google.com (John Lenz) + */ +public class AstUtil { + + public static JsInvocation newInvocation( + JsExpression target, JsExpression... params) { + JsInvocation invoke = new JsInvocation(); + invoke.setQualifier(target); + for (JsExpression expr : params) { + invoke.getArguments().add(expr); + } + return invoke; + } + + public static JsInvocation newInvocation( + JsExpression target, List params) { + JsInvocation invoke = new JsInvocation(); + invoke.setQualifier(target); + for (JsExpression expr : params) { + invoke.getArguments().add(expr); + } + return invoke; + } + + public static JsNameRef newQualifiedNameRef(String name) { + JsNameRef node = null; + int endPos = -1; + int startPos = 0; + do { + endPos = name.indexOf('.', startPos); + String part = (endPos == -1 + ? name.substring(startPos) + : name.substring(startPos, endPos)); + node = newNameRef(node, part); + startPos = endPos + 1; + } while (endPos != -1); + + return node; + } + + public static JsNameRef newNameRef(JsExpression qualifier, String prop) { + JsNameRef nameRef = new JsNameRef(prop); + if (qualifier != null) { + nameRef.setQualifier(qualifier); + } + return nameRef; + } + + public static JsNameRef nameref(SourceInfo info, JsName qualifier, JsName prop) { + JsNameRef nameRef = new JsNameRef(prop); + if (qualifier != null) { + nameRef.setQualifier(qualifier.makeRef()); + } + nameRef.setSourceRef(info); + return nameRef; + } + + public static JsNameRef newNameRef(JsExpression qualifier, JsName prop) { + JsNameRef nameRef = new JsNameRef(prop); + if (qualifier != null) { + nameRef.setQualifier(qualifier); + } + return nameRef; + } + + public static JsNameRef newPrototypeNameRef(JsExpression qualifier) { + return newNameRef(qualifier, "prototype"); + } + + public static JsArrayAccess newArrayAccess(JsExpression target, JsExpression key) { + JsArrayAccess arr = new JsArrayAccess(); + arr.setArrayExpr(target); + arr.setIndexExpr(key); + return arr; + } + + public static JsBlock newBlock(JsStatement... stmts) { + JsBlock jsBlock = new JsBlock(); + for (JsStatement stmt : stmts) { + jsBlock.getStatements().add(stmt); + } + return jsBlock; + } + + /** + * Returns a sequence of expressions (using the binary sequence operator). + * + * @param exprs - expressions to add to sequence + * @return a sequence of expressions. + */ + public static JsBinaryOperation newSequence(JsExpression... exprs) { + if (exprs.length < 2) { + throw new InternalCompilerException("newSequence expects at least two arguments"); + } + JsExpression result = exprs[exprs.length - 1]; + for (int i = exprs.length - 2; i >= 0; i--) { + result = new JsBinaryOperation(JsBinaryOperator.COMMA, exprs[i], result); + } + return (JsBinaryOperation) result; + } + + // Ensure a valid LHS + public static JsBinaryOperation newAssignment( + JsNameRef nameRef, JsExpression expr) { + return new JsBinaryOperation(JsBinaryOperator.ASG, nameRef, expr); + } + + public static JsBinaryOperation newAssignment( + JsArrayAccess target, JsExpression expr) { + return new JsBinaryOperation(JsBinaryOperator.ASG, target, expr); + } + + public static JsVars newVar(SourceInfo info, JsName name, JsExpression expr) { + JsVars.JsVar var = new JsVars.JsVar(name).setSourceRef(info); + var.setInitExpr(expr); + JsVars vars = new JsVars(); + vars.add(var); + return vars; + } + + public static JsVars newVar(JsName name, JsExpression expr) { + JsVars.JsVar var = new JsVars.JsVar(name); + if (expr != null) { + var.setInitExpr(expr); + } + JsVars vars = new JsVars(); + vars.add(var); + return vars; + } + + public static JsSwitch newSwitch( + JsExpression expr, JsSwitchMember... cases) { + JsSwitch jsSwitch = new JsSwitch(); + jsSwitch.setExpr(expr); + for (JsSwitchMember jsCase : cases) { + jsSwitch.getCases().add(jsCase); + } + return jsSwitch; + } + + public static JsCase newCase(JsExpression expr, JsStatement... stmts) { + JsCase jsCase = new JsCase(); + jsCase.setCaseExpr(expr); + for (JsStatement stmt : stmts) { + jsCase.getStmts().add(stmt); + } + return jsCase; + } + + public static JsDefault newDefaultCase(JsStatement... stmts) { + JsDefault jsCase = new JsDefault(); + for (JsStatement stmt : stmts) { + jsCase.getStmts().add(stmt); + } + return jsCase; + } + + public static JsFunction newFunction( + JsScope scope, JsName name, List params, JsBlock body) { + JsFunction fn = new JsFunction(scope); + if (name != null) { + fn.setName(name); + } + if (params != null) { + for (JsParameter param : params) { + fn.getParameters().add(param); + } + } + fn.setBody(body); + return fn; + } + + public static JsInvocation call(SourceInfo src, JsExpression target, JsExpression... params) { + return (JsInvocation) newInvocation(target, params).setSourceRef(src); + } + + public static JsBinaryOperation comma(SourceInfo src, JsExpression op1, JsExpression op2) { + return (JsBinaryOperation) new JsBinaryOperation(JsBinaryOperator.COMMA, op1, op2) + .setSourceRef(src); + } + + public static JsNameRef nameref(SourceInfo src, String name) { + return (JsNameRef) new JsNameRef(name).setSourceRef(src); + } + + public static JsNameRef nameref(SourceInfo src, JsName qualifier, String prop) { + return nameref(src, qualifier.makeRef().setSourceRef(src), prop); + } + + public static JsNameRef nameref(SourceInfo src, JsExpression qualifier, String prop) { + return (JsNameRef) newNameRef(qualifier, prop).setSourceRef(src); + } + + public static JsExpression assign(SourceInfo src, JsNameRef op1, JsExpression op2) { + return newAssignment(op1, op2).setSourceRef(src); + } + + public static JsExpression neq(SourceInfo src, JsExpression op1, JsExpression op2) { + return new JsBinaryOperation(JsBinaryOperator.NEQ, op1, op2).setSourceRef(src); + } + + public static JsExpression not(SourceInfo src, JsExpression op1) { + return new JsPrefixOperation(JsUnaryOperator.NOT, op1).setSourceRef(src); + } + + public static JsExpression preinc(SourceInfo src, JsExpression op1) { + return new JsPrefixOperation(JsUnaryOperator.INC, op1).setSourceRef(src); + } + + public static JsExpression and(SourceInfo src, JsExpression op1, JsExpression op2) { + return new JsBinaryOperation(JsBinaryOperator.AND, op1, op2).setSourceRef(src); + } + + public static JsExpression in(SourceInfo src, JsExpression propName, JsExpression obj) { + return new JsBinaryOperation(JsBinaryOperator.INOP, propName, obj).setSourceRef(src); + } + + + // Pavel Talanov + public static JsPropertyInitializer newNamedMethod(JsName name, JsFunction function) { + JsNameRef methodName = name.makeRef(); + return new JsPropertyInitializer(methodName, function); + } + + public static JsStatement convertToStatement(JsNode jsNode) { + assert (jsNode instanceof JsExpression) || (jsNode instanceof JsStatement) + : "Unexpected node of type: " + jsNode.getClass().toString(); + if (jsNode instanceof JsExpression) { + return new JsExprStmt((JsExpression) jsNode); + } + return (JsStatement) jsNode; + } + + public static JsBlock convertToBlock(JsNode jsNode) { + if (jsNode instanceof JsBlock) { + return (JsBlock) jsNode; + } + JsStatement jsStatement = convertToStatement(jsNode); + return new JsBlock(jsStatement); + } + + public static JsExpression convertToExpression(JsNode jsNode) { + assert jsNode instanceof JsExpression : "Unexpected node of type: " + jsNode.getClass().toString(); + return (JsExpression) jsNode; + } + + public static JsNameRef thisQualifiedReference(JsName name) { + JsNameRef result = name.makeRef(); + result.setQualifier(new JsThisRef()); + return result; + } + + public static JsBlock newBlock(List statements) { + JsBlock result = new JsBlock(); + result.setStatements(statements); + return result; + } + + public static JsPrefixOperation negated(JsExpression expression) { + return new JsPrefixOperation(JsUnaryOperator.NOT, expression); + } + + public static JsStatement newAssignmentStatement(JsNameRef nameRef, JsExpression expr) { + return convertToStatement(new JsBinaryOperation(JsBinaryOperator.ASG, nameRef, expr)); + } + + public static JsExpression extractExpressionFromStatement(JsStatement statement) { + assert statement instanceof JsExprStmt : "Cannot extract expression from statement: " + statement; + return (((JsExprStmt) statement).getExpression()); + } + + public static JsBinaryOperation and(JsExpression op1, JsExpression op2) { + return new JsBinaryOperation(JsBinaryOperator.AND, op1, op2); + } + + public static JsBinaryOperation or(JsExpression op1, JsExpression op2) { + return new JsBinaryOperation(JsBinaryOperator.OR, op1, op2); + } + + //TODO refactor + public static void setQualifier(JsExpression selector, JsExpression receiver) { + if (selector instanceof JsInvocation) { + setQualifier(((JsInvocation) selector).getQualifier(), receiver); + return; + } + if (selector instanceof JsNameRef) { + JsNameRef nameRef = (JsNameRef) selector; + JsExpression qualifier = nameRef.getQualifier(); + if (qualifier == null) { + nameRef.setQualifier(receiver); + } else { + setQualifier(qualifier, receiver); + } + return; + } + throw new AssertionError("Set qualifier should be applied only to JsInvocation or JsNameRef instances"); + } + + //TODO: look for should-be-usages + public static JsNameRef qualified(JsName selector, JsExpression qualifier) { + JsNameRef reference = selector.makeRef(); + AstUtil.setQualifier(reference, qualifier); + return reference; + } + + public static JsBinaryOperation equals(JsExpression arg1, JsExpression arg2) { + return new JsBinaryOperation(JsBinaryOperator.EQ, arg1, arg2); + } + + public static JsBinaryOperation notEqual(JsExpression arg1, JsExpression arg2) { + return new JsBinaryOperation(JsBinaryOperator.NEQ, arg1, arg2); + } + + public static JsExpression equalsTrue(JsExpression expression, JsProgram program) { + return equals(expression, program.getTrueLiteral()); + } + + public static JsExpression assignment(JsExpression left, JsExpression right) { + return new JsBinaryOperation(JsBinaryOperator.ASG, left, right); + } + + public static JsBinaryOperation sum(JsExpression left, JsExpression right) { + return new JsBinaryOperation(JsBinaryOperator.ADD, left, right); + } + + public static JsBinaryOperation subtract(JsExpression left, JsExpression right) { + return new JsBinaryOperation(JsBinaryOperator.SUB, left, right); + } + + public static JsPrefixOperation not(JsExpression expression) { + return new JsPrefixOperation(JsUnaryOperator.NOT, expression); + } + + public static JsBinaryOperation typeof(JsExpression expression, JsStringLiteral string) { + return equals(new JsPrefixOperation(JsUnaryOperator.TYPEOF, expression), string); + } + + public static interface Mutator { + public JsNode mutate(JsNode node); + } + + + //TODO: move somewhere + //TODO: refactor and review + public static JsNode mutateLastExpression(JsNode node, Mutator mutator) { + if (node instanceof JsBlock) { + JsBlock block = (JsBlock) node; + List statements = block.getStatements(); + + if (statements.isEmpty()) return block; + + int size = statements.size(); + statements.set(size - 1, + AstUtil.convertToStatement(mutateLastExpression(statements.get(size - 1), mutator))); + return block; + } + if (node instanceof JsIf) { + JsIf ifExpr = (JsIf) node; + ifExpr.setThenStmt(AstUtil.convertToStatement(mutateLastExpression(ifExpr.getThenStmt(), mutator))); + JsStatement elseStmt = ifExpr.getElseStmt(); + if (elseStmt != null) { + ifExpr.setElseStmt(AstUtil.convertToStatement(mutateLastExpression(elseStmt, mutator))); + } + return ifExpr; + } + if (node instanceof JsExprStmt) { + return AstUtil.convertToStatement(mutateLastExpression(((JsExprStmt) node).getExpression(), mutator)); + } + return mutator.mutate(node); + } + + + public static final class SaveLastExpressionMutator implements Mutator { + + @NotNull + private final JsExpression toAssign; + + public SaveLastExpressionMutator(@NotNull JsExpression toAssign) { + this.toAssign = toAssign; + } + + @Override + public JsNode mutate(JsNode node) { + if (!(node instanceof JsExpression)) { + return node; + } + return AstUtil.assignment(toAssign, (JsExpression) node); + } + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/util/DefaultTextOutput.java b/js/js.ast/src/com/google/dart/compiler/util/DefaultTextOutput.java new file mode 100644 index 00000000000..2a064ff87fd --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/util/DefaultTextOutput.java @@ -0,0 +1,32 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.util; + +import java.io.PrintWriter; +import java.io.StringWriter; + +/** + * Adapts {@link TextOutput} to an internal text buffer. + */ +public class DefaultTextOutput extends AbstractTextOutput { + + private final StringWriter sw = new StringWriter(); + private final PrintWriter out; + + public DefaultTextOutput(boolean compact) { + super(compact); + setPrintWriter(out = new PrintWriter(sw)); + } + + @Override + public String toString() { + out.flush(); + if (sw != null) { + return sw.toString(); + } else { + return super.toString(); + } + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/util/Hack.java b/js/js.ast/src/com/google/dart/compiler/util/Hack.java new file mode 100644 index 00000000000..642f416394f --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/util/Hack.java @@ -0,0 +1,16 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.util; + +/** + * Utility to hack around untypable (or yet to be) code. + */ +public class Hack { + public static T cast(Object o) { + @SuppressWarnings("unchecked") + T t = (T) o; + return t; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/util/Lists.java b/js/js.ast/src/com/google/dart/compiler/util/Lists.java new file mode 100644 index 00000000000..b0ca023d280 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/util/Lists.java @@ -0,0 +1,291 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use 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.*; + +/** + * Utility methods for operating on memory-efficient lists. All lists of size 0 + * or 1 are assumed to be immutable. All lists of size greater than 1 are + * assumed to be mutable. + */ +public class Lists { + + private static final Class MULTI_LIST_CLASS = ArrayList.class; + private static final Class SINGLETON_LIST_CLASS = Collections.singletonList(null).getClass(); + + public static List add(List list, int index, T toAdd) { + switch (list.size()) { + case 0: + // Empty -> Singleton + if (index != 0) { + throw newIndexOutOfBounds(list, index); + } + return Collections.singletonList(toAdd); + case 1: { + // Singleton -> ArrayList + List result = new ArrayList(2); + switch (index) { + case 0: + result.add(toAdd); + result.add(list.get(0)); + return result; + case 1: + result.add(list.get(0)); + result.add(toAdd); + return result; + default: + throw newIndexOutOfBounds(list, index); + } + } + default: + // ArrayList + list.add(index, toAdd); + return list; + } + } + + public static List add(List list, T toAdd) { + switch (list.size()) { + case 0: + // Empty -> Singleton + return Collections.singletonList(toAdd); + case 1: { + // Singleton -> ArrayList + List result = new ArrayList(2); + result.add(list.get(0)); + result.add(toAdd); + return result; + } + default: + // ArrayList + list.add(toAdd); + return list; + } + } + + public static List addAll(List list, int index, List toAdd) { + switch (toAdd.size()) { + case 0: + // No-op. + return list; + case 1: + // Add one element. + return add(list, index, toAdd.get(0)); + default: + // True list merge, result >= 2. + switch (list.size()) { + case 0: + if (index != 0) { + throw newIndexOutOfBounds(list, index); + } + return new ArrayList(toAdd); + case 1: { + List result = new ArrayList(1 + toAdd.size()); + switch (index) { + case 0: + result.addAll(toAdd); + result.add(list.get(0)); + return result; + case 1: + result.add(list.get(0)); + result.addAll(toAdd); + return result; + default: + throw newIndexOutOfBounds(list, index); + } + } + default: + list.addAll(index, toAdd); + return list; + } + } + } + + public static List addAll(List list, List toAdd) { + switch (toAdd.size()) { + case 0: + // No-op. + return list; + case 1: + // Add one element. + return add(list, toAdd.get(0)); + default: + // True list merge, result >= 2. + switch (list.size()) { + case 0: + return new ArrayList(toAdd); + case 1: { + List result = new ArrayList(1 + toAdd.size()); + result.add(list.get(0)); + result.addAll(toAdd); + return result; + } + default: + list.addAll(toAdd); + return list; + } + } + } + + public static List addAll(List list, T... toAdd) { + switch (toAdd.length) { + case 0: + // No-op. + return list; + case 1: + // Add one element. + return add(list, toAdd[0]); + default: + // True list merge, result >= 2. + switch (list.size()) { + case 0: + return new ArrayList(Arrays.asList(toAdd)); + case 1: { + List result = new ArrayList(1 + toAdd.length); + result.add(list.get(0)); + result.addAll(Arrays.asList(toAdd)); + return result; + } + default: + list.addAll(Arrays.asList(toAdd)); + return list; + } + } + } + + public static List create() { + return Collections.emptyList(); + } + + public static List create(Collection collection) { + switch (collection.size()) { + case 0: + return create(); + default: + return new ArrayList(collection); + } + } + + public static List create(List list) { + switch (list.size()) { + case 0: + return create(); + case 1: + return create(list.get(0)); + default: + return new ArrayList(list); + } + } + + public static List create(T item) { + return Collections.singletonList(item); + } + + public static List create(T... items) { + switch (items.length) { + case 0: + return create(); + case 1: + return create(items[0]); + default: + return new ArrayList(Arrays.asList(items)); + } + } + + public static List normalize(List list) { + switch (list.size()) { + case 0: + return create(); + case 1: { + if (list.getClass() == SINGLETON_LIST_CLASS) { + return list; + } + return create(list.get(0)); + } + default: + if (list.getClass() == MULTI_LIST_CLASS) { + return list; + } + return new ArrayList(list); + } + } + + public static List normalizeUnmodifiable(List list) { + if (list.size() < 2) { + return normalize(list); + } else { + List copy = new ArrayList(list.size()); + Collections.copy(copy, list); + return copy; + } + } + + public static List remove(List list, int toRemove) { + switch (list.size()) { + case 0: + // Empty + throw newIndexOutOfBounds(list, toRemove); + case 1: + // Singleton -> Empty + if (toRemove == 0) { + return Collections.emptyList(); + } else { + throw newIndexOutOfBounds(list, toRemove); + } + case 2: + // ArrayList -> Singleton + switch (toRemove) { + case 0: + return Collections.singletonList(list.get(1)); + case 1: + return Collections.singletonList(list.get(0)); + default: + throw newIndexOutOfBounds(list, toRemove); + } + default: + // ArrayList + list.remove(toRemove); + return list; + } + } + + public static List set(List list, int index, T e) { + switch (list.size()) { + case 0: + // Empty + throw newIndexOutOfBounds(list, index); + case 1: + // Singleton + if (index == 0) { + return Collections.singletonList(e); + } else { + throw newIndexOutOfBounds(list, index); + } + default: + // ArrayList + list.set(index, e); + return list; + } + } + + public static > List sort(List list) { + if (list.size() > 1) { + Collections.sort(list); + } + return list; + } + + public static List sort(List list, Comparator sort) { + if (list.size() > 1) { + Collections.sort(list, sort); + } + return list; + } + + private static IndexOutOfBoundsException newIndexOutOfBounds(List list, int index) { + return new IndexOutOfBoundsException("Index: " + index + ", Size: " + list.size()); + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/util/Maps.java b/js/js.ast/src/com/google/dart/compiler/util/Maps.java new file mode 100644 index 00000000000..15274b04902 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/util/Maps.java @@ -0,0 +1,160 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.util; + +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +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 static final Class MULTI_MAP_CLASS = HashMap.class; + private static final Class SINGLETON_MAP_CLASS = + Collections.singletonMap(null, null).getClass(); + + public static Map create() { + return Collections.emptyMap(); + } + + public static Map create(K key, V value) { + return Collections.singletonMap(key, value); + } + + public static Map normalize(Map map) { + switch (map.size()) { + case 0: + return create(); + case 1: { + if (map.getClass() == SINGLETON_MAP_CLASS) { + return map; + } + K key = map.keySet().iterator().next(); + return create(key, map.get(key)); + } + default: + if (map.getClass() == MULTI_MAP_CLASS) { + return map; + } + return new HashMap(map); + } + } + + public static Map normalizeUnmodifiable(Map map) { + if (map.size() < 2) { + return normalize(map); + } else { + // TODO: implement an UnmodifiableHashMap? + return Collections.unmodifiableMap(normalize(map)); + } + } + + public static Map put(Map map, K key, V value) { + switch (map.size()) { + case 0: + // Empty -> Singleton + return Collections.singletonMap(key, value); + case 1: { + if (map.containsKey(key)) { + return create(key, value); + } + // Singleton -> HashMap + Map result = new HashMap(); + result.put(map.keySet().iterator().next(), map.values().iterator().next()); + result.put(key, value); + return result; + } + default: + // HashMap + map.put(key, value); + return map; + } + } + + public static Map putAll(Map map, Map toAdd) { + switch (toAdd.size()) { + case 0: + // No-op. + return map; + case 1: { + // Add one element. + K key = toAdd.keySet().iterator().next(); + return put(map, key, toAdd.get(key)); + } + default: + // True list merge, result >= 2. + switch (map.size()) { + case 0: + return new HashMap(toAdd); + case 1: { + HashMap result = new HashMap(); + K key = map.keySet().iterator().next(); + result.put(key, map.get(key)); + result.putAll(toAdd); + return result; + } + default: + map.putAll(toAdd); + return map; + } + } + } + + /** + * A variation of the put method which uses a LinkedHashMap. + */ + public static Map putOrdered(Map map, K key, V value) { + switch (map.size()) { + case 0: + // Empty -> Singleton + return Collections.singletonMap(key, value); + case 1: { + if (map.containsKey(key)) { + return create(key, value); + } + // Singleton -> LinkedHashMap + Map result = new LinkedHashMap(); + result.put(map.keySet().iterator().next(), map.values().iterator().next()); + result.put(key, value); + return result; + } + default: + // LinkedHashMap + map.put(key, value); + return map; + } + } + + public static Map remove(Map map, K key) { + switch (map.size()) { + case 0: + // Empty + return map; + case 1: + // Singleton -> Empty + if (map.containsKey(key)) { + return create(); + } + return map; + case 2: + // HashMap -> Singleton + if (map.containsKey(key)) { + map.remove(key); + key = map.keySet().iterator().next(); + return create(key, map.get(key)); + } + return map; + default: + // IdentityHashMap + map.remove(key); + return map; + } + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/util/Paths.java b/js/js.ast/src/com/google/dart/compiler/util/Paths.java new file mode 100644 index 00000000000..cdda910ccb1 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/util/Paths.java @@ -0,0 +1,100 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.util; + +import java.io.File; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; + +/** + * Utility methods for obtaining relative paths from files and translating + * relative paths into files. + */ +public class Paths { + + /** + * Answer the relative path from baseFile to relFile + * + * @param baseFile the base file (not null) from which the + * desired path starts + * @param relFile the file referenced by the desired relative path (not + * null) + * @return a relative path (not null) + */ + public static String relativePathFor(File baseFile, File relFile) { + String baseFilePath = baseFile.getPath().replace(File.separatorChar, '/'); + String relFilePath = relFile.getPath().replace(File.separatorChar, '/'); + int baseFilePathLen = baseFilePath.length(); + int relFilePathLen = relFilePath.length(); + + // Find the com.google.dart.compiler.backend.common path elements + int index = 0; + while (index < baseFilePathLen - 1 && index < relFilePathLen - 1) { + if (baseFilePath.charAt(index) != relFilePath.charAt(index)) { + break; + } + index++; + } + while (index >= 0 + && (baseFilePath.charAt(index) != '/' || relFilePath.charAt(index) != '/')) { + index--; + } + int commonStart = index; + + // Build a path up from the base file + StringBuilder relPath = new StringBuilder(baseFilePathLen + relFilePathLen + - commonStart * 2); + index = commonStart + 1; + while (true) { + index = baseFilePath.indexOf('/', index); + if (index == -1) { + break; + } + relPath.append("../"); + index++; + } + relPath.append(relFilePath.substring(commonStart + 1)); + return relPath.toString(); + } + + /** + * Answer the file relative to the specified file + * + * @param baseFile the base file (not null) + * @param relPath the path to the desired file relative to baseFile + * @return the file (not null) + */ + public static File relativePathToFile(File baseFile, String relPath) { + if (relPath.startsWith("/")) { + return new File(relPath); + } + File parentFile = baseFile.getParentFile(); + String name; + if (parentFile == null) { + name = "."; + } else { + name = parentFile.getPath().replace(File.separatorChar, '/'); + } + name = URI.create(name + "/" + relPath).normalize().getPath(); + return new File(name); + } + + /** + * Given a collection of paths, return a collection of files + * + * @param a collection of paths to various files (not null, + * contains no nulls) + * @return a collection of files (not null, contains no + * nulls) + */ + public static List toFiles(List filePaths) { + List files = new ArrayList(); + for (String path : filePaths) { + files.add(new File(path)); + } + return files; + } +} diff --git a/js/js.ast/src/com/google/dart/compiler/util/TextOutput.java b/js/js.ast/src/com/google/dart/compiler/util/TextOutput.java new file mode 100644 index 00000000000..99a465b1532 --- /dev/null +++ b/js/js.ast/src/com/google/dart/compiler/util/TextOutput.java @@ -0,0 +1,37 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.util; + +/** + * Interface used for printing text output. + */ +public interface TextOutput { + + int getPosition(); + + int getLine(); + + int getColumn(); + + void indentIn(); + + void indentOut(); + + void newline(); + + void newlineOpt(); + + void print(char c); + + void print(char[] s); + + void print(String s); + + void printOpt(char c); + + void printOpt(char[] s); + + void printOpt(String s); +} diff --git a/js/js.backend/js.backend.iml b/js/js.backend/js.backend.iml new file mode 100644 index 00000000000..6333c03551e --- /dev/null +++ b/js/js.backend/js.backend.iml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/js/js.backend/lib/asm-util-3.3.1.jar b/js/js.backend/lib/asm-util-3.3.1.jar new file mode 100644 index 00000000000..0230bbcfb71 Binary files /dev/null and b/js/js.backend/lib/asm-util-3.3.1.jar differ diff --git a/js/js.backend/lib/guava-10.0.1.jar b/js/js.backend/lib/guava-10.0.1.jar new file mode 100644 index 00000000000..d107c0f3b0c Binary files /dev/null and b/js/js.backend/lib/guava-10.0.1.jar differ diff --git a/js/js.backend/lib/js.jar b/js/js.backend/lib/js.jar new file mode 100644 index 00000000000..878b0d9422b Binary files /dev/null and b/js/js.backend/lib/js.jar differ diff --git a/js/js.backend/src/org/jetbrains/k2js/analyze/Analyzer.java b/js/js.backend/src/org/jetbrains/k2js/analyze/Analyzer.java new file mode 100644 index 00000000000..d0aba16b5a6 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/analyze/Analyzer.java @@ -0,0 +1,117 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.analyze; + +import com.google.common.base.Predicate; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiFile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.Configuration; +import org.jetbrains.jet.lang.cfg.pseudocode.JetControlFlowDataTraceFactory; +import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor; +import org.jetbrains.jet.lang.psi.JetFile; +import org.jetbrains.jet.lang.psi.JetPsiFactory; +import org.jetbrains.jet.lang.resolve.*; +import org.jetbrains.jet.lang.resolve.scopes.WritableScope; +import org.jetbrains.k2js.config.Config; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Pavel Talanov + */ +public final class Analyzer { + + private Analyzer() { + } + + @NotNull + public static BindingContext analyzeFilesAndCheckErrors(@NotNull List files, + @NotNull Config config) { + BindingContext bindingContext = analyzeFiles(files, config); + checkForErrors(withJsLibAdded(files, config), bindingContext); + return bindingContext; + } + + @NotNull + public static BindingContext analyzeFiles(@NotNull List files, + @NotNull Config config) { + Project project = config.getProject(); + return AnalyzingUtils.analyzeFiles(project, + JsConfiguration.jsLibConfiguration(project), + withJsLibAdded(files, config), + notLibFiles(config.getLibFiles()), + JetControlFlowDataTraceFactory.EMPTY); + } + + private static void checkForErrors(@NotNull List allFiles, @NotNull BindingContext bindingContext) { + AnalyzingUtils.throwExceptionOnErrors(bindingContext); + for (JetFile file : allFiles) { + AnalyzingUtils.checkForSyntacticErrors(file); + } + } + + //TODO: use some mechanism to avoid this + //TODO: move method somewhere + @NotNull + public static List withJsLibAdded(@NotNull List files, @NotNull Config config) { + List allFiles = new ArrayList(); + allFiles.addAll(files); + allFiles.addAll(config.getLibFiles()); + return allFiles; + } + + @NotNull + private static Predicate notLibFiles(@NotNull final List jsLibFiles) { + return new Predicate() { + @Override + public boolean apply(@Nullable PsiFile file) { + assert file instanceof JetFile; + @SuppressWarnings("UnnecessaryLocalVariable") boolean notLibFile = !jsLibFiles.contains(file); + return notLibFile; + } + }; + } + + private static final class JsConfiguration implements Configuration { + + @NotNull + private Project project; + + public static JsConfiguration jsLibConfiguration(@NotNull Project project) { + return new JsConfiguration(project); + } + + private JsConfiguration(@NotNull Project project) { + this.project = project; + } + + @Override + public void addDefaultImports(@NotNull BindingTrace trace, @NotNull WritableScope rootScope, + @NotNull Importer importer) { + ImportsResolver.ImportResolver importResolver = new ImportsResolver.ImportResolver(trace, true); + importResolver.processImportReference(JetPsiFactory.createImportDirective(project, "js.*"), rootScope, importer); + } + + @Override + public void extendNamespaceScope(@NotNull BindingTrace trace, @NotNull NamespaceDescriptor namespaceDescriptor, + @NotNull WritableScope namespaceMemberScope) { + } + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/config/Config.java b/js/js.backend/src/org/jetbrains/k2js/config/Config.java new file mode 100644 index 00000000000..9bef13e1220 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/config/Config.java @@ -0,0 +1,38 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.config; + +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.JetFile; + +import java.util.List; + +/** + * @author Pavel Talanov + *

+ * Base class reprenting a configuration of translator. + */ +public abstract class Config { + + @NotNull + public abstract Project getProject(); + + @NotNull + public abstract List getLibFiles(); + +} diff --git a/js/js.backend/src/org/jetbrains/k2js/config/IDEAConfig.java b/js/js.backend/src/org/jetbrains/k2js/config/IDEAConfig.java new file mode 100644 index 00000000000..b421601c274 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/config/IDEAConfig.java @@ -0,0 +1,49 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.config; + +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.JetFile; + +import java.util.Collections; +import java.util.List; + +/** + * @author Pavel Talanov + */ +public final class IDEAConfig extends Config { + + @NotNull + private final Project project; + + public IDEAConfig(@NotNull Project project) { + this.project = project; + } + + @NotNull + @Override + public Project getProject() { + return project; + } + + @NotNull + @Override + public List getLibFiles() { + return Collections.emptyList(); + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/config/TestConfig.java b/js/js.backend/src/org/jetbrains/k2js/config/TestConfig.java new file mode 100644 index 00000000000..68a91518455 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/config/TestConfig.java @@ -0,0 +1,126 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.config; + +import com.intellij.openapi.Disposable; +import com.intellij.openapi.project.Project; +import core.Dummy; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.compiler.JetCoreEnvironment; +import org.jetbrains.jet.lang.psi.JetFile; +import org.jetbrains.k2js.utils.JetFileUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * @author Pavel Talanov + */ +//TODO: review/refactor +public final class TestConfig extends Config { + + //TODO: provide some generic way to get the files of the project + @NotNull + private static final List LIB_FILE_NAMES = Arrays.asList( + "/core/annotations.kt", + "/jquery/common.kt", + "/jquery/ui.kt", + "/core/javautil.kt", + "/core/javalang.kt", + "/core/core.kt", + "/core/math.kt", + "/core/json.kt", + "/raphael/raphael.kt", + "/html5/canvas.kt", + "/html5/files.kt", + "/html5/image.kt" + ); + + @NotNull + private static JetCoreEnvironment getTestEnvironment() { + if (testOnlyEnvironment == null) { + testOnlyEnvironment = new JetCoreEnvironment(new Disposable() { + @Override + public void dispose() { + } + }); + } + return testOnlyEnvironment; + } + + @Nullable + private static /*var*/ JetCoreEnvironment testOnlyEnvironment = null; + + + @Nullable + private /*var*/ List jsLibFiles = null; + + public TestConfig() { + } + + @NotNull + @Override + public Project getProject() { + return getTestEnvironment().getProject(); + } + + @NotNull + private static List initLibFiles(@NotNull Project project) { + List libFiles = new ArrayList(); + for (String libFileName : LIB_FILE_NAMES) { + JetFile file = null; + //TODO: close stream? + InputStream stream = Dummy.class.getResourceAsStream(libFileName); + try { + String text = readString(stream); + file = JetFileUtils.createPsiFile(libFileName, text, project); + } catch (IOException e) { + e.printStackTrace(); + } + libFiles.add(file); + } + return libFiles; + } + + @NotNull + public List getLibFiles() { + if (jsLibFiles == null) { + jsLibFiles = initLibFiles(getProject()); + } + return jsLibFiles; + } + + @NotNull + private static String readString(@NotNull InputStream is) throws IOException { + char[] buf = new char[2048]; + Reader r = new InputStreamReader(is, "UTF-8"); + StringBuilder s = new StringBuilder(); + while (true) { + int n = r.read(buf); + if (n < 0) + break; + s.append(buf, 0, n); + } + return s.toString(); + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/facade/K2JSTranslator.java b/js/js.backend/src/org/jetbrains/k2js/facade/K2JSTranslator.java new file mode 100644 index 00000000000..86b330ea218 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/facade/K2JSTranslator.java @@ -0,0 +1,155 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.facade; + +import com.google.dart.compiler.backend.js.ast.JsProgram; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.JetFile; +import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.types.JetStandardLibrary; +import org.jetbrains.jet.plugin.JetMainDetector; +import org.jetbrains.k2js.analyze.Analyzer; +import org.jetbrains.k2js.config.Config; +import org.jetbrains.k2js.config.IDEAConfig; +import org.jetbrains.k2js.config.TestConfig; +import org.jetbrains.k2js.generate.CodeGenerator; +import org.jetbrains.k2js.translate.general.Translation; +import org.jetbrains.k2js.utils.GenerationUtils; +import org.jetbrains.k2js.utils.JetFileUtils; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.*; + +import static org.jetbrains.k2js.translate.utils.PsiUtils.getNamespaceName; +import static org.jetbrains.k2js.utils.JetFileUtils.createPsiFileList; + +//TODO: clean up the code + +/** + * @author Pavel Talanov + *

+ * An entry point of translator. + */ +public final class K2JSTranslator { + + public static void translateWithCallToMainAndSaveToFile(@NotNull List files, + @NotNull String outputPath, + @NotNull Project project) throws Exception { + K2JSTranslator translator = new K2JSTranslator(new IDEAConfig(project)); + String programCode = translator.generateProgramCode(files) + "\n"; + JetFile fileWithMain = JetMainDetector.getFileWithMain(files); + if (fileWithMain == null) { + throw new RuntimeException("No file with main detected."); + } + String callToMain = translator.generateCallToMain(fileWithMain, ""); + FileWriter writer = new FileWriter(new File(outputPath)); + writer.write(programCode + callToMain); + writer.close(); + } + + public static void testTranslateFile(@NotNull String inputFile, + @NotNull String outputFile) throws Exception { + testTranslateFiles(Collections.singletonList(inputFile), outputFile); + } + + public static void testTranslateFiles(@NotNull List inputFiles, + @NotNull String outputFile) throws Exception { + K2JSTranslator translator = new K2JSTranslator(new TestConfig()); + List psiFiles = createPsiFileList(inputFiles, translator.getProject()); + JsProgram program = translator.generateProgram(psiFiles); + saveProgramToFile(outputFile, program); + } + + private static void saveProgramToFile(@NotNull String outputFile, @NotNull JsProgram program) throws IOException { + CodeGenerator generator = new CodeGenerator(); + generator.generateToFile(program, new File(outputFile)); + } + + @NotNull + private Config config; + + + public K2JSTranslator(@NotNull Config config) { + this.config = config; + } + + //TODO: refactor + @NotNull + public String translateStringWithCallToMain(@NotNull String programText, @NotNull String argumentsString) { + JetFile file = JetFileUtils.createPsiFile("test", programText, getProject()); + String programCode = generateProgramCode(file) + "\n"; + String flushOutput = "Kotlin.System.flush();\n"; + String callToMain = generateCallToMain(file, argumentsString); + String programOutput = "Kotlin.System.output();\n"; + return programCode + flushOutput + callToMain + programOutput; + } + + @NotNull + private String generateProgramCode(@NotNull JetFile psiFile) { + JsProgram program = generateProgram(Arrays.asList(psiFile)); + CodeGenerator generator = new CodeGenerator(); + return generator.generateToString(program); + } + + @NotNull + private String generateProgramCode(@NotNull List files) { + JsProgram program = generateProgram(files); + CodeGenerator generator = new CodeGenerator(); + return generator.generateToString(program); + } + + @NotNull + public BindingContext analyzeProgramCode(@NotNull String programText) { + JetFile file = JetFileUtils.createPsiFile("test", programText, getProject()); + return Analyzer.analyzeFiles(Arrays.asList(file), config); + } + + + @NotNull + private JsProgram generateProgram(@NotNull List filesToTranslate) { + JetStandardLibrary.initialize(config.getProject()); + BindingContext bindingContext = Analyzer.analyzeFilesAndCheckErrors(filesToTranslate, config); + return Translation.generateAst(bindingContext, Analyzer.withJsLibAdded(filesToTranslate, config)); + } + + + @NotNull + public String generateCallToMain(@NotNull JetFile file, @NotNull String argumentString) { + String namespaceName = getNamespaceName(file); + List arguments = parseString(argumentString); + return GenerationUtils.generateCallToMain(namespaceName, arguments); + } + + @NotNull + private List parseString(@NotNull String argumentString) { + List result = new ArrayList(); + StringTokenizer stringTokenizer = new StringTokenizer(argumentString); + while (stringTokenizer.hasMoreTokens()) { + result.add(stringTokenizer.nextToken()); + } + return result; + } + + @NotNull + private Project getProject() { + return config.getProject(); + } + +} diff --git a/js/js.backend/src/org/jetbrains/k2js/facade/K2JSTranslatorUtils.java b/js/js.backend/src/org/jetbrains/k2js/facade/K2JSTranslatorUtils.java new file mode 100644 index 00000000000..8fafffc52ff --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/facade/K2JSTranslatorUtils.java @@ -0,0 +1,73 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.facade; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.k2js.config.TestConfig; + +/** + * Created by IntelliJ IDEA. + * User: Natalia.Ukhorskaya + * Date: 2/9/12 + * Time: 7:49 PM + */ + +public class K2JSTranslatorUtils { + @SuppressWarnings("FieldCanBeLocal") + private static String EXCEPTION = "exception="; + + @Nullable + public String translateToJS(@NotNull String code, @NotNull String arguments) { + try { + return generateJSCode(code, arguments); + } catch (AssertionError e) { + reportException(e); + return EXCEPTION + "Translation error."; + } catch (UnsupportedOperationException e) { + reportException(e); + return EXCEPTION + "Unsupported feature."; + } catch (Throwable e) { + reportException(e); + return EXCEPTION + "Unexpected exception."; + } + } + + @Nullable + public BindingContext getBindingContext(@NotNull String programText) { + try { + K2JSTranslator k2JSTranslator = new K2JSTranslator(new TestConfig()); + return k2JSTranslator.analyzeProgramCode(programText); + } catch (Throwable e) { + e.printStackTrace(); + reportException(e); + return null; + } + } + + @NotNull + private String generateJSCode(@NotNull String code, @NotNull String arguments) { + String generatedCode = (new K2JSTranslator(new TestConfig())).translateStringWithCallToMain(code, arguments); + return generatedCode; + } + + private void reportException(@NotNull Throwable e) { + System.out.println("Exception in translateToJS!!!"); + e.printStackTrace(); + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/generate/CodeGenerator.java b/js/js.backend/src/org/jetbrains/k2js/generate/CodeGenerator.java new file mode 100644 index 00000000000..ecb5d30b2c0 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/generate/CodeGenerator.java @@ -0,0 +1,58 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.generate; + +import com.google.dart.compiler.backend.js.JsSourceGenerationVisitor; +import com.google.dart.compiler.backend.js.ast.JsProgram; +import com.google.dart.compiler.util.DefaultTextOutput; +import com.google.dart.compiler.util.TextOutput; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; + +/** + * @author Pavel.Talanov + */ +public final class CodeGenerator { + + @NotNull + private final TextOutput output = new DefaultTextOutput(false); + + public CodeGenerator() { + } + + public void generateToFile(@NotNull JsProgram program, @NotNull File file) throws IOException { + generateCode(program); + FileWriter writer = new FileWriter(file); + writer.write(output.toString()); + writer.close(); + } + + @NotNull + public String generateToString(@NotNull JsProgram program) { + generateCode(program); + return output.toString(); + } + + private void generateCode(@NotNull JsProgram program) { + JsSourceGenerationVisitor sourceGenerator = + new JsSourceGenerationVisitor(output); + program.traverse(sourceGenerator, null); + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/context/Aliaser.java b/js/js.backend/src/org/jetbrains/k2js/translate/context/Aliaser.java new file mode 100644 index 00000000000..f77a65a7e0d --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/context/Aliaser.java @@ -0,0 +1,100 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.context; + +import com.google.dart.compiler.backend.js.ast.JsName; +import com.google.dart.compiler.backend.js.ast.JsNameRef; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; + +import java.util.HashMap; +import java.util.Map; +import java.util.Stack; + +public class Aliaser { + + public static Aliaser newInstance() { + return new Aliaser(); + } + + @NotNull + private final Map aliasesForDescriptors = new HashMap(); + @NotNull + private final Map> aliasesForThis + = new HashMap>(); + + private Aliaser() { + } + + @NotNull + public JsNameRef getAliasForThis(@NotNull DeclarationDescriptor descriptor) { + Stack aliasStack = aliasesForThis.get(descriptor.getOriginal()); + assert !aliasStack.empty(); + JsName aliasName = aliasStack.peek(); + assert aliasName != null : "This " + descriptor.getOriginal() + " doesn't have an alias."; + return aliasName.makeRef(); + } + + public void setAliasForThis(@NotNull DeclarationDescriptor descriptor, @NotNull JsName alias) { + Stack aliasStack = aliasesForThis.get(descriptor.getOriginal()); + if (aliasStack == null) { + aliasStack = new Stack(); + aliasesForThis.put(descriptor, aliasStack); + } + aliasStack.push(alias); + } + + public void removeAliasForThis(@NotNull DeclarationDescriptor descriptor) { + Stack aliasStack = aliasesForThis.get(descriptor.getOriginal()); + assert !aliasStack.empty(); + aliasStack.pop(); + if (aliasStack.empty()) { + aliasesForThis.put(descriptor, null); + } + } + + public boolean hasAliasForThis(@NotNull DeclarationDescriptor descriptor) { + Stack aliasStack = aliasesForThis.get(descriptor.getOriginal()); + if (aliasStack == null) { + return false; + } + return (!aliasStack.empty()); + } + + @NotNull + public JsName getAliasForDeclaration(@NotNull DeclarationDescriptor declaration) { + JsName alias = aliasesForDescriptors.get(declaration.getOriginal()); + assert alias != null : "Use has alias for declaration to check."; + return alias; + } + + public void setAliasForDescriptor(@NotNull DeclarationDescriptor declaration, @NotNull JsName alias) { + assert (!hasAliasForDeclaration(declaration.getOriginal())) : "This declaration already has an alias!"; + aliasesForDescriptors.put(declaration.getOriginal(), alias); + } + + public void removeAliasForDescriptor(@NotNull DeclarationDescriptor declaration) { + assert (hasAliasForDeclaration(declaration.getOriginal())) : "This declaration does not has an alias!"; + aliasesForDescriptors.remove(declaration.getOriginal()); + } + + public boolean hasAliasForDeclaration(@NotNull DeclarationDescriptor declaration) { + return aliasesForDescriptors.containsKey(declaration.getOriginal()); + } + + +} \ No newline at end of file diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/context/DynamicContext.java b/js/js.backend/src/org/jetbrains/k2js/translate/context/DynamicContext.java new file mode 100644 index 00000000000..7e09549fdb9 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/context/DynamicContext.java @@ -0,0 +1,84 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.context; + +import com.google.dart.compiler.backend.js.ast.*; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; + +public class DynamicContext { + + @NotNull + public static DynamicContext rootContext(@NotNull NamingScope rootScope, @NotNull JsBlock globalBlock) { + return new DynamicContext(rootScope, rootScope, globalBlock); + } + + @NotNull + public static DynamicContext contextWithScope(@NotNull NamingScope scope) { + return new DynamicContext(scope, scope, new JsBlock()); + } + + //TODO: current/block scope logic unclear. is it necessary? + @NotNull + private NamingScope currentScope; + + @NotNull + private NamingScope blockScope; + + @NotNull + private JsBlock currentBlock; + + private DynamicContext(@NotNull NamingScope scope, @NotNull NamingScope blockScope, @NotNull JsBlock block) { + this.currentScope = scope; + this.currentBlock = block; + this.blockScope = blockScope; + } + + @NotNull + public DynamicContext innerScope(@NotNull JsScope scope) { + return new DynamicContext(currentScope.innerScope(scope), blockScope, currentBlock); + } + + @NotNull + public NamingScope getScope() { + return blockScope; + } + + @NotNull + public DynamicContext innerBlock(@NotNull JsBlock block) { + return new DynamicContext(currentScope, currentScope, block); + } + + @NotNull + public TemporaryVariable declareTemporary(@NotNull JsExpression initExpression) { + JsName temporaryName = blockScope.declareTemporary(); + JsVars temporaryDeclaration = AstUtil.newVar(temporaryName, null); + jsBlock().addVarDeclaration(temporaryDeclaration); + return new TemporaryVariable(temporaryName, initExpression); + } + + + @NotNull + public JsScope jsScope() { + return currentScope.jsScope(); + } + + @NotNull + public JsBlock jsBlock() { + return currentBlock; + } +} \ No newline at end of file diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/context/Namer.java b/js/js.backend/src/org/jetbrains/k2js/translate/context/Namer.java new file mode 100644 index 00000000000..c92af4d1bac --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/context/Namer.java @@ -0,0 +1,170 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.context; + +import com.google.dart.compiler.backend.js.ast.JsName; +import com.google.dart.compiler.backend.js.ast.JsNameRef; +import com.google.dart.compiler.backend.js.ast.JsScope; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; + +/** + * @author Pavel Talanov + *

+ * Encapuslates different types of constants and naming conventions. + */ +public final class Namer { + + private static final String INITIALIZE_METHOD_NAME = "initialize"; + private static final String CLASS_OBJECT_NAME = "Class"; + private static final String TRAIT_OBJECT_NAME = "Trait"; + private static final String NAMESPACE_OBJECT_NAME = "Namespace"; + private static final String OBJECT_OBJECT_NAME = "object"; + private static final String SETTER_PREFIX = "set_"; + private static final String GETTER_PREFIX = "get_"; + private static final String BACKING_FIELD_PREFIX = "$"; + private static final String SUPER_METHOD_NAME = "super_init"; + private static final String KOTLIN_OBJECT_NAME = "Kotlin"; + private static final String ANONYMOUS_NAMESPACE = "Anonymous"; + private static final String RECEIVER_PARAMETER_NAME = "receiver"; + + @NotNull + public static String getReceiverParameterName() { + return RECEIVER_PARAMETER_NAME; + } + + @NotNull + public static String getAnonymousNamespaceName() { + return ANONYMOUS_NAMESPACE; + } + + @NotNull + public static JsNameRef initializeMethodReference() { + return AstUtil.newQualifiedNameRef(INITIALIZE_METHOD_NAME); + } + + @NotNull + public static String superMethodName() { + return SUPER_METHOD_NAME; + } + + @NotNull + public static String nameForClassesVariable() { + return "classes"; + } + + @NotNull + public static String getNameForAccessor(@NotNull String propertyName, boolean isGetter) { + if (isGetter) { + return getNameForGetter(propertyName); + } else { + return getNameForSetter(propertyName); + } + } + + public static String getKotlinBackingFieldName(String propertyName) { + return getNameWithPrefix(propertyName, BACKING_FIELD_PREFIX); + } + + public static String getNameForGetter(String propertyName) { + return getNameWithPrefix(propertyName, GETTER_PREFIX); + } + + public static String getNameForSetter(String propertyName) { + return getNameWithPrefix(propertyName, SETTER_PREFIX); + } + + private static String getNameWithPrefix(String name, String prefix) { + return prefix + name; + } + + public static Namer newInstance(@NotNull JsScope rootScope) { + return new Namer(rootScope); + } + + @NotNull + private final JsName kotlinName; + @NotNull + private final JsScope kotlinScope; + @NotNull + private final JsName className; + @NotNull + private final JsName traitName; + @NotNull + private final JsName namespaceName; + @NotNull + private final JsName objectName; + + private Namer(@NotNull JsScope rootScope) { + kotlinName = rootScope.declareName(KOTLIN_OBJECT_NAME); + kotlinScope = new JsScope(rootScope, "Kotlin standard object"); + traitName = kotlinScope.declareName(TRAIT_OBJECT_NAME); + namespaceName = kotlinScope.declareName(NAMESPACE_OBJECT_NAME); + className = kotlinScope.declareName(CLASS_OBJECT_NAME); + objectName = kotlinScope.declareName(OBJECT_OBJECT_NAME); + } + + @NotNull + public JsNameRef classCreationMethodReference() { + return kotlin(createMethodReference(className)); + } + + @NotNull + public JsNameRef traitCreationMethodReference() { + return kotlin(createMethodReference(traitName)); + } + + @NotNull + public JsNameRef namespaceCreationMethodReference() { + return kotlin(createMethodReference(namespaceName)); + } + + @NotNull + public JsNameRef objectCreationMethodReference() { + return kotlin(createMethodReference(objectName)); + } + + @NotNull + private JsNameRef createMethodReference(@NotNull JsName name) { + JsNameRef qualifier = name.makeRef(); + JsNameRef reference = AstUtil.newQualifiedNameRef("create"); + AstUtil.setQualifier(reference, qualifier); + return reference; + } + + @NotNull + private JsNameRef kotlin(@NotNull JsNameRef reference) { + JsNameRef kotlinReference = kotlinName.makeRef(); + AstUtil.setQualifier(reference, kotlinReference); + return reference; + } + + @NotNull + public JsNameRef kotlinObject() { + return kotlinName.makeRef(); + } + + @NotNull + public JsNameRef isOperationReference() { + return kotlin(AstUtil.newQualifiedNameRef("isType")); + } + + @NotNull + /*package*/ JsScope getKotlinScope() { + return kotlinScope; + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/context/NamingScope.java b/js/js.backend/src/org/jetbrains/k2js/translate/context/NamingScope.java new file mode 100644 index 00000000000..650f0ffb5b1 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/context/NamingScope.java @@ -0,0 +1,98 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.context; + +import com.google.dart.compiler.backend.js.ast.JsName; +import com.google.dart.compiler.backend.js.ast.JsScope; +import org.jetbrains.annotations.NotNull; + +/** + * @author Pavel Talanov + *

+ * Basically a wrapper around JsScope. + */ +public final class NamingScope { + + @NotNull + public static NamingScope rootScope(@NotNull JsScope rootScope) { + return new NamingScope(rootScope); + } + + @NotNull + private final JsScope scope; + + private NamingScope(@NotNull JsScope correspondingScope) { + this.scope = correspondingScope; + } + + @NotNull + public NamingScope innerScope(@NotNull String scopeName) { + JsScope innerJsScope = new JsScope(jsScope(), scopeName); + return new NamingScope(innerJsScope); + } + + @NotNull + public NamingScope innerScope(@NotNull JsScope correspondingScope) { + return new NamingScope(correspondingScope); + } + + @NotNull + /*package*/ JsName declareUnobfuscatableName(@NotNull String name) { + JsName declaredName = scope.declareName(name); + declaredName.setObfuscatable(false); + return declaredName; + } + + @NotNull + /*package*/ JsName declareObfuscatableName(@NotNull String name) { + return scope.declareName(mayBeObfuscateName(name, true)); + } + + //TODO: temporary solution + @NotNull + private String mayBeObfuscateName(@NotNull String name, boolean shouldObfuscate) { + if (!shouldObfuscate) { + return name; + } + return doObfuscate(name); + } + + @NotNull + private String doObfuscate(@NotNull String name) { + int obfuscate = 0; + String result = name; + while (true) { + JsName existingNameWithSameIdent = scope.findExistingName(result); + boolean isDuplicate = (existingNameWithSameIdent != null) && (scope.ownsName(existingNameWithSameIdent)); + + if (!isDuplicate) break; + + result = name + "$" + obfuscate; + obfuscate++; + } + return result; + } + + public JsName declareTemporary() { + return scope.declareTemporary(); + } + + @NotNull + public JsScope jsScope() { + return scope; + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/context/StandardClasses.java b/js/js.backend/src/org/jetbrains/k2js/translate/context/StandardClasses.java new file mode 100644 index 00000000000..b1d0e5e8fef --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/context/StandardClasses.java @@ -0,0 +1,180 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.context; + +import com.google.dart.compiler.backend.js.ast.JsName; +import com.google.dart.compiler.backend.js.ast.JsScope; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; + +import java.util.HashMap; +import java.util.Map; + +import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getFQName; + +/** + * @author Pavel Talanov + *

+ * Provides a mechanism to bind some of the kotlin/java declations with library implementations. + * Makes sense only for those declaration that cannot be annotated. (Use library annotation in this case) + */ +public final class StandardClasses { + + private final class Builder { + + @Nullable + private /*var*/ String currentFQName = null; + @Nullable + private /*var*/ String currentObjectName = null; + + @NotNull + public Builder forFQ(@NotNull String classFQName) { + currentFQName = classFQName; + return this; + } + + @NotNull + public Builder kotlinClass(@NotNull String kotlinName) { + kotlinTopLevelObject(kotlinName); + constructor(); + return this; + } + + private void kotlinTopLevelObject(@NotNull String kotlinName) { + assert currentFQName != null; + currentObjectName = kotlinName; + declareKotlinObject(currentFQName, kotlinName); + } + + @NotNull + public Builder kotlinFunction(@NotNull String kotlinName) { + kotlinTopLevelObject(kotlinName); + return this; + } + + @NotNull + private Builder constructor() { + assert currentFQName != null; + assert currentObjectName != null; + declareInner(currentFQName, "", currentObjectName); + return this; + } + + @NotNull + public Builder methods(@NotNull String... methodNames) { + assert currentFQName != null; + declareMethods(currentFQName, methodNames); + return this; + } + + @NotNull + public Builder properties(@NotNull String... propertyNames) { + assert currentFQName != null; + declareReadonlyProperties(currentFQName, propertyNames); + return this; + } + } + + @NotNull + public static StandardClasses bindImplementations(@NotNull JsScope kotlinObjectScope) { + StandardClasses standardClasses = new StandardClasses(kotlinObjectScope); + declareJetObjects(standardClasses); + return standardClasses; + } + + private static void declareJetObjects(@NotNull StandardClasses standardClasses) { + + standardClasses.declare().forFQ("jet.Iterator").kotlinClass("ArrayIteratorIntrinsic") + .methods("next").properties("hasNext"); + + standardClasses.declare().forFQ("jet.IntRange").kotlinClass("NumberRange") + .methods("iterator", "contains").properties("start", "size", "end", "reversed"); + + standardClasses.declare().forFQ("jet.sure").kotlinFunction("sure"); + + standardClasses.declare().forFQ("jet.Any.toString").kotlinFunction("toString"); + + standardClasses.declare().forFQ("java.util.Collections..max").kotlinFunction("collectionsMax"); + } + + + @NotNull + private final JsScope kotlinScope; + + + @NotNull + private final Map standardObjects = new HashMap(); + + @NotNull + private final Map scopeMap = new HashMap(); + + private StandardClasses(@NotNull JsScope kotlinScope) { + this.kotlinScope = kotlinScope; + } + + private void declareTopLevelObjectInScope(@NotNull JsScope scope, @NotNull Map map, + @NotNull String fullQualifiedName, @NotNull String name) { + JsName declaredName = scope.declareName(name); + declaredName.setObfuscatable(false); + map.put(fullQualifiedName, declaredName); + scopeMap.put(fullQualifiedName, new JsScope(scope, "scope for " + name)); + } + + private void declareKotlinObject(@NotNull String fullQualifiedName, @NotNull String kotlinLibName) { + declareTopLevelObjectInScope(kotlinScope, standardObjects, fullQualifiedName, kotlinLibName); + } + + private void declareInner(@NotNull String fullQualifiedClassName, + @NotNull String shortMethodName, + @NotNull String javascriptName) { + JsScope classScope = scopeMap.get(fullQualifiedClassName); + assert classScope != null; + String fullQualifiedMethodName = fullQualifiedClassName + "." + shortMethodName; + JsName declaredName = classScope.declareName(javascriptName); + declaredName.setObfuscatable(false); + standardObjects.put(fullQualifiedMethodName, declaredName); + } + + private void declareMethods(@NotNull String classFQName, + @NotNull String... methodNames) { + for (String methodName : methodNames) { + declareInner(classFQName, methodName, methodName); + } + } + + private void declareReadonlyProperties(@NotNull String classFQName, + @NotNull String... propertyNames) { + for (String propertyName : propertyNames) { + declareInner(classFQName, propertyName, propertyName); + } + } + + public boolean isStandardObject(@NotNull DeclarationDescriptor descriptor) { + return standardObjects.containsKey(getFQName(descriptor)); + } + + @NotNull + public JsName getStandardObjectName(@NotNull DeclarationDescriptor descriptor) { + return standardObjects.get(getFQName(descriptor)); + } + + @NotNull + private Builder declare() { + return new Builder(); + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/context/StaticContext.java b/js/js.backend/src/org/jetbrains/k2js/translate/context/StaticContext.java new file mode 100644 index 00000000000..32a0065efb2 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/context/StaticContext.java @@ -0,0 +1,478 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.context; + +import com.google.dart.compiler.backend.js.ast.JsName; +import com.google.dart.compiler.backend.js.ast.JsNameRef; +import com.google.dart.compiler.backend.js.ast.JsProgram; +import com.google.dart.compiler.backend.js.ast.JsRootScope; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.*; +import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor; +import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.types.JetStandardLibrary; +import org.jetbrains.k2js.translate.context.generator.Generator; +import org.jetbrains.k2js.translate.context.generator.Rule; +import org.jetbrains.k2js.translate.intrinsic.Intrinsics; +import org.jetbrains.k2js.translate.utils.AnnotationsUtils; + +import java.util.Set; + +import static org.jetbrains.k2js.translate.utils.AnnotationsUtils.*; +import static org.jetbrains.k2js.translate.utils.DescriptorUtils.*; + +/** + * @author Pavel Talanov + *

+ * Aggregates all the static parts of the context. + */ +public final class StaticContext { + + public static StaticContext generateStaticContext(@NotNull JetStandardLibrary library, + @NotNull BindingContext bindingContext) { + JsProgram program = new JsProgram("main"); + JsRootScope jsRootScope = program.getRootScope(); + Namer namer = Namer.newInstance(jsRootScope); + Aliaser aliaser = Aliaser.newInstance(); + NamingScope scope = NamingScope.rootScope(jsRootScope); + Intrinsics intrinsics = Intrinsics.standardLibraryIntrinsics(library); + StandardClasses standardClasses = + StandardClasses.bindImplementations(namer.getKotlinScope()); + return new StaticContext(program, bindingContext, aliaser, + namer, intrinsics, standardClasses, scope); + } + + @NotNull + private final JsProgram program; + + @NotNull + private final BindingContext bindingContext; + + @NotNull + private final Aliaser aliaser; + + @NotNull + private final Namer namer; + + @NotNull + private final Intrinsics intrinsics; + + @NotNull + private final StandardClasses standardClasses; + + @NotNull + private final NamingScope rootScope; + + @NotNull + private final Generator names = new NameGenerator(); + @NotNull + private final Generator scopes = new ScopeGenerator(); + @NotNull + private final Generator qualifiers = new QualifierGenerator(); + @NotNull + private final Generator qualifierIsNull = new QualifierIsNullGenerator(); + + + //TODO: too many parameters in constructor + private StaticContext(@NotNull JsProgram program, @NotNull BindingContext bindingContext, + @NotNull Aliaser aliaser, + @NotNull Namer namer, @NotNull Intrinsics intrinsics, + @NotNull StandardClasses standardClasses, @NotNull NamingScope rootScope) { + this.program = program; + this.bindingContext = bindingContext; + this.aliaser = aliaser; + this.namer = namer; + this.intrinsics = intrinsics; + this.rootScope = rootScope; + this.standardClasses = standardClasses; + } + + @NotNull + public JsProgram getProgram() { + return program; + } + + @NotNull + public BindingContext getBindingContext() { + return bindingContext; + } + + @NotNull + public Aliaser getAliaser() { + return aliaser; + } + + @NotNull + public Intrinsics getIntrinsics() { + return intrinsics; + } + + @NotNull + public Namer getNamer() { + return namer; + } + + @NotNull + public NamingScope getRootScope() { + return rootScope; + } + + @NotNull + public StandardClasses getStandardClasses() { + return standardClasses; + } + + @NotNull + public NamingScope getScopeForDescriptor(@NotNull DeclarationDescriptor descriptor) { + NamingScope namingScope = scopes.get(descriptor); + assert namingScope != null : "Must have a scope for descriptor"; + return namingScope; + } + + @NotNull + public JsName getNameForDescriptor(@NotNull DeclarationDescriptor descriptor) { + JsName name = names.get(descriptor.getOriginal()); + assert name != null : "Must have name for descriptor"; + return name; + } + + private final class NameGenerator extends Generator { + public NameGenerator() { + Rule namesForStandardClasses = new Rule() { + @Override + @Nullable + public JsName apply(@NotNull DeclarationDescriptor data) { + if (!standardClasses.isStandardObject(data)) { + return null; + } + return standardClasses.getStandardObjectName(data); + } + }; + Rule namespacesShouldBeDefinedInRootScope = new Rule() { + @Override + @Nullable + public JsName apply(@NotNull DeclarationDescriptor descriptor) { + if (!(descriptor instanceof NamespaceDescriptor)) { + return null; + } + String nameForNamespace = getNameForNamespace((NamespaceDescriptor) descriptor); + return getRootScope().declareUnobfuscatableName(nameForNamespace); + } + }; + Rule memberDeclarationsInsideParentsScope = new Rule() { + @Override + @Nullable + public JsName apply(@NotNull DeclarationDescriptor descriptor) { + NamingScope namingScope = getEnclosingScope(descriptor); + return namingScope.declareObfuscatableName(descriptor.getName()); + } + }; + Rule constructorHasTheSameNameAsTheClass = new Rule() { + @Override + public JsName apply(@NotNull DeclarationDescriptor descriptor) { + if (!(descriptor instanceof ConstructorDescriptor)) { + return null; + } + ClassDescriptor containingClass = getContainingClass(descriptor); + assert containingClass != null : "Can't have constructor without a class"; + return getNameForDescriptor(containingClass); + } + }; + Rule accessorsHasNamesWithSpecialPrefixes = new Rule() { + @Override + public JsName apply(@NotNull DeclarationDescriptor descriptor) { + if (!(descriptor instanceof PropertyAccessorDescriptor)) { + return null; + } + boolean isGetter = descriptor instanceof PropertyGetterDescriptor; + String propertyName = ((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty().getName(); + String accessorName = Namer.getNameForAccessor(propertyName, isGetter); + NamingScope enclosingScope = getEnclosingScope(descriptor); + return enclosingScope.declareObfuscatableName(accessorName); + } + }; + + Rule namesAnnotatedAsLibraryHasUnobfuscatableNames = new Rule() { + @Override + public JsName apply(@NotNull DeclarationDescriptor descriptor) { + //TODO: refactor + String name = null; + AnnotationDescriptor annotation = getAnnotationByName(descriptor, LIBRARY_ANNOTATION_FQNAME); + if (annotation != null) { + name = AnnotationsUtils.getAnnotationStringParameter(descriptor, LIBRARY_ANNOTATION_FQNAME); + name = (!name.isEmpty()) ? name : descriptor.getName(); + } else { + ClassDescriptor containingClass = getContainingClass(descriptor); + if (containingClass == null) return null; + if (getAnnotationByName(containingClass, LIBRARY_ANNOTATION_FQNAME) != null) { + name = descriptor.getName(); + } + } + if (name != null) { + return getEnclosingScope(descriptor).declareUnobfuscatableName(name); + } + return null; + } + }; + Rule propertiesCorrespondToSpeciallyTreatedBackingFieldNames = new Rule() { + @Override + public JsName apply(@NotNull DeclarationDescriptor descriptor) { + if (!(descriptor instanceof PropertyDescriptor)) { + return null; + } + NamingScope enclosingScope = getEnclosingScope(descriptor); + return enclosingScope.declareObfuscatableName(Namer.getKotlinBackingFieldName(descriptor.getName())); + } + }; + //TODO: hack! + Rule toStringHack = new Rule() { + @Override + public JsName apply(@NotNull DeclarationDescriptor descriptor) { + if (!(descriptor instanceof FunctionDescriptor)) { + return null; + } + if (!descriptor.getName().equals("toString")) { + return null; + } + if (((FunctionDescriptor) descriptor).getValueParameters().isEmpty()) { + return getEnclosingScope(descriptor).declareUnobfuscatableName("toString"); + } + return null; + } + + }; + Rule namesForNativeObjectsAreUnobfuscatable = new Rule() { + @Override + public JsName apply(@NotNull DeclarationDescriptor descriptor) { + String name = null; + AnnotationDescriptor annotation = getAnnotationByName(descriptor, NATIVE_ANNOTATION_FQNAME); + if (annotation != null) { + name = AnnotationsUtils.getAnnotationStringParameter(descriptor, NATIVE_ANNOTATION_FQNAME); + name = (!name.isEmpty()) ? name : descriptor.getName(); + } else { + ClassDescriptor containingClass = getContainingClass(descriptor); + if (containingClass == null) return null; + if (getAnnotationByName(containingClass, NATIVE_ANNOTATION_FQNAME) != null) { + name = descriptor.getName(); + } + } + if (name != null) { + return getEnclosingScope(descriptor).declareUnobfuscatableName(name); + } + return null; + + } + }; + Rule overridingDescriptorsReferToOriginalName = new Rule() { + @Override + public JsName apply(@NotNull DeclarationDescriptor descriptor) { + //TODO: refactor + if (descriptor instanceof FunctionDescriptor) { + Set overriddenDescriptors = ((FunctionDescriptor) descriptor).getOverriddenDescriptors(); + if (overriddenDescriptors.isEmpty()) { + return null; + } else { + //assert overriddenDescriptors.size() == 1; + //TODO: for now translator can't deal with multiple inheritance good enough + for (FunctionDescriptor overriddenDescriptor : overriddenDescriptors) { + return getNameForDescriptor(overriddenDescriptor); + } + } + } + return null; + } + }; + addRule(namesForStandardClasses); + addRule(constructorHasTheSameNameAsTheClass); + addRule(namesAnnotatedAsLibraryHasUnobfuscatableNames); + addRule(namesForNativeObjectsAreUnobfuscatable); + addRule(toStringHack); + addRule(propertiesCorrespondToSpeciallyTreatedBackingFieldNames); + addRule(namespacesShouldBeDefinedInRootScope); + addRule(accessorsHasNamesWithSpecialPrefixes); + addRule(overridingDescriptorsReferToOriginalName); + addRule(memberDeclarationsInsideParentsScope); + } + } + + private NamingScope getEnclosingScope(@NotNull DeclarationDescriptor descriptor) { + DeclarationDescriptor containingDeclaration = getContainingDeclaration(descriptor); + return getScopeForDescriptor(containingDeclaration.getOriginal()); + } + + + private final class ScopeGenerator extends Generator { + + public ScopeGenerator() { + Rule generateNewScopesForClassesWithNoAncestors = new Rule() { + @Override + public NamingScope apply(@NotNull DeclarationDescriptor descriptor) { + if (!(descriptor instanceof ClassDescriptor)) { + return null; + } + if (getSuperclass((ClassDescriptor) descriptor) == null) { + return getRootScope().innerScope("Scope for class " + descriptor.getName()); + } + return null; + } + }; + Rule generateInnerScopesForDerivedClasses = new Rule() { + @Override + public NamingScope apply(@NotNull DeclarationDescriptor descriptor) { + if (!(descriptor instanceof ClassDescriptor)) { + return null; + } + ClassDescriptor superclass = getSuperclass((ClassDescriptor) descriptor); + if (superclass == null) { + return null; + } + return getScopeForDescriptor(superclass).innerScope("Scope for class " + descriptor.getName()); + } + }; + Rule generateNewScopesForNamespaceDescriptors = new Rule() { + @Override + public NamingScope apply(@NotNull DeclarationDescriptor descriptor) { + if (!(descriptor instanceof NamespaceDescriptor)) { + return null; + } + return getRootScope().innerScope("Namespace " + descriptor.getName()); + } + }; + Rule generateInnerScopesForMembers = new Rule() { + @Override + public NamingScope apply(@NotNull DeclarationDescriptor descriptor) { + NamingScope enclosingScope = getEnclosingScope(descriptor); + return enclosingScope.innerScope("Scope for member " + descriptor.getName()); + } + }; + addRule(generateNewScopesForClassesWithNoAncestors); + addRule(generateInnerScopesForDerivedClasses); + addRule(generateNewScopesForNamespaceDescriptors); + addRule(generateInnerScopesForMembers); + } + } + + @Nullable + public JsNameRef getQualifierForDescriptor(@NotNull DeclarationDescriptor descriptor) { + if (qualifierIsNull.get(descriptor) != null) { + return null; + } + return qualifiers.get(descriptor.getOriginal()); + } + + private final class QualifierGenerator extends Generator { + public QualifierGenerator() { + Rule namespacesHaveNoQualifiers = new Rule() { + @Override + public JsNameRef apply(@NotNull DeclarationDescriptor descriptor) { + if (!standardClasses.isStandardObject(descriptor)) { + return null; + } + return namer.kotlinObject(); + } + }; + Rule namespaceLevelDeclarationsHaveEnclosingNamespacesNamesAsQualifier = new Rule() { + @Override + public JsNameRef apply(@NotNull DeclarationDescriptor descriptor) { + DeclarationDescriptor containingDeclaration = getContainingDeclaration(descriptor); + if (!(containingDeclaration instanceof NamespaceDescriptor)) { + return null; + } + JsName containingDeclarationName = getNameForDescriptor(containingDeclaration); + return containingDeclarationName.makeRef(); + } + }; + Rule constructorHaveTheSameQualifierAsTheClass = new Rule() { + @Override + public JsNameRef apply(@NotNull DeclarationDescriptor descriptor) { + if (!(descriptor instanceof ConstructorDescriptor)) { + return null; + } + ClassDescriptor containingClass = getContainingClass(descriptor); + assert containingClass != null : "Can't have constructor without a class"; + return getQualifierForDescriptor(containingClass); + } + }; + Rule libraryObjectsHaveKotlinQualifier = new Rule() { + + @Override + public JsNameRef apply(@NotNull DeclarationDescriptor descriptor) { + if (getAnnotationByName(descriptor, AnnotationsUtils.LIBRARY_ANNOTATION_FQNAME) != null) { + return namer.kotlinObject(); + } + return null; + } + }; + Rule membersOfAnnotatedClassesHaveKotlinQualifier = new Rule() { + @Override + public JsNameRef apply(@NotNull DeclarationDescriptor descriptor) { + ClassDescriptor containingClass = getContainingClass(descriptor); + if (containingClass == null) { + return null; + } + if (getAnnotationByName(descriptor, LIBRARY_ANNOTATION_FQNAME) != null) { + return namer.kotlinObject(); + } + return null; + } + }; + addRule(libraryObjectsHaveKotlinQualifier); + addRule(membersOfAnnotatedClassesHaveKotlinQualifier); + addRule(constructorHaveTheSameQualifierAsTheClass); + addRule(namespacesHaveNoQualifiers); + addRule(namespaceLevelDeclarationsHaveEnclosingNamespacesNamesAsQualifier); + } + } + + private class QualifierIsNullGenerator extends Generator { + + private QualifierIsNullGenerator() { + Rule propertiesHaveNoQualifiers = new Rule() { + @Override + public Boolean apply(@NotNull DeclarationDescriptor descriptor) { + if (!(descriptor instanceof PropertyDescriptor)) { + return null; + } + return true; + } + }; + Rule variableAsFunctionsHaveNoQualifiers = new Rule() { + @Override + public Boolean apply(@NotNull DeclarationDescriptor descriptor) { + if (!isVariableAsFunction(descriptor)) { + return null; + } + return true; + } + }; + //TODO: hack! + Rule nativeObjectsHaveNoQualifiers = new Rule() { + @Override + public Boolean apply(@NotNull DeclarationDescriptor descriptor) { + if (!AnnotationsUtils.isNativeObject(descriptor)) { + return null; + } + return true; + } + }; + addRule(propertiesHaveNoQualifiers); + addRule(variableAsFunctionsHaveNoQualifiers); + addRule(nativeObjectsHaveNoQualifiers); + } + } +} \ No newline at end of file diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/context/TemporaryVariable.java b/js/js.backend/src/org/jetbrains/k2js/translate/context/TemporaryVariable.java new file mode 100644 index 00000000000..265d2653d7b --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/context/TemporaryVariable.java @@ -0,0 +1,54 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.context; + +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsName; +import com.google.dart.compiler.backend.js.ast.JsNameRef; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; + +/** + * @author Pavel Talanov + */ +public final class TemporaryVariable { + + @NotNull + private final JsExpression assignmentExpression; + @NotNull + private final JsName variableName; + + /*package*/ TemporaryVariable(@NotNull JsName temporaryName, @NotNull JsExpression initExpression) { + this.variableName = temporaryName; + this.assignmentExpression = AstUtil.newAssignment(variableName.makeRef(), initExpression); + } + + @NotNull + public JsNameRef reference() { + return variableName.makeRef(); + } + + @NotNull + public JsName name() { + return variableName; + } + + @NotNull + public JsExpression assignmentExpression() { + return assignmentExpression; + } +} \ No newline at end of file diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/context/TranslationContext.java b/js/js.backend/src/org/jetbrains/k2js/translate/context/TranslationContext.java new file mode 100644 index 00000000000..e28a0332e0b --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/context/TranslationContext.java @@ -0,0 +1,155 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.context; + +import com.google.dart.compiler.backend.js.ast.*; +import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; +import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.k2js.translate.intrinsic.Intrinsics; + +import static org.jetbrains.k2js.translate.utils.BindingUtils.getDescriptorForElement; + +/** + * @author Pavel Talanov + *

+ * All the info about the state of the translation process. + */ +public final class TranslationContext { + + @NotNull + private final DynamicContext dynamicContext; + @NotNull + private final StaticContext staticContext; + + @NotNull + public static TranslationContext rootContext(@NotNull StaticContext staticContext) { + JsProgram program = staticContext.getProgram(); + JsBlock globalBlock = program.getGlobalBlock(); + return new TranslationContext(staticContext, + DynamicContext.rootContext(staticContext.getRootScope(), globalBlock)); + } + + private TranslationContext(@NotNull StaticContext staticContext, @NotNull DynamicContext dynamicContext) { + this.dynamicContext = dynamicContext; + this.staticContext = staticContext; + } + + @NotNull + public TranslationContext contextWithScope(@NotNull NamingScope newScope) { + return new TranslationContext(staticContext, DynamicContext.contextWithScope(newScope)); + } + + // Note: Should be used ONLY if scope has no corresponding descriptor + @NotNull + public TranslationContext innerJsScope(@NotNull JsScope enclosingScope) { + return new TranslationContext(staticContext, dynamicContext.innerScope(enclosingScope)); + } + + @NotNull + public TranslationContext innerBlock(@NotNull JsBlock block) { + return new TranslationContext(staticContext, dynamicContext.innerBlock(block)); + } + + @NotNull + public TranslationContext newDeclaration(@NotNull DeclarationDescriptor descriptor) { + return contextWithScope(getScopeForDescriptor(descriptor)); + } + + @NotNull + public TranslationContext newDeclaration(@NotNull PsiElement element) { + return newDeclaration(getDescriptorForElement(bindingContext(), element)); + } + + @NotNull + public BindingContext bindingContext() { + return staticContext.getBindingContext(); + } + + @NotNull + public NamingScope getScopeForDescriptor(@NotNull DeclarationDescriptor descriptor) { + return staticContext.getScopeForDescriptor(descriptor); + } + + @NotNull + public NamingScope getScopeForElement(@NotNull PsiElement element) { + DeclarationDescriptor descriptorForElement = getDescriptorForElement(bindingContext(), element); + return getScopeForDescriptor(descriptorForElement); + } + + @NotNull + public JsName getNameForElement(@NotNull PsiElement element) { + DeclarationDescriptor descriptor = getDescriptorForElement(bindingContext(), element); + return getNameForDescriptor(descriptor); + } + + @NotNull + public JsName getNameForDescriptor(@NotNull DeclarationDescriptor descriptor) { + return staticContext.getNameForDescriptor(descriptor); + } + + @Nullable + public JsNameRef getQualifierForDescriptor(@NotNull DeclarationDescriptor descriptor) { + return staticContext.getQualifierForDescriptor(descriptor.getOriginal()); + } + + @NotNull + public TemporaryVariable declareTemporary(@NotNull JsExpression initExpression) { + return dynamicContext.declareTemporary(initExpression); + } + + @NotNull + public Aliaser aliaser() { + return staticContext.getAliaser(); + } + + @NotNull + public Namer namer() { + return staticContext.getNamer(); + } + + @NotNull + public Intrinsics intrinsics() { + return staticContext.getIntrinsics(); + } + + @NotNull + public JsProgram program() { + return staticContext.getProgram(); + } + + @NotNull + private StandardClasses standardClasses() { + return staticContext.getStandardClasses(); + } + + @NotNull + public NamingScope getScope() { + return dynamicContext.getScope(); + } + + @NotNull + public JsScope jsScope() { + return dynamicContext.jsScope(); + } + + public void addStatementToCurrentBlock(@NotNull JsStatement statement) { + dynamicContext.jsBlock().addStatement(statement); + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/context/generator/Generator.java b/js/js.backend/src/org/jetbrains/k2js/translate/context/generator/Generator.java new file mode 100644 index 00000000000..720e3a8d7e1 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/context/generator/Generator.java @@ -0,0 +1,65 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.context.generator; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; + +import java.util.List; +import java.util.Map; + +/** + * @author Pavel Talanov + */ +public class Generator { + + + @NotNull + private final Map values = Maps.newHashMap(); + @NotNull + private final List> rules = Lists.newArrayList(); + + public void addRule(@NotNull Rule rule) { + rules.add(rule); + } + + @Nullable + public V get(@NotNull DeclarationDescriptor descriptor) { + V result = values.get(descriptor); + if (result != null) { + return result; + } + result = generate(descriptor); + values.put(descriptor, result); + return result; + } + + @Nullable + private V generate(@NotNull DeclarationDescriptor descriptor) { + V result = null; + for (Rule rule : rules) { + result = rule.apply(descriptor); + if (result != null) { + return result; + } + } + return result; + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/context/generator/Rule.java b/js/js.backend/src/org/jetbrains/k2js/translate/context/generator/Rule.java new file mode 100644 index 00000000000..e3550578ef5 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/context/generator/Rule.java @@ -0,0 +1,30 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.context.generator; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; + +/** + * @author Pavel Talanov + */ +public interface Rule { + + @Nullable + V apply(@NotNull DeclarationDescriptor descriptor); +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/declaration/ClassDeclarationTranslator.java b/js/js.backend/src/org/jetbrains/k2js/translate/declaration/ClassDeclarationTranslator.java new file mode 100644 index 00000000000..e4daf2a6300 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/declaration/ClassDeclarationTranslator.java @@ -0,0 +1,167 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.declaration; + +import com.google.dart.compiler.backend.js.ast.*; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.ClassDescriptor; +import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor; +import org.jetbrains.jet.lang.psi.JetClass; +import org.jetbrains.k2js.translate.context.Namer; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.general.AbstractTranslator; +import org.jetbrains.k2js.translate.general.Translation; +import org.jetbrains.k2js.translate.utils.BindingUtils; +import org.jetbrains.k2js.translate.utils.ClassSorter; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.jetbrains.k2js.translate.utils.DescriptorUtils.getAllClassesDefinedInNamespace; + +/** + * @author Pavel Talanov + *

+ * Generates a big block where are all the classes(objects representing them) are created. + */ +public final class ClassDeclarationTranslator extends AbstractTranslator { + + @NotNull + private final List descriptors; + @NotNull + private final Map localToGlobalClassName; + @NotNull + private final JsScope dummyFunctionScope; + @Nullable + private JsName declarationsObject = null; + @Nullable + private JsStatement declarationsStatement = null; + + public ClassDeclarationTranslator(@NotNull List descriptors, + @NotNull TranslationContext context) { + super(context); + this.descriptors = descriptors; + this.localToGlobalClassName = new HashMap(); + this.dummyFunctionScope = new JsScope(context().jsScope(), "class declaration function"); + } + + public void generateDeclarations() { + declarationsObject = context().jsScope().declareName(Namer.nameForClassesVariable()); + assert declarationsObject != null; + declarationsStatement = + AstUtil.newVar(declarationsObject, generateDummyFunctionInvocation()); + } + + @NotNull + public JsName getDeclarationsObjectName() { + assert declarationsObject != null : "Should run generateDeclarations first"; + return declarationsObject; + } + + @NotNull + public JsStatement getDeclarationsStatement() { + assert declarationsStatement != null : "Should run generateDeclarations first"; + return declarationsStatement; + } + + @NotNull + private JsInvocation generateDummyFunctionInvocation() { + JsFunction dummyFunction = JsFunction.getAnonymousFunctionWithScope(dummyFunctionScope); + List classDeclarations = generateClassDeclarationStatements(); + classDeclarations.add(new JsReturn(generateReturnedObjectLiteral())); + dummyFunction.setBody(AstUtil.newBlock(classDeclarations)); + return AstUtil.newInvocation(dummyFunction); + } + + @NotNull + private JsObjectLiteral generateReturnedObjectLiteral() { + JsObjectLiteral returnedValueLiteral = new JsObjectLiteral(); + for (JsName localName : localToGlobalClassName.keySet()) { + returnedValueLiteral.getPropertyInitializers().add(classEntry(localName)); + } + return returnedValueLiteral; + } + + @NotNull + private JsPropertyInitializer classEntry(@NotNull JsName localName) { + return new JsPropertyInitializer(localToGlobalClassName.get(localName).makeRef(), localName.makeRef()); + } + + @NotNull + private List generateClassDeclarationStatements() { + List classDeclarations = new ArrayList(); + for (JetClass jetClass : getClassDeclarations()) { + classDeclarations.add(generateDeclaration(jetClass)); + } + removeAliases(); + return classDeclarations; + } + + private void removeAliases() { + for (JetClass jetClass : getClassDeclarations()) { + ClassDescriptor descriptor = BindingUtils.getClassDescriptor(bindingContext(), jetClass); + aliaser().removeAliasForDescriptor(descriptor); + } + } + + @NotNull + private List getClassDeclarations() { + List classes = new ArrayList(); + for (ClassDescriptor classDescriptor : descriptors) { + classes.add(BindingUtils.getClassForDescriptor(bindingContext(), classDescriptor)); + } + return ClassSorter.sortUsingInheritanceOrder(classes, bindingContext()); + } + + @NotNull + private JsStatement generateDeclaration(@NotNull JetClass declaration) { + JsName localClassName = generateLocalAlias(declaration); + JsInvocation classDeclarationExpression = + Translation.translateClassDeclaration(declaration, context()); + return AstUtil.newVar(localClassName, classDeclarationExpression); + } + + @NotNull + private JsName generateLocalAlias(@NotNull JetClass declaration) { + JsName globalClassName = context().getNameForElement(declaration); + JsName localAlias = dummyFunctionScope.declareTemporary(); + localToGlobalClassName.put(localAlias, globalClassName); + ClassDescriptor descriptor = BindingUtils.getClassDescriptor(bindingContext(), declaration); + aliaser().setAliasForDescriptor(descriptor, localAlias); + return localAlias; + } + + @NotNull + public JsObjectLiteral classDeclarationsForNamespace(@NotNull NamespaceDescriptor namespaceDescriptor) { + JsObjectLiteral result = new JsObjectLiteral(); + for (ClassDescriptor classDescriptor : getAllClassesDefinedInNamespace(namespaceDescriptor)) { + result.getPropertyInitializers().add(getClassNameToClassObject(classDescriptor)); + } + return result; + } + + @NotNull + private JsPropertyInitializer getClassNameToClassObject(@NotNull ClassDescriptor classDescriptor) { + JsName className = context().getNameForDescriptor(classDescriptor); + JsNameRef alreadyDefinedClassReferernce = AstUtil.qualified(className, getDeclarationsObjectName().makeRef()); + return new JsPropertyInitializer(className.makeRef(), alreadyDefinedClassReferernce); + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/declaration/ClassTranslator.java b/js/js.backend/src/org/jetbrains/k2js/translate/declaration/ClassTranslator.java new file mode 100644 index 00000000000..590121d94ff --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/declaration/ClassTranslator.java @@ -0,0 +1,181 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.declaration; + +import com.google.dart.compiler.backend.js.ast.*; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.ClassDescriptor; +import org.jetbrains.jet.lang.descriptors.ClassKind; +import org.jetbrains.jet.lang.descriptors.PropertyDescriptor; +import org.jetbrains.jet.lang.psi.JetClassOrObject; +import org.jetbrains.jet.lang.psi.JetParameter; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.general.AbstractTranslator; +import org.jetbrains.k2js.translate.general.Translation; + +import java.util.ArrayList; +import java.util.List; + +import static org.jetbrains.k2js.translate.utils.BindingUtils.getClassDescriptor; +import static org.jetbrains.k2js.translate.utils.BindingUtils.getPropertyDescriptorForConstructorParameter; +import static org.jetbrains.k2js.translate.utils.DescriptorUtils.findAncestorClass; +import static org.jetbrains.k2js.translate.utils.DescriptorUtils.getSuperclassDescriptors; +import static org.jetbrains.k2js.translate.utils.PsiUtils.getPrimaryConstructorParameters; + +/** + * @author Pavel Talanov + *

+ * Generates a definition of a single class. + */ +public final class ClassTranslator extends AbstractTranslator { + + @NotNull + public static JsPropertyInitializer translateAsProperty(@NotNull JetClassOrObject classDeclaration, + @NotNull TranslationContext context) { + JsInvocation classCreationExpression = generateClassCreationExpression(classDeclaration, context); + JsName className = context.getNameForElement(classDeclaration); + return new JsPropertyInitializer(className.makeRef(), classCreationExpression); + } + + @NotNull + public static JsInvocation generateClassCreationExpression(@NotNull JetClassOrObject classDeclaration, + @NotNull TranslationContext context) { + return (new ClassTranslator(classDeclaration, context)).translateClass(); + } + + @NotNull + private final DeclarationBodyVisitor declarationBodyVisitor = new DeclarationBodyVisitor(); + + @NotNull + private final JetClassOrObject classDeclaration; + + @NotNull + private final ClassDescriptor descriptor; + + private ClassTranslator(@NotNull JetClassOrObject classDeclaration, @NotNull TranslationContext context) { + super(context.newDeclaration(classDeclaration)); + this.descriptor = getClassDescriptor(context.bindingContext(), classDeclaration); + this.classDeclaration = classDeclaration; + } + + @NotNull + private JsInvocation translateClass() { + JsInvocation jsClassDeclaration = classCreateMethodInvocation(); + addSuperclassReferences(jsClassDeclaration); + addClassOwnDeclarations(jsClassDeclaration); + return jsClassDeclaration; + } + + @NotNull + private JsInvocation classCreateMethodInvocation() { + if (isObject()) { + return AstUtil.newInvocation(context().namer().objectCreationMethodReference()); + } else if (isTrait()) { + return AstUtil.newInvocation(context().namer().traitCreationMethodReference()); + } else { + return AstUtil.newInvocation(context().namer().classCreationMethodReference()); + } + } + + private boolean isObject() { + return descriptor.getKind().equals(ClassKind.OBJECT); + } + + private boolean isTrait() { + return descriptor.getKind().equals(ClassKind.TRAIT); + } + + private void addClassOwnDeclarations(@NotNull JsInvocation jsClassDeclaration) { + JsObjectLiteral jsClassDescription = translateClassDeclarations(); + jsClassDeclaration.getArguments().add(jsClassDescription); + } + + private void addSuperclassReferences(@NotNull JsInvocation jsClassDeclaration) { + for (JsExpression superClassReference : getSuperclassNameReferences()) { + jsClassDeclaration.getArguments().add(superClassReference); + } + } + + @NotNull + private List getSuperclassNameReferences() { + List superclassReferences = new ArrayList(); + List superclassDescriptors = getSuperclassDescriptors(descriptor); + addAncestorClass(superclassReferences, superclassDescriptors); + addTraits(superclassReferences, superclassDescriptors); + return superclassReferences; + } + + private void addTraits(@NotNull List superclassReferences, + @NotNull List superclassDescriptors) { + for (ClassDescriptor superClassDescriptor : + superclassDescriptors) { + assert (superClassDescriptor.getKind() == ClassKind.TRAIT) : "Only traits are expected here"; + superclassReferences.add(getClassReference(superClassDescriptor)); + } + } + + private void addAncestorClass(@NotNull List superclassReferences, + @NotNull List superclassDescriptors) { + //here we remove ancestor class from the list + ClassDescriptor ancestorClass = findAndRemoveAncestorClass(superclassDescriptors); + if (ancestorClass != null) { + superclassReferences.add(getClassReference(ancestorClass)); + } + } + + @NotNull + private JsExpression getClassReference(@NotNull ClassDescriptor superClassDescriptor) { + //NOTE: aliasing here is needed for the declaration generation step + if (aliaser().hasAliasForDeclaration(superClassDescriptor)) { + return aliaser().getAliasForDeclaration(superClassDescriptor).makeRef(); + } + return context().getNameForDescriptor(superClassDescriptor).makeRef(); + } + + @Nullable + private ClassDescriptor findAndRemoveAncestorClass(@NotNull List superclassDescriptors) { + ClassDescriptor ancestorClass = findAncestorClass(superclassDescriptors); + superclassDescriptors.remove(ancestorClass); + return ancestorClass; + } + + @NotNull + private JsObjectLiteral translateClassDeclarations() { + List propertyList = new ArrayList(); + if (!isTrait()) { + propertyList.add(Translation.generateClassInitializerMethod(classDeclaration, context())); + } + propertyList.addAll(translatePropertiesAsConstructorParameters()); + propertyList.addAll(declarationBodyVisitor.traverseClass(classDeclaration, context())); + return new JsObjectLiteral(propertyList); + } + + @NotNull + private List translatePropertiesAsConstructorParameters() { + List result = new ArrayList(); + for (JetParameter parameter : getPrimaryConstructorParameters(classDeclaration)) { + PropertyDescriptor descriptor = + getPropertyDescriptorForConstructorParameter(bindingContext(), parameter); + if (descriptor != null) { + result.addAll(PropertyTranslator.translateAccessors(descriptor, context())); + } + } + return result; + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/declaration/DeclarationBodyVisitor.java b/js/js.backend/src/org/jetbrains/k2js/translate/declaration/DeclarationBodyVisitor.java new file mode 100644 index 00000000000..2cdb5629247 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/declaration/DeclarationBodyVisitor.java @@ -0,0 +1,107 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.declaration; + +import com.google.dart.compiler.backend.js.ast.JsPropertyInitializer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor; +import org.jetbrains.jet.lang.descriptors.PropertyDescriptor; +import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.general.Translation; +import org.jetbrains.k2js.translate.general.TranslatorVisitor; +import org.jetbrains.k2js.translate.utils.BindingUtils; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static org.jetbrains.k2js.translate.utils.BindingUtils.getDeclarationsForNamespace; +import static org.jetbrains.k2js.translate.utils.BindingUtils.getPropertyDescriptorForObjectDeclaration; + +/** + * @author Pavel Talanov + */ +public final class DeclarationBodyVisitor extends TranslatorVisitor> { + + + @NotNull + public List traverseClass(@NotNull JetClassOrObject jetClass, + @NotNull TranslationContext context) { + List properties = new ArrayList(); + for (JetDeclaration declaration : jetClass.getDeclarations()) { + properties.addAll(declaration.accept(this, context)); + } + return properties; + } + + @NotNull + public List traverseNamespace(@NotNull NamespaceDescriptor namespace, + @NotNull TranslationContext context) { + List properties = new ArrayList(); + for (JetDeclaration declaration : getDeclarationsForNamespace(context.bindingContext(), namespace)) { + properties.addAll(declaration.accept(this, context)); + } + return properties; + } + + @Override + @NotNull + public List visitClass(@NotNull JetClass expression, @NotNull TranslationContext context) { + return Collections.emptyList(); + } + + @Override + @NotNull + public List visitNamedFunction(@NotNull JetNamedFunction expression, + @NotNull TranslationContext context) { + return Collections.singletonList(Translation.functionTranslator(expression, context).translateAsMethod()); + } + + @Override + @NotNull + public List visitProperty(@NotNull JetProperty expression, + @NotNull TranslationContext context) { + PropertyDescriptor propertyDescriptor = + BindingUtils.getPropertyDescriptor(context.bindingContext(), expression); + return PropertyTranslator.translateAccessors(propertyDescriptor, context); + } + + @Override + @NotNull + public List visitObjectDeclaration(@NotNull JetObjectDeclaration expression, + @NotNull TranslationContext context) { + return Collections.singletonList(ClassTranslator.translateAsProperty(expression, context)); + } + + @Override + @NotNull + public List visitObjectDeclarationName(@NotNull JetObjectDeclarationName expression, + @NotNull TranslationContext context) { + PropertyDescriptor propertyDescriptor = + getPropertyDescriptorForObjectDeclaration(context.bindingContext(), expression); + return PropertyTranslator.translateAccessors(propertyDescriptor, context); + } + + @Override + @NotNull + public List visitAnonymousInitializer(@NotNull JetClassInitializer expression, + @NotNull TranslationContext context) { + // parsed it in initializer visitor => no additional actions are needed + return Collections.emptyList(); + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/declaration/NamespaceDeclarationTranslator.java b/js/js.backend/src/org/jetbrains/k2js/translate/declaration/NamespaceDeclarationTranslator.java new file mode 100644 index 00000000000..4309096dd25 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/declaration/NamespaceDeclarationTranslator.java @@ -0,0 +1,119 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.declaration; + +import com.google.common.collect.Lists; +import com.google.dart.compiler.backend.js.ast.JsStatement; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.descriptors.ClassDescriptor; +import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor; +import org.jetbrains.jet.lang.psi.JetFile; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.general.AbstractTranslator; + +import java.util.List; +import java.util.Set; + +import static org.jetbrains.k2js.translate.utils.BindingUtils.getAllNonNativeNamespaceDescriptors; +import static org.jetbrains.k2js.translate.utils.DescriptorUtils.getAllClassesDefinedInNamespace; + +/** + * @author Pavel Talanov + */ +public final class NamespaceDeclarationTranslator extends AbstractTranslator { + + public static List translateFiles(@NotNull List files, @NotNull TranslationContext context) { + Set namespaceDescriptorSet = getAllNonNativeNamespaceDescriptors(context.bindingContext(), files); + return (new NamespaceDeclarationTranslator(Lists.newArrayList(namespaceDescriptorSet), context)).translate(); + } + + @NotNull + private final ClassDeclarationTranslator classDeclarationTranslator; + @NotNull + private final List namespaceDescriptors; + + private NamespaceDeclarationTranslator(@NotNull List namespaceDescriptors, + @NotNull TranslationContext context) { + super(context); + this.namespaceDescriptors = namespaceDescriptors; + this.classDeclarationTranslator = new ClassDeclarationTranslator(getAllClasses(), context); + } + + @NotNull + private List getAllClasses() { + List result = Lists.newArrayList(); + for (NamespaceDescriptor namespaceDescriptor : namespaceDescriptors) { + result.addAll(getAllClassesDefinedInNamespace(namespaceDescriptor)); + } + return result; + } + + @NotNull + private List translate() { + List result = classesDeclarations(); + result.addAll(namespacesDeclarations()); + return result; + } + + @NotNull + private List classesDeclarations() { + List result = Lists.newArrayList(); + classDeclarationTranslator.generateDeclarations(); + result.add(classDeclarationTranslator.getDeclarationsStatement()); + return result; + } + + @NotNull + private List namespacesDeclarations() { + List result = Lists.newArrayList(); + List namespaceTranslators = getTranslatorsForNonEmptyNamespaces(); + result.addAll(declarationStatements(namespaceTranslators)); + result.addAll(initializeStatements(namespaceTranslators)); + return result; + } + + @NotNull + private List getTranslatorsForNonEmptyNamespaces() { + List namespaceTranslators = Lists.newArrayList(); + for (NamespaceDescriptor descriptor : namespaceDescriptors) { + NamespaceTranslator namespaceTranslator = + new NamespaceTranslator(descriptor, classDeclarationTranslator, context()); + if (!namespaceTranslator.isNamespaceEmpty()) { + namespaceTranslators.add(namespaceTranslator); + } + } + return namespaceTranslators; + } + + @NotNull + private List declarationStatements(@NotNull List namespaceTranslators) { + List result = Lists.newArrayList(); + for (NamespaceTranslator translator : namespaceTranslators) { + result.add(translator.getDeclarationStatement()); + } + return result; + } + + @NotNull + private List initializeStatements(@NotNull List namespaceTranslators) { + List result = Lists.newArrayList(); + for (NamespaceTranslator translator : namespaceTranslators) { + result.add(translator.getInitializeStatement()); + } + return result; + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/declaration/NamespaceTranslator.java b/js/js.backend/src/org/jetbrains/k2js/translate/declaration/NamespaceTranslator.java new file mode 100644 index 00000000000..6562fe3c40e --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/declaration/NamespaceTranslator.java @@ -0,0 +1,98 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.declaration; + +import com.google.dart.compiler.backend.js.ast.*; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor; +import org.jetbrains.k2js.translate.context.Namer; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.general.AbstractTranslator; +import org.jetbrains.k2js.translate.general.Translation; +import org.jetbrains.k2js.translate.utils.BindingUtils; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Pavel.Talanov + *

+ * Genereate code for a single descriptor. + */ +public final class NamespaceTranslator extends AbstractTranslator { + + @NotNull + private final NamespaceDescriptor descriptor; + @NotNull + private final JsName namespaceName; + @NotNull + private final ClassDeclarationTranslator classDeclarationTranslator; + + /*package*/ NamespaceTranslator(@NotNull NamespaceDescriptor descriptor, + @NotNull ClassDeclarationTranslator classDeclarationTranslator, + @NotNull TranslationContext context) { + super(context.newDeclaration(descriptor)); + this.descriptor = descriptor; + this.namespaceName = context.getNameForDescriptor(descriptor); + this.classDeclarationTranslator = classDeclarationTranslator; + } + + //TODO: at the moment this check is very ineffective, possible solution is to cash the result of getDFN + // other solution is to determine it's not affecting performance :D + public boolean isNamespaceEmpty() { + return BindingUtils.getDeclarationsForNamespace(bindingContext(), descriptor).isEmpty(); + } + + + @NotNull + public JsStatement getInitializeStatement() { + JsNameRef initializeMethodReference = Namer.initializeMethodReference(); + AstUtil.setQualifier(initializeMethodReference, namespaceName.makeRef()); + return AstUtil.newInvocation(initializeMethodReference).makeStmt(); + } + + @NotNull + private JsInvocation namespaceCreateMethodInvocation() { + return AstUtil.newInvocation(context().namer().namespaceCreationMethodReference()); + } + + @NotNull + public JsStatement getDeclarationStatement() { + JsInvocation namespaceDeclaration = namespaceCreateMethodInvocation(); + addMemberDeclarations(namespaceDeclaration); + addClassesDeclarations(namespaceDeclaration); + return AstUtil.newVar(namespaceName, namespaceDeclaration); + } + + private void addClassesDeclarations(@NotNull JsInvocation namespaceDeclaration) { + namespaceDeclaration.getArguments().add(classDeclarationTranslator.classDeclarationsForNamespace(descriptor)); + } + + private void addMemberDeclarations(@NotNull JsInvocation jsNamespace) { + JsObjectLiteral jsClassDescription = translateNamespaceMemberDeclarations(); + jsNamespace.getArguments().add(jsClassDescription); + } + + @NotNull + private JsObjectLiteral translateNamespaceMemberDeclarations() { + List propertyList = new ArrayList(); + propertyList.add(Translation.generateNamespaceInitializerMethod(descriptor, context())); + propertyList.addAll(new DeclarationBodyVisitor().traverseNamespace(descriptor, context())); + return new JsObjectLiteral(propertyList); + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/declaration/PropertyTranslator.java b/js/js.backend/src/org/jetbrains/k2js/translate/declaration/PropertyTranslator.java new file mode 100644 index 00000000000..7fb2a4ac820 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/declaration/PropertyTranslator.java @@ -0,0 +1,158 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.declaration; + +import com.google.dart.compiler.backend.js.ast.*; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.PropertyDescriptor; +import org.jetbrains.jet.lang.descriptors.PropertyGetterDescriptor; +import org.jetbrains.jet.lang.descriptors.PropertySetterDescriptor; +import org.jetbrains.jet.lang.psi.JetProperty; +import org.jetbrains.jet.lang.psi.JetPropertyAccessor; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.general.AbstractTranslator; +import org.jetbrains.k2js.translate.general.Translation; +import org.jetbrains.k2js.translate.utils.BindingUtils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.jetbrains.k2js.translate.utils.TranslationUtils.*; + +/** + * @author Pavel Talanov + *

+ * Translates single property /w accessors. + */ +public final class PropertyTranslator extends AbstractTranslator { + + @NotNull + private final PropertyDescriptor property; + @NotNull + private final List accessors = new ArrayList(); + @Nullable + private final JetProperty declaration; + + static public List translateAccessors(@NotNull PropertyDescriptor descriptor, + @NotNull TranslationContext context) { + PropertyTranslator propertyTranslator = new PropertyTranslator(descriptor, context); + return propertyTranslator.translate(); + } + + private PropertyTranslator(@NotNull PropertyDescriptor property, @NotNull TranslationContext context) { + super(context); + this.property = property; + this.declaration = BindingUtils.getPropertyForDescriptor(bindingContext(), property); + } + + @NotNull + private List translate() { + addGetter(); + if (property.isVar()) { + addSetter(); + } + return accessors; + } + + private void addGetter() { + if (hasCustomGetter()) { + accessors.add(translateCustomAccessor(getCustomGetterDeclaration())); + } else { + accessors.add(generateDefaultGetter()); + } + } + + private void addSetter() { + if (hasCustomSetter()) { + accessors.add(translateCustomAccessor(getCustomSetterDeclaration())); + } else { + accessors.add(generateDefaultSetter()); + } + } + + private boolean hasCustomGetter() { + return ((declaration != null) && (declaration.getGetter() != null)); + } + + private boolean hasCustomSetter() { + return ((declaration != null) && (declaration.getSetter() != null)); + } + + @NotNull + private JetPropertyAccessor getCustomGetterDeclaration() { + assert declaration != null; + JetPropertyAccessor getterDeclaration = declaration.getGetter(); + assert getterDeclaration != null; + return getterDeclaration; + } + + @NotNull + private JetPropertyAccessor getCustomSetterDeclaration() { + assert declaration != null; + JetPropertyAccessor setter = declaration.getSetter(); + assert setter != null; + return setter; + } + + @NotNull + private JsPropertyInitializer generateDefaultGetter() { + PropertyGetterDescriptor getterDescriptor = property.getGetter(); + assert getterDescriptor != null : "Getter descriptor should not be null"; + return AstUtil.newNamedMethod(context().getNameForDescriptor(getterDescriptor), + generateDefaultGetterFunction(getterDescriptor)); + } + + @NotNull + private JsFunction generateDefaultGetterFunction(@NotNull PropertyGetterDescriptor descriptor) { + JsReturn returnExpression = new JsReturn(backingFieldReference(context(), property)); + JsFunction getterFunction = functionWithScope(context().getScopeForDescriptor(descriptor)); + getterFunction.setBody(AstUtil.convertToBlock(returnExpression)); + return getterFunction; + } + + @NotNull + private JsPropertyInitializer generateDefaultSetter() { + PropertySetterDescriptor setterDescriptor = property.getSetter(); + assert setterDescriptor != null : "Setter descriptor should not be null"; + return AstUtil.newNamedMethod(context().getNameForDescriptor(setterDescriptor), + generateDefaultSetterFunction(setterDescriptor)); + } + + @NotNull + private JsFunction generateDefaultSetterFunction(@NotNull PropertySetterDescriptor propertySetterDescriptor) { + JsFunction result = functionWithScope(context().getScopeForDescriptor(propertySetterDescriptor)); + JsParameter defaultParameter = + new JsParameter(propertyAccessContext(propertySetterDescriptor).jsScope().declareTemporary()); + JsStatement assignment = assignmentToBackingField(context(), property, defaultParameter.getName().makeRef()); + result.setParameters(Arrays.asList(defaultParameter)); + result.setBody(AstUtil.convertToBlock(assignment)); + return result; + } + + @NotNull + private TranslationContext propertyAccessContext(@NotNull PropertySetterDescriptor propertySetterDescriptor) { + return context().newDeclaration(propertySetterDescriptor); + } + + @NotNull + private JsPropertyInitializer translateCustomAccessor(@NotNull JetPropertyAccessor expression) { + return Translation.functionTranslator(expression, context()).translateAsMethod(); + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/expression/ExpressionVisitor.java b/js/js.backend/src/org/jetbrains/k2js/translate/expression/ExpressionVisitor.java new file mode 100644 index 00000000000..ba564959d8a --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/expression/ExpressionVisitor.java @@ -0,0 +1,420 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.expression; + +import com.google.dart.compiler.backend.js.ast.*; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; +import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant; +import org.jetbrains.jet.lang.resolve.constants.NullValue; +import org.jetbrains.k2js.translate.context.TemporaryVariable; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.declaration.ClassTranslator; +import org.jetbrains.k2js.translate.expression.foreach.ForTranslator; +import org.jetbrains.k2js.translate.general.Translation; +import org.jetbrains.k2js.translate.general.TranslatorVisitor; +import org.jetbrains.k2js.translate.operation.BinaryOperationTranslator; +import org.jetbrains.k2js.translate.operation.IncrementTranslator; +import org.jetbrains.k2js.translate.operation.UnaryOperationTranslator; +import org.jetbrains.k2js.translate.reference.*; +import org.jetbrains.k2js.translate.utils.BindingUtils; +import org.jetbrains.k2js.translate.utils.TranslationUtils; + +import java.util.List; + +import static org.jetbrains.k2js.translate.utils.BindingUtils.*; +import static org.jetbrains.k2js.translate.utils.PsiUtils.getObjectDeclarationName; +import static org.jetbrains.k2js.translate.utils.TranslationUtils.translateInitializerForProperty; + +/** + * @author Pavel Talanov + */ +public final class ExpressionVisitor extends TranslatorVisitor { + + @NotNull + private JsExpression translateAsExpression(@NotNull JetExpression expression, + @NotNull TranslationContext context) { + return AstUtil.convertToExpression(expression.accept(this, context)); + } + + @Override + @NotNull + public JsNode visitConstantExpression(@NotNull JetConstantExpression expression, + @NotNull TranslationContext context) { + CompileTimeConstant compileTimeValue = + context.bindingContext().get(BindingContext.COMPILE_TIME_VALUE, expression); + assert compileTimeValue != null; + if (compileTimeValue instanceof NullValue) { + return context.program().getNullLiteral(); + } + Object value = compileTimeValue.getValue(); + if (value instanceof Integer) { + return context.program().getNumberLiteral((Integer) value); + } + if (value instanceof Boolean) { + return context.program().getBooleanLiteral((Boolean) value); + } + + //TODO: test + if (value instanceof Float) { + return context.program().getNumberLiteral((Float) value); + } + if (value instanceof Double) { + return context.program().getNumberLiteral((Double) value); + } + if (value instanceof String) { + return context.program().getStringLiteral((String) value); + } + if (value instanceof Character) { + return context.program().getStringLiteral(value.toString()); + } + //TODO: all values + throw new AssertionError("Unsupported constant expression" + expression.toString()); + } + + @Override + @NotNull + public JsNode visitBlockExpression(@NotNull JetBlockExpression jetBlock, @NotNull TranslationContext context) { + List statements = jetBlock.getStatements(); + JsBlock jsBlock = new JsBlock(); + TranslationContext blockContext = context.innerBlock(jsBlock); + for (JetElement statement : statements) { + assert statement instanceof JetExpression : "Elements in JetBlockExpression " + + "should be of type JetExpression"; + JsNode jsNode = statement.accept(this, blockContext); + jsBlock.addStatement(AstUtil.convertToStatement(jsNode)); + } + return jsBlock; + } + + @Override + @NotNull + public JsNode visitReturnExpression(@NotNull JetReturnExpression jetReturnExpression, + @NotNull TranslationContext context) { + JetExpression returnedExpression = jetReturnExpression.getReturnedExpression(); + if (returnedExpression != null) { + JsExpression jsExpression = translateAsExpression(returnedExpression, context); + return new JsReturn(jsExpression); + } + return new JsReturn(); + } + + @Override + @NotNull + public JsNode visitParenthesizedExpression(@NotNull JetParenthesizedExpression expression, + @NotNull TranslationContext context) { + JetExpression expressionInside = expression.getExpression(); + if (expressionInside != null) { + return expressionInside.accept(this, context); + } + return context.program().getEmptyStmt(); + } + + @Override + @NotNull + public JsNode visitBinaryExpression(@NotNull JetBinaryExpression expression, + @NotNull TranslationContext context) { + return BinaryOperationTranslator.translate(expression, context); + } + + @Override + @NotNull + // assume it is a local variable declaration + public JsNode visitProperty(@NotNull JetProperty expression, @NotNull TranslationContext context) { + DeclarationDescriptor descriptor = getDescriptorForElement(context.bindingContext(), expression); + JsName jsPropertyName = context.getNameForDescriptor(descriptor); + JsExpression jsInitExpression = translateInitializerForProperty(expression, context); + return AstUtil.newVar(jsPropertyName, jsInitExpression); + } + + @Override + @NotNull + public JsNode visitCallExpression(@NotNull JetCallExpression expression, + @NotNull TranslationContext context) { + return CallExpressionTranslator.translate(expression, null, CallType.NORMAL, context); + } + + @Override + @NotNull + public JsNode visitIfExpression(@NotNull JetIfExpression expression, @NotNull TranslationContext context) { + JsIf ifStatement = translateAsIfStatement(expression, context); + if (BindingUtils.isStatement(context.bindingContext(), expression)) { + return ifStatement; + } + TemporaryVariable result = context.declareTemporary(context.program().getNullLiteral()); + AstUtil.SaveLastExpressionMutator saveResultToTemporaryMutator = + new AstUtil.SaveLastExpressionMutator(result.reference()); + JsNode mutatedIfStatement = AstUtil.mutateLastExpression(ifStatement, + saveResultToTemporaryMutator); + JsStatement resultingStatement = AstUtil.convertToStatement(mutatedIfStatement); + context.addStatementToCurrentBlock(resultingStatement); + return result.reference(); + } + + @Override + @NotNull + public JsNode visitSimpleNameExpression(@NotNull JetSimpleNameExpression expression, + @NotNull TranslationContext context) { + return ReferenceTranslator.translateSimpleName(expression, context); + } + + + @NotNull + private JsIf translateAsIfStatement(@NotNull JetIfExpression expression, + @NotNull TranslationContext context) { + JsIf result = new JsIf(); + result.setIfExpr(translateConditionExpression(expression.getCondition(), context)); + result.setThenStmt(translateNullableExpressionAsNotNullStatement(expression.getThen(), context)); + result.setElseStmt(translateElseAsStatement(expression, context)); + return result; + } + + @Nullable + private JsStatement translateElseAsStatement(@NotNull JetIfExpression expression, + @NotNull TranslationContext context) { + JetExpression jetElse = expression.getElse(); + if (jetElse == null) { + return null; + } + return AstUtil.convertToStatement(jetElse.accept(this, context)); + } + + @NotNull + private JsStatement translateNullableExpressionAsNotNullStatement(@Nullable JetExpression nullableExpression, + @NotNull TranslationContext context) { + if (nullableExpression == null) { + return context.program().getEmptyStmt(); + } + return AstUtil.convertToStatement(nullableExpression.accept(this, context)); + } + + @NotNull + private JsExpression translateConditionExpression(@Nullable JetExpression expression, + @NotNull TranslationContext context) { + JsExpression jsCondition = translateNullableExpression(expression, context); + assert (jsCondition != null) : "Condition should not be empty"; + return AstUtil.convertToExpression(jsCondition); + } + + @Nullable + private JsExpression translateNullableExpression(@Nullable JetExpression expression, + @NotNull TranslationContext context) { + if (expression == null) { + return null; + } + return translateAsExpression(expression, context); + } + + @Override + @NotNull + public JsNode visitWhileExpression(@NotNull JetWhileExpression expression, @NotNull TranslationContext context) { + JsWhile result = new JsWhile(); + result.setCondition(translateConditionExpression(expression.getCondition(), context)); + result.setBody(translateNullableExpressionAsNotNullStatement(expression.getBody(), context)); + return result; + } + + @Override + @NotNull + public JsNode visitDoWhileExpression(@NotNull JetDoWhileExpression expression, @NotNull TranslationContext context) { + JsDoWhile result = new JsDoWhile(); + result.setCondition(translateConditionExpression(expression.getCondition(), context)); + result.setBody(translateNullableExpressionAsNotNullStatement(expression.getBody(), context)); + return result; + } + + @Override + @NotNull + public JsNode visitStringTemplateExpression(@NotNull JetStringTemplateExpression expression, + @NotNull TranslationContext context) { + JsStringLiteral stringLiteral = resolveAsStringConstant(expression, context); + if (stringLiteral != null) { + return stringLiteral; + } + return resolveAsTemplate(expression, context); + } + + @NotNull + private JsNode resolveAsTemplate(@NotNull JetStringTemplateExpression expression, + @NotNull TranslationContext context) { + return StringTemplateTranslator.translate(expression, context); + } + + @Nullable + private JsStringLiteral resolveAsStringConstant(@NotNull JetExpression expression, + @NotNull TranslationContext context) { + Object value = getCompileTimeValue(context.bindingContext(), expression); + if (value == null) { + return null; + } + assert value instanceof String : "Compile time constant template should be a String constant."; + String constantString = (String) value; + return context.program().getStringLiteral(constantString); + } + + @Override + @NotNull + public JsNode visitDotQualifiedExpression(@NotNull JetDotQualifiedExpression expression, + @NotNull TranslationContext context) { + return QualifiedExpressionTranslator.translateQualifiedExpression(expression, context); + } + + @Override + @NotNull + public JsNode visitPrefixExpression(@NotNull JetPrefixExpression expression, + @NotNull TranslationContext context) { + return UnaryOperationTranslator.translate(expression, context); + + } + + @Override + @NotNull + public JsNode visitPostfixExpression(@NotNull JetPostfixExpression expression, + @NotNull TranslationContext context) { + return IncrementTranslator.translate(expression, context); + } + + @Override + @NotNull + public JsNode visitIsExpression(@NotNull JetIsExpression expression, + @NotNull TranslationContext context) { + return Translation.patternTranslator(context).translateIsExpression(expression); + } + + @Override + @NotNull + public JsNode visitSafeQualifiedExpression(@NotNull JetSafeQualifiedExpression expression, + @NotNull TranslationContext context) { + return QualifiedExpressionTranslator.translateQualifiedExpression(expression, context); + } + + @Override + @NotNull + public JsNode visitWhenExpression(@NotNull JetWhenExpression expression, + @NotNull TranslationContext context) { + return Translation.translateWhenExpression(expression, context); + } + + + @Override + @NotNull + public JsNode visitBinaryWithTypeRHSExpression(@NotNull JetBinaryExpressionWithTypeRHS expression, + @NotNull TranslationContext context) { + // we actually do not care for types in js + return Translation.translateExpression(expression.getLeft(), context); + } + + @Override + @NotNull + public JsNode visitBreakExpression(@NotNull JetBreakExpression expression, + @NotNull TranslationContext context) { + return new JsBreak(); + } + + @Override + @NotNull + public JsNode visitContinueExpression(@NotNull JetContinueExpression expression, + @NotNull TranslationContext context) { + return new JsContinue(); + } + + @Override + @NotNull + public JsNode visitFunctionLiteralExpression(@NotNull JetFunctionLiteralExpression expression, + @NotNull TranslationContext context) { + return Translation.functionTranslator(expression, context).translateAsLiteral(); + } + + @Override + @NotNull + public JsNode visitThisExpression(@NotNull JetThisExpression expression, + @NotNull TranslationContext context) { + DeclarationDescriptor descriptor = + getDescriptorForReferenceExpression(context.bindingContext(), expression.getInstanceReference()); + return TranslationUtils.getThisObject(context, descriptor); + } + + @Override + @NotNull + public JsNode visitArrayAccessExpression(@NotNull JetArrayAccessExpression expression, + @NotNull TranslationContext context) { + return AccessTranslator.translateAsGet(expression, context); + } + + @Override + @NotNull + public JsNode visitForExpression(@NotNull JetForExpression expression, + @NotNull TranslationContext context) { + return ForTranslator.translate(expression, context); + } + + @Override + @NotNull + public JsNode visitTryExpression(@NotNull JetTryExpression expression, + @NotNull TranslationContext context) { + return TryTranslator.translate(expression, context); + } + + @Override + @NotNull + public JsNode visitTupleExpression(@NotNull JetTupleExpression expression, + @NotNull TranslationContext context) { + JsArrayLiteral result = new JsArrayLiteral(); + for (JetExpression entry : expression.getEntries()) { + result.getExpressions().add(Translation.translateAsExpression(entry, context)); + } + return result; + } + + @Override + @NotNull + public JsNode visitThrowExpression(@NotNull JetThrowExpression expression, + @NotNull TranslationContext context) { + JetExpression thrownExpression = expression.getThrownExpression(); + assert thrownExpression != null : "Thrown expression must not be null"; + return new JsThrow(translateAsExpression(thrownExpression, context)); + } + + @Override + @NotNull + public JsNode visitObjectLiteralExpression(@NotNull JetObjectLiteralExpression expression, + @NotNull TranslationContext context) { + return ClassTranslator.generateClassCreationExpression(expression.getObjectDeclaration(), context); + } + + @Override + @NotNull + public JsNode visitObjectDeclaration(@NotNull JetObjectDeclaration expression, + @NotNull TranslationContext context) { + JetObjectDeclarationName objectDeclarationName = getObjectDeclarationName(expression); + DeclarationDescriptor descriptor = getDescriptorForElement(context.bindingContext(), objectDeclarationName); + JsName propertyName = context.getNameForDescriptor(descriptor); + JsExpression value = ClassTranslator.generateClassCreationExpression(expression, context); + return AstUtil.newVar(propertyName, value); + } + + @Override + @NotNull + public JsNode visitNamedFunction(@NotNull JetNamedFunction function, + @NotNull TranslationContext context) { + return FunctionTranslator.newInstance(function, context).translateAsLocalFunction(); + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/expression/FunctionTranslator.java b/js/js.backend/src/org/jetbrains/k2js/translate/expression/FunctionTranslator.java new file mode 100644 index 00000000000..44be9f3f8a3 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/expression/FunctionTranslator.java @@ -0,0 +1,223 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.expression; + + +import com.google.dart.compiler.backend.js.ast.*; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.descriptors.*; +import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.jet.lang.types.JetStandardClasses; +import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.k2js.translate.context.Namer; +import org.jetbrains.k2js.translate.context.TemporaryVariable; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.general.AbstractTranslator; +import org.jetbrains.k2js.translate.general.Translation; +import org.jetbrains.k2js.translate.utils.DescriptorUtils; + +import java.util.ArrayList; +import java.util.List; + +import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptor; +import static org.jetbrains.k2js.translate.utils.DescriptorUtils.*; +import static org.jetbrains.k2js.translate.utils.TranslationUtils.*; + + +/** + * @author Pavel Talanov + */ +public final class FunctionTranslator extends AbstractTranslator { + + @NotNull + public static FunctionTranslator newInstance(@NotNull JetDeclarationWithBody function, + @NotNull TranslationContext context) { + return new FunctionTranslator(function, context); + } + + @NotNull + private final JetDeclarationWithBody functionDeclaration; + @NotNull + private final JsFunction functionObject; + @NotNull + private final TranslationContext functionBodyContext; + @NotNull + private final FunctionDescriptor descriptor; + // function body needs to be explicitly created here to include it in the context + @NotNull + private final JsBlock functionBody; + + private FunctionTranslator(@NotNull JetDeclarationWithBody functionDeclaration, + @NotNull TranslationContext context) { + super(context); + this.functionBody = new JsBlock(); + this.descriptor = getFunctionDescriptor(context.bindingContext(), functionDeclaration); + this.functionDeclaration = functionDeclaration; + this.functionObject = createFunctionObject(); + this.functionBodyContext = functionBodyContext().innerBlock(functionBody); + } + + + @NotNull + public JsFunction translateAsLocalFunction() { + JsName functionName = context().getNameForElement(functionDeclaration); + JsFunction function = generateFunctionObject(); + function.setName(functionName); + return function; + } + + @NotNull + public JsPropertyInitializer translateAsMethod() { + JsName functionName = context().getNameForElement(functionDeclaration); + JsFunction function = generateFunctionObject(); + return new JsPropertyInitializer(functionName.makeRef(), function); + } + + @NotNull + public JsExpression translateAsLiteral() { + assert getExpectedThisDescriptor(descriptor) == null; + ClassDescriptor containingClass = getContainingClass(descriptor); + if (containingClass == null) { + return generateFunctionObject(); + } + return generateFunctionObjectWithAliasedThisReference(containingClass); + } + + @NotNull + private JsExpression generateFunctionObjectWithAliasedThisReference(@NotNull ClassDescriptor containingClass) { + TemporaryVariable aliasForThis = newAliasForThis(context(), containingClass); + JsFunction function = generateFunctionObject(); + removeAliasForThis(context(), containingClass); + return AstUtil.newSequence(aliasForThis.assignmentExpression(), function); + } + + @NotNull + private JsFunction generateFunctionObject() { + functionObject.setParameters(translateParameters()); + translateBody(); + functionObject.setBody(functionBody); + restoreContext(); + return functionObject; + } + + private void restoreContext() { + if (isExtensionFunction()) { + DeclarationDescriptor expectedReceiverDescriptor = getExpectedReceiverDescriptor(descriptor); + assert expectedReceiverDescriptor != null : "Extension functions should always have receiver descriptors."; + functionBodyContext.aliaser().removeAliasForThis(expectedReceiverDescriptor); + } + } + + @NotNull + private JsFunction createFunctionObject() { + if (isDeclaration()) { + return functionWithScope(context().getScopeForDescriptor(descriptor)); + } + if (isLiteral()) { + //TODO: changing this piece of code to more natural "same as for declaration" results in life test failing + //TODO: must investigate + return new JsFunction(context().jsScope()); + } + throw new AssertionError("Unsupported type of functionDeclaration."); + } + + private void translateBody() { + JetExpression jetBodyExpression = functionDeclaration.getBodyExpression(); + if (jetBodyExpression == null) { + assert descriptor.getModality().equals(Modality.ABSTRACT); + return; + } + JsNode realBody = Translation.translateExpression(jetBodyExpression, functionBodyContext); + functionBody.addStatement(wrapWithReturnIfNeeded(realBody, mustAddReturnToGeneratedFunctionBody())); + } + + private boolean mustAddReturnToGeneratedFunctionBody() { + JetType functionReturnType = descriptor.getReturnType(); + assert functionReturnType != null : "Function return typed type must be resolved."; + return (!functionDeclaration.hasBlockBody()) && (!JetStandardClasses.isUnit(functionReturnType)); + } + + @NotNull + private JsBlock wrapWithReturnIfNeeded(@NotNull JsNode body, boolean needsReturn) { + if (!needsReturn) { + return AstUtil.convertToBlock(body); + } + return AstUtil.convertToBlock(lastExpressionReturned(body)); + } + + private JsNode lastExpressionReturned(@NotNull JsNode body) { + return AstUtil.mutateLastExpression(body, new AstUtil.Mutator() { + @Override + @NotNull + public JsNode mutate(@NotNull JsNode node) { + if (!(node instanceof JsExpression)) { + return node; + } + return new JsReturn((JsExpression) node); + } + }); + } + + @NotNull + private TranslationContext functionBodyContext() { + if (isLiteral()) { + return context().innerJsScope(functionObject.getScope()); + } else { + return context().newDeclaration(functionDeclaration); + } + } + + @NotNull + private List translateParameters() { + List jsParameters = new ArrayList(); + mayBeAddThisParameterForExtensionFunction(jsParameters); + for (ValueParameterDescriptor valueParameter : descriptor.getValueParameters()) { + JsName parameterName = declareParameter(valueParameter); + jsParameters.add(new JsParameter(parameterName)); + } + return jsParameters; + } + + @NotNull + private JsName declareParameter(@NotNull ValueParameterDescriptor valueParameter) { + return context().getNameForDescriptor(valueParameter); + } + + private void mayBeAddThisParameterForExtensionFunction(@NotNull List jsParameters) { + if (isExtensionFunction()) { + JsName receiver = functionBodyContext.jsScope().declareName(Namer.getReceiverParameterName()); + DeclarationDescriptor expectedReceiverDescriptor = getExpectedReceiverDescriptor(descriptor); + assert expectedReceiverDescriptor != null; + aliaser().setAliasForThis(expectedReceiverDescriptor, receiver); + jsParameters.add(new JsParameter(receiver)); + } + } + + private boolean isExtensionFunction() { + return DescriptorUtils.isExtensionFunction(descriptor) && !isLiteral(); + } + + private boolean isLiteral() { + return functionDeclaration instanceof JetFunctionLiteralExpression; + } + + private boolean isDeclaration() { + return (functionDeclaration instanceof JetNamedFunction) || + (functionDeclaration instanceof JetPropertyAccessor); + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/expression/PatternTranslator.java b/js/js.backend/src/org/jetbrains/k2js/translate/expression/PatternTranslator.java new file mode 100644 index 00000000000..8792faead7e --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/expression/PatternTranslator.java @@ -0,0 +1,134 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.expression; + +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsInvocation; +import com.google.dart.compiler.backend.js.ast.JsName; +import com.google.dart.compiler.backend.js.ast.JsNameRef; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.descriptors.ClassDescriptor; +import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.general.AbstractTranslator; +import org.jetbrains.k2js.translate.general.Translation; +import org.jetbrains.k2js.translate.utils.BindingUtils; +import org.jetbrains.k2js.translate.utils.TranslationUtils; + +/** + * @author Pavel Talanov + */ +public final class PatternTranslator extends AbstractTranslator { + + @NotNull + public static PatternTranslator newInstance(@NotNull TranslationContext context) { + return new PatternTranslator(context); + } + + private PatternTranslator(@NotNull TranslationContext context) { + super(context); + } + + @NotNull + public JsExpression translateIsExpression(@NotNull JetIsExpression expression) { + JsExpression left = Translation.translateAsExpression(expression.getLeftHandSide(), context()); + JetPattern pattern = getPattern(expression); + JsExpression resultingExpression = translatePattern(pattern, left); + if (expression.isNegated()) { + return AstUtil.negated(resultingExpression); + } + return resultingExpression; + } + + @NotNull + private JetPattern getPattern(@NotNull JetIsExpression expression) { + JetPattern pattern = expression.getPattern(); + assert pattern != null : "Pattern should not be null"; + return pattern; + } + + @NotNull + public JsExpression translatePattern(@NotNull JetPattern pattern, @NotNull JsExpression expressionToMatch) { + if (pattern instanceof JetTypePattern) { + return translateTypePattern(expressionToMatch, (JetTypePattern) pattern); + } + if (pattern instanceof JetExpressionPattern) { + return translateExpressionPattern(expressionToMatch, (JetExpressionPattern) pattern); + } + throw new AssertionError("Unsupported pattern type " + pattern.getClass()); + } + + @NotNull + private JsExpression translateTypePattern(@NotNull JsExpression expressionToMatch, + @NotNull JetTypePattern pattern) { + //TODO: look into using intrinsics or other mechanisms to implement this logic + JsName className = getClassReference(pattern).getName(); + if (className.getIdent().equals("String")) { + return AstUtil.typeof(expressionToMatch, program().getStringLiteral("string")); + } + if (className.getIdent().equals("Int")) { + return AstUtil.typeof(expressionToMatch, program().getStringLiteral("number")); + } + + JsInvocation isCheck = AstUtil.newInvocation(context().namer().isOperationReference(), + expressionToMatch, getClassReference(pattern)); + if (isNullable(pattern)) { + return addNullCheck(expressionToMatch, isCheck); + } + return isCheck; + } + + @NotNull + private JsExpression addNullCheck(@NotNull JsExpression expressionToMatch, @NotNull JsInvocation isCheck) { + return AstUtil.or(TranslationUtils.isNullCheck(context(), expressionToMatch), isCheck); + } + + private boolean isNullable(JetTypePattern pattern) { + return BindingUtils.getTypeByReference(bindingContext(), + getTypeReference(pattern)).isNullable(); + } + + @NotNull + private JsNameRef getClassReference(@NotNull JetTypePattern pattern) { + JetTypeReference typeReference = getTypeReference(pattern); + return getClassNameReferenceForTypeReference(typeReference); + } + + @NotNull + private JetTypeReference getTypeReference(@NotNull JetTypePattern pattern) { + JetTypeReference typeReference = pattern.getTypeReference(); + assert typeReference != null : "Type pattern should contain a type reference"; + return typeReference; + } + + @NotNull + private JsNameRef getClassNameReferenceForTypeReference(@NotNull JetTypeReference typeReference) { + ClassDescriptor referencedClass = BindingUtils.getClassDescriptorForTypeReference + (bindingContext(), typeReference); + return TranslationUtils.getQualifiedReference(context(), referencedClass); + } + + @NotNull + private JsExpression translateExpressionPattern(JsExpression expressionToMatch, JetExpressionPattern pattern) { + JetExpression patternExpression = pattern.getExpression(); + assert patternExpression != null : "Expression patter should have an expression."; + JsExpression expressionToMatchAgainst = + Translation.translateAsExpression(patternExpression, context()); + return AstUtil.equals(expressionToMatch, expressionToMatchAgainst); + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/expression/StringTemplateTranslator.java b/js/js.backend/src/org/jetbrains/k2js/translate/expression/StringTemplateTranslator.java new file mode 100644 index 00000000000..2391dac7fe1 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/expression/StringTemplateTranslator.java @@ -0,0 +1,101 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.expression; + +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsStringLiteral; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.general.AbstractTranslator; +import org.jetbrains.k2js.translate.general.Translation; + +import static com.google.dart.compiler.util.AstUtil.sum; + +/** + * @author Pavel Talanov + */ +//TODO: add toString call for non-primitive object +public final class StringTemplateTranslator extends AbstractTranslator { + + @NotNull + public static JsExpression translate(@NotNull JetStringTemplateExpression expression, + @NotNull TranslationContext context) { + return (new StringTemplateTranslator(expression, context).translate()); + } + + @NotNull + private final JetStringTemplateExpression expression; + + private StringTemplateTranslator(@NotNull JetStringTemplateExpression expression, + @NotNull TranslationContext context) { + super(context); + this.expression = expression; + } + + @NotNull + private JsExpression translate() { + assert expression.getEntries().length != 0 : "String template must have one or more entries."; + EntryVisitor entryVisitor = new EntryVisitor(); + for (JetStringTemplateEntry entry : expression.getEntries()) { + entry.accept(entryVisitor); + } + return entryVisitor.getResultingExpression(); + } + + private final class EntryVisitor extends JetVisitorVoid { + + @Nullable + private JsExpression resultingExpression = null; + + void append(@NotNull JsExpression expression) { + if (resultingExpression == null) { + resultingExpression = expression; + } else { + resultingExpression = sum(resultingExpression, expression); + } + } + + @Override + public void visitStringTemplateEntryWithExpression(@NotNull JetStringTemplateEntryWithExpression entry) { + JetExpression entryExpression = entry.getExpression(); + assert entryExpression != null : + "JetStringTemplateEntryWithExpression must have not null entry expression."; + append(Translation.translateAsExpression(entryExpression, context())); + } + + //TODO: duplication + @Override + public void visitLiteralStringTemplateEntry(@NotNull JetLiteralStringTemplateEntry entry) { + JsStringLiteral stringConstant = program().getStringLiteral(entry.getText()); + append(stringConstant); + } + + @Override + public void visitEscapeStringTemplateEntry(@NotNull JetEscapeStringTemplateEntry entry) { + JsStringLiteral escapedValue = program().getStringLiteral(entry.getUnescapedValue()); + append(escapedValue); + } + + @NotNull + public JsExpression getResultingExpression() { + assert resultingExpression != null; + return resultingExpression; + } + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/expression/TryTranslator.java b/js/js.backend/src/org/jetbrains/k2js/translate/expression/TryTranslator.java new file mode 100644 index 00000000000..a163939a748 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/expression/TryTranslator.java @@ -0,0 +1,108 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.expression; + +import com.google.dart.compiler.backend.js.ast.JsBlock; +import com.google.dart.compiler.backend.js.ast.JsCatch; +import com.google.dart.compiler.backend.js.ast.JsName; +import com.google.dart.compiler.backend.js.ast.JsTry; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.general.AbstractTranslator; +import org.jetbrains.k2js.translate.general.Translation; + +import java.util.ArrayList; +import java.util.List; + +import static com.google.dart.compiler.util.AstUtil.convertToBlock; + +/** + * @author Pavel Talanov + */ +//TODO: not tested at all +//TODO: not implemented catch logic +public final class TryTranslator extends AbstractTranslator { + + @NotNull + public static JsTry translate(@NotNull JetTryExpression expression, + @NotNull TranslationContext context) { + return (new TryTranslator(expression, context)).translate(); + } + + @NotNull + private final JetTryExpression expression; + + private TryTranslator(@NotNull JetTryExpression expression, + @NotNull TranslationContext context) { + super(context); + this.expression = expression; + } + + private JsTry translate() { + JsTry result = new JsTry(); + result.setTryBlock(translateTryBlock()); + result.setFinallyBlock(translateFinallyBlock()); + result.getCatches().addAll(translateCatches()); + return result; + } + + @Nullable + private JsBlock translateFinallyBlock() { + JetFinallySection finallyBlock = expression.getFinallyBlock(); + if (finallyBlock == null) return null; + + return convertToBlock(Translation.translateAsStatement(finallyBlock.getFinalExpression(), context())); + } + + @NotNull + private JsBlock translateTryBlock() { + return convertToBlock(Translation.translateAsStatement(expression.getTryBlock(), context())); + } + + + @NotNull + private List translateCatches() { + List result = new ArrayList(); + for (JetCatchClause catchClause : expression.getCatchClauses()) { + result.add(translateCatchClause(catchClause)); + } + return result; + } + + @NotNull + private JsCatch translateCatchClause(@NotNull JetCatchClause catchClause) { + JetParameter catchParameter = catchClause.getCatchParameter(); + assert catchParameter != null : "Valid catch must have a parameter."; + + JsName parameterName = context().getNameForElement(catchParameter); + JsCatch result = new JsCatch(context().jsScope(), parameterName.getIdent()); + result.setBody(translateCatchBody(catchClause)); + return result; + } + + @NotNull + private JsBlock translateCatchBody(@NotNull JetCatchClause catchClause) { + JetExpression catchBody = catchClause.getCatchBody(); + if (catchBody == null) { + return convertToBlock(program().getEmptyStmt()); + } + return convertToBlock(Translation.translateAsStatement(catchBody, context())); + } + +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/expression/WhenTranslator.java b/js/js.backend/src/org/jetbrains/k2js/translate/expression/WhenTranslator.java new file mode 100644 index 00000000000..5af8ed98cbb --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/expression/WhenTranslator.java @@ -0,0 +1,211 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.expression; + +import com.google.dart.compiler.backend.js.ast.*; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.k2js.translate.context.TemporaryVariable; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.general.AbstractTranslator; +import org.jetbrains.k2js.translate.general.Translation; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Pavel Talanov + */ +public class WhenTranslator extends AbstractTranslator { + + @NotNull + static public JsNode translateWhenExpression(@NotNull JetWhenExpression expression, @NotNull TranslationContext context) { + WhenTranslator translator = new WhenTranslator(expression, context); + return translator.translate(); + } + + @NotNull + private final JetWhenExpression whenExpression; + @NotNull + private final JsExpression expressionToMatch; + @NotNull + private final TemporaryVariable dummyCounter; + @NotNull + private final TemporaryVariable result; + + private int currentEntryNumber = 0; + + private WhenTranslator(@NotNull JetWhenExpression expression, @NotNull TranslationContext context) { + super(context); + this.whenExpression = expression; + this.expressionToMatch = translateExpressionToMatch(whenExpression); + this.dummyCounter = context.declareTemporary(program().getNumberLiteral(0)); + this.result = context.declareTemporary(program().getNullLiteral()); + } + + @NotNull + JsNode translate() { + JsFor resultingFor = generateDummyFor(); + List entries = translateEntries(); + resultingFor.setBody(AstUtil.newBlock(entries)); + context().addStatementToCurrentBlock(resultingFor); + return result.reference(); + } + + @NotNull + private List translateEntries() { + List entries = new ArrayList(); + for (JetWhenEntry entry : whenExpression.getEntries()) { + entries.add(surroundWithDummyIf(translateEntry(entry))); + } + return entries; + } + + @NotNull + private JsStatement surroundWithDummyIf(@NotNull JsStatement entryStatement) { + JsExpression stepNumberEqualsCurrentEntryNumber = new JsBinaryOperation(JsBinaryOperator.EQ, + dummyCounter.reference(), program().getNumberLiteral(currentEntryNumber)); + currentEntryNumber++; + return new JsIf(stepNumberEqualsCurrentEntryNumber, entryStatement, null); + } + + @NotNull + private JsFor generateDummyFor() { + JsFor result = new JsFor(); + result.setInitExpr(dummyCounter.assignmentExpression()); + result.setIncrExpr(generateIncrementStatement()); + result.setCondition(generateConditionStatement()); + return result; + } + + @NotNull + private JsBinaryOperation generateConditionStatement() { + JsNumberLiteral entriesNumber = program().getNumberLiteral(whenExpression.getEntries().size()); + return new JsBinaryOperation(JsBinaryOperator.LT, dummyCounter.reference(), entriesNumber); + } + + @NotNull + private JsPrefixOperation generateIncrementStatement() { + return new JsPrefixOperation(JsUnaryOperator.INC, dummyCounter.reference()); + } + + @NotNull + private JsStatement translateEntry(@NotNull JetWhenEntry entry) { + JsStatement statementToExecute = withReturnValueCaptured(translateEntryExpression(entry)); + if (entry.isElse()) { + return statementToExecute; + } + JsExpression condition = translateConditions(entry); + return new JsIf(condition, addDummyBreak(statementToExecute), null); + } + + @NotNull + JsStatement withReturnValueCaptured(@NotNull JsNode node) { + + return AstUtil.convertToStatement(AstUtil.mutateLastExpression(node, + new AstUtil.SaveLastExpressionMutator(result.reference()))); + } + + @NotNull + private JsNode translateEntryExpression(@NotNull JetWhenEntry entry) { + JetExpression expressionToExecute = entry.getExpression(); + assert expressionToExecute != null : "WhenEntry should have whenExpression to execute."; + return Translation.translateExpression(expressionToExecute, context()); + } + + @NotNull + private JsExpression translateConditions(@NotNull JetWhenEntry entry) { + List conditions = new ArrayList(); + for (JetWhenCondition condition : entry.getConditions()) { + conditions.add(translateCondition(condition)); + } + return anyOfThemIsTrue(conditions); + } + + @NotNull + private JsExpression anyOfThemIsTrue(List conditions) { + assert !conditions.isEmpty() : "When entry (not else) should have at least one condition"; + JsExpression current = null; + for (JsExpression condition : conditions) { + current = addCaseCondition(current, condition); + } + assert current != null; + return current; + } + + @NotNull + private JsExpression addCaseCondition(@Nullable JsExpression current, @NotNull JsExpression condition) { + if (current == null) { + return condition; + } else { + return AstUtil.or(current, condition); + } + } + + @NotNull + private JsExpression translateCondition(@NotNull JetWhenCondition condition) { + if ((condition instanceof JetWhenConditionIsPattern) || (condition instanceof JetWhenConditionWithExpression)) { + return translatePatternCondition(condition); + } + throw new AssertionError("Unsupported when condition " + condition.getClass()); + } + + @NotNull + private JsBlock addDummyBreak(@NotNull JsStatement statement) { + return AstUtil.newBlock(statement, new JsBreak()); + } + + @NotNull + private JsExpression translatePatternCondition(@NotNull JetWhenCondition condition) { + JsExpression patternMatchExpression = Translation.patternTranslator(context()). + translatePattern(getPattern(condition), expressionToMatch); + if (isNegated(condition)) { + return AstUtil.negated(patternMatchExpression); + } + return patternMatchExpression; + } + + private boolean isNegated(@NotNull JetWhenCondition condition) { + if (condition instanceof JetWhenConditionIsPattern) { + return ((JetWhenConditionIsPattern) condition).isNegated(); + } + return false; + } + + @NotNull + private JetPattern getPattern(@NotNull JetWhenCondition condition) { + JetPattern pattern; + if (condition instanceof JetWhenConditionIsPattern) { + pattern = ((JetWhenConditionIsPattern) condition).getPattern(); + } else if (condition instanceof JetWhenConditionWithExpression) { + pattern = ((JetWhenConditionWithExpression) condition).getPattern(); + } else { + throw new AssertionError("Wrong type of JetWhenCondition"); + } + assert pattern != null : "Condition should have a non null pattern."; + return pattern; + } + + @NotNull + private JsExpression translateExpressionToMatch(@NotNull JetWhenExpression expression) { + JetExpression subject = expression.getSubjectExpression(); + assert subject != null : "Subject should not be null."; + return Translation.translateAsExpression(subject, context()); + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/expression/foreach/ArrayForTranslator.java b/js/js.backend/src/org/jetbrains/k2js/translate/expression/foreach/ArrayForTranslator.java new file mode 100644 index 00000000000..296427f2163 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/expression/foreach/ArrayForTranslator.java @@ -0,0 +1,116 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.expression.foreach; + +import com.google.dart.compiler.backend.js.ast.*; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.JetExpression; +import org.jetbrains.jet.lang.psi.JetForExpression; +import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.k2js.translate.context.TemporaryVariable; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.general.Translation; +import org.jetbrains.k2js.translate.intrinsic.string.LengthIntrinsic; +import org.jetbrains.k2js.translate.utils.BindingUtils; + +import java.util.Collections; +import java.util.List; + +import static org.jetbrains.k2js.translate.expression.foreach.ForTranslatorUtils.temporariesInitialization; +import static org.jetbrains.k2js.translate.utils.DescriptorUtils.getClassDescriptorForType; +import static org.jetbrains.k2js.translate.utils.PsiUtils.getLoopBody; +import static org.jetbrains.k2js.translate.utils.PsiUtils.getLoopRange; + +/** + * @author Pavel Talanov + */ +public final class ArrayForTranslator extends ForTranslator { + + @NotNull + public static JsStatement translate(@NotNull JetForExpression expression, + @NotNull TranslationContext context) { + return (new ArrayForTranslator(expression, context).translate()); + } + + public static boolean isApplicable(@NotNull JetForExpression expression, + @NotNull TranslationContext context) { + JetExpression loopRange = getLoopRange(expression); + JetType rangeType = BindingUtils.getTypeForExpression(context.bindingContext(), loopRange); + //TODO: better check + //TODO: IMPORTANT! + return getClassDescriptorForType(rangeType).getName().equals("Array") + || getClassDescriptorForType(rangeType).getName().equals("IntArray"); + } + + @NotNull + private final TemporaryVariable loopRange; + + @NotNull + private final TemporaryVariable end; + + @NotNull + private final TemporaryVariable index; + + private ArrayForTranslator(@NotNull JetForExpression forExpression, @NotNull TranslationContext context) { + super(forExpression, context); + loopRange = context.declareTemporary(Translation.translateAsExpression(getLoopRange(expression), context)); + JsExpression length = LengthIntrinsic.INSTANCE.apply(loopRange.reference(), Collections.emptyList(), context()); + end = context().declareTemporary(length); + index = context().declareTemporary(program().getNumberLiteral(0)); + } + + @NotNull + private JsBlock translate() { + List blockStatements = temporariesInitialization(loopRange, end); + blockStatements.add(generateForExpression()); + return AstUtil.newBlock(blockStatements); + } + + @NotNull + private JsFor generateForExpression() { + JsFor result = new JsFor(); + result.setInitVars(initExpression()); + result.setCondition(getCondition()); + result.setIncrExpr(getIncrExpression()); + result.setBody(getBody()); + return result; + } + + @NotNull + private JsStatement getBody() { + JsArrayAccess arrayAccess = new JsArrayAccess(loopRange.reference(), index.reference()); + JsStatement currentVar = AstUtil.newVar(parameterName, arrayAccess); + JsStatement realBody = Translation.translateAsStatement(getLoopBody(expression), context()); + return AstUtil.newBlock(currentVar, realBody); + } + + @NotNull + private JsVars initExpression() { + return AstUtil.newVar(index.name(), program().getNumberLiteral(0)); + } + + @NotNull + private JsExpression getCondition() { + return AstUtil.notEqual(index.reference(), end.reference()); + } + + @NotNull + private JsExpression getIncrExpression() { + return new JsPrefixOperation(JsUnaryOperator.INC, index.reference()); + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/expression/foreach/ForTranslator.java b/js/js.backend/src/org/jetbrains/k2js/translate/expression/foreach/ForTranslator.java new file mode 100644 index 00000000000..776d8d578e8 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/expression/foreach/ForTranslator.java @@ -0,0 +1,77 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.expression.foreach; + +import com.google.dart.compiler.backend.js.ast.JsName; +import com.google.dart.compiler.backend.js.ast.JsStatement; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.JetForExpression; +import org.jetbrains.jet.lang.psi.JetParameter; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.general.AbstractTranslator; +import org.jetbrains.k2js.translate.general.Translation; + +import static org.jetbrains.k2js.translate.utils.PsiUtils.getLoopBody; +import static org.jetbrains.k2js.translate.utils.PsiUtils.getLoopParameter; + +/** + * @author Pavel Talanov + */ + +//TODO: create util class for managing stuff like binary operations +public abstract class ForTranslator extends AbstractTranslator { + + @NotNull + public static JsStatement translate(@NotNull JetForExpression expression, + @NotNull TranslationContext context) { + if (RangeLiteralForTranslator.isApplicable(expression, context)) { + return RangeLiteralForTranslator.translate(expression, context); + } + if (RangeForTranslator.isApplicable(expression, context)) { + return RangeForTranslator.translate(expression, context); + } + if (ArrayForTranslator.isApplicable(expression, context)) { + return ArrayForTranslator.translate(expression, context); + } + return IteratorForTranslator.translate(expression, context); + } + + @NotNull + protected final JetForExpression expression; + @NotNull + protected final JsName parameterName; + + protected ForTranslator(@NotNull JetForExpression forExpression, @NotNull TranslationContext context) { + super(context); + this.expression = forExpression; + this.parameterName = declareParameter(); + } + + @NotNull + private JsName declareParameter() { + JetParameter loopParameter = getLoopParameter(expression); + return context().getNameForElement(loopParameter); + } + + //TODO: look for should-be-usages + @NotNull + protected JsStatement translateOriginalBodyExpression() { + return Translation.translateAsStatement(getLoopBody(expression), context()); + } + + +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/expression/foreach/ForTranslatorUtils.java b/js/js.backend/src/org/jetbrains/k2js/translate/expression/foreach/ForTranslatorUtils.java new file mode 100644 index 00000000000..6dc81395ca3 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/expression/foreach/ForTranslatorUtils.java @@ -0,0 +1,43 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.expression.foreach; + +import com.google.common.collect.Lists; +import com.google.dart.compiler.backend.js.ast.JsStatement; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.k2js.translate.context.TemporaryVariable; + +import java.util.List; + +/** + * @author Pavel Talanov + */ +//TODO: consider moving some of the binary/unary operations stuff here +public final class ForTranslatorUtils { + + private ForTranslatorUtils() { + } + + @NotNull + public static List temporariesInitialization(@NotNull TemporaryVariable... temporaries) { + List result = Lists.newArrayList(); + for (TemporaryVariable temporary : temporaries) { + result.add(temporary.assignmentExpression().makeStmt()); + } + return result; + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/expression/foreach/IteratorForTranslator.java b/js/js.backend/src/org/jetbrains/k2js/translate/expression/foreach/IteratorForTranslator.java new file mode 100644 index 00000000000..e1c0e15f9a8 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/expression/foreach/IteratorForTranslator.java @@ -0,0 +1,100 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.expression.foreach; + +import com.google.dart.compiler.backend.js.ast.*; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.CallableDescriptor; +import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; +import org.jetbrains.jet.lang.psi.JetExpression; +import org.jetbrains.jet.lang.psi.JetForExpression; +import org.jetbrains.k2js.translate.context.TemporaryVariable; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.general.Translation; +import org.jetbrains.k2js.translate.reference.CallBuilder; + +import static org.jetbrains.k2js.translate.utils.BindingUtils.*; +import static org.jetbrains.k2js.translate.utils.PsiUtils.getLoopBody; +import static org.jetbrains.k2js.translate.utils.PsiUtils.getLoopRange; + +/** + * @author Pavel Talanov + */ +public final class IteratorForTranslator extends ForTranslator { + + @NotNull + public static JsStatement translate(@NotNull JetForExpression expression, + @NotNull TranslationContext context) { + return (new IteratorForTranslator(expression, context).translate()); + } + + @NotNull + private final TemporaryVariable iterator; + + private IteratorForTranslator(@NotNull JetForExpression forExpression, @NotNull TranslationContext context) { + super(forExpression, context); + iterator = context().declareTemporary(iteratorMethodInvocation()); + } + + @NotNull + private JsBlock translate() { + JsBlock bodyBlock = generateCycleBody(); + JsWhile cycle = new JsWhile(hasNextMethodInvocation(), bodyBlock); + return AstUtil.newBlock(iterator.assignmentExpression().makeStmt(), cycle); + } + + @NotNull + private JsBlock generateCycleBody() { + JsBlock cycleBody = new JsBlock(); + JsStatement parameterAssignment = AstUtil.newVar(parameterName, nextMethodInvocation()); + JsNode originalBody = Translation.translateExpression(getLoopBody(expression), context().innerBlock(cycleBody)); + cycleBody.addStatement(parameterAssignment); + cycleBody.addStatement(AstUtil.convertToBlock(originalBody)); + return cycleBody; + } + + @NotNull + private JsExpression nextMethodInvocation() { + FunctionDescriptor nextFunction = getNextFunction(bindingContext(), getLoopRange(expression)); + return translateMethodInvocation(iterator.reference(), nextFunction); + } + + @NotNull + private JsExpression hasNextMethodInvocation() { + CallableDescriptor hasNextFunction = getHasNextCallable(bindingContext(), getLoopRange(expression)); + return translateMethodInvocation(iterator.reference(), hasNextFunction); + } + + @NotNull + private JsExpression iteratorMethodInvocation() { + JetExpression rangeExpression = getLoopRange(expression); + JsExpression range = Translation.translateAsExpression(rangeExpression, context()); + FunctionDescriptor iteratorFunction = getIteratorFunction(bindingContext(), rangeExpression); + return translateMethodInvocation(range, iteratorFunction); + } + + @NotNull + private JsExpression translateMethodInvocation(@Nullable JsExpression receiver, + @NotNull CallableDescriptor descriptor) { + return CallBuilder.build(context()) + .receiver(receiver) + .descriptor(descriptor) + .translate(); + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/expression/foreach/RangeForTranslator.java b/js/js.backend/src/org/jetbrains/k2js/translate/expression/foreach/RangeForTranslator.java new file mode 100644 index 00000000000..9cf83198ba2 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/expression/foreach/RangeForTranslator.java @@ -0,0 +1,120 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.expression.foreach; + +import com.google.dart.compiler.backend.js.ast.*; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.JetExpression; +import org.jetbrains.jet.lang.psi.JetForExpression; +import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.k2js.translate.context.TemporaryVariable; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.general.Translation; +import org.jetbrains.k2js.translate.utils.BindingUtils; + +import java.util.List; + +import static org.jetbrains.k2js.translate.expression.foreach.ForTranslatorUtils.temporariesInitialization; +import static org.jetbrains.k2js.translate.utils.DescriptorUtils.getClassDescriptorForType; +import static org.jetbrains.k2js.translate.utils.PsiUtils.getLoopBody; +import static org.jetbrains.k2js.translate.utils.PsiUtils.getLoopRange; + +/** + * @author Pavel Talanov + */ +public final class RangeForTranslator extends ForTranslator { + + @NotNull + public static JsStatement translate(@NotNull JetForExpression expression, + @NotNull TranslationContext context) { + return (new RangeForTranslator(expression, context).translate()); + } + + public static boolean isApplicable(@NotNull JetForExpression expression, + @NotNull TranslationContext context) { + JetExpression loopRange = getLoopRange(expression); + JetType rangeType = BindingUtils.getTypeForExpression(context.bindingContext(), loopRange); + //TODO: better check + return getClassDescriptorForType(rangeType).getName().equals("IntRange"); + } + + @NotNull + private final TemporaryVariable rangeExpression; + @NotNull + private final TemporaryVariable incrVar; + @NotNull + private final TemporaryVariable start; + @NotNull + private final TemporaryVariable end; + + private RangeForTranslator(@NotNull JetForExpression forExpression, @NotNull TranslationContext context) { + super(forExpression, context); + rangeExpression = context.declareTemporary(Translation.translateAsExpression(getLoopRange(expression), context)); + JsExpression isReversed = callFunction("get_reversed"); + JsConditional incrVarValue = new JsConditional(isReversed, + program().getNumberLiteral(-1), + program().getNumberLiteral(1)); + incrVar = context().declareTemporary(incrVarValue); + start = context().declareTemporary(callFunction("get_start")); + end = context().declareTemporary(new JsBinaryOperation(JsBinaryOperator.ADD, callFunction("get_end"), incrVar.reference())); + } + + @NotNull + private JsBlock translate() { + List blockStatements = temporariesInitialization(rangeExpression, incrVar, start, end); + blockStatements.add(generateForExpression()); + return AstUtil.newBlock(blockStatements); + } + + @NotNull + private JsFor generateForExpression() { + JsFor result = new JsFor(); + result.setInitVars(initExpression()); + result.setCondition(getCondition()); + result.setIncrExpr(getIncrExpression()); + result.setBody(Translation.translateAsStatement(getLoopBody(expression), context())); + return result; + } + + @NotNull + private JsVars initExpression() { + return AstUtil.newVar(parameterName, start.reference()); + } + + @NotNull + private JsExpression getCondition() { + return AstUtil.notEqual(parameterName.makeRef(), end.reference()); + } + + @NotNull + private JsExpression getIncrExpression() { + return new JsBinaryOperation(JsBinaryOperator.ASG_ADD, parameterName.makeRef(), incrVar.reference()); + } + + @NotNull + private JsExpression getField(@NotNull String fieldName) { + JsNameRef nameRef = AstUtil.newQualifiedNameRef(fieldName); + AstUtil.setQualifier(nameRef, rangeExpression.reference()); + return nameRef; + } + + @NotNull + private JsExpression callFunction(@NotNull String funName) { + return AstUtil.newInvocation(getField(funName)); + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/expression/foreach/RangeLiteralForTranslator.java b/js/js.backend/src/org/jetbrains/k2js/translate/expression/foreach/RangeLiteralForTranslator.java new file mode 100644 index 00000000000..25cc708664a --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/expression/foreach/RangeLiteralForTranslator.java @@ -0,0 +1,112 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.expression.foreach; + +import com.google.common.collect.Lists; +import com.google.dart.compiler.backend.js.ast.*; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.JetBinaryExpression; +import org.jetbrains.jet.lang.psi.JetExpression; +import org.jetbrains.jet.lang.psi.JetForExpression; +import org.jetbrains.jet.lexer.JetTokens; +import org.jetbrains.k2js.translate.context.TemporaryVariable; +import org.jetbrains.k2js.translate.context.TranslationContext; + +import java.util.List; + +import static org.jetbrains.k2js.translate.expression.foreach.ForTranslatorUtils.temporariesInitialization; +import static org.jetbrains.k2js.translate.utils.PsiUtils.getLoopRange; +import static org.jetbrains.k2js.translate.utils.TranslationUtils.translateLeftExpression; +import static org.jetbrains.k2js.translate.utils.TranslationUtils.translateRightExpression; + +/** + * @author Pavel Talanov + */ + +// TODO: implement reverse semantics +public final class RangeLiteralForTranslator extends ForTranslator { + + @NotNull + public static JsStatement translate(@NotNull JetForExpression expression, + @NotNull TranslationContext context) { + return (new RangeLiteralForTranslator(expression, context).translate()); + } + + public static boolean isApplicable(@NotNull JetForExpression expression, + @NotNull TranslationContext context) { + JetExpression loopRange = getLoopRange(expression); + if (!(loopRange instanceof JetBinaryExpression)) { + return false; + } + boolean isRangeToOperation = ((JetBinaryExpression) loopRange).getOperationToken() == JetTokens.RANGE; + return isRangeToOperation && RangeForTranslator.isApplicable(expression, context); + } + + @NotNull + private final JsExpression rangeStart; + + @NotNull + private final TemporaryVariable rangeEnd; + + private RangeLiteralForTranslator(@NotNull JetForExpression forExpression, @NotNull TranslationContext context) { + super(forExpression, context); + JetExpression loopRange = getLoopRange(expression); + assert loopRange instanceof JetBinaryExpression; + JetBinaryExpression loopRangeAsBinary = ((JetBinaryExpression) loopRange); + rangeStart = translateLeftExpression(context, loopRangeAsBinary); + rangeEnd = context.declareTemporary(getRangeEnd(loopRangeAsBinary)); + } + + @NotNull + private JsExpression getRangeEnd(@NotNull JetBinaryExpression loopRangeAsBinary) { + JsExpression rightExpression = translateRightExpression(context(), loopRangeAsBinary); + return new JsBinaryOperation(JsBinaryOperator.ADD, rightExpression, program().getNumberLiteral(1)); + } + + @NotNull + private JsBlock translate() { + List blockStatements = temporariesInitialization(rangeEnd); + blockStatements.add(generateForExpression()); + return AstUtil.newBlock(blockStatements); + } + + @NotNull + private JsFor generateForExpression() { + JsFor result = new JsFor(); + result.setInitVars(initExpression()); + result.setCondition(getCondition()); + result.setIncrExpr(getIncrExpression()); + result.setBody(translateOriginalBodyExpression()); + return result; + } + + @NotNull + private JsVars initExpression() { + return AstUtil.newVar(parameterName, rangeStart); + } + + @NotNull + private JsExpression getCondition() { + return AstUtil.notEqual(parameterName.makeRef(), rangeEnd.reference()); + } + + @NotNull + private JsExpression getIncrExpression() { + return new JsPrefixOperation(JsUnaryOperator.INC, parameterName.makeRef()); + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/general/AbstractTranslator.java b/js/js.backend/src/org/jetbrains/k2js/translate/general/AbstractTranslator.java new file mode 100644 index 00000000000..8113fc46054 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/general/AbstractTranslator.java @@ -0,0 +1,56 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.general; + +import com.google.dart.compiler.backend.js.ast.JsProgram; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.k2js.translate.context.Aliaser; +import org.jetbrains.k2js.translate.context.TranslationContext; + +/** + * @author Pavel Talanov + */ +public abstract class AbstractTranslator { + + @NotNull + private final TranslationContext context; + + protected AbstractTranslator(@NotNull TranslationContext context) { + this.context = context; + } + + @NotNull + protected JsProgram program() { + return context.program(); + } + + @NotNull + protected TranslationContext context() { + return context; + } + + @NotNull + protected Aliaser aliaser() { + return context.aliaser(); + } + + @NotNull + protected BindingContext bindingContext() { + return context.bindingContext(); + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/general/Translation.java b/js/js.backend/src/org/jetbrains/k2js/translate/general/Translation.java new file mode 100644 index 00000000000..dc69b2e67d9 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/general/Translation.java @@ -0,0 +1,121 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.general; + +import com.google.dart.compiler.backend.js.JsNamer; +import com.google.dart.compiler.backend.js.JsPrettyNamer; +import com.google.dart.compiler.backend.js.ast.*; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor; +import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.types.JetStandardLibrary; +import org.jetbrains.k2js.translate.context.StaticContext; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.declaration.ClassTranslator; +import org.jetbrains.k2js.translate.declaration.NamespaceDeclarationTranslator; +import org.jetbrains.k2js.translate.expression.ExpressionVisitor; +import org.jetbrains.k2js.translate.expression.FunctionTranslator; +import org.jetbrains.k2js.translate.expression.PatternTranslator; +import org.jetbrains.k2js.translate.expression.WhenTranslator; +import org.jetbrains.k2js.translate.initializer.ClassInitializerTranslator; +import org.jetbrains.k2js.translate.initializer.NamespaceInitializerTranslator; + +import java.util.List; + +/** + * @author Pavel Talanov + *

+ * This class provides a interface which all translators use to interact with each other. + * Goal is to simlify interaction between translators. + */ +public final class Translation { + + @NotNull + public static FunctionTranslator functionTranslator(@NotNull JetDeclarationWithBody function, + @NotNull TranslationContext context) { + return FunctionTranslator.newInstance(function, context); + } + + @NotNull + public static List translateFiles(@NotNull List files, @NotNull TranslationContext context) { + return NamespaceDeclarationTranslator.translateFiles(files, context); + } + + @NotNull + public static JsInvocation translateClassDeclaration(@NotNull JetClass classDeclaration, + @NotNull TranslationContext context) { + return ClassTranslator.generateClassCreationExpression(classDeclaration, context); + } + + @NotNull + public static PatternTranslator patternTranslator(@NotNull TranslationContext context) { + return PatternTranslator.newInstance(context); + } + + @NotNull + public static JsNode translateExpression(@NotNull JetExpression expression, @NotNull TranslationContext context) { + return expression.accept(new ExpressionVisitor(), context); + } + + @NotNull + public static JsExpression translateAsExpression(@NotNull JetExpression expression, + @NotNull TranslationContext context) { + return AstUtil.convertToExpression(translateExpression(expression, context)); + } + + @NotNull + public static JsStatement translateAsStatement(@NotNull JetExpression expression, + @NotNull TranslationContext context) { + return AstUtil.convertToStatement(translateExpression(expression, context)); + } + + @NotNull + public static JsNode translateWhenExpression(@NotNull JetWhenExpression expression, + @NotNull TranslationContext context) { + return WhenTranslator.translateWhenExpression(expression, context); + } + + //TODO: see if generate*Initializer methods fit somewhere else + @NotNull + public static JsPropertyInitializer generateClassInitializerMethod(@NotNull JetClassOrObject classDeclaration, + @NotNull TranslationContext context) { + return (new ClassInitializerTranslator(classDeclaration, context)).generateInitializeMethod(); + } + + @NotNull + public static JsPropertyInitializer generateNamespaceInitializerMethod(@NotNull NamespaceDescriptor namespace, + @NotNull TranslationContext context) { + return (new NamespaceInitializerTranslator(namespace, context)).generateInitializeMethod(); + } + + public static JsProgram generateAst(@NotNull BindingContext bindingContext, + @NotNull List files) { + //TODO: move some of the code somewhere + JetStandardLibrary standardLibrary = JetStandardLibrary.getInstance(); + StaticContext staticContext = StaticContext.generateStaticContext(standardLibrary, bindingContext); + JsBlock block = staticContext.getProgram().getFragmentBlock(0); + TranslationContext context = TranslationContext.rootContext(staticContext); + block.getStatements().addAll(Translation.translateFiles(files, context)); + JsNamer namer = new JsPrettyNamer(); + namer.exec(context.program()); + return context.program(); + } + + +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/general/TranslatorVisitor.java b/js/js.backend/src/org/jetbrains/k2js/translate/general/TranslatorVisitor.java new file mode 100644 index 00000000000..c42299032f2 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/general/TranslatorVisitor.java @@ -0,0 +1,37 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.general; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.JetElement; +import org.jetbrains.jet.lang.psi.JetVisitor; +import org.jetbrains.k2js.translate.context.TranslationContext; + +/** + * @author Pavel Talanov + *

+ * This class is a base class for all visitors. + */ +public class TranslatorVisitor extends JetVisitor { + + @Override + @NotNull + public T visitJetElement(JetElement expression, TranslationContext context) { + throw new UnsupportedOperationException("Unsupported expression encountered:" + expression.toString()); + } + +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/initializer/AbstractInitializerTranslator.java b/js/js.backend/src/org/jetbrains/k2js/translate/initializer/AbstractInitializerTranslator.java new file mode 100644 index 00000000000..6c53ec13495 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/initializer/AbstractInitializerTranslator.java @@ -0,0 +1,68 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.initializer; + +import com.google.dart.compiler.backend.js.ast.JsFunction; +import com.google.dart.compiler.backend.js.ast.JsPropertyInitializer; +import com.google.dart.compiler.backend.js.ast.JsStatement; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor; +import org.jetbrains.jet.lang.psi.JetClassOrObject; +import org.jetbrains.k2js.translate.context.Namer; +import org.jetbrains.k2js.translate.context.NamingScope; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.general.AbstractTranslator; + +import java.util.List; + +/** + * @author Pavel Talanov + */ +public abstract class AbstractInitializerTranslator extends AbstractTranslator { + + @NotNull + private final InitializerVisitor visitor; + @NotNull + protected final NamingScope initializerMethodScope; + + protected AbstractInitializerTranslator(@NotNull NamingScope scope, @NotNull TranslationContext context) { + super(context.contextWithScope(scope)); + this.visitor = new InitializerVisitor(); + this.initializerMethodScope = scope; + } + + abstract protected JsFunction generateInitializerFunction(); + + @NotNull + public JsPropertyInitializer generateInitializeMethod() { + JsPropertyInitializer initializer = new JsPropertyInitializer(); + initializer.setLabelExpr(Namer.initializeMethodReference()); + initializer.setValueExpr(generateInitializerFunction()); + return initializer; + } + + @NotNull + protected List translateClassInitializers(@NotNull JetClassOrObject declaration) { + return visitor.traverseClass(declaration, context()); + } + + @NotNull + protected List translateNamespaceInitializers(@NotNull NamespaceDescriptor namespace) { + return visitor.traverseNamespace(namespace, context()); + + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/initializer/ClassInitializerTranslator.java b/js/js.backend/src/org/jetbrains/k2js/translate/initializer/ClassInitializerTranslator.java new file mode 100644 index 00000000000..374299eb26a --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/initializer/ClassInitializerTranslator.java @@ -0,0 +1,141 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.initializer; + +import com.google.dart.compiler.backend.js.ast.*; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; +import org.jetbrains.jet.lang.descriptors.PropertyDescriptor; +import org.jetbrains.jet.lang.psi.JetClassOrObject; +import org.jetbrains.jet.lang.psi.JetDelegationSpecifier; +import org.jetbrains.jet.lang.psi.JetDelegatorToSuperCall; +import org.jetbrains.jet.lang.psi.JetParameter; +import org.jetbrains.k2js.translate.context.Namer; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.utils.PsiUtils; +import org.jetbrains.k2js.translate.utils.TranslationUtils; + +import java.util.ArrayList; +import java.util.List; + +import static org.jetbrains.k2js.translate.utils.BindingUtils.*; +import static org.jetbrains.k2js.translate.utils.TranslationUtils.assignmentToBackingField; +import static org.jetbrains.k2js.translate.utils.TranslationUtils.functionWithScope; + +/** + * @author Pavel Talanov + */ +public final class ClassInitializerTranslator extends AbstractInitializerTranslator { + + @NotNull + private final JetClassOrObject classDeclaration; + @NotNull + private final List initializerStatements = new ArrayList(); + + public ClassInitializerTranslator(@NotNull JetClassOrObject classDeclaration, @NotNull TranslationContext context) { + super(context.getScopeForElement(classDeclaration).innerScope + ("initializer " + classDeclaration.getName()), context); + this.classDeclaration = classDeclaration; + } + + @Override + @NotNull + protected JsFunction generateInitializerFunction() { + JsFunction result = functionWithScope(initializerMethodScope); + //NOTE: that while we translateAsLocalNameReference constructor parameters we also add property initializer statements + // for properties declared as constructor parameters + result.setParameters(translatePrimaryConstructorParameters()); + mayBeAddCallToSuperMethod(); + result.setBody(generateInitializerMethodBody()); + return result; + } + + @NotNull + private JsBlock generateInitializerMethodBody() { + initializerStatements.addAll(translateClassInitializers(classDeclaration)); + return AstUtil.newBlock(initializerStatements); + } + + private void mayBeAddCallToSuperMethod() { + if (hasAncestorClass(bindingContext(), classDeclaration)) { + JetDelegatorToSuperCall superCall = getSuperCall(); + if (superCall == null) return; + addCallToSuperMethod(superCall); + } + } + + private void addCallToSuperMethod(@NotNull JetDelegatorToSuperCall superCall) { + //TODO: look into + JsName superMethodName = initializerMethodScope.jsScope().declareName(Namer.superMethodName()); + List arguments = translateArguments(superCall); + initializerStatements.add(AstUtil.convertToStatement + (AstUtil.newInvocation(AstUtil.thisQualifiedReference(superMethodName), arguments))); + } + + @NotNull + private List translateArguments(@NotNull JetDelegatorToSuperCall superCall) { + //TODO: use the same mechanism as in call translator + return TranslationUtils.translateArgumentList(context(), superCall.getValueArguments()); + } + + @Nullable + private JetDelegatorToSuperCall getSuperCall() { + JetDelegatorToSuperCall result = null; + for (JetDelegationSpecifier specifier : classDeclaration.getDelegationSpecifiers()) { + if (specifier instanceof JetDelegatorToSuperCall) { + result = (JetDelegatorToSuperCall) specifier; + } + } + //assert result != null : "Class must call ancestor's constructor."; + return result; + } + + @NotNull + List translatePrimaryConstructorParameters() { + List parameterList = PsiUtils.getPrimaryConstructorParameters(classDeclaration); + List result = new ArrayList(); + for (JetParameter jetParameter : parameterList) { + result.add(translateParameter(jetParameter)); + } + return result; + } + + @NotNull + private JsParameter translateParameter(@NotNull JetParameter jetParameter) { + DeclarationDescriptor parameterDescriptor = + getDescriptorForElement(bindingContext(), jetParameter); + JsName parameterName = context().getNameForDescriptor(parameterDescriptor); + JsParameter jsParameter = new JsParameter(parameterName); + mayBeAddInitializerStatementForProperty(jsParameter, jetParameter); + return jsParameter; + } + + private void mayBeAddInitializerStatementForProperty(@NotNull JsParameter jsParameter, + @NotNull JetParameter jetParameter) { + PropertyDescriptor propertyDescriptor = + getPropertyDescriptorForConstructorParameter(bindingContext(), jetParameter); + if (propertyDescriptor != null) { + JsStatement assignmentToBackingFieldExpression = assignmentToBackingField + (context(), propertyDescriptor, jsParameter.getName().makeRef()); + initializerStatements.add(assignmentToBackingFieldExpression); + } + } + + +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/initializer/InitializerVisitor.java b/js/js.backend/src/org/jetbrains/k2js/translate/initializer/InitializerVisitor.java new file mode 100644 index 00000000000..20b5cc5f835 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/initializer/InitializerVisitor.java @@ -0,0 +1,122 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.initializer; + +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsInvocation; +import com.google.dart.compiler.backend.js.ast.JsName; +import com.google.dart.compiler.backend.js.ast.JsStatement; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor; +import org.jetbrains.jet.lang.descriptors.PropertyDescriptor; +import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.declaration.ClassTranslator; +import org.jetbrains.k2js.translate.general.Translation; +import org.jetbrains.k2js.translate.general.TranslatorVisitor; +import org.jetbrains.k2js.translate.utils.BindingUtils; +import org.jetbrains.k2js.translate.utils.TranslationUtils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static java.util.Collections.singletonList; +import static org.jetbrains.k2js.translate.utils.BindingUtils.getDeclarationsForNamespace; +import static org.jetbrains.k2js.translate.utils.BindingUtils.getPropertyDescriptorForObjectDeclaration; +import static org.jetbrains.k2js.translate.utils.PsiUtils.getObjectDeclarationForName; + +/** + * @author Pavel Talanov + */ +public final class InitializerVisitor extends TranslatorVisitor> { + + /*package*/ InitializerVisitor() { + } + + @Override + @NotNull + public List visitProperty(@NotNull JetProperty expression, @NotNull TranslationContext context) { + JetExpression initializer = expression.getInitializer(); + if (initializer == null) { + return new ArrayList(); + } + return Arrays.asList(translateInitializer(expression, context, initializer)); + } + + @NotNull + private JsStatement translateInitializer(@NotNull JetProperty property, @NotNull TranslationContext context, + @NotNull JetExpression initializer) { + JsExpression initExpression = Translation.translateAsExpression(initializer, context); + return assignmentToBackingField(property, initExpression, context); + } + + //TODO: + @NotNull + JsStatement assignmentToBackingField(@NotNull JetProperty property, @NotNull JsExpression initExpression, + @NotNull TranslationContext context) { + + PropertyDescriptor propertyDescriptor = BindingUtils.getPropertyDescriptor(context.bindingContext(), property); + return TranslationUtils.assignmentToBackingField(context, propertyDescriptor, initExpression); + } + + @Override + @NotNull + public List visitAnonymousInitializer(@NotNull JetClassInitializer initializer, + @NotNull TranslationContext context) { + return Arrays.asList(Translation.translateAsStatement(initializer.getBody(), context)); + } + + @Override + @NotNull + // Not interested in other types of declarations, they do not contain initializers. + public List visitDeclaration(@NotNull JetDeclaration expression, @NotNull TranslationContext context) { + return Collections.emptyList(); + } + + @Override + @NotNull + public List visitObjectDeclarationName(@NotNull JetObjectDeclarationName objectName, + @NotNull TranslationContext context) { + PropertyDescriptor propertyDescriptorForObjectDeclaration + = getPropertyDescriptorForObjectDeclaration(context.bindingContext(), objectName); + JsName objectPropertyName = context.getNameForDescriptor(propertyDescriptorForObjectDeclaration); + JetObjectDeclaration objectDeclaration = getObjectDeclarationForName(objectName); + JsInvocation objectValue = ClassTranslator.generateClassCreationExpression(objectDeclaration, context); + return singletonList(TranslationUtils.assignmentToBackingField(context, + propertyDescriptorForObjectDeclaration, objectValue)); + } + + @NotNull + public List traverseClass(@NotNull JetClassOrObject expression, @NotNull TranslationContext context) { + List initializerStatements = new ArrayList(); + for (JetDeclaration declaration : expression.getDeclarations()) { + initializerStatements.addAll(declaration.accept(this, context)); + } + return initializerStatements; + } + + @NotNull + public List traverseNamespace(@NotNull NamespaceDescriptor namespace, @NotNull TranslationContext context) { + List initializerStatements = new ArrayList(); + for (JetDeclaration declaration : getDeclarationsForNamespace(context.bindingContext(), namespace)) { + initializerStatements.addAll(declaration.accept(this, context)); + } + return initializerStatements; + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/initializer/NamespaceInitializerTranslator.java b/js/js.backend/src/org/jetbrains/k2js/translate/initializer/NamespaceInitializerTranslator.java new file mode 100644 index 00000000000..4132315e645 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/initializer/NamespaceInitializerTranslator.java @@ -0,0 +1,50 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.initializer; + +import com.google.dart.compiler.backend.js.ast.JsFunction; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor; +import org.jetbrains.k2js.translate.context.TranslationContext; + +import static org.jetbrains.k2js.translate.utils.TranslationUtils.functionWithScope; + +/** + * @author Pavel Talanov + */ +public final class NamespaceInitializerTranslator extends AbstractInitializerTranslator { + + @NotNull + private final NamespaceDescriptor namespace; + + public NamespaceInitializerTranslator(@NotNull NamespaceDescriptor namespace, @NotNull TranslationContext context) { + super(context.getScopeForDescriptor(namespace).innerScope + ("initializer " + namespace.getName()), context); + this.namespace = namespace; + } + + @Override + @NotNull + protected JsFunction generateInitializerFunction() { + JsFunction result = functionWithScope(initializerMethodScope); + result.setBody(AstUtil.newBlock(translateNamespaceInitializers(namespace))); + return result; + } + + +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/CompareToIntrinsic.java b/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/CompareToIntrinsic.java new file mode 100644 index 00000000000..0dbeeb4fca4 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/CompareToIntrinsic.java @@ -0,0 +1,41 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.intrinsic; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.types.expressions.OperatorConventions; +import org.jetbrains.jet.lexer.JetToken; + +/** + * @author Pavel Talanov + */ +public abstract class CompareToIntrinsic implements Intrinsic { + + private JetToken comparisonToken = null; + + @NotNull + protected JetToken getComparisonToken() { + assert comparisonToken != null : "Should use set token first"; + return comparisonToken; + } + + public void setComparisonToken(@NotNull JetToken comparisonToken) { + assert OperatorConventions.COMPARISON_OPERATIONS.contains(comparisonToken) + : "Should be a comparison operation"; + this.comparisonToken = comparisonToken; + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/EqualsIntrinsic.java b/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/EqualsIntrinsic.java new file mode 100644 index 00000000000..3cd074970eb --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/EqualsIntrinsic.java @@ -0,0 +1,36 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.intrinsic; + +import org.jetbrains.annotations.NotNull; + +/** + * @author Pavel Talanov + */ +public abstract class EqualsIntrinsic implements Intrinsic { + + @NotNull + private Boolean isNegated = false; + + public void setNegated(boolean isNegated) { + this.isNegated = isNegated; + } + + protected boolean isNegated() { + return isNegated; + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/FunctionIntrinsic.java b/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/FunctionIntrinsic.java new file mode 100644 index 00000000000..9a5e0eab723 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/FunctionIntrinsic.java @@ -0,0 +1,24 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.intrinsic; + +/** + * @author Pavel Talanov + */ +public interface FunctionIntrinsic extends Intrinsic { + +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/Intrinsic.java b/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/Intrinsic.java new file mode 100644 index 00000000000..425e5f04b68 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/Intrinsic.java @@ -0,0 +1,36 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.intrinsic; + +import com.google.dart.compiler.backend.js.ast.JsExpression; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.k2js.translate.context.TranslationContext; + +import java.util.List; + +/** + * @author Pavel Talanov + *

+ * Base class for all intrinsics. + */ +public interface Intrinsic { + + @NotNull + JsExpression apply(@Nullable JsExpression receiver, @NotNull List arguments, + @NotNull TranslationContext context); +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/Intrinsics.java b/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/Intrinsics.java new file mode 100644 index 00000000000..9893e8f489d --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/Intrinsics.java @@ -0,0 +1,257 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.intrinsic; + +import com.google.common.collect.Lists; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.*; +import org.jetbrains.jet.lang.resolve.scopes.JetScope; +import org.jetbrains.jet.lang.types.JetStandardClasses; +import org.jetbrains.jet.lang.types.JetStandardLibrary; +import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.lang.types.PrimitiveType; +import org.jetbrains.jet.lexer.JetToken; +import org.jetbrains.k2js.translate.intrinsic.array.*; +import org.jetbrains.k2js.translate.intrinsic.primitive.*; +import org.jetbrains.k2js.translate.intrinsic.string.CharAtIntrinsic; +import org.jetbrains.k2js.translate.intrinsic.string.LengthIntrinsic; +import org.jetbrains.k2js.translate.intrinsic.tuple.TupleAccessIntrinsic; +import org.jetbrains.k2js.translate.operation.OperatorTable; +import org.jetbrains.k2js.translate.utils.DescriptorUtils; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.jetbrains.jet.lang.types.expressions.OperatorConventions.*; +import static org.jetbrains.k2js.translate.utils.DescriptorUtils.getFunctionByName; +import static org.jetbrains.k2js.translate.utils.DescriptorUtils.getPropertyByName; + +/** + * @author Pavel Talanov + *

+ * Provides mechanism to substitute method calls /w native constucts directly. + */ +public final class Intrinsics { + + @NotNull + private final Map functionIntrinsics = + new HashMap(); + + @NotNull + private final Map equalsIntrinsics = + new HashMap(); + + @NotNull + private final Map compareToIntrinsics = + new HashMap(); + + public static Intrinsics standardLibraryIntrinsics(@NotNull JetStandardLibrary library) { + return new Intrinsics(library); + } + + @NotNull + private final JetStandardLibrary library; + + private Intrinsics(@NotNull JetStandardLibrary library) { + this.library = library; + declareOperatorIntrinsics(); + declareStringIntrinsics(); + declareTuplesIntrinsics(); + declareArrayIntrinsics(); + } + + private void declareTuplesIntrinsics() { + for (int tupleSize = 0; tupleSize < JetStandardClasses.TUPLE_COUNT; ++tupleSize) { + declareTupleIntrinsics(tupleSize); + } + } + + //TODO: provide generic mechanism or refactor + private void declareArrayIntrinsics() { + + List arrayTypes = getLibraryArrayTypes(); + + for (JetType arrayType : arrayTypes) { + declareIntrinsicsForArrayType(arrayType); + } + declareNullConstructorIntrinsic(); + } + + private void declareNullConstructorIntrinsic() { + //TODO: + FunctionDescriptor nullArrayConstructor = library.getLibraryScope().getFunctions("Array").iterator().next(); + functionIntrinsics.put(nullArrayConstructor, ArrayNullConstructorIntrinsic.INSTANCE); + } + + //TODO: some dangerous operation unchecked here + private void declareIntrinsicsForArrayType(@NotNull JetType arrayType) { + JetScope arrayMemberScope = arrayType.getMemberScope(); + FunctionDescriptor setFunction = getFunctionByName(arrayMemberScope, "set"); + functionIntrinsics.put(setFunction, ArraySetIntrinsic.INSTANCE); + FunctionDescriptor getFunction = getFunctionByName(arrayMemberScope, "get"); + functionIntrinsics.put(getFunction, ArrayGetIntrinsic.INSTANCE); + PropertyDescriptor sizeProperty = getPropertyByName(arrayMemberScope, "size"); + functionIntrinsics.put(sizeProperty.getGetter(), ArraySizeIntrinsic.INSTANCE); + PropertyDescriptor indicesProperty = getPropertyByName(arrayMemberScope, "indices"); + functionIntrinsics.put(indicesProperty.getGetter(), ArrayIndicesIntrinsic.INSTANCE); + FunctionDescriptor iteratorFunction = getFunctionByName(arrayMemberScope, "iterator"); + functionIntrinsics.put(iteratorFunction, ArrayIteratorIntrinsic.INSTANCE); + ConstructorDescriptor arrayConstructor = + ((ClassDescriptor) arrayMemberScope.getContainingDeclaration()).getConstructors().iterator().next(); + functionIntrinsics.put(arrayConstructor, ArrayFunctionConstructorIntrinsic.INSTANCE); + } + + private List getLibraryArrayTypes() { + List arrayTypes = Lists.newArrayList(); + for (PrimitiveType type : PrimitiveType.values()) { + arrayTypes.add(library.getPrimitiveArrayJetType(type)); + } + arrayTypes.add(library.getArray().getDefaultType()); + return arrayTypes; + } + + private void declareTupleIntrinsics(int tupleSize) { + JetScope libraryScope = library.getLibraryScope(); + assert libraryScope != null; + ClassifierDescriptor tupleDescriptor = libraryScope.getClassifier("Tuple" + tupleSize); + assert tupleDescriptor != null; + declareTupleIntrinsicAccessors(tupleDescriptor, tupleSize); + } + + private void declareStringIntrinsics() { + PropertyDescriptor lengthProperty = + getPropertyByName(library.getCharSequence().getDefaultType().getMemberScope(), "length"); + functionIntrinsics.put(lengthProperty.getGetter(), LengthIntrinsic.INSTANCE); + FunctionDescriptor getFunction = + getFunctionByName(library.getString().getDefaultType().getMemberScope(), "get"); + functionIntrinsics.put(getFunction, CharAtIntrinsic.INSTANCE); + } + + private void declareTupleIntrinsicAccessors(@NotNull ClassifierDescriptor tupleDescriptor, + int tupleSize) { + for (int elementIndex = 0; elementIndex < tupleSize; ++elementIndex) { + String accessorName = "_" + (elementIndex + 1); + PropertyDescriptor propertyDescriptor = + getPropertyByName(tupleDescriptor.getDefaultType().getMemberScope(), accessorName); + functionIntrinsics.put(propertyDescriptor.getGetter(), new TupleAccessIntrinsic(elementIndex)); + } + } + + private void declareOperatorIntrinsics() { + IntrinsicDeclarationVisitor visitor = new IntrinsicDeclarationVisitor(); + for (DeclarationDescriptor descriptor : library.getLibraryScope().getAllDescriptors()) { + //noinspection NullableProblems + descriptor.accept(visitor, null); + } + } + + public boolean isIntrinsic(@NotNull DeclarationDescriptor descriptor) { + //NOTE: that if we want to add other intrinsics we have to modify this method + if (descriptor instanceof FunctionDescriptor) { + FunctionDescriptor functionDescriptor = (FunctionDescriptor) descriptor.getOriginal(); + return (equalsIntrinsics.containsKey(functionDescriptor) || + compareToIntrinsics.containsKey(functionDescriptor) || + functionIntrinsics.containsKey(functionDescriptor)); + } + return false; + } + + @NotNull + public FunctionIntrinsic getFunctionIntrinsic(@NotNull FunctionDescriptor descriptor) { + return functionIntrinsics.get(descriptor.getOriginal()); + } + + @NotNull + public CompareToIntrinsic getCompareToIntrinsic(@NotNull FunctionDescriptor descriptor) { + return compareToIntrinsics.get(descriptor.getOriginal()); + } + + @NotNull + public EqualsIntrinsic getEqualsIntrinsic(@NotNull FunctionDescriptor descriptor) { + return equalsIntrinsics.get(descriptor.getOriginal()); + } + + private final class IntrinsicDeclarationVisitor extends DeclarationDescriptorVisitor { + + @Override + public Void visitClassDescriptor(@NotNull ClassDescriptor descriptor, @Nullable Void nothing) { + for (DeclarationDescriptor memberDescriptor : + descriptor.getDefaultType().getMemberScope().getAllDescriptors()) { + //noinspection NullableProblems + memberDescriptor.accept(this, null); + } + return null; + } + + @Override + public Void visitFunctionDescriptor(@NotNull FunctionDescriptor descriptor, @Nullable Void nothing) { + if (!isIntrinsic(descriptor)) { + declareOperatorIntrinsic(descriptor); + } + return null; + } + + + /*package*/ void declareOperatorIntrinsic(@NotNull FunctionDescriptor descriptor) { + tryResolveAsEqualsCompareToOrRangeToIntrinsic(descriptor); + tryResolveAsUnaryIntrinsics(descriptor); + tryResolveAsBinaryIntrinsics(descriptor); + } + + private void tryResolveAsEqualsCompareToOrRangeToIntrinsic(@NotNull FunctionDescriptor descriptor) { + String functionName = descriptor.getName(); + if (functionName.equals(COMPARE_TO)) { + compareToIntrinsics.put(descriptor, PrimitiveCompareToIntrinsic.newInstance()); + } + if (functionName.equals(EQUALS)) { + equalsIntrinsics.put(descriptor, PrimitiveEqualsIntrinsic.newInstance()); + } + if (functionName.equals("rangeTo")) { + functionIntrinsics.put(descriptor, PrimitiveRangeToIntrinsic.newInstance()); + } + } + + private void tryResolveAsUnaryIntrinsics(@NotNull FunctionDescriptor descriptor) { + String functionName = descriptor.getName(); + JetToken token = UNARY_OPERATION_NAMES.inverse().get(functionName); + + if (token == null) return; + if (!isUnaryOperation(descriptor)) return; + + functionIntrinsics.put(descriptor, PrimitiveUnaryOperationIntrinsic.newInstance(token)); + } + + private void tryResolveAsBinaryIntrinsics(@NotNull FunctionDescriptor descriptor) { + String functionName = descriptor.getName(); + + if (isUnaryOperation(descriptor)) return; + + JetToken token = BINARY_OPERATION_NAMES.inverse().get(functionName); + if (token == null) return; + + if (!OperatorTable.hasCorrespondingBinaryOperator(token)) return; + functionIntrinsics.put(descriptor, PrimitiveBinaryOperationIntrinsic.newInstance(token)); + } + + private boolean isUnaryOperation(@NotNull FunctionDescriptor descriptor) { + return !DescriptorUtils.hasParameters(descriptor); + } + } + +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/array/ArrayFunctionConstructorIntrinsic.java b/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/array/ArrayFunctionConstructorIntrinsic.java new file mode 100644 index 00000000000..6cd6f5cd2fd --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/array/ArrayFunctionConstructorIntrinsic.java @@ -0,0 +1,48 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.intrinsic.array; + +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsNameRef; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.intrinsic.FunctionIntrinsic; + +import java.util.List; + +/** + * @author Pavel Talanov + */ +public enum ArrayFunctionConstructorIntrinsic implements FunctionIntrinsic { + + INSTANCE; + + + @NotNull + @Override + public JsExpression apply(@Nullable JsExpression receiver, + @NotNull List arguments, + @NotNull TranslationContext context) { + assert receiver == null; + assert arguments.size() == 2; + //TODO: provide better mechanism + JsNameRef iteratorFunName = AstUtil.newQualifiedNameRef("Kotlin.arrayFromFun"); + return AstUtil.newInvocation(iteratorFunName, arguments); + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/array/ArrayGetIntrinsic.java b/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/array/ArrayGetIntrinsic.java new file mode 100644 index 00000000000..ea0d565247d --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/array/ArrayGetIntrinsic.java @@ -0,0 +1,44 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.intrinsic.array; + +import com.google.dart.compiler.backend.js.ast.JsArrayAccess; +import com.google.dart.compiler.backend.js.ast.JsExpression; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.intrinsic.FunctionIntrinsic; + +import java.util.List; + +/** + * @author Pavel Talanov + */ +public enum ArrayGetIntrinsic implements FunctionIntrinsic { + + INSTANCE; + + @NotNull + @Override + public JsExpression apply(@Nullable JsExpression receiver, @NotNull List arguments, + @NotNull TranslationContext context) { + assert receiver != null; + assert arguments.size() == 1 : "Array get expression must have one argument."; + JsExpression indexExpression = arguments.get(0); + return new JsArrayAccess(receiver, indexExpression); + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/array/ArrayIndicesIntrinsic.java b/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/array/ArrayIndicesIntrinsic.java new file mode 100644 index 00000000000..c9e9f9d1005 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/array/ArrayIndicesIntrinsic.java @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.intrinsic.array; + +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsNameRef; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.intrinsic.FunctionIntrinsic; + +import java.util.List; + +/** + * @author Pavel Talanov + */ +public enum ArrayIndicesIntrinsic implements FunctionIntrinsic { + + INSTANCE; + + + @NotNull + @Override + public JsExpression apply(@Nullable JsExpression receiver, @NotNull List arguments, + @NotNull TranslationContext context) { + assert receiver != null; + assert arguments.size() == 0; + //TODO: provide better mechanism + JsNameRef iteratorFunName = AstUtil.newQualifiedNameRef("Kotlin.arrayIndices"); + return AstUtil.newInvocation(iteratorFunName, receiver); + } +} \ No newline at end of file diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/array/ArrayIteratorIntrinsic.java b/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/array/ArrayIteratorIntrinsic.java new file mode 100644 index 00000000000..cc04aa26246 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/array/ArrayIteratorIntrinsic.java @@ -0,0 +1,46 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.intrinsic.array; + +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsNameRef; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.intrinsic.FunctionIntrinsic; + +import java.util.List; + +/** + * @author Pavel Talanov + */ +public enum ArrayIteratorIntrinsic implements FunctionIntrinsic { + + INSTANCE; + + @NotNull + @Override + public JsExpression apply(@Nullable JsExpression receiver, @NotNull List arguments, + @NotNull TranslationContext context) { + assert receiver != null; + assert arguments.size() == 0; + //TODO: provide better mechanism + JsNameRef iteratorFunName = AstUtil.newQualifiedNameRef("Kotlin.arrayIterator"); + return AstUtil.newInvocation(iteratorFunName, receiver); + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/array/ArrayNullConstructorIntrinsic.java b/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/array/ArrayNullConstructorIntrinsic.java new file mode 100644 index 00000000000..a9e0ee1cea3 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/array/ArrayNullConstructorIntrinsic.java @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.intrinsic.array; + +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsNameRef; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.intrinsic.FunctionIntrinsic; + +import java.util.List; + +/** + * @author Pavel Talanov + */ +public enum ArrayNullConstructorIntrinsic implements FunctionIntrinsic { + + INSTANCE; + + //TODO: implement function passing to array constructor + @NotNull + @Override + public JsExpression apply(@Nullable JsExpression receiver, @NotNull List arguments, + @NotNull TranslationContext context) { + assert receiver == null; + assert arguments.size() == 1; + //TODO: provide better mechanism + JsNameRef nullArrayFunName = AstUtil.newQualifiedNameRef("Kotlin.nullArray"); + return AstUtil.newInvocation(nullArrayFunName, arguments); + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/array/ArraySetIntrinsic.java b/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/array/ArraySetIntrinsic.java new file mode 100644 index 00000000000..03f41d57c7c --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/array/ArraySetIntrinsic.java @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.intrinsic.array; + +import com.google.dart.compiler.backend.js.ast.JsArrayAccess; +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.intrinsic.FunctionIntrinsic; + +import java.util.List; + +/** + * @author Pavel Talanov + */ +public enum ArraySetIntrinsic implements FunctionIntrinsic { + + INSTANCE; + + @NotNull + @Override + public JsExpression apply(@Nullable JsExpression receiver, @NotNull List arguments, + @NotNull TranslationContext context) { + assert receiver != null; + assert arguments.size() == 2 : "Array set expression must have two arguments."; + JsExpression indexExpression = arguments.get(0); + JsExpression value = arguments.get(1); + JsArrayAccess arrayAccess = AstUtil.newArrayAccess(receiver, indexExpression); + return AstUtil.newAssignment(arrayAccess, value); + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/array/ArraySizeIntrinsic.java b/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/array/ArraySizeIntrinsic.java new file mode 100644 index 00000000000..69a04d2231f --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/array/ArraySizeIntrinsic.java @@ -0,0 +1,48 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.intrinsic.array; + +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsNameRef; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.intrinsic.FunctionIntrinsic; + +import java.util.List; + +/** + * @author Pavel Talanov + */ +public enum ArraySizeIntrinsic implements FunctionIntrinsic { + + INSTANCE; + + + @NotNull + @Override + public JsExpression apply(@Nullable JsExpression receiver, @NotNull List arguments, + @NotNull TranslationContext context) { + assert receiver != null; + assert arguments.isEmpty() : "Length expression must have zero arguments."; + //TODO: provide better way + JsNameRef lengthProperty = AstUtil.newQualifiedNameRef("length"); + AstUtil.setQualifier(lengthProperty, receiver); + return lengthProperty; + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/primitive/PrimitiveBinaryOperationIntrinsic.java b/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/primitive/PrimitiveBinaryOperationIntrinsic.java new file mode 100644 index 00000000000..5d6039319e9 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/primitive/PrimitiveBinaryOperationIntrinsic.java @@ -0,0 +1,56 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.intrinsic.primitive; + +import com.google.dart.compiler.backend.js.ast.JsBinaryOperation; +import com.google.dart.compiler.backend.js.ast.JsBinaryOperator; +import com.google.dart.compiler.backend.js.ast.JsExpression; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lexer.JetToken; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.intrinsic.FunctionIntrinsic; +import org.jetbrains.k2js.translate.operation.OperatorTable; + +import java.util.List; + +/** + * @author Pavel Talanov + */ +public final class PrimitiveBinaryOperationIntrinsic implements FunctionIntrinsic { + + @NotNull + public static PrimitiveBinaryOperationIntrinsic newInstance(@NotNull JetToken token) { + JsBinaryOperator operator = OperatorTable.getBinaryOperator(token); + return new PrimitiveBinaryOperationIntrinsic(operator); + } + + @NotNull + private final JsBinaryOperator operator; + + private PrimitiveBinaryOperationIntrinsic(@NotNull JsBinaryOperator operator) { + this.operator = operator; + } + + @NotNull + @Override + public JsExpression apply(@Nullable JsExpression receiver, @NotNull List arguments, + @NotNull TranslationContext context) { + assert arguments.size() == 1 : "Binary operator should have a receiver and one argument"; + return new JsBinaryOperation(operator, receiver, arguments.get(0)); + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/primitive/PrimitiveCompareToIntrinsic.java b/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/primitive/PrimitiveCompareToIntrinsic.java new file mode 100644 index 00000000000..c43708008e5 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/primitive/PrimitiveCompareToIntrinsic.java @@ -0,0 +1,52 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.intrinsic.primitive; + +import com.google.dart.compiler.backend.js.ast.JsBinaryOperation; +import com.google.dart.compiler.backend.js.ast.JsBinaryOperator; +import com.google.dart.compiler.backend.js.ast.JsExpression; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.intrinsic.CompareToIntrinsic; +import org.jetbrains.k2js.translate.operation.OperatorTable; + +import java.util.List; + +/** + * @author Pavel Talanov + */ +public final class PrimitiveCompareToIntrinsic extends CompareToIntrinsic { + + @NotNull + public static PrimitiveCompareToIntrinsic newInstance() { + return new PrimitiveCompareToIntrinsic(); + } + + private PrimitiveCompareToIntrinsic() { + } + + @NotNull + public JsExpression apply(@Nullable JsExpression receiver, @NotNull List arguments, + @NotNull TranslationContext context) { + assert arguments.size() == 1 : "Equals operation should have one argument"; + JsBinaryOperator operator = OperatorTable.getBinaryOperator(getComparisonToken()); + JsExpression argument = arguments.get(0); + return new JsBinaryOperation(operator, receiver, argument); + } + +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/primitive/PrimitiveEqualsIntrinsic.java b/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/primitive/PrimitiveEqualsIntrinsic.java new file mode 100644 index 00000000000..05cd57be9cd --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/primitive/PrimitiveEqualsIntrinsic.java @@ -0,0 +1,52 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.intrinsic.primitive; + +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.intrinsic.EqualsIntrinsic; + +import java.util.List; + +/** + * @author Pavel Talanov + */ +public final class PrimitiveEqualsIntrinsic extends EqualsIntrinsic { + + @NotNull + public static PrimitiveEqualsIntrinsic newInstance() { + return new PrimitiveEqualsIntrinsic(); + } + + private PrimitiveEqualsIntrinsic() { + } + + @NotNull + public JsExpression apply(@Nullable JsExpression receiver, @NotNull List arguments, + @NotNull TranslationContext context) { + assert arguments.size() == 1 : "Equals operation should have one argument"; + if (isNegated()) { + return AstUtil.notEqual(receiver, arguments.get(0)); + } else { + return AstUtil.equals(receiver, arguments.get(0)); + } + } + +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/primitive/PrimitiveRangeToIntrinsic.java b/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/primitive/PrimitiveRangeToIntrinsic.java new file mode 100644 index 00000000000..723beaf07dc --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/primitive/PrimitiveRangeToIntrinsic.java @@ -0,0 +1,61 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.intrinsic.primitive; + +import com.google.dart.compiler.backend.js.ast.JsBinaryOperation; +import com.google.dart.compiler.backend.js.ast.JsBooleanLiteral; +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsNew; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.intrinsic.FunctionIntrinsic; + +import java.util.Arrays; +import java.util.List; + +/** + * @author Pavel Talanov + */ +public final class PrimitiveRangeToIntrinsic implements FunctionIntrinsic { + + @NotNull + public static PrimitiveRangeToIntrinsic newInstance() { + return new PrimitiveRangeToIntrinsic(); + } + + private PrimitiveRangeToIntrinsic() { + } + + @NotNull + @Override + public JsExpression apply(@Nullable JsExpression rangeStart, @NotNull List arguments, + @NotNull TranslationContext context) { + assert arguments.size() == 1 : "RangeTo must have one argument."; + JsExpression rangeEnd = arguments.get(0); + JsBinaryOperation rangeSize = AstUtil.sum(AstUtil.subtract(rangeEnd, rangeStart), + context.program().getNumberLiteral(1)); + //TODO: provide a way not to hard code this value + JsNew numberRangeConstructorInvocation + = new JsNew(AstUtil.newQualifiedNameRef("Kotlin.NumberRange")); + //TODO: add tests and correct expression for reversed ranges. + JsBooleanLiteral isRangeReversed = context.program().getFalseLiteral(); + numberRangeConstructorInvocation.setArguments(Arrays.asList(rangeStart, rangeSize, isRangeReversed)); + return numberRangeConstructorInvocation; + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/primitive/PrimitiveUnaryOperationIntrinsic.java b/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/primitive/PrimitiveUnaryOperationIntrinsic.java new file mode 100644 index 00000000000..817461472a3 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/primitive/PrimitiveUnaryOperationIntrinsic.java @@ -0,0 +1,58 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.intrinsic.primitive; + +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsPrefixOperation; +import com.google.dart.compiler.backend.js.ast.JsUnaryOperator; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lexer.JetToken; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.intrinsic.FunctionIntrinsic; +import org.jetbrains.k2js.translate.operation.OperatorTable; + +import java.util.List; + +/** + * @author Pavel Talanov + */ +public final class PrimitiveUnaryOperationIntrinsic implements FunctionIntrinsic { + + @NotNull + public static PrimitiveUnaryOperationIntrinsic newInstance(@NotNull JetToken token) { + JsUnaryOperator operator = OperatorTable.getUnaryOperator(token); + return new PrimitiveUnaryOperationIntrinsic(operator); + } + + @NotNull + private final JsUnaryOperator operator; + + private PrimitiveUnaryOperationIntrinsic(@NotNull JsUnaryOperator operator) { + this.operator = operator; + } + + @NotNull + @Override + public JsExpression apply(@Nullable JsExpression receiver, @NotNull List arguments, + @NotNull TranslationContext context) { + assert receiver != null; + assert arguments.size() == 0 : "Unary operator should not have arguments."; + //NOTE: cannot use this for increment/decrement + return new JsPrefixOperation(operator, receiver); + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/string/CharAtIntrinsic.java b/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/string/CharAtIntrinsic.java new file mode 100644 index 00000000000..e2bcfb82eb3 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/string/CharAtIntrinsic.java @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.intrinsic.string; + +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsNameRef; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.intrinsic.FunctionIntrinsic; + +import java.util.List; + +/** + * @author Pavel Talanov + */ +public enum CharAtIntrinsic implements FunctionIntrinsic { + + INSTANCE; + + @NotNull + @Override + public JsExpression apply(@Nullable JsExpression receiver, @NotNull List arguments, + @NotNull TranslationContext context) { + assert receiver != null; + assert arguments.size() == 1 : "get Char expression must have 1 arguments."; + //TODO: provide better way + JsNameRef charAtReference = AstUtil.newQualifiedNameRef("charAt"); + AstUtil.setQualifier(charAtReference, receiver); + return AstUtil.newInvocation(charAtReference, arguments); + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/string/LengthIntrinsic.java b/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/string/LengthIntrinsic.java new file mode 100644 index 00000000000..3e66610f8e2 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/string/LengthIntrinsic.java @@ -0,0 +1,48 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.intrinsic.string; + + +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsNameRef; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.intrinsic.FunctionIntrinsic; + +import java.util.List; + +/** + * @author Pavel Talanov + */ +public enum LengthIntrinsic implements FunctionIntrinsic { + + INSTANCE; + + @NotNull + @Override + public JsExpression apply(@Nullable JsExpression receiver, @NotNull List arguments, + @NotNull TranslationContext context) { + assert receiver != null; + assert arguments.isEmpty() : "Length expression must have zero arguments."; + //TODO: provide better way + JsNameRef lengthProperty = AstUtil.newQualifiedNameRef("length"); + AstUtil.setQualifier(lengthProperty, receiver); + return lengthProperty; + } +} \ No newline at end of file diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/tuple/TupleAccessIntrinsic.java b/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/tuple/TupleAccessIntrinsic.java new file mode 100644 index 00000000000..0dd7aa92dcb --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/intrinsic/tuple/TupleAccessIntrinsic.java @@ -0,0 +1,46 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.intrinsic.tuple; + +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.intrinsic.FunctionIntrinsic; + +import java.util.List; + +/** + * @author Pavel Talanov + */ +public final class TupleAccessIntrinsic implements FunctionIntrinsic { + + private final int elementIndex; + + public TupleAccessIntrinsic(int elementIndex) { + this.elementIndex = elementIndex; + } + + @NotNull + @Override + public JsExpression apply(@Nullable JsExpression receiver, @NotNull List arguments, + @NotNull TranslationContext context) { + assert arguments.isEmpty() : "Tuple access expression should not have any arguments."; + return AstUtil.newArrayAccess(receiver, context.program().getNumberLiteral(elementIndex)); + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/operation/AssignmentTranslator.java b/js/js.backend/src/org/jetbrains/k2js/translate/operation/AssignmentTranslator.java new file mode 100644 index 00000000000..59a0b1fbe69 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/operation/AssignmentTranslator.java @@ -0,0 +1,69 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.operation; + +import com.google.dart.compiler.backend.js.ast.JsExpression; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.JetBinaryExpression; +import org.jetbrains.jet.lang.types.expressions.OperatorConventions; +import org.jetbrains.jet.lexer.JetToken; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.general.AbstractTranslator; +import org.jetbrains.k2js.translate.reference.AccessTranslator; + +import static org.jetbrains.k2js.translate.utils.BindingUtils.isVariableReassignment; +import static org.jetbrains.k2js.translate.utils.PsiUtils.getOperationToken; +import static org.jetbrains.k2js.translate.utils.PsiUtils.isAssignment; +import static org.jetbrains.k2js.translate.utils.TranslationUtils.isIntrinsicOperation; +import static org.jetbrains.k2js.translate.utils.TranslationUtils.translateRightExpression; + +/** + * @author Pavel Talanov + */ +public abstract class AssignmentTranslator extends AbstractTranslator { + + public static boolean isAssignmentOperator(JetBinaryExpression expression) { + JetToken operationToken = getOperationToken(expression); + return (OperatorConventions.ASSIGNMENT_OPERATIONS.keySet().contains(operationToken) + || isAssignment(operationToken)); + } + + @NotNull + public static JsExpression translate(@NotNull JetBinaryExpression expression, + @NotNull TranslationContext context) { + if (isIntrinsicOperation(context, expression)) { + return IntrinsicAssignmentTranslator.translate(expression, context); + } + return OverloadedAssignmentTranslator.translate(expression, context); + } + + @NotNull + protected final JetBinaryExpression expression; + protected final AccessTranslator accessTranslator; + protected final boolean isVariableReassignment; + @NotNull + protected final JsExpression right; + + protected AssignmentTranslator(@NotNull JetBinaryExpression expression, + @NotNull TranslationContext context) { + super(context); + this.expression = expression; + this.isVariableReassignment = isVariableReassignment(context.bindingContext(), expression); + this.accessTranslator = AccessTranslator.getAccessTranslator(expression.getLeft(), context()); + this.right = translateRightExpression(context(), expression); + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/operation/BinaryOperationTranslator.java b/js/js.backend/src/org/jetbrains/k2js/translate/operation/BinaryOperationTranslator.java new file mode 100644 index 00000000000..6fdf03194f2 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/operation/BinaryOperationTranslator.java @@ -0,0 +1,157 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.operation; + +import com.google.dart.compiler.backend.js.ast.JsBinaryOperation; +import com.google.dart.compiler.backend.js.ast.JsBinaryOperator; +import com.google.dart.compiler.backend.js.ast.JsExpression; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; +import org.jetbrains.jet.lang.psi.JetBinaryExpression; +import org.jetbrains.jet.lang.resolve.calls.ResolvedCall; +import org.jetbrains.jet.lang.types.expressions.OperatorConventions; +import org.jetbrains.jet.lexer.JetToken; +import org.jetbrains.jet.lexer.JetTokens; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.general.AbstractTranslator; +import org.jetbrains.k2js.translate.intrinsic.EqualsIntrinsic; +import org.jetbrains.k2js.translate.reference.CallBuilder; +import org.jetbrains.k2js.translate.reference.CallType; + +import java.util.Arrays; + +import static com.google.dart.compiler.util.AstUtil.not; +import static org.jetbrains.k2js.translate.operation.AssignmentTranslator.isAssignmentOperator; +import static org.jetbrains.k2js.translate.operation.CompareToTranslator.isCompareToCall; +import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptorForOperationExpression; +import static org.jetbrains.k2js.translate.utils.BindingUtils.getResolvedCall; +import static org.jetbrains.k2js.translate.utils.DescriptorUtils.isEquals; +import static org.jetbrains.k2js.translate.utils.PsiUtils.*; +import static org.jetbrains.k2js.translate.utils.TranslationUtils.translateLeftExpression; +import static org.jetbrains.k2js.translate.utils.TranslationUtils.translateRightExpression; + + +/** + * @author Pavel Talanov + */ +public final class BinaryOperationTranslator extends AbstractTranslator { + + @NotNull + public static JsExpression translate(@NotNull JetBinaryExpression expression, + @NotNull TranslationContext context) { + return (new BinaryOperationTranslator(expression, context).translate()); + } + + @NotNull + /*package*/ static JsExpression translateAsOverloadedCall(@NotNull JetBinaryExpression expression, + @NotNull TranslationContext context) { + return (new BinaryOperationTranslator(expression, context)).translateAsOverloadedBinaryOperation(); + } + + @NotNull + private final JetBinaryExpression expression; + + @Nullable + private final FunctionDescriptor operationDescriptor; + + private BinaryOperationTranslator(@NotNull JetBinaryExpression expression, + @NotNull TranslationContext context) { + super(context); + this.expression = expression; + this.operationDescriptor = + getFunctionDescriptorForOperationExpression(bindingContext(), expression); + } + + @NotNull + private JsExpression translate() { + if (isAssignmentOperator(expression)) { + return AssignmentTranslator.translate(expression, context()); + } + if (isNotOverloadable()) { + return translateAsUnOverloadableBinaryOperation(); + } + if (isCompareToCall(expression, context())) { + return CompareToTranslator.translate(expression, context()); + } + assert operationDescriptor != null : + "Overloadable operations must have not null descriptor"; + if (isEquals(operationDescriptor)) { + return translateAsEqualsCall(); + } + return translateAsOverloadedBinaryOperation(); + } + + private boolean isNotOverloadable() { + return operationDescriptor == null; + } + + @NotNull + private JsExpression translateAsEqualsCall() { + assert operationDescriptor != null : "Equals operation must resolve to descriptor."; + EqualsIntrinsic intrinsic = context().intrinsics().getEqualsIntrinsic(operationDescriptor); + intrinsic.setNegated(expression.getOperationToken().equals(JetTokens.EXCLEQ)); + JsExpression left = translateLeftExpression(context(), expression); + JsExpression right = translateRightExpression(context(), expression); + return intrinsic.apply(left, Arrays.asList(right), context()); + } + + @NotNull + private JsExpression translateAsUnOverloadableBinaryOperation() { + JetToken token = getOperationToken(expression); + JsBinaryOperator operator = OperatorTable.getBinaryOperator(token); + assert OperatorConventions.NOT_OVERLOADABLE.contains(token); + JsExpression left = translateLeftExpression(context(), expression); + JsExpression right = translateRightExpression(context(), expression); + return new JsBinaryOperation(operator, left, right); + } + + + @NotNull + private JsExpression translateAsOverloadedBinaryOperation() { + CallBuilder callBuilder = setReceiverAndArguments(); + ResolvedCall resolvedCall1 = + getResolvedCall(bindingContext(), expression.getOperationReference()); + JsExpression result = callBuilder.resolvedCall(resolvedCall1) + .type(CallType.NORMAL).translate(); + return mayBeWrapWithNegation(result); + } + + @NotNull + private CallBuilder setReceiverAndArguments() { + CallBuilder callBuilder = CallBuilder.build(context()); + + JsExpression leftExpression = translateLeftExpression(context(), expression); + JsExpression rightExpression = translateRightExpression(context(), expression); + + if (isInOrNotInOperation(expression)) { + return callBuilder.receiver(rightExpression).args(leftExpression); + } else { + return callBuilder.receiver(leftExpression).args(rightExpression); + } + } + + @NotNull + private JsExpression mayBeWrapWithNegation(@NotNull JsExpression result) { + if (isNotInOperation(expression)) { + return not(result); + } else { + return result; + } + } + +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/operation/CompareToTranslator.java b/js/js.backend/src/org/jetbrains/k2js/translate/operation/CompareToTranslator.java new file mode 100644 index 00000000000..3ce5449c216 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/operation/CompareToTranslator.java @@ -0,0 +1,100 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.operation; + +import com.google.dart.compiler.backend.js.ast.JsBinaryOperation; +import com.google.dart.compiler.backend.js.ast.JsBinaryOperator; +import com.google.dart.compiler.backend.js.ast.JsExpression; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; +import org.jetbrains.jet.lang.psi.JetBinaryExpression; +import org.jetbrains.jet.lang.types.expressions.OperatorConventions; +import org.jetbrains.jet.lexer.JetToken; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.general.AbstractTranslator; +import org.jetbrains.k2js.translate.intrinsic.CompareToIntrinsic; +import org.jetbrains.k2js.translate.utils.TranslationUtils; + +import java.util.Arrays; + +import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptorForOperationExpression; +import static org.jetbrains.k2js.translate.utils.DescriptorUtils.isCompareTo; +import static org.jetbrains.k2js.translate.utils.PsiUtils.getOperationToken; +import static org.jetbrains.k2js.translate.utils.TranslationUtils.*; + +/** + * @author Pavel Talanov + */ +public final class CompareToTranslator extends AbstractTranslator { + + public static boolean isCompareToCall(@NotNull JetBinaryExpression expression, + @NotNull TranslationContext context) { + FunctionDescriptor operationDescriptor = + getFunctionDescriptorForOperationExpression(context.bindingContext(), expression); + + if (operationDescriptor == null) return false; + + return (isCompareTo(operationDescriptor)); + } + + @NotNull + public static JsExpression translate(@NotNull JetBinaryExpression expression, + @NotNull TranslationContext context) { + return (new CompareToTranslator(expression, context)).translate(); + } + + @NotNull + private final JetBinaryExpression expression; + + @NotNull + private final FunctionDescriptor descriptor; + + private CompareToTranslator(@NotNull JetBinaryExpression expression, + @NotNull TranslationContext context) { + super(context); + this.expression = expression; + FunctionDescriptor functionDescriptor = + getFunctionDescriptorForOperationExpression(context.bindingContext(), expression); + assert functionDescriptor != null : "CompareTo should always have a descriptor"; + this.descriptor = functionDescriptor; + assert (OperatorConventions.COMPARISON_OPERATIONS.contains(getOperationToken(expression))); + } + + @NotNull + private JsExpression translate() { + if (isIntrinsicOperation(context(), expression)) { + return intrinsicCompareTo(); + } + return overloadedCompareTo(); + } + + @NotNull + private JsExpression overloadedCompareTo() { + JsBinaryOperator correspondingOperator = OperatorTable.getBinaryOperator(getOperationToken(expression)); + JsExpression methodCall = BinaryOperationTranslator.translateAsOverloadedCall(expression, context()); + return new JsBinaryOperation(correspondingOperator, methodCall, TranslationUtils.zeroLiteral(context())); + } + + @NotNull + private JsExpression intrinsicCompareTo() { + CompareToIntrinsic intrinsic = context().intrinsics().getCompareToIntrinsic(descriptor); + intrinsic.setComparisonToken((JetToken) expression.getOperationToken()); + JsExpression left = translateLeftExpression(context(), expression); + JsExpression right = translateRightExpression(context(), expression); + return intrinsic.apply(left, Arrays.asList(right), context()); + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/operation/IncrementTranslator.java b/js/js.backend/src/org/jetbrains/k2js/translate/operation/IncrementTranslator.java new file mode 100644 index 00000000000..0852a3e1179 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/operation/IncrementTranslator.java @@ -0,0 +1,126 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.operation; + +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.JetExpression; +import org.jetbrains.jet.lang.psi.JetUnaryExpression; +import org.jetbrains.jet.lang.types.expressions.OperatorConventions; +import org.jetbrains.k2js.translate.context.TemporaryVariable; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.general.AbstractTranslator; +import org.jetbrains.k2js.translate.reference.AccessTranslator; + +import static org.jetbrains.k2js.translate.utils.BindingUtils.isStatement; +import static org.jetbrains.k2js.translate.utils.BindingUtils.isVariableReassignment; +import static org.jetbrains.k2js.translate.utils.PsiUtils.*; +import static org.jetbrains.k2js.translate.utils.TranslationUtils.isIntrinsicOperation; + +/** + * @author Pavel Talanov + */ +// TODO: provide better increment translator logic +public abstract class IncrementTranslator extends AbstractTranslator { + + public static boolean isIncrement(@NotNull JetUnaryExpression expression) { + return OperatorConventions.INCREMENT_OPERATIONS.contains(getOperationToken(expression)); + } + + @NotNull + public static JsExpression translate(@NotNull JetUnaryExpression expression, + @NotNull TranslationContext context) { + if (isIntrinsicOperation(context, expression)) { + return IntrinsicIncrementTranslator.translate(expression, context); + } + return OverloadedIncrementTranslator.translate(expression, context); + } + + @NotNull + protected final JetUnaryExpression expression; + @NotNull + protected final AccessTranslator accessTranslator; + private final boolean isVariableReassignment; + + protected IncrementTranslator(@NotNull JetUnaryExpression expression, + @NotNull TranslationContext context) { + super(context); + this.expression = expression; + this.isVariableReassignment = isVariableReassignment(context.bindingContext(), expression); + JetExpression baseExpression = getBaseExpression(expression); + this.accessTranslator = AccessTranslator.getAccessTranslator(baseExpression, context()); + } + + @NotNull + protected JsExpression translateAsMethodCall() { + if (returnValueIgnored() || isPrefix(expression)) { + return asPrefix(); + } + if (isVariableReassignment) { + return asPostfixWithReassignment(); + } else { + return asPostfixWithNoReassignment(); + } + } + + private boolean returnValueIgnored() { + return isStatement(bindingContext(), expression); + } + + @NotNull + private JsExpression asPrefix() { + JsExpression getExpression = accessTranslator.translateAsGet(); + if (isVariableReassignment) { + return variableReassignment(getExpression); + } + return operationExpression(getExpression); + } + + //TODO: decide if this expression can be optimised in case of direct access (not property) + @NotNull + private JsExpression asPostfixWithReassignment() { + // code fragment: expr(a++) + // generate: expr( (t1 = a, t2 = t1, a = t1.inc(), t2) ) + TemporaryVariable t1 = context().declareTemporary(accessTranslator.translateAsGet()); + TemporaryVariable t2 = context().declareTemporary(t1.reference()); + JsExpression variableReassignment = variableReassignment(t1.reference()); + return AstUtil.newSequence(t1.assignmentExpression(), t2.assignmentExpression(), + variableReassignment, t2.reference()); + } + + //TODO: TEST + @NotNull + private JsExpression asPostfixWithNoReassignment() { + // code fragment: expr(a++) + // generate: expr( (t1 = a, t2 = t1, t2.inc(), t1) ) + TemporaryVariable t1 = context().declareTemporary(accessTranslator.translateAsGet()); + TemporaryVariable t2 = context().declareTemporary(t1.reference()); + JsExpression methodCall = operationExpression(t2.reference()); + JsExpression returnedValue = t1.reference(); + return AstUtil.newSequence(t1.assignmentExpression(), t2.assignmentExpression(), methodCall, returnedValue); + } + + @NotNull + private JsExpression variableReassignment(@NotNull JsExpression toCallMethodUpon) { + JsExpression overloadedMethodCallOnPropertyGetter = operationExpression(toCallMethodUpon); + return accessTranslator.translateAsSet(overloadedMethodCallOnPropertyGetter); + } + + @NotNull + abstract JsExpression operationExpression(@NotNull JsExpression receiver); +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/operation/IntrinsicAssignmentTranslator.java b/js/js.backend/src/org/jetbrains/k2js/translate/operation/IntrinsicAssignmentTranslator.java new file mode 100644 index 00000000000..e845b34a175 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/operation/IntrinsicAssignmentTranslator.java @@ -0,0 +1,102 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.operation; + +import com.google.dart.compiler.backend.js.ast.JsBinaryOperation; +import com.google.dart.compiler.backend.js.ast.JsBinaryOperator; +import com.google.dart.compiler.backend.js.ast.JsExpression; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.JetBinaryExpression; +import org.jetbrains.jet.lang.types.expressions.OperatorConventions; +import org.jetbrains.jet.lexer.JetToken; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.reference.ReferenceAccessTranslator; + +import static org.jetbrains.k2js.translate.utils.PsiUtils.getOperationToken; +import static org.jetbrains.k2js.translate.utils.PsiUtils.isAssignment; + +/** + * @author Pavel Talanov + */ +public final class IntrinsicAssignmentTranslator extends AssignmentTranslator { + + + @NotNull + public static JsExpression translate(@NotNull JetBinaryExpression expression, + @NotNull TranslationContext context) { + return (new IntrinsicAssignmentTranslator(expression, context)).translate(); + } + + private IntrinsicAssignmentTranslator(@NotNull JetBinaryExpression expression, + @NotNull TranslationContext context) { + super(expression, context); + } + + @NotNull + protected JsExpression translate() { + if (isAssignment(getOperationToken(expression))) { + return translateAsPlainAssignment(); + } + return translateAsAssignmentOperation(); + } + + @NotNull + private JsExpression translateAsAssignmentOperation() { + if (accessTranslator instanceof ReferenceAccessTranslator) { + return translateAsPlainAssignmentOperation(); + } + return translateAsAssignToCounterpart(); + } + + @NotNull + private JsExpression translateAsAssignToCounterpart() { + JsBinaryOperator operator = getCounterpartOperator(); + JsBinaryOperation counterpartOperation = + new JsBinaryOperation(operator, accessTranslator.translateAsGet(), right); + return accessTranslator.translateAsSet(counterpartOperation); + } + + @NotNull + private JsBinaryOperator getCounterpartOperator() { + JetToken assignmentOperationToken = getOperationToken(expression); + assert OperatorConventions.ASSIGNMENT_OPERATIONS.containsKey(assignmentOperationToken); + JetToken counterpartToken = OperatorConventions.ASSIGNMENT_OPERATION_COUNTERPARTS.get(assignmentOperationToken); + assert OperatorTable.hasCorrespondingBinaryOperator(counterpartToken) : + "Unsupported token encountered: " + counterpartToken.toString(); + return OperatorTable.getBinaryOperator(counterpartToken); + } + + @NotNull + private JsExpression translateAsPlainAssignmentOperation() { + JsBinaryOperator operator = getAssignmentOperator(); + return new JsBinaryOperation(operator, accessTranslator.translateAsGet(), right); + } + + @NotNull + private JsBinaryOperator getAssignmentOperator() { + JetToken token = getOperationToken(expression); + assert OperatorConventions.ASSIGNMENT_OPERATIONS.containsKey(token); + assert OperatorTable.hasCorrespondingBinaryOperator(token) : + "Unsupported token encountered: " + token.toString(); + return OperatorTable.getBinaryOperator(token); + } + + @NotNull + private JsExpression translateAsPlainAssignment() { + return accessTranslator.translateAsSet(right); + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/operation/IntrinsicIncrementTranslator.java b/js/js.backend/src/org/jetbrains/k2js/translate/operation/IntrinsicIncrementTranslator.java new file mode 100644 index 00000000000..4fe2c79bab6 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/operation/IntrinsicIncrementTranslator.java @@ -0,0 +1,90 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.operation; + +import com.google.dart.compiler.backend.js.ast.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.JetUnaryExpression; +import org.jetbrains.jet.lexer.JetToken; +import org.jetbrains.jet.lexer.JetTokens; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.reference.ReferenceAccessTranslator; + +import static org.jetbrains.k2js.translate.utils.PsiUtils.getOperationToken; +import static org.jetbrains.k2js.translate.utils.PsiUtils.isPrefix; + + +/** + * @author Pavel Talanov + */ +public final class IntrinsicIncrementTranslator extends IncrementTranslator { + + + @NotNull + public static JsExpression translate(@NotNull JetUnaryExpression expression, + @NotNull TranslationContext context) { + return (new IntrinsicIncrementTranslator(expression, context)) + .translate(); + } + + private IntrinsicIncrementTranslator(@NotNull JetUnaryExpression expression, + @NotNull TranslationContext context) { + super(expression, context); + } + + @NotNull + protected JsExpression translate() { + if (isPrimitiveExpressionIncrement()) { + return jsUnaryExpression(); + } + return translateAsMethodCall(); + } + + private boolean isPrimitiveExpressionIncrement() { + return accessTranslator instanceof ReferenceAccessTranslator; + } + + @NotNull + private JsExpression jsUnaryExpression() { + JsUnaryOperator operator = OperatorTable.getUnaryOperator(getOperationToken(expression)); + JsExpression getExpression = accessTranslator.translateAsGet(); + if (isPrefix(expression)) { + return new JsPrefixOperation(operator, getExpression); + } else { + return new JsPostfixOperation(operator, getExpression); + } + } + + @Override + @NotNull + protected JsExpression operationExpression(@NotNull JsExpression receiver) { + return unaryAsBinary(receiver); + } + + public JsBinaryOperation unaryAsBinary(@NotNull JsExpression leftExpression) { + JsNumberLiteral oneLiteral = program().getNumberLiteral(1); + JetToken token = getOperationToken(expression); + if (token.equals(JetTokens.PLUSPLUS)) { + return new JsBinaryOperation(JsBinaryOperator.ADD, leftExpression, oneLiteral); + } + if (token.equals(JetTokens.MINUSMINUS)) { + return new JsBinaryOperation(JsBinaryOperator.SUB, leftExpression, oneLiteral); + } + throw new AssertionError("This method should be called only for increment and decrement operators"); + } + +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/operation/OperatorTable.java b/js/js.backend/src/org/jetbrains/k2js/translate/operation/OperatorTable.java new file mode 100644 index 00000000000..25405b9042b --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/operation/OperatorTable.java @@ -0,0 +1,85 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.operation; + +import com.google.dart.compiler.backend.js.ast.JsBinaryOperator; +import com.google.dart.compiler.backend.js.ast.JsUnaryOperator; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lexer.JetToken; +import org.jetbrains.jet.lexer.JetTokens; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author Pavel Talanov + */ + +//TODO: refactor using guava builders +public final class OperatorTable { + + private static final Map binaryOperatorsMap = new HashMap(); + private static final Map unaryOperatorsMap = new HashMap(); + + static { + unaryOperatorsMap.put(JetTokens.PLUSPLUS, JsUnaryOperator.INC); //++ + unaryOperatorsMap.put(JetTokens.MINUSMINUS, JsUnaryOperator.DEC); //-- + unaryOperatorsMap.put(JetTokens.EXCL, JsUnaryOperator.NOT); //! + unaryOperatorsMap.put(JetTokens.MINUS, JsUnaryOperator.NEG); //- + unaryOperatorsMap.put(JetTokens.PLUS, JsUnaryOperator.POS); //+ + } + + //TODO : not all operators , add and test bit operators + static { + binaryOperatorsMap.put(JetTokens.PLUS, JsBinaryOperator.ADD); + binaryOperatorsMap.put(JetTokens.MINUS, JsBinaryOperator.SUB); + binaryOperatorsMap.put(JetTokens.MUL, JsBinaryOperator.MUL); + binaryOperatorsMap.put(JetTokens.DIV, JsBinaryOperator.DIV); + binaryOperatorsMap.put(JetTokens.EQ, JsBinaryOperator.ASG); + binaryOperatorsMap.put(JetTokens.GT, JsBinaryOperator.GT); + binaryOperatorsMap.put(JetTokens.GTEQ, JsBinaryOperator.GTE); + binaryOperatorsMap.put(JetTokens.LT, JsBinaryOperator.LT); + binaryOperatorsMap.put(JetTokens.LTEQ, JsBinaryOperator.LTE); + binaryOperatorsMap.put(JetTokens.EQEQ, JsBinaryOperator.REF_EQ); + binaryOperatorsMap.put(JetTokens.ANDAND, JsBinaryOperator.AND); + binaryOperatorsMap.put(JetTokens.OROR, JsBinaryOperator.OR); + binaryOperatorsMap.put(JetTokens.EXCLEQ, JsBinaryOperator.NEQ); + binaryOperatorsMap.put(JetTokens.PERC, JsBinaryOperator.MOD); + binaryOperatorsMap.put(JetTokens.PLUSEQ, JsBinaryOperator.ASG_ADD); + binaryOperatorsMap.put(JetTokens.MINUSEQ, JsBinaryOperator.ASG_SUB); + binaryOperatorsMap.put(JetTokens.DIVEQ, JsBinaryOperator.ASG_DIV); + binaryOperatorsMap.put(JetTokens.MULTEQ, JsBinaryOperator.ASG_MUL); + binaryOperatorsMap.put(JetTokens.PERCEQ, JsBinaryOperator.ASG_MOD); + } + + + public static boolean hasCorrespondingBinaryOperator(@NotNull JetToken token) { + return binaryOperatorsMap.containsKey(token); + } + + @NotNull + static public JsBinaryOperator getBinaryOperator(@NotNull JetToken token) { + assert JetTokens.OPERATIONS.contains(token) : "Token should represent an operation!"; + return binaryOperatorsMap.get(token); + } + + @NotNull + static public JsUnaryOperator getUnaryOperator(@NotNull JetToken token) { + assert JetTokens.OPERATIONS.contains(token) : "Token should represent an operation!"; + return unaryOperatorsMap.get(token); + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/operation/OverloadedAssignmentTranslator.java b/js/js.backend/src/org/jetbrains/k2js/translate/operation/OverloadedAssignmentTranslator.java new file mode 100644 index 00000000000..b51a05e360f --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/operation/OverloadedAssignmentTranslator.java @@ -0,0 +1,67 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.operation; + +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsNameRef; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.JetBinaryExpression; +import org.jetbrains.k2js.translate.context.TranslationContext; + +import static org.jetbrains.k2js.translate.utils.TranslationUtils.getMethodReferenceForOverloadedOperation; + +/** + * @author Pavel Talanov + */ +public final class OverloadedAssignmentTranslator extends AssignmentTranslator { + + @NotNull + public static JsExpression translate(@NotNull JetBinaryExpression expression, + @NotNull TranslationContext context) { + return (new OverloadedAssignmentTranslator(expression, context)).translate(); + } + + @NotNull + private final JsNameRef operationReference; + + private OverloadedAssignmentTranslator(@NotNull JetBinaryExpression expression, + @NotNull TranslationContext context) { + super(expression, context); + this.operationReference = getMethodReferenceForOverloadedOperation(context, expression); + } + + @NotNull + protected JsExpression translate() { + if (isVariableReassignment) { + return reassignment(); + } + return overloadedMethodInvocation(); + } + + @NotNull + private JsExpression reassignment() { + return accessTranslator.translateAsSet(overloadedMethodInvocation()); + } + + @NotNull + private JsExpression overloadedMethodInvocation() { + AstUtil.setQualifier(operationReference, accessTranslator.translateAsGet()); + return AstUtil.newInvocation(operationReference, right); + } + +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/operation/OverloadedIncrementTranslator.java b/js/js.backend/src/org/jetbrains/k2js/translate/operation/OverloadedIncrementTranslator.java new file mode 100644 index 00000000000..bbeea97e9b0 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/operation/OverloadedIncrementTranslator.java @@ -0,0 +1,61 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.operation; + +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsNameRef; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.JetUnaryExpression; +import org.jetbrains.k2js.translate.context.TranslationContext; + +import static org.jetbrains.k2js.translate.utils.TranslationUtils.getMethodReferenceForOverloadedOperation; + +/** + * @author Pavel Talanov + */ +public final class OverloadedIncrementTranslator extends IncrementTranslator { + + @NotNull + public static JsExpression translate(@NotNull JetUnaryExpression expression, + @NotNull TranslationContext context) { + return (new OverloadedIncrementTranslator(expression, context)) + .translate(); + } + + @NotNull + private final JsNameRef operationReference; + + private OverloadedIncrementTranslator(@NotNull JetUnaryExpression expression, + @NotNull TranslationContext context) { + super(expression, context); + this.operationReference = getMethodReferenceForOverloadedOperation(context, expression); + } + + @NotNull + protected JsExpression translate() { + return translateAsMethodCall(); + } + + @Override + @NotNull + protected JsExpression operationExpression(@NotNull JsExpression receiver) { + AstUtil.setQualifier(operationReference, receiver); + return AstUtil.newInvocation(operationReference); + } + +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/operation/UnaryOperationTranslator.java b/js/js.backend/src/org/jetbrains/k2js/translate/operation/UnaryOperationTranslator.java new file mode 100644 index 00000000000..f6e9f1fc060 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/operation/UnaryOperationTranslator.java @@ -0,0 +1,55 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.operation; + +import com.google.dart.compiler.backend.js.ast.JsExpression; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.JetUnaryExpression; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.reference.CallBuilder; +import org.jetbrains.k2js.translate.reference.CallType; +import org.jetbrains.k2js.translate.utils.TranslationUtils; + +import java.util.Collections; + +import static org.jetbrains.k2js.translate.utils.BindingUtils.getResolvedCall; + +/** + * @author Pavel Talanov + */ + +public final class UnaryOperationTranslator { + + @NotNull + public static JsExpression translate(@NotNull JetUnaryExpression expression, + @NotNull TranslationContext context) { + if (IncrementTranslator.isIncrement(expression)) { + return IncrementTranslator.translate(expression, context); + } + return translateAsCall(expression, context); + } + + @NotNull + private static JsExpression translateAsCall(@NotNull JetUnaryExpression expression, + @NotNull TranslationContext context) { + return CallBuilder.build(context) + .receiver(TranslationUtils.translateBaseExpression(context, expression)) + .args(Collections.emptyList()) + .resolvedCall(getResolvedCall(context.bindingContext(), expression.getOperationReference())) + .type(CallType.NORMAL).translate(); + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/reference/AccessTranslator.java b/js/js.backend/src/org/jetbrains/k2js/translate/reference/AccessTranslator.java new file mode 100644 index 00000000000..33a59596c2b --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/reference/AccessTranslator.java @@ -0,0 +1,68 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.reference; + +import com.google.dart.compiler.backend.js.ast.JsExpression; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.general.AbstractTranslator; + +/** + * @author Pavel Talanov + *

+ * Abstract entity for language constructs that you can get/set. Also dispatches to the real implemntation. + */ +public abstract class AccessTranslator extends AbstractTranslator { + + //TODO: this piece of code represents dangerously convoluted logic, think of the ways it can be improved + @NotNull + public static AccessTranslator getAccessTranslator(@NotNull JetExpression referenceExpression, + @NotNull TranslationContext context) { + assert ((referenceExpression instanceof JetReferenceExpression) || + (referenceExpression instanceof JetQualifiedExpression)); + if (PropertyAccessTranslator.canBePropertyAccess(referenceExpression, context)) { + if (referenceExpression instanceof JetQualifiedExpression) { + return QualifiedExpressionTranslator.getAccessTranslator((JetQualifiedExpression) referenceExpression, context); + } + assert referenceExpression instanceof JetSimpleNameExpression; + return PropertyAccessTranslator.newInstance((JetSimpleNameExpression) referenceExpression, + null, CallType.NORMAL, context); + } + if (referenceExpression instanceof JetArrayAccessExpression) { + return ArrayAccessTranslator.newInstance((JetArrayAccessExpression) referenceExpression, context); + } + return ReferenceAccessTranslator.newInstance((JetSimpleNameExpression) referenceExpression, context); + } + + + @NotNull + public static JsExpression translateAsGet(@NotNull JetReferenceExpression expression, + @NotNull TranslationContext context) { + return (getAccessTranslator(expression, context)).translateAsGet(); + } + + + protected AccessTranslator(@Deprecated TranslationContext context) { + super(context); + } + + public abstract JsExpression translateAsGet(); + + public abstract JsExpression translateAsSet(@NotNull JsExpression setTo); + +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/reference/ArrayAccessTranslator.java b/js/js.backend/src/org/jetbrains/k2js/translate/reference/ArrayAccessTranslator.java new file mode 100644 index 00000000000..963a05e4b05 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/reference/ArrayAccessTranslator.java @@ -0,0 +1,96 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.reference; + +import com.google.dart.compiler.backend.js.ast.JsExpression; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; +import org.jetbrains.jet.lang.psi.JetArrayAccessExpression; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.general.Translation; +import org.jetbrains.k2js.translate.utils.BindingUtils; +import org.jetbrains.k2js.translate.utils.TranslationUtils; + +import java.util.List; + +import static org.jetbrains.k2js.translate.utils.BindingUtils.getDescriptorForReferenceExpression; + +/** + * @author Pavel Talanov + */ + +//TODO: inspect not clear how the class handles set and get operations differently +public final class ArrayAccessTranslator extends AccessTranslator { + + /*package*/ + static ArrayAccessTranslator newInstance(@NotNull JetArrayAccessExpression expression, + @NotNull TranslationContext context) { + return new ArrayAccessTranslator(expression, context); + } + + @NotNull + private final JetArrayAccessExpression expression; + @NotNull + private final FunctionDescriptor methodDescriptor; + + private ArrayAccessTranslator(@NotNull JetArrayAccessExpression expression, + @NotNull TranslationContext context) { + super(context); + this.expression = expression; + //TODO: that is strange + this.methodDescriptor = (FunctionDescriptor) + getDescriptorForReferenceExpression(context.bindingContext(), expression); + } + + @Override + @NotNull + public JsExpression translateAsGet() { + List arguments = translateIndexExpressions(); + return translateAsMethodCall(arguments); + } + + @Override + @NotNull + public JsExpression translateAsSet(@NotNull JsExpression expression) { + + List arguments = translateIndexExpressions(); + arguments.add(expression); + return translateAsMethodCall(arguments); + } + + @NotNull + private JsExpression translateAsMethodCall(@NotNull List arguments) { + return CallBuilder.build(context()) + .receiver(translateArrayExpression()) + .args(arguments) + .resolvedCall(BindingUtils.getResolvedCall(bindingContext(), expression)) + .descriptor(methodDescriptor) + .translate(); + } + + @NotNull + private List translateIndexExpressions() { + return TranslationUtils.translateExpressionList(context(), expression.getIndexExpressions()); + } + + @NotNull + private JsExpression translateArrayExpression() { + return Translation.translateAsExpression(expression.getArrayExpression(), context()); + } + + +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/reference/CallBuilder.java b/js/js.backend/src/org/jetbrains/k2js/translate/reference/CallBuilder.java new file mode 100644 index 00000000000..fb8d82ea3a9 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/reference/CallBuilder.java @@ -0,0 +1,208 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.reference; + +import com.google.common.collect.Lists; +import com.google.dart.compiler.backend.js.ast.JsExpression; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.CallableDescriptor; +import org.jetbrains.jet.lang.resolve.calls.ResolvedCall; +import org.jetbrains.jet.lang.resolve.calls.ResolvedCallImpl; +import org.jetbrains.k2js.translate.context.TranslationContext; + +import java.util.Arrays; +import java.util.List; + +/** + * @author Pavel Talanov + */ +public final class CallBuilder { + + public static CallBuilder build(@NotNull TranslationContext context) { + return new CallBuilder(context); + } + + @NotNull + private final TranslationContext context; + @Nullable + private /*var*/ JsExpression receiver = null; + @NotNull + private final List args = Lists.newArrayList(); + @NotNull + private /*var*/ CallType callType = CallType.NORMAL; + @Nullable + private /*var*/ ResolvedCall resolvedCall = null; + @Nullable + private /*var*/ CallableDescriptor descriptor = null; + @Nullable + private /*var*/ JsExpression callee = null; + + + private CallBuilder(@NotNull TranslationContext context) { + this.context = context; + } + + @NotNull + public CallBuilder receiver(@Nullable JsExpression receiver) { + this.receiver = receiver; + return this; + } + + @NotNull + public CallBuilder args(@NotNull List args) { + assert this.args.isEmpty(); + this.args.addAll(args); + return this; + } + + @NotNull + public CallBuilder args(@NotNull JsExpression... args) { + return args(Arrays.asList(args)); + } + + @NotNull + public CallBuilder descriptor(@NotNull CallableDescriptor descriptor) { + this.descriptor = descriptor; + return this; + } + + @NotNull + public CallBuilder callee(@Nullable JsExpression callee) { + this.callee = callee; + return this; + } + + @NotNull + public CallBuilder resolvedCall(@NotNull ResolvedCall call) { + this.resolvedCall = call; + return this; + } + + @NotNull + public CallBuilder type(@NotNull CallType type) { + this.callType = type; + return this; + } + + //TODO: must be private + @NotNull + public CallTranslator finish() { + if (resolvedCall == null) { + assert descriptor != null; + resolvedCall = ResolvedCallImpl.create(descriptor); + } + if (descriptor == null) { + descriptor = resolvedCall.getCandidateDescriptor().getOriginal(); + } + assert resolvedCall != null; + return new CallTranslator(receiver, callee, args, resolvedCall, descriptor, callType, context); + } + + @NotNull + public JsExpression translate() { + return finish().translate(); + } + /* + @NotNull + private CallTranslator buildFromUnary(@NotNull JetUnaryExpression unaryExpression) { + JsExpression receiver = TranslationUtils.translateBaseExpression(context, unaryExpression); + List arguments = Collections.emptyList(); + ResolvedCall resolvedCall = + getResolvedCall(context.bindingContext(), unaryExpression.getOperationReference()); + return new CallTranslator(receiver, null, arguments, resolvedCall, null, CallType.NORMAL, context); + } + + //TODO: method too long + @NotNull + private CallTranslator buildFromBinary(@NotNull JetBinaryExpression binaryExpression, + boolean swapReceiverAndArgument) { + + JsExpression leftExpression = translateLeftExpression(context, binaryExpression); + JsExpression rightExpression = translateRightExpression(context, binaryExpression); + + JsExpression receiver; + List arguments; + if (swapReceiverAndArgument) { + receiver = rightExpression; + arguments = Arrays.asList(leftExpression); + } else { + receiver = leftExpression; + arguments = Arrays.asList(rightExpression); + } + + ResolvedCall resolvedCall = + getResolvedCall(context.bindingContext(), binaryExpression.getOperationReference()); + return new CallTranslator(receiver, null, arguments, resolvedCall, null, CallType.NORMAL, context); + } + + @NotNull + private CallTranslator buildFromCallExpression(@NotNull JetCallExpression callExpression, + @Nullable JsExpression receiver, + @NotNull CallType callType) { + ResolvedCall resolvedCall = getResolvedCallForCallExpression(context.bindingContext(), callExpression); + List arguments = translateArgumentsForCallExpression(callExpression, context); + JsExpression callee = null; + if (resolvedCall.getCandidateDescriptor() instanceof ExpressionAsFunctionDescriptor) { + callee = Translation.translateAsExpression(getCallee(callExpression), context); + } + return new CallTranslator(receiver, callee, arguments, resolvedCall, null, callType, context); + } + + @NotNull + private List translateArgumentsForCallExpression(@NotNull JetCallExpression callExpression, + @NotNull TranslationContext context) { + List result = new ArrayList(); + ResolvedCall resolvedCall = getResolvedCallForCallExpression(context.bindingContext(), callExpression); + Map formalToActualArguments = resolvedCall.getValueArguments(); + for (ValueParameterDescriptor parameterDescriptor : resolvedCall.getResultingDescriptor().getValueParameters()) { + ResolvedValueArgument actualArgument = formalToActualArguments.get(parameterDescriptor); + result.add(translateSingleArgument(actualArgument, parameterDescriptor)); + } + return result; + } + + + //TODO: refactor + @NotNull + private JsExpression translateSingleArgument(@NotNull ResolvedValueArgument actualArgument, + @NotNull ValueParameterDescriptor parameterDescriptor) { + List argumentExpressions = actualArgument.getArgumentExpressions(); + if (actualArgument instanceof VarargValueArgument) { + return translateVarargArgument(argumentExpressions); + } + if (actualArgument instanceof DefaultValueArgument) { + JetExpression defaultArgument = getDefaultArgument(context.bindingContext(), parameterDescriptor); + return Translation.translateAsExpression(defaultArgument, context); + } + assert actualArgument instanceof ExpressionValueArgument; + assert argumentExpressions.size() == 1; + return Translation.translateAsExpression(argumentExpressions.get(0), context); + } + + @NotNull + private JsExpression translateVarargArgument(@NotNull List arguments) { + JsArrayLiteral varargArgument = new JsArrayLiteral(); + for (JetExpression argument : arguments) { + varargArgument.getExpressions().add(Translation.translateAsExpression(argument, context)); + } + return varargArgument; + } + */ + + +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/reference/CallExpressionTranslator.java b/js/js.backend/src/org/jetbrains/k2js/translate/reference/CallExpressionTranslator.java new file mode 100644 index 00000000000..713a9fd9e73 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/reference/CallExpressionTranslator.java @@ -0,0 +1,133 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.reference; + +import com.google.common.collect.Lists; +import com.google.dart.compiler.backend.js.ast.JsArrayLiteral; +import com.google.dart.compiler.backend.js.ast.JsExpression; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor; +import org.jetbrains.jet.lang.psi.JetCallExpression; +import org.jetbrains.jet.lang.psi.JetExpression; +import org.jetbrains.jet.lang.resolve.calls.*; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.general.AbstractTranslator; +import org.jetbrains.k2js.translate.general.Translation; +import org.jetbrains.k2js.translate.utils.AnnotationsUtils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.jetbrains.k2js.translate.utils.BindingUtils.getDefaultArgument; +import static org.jetbrains.k2js.translate.utils.BindingUtils.getResolvedCallForCallExpression; +import static org.jetbrains.k2js.translate.utils.PsiUtils.getCallee; + +/** + * @author Pavel Talanov + */ +public final class CallExpressionTranslator extends AbstractTranslator { + + @NotNull + public static JsExpression translate(@NotNull JetCallExpression expression, + @Nullable JsExpression receiver, + @NotNull CallType callType, + @NotNull TranslationContext context) { + return (new CallExpressionTranslator(expression, context)).translate(receiver, callType); + } + + @NotNull + private final JetCallExpression expression; + @NotNull + private final ResolvedCall resolvedCall; + private final boolean isNativeFunctionCall; + + private CallExpressionTranslator(@NotNull JetCallExpression expression, + @NotNull TranslationContext context) { + super(context); + this.expression = expression; + this.resolvedCall = getResolvedCallForCallExpression(bindingContext(), expression); + this.isNativeFunctionCall = AnnotationsUtils.isNativeObject(resolvedCall.getCandidateDescriptor()); + } + + @NotNull + private JsExpression translate(@Nullable JsExpression receiver, + @NotNull CallType callType) { + return CallBuilder.build(context()) + .receiver(receiver) + .callee(getCalleeExpression()) + .args(translateArguments()) + .resolvedCall(resolvedCall) + .type(callType) + .translate(); + } + + @Nullable + private JsExpression getCalleeExpression() { + if (resolvedCall.getCandidateDescriptor() instanceof ExpressionAsFunctionDescriptor) { + return Translation.translateAsExpression(getCallee(expression), context()); + } + return null; + } + + @NotNull + private List translateArguments() { + List result = new ArrayList(); + ResolvedCall resolvedCall = getResolvedCallForCallExpression(bindingContext(), expression); + for (ValueParameterDescriptor parameterDescriptor : resolvedCall.getResultingDescriptor().getValueParameters()) { + ResolvedValueArgument actualArgument = resolvedCall.getValueArgumentsByIndex().get(parameterDescriptor.getIndex()); + result.addAll(translateSingleArgument(actualArgument, parameterDescriptor)); + } + return result; + } + + @NotNull + private List translateSingleArgument(@NotNull ResolvedValueArgument actualArgument, + @NotNull ValueParameterDescriptor parameterDescriptor) { + List argumentExpressions = actualArgument.getArgumentExpressions(); + if (actualArgument instanceof VarargValueArgument) { + return translateVarargArgument(argumentExpressions); + } + if (actualArgument instanceof DefaultValueArgument) { + JetExpression defaultArgument = getDefaultArgument(bindingContext(), parameterDescriptor); + return Arrays.asList(Translation.translateAsExpression(defaultArgument, context())); + } + assert actualArgument instanceof ExpressionValueArgument; + assert argumentExpressions.size() == 1; + return Arrays.asList(Translation.translateAsExpression(argumentExpressions.get(0), context())); + } + + @NotNull + private List translateVarargArgument(@NotNull List arguments) { + List translatedArgs = Lists.newArrayList(); + for (JetExpression argument : arguments) { + translatedArgs.add(Translation.translateAsExpression(argument, context())); + } + if (isNativeFunctionCall) { + return translatedArgs; + } + return wrapInArrayLiteral(translatedArgs); + } + + @NotNull + private List wrapInArrayLiteral(@NotNull List translatedArgs) { + JsArrayLiteral argsWrappedInArray = new JsArrayLiteral(); + argsWrappedInArray.getExpressions().addAll(translatedArgs); + return Arrays.asList(argsWrappedInArray); + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/reference/CallTranslator.java b/js/js.backend/src/org/jetbrains/k2js/translate/reference/CallTranslator.java new file mode 100644 index 00000000000..199d9e4b446 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/reference/CallTranslator.java @@ -0,0 +1,296 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.reference; + +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsInvocation; +import com.google.dart.compiler.backend.js.ast.JsNameRef; +import com.google.dart.compiler.backend.js.ast.JsNew; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.*; +import org.jetbrains.jet.lang.resolve.calls.ExpressionAsFunctionDescriptor; +import org.jetbrains.jet.lang.resolve.calls.ResolvedCall; +import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.general.AbstractTranslator; +import org.jetbrains.k2js.translate.intrinsic.FunctionIntrinsic; +import org.jetbrains.k2js.translate.utils.AnnotationsUtils; +import org.jetbrains.k2js.translate.utils.TranslationUtils; + +import java.util.ArrayList; +import java.util.List; + +import static org.jetbrains.k2js.translate.utils.DescriptorUtils.*; +import static org.jetbrains.k2js.translate.utils.TranslationUtils.getThisObject; + +/** + * @author Pavel Talanov + */ +//TODO: write tests on calling backing fields as functions +public final class CallTranslator extends AbstractTranslator { + + private static class CallParameters { + + public CallParameters(@Nullable JsExpression receiver, @NotNull JsExpression functionReference) { + this.receiver = receiver; + this.functionReference = functionReference; + } + + @Nullable + public /*var*/ JsExpression receiver; + @NotNull + public /*var*/ JsExpression functionReference; + } + + //NOTE: receiver may mean this object as well + @Nullable + private /*var*/ JsExpression receiver; + + @Nullable + private final JsExpression callee; + + @NotNull + private final List arguments; + + @NotNull + private final ResolvedCall resolvedCall; + + @NotNull + private final CallableDescriptor descriptor; + + @NotNull + private final CallType callType; + + /*package*/ CallTranslator(@Nullable JsExpression receiver, @Nullable JsExpression callee, + @NotNull List arguments, + @NotNull ResolvedCall resolvedCall, + @NotNull CallableDescriptor descriptorToCall, + @NotNull CallType callType, + @NotNull TranslationContext context) { + super(context); + this.receiver = receiver; + this.arguments = arguments; + this.resolvedCall = resolvedCall; + this.callType = callType; + this.descriptor = descriptorToCall; + this.callee = callee; + } + + @NotNull + /*package*/ JsExpression translate() { + //NOTE: treat native extension function calls as usual calls + if (isIntrinsic()) { + return intrinsicInvocation(); + } + if (isConstructor()) { + return constructorCall(); + } + if (isNative()) { + return methodCall(); + } + if (isExtensionFunctionLiteral()) { + return extensionFunctionLiteralCall(); + } + if (isExtensionFunction()) { + return extensionFunctionCall(); + } + return methodCall(); + } + + private boolean isIntrinsic() { + return context().intrinsics().isIntrinsic(descriptor); + } + + @NotNull + private JsExpression intrinsicInvocation() { + assert descriptor instanceof FunctionDescriptor; + FunctionIntrinsic functionIntrinsic = + context().intrinsics().getFunctionIntrinsic((FunctionDescriptor) descriptor); + JsExpression receiverExpression = resolveThisObject(/*do not get qualifier*/false); + return functionIntrinsic.apply(receiverExpression, arguments, context()); + } + + private boolean isConstructor() { + return isConstructorDescriptor(descriptor); + } + + @NotNull + private JsExpression constructorCall() { + JsExpression constructorReference = translateAsFunctionWithNoThisObject(descriptor); + JsNew constructorCall = new JsNew(constructorReference); + constructorCall.setArguments(arguments); + return constructorCall; + } + + @NotNull + private JsExpression translateAsFunctionWithNoThisObject(@NotNull DeclarationDescriptor descriptor) { + return ReferenceTranslator.translateAsFQReference(descriptor, context()); + } + + private boolean isNative() { + return AnnotationsUtils.isNativeObject(descriptor); + } + + private boolean isExtensionFunctionLiteral() { + boolean isLiteral = descriptor instanceof VariableAsFunctionDescriptor + || descriptor instanceof ExpressionAsFunctionDescriptor; + return isExtensionFunction() && isLiteral; + } + + @NotNull + private JsExpression extensionFunctionLiteralCall() { + JsExpression realReceiver = getExtensionFunctionCallReceiver(); + return callType.constructCall(realReceiver, new CallType.CallConstructor() { + @NotNull + @Override + public JsExpression construct(@Nullable JsExpression receiver) { + assert receiver != null : "Could not be null for extensions"; + return constructExtensionLiteralCall(receiver); + } + }, context()); + } + + @NotNull + private JsExpression constructExtensionLiteralCall(@NotNull JsExpression realReceiver) { + List callArguments = generateExtensionCallArgumentList(realReceiver); + JsInvocation callMethodInvocation = generateCallMethodInvocation(); + callMethodInvocation.setArguments(callArguments); + return callMethodInvocation; + } + + @NotNull + private JsInvocation generateCallMethodInvocation() { + JsNameRef callMethodNameRef = AstUtil.newQualifiedNameRef("call"); + JsInvocation callMethodInvocation = new JsInvocation(); + callMethodInvocation.setQualifier(callMethodNameRef); + AstUtil.setQualifier(callMethodInvocation, callParameters().functionReference); + return callMethodInvocation; + } + + @SuppressWarnings("UnnecessaryLocalVariable") + private boolean isExtensionFunction() { + boolean hasReceiver = resolvedCall.getReceiverArgument().exists(); + return hasReceiver; + } + + @NotNull + private JsExpression extensionFunctionCall() { + JsExpression realReceiver = getExtensionFunctionCallReceiver(); + return callType.constructCall(realReceiver, new CallType.CallConstructor() { + @NotNull + @Override + public JsExpression construct(@Nullable JsExpression receiver) { + assert receiver != null : "Could not be null for extensions"; + return constructExtensionFunctionCall(receiver); + } + }, context()); + } + + @NotNull + private JsExpression getExtensionFunctionCallReceiver() { + if (receiver != null) { + JsExpression result = receiver; + //Now the rest of the code can work as if it was simple method invocation + receiver = null; + return result; + } + DeclarationDescriptor expectedReceiverDescriptor = getExpectedReceiverDescriptor(descriptor); + assert expectedReceiverDescriptor != null; + return getThisObject(context(), expectedReceiverDescriptor); + } + + @NotNull + private JsExpression constructExtensionFunctionCall(@NotNull JsExpression receiver) { + List argumentList = generateExtensionCallArgumentList(receiver); + JsExpression functionReference = callParameters().functionReference; + AstUtil.setQualifier(functionReference, callParameters().receiver); + return AstUtil.newInvocation(functionReference, argumentList); + } + + @NotNull + private List generateExtensionCallArgumentList(@NotNull JsExpression receiver) { + List argumentList = new ArrayList(); + assert this.receiver == null : "Should be null at that point"; + argumentList.add(receiver); + argumentList.addAll(arguments); + return argumentList; + } + + @NotNull + private JsExpression methodCall() { + final CallParameters callParameters = callParameters(); + return callType.constructCall(callParameters.receiver, new CallType.CallConstructor() { + @NotNull + @Override + public JsExpression construct(@Nullable JsExpression receiver) { + JsExpression functionReference = callParameters.functionReference; + if (receiver != null) { + AstUtil.setQualifier(functionReference, receiver); + } + return AstUtil.newInvocation(functionReference, arguments); + } + }, context()); + } + + @NotNull + private CallParameters callParameters() { + if (callee != null) { + return new CallParameters(null, callee); + } + JsExpression thisObject = resolveThisObject(/*just get qualifier if null*/ true); + JsExpression functionReference = functionReference(); + return new CallParameters(thisObject, functionReference); + } + + @NotNull + private JsExpression functionReference() { + if (!isVariableAsFunction(descriptor)) { + return ReferenceTranslator.translateAsLocalNameReference(descriptor, context()); + } + VariableDescriptor variableDescriptor = + getVariableDescriptorForVariableAsFunction((VariableAsFunctionDescriptor) descriptor); + if (variableDescriptor instanceof PropertyDescriptor) { + return getterCall((PropertyDescriptor) variableDescriptor); + } + return ReferenceTranslator.translateAsLocalNameReference(variableDescriptor, context()); + } + + @NotNull + private JsExpression getterCall(@NotNull PropertyDescriptor variableDescriptor) { + //TODO: call type? + return PropertyAccessTranslator.translateAsPropertyGetterCall(variableDescriptor, resolvedCall, context()); + } + + //TODO: refactor + @Nullable + private JsExpression resolveThisObject(boolean getQualifierIfNull) { + if (receiver != null) { + return receiver; + } + ReceiverDescriptor thisObject = resolvedCall.getThisObject(); + if (thisObject.exists()) { + DeclarationDescriptor expectedThisDescriptor = getDeclarationDescriptorForReceiver(thisObject); + return TranslationUtils.getThisObject(context(), expectedThisDescriptor); + } + if (getQualifierIfNull) { + return context().getQualifierForDescriptor(descriptor); + } + return null; + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/reference/CallType.java b/js/js.backend/src/org/jetbrains/k2js/translate/reference/CallType.java new file mode 100644 index 00000000000..73f4c4842b4 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/reference/CallType.java @@ -0,0 +1,82 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.reference; + +import com.google.dart.compiler.backend.js.ast.JsBinaryOperation; +import com.google.dart.compiler.backend.js.ast.JsConditional; +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsNullLiteral; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.psi.JetDotQualifiedExpression; +import org.jetbrains.jet.lang.psi.JetQualifiedExpression; +import org.jetbrains.jet.lang.psi.JetSafeQualifiedExpression; +import org.jetbrains.k2js.translate.context.TemporaryVariable; +import org.jetbrains.k2js.translate.context.TranslationContext; + +import static com.google.dart.compiler.util.AstUtil.newSequence; + +/** + * @author Pavel Talanov + */ +public enum CallType { + SAFE { + @NotNull + @Override + JsExpression constructCall(@Nullable JsExpression receiver, @NotNull CallConstructor constructor, + @NotNull TranslationContext context) { + assert receiver != null; + TemporaryVariable temporaryVariable = context.declareTemporary(receiver); + JsNullLiteral nullLiteral = context.program().getNullLiteral(); + //TODO: find similar not null checks + JsBinaryOperation notNullCheck = AstUtil.notEqual(temporaryVariable.reference(), nullLiteral); + JsConditional callMethodIfNotNullElseNull = + new JsConditional(notNullCheck, constructor.construct(temporaryVariable.reference()), nullLiteral); + return newSequence(temporaryVariable.assignmentExpression(), callMethodIfNotNullElseNull); + } + }, + //TODO: bang qualifier is not implemented in frontend for now + // BANG, + NORMAL { + @NotNull + @Override + JsExpression constructCall(@Nullable JsExpression receiver, @NotNull CallConstructor constructor, + @NotNull TranslationContext context) { + return constructor.construct(receiver); + } + }; + + @NotNull + abstract JsExpression constructCall(@Nullable JsExpression receiver, @NotNull CallConstructor constructor, + @NotNull TranslationContext context); + + @NotNull + public static CallType getCallTypeForQualifiedExpression(@NotNull JetQualifiedExpression expression) { + if (expression instanceof JetSafeQualifiedExpression) { + return SAFE; + } + assert expression instanceof JetDotQualifiedExpression; + return NORMAL; + } + + public interface CallConstructor { + @NotNull + JsExpression construct(@Nullable JsExpression receiver); + } + +} \ No newline at end of file diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/reference/KotlinPropertyAccessTranslator.java b/js/js.backend/src/org/jetbrains/k2js/translate/reference/KotlinPropertyAccessTranslator.java new file mode 100644 index 00000000000..fb35e37f667 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/reference/KotlinPropertyAccessTranslator.java @@ -0,0 +1,84 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.reference; + +import com.google.dart.compiler.backend.js.ast.JsExpression; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.PropertyDescriptor; +import org.jetbrains.jet.lang.descriptors.PropertyGetterDescriptor; +import org.jetbrains.jet.lang.descriptors.PropertySetterDescriptor; +import org.jetbrains.jet.lang.resolve.calls.ResolvedCall; +import org.jetbrains.k2js.translate.context.TranslationContext; + +/** + * @author Pavel Talanov + *

+ * For properies /w accessors. + */ +public final class KotlinPropertyAccessTranslator extends PropertyAccessTranslator { + + @Nullable + private final JsExpression qualifier; + @NotNull + private final PropertyDescriptor propertyDescriptor; + @NotNull + ResolvedCall resolvedCall; + + //TODO: too many params in constructor + /*package*/ KotlinPropertyAccessTranslator(@NotNull PropertyDescriptor descriptor, + @Nullable JsExpression qualifier, + @NotNull ResolvedCall resolvedCall, + @NotNull TranslationContext context) { + super(context); + this.qualifier = qualifier; + this.propertyDescriptor = descriptor.getOriginal(); + this.resolvedCall = resolvedCall; + } + + @Override + @NotNull + public JsExpression translateAsGet() { + //TODO: check for duplication + PropertyGetterDescriptor getter = propertyDescriptor.getGetter(); + assert getter != null : "Getter for kotlin properties should bot be null."; + return callBuilderForAccessor() + .descriptor(getter) + .translate(); + } + + @Override + @NotNull + public JsExpression translateAsSet(@NotNull JsExpression toSetTo) { + //TODO: check for duplication + PropertySetterDescriptor setter = propertyDescriptor.getSetter(); + assert setter != null : "Getter for kotlin properties should bot be null."; + return callBuilderForAccessor() + .args(toSetTo) + .descriptor(setter) + .translate(); + } + + @NotNull + private CallBuilder callBuilderForAccessor() { + return CallBuilder.build(context()) + .receiver(qualifier) + .resolvedCall(resolvedCall) + .type(getCallType()); + } + +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/reference/NativePropertyAccessTranslator.java b/js/js.backend/src/org/jetbrains/k2js/translate/reference/NativePropertyAccessTranslator.java new file mode 100644 index 00000000000..f6744be776c --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/reference/NativePropertyAccessTranslator.java @@ -0,0 +1,84 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.reference; + +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsName; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; +import org.jetbrains.jet.lang.descriptors.PropertyDescriptor; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.utils.TranslationUtils; + +import static org.jetbrains.k2js.translate.utils.DescriptorUtils.getExpectedThisDescriptor; + +/** + * @author Pavel Talanov + *

+ * For native apis that use .property notation for access. + */ +public final class NativePropertyAccessTranslator extends PropertyAccessTranslator { + + + @Nullable + private final JsExpression qualifier; + @NotNull + private final PropertyDescriptor propertyDescriptor; + + /*package*/ + NativePropertyAccessTranslator(@NotNull PropertyDescriptor descriptor, + @Nullable JsExpression qualifier, + @NotNull TranslationContext context) { + super(context); + this.qualifier = qualifier; + this.propertyDescriptor = descriptor.getOriginal(); + } + + + @Override + @NotNull + public JsExpression translateAsGet() { + JsName nativePropertyName = context().getNameForDescriptor(propertyDescriptor); + JsExpression realQualifier = getQualifier(); + if (realQualifier != null) { + return AstUtil.qualified(nativePropertyName, realQualifier); + } else { + return nativePropertyName.makeRef(); + } + } + + @Override + @NotNull + public JsExpression translateAsSet(@NotNull JsExpression setTo) { + return AstUtil.assignment(translateAsGet(), setTo); + } + + @Nullable + public JsExpression getQualifier() { + if (qualifier != null) { + return qualifier; + } + assert !propertyDescriptor.getReceiverParameter().exists() : "Cant have native extension properties."; + DeclarationDescriptor expectedThisDescriptor = getExpectedThisDescriptor(propertyDescriptor); + if (expectedThisDescriptor == null) { + return null; + } + return TranslationUtils.getThisObject(context(), expectedThisDescriptor); + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/reference/PropertyAccessTranslator.java b/js/js.backend/src/org/jetbrains/k2js/translate/reference/PropertyAccessTranslator.java new file mode 100644 index 00000000000..4de16989916 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/reference/PropertyAccessTranslator.java @@ -0,0 +1,143 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.reference; + +import com.google.dart.compiler.backend.js.ast.JsExpression; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; +import org.jetbrains.jet.lang.descriptors.PropertyDescriptor; +import org.jetbrains.jet.lang.psi.JetExpression; +import org.jetbrains.jet.lang.psi.JetQualifiedExpression; +import org.jetbrains.jet.lang.psi.JetSimpleNameExpression; +import org.jetbrains.jet.lang.resolve.calls.ResolvedCall; +import org.jetbrains.k2js.translate.context.TranslationContext; + +import static org.jetbrains.k2js.translate.utils.AnnotationsUtils.isNativeObject; +import static org.jetbrains.k2js.translate.utils.BindingUtils.getDescriptorForReferenceExpression; +import static org.jetbrains.k2js.translate.utils.BindingUtils.getResolvedCall; +import static org.jetbrains.k2js.translate.utils.PsiUtils.getSelectorAsSimpleName; +import static org.jetbrains.k2js.translate.utils.PsiUtils.isBackingFieldReference; + +/** + * @author Pavel Talanov + */ +public abstract class PropertyAccessTranslator extends AccessTranslator { + + @NotNull + public static PropertyAccessTranslator newInstance(@NotNull PropertyDescriptor descriptor, + @NotNull ResolvedCall resolvedCall, + @NotNull TranslationContext context) { + if (isNativeObject(descriptor)) { + return new NativePropertyAccessTranslator(descriptor, /*qualifier = */ null, context); + } else { + return new KotlinPropertyAccessTranslator(descriptor, /*qualifier = */ null, resolvedCall, context); + } + } + + @NotNull + public static PropertyAccessTranslator newInstance(@NotNull JetSimpleNameExpression expression, + @Nullable JsExpression qualifier, + @NotNull CallType callType, + @NotNull TranslationContext context) { + PropertyAccessTranslator result; + PropertyDescriptor propertyDescriptor = getPropertyDescriptor(expression, context); + if (isNativeObject(propertyDescriptor) || isBackingFieldReference(expression)) { + result = new NativePropertyAccessTranslator(propertyDescriptor, qualifier, context); + } else { + ResolvedCall resolvedCall = getResolvedCall(context.bindingContext(), expression); + result = new KotlinPropertyAccessTranslator(propertyDescriptor, qualifier, resolvedCall, context); + } + result.setCallType(callType); + return result; + } + + @NotNull + /*package*/ static PropertyDescriptor getPropertyDescriptor(@NotNull JetSimpleNameExpression expression, + @NotNull TranslationContext context) { + DeclarationDescriptor descriptor = + getDescriptorForReferenceExpression(context.bindingContext(), expression); + assert descriptor instanceof PropertyDescriptor : "Must be a property descriptor."; + return (PropertyDescriptor) descriptor; + } + + + @NotNull + /*package*/ + static JsExpression translateAsPropertyGetterCall(@NotNull PropertyDescriptor descriptor, + @NotNull ResolvedCall resolvedCall, + @NotNull TranslationContext context) { + return (newInstance(descriptor, resolvedCall, context)) + .translateAsGet(); + } + + @NotNull + public static JsExpression translateAsPropertyGetterCall(@NotNull JetSimpleNameExpression expression, + @Nullable JsExpression qualifier, + @NotNull CallType callType, + @NotNull TranslationContext context) { + return (newInstance(expression, qualifier, callType, context)) + .translateAsGet(); + } + + + public static boolean canBePropertyGetterCall(@NotNull JetQualifiedExpression expression, + @NotNull TranslationContext context) { + JetSimpleNameExpression selector = getSelectorAsSimpleName(expression); + assert selector != null : "Only names are allowed after the dot"; + return canBePropertyGetterCall(selector, context); + } + + public static boolean canBePropertyGetterCall(@NotNull JetSimpleNameExpression expression, + @NotNull TranslationContext context) { + return (getDescriptorForReferenceExpression + (context.bindingContext(), expression) instanceof PropertyDescriptor); + } + + public static boolean canBePropertyGetterCall(@NotNull JetExpression expression, + @NotNull TranslationContext context) { + if (expression instanceof JetQualifiedExpression) { + return canBePropertyGetterCall((JetQualifiedExpression) expression, context); + } + if (expression instanceof JetSimpleNameExpression) { + return canBePropertyGetterCall((JetSimpleNameExpression) expression, context); + } + return false; + } + + public static boolean canBePropertyAccess(@NotNull JetExpression expression, + @NotNull TranslationContext context) { + return canBePropertyGetterCall(expression, context); + } + + //TODO: we use normal by default but may cause bugs + private /*var*/ CallType callType = CallType.NORMAL; + + protected PropertyAccessTranslator(@NotNull TranslationContext context) { + super(context); + } + + public void setCallType(@NotNull CallType callType) { + this.callType = callType; + } + + @NotNull + protected CallType getCallType() { + assert callType != null : "CallType not set"; + return callType; + } +} \ No newline at end of file diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/reference/QualifiedExpressionTranslator.java b/js/js.backend/src/org/jetbrains/k2js/translate/reference/QualifiedExpressionTranslator.java new file mode 100644 index 00000000000..be4321a95a0 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/reference/QualifiedExpressionTranslator.java @@ -0,0 +1,82 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.reference; + +import com.google.dart.compiler.backend.js.ast.JsExpression; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.JetCallExpression; +import org.jetbrains.jet.lang.psi.JetExpression; +import org.jetbrains.jet.lang.psi.JetQualifiedExpression; +import org.jetbrains.jet.lang.psi.JetSimpleNameExpression; +import org.jetbrains.k2js.translate.context.TranslationContext; + +import static org.jetbrains.k2js.translate.general.Translation.translateAsExpression; +import static org.jetbrains.k2js.translate.utils.PsiUtils.getNotNullSimpleNameSelector; +import static org.jetbrains.k2js.translate.utils.PsiUtils.getSelector; + +/** + * @author Pavel Talanov + */ +public final class QualifiedExpressionTranslator { + + private QualifiedExpressionTranslator() { + } + + @NotNull + public static AccessTranslator getAccessTranslator(@NotNull JetQualifiedExpression expression, + @NotNull TranslationContext context) { + + JsExpression receiver = translateReceiver(expression, context); + PropertyAccessTranslator result = + PropertyAccessTranslator.newInstance(getNotNullSimpleNameSelector(expression), receiver, + CallType.getCallTypeForQualifiedExpression(expression), context); + result.setCallType(CallType.getCallTypeForQualifiedExpression(expression)); + return result; + } + + @NotNull + public static JsExpression translateQualifiedExpression(@NotNull JetQualifiedExpression expression, + @NotNull TranslationContext context) { + JsExpression receiver = translateReceiver(expression, context); + JetExpression selector = getSelector(expression); + CallType callType = CallType.getCallTypeForQualifiedExpression(expression); + return dispatchToCorrectTranslator(receiver, selector, callType, context); + } + + @NotNull + private static JsExpression dispatchToCorrectTranslator(@NotNull JsExpression receiver, + @NotNull JetExpression selector, + @NotNull CallType callType, + @NotNull TranslationContext context) { + if (PropertyAccessTranslator.canBePropertyGetterCall(selector, context)) { + assert selector instanceof JetSimpleNameExpression : "Selectors for properties must be simple names."; + return PropertyAccessTranslator.translateAsPropertyGetterCall + ((JetSimpleNameExpression) selector, receiver, callType, context); + } + if (selector instanceof JetCallExpression) { + return CallExpressionTranslator.translate((JetCallExpression) selector, receiver, callType, context); + } + throw new AssertionError("Unexpected qualified expression"); + } + + //TODO: if has duplications + @NotNull + private static JsExpression translateReceiver(@NotNull JetQualifiedExpression expression, + @NotNull TranslationContext context) { + return translateAsExpression(expression.getReceiverExpression(), context); + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/reference/ReferenceAccessTranslator.java b/js/js.backend/src/org/jetbrains/k2js/translate/reference/ReferenceAccessTranslator.java new file mode 100644 index 00000000000..a80842e8ec0 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/reference/ReferenceAccessTranslator.java @@ -0,0 +1,62 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.reference; + +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsNameRef; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.JetSimpleNameExpression; +import org.jetbrains.k2js.translate.context.TranslationContext; + +/** + * @author Pavel Talanov + */ +public final class ReferenceAccessTranslator extends AccessTranslator { + + @NotNull + /*package*/ static ReferenceAccessTranslator newInstance(@NotNull JetSimpleNameExpression expression, + @NotNull TranslationContext context) { + return new ReferenceAccessTranslator(expression, context); + } + + @NotNull + private final JetSimpleNameExpression expression; + + private ReferenceAccessTranslator(@NotNull JetSimpleNameExpression expression, + @NotNull TranslationContext context) { + super(context); + this.expression = expression; + } + + @Override + @NotNull + public JsExpression translateAsGet() { + //TODO: consider evaluating only once + return ReferenceTranslator.translateSimpleName(expression, context()); + } + + @Override + @NotNull + public JsExpression translateAsSet(@NotNull JsExpression toSetTo) { + //TODO: consider evaluating only once + JsExpression reference = ReferenceTranslator.translateSimpleName(expression, context()); + assert reference instanceof JsNameRef; + return AstUtil.newAssignment((JsNameRef) reference, toSetTo); + } + +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/reference/ReferenceTranslator.java b/js/js.backend/src/org/jetbrains/k2js/translate/reference/ReferenceTranslator.java new file mode 100644 index 00000000000..34c0f453f54 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/reference/ReferenceTranslator.java @@ -0,0 +1,62 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.reference; + +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsName; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; +import org.jetbrains.jet.lang.psi.JetSimpleNameExpression; +import org.jetbrains.k2js.translate.context.TranslationContext; + +import static org.jetbrains.k2js.translate.utils.BindingUtils.getDescriptorForReferenceExpression; + +/** + * @author Pavel Talanov + */ +public final class ReferenceTranslator { + + @NotNull + public static JsExpression translateSimpleName(@NotNull JetSimpleNameExpression expression, + @NotNull TranslationContext context) { + if (PropertyAccessTranslator.canBePropertyGetterCall(expression, context)) { + return PropertyAccessTranslator.translateAsPropertyGetterCall(expression, null, CallType.NORMAL, context); + } + DeclarationDescriptor referencedDescriptor = + getDescriptorForReferenceExpression(context.bindingContext(), expression); + return translateAsLocalNameReference(referencedDescriptor, context); + } + + @NotNull + public static JsExpression translateAsFQReference(@NotNull DeclarationDescriptor referencedDescriptor, + @NotNull TranslationContext context) { + JsExpression qualifier = context.getQualifierForDescriptor(referencedDescriptor); + if (qualifier == null) { + return translateAsLocalNameReference(referencedDescriptor, context); + } + JsName referencedName = context.getNameForDescriptor(referencedDescriptor); + return AstUtil.qualified(referencedName, qualifier); + } + + @NotNull + public static JsExpression translateAsLocalNameReference(@NotNull DeclarationDescriptor referencedDescriptor, + @NotNull TranslationContext context) { + JsName referencedName = context.getNameForDescriptor(referencedDescriptor); + return referencedName.makeRef(); + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/utils/AnnotationsUtils.java b/js/js.backend/src/org/jetbrains/k2js/translate/utils/AnnotationsUtils.java new file mode 100644 index 00000000000..d169f11ac6e --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/utils/AnnotationsUtils.java @@ -0,0 +1,111 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.utils; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.ClassDescriptor; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; +import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor; +import org.jetbrains.jet.lang.resolve.DescriptorUtils; +import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant; + +import static org.jetbrains.k2js.translate.utils.DescriptorUtils.getContainingClass; + +/** + * @author Pavel Talanov + */ +public final class AnnotationsUtils { + + @NotNull + public static final String NATIVE_ANNOTATION_FQNAME = "js.native"; + @NotNull + public static final String LIBRARY_ANNOTATION_FQNAME = "js.library"; + + private AnnotationsUtils() { + } + + //TODO: make public, use when necessary + private static boolean hasAnnotation(@NotNull DeclarationDescriptor descriptor, + @NotNull String annotationFQNAme) { + return getAnnotationByName(descriptor, annotationFQNAme) != null; + } + + @NotNull + public static String getAnnotationStringParameter(@NotNull DeclarationDescriptor declarationDescriptor, + @NotNull String annotationFQName) { + AnnotationDescriptor annotationDescriptor = + getAnnotationByName(declarationDescriptor, annotationFQName); + assert annotationDescriptor != null; + //TODO: this is a quick fix for unsupported default args problem + if (annotationDescriptor.getValueArguments().isEmpty()) { + return ""; + } + CompileTimeConstant constant = annotationDescriptor.getValueArguments().iterator().next(); + //TODO: this is a quick fix for unsupported default args problem + if (constant == null) { + return ""; + } + Object value = constant.getValue(); + assert value instanceof String : "Native function annotation should have one String parameter"; + return (String) value; + } + + @Nullable + public static AnnotationDescriptor getAnnotationByName(@NotNull DeclarationDescriptor descriptor, + @NotNull String FQName) { + for (AnnotationDescriptor annotationDescriptor : descriptor.getAnnotations()) { + String annotationClassFQName = getAnnotationClassFQName(annotationDescriptor); + if (annotationClassFQName.equals(FQName)) { + return annotationDescriptor; + } + } + return null; + } + + @NotNull + private static String getAnnotationClassFQName(@NotNull AnnotationDescriptor annotationDescriptor) { + DeclarationDescriptor annotationDeclaration = + annotationDescriptor.getType().getConstructor().getDeclarationDescriptor(); + assert annotationDeclaration != null : "Annotation supposed to have a declaration"; + return DescriptorUtils.getFQName(annotationDeclaration); + } + + public static boolean isNativeObject(@NotNull DeclarationDescriptor descriptor) { + return hasAnnotationOrInsideAnnotatedClass(descriptor, NATIVE_ANNOTATION_FQNAME); + } + + public static boolean isLibraryObject(@NotNull DeclarationDescriptor descriptor) { + return hasAnnotationOrInsideAnnotatedClass(descriptor, LIBRARY_ANNOTATION_FQNAME); + } + + //TODO: the use of this method is splattered across the code which can be hard to track + public static boolean isPredefinedObject(@NotNull DeclarationDescriptor descriptor) { + return isLibraryObject(descriptor) || isNativeObject(descriptor); + } + + private static boolean hasAnnotationOrInsideAnnotatedClass(DeclarationDescriptor descriptor, String annotationFQName) { + if (getAnnotationByName(descriptor, annotationFQName) != null) { + return true; + } + ClassDescriptor containingClass = getContainingClass(descriptor); + if (containingClass == null) { + return false; + } + return (getAnnotationByName(containingClass, annotationFQName) != null); + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/utils/BindingUtils.java b/js/js.backend/src/org/jetbrains/k2js/translate/utils/BindingUtils.java new file mode 100644 index 00000000000..636d0e6b9ff --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/utils/BindingUtils.java @@ -0,0 +1,341 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.utils; + +import com.google.common.collect.Sets; +import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.*; +import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.resolve.calls.ResolvedCall; +import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant; +import org.jetbrains.jet.lang.types.JetStandardClasses; +import org.jetbrains.jet.lang.types.JetType; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import static org.jetbrains.k2js.translate.utils.DescriptorUtils.*; + +/** + * @author Pavel Talanov + *

+ * This class contains some code related to BindingContext use. Intention is not to pollute other classes. + * Every call to BindingContext.get() is supposed to be wrapped by this utility class. + */ +public final class BindingUtils { + + private BindingUtils() { + } + + @NotNull + static private + D getDescriptorForExpression(@NotNull BindingContext context, @NotNull E expression, Class descriptorClass) { + DeclarationDescriptor descriptor = context.get(BindingContext.DECLARATION_TO_DESCRIPTOR, expression); + assert descriptor != null; + assert descriptorClass.isInstance(descriptor) + : expression.toString() + " expected to have of type" + descriptorClass.toString(); + //noinspection unchecked + return (D) descriptor; + } + + @NotNull + public static ClassDescriptor getClassDescriptor(@NotNull BindingContext context, + @NotNull JetClassOrObject declaration) { + return getDescriptorForExpression(context, declaration, ClassDescriptor.class); + } + + @NotNull + public static NamespaceDescriptor getNamespaceDescriptor(@NotNull BindingContext context, + @NotNull JetFile declaration) { + NamespaceDescriptor namespaceDescriptor = + context.get(BindingContext.FQNAME_TO_NAMESPACE_DESCRIPTOR, JetPsiUtil.getFQName(declaration)); + assert namespaceDescriptor != null : "File should have a namespace descriptor."; + return namespaceDescriptor; + } + + @NotNull + public static FunctionDescriptor getFunctionDescriptor(@NotNull BindingContext context, + @NotNull JetDeclarationWithBody declaration) { + return getDescriptorForExpression(context, declaration, FunctionDescriptor.class); + } + + //TODO: + @NotNull + public static PropertyAccessorDescriptor getPropertyAccessorDescriptor(@NotNull BindingContext context, + @NotNull JetPropertyAccessor declaration) { + return getDescriptorForExpression(context, declaration, PropertyAccessorDescriptor.class); + } + + @NotNull + public static PropertyDescriptor getPropertyDescriptor(@NotNull BindingContext context, + @NotNull JetProperty declaration) { + return getDescriptorForExpression(context, declaration, PropertyDescriptor.class); + } + + @NotNull + public static JetClass getClassForDescriptor(@NotNull BindingContext context, + @NotNull ClassDescriptor descriptor) { + PsiElement result = context.get(BindingContext.DESCRIPTOR_TO_DECLARATION, descriptor); + assert result instanceof JetClass : "ClassDescriptor should have declaration of type JetClass"; + return (JetClass) result; + } + + @NotNull + public static List getDeclarationsForNamespace(@NotNull BindingContext bindingContext, + @NotNull NamespaceDescriptor namespace) { + List declarations = new ArrayList(); + for (DeclarationDescriptor descriptor : namespace.getMemberScope().getAllDescriptors()) { + if (AnnotationsUtils.isPredefinedObject(descriptor)) { + continue; + } + //TODO: + if (descriptor instanceof NamespaceDescriptor) { + continue; + } + JetDeclaration declaration = BindingUtils.getDeclarationForDescriptor(bindingContext, descriptor); + if (declaration != null) { + declarations.add(declaration); + } + } + return declarations; + } + + @Nullable + private static JetDeclaration getDeclarationForDescriptor(@NotNull BindingContext context, + @NotNull DeclarationDescriptor descriptor) { + PsiElement result = context.get(BindingContext.DESCRIPTOR_TO_DECLARATION, descriptor); + if (result == null) { + //TODO: never get there + return null; + } + assert result instanceof JetDeclaration : "Descriptor should correspond to an element."; + return (JetDeclaration) result; + } + + @NotNull + private static JetParameter getParameterForDescriptor(@NotNull BindingContext context, + @NotNull ValueParameterDescriptor descriptor) { + PsiElement result = context.get(BindingContext.DESCRIPTOR_TO_DECLARATION, descriptor); + assert result instanceof JetParameter : "ValueParameterDescriptor should have corresponding JetParameter."; + return (JetParameter) result; + } + + public static boolean hasAncestorClass(@NotNull BindingContext context, @NotNull JetClassOrObject classDeclaration) { + ClassDescriptor classDescriptor = getClassDescriptor(context, classDeclaration); + List superclassDescriptors = getSuperclassDescriptors(classDescriptor); + return (DescriptorUtils.findAncestorClass(superclassDescriptors) != null); + } + + public static boolean isStatement(@NotNull BindingContext context, @NotNull JetExpression expression) { + Boolean isStatement = context.get(BindingContext.STATEMENT, expression); + assert isStatement != null : "Invalid behaviour of get(BindingContext.STATEMENT)"; + return isStatement; + // return IsStatement.isStatement(expression); + } + + @NotNull + public static JetType getTypeByReference(@NotNull BindingContext context, + @NotNull JetTypeReference typeReference) { + JetType result = context.get(BindingContext.TYPE, typeReference); + assert result != null : "TypeReference should reference a type"; + return result; + } + + @NotNull + public static ClassDescriptor getClassDescriptorForTypeReference(@NotNull BindingContext context, + @NotNull JetTypeReference typeReference) { + return getClassDescriptorForType(getTypeByReference(context, typeReference)); + } + + @Nullable + public static PropertyDescriptor getPropertyDescriptorForConstructorParameter(@NotNull BindingContext context, + @NotNull JetParameter parameter) { + return context.get(BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, parameter); + } + + @Nullable + public static JetProperty getPropertyForDescriptor(@NotNull BindingContext context, + @NotNull PropertyDescriptor property) { + PsiElement result = context.get(BindingContext.DESCRIPTOR_TO_DECLARATION, property); + if (!(result instanceof JetProperty)) { + return null; + } + return (JetProperty) result; + } + + @NotNull + public static DeclarationDescriptor getDescriptorForReferenceExpression(@NotNull BindingContext context, + @NotNull JetReferenceExpression reference) { + DeclarationDescriptor referencedDescriptor = getNullableDescriptorForReferenceExpression(context, reference); + assert referencedDescriptor != null : "Reference expression must reference a descriptor."; + return referencedDescriptor; + } + + @Nullable + private static DeclarationDescriptor getNullableDescriptorForReferenceExpression(@NotNull BindingContext context, + @NotNull JetReferenceExpression reference) { + DeclarationDescriptor referencedDescriptor = context.get(BindingContext.REFERENCE_TARGET, reference); + if (isVariableAsFunction(referencedDescriptor)) { + assert referencedDescriptor != null; + return getVariableDescriptorForVariableAsFunction((VariableAsFunctionDescriptor) referencedDescriptor); + } + return referencedDescriptor; + } + + public static boolean isNotAny(@NotNull DeclarationDescriptor superClassDescriptor) { + return !superClassDescriptor.equals(JetStandardClasses.getAny()); + } + + + //TODO: check where we use there, suspicious + public static boolean isOwnedByNamespace(@NotNull DeclarationDescriptor descriptor) { + if (descriptor instanceof ConstructorDescriptor) { + DeclarationDescriptor classDescriptor = descriptor.getContainingDeclaration(); + assert classDescriptor != null; + return isOwnedByNamespace(classDescriptor); + } + return (descriptor.getContainingDeclaration() instanceof NamespaceDescriptor); + } + + public static boolean isOwnedByClass(@NotNull DeclarationDescriptor descriptor) { + if (descriptor instanceof ConstructorDescriptor) { + DeclarationDescriptor classDescriptor = descriptor.getContainingDeclaration(); + assert classDescriptor != null; + return isOwnedByClass(classDescriptor); + } + return (descriptor.getContainingDeclaration() instanceof ClassDescriptor); + } + + @NotNull + public static ResolvedCall getResolvedCall(@NotNull BindingContext context, + @NotNull JetExpression expression) { + ResolvedCall resolvedCall = context.get(BindingContext.RESOLVED_CALL, expression); + assert resolvedCall != null : "Must resolve to a call."; + return resolvedCall; + } + + @NotNull + public static ResolvedCall getResolvedCallForCallExpression(@NotNull BindingContext context, + @NotNull JetCallExpression expression) { + JetExpression calleeExpression = PsiUtils.getCallee(expression); + return getResolvedCall(context, calleeExpression); + } + + public static boolean isVariableReassignment(@NotNull BindingContext context, @NotNull JetExpression expression) { + Boolean result = context.get(BindingContext.VARIABLE_REASSIGNMENT, expression); + assert result != null; + return result; + } + + + @Nullable + public static FunctionDescriptor getFunctionDescriptorForOperationExpression(@NotNull BindingContext context, + @NotNull JetOperationExpression expression) { + DeclarationDescriptor descriptorForReferenceExpression = getNullableDescriptorForReferenceExpression + (context, expression.getOperationReference()); + + if (descriptorForReferenceExpression == null) return null; + + assert descriptorForReferenceExpression instanceof FunctionDescriptor + : "Operation should resolve to function descriptor."; + return (FunctionDescriptor) descriptorForReferenceExpression; + } + + @NotNull + public static DeclarationDescriptor getDescriptorForElement(@NotNull BindingContext context, + @NotNull PsiElement element) { + DeclarationDescriptor descriptor = context.get(BindingContext.DECLARATION_TO_DESCRIPTOR, element); + assert descriptor != null : element + " doesn't have a descriptor."; + return descriptor; + } + + @Nullable + public static Object getCompileTimeValue(@NotNull BindingContext context, @NotNull JetExpression expression) { + CompileTimeConstant compileTimeValue = context.get(BindingContext.COMPILE_TIME_VALUE, expression); + if (compileTimeValue != null) { + return compileTimeValue.getValue(); + } + return null; + } + + @NotNull + public static JetExpression getDefaultArgument(@NotNull BindingContext context, + @NotNull ValueParameterDescriptor parameterDescriptor) { + assert parameterDescriptor.hasDefaultValue() : "Unsupplied parameter must have default value."; + JetParameter psiParameter = getParameterForDescriptor(context, parameterDescriptor); + JetExpression defaultValue = psiParameter.getDefaultValue(); + assert defaultValue != null : "No default value found in PSI."; + return defaultValue; + } + + @NotNull + public static FunctionDescriptor getIteratorFunction(@NotNull BindingContext context, + @NotNull JetExpression rangeExpression) { + FunctionDescriptor functionDescriptor = context.get(BindingContext.LOOP_RANGE_ITERATOR, rangeExpression); + assert functionDescriptor != null : "Range expression must have a descriptor for iterator function."; + return functionDescriptor; + } + + @NotNull + public static FunctionDescriptor getNextFunction(@NotNull BindingContext context, + @NotNull JetExpression rangeExpression) { + FunctionDescriptor functionDescriptor = context.get(BindingContext.LOOP_RANGE_NEXT, rangeExpression); + assert functionDescriptor != null : "Range expression must have a descriptor for next function."; + return functionDescriptor; + } + + @NotNull + public static CallableDescriptor getHasNextCallable(@NotNull BindingContext context, + @NotNull JetExpression rangeExpression) { + CallableDescriptor hasNextDescriptor = context.get(BindingContext.LOOP_RANGE_HAS_NEXT, rangeExpression); + assert hasNextDescriptor != null : "Range expression must have a descriptor for hasNext function or property."; + return hasNextDescriptor; + } + + @NotNull + public static PropertyDescriptor getPropertyDescriptorForObjectDeclaration(@NotNull BindingContext context, + @NotNull JetObjectDeclarationName name) { + PropertyDescriptor propertyDescriptor = context.get(BindingContext.OBJECT_DECLARATION, name); + assert propertyDescriptor != null; + return propertyDescriptor; + } + + @NotNull + public static Set getAllNonNativeNamespaceDescriptors(@NotNull BindingContext context, + @NotNull List files) { + Set descriptorSet = Sets.newHashSet(); + for (JetFile file : files) { + NamespaceDescriptor namespaceDescriptor = getNamespaceDescriptor(context, file); + if (!AnnotationsUtils.isPredefinedObject(namespaceDescriptor)) { + descriptorSet.add(namespaceDescriptor); + } + } + return descriptorSet; + } + + @NotNull + public static JetType getTypeForExpression(@NotNull BindingContext context, + @NotNull JetExpression expression) { + JetType type = context.get(BindingContext.EXPRESSION_TYPE, expression); + assert type != null; + return type; + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/utils/ClassSorter.java b/js/js.backend/src/org/jetbrains/k2js/translate/utils/ClassSorter.java new file mode 100644 index 00000000000..2ad048cd13c --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/utils/ClassSorter.java @@ -0,0 +1,141 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.utils; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.descriptors.ClassDescriptor; +import org.jetbrains.jet.lang.psi.JetClass; +import org.jetbrains.jet.lang.resolve.BindingContext; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.jetbrains.k2js.translate.utils.DescriptorUtils.getSuperclassDescriptors; + + +//TODO: can optimise using less dumb implementation +//TODO: pass list of descriptors here, not the list of jet classes + +/** + * @author Pavel Talanov + */ +public final class ClassSorter { + + @NotNull + private final List descriptorList; + @NotNull + private final List classesWithNoAncestors; + @NotNull + private final Map classWasInheritedCount = new HashMap(); + @NotNull + private final BindingContext bindingContext; + + @NotNull + public static List sortUsingInheritanceOrder(@NotNull List original, + @NotNull BindingContext bindingContext) { + ClassSorter sorter = new ClassSorter(original, bindingContext); + return sorter.sortUsingInheritanceOrder(); + } + + private ClassSorter(@NotNull List original, @NotNull BindingContext bindingContext) { + this.bindingContext = bindingContext; + this.descriptorList = getDescriptorList(original); + this.classesWithNoAncestors = new ArrayList(descriptorList); + setInitialCount(); + } + + @NotNull + private List sortUsingInheritanceOrder() { + List sortedClasses = new ArrayList(); + while (!classesWithNoAncestors.isEmpty()) { + ClassDescriptor classDescriptor = getNextClass(); + sortedClasses.add(BindingUtils.getClassForDescriptor(bindingContext, classDescriptor)); + } + assert sortedClasses.size() == descriptorList.size(); + return sortedClasses; + } + + @NotNull + private ClassDescriptor getNextClass() { + ClassDescriptor result = popFromList(); + decreaseCountForDerivedClasses(result); + classWasInheritedCount.remove(result); + return result; + } + + private void decreaseCountForDerivedClasses(@NotNull ClassDescriptor result) { + for (ClassDescriptor derived : descriptorList) { + if (isDerivedClass(result, derived)) { + decreaseCountForDerivedClass(derived); + } + } + } + + private void decreaseCountForDerivedClass(@NotNull ClassDescriptor derived) { + Integer timesInherited = classWasInheritedCount.get(derived); + assert timesInherited != null; + assert timesInherited > 0; + int newCount = timesInherited - 1; + classWasInheritedCount.put(derived, newCount); + if (newCount == 0) { + classesWithNoAncestors.add(derived); + } + } + + private boolean isDerivedClass(@NotNull ClassDescriptor ancestor, @NotNull ClassDescriptor derived) { + return (getSuperclassDescriptors(derived).contains(ancestor)); + } + + @NotNull + private ClassDescriptor popFromList() { + assert !classesWithNoAncestors.isEmpty(); + ClassDescriptor result = classesWithNoAncestors.get(classesWithNoAncestors.size() - 1); + ClassDescriptor removed = classesWithNoAncestors.remove(classesWithNoAncestors.size() - 1); + assert removed != null; + return result; + } + + + @NotNull + private List getDescriptorList(@NotNull List classesToSort) { + List descriptorList = new ArrayList(); + for (JetClass jetClass : classesToSort) { + descriptorList.add(BindingUtils.getClassDescriptor(bindingContext, jetClass)); + } + return descriptorList; + } + + private void setInitialCount() { + for (ClassDescriptor descriptor : descriptorList) { + List superclasses = getSuperclassDescriptors(descriptor); + int count = 0; + for (ClassDescriptor superclassDescriptor : superclasses) { + if (descriptorList.contains(superclassDescriptor)) { + count++; + } + } + classWasInheritedCount.put(descriptor, superclasses.size()); + if (count > 0) { + boolean success = classesWithNoAncestors.remove(descriptor); + assert success; + } + } + } + +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/utils/DescriptorUtils.java b/js/js.backend/src/org/jetbrains/k2js/translate/utils/DescriptorUtils.java new file mode 100644 index 00000000000..19fc3588871 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/utils/DescriptorUtils.java @@ -0,0 +1,222 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.utils; + +import com.google.common.collect.Lists; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.*; +import org.jetbrains.jet.lang.resolve.scopes.JetScope; +import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor; +import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.lang.types.expressions.OperatorConventions; +import org.jetbrains.k2js.translate.context.Namer; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import static org.jetbrains.k2js.translate.utils.BindingUtils.isNotAny; + +/** + * @author Pavel Talanov + */ +public final class DescriptorUtils { + + private DescriptorUtils() { + } + + private static int valueParametersCount(@NotNull FunctionDescriptor functionDescriptor) { + return functionDescriptor.getValueParameters().size(); + } + + public static boolean hasParameters(@NotNull FunctionDescriptor functionDescriptor) { + return (valueParametersCount(functionDescriptor) > 0); + } + + public static boolean isEquals(@NotNull FunctionDescriptor functionDescriptor) { + return (functionDescriptor.getName().equals(OperatorConventions.EQUALS)); + } + + public static boolean isCompareTo(@NotNull FunctionDescriptor functionDescriptor) { + return (functionDescriptor.getName().equals(OperatorConventions.COMPARE_TO)); + } + + public static boolean isConstructorDescriptor(@NotNull CallableDescriptor descriptor) { + return (descriptor instanceof ConstructorDescriptor); + } + + @NotNull + public static FunctionDescriptor getFunctionByName(@NotNull JetScope scope, + @NotNull String name) { + Set functionDescriptors = scope.getFunctions(name); + assert functionDescriptors.size() == 1 : + "In scope " + scope + " supposed to be exactly one " + name + " function.\n" + + "Found: " + functionDescriptors.size(); + //noinspection LoopStatementThatDoesntLoop + for (FunctionDescriptor descriptor : functionDescriptors) { + return descriptor; + } + throw new AssertionError("In scope " + scope + + " supposed to be exactly one " + name + " function."); + } + + //TODO: some stange stuff happening to this method + @NotNull + public static PropertyDescriptor getPropertyByName(@NotNull JetScope scope, + @NotNull String name) { + VariableDescriptor variable = scope.getLocalVariable(name); + if (variable == null) { + variable = scope.getPropertyByFieldReference("$" + name); + } + Set variables = scope.getProperties(name); + assert variables.size() == 1 : "Actual size: " + variables.size(); + variable = variables.iterator().next(); + PropertyDescriptor descriptor = (PropertyDescriptor) variable; + assert descriptor != null : "Must have a descriptor."; + return descriptor; + } + + @Nullable + public static ClassDescriptor findAncestorClass(@NotNull List superclassDescriptors) { + for (ClassDescriptor descriptor : superclassDescriptors) { + if (descriptor.getKind() == ClassKind.CLASS) { + return descriptor; + } + } + return null; + } + + @NotNull + public static List getSuperclassDescriptors(@NotNull ClassDescriptor classDescriptor) { + Collection superclassTypes = classDescriptor.getTypeConstructor().getSupertypes(); + List superClassDescriptors = new ArrayList(); + for (JetType type : superclassTypes) { + ClassDescriptor result = getClassDescriptorForType(type); + if (isNotAny(result)) { + superClassDescriptors.add(result); + } + } + return superClassDescriptors; + } + + @Nullable + public static ClassDescriptor getSuperclass(@NotNull ClassDescriptor classDescriptor) { + return findAncestorClass(getSuperclassDescriptors(classDescriptor)); + } + + @NotNull + public static ClassDescriptor getClassDescriptorForType(@NotNull JetType type) { + DeclarationDescriptor superClassDescriptor = + type.getConstructor().getDeclarationDescriptor(); + assert superClassDescriptor instanceof ClassDescriptor + : "Superclass descriptor of a type should be of type ClassDescriptor"; + return (ClassDescriptor) superClassDescriptor; + } + + @NotNull + public static VariableDescriptor getVariableDescriptorForVariableAsFunction + (@NotNull VariableAsFunctionDescriptor descriptor) { + VariableDescriptor functionVariable = descriptor.getVariableDescriptor(); + assert functionVariable != null; + return functionVariable; + } + + + public static boolean isVariableAsFunction(@Nullable DeclarationDescriptor referencedDescriptor) { + return referencedDescriptor instanceof VariableAsFunctionDescriptor; + } + + @NotNull + public static DeclarationDescriptor getContainingDeclaration(@NotNull DeclarationDescriptor descriptor) { + DeclarationDescriptor containing = descriptor.getContainingDeclaration(); + assert containing != null : "Should be called on objects that have containing declaration."; + return containing; + } + + public static boolean isExtensionFunction(@NotNull CallableDescriptor functionDescriptor) { + return (functionDescriptor.getReceiverParameter().exists()); + } + + @NotNull + public static String getNameForNamespace(@NotNull NamespaceDescriptor descriptor) { + String name = descriptor.getName(); + if (name.equals("")) { + return Namer.getAnonymousNamespaceName(); + } + return name; + } + + //TODO: why callable descriptor + @Nullable + public static DeclarationDescriptor getExpectedThisDescriptor(@NotNull CallableDescriptor callableDescriptor) { + ReceiverDescriptor expectedThisObject = callableDescriptor.getExpectedThisObject(); + if (!expectedThisObject.exists()) { + return null; + } + return getDeclarationDescriptorForReceiver(expectedThisObject); + } + + @NotNull + public static DeclarationDescriptor getDeclarationDescriptorForReceiver + (@NotNull ReceiverDescriptor receiverParameter) { + DeclarationDescriptor declarationDescriptor = + receiverParameter.getType().getConstructor().getDeclarationDescriptor(); + //TODO: WHY assert? + assert declarationDescriptor != null; + return declarationDescriptor.getOriginal(); + } + + @Nullable + public static DeclarationDescriptor getExpectedReceiverDescriptor(@NotNull CallableDescriptor callableDescriptor) { + ReceiverDescriptor receiverParameter = callableDescriptor.getReceiverParameter(); + if (!receiverParameter.exists()) { + return null; + } + return getDeclarationDescriptorForReceiver(receiverParameter); + } + + //TODO: maybe we have similar routine + @Nullable + public static ClassDescriptor getContainingClass(@NotNull DeclarationDescriptor descriptor) { + DeclarationDescriptor containing = descriptor.getContainingDeclaration(); + while (containing != null) { + if (containing instanceof ClassDescriptor) { + return (ClassDescriptor) containing; + } + containing = containing.getContainingDeclaration(); + } + return null; + } + + @NotNull + public static List getAllClassesDefinedInNamespace(@NotNull NamespaceDescriptor namespaceDescriptor) { + List classDescriptors = Lists.newArrayList(); + for (DeclarationDescriptor descriptor : namespaceDescriptor.getMemberScope().getAllDescriptors()) { + if (AnnotationsUtils.isPredefinedObject(descriptor)) { + continue; + } + if (descriptor instanceof ClassDescriptor) { + classDescriptors.add((ClassDescriptor) descriptor); + } + } + return classDescriptors; + } + + +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/utils/PsiUtils.java b/js/js.backend/src/org/jetbrains/k2js/translate/utils/PsiUtils.java new file mode 100644 index 00000000000..1625d013acb --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/utils/PsiUtils.java @@ -0,0 +1,163 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.utils; + +import com.intellij.psi.PsiElement; +import com.intellij.psi.tree.IElementType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.jet.lexer.JetToken; +import org.jetbrains.jet.lexer.JetTokens; +import org.jetbrains.k2js.translate.context.Namer; + +import java.util.Collections; +import java.util.List; + +/** + * @author Pavel Talanov + */ +public final class PsiUtils { + + private PsiUtils() { + } + + @Nullable + public static JetSimpleNameExpression getSelectorAsSimpleName(@NotNull JetQualifiedExpression expression) { + JetExpression selectorExpression = getSelector(expression); + if (!(selectorExpression instanceof JetSimpleNameExpression)) { + return null; + } + return (JetSimpleNameExpression) selectorExpression; + } + + @NotNull + public static JetExpression getSelector(@NotNull JetQualifiedExpression expression) { + JetExpression selectorExpression = expression.getSelectorExpression(); + assert selectorExpression != null : "Selector should not be null."; + return selectorExpression; + } + + @NotNull + public static JetSimpleNameExpression getNotNullSimpleNameSelector(@NotNull JetQualifiedExpression expression) { + JetSimpleNameExpression selectorAsSimpleName = getSelectorAsSimpleName(expression); + assert selectorAsSimpleName != null; + return selectorAsSimpleName; + } + + @NotNull + public static JetToken getOperationToken(@NotNull JetOperationExpression expression) { + JetSimpleNameExpression operationExpression = expression.getOperationReference(); + IElementType elementType = operationExpression.getReferencedNameElementType(); + assert elementType instanceof JetToken : "Unary expression should have operation token of type JetToken"; + return (JetToken) elementType; + } + + @NotNull + public static JetExpression getBaseExpression(@NotNull JetUnaryExpression expression) { + JetExpression baseExpression = expression.getBaseExpression(); + assert baseExpression != null; + return baseExpression; + } + + public static boolean isPrefix(@NotNull JetUnaryExpression expression) { + return (expression instanceof JetPrefixExpression); + } + + public static boolean isAssignment(JetToken token) { + return (token == JetTokens.EQ); + } + + public static boolean isBackingFieldReference(@NotNull JetSimpleNameExpression expression) { + return expression.getReferencedNameElementType() == JetTokens.FIELD_IDENTIFIER; + } + + public static boolean isInOrNotInOperation(@NotNull JetBinaryExpression binaryExpression) { + return isInOperation(binaryExpression) || isNotInOperation(binaryExpression); + } + + public static boolean isNotInOperation(@NotNull JetBinaryExpression binaryExpression) { + return (binaryExpression.getOperationToken() == JetTokens.NOT_IN); + } + + private static boolean isInOperation(@NotNull JetBinaryExpression binaryExpression) { + return (binaryExpression.getOperationToken() == JetTokens.IN_KEYWORD); + } + + @NotNull + public static JetExpression getCallee(@NotNull JetCallExpression expression) { + JetExpression calleeExpression = expression.getCalleeExpression(); + assert calleeExpression != null; + return calleeExpression; + } + + @NotNull + public static JetExpression getLoopBody(@NotNull JetLoopExpression expression) { + JetExpression body = expression.getBody(); + assert body != null : "Loops cannot have null bodies."; + return body; + } + + @NotNull + public static JetParameter getLoopParameter(@NotNull JetForExpression expression) { + JetParameter loopParameter = expression.getLoopParameter(); + assert loopParameter != null; + return loopParameter; + } + + @NotNull + public static List getPrimaryConstructorParameters(@NotNull JetClassOrObject classDeclaration) { + if (classDeclaration instanceof JetClass) { + return ((JetClass) classDeclaration).getPrimaryConstructorParameters(); + } + return Collections.emptyList(); + } + + @NotNull + public static JetObjectDeclaration getObjectDeclarationForName(@NotNull JetObjectDeclarationName name) { + PsiElement parent = name.getParent(); + assert parent instanceof JetObjectDeclaration : + "ObjectDeclarationName should have a parent of type ObjectDeclaration."; + return (JetObjectDeclaration) parent; + } + + @NotNull + public static JetObjectDeclarationName getObjectDeclarationName(@NotNull JetObjectDeclaration objectDeclaration) { + //TODO: util + JetObjectDeclarationName nameAsDeclaration = objectDeclaration.getNameAsDeclaration(); + assert nameAsDeclaration != null; + return nameAsDeclaration; + } + + @NotNull + public static String getNamespaceName(@NotNull JetFile psiFile) { + JetNamespaceHeader namespaceHeader = psiFile.getNamespaceHeader(); + String name = namespaceHeader.getName(); + assert name != null : "NamespaceHeader must have a name"; + if (name.equals("")) { + return Namer.getAnonymousNamespaceName(); + } + return name; + } + + @NotNull + public static JetExpression getLoopRange(@NotNull JetForExpression expression) { + JetExpression rangeExpression = expression.getLoopRange(); + assert rangeExpression != null; + return rangeExpression; + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java b/js/js.backend/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java new file mode 100644 index 00000000000..d0832046e8e --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java @@ -0,0 +1,233 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.translate.utils; + +import com.google.dart.compiler.backend.js.ast.*; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.*; +import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.k2js.translate.context.NamingScope; +import org.jetbrains.k2js.translate.context.TemporaryVariable; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.general.Translation; + +import java.util.ArrayList; +import java.util.List; + +import static org.jetbrains.k2js.translate.utils.BindingUtils.*; +import static org.jetbrains.k2js.translate.utils.DescriptorUtils.getExpectedReceiverDescriptor; + +/** + * @author Pavel Talanov + */ +public final class TranslationUtils { + + @NotNull + public static JsBinaryOperation notNullCheck(@NotNull TranslationContext context, + @NotNull JsExpression expressionToCheck) { + JsNullLiteral nullLiteral = context.program().getNullLiteral(); + return AstUtil.notEqual(expressionToCheck, nullLiteral); + } + + @NotNull + public static JsBinaryOperation isNullCheck(@NotNull TranslationContext context, + @NotNull JsExpression expressionToCheck) { + JsNullLiteral nullLiteral = context.program().getNullLiteral(); + return AstUtil.equals(expressionToCheck, nullLiteral); + } + + @NotNull + public static List translateArgumentList(@NotNull TranslationContext context, + @NotNull List jetArguments) { + List jsArguments = new ArrayList(); + for (ValueArgument argument : jetArguments) { + jsArguments.add(translateArgument(context, argument)); + } + return jsArguments; + } + + @NotNull + private static JsExpression translateArgument(@NotNull TranslationContext context, @NotNull ValueArgument argument) { + JetExpression jetExpression = argument.getArgumentExpression(); + assert jetExpression != null : "Argument with no expression"; + return Translation.translateAsExpression(jetExpression, context); + } + + //TODO: refactor backing field reference generation to use the generic way + @NotNull + public static JsNameRef backingFieldReference(@NotNull TranslationContext context, + @NotNull JetProperty expression) { + PropertyDescriptor propertyDescriptor = getPropertyDescriptor(context.bindingContext(), expression); + return backingFieldReference(context, propertyDescriptor); + } + + @NotNull + public static JsNameRef backingFieldReference(@NotNull TranslationContext context, + @NotNull PropertyDescriptor descriptor) { + JsName backingFieldName = context.getNameForDescriptor(descriptor); + if (isOwnedByClass(descriptor)) { + return AstUtil.qualified(backingFieldName, new JsThisRef()); + } + assert isOwnedByNamespace(descriptor) + : "Only classes and namespaces may own backing fields."; + JsNameRef qualifier = context.getQualifierForDescriptor(descriptor); + return AstUtil.qualified(backingFieldName, qualifier); + } + + @NotNull + public static JsStatement assignmentToBackingField(@NotNull TranslationContext context, + @NotNull PropertyDescriptor descriptor, + @NotNull JsExpression assignTo) { + JsNameRef backingFieldReference = backingFieldReference(context, descriptor); + return AstUtil.newAssignmentStatement(backingFieldReference, assignTo); + } + + @Nullable + public static JsExpression translateInitializerForProperty(@NotNull JetProperty declaration, + @NotNull TranslationContext context) { + JsExpression jsInitExpression = null; + JetExpression initializer = declaration.getInitializer(); + if (initializer != null) { + jsInitExpression = Translation.translateAsExpression(initializer, context); + } + return jsInitExpression; + } + + @NotNull + public static JsNameRef getQualifiedReference(@NotNull TranslationContext context, + @NotNull DeclarationDescriptor descriptor) { + JsName name = context.getNameForDescriptor(descriptor); + JsNameRef reference = name.makeRef(); + JsNameRef qualifier = context.getQualifierForDescriptor(descriptor); + if (qualifier != null) { + AstUtil.setQualifier(reference, qualifier); + } + return reference; + } + + //TODO: refactor + @NotNull + public static JsExpression getThisObject(@NotNull TranslationContext context, + @NotNull DeclarationDescriptor correspondingDeclaration) { + JsExpression thisRef = null; + if (correspondingDeclaration instanceof ClassDescriptor) { + if (context.aliaser().hasAliasForThis(correspondingDeclaration)) { + thisRef = context.aliaser().getAliasForThis(correspondingDeclaration); + } + } + if (correspondingDeclaration instanceof CallableDescriptor) { + DeclarationDescriptor receiverDescriptor = + getExpectedReceiverDescriptor((CallableDescriptor) correspondingDeclaration); + assert receiverDescriptor != null; + if (context.aliaser().hasAliasForThis(receiverDescriptor)) { + thisRef = context.aliaser().getAliasForThis(receiverDescriptor); + } + } + if (thisRef != null) { + return thisRef; + } + return new JsThisRef(); + } + + @NotNull + public static List translateExpressionList(@NotNull TranslationContext context, + @NotNull List expressions) { + List result = new ArrayList(); + for (JetExpression expression : expressions) { + result.add(Translation.translateAsExpression(expression, context)); + } + return result; + } + + @NotNull + public static JsExpression translateBaseExpression(@NotNull TranslationContext context, + @NotNull JetUnaryExpression expression) { + JetExpression baseExpression = PsiUtils.getBaseExpression(expression); + return Translation.translateAsExpression(baseExpression, context); + } + + //TODO: + @NotNull + public static JsExpression translateReceiver(@NotNull TranslationContext context, + @NotNull JetDotQualifiedExpression expression) { + return Translation.translateAsExpression(expression.getReceiverExpression(), context); + } + + + @NotNull + public static JsExpression translateLeftExpression(@NotNull TranslationContext context, + @NotNull JetBinaryExpression expression) { + return Translation.translateAsExpression(expression.getLeft(), context); + } + + @NotNull + public static JsExpression translateRightExpression(@NotNull TranslationContext context, + @NotNull JetBinaryExpression expression) { + JetExpression rightExpression = expression.getRight(); + assert rightExpression != null : "Binary expression should have a right expression"; + return Translation.translateAsExpression(rightExpression, context); + } + + public static boolean isIntrinsicOperation(@NotNull TranslationContext context, + @NotNull JetOperationExpression expression) { + FunctionDescriptor operationDescriptor = + BindingUtils.getFunctionDescriptorForOperationExpression(context.bindingContext(), expression); + + if (operationDescriptor == null) return true; + if (context.intrinsics().isIntrinsic(operationDescriptor)) return true; + + return false; + } + + @NotNull + public static JsNameRef getMethodReferenceForOverloadedOperation(@NotNull TranslationContext context, + @NotNull JetOperationExpression expression) { + FunctionDescriptor overloadedOperationDescriptor = getFunctionDescriptorForOperationExpression + (context.bindingContext(), expression); + assert overloadedOperationDescriptor != null; + JsNameRef overloadedOperationReference = context.getNameForDescriptor(overloadedOperationDescriptor).makeRef(); + assert overloadedOperationReference != null; + return overloadedOperationReference; + } + + @NotNull + public static JsFunction functionWithScope(@NotNull NamingScope scope) { + return JsFunction.getAnonymousFunctionWithScope(scope.jsScope()); + } + + @NotNull + public static JsNumberLiteral zeroLiteral(@NotNull TranslationContext context) { + return context.program().getNumberLiteral(0); + } + + @NotNull + public static TemporaryVariable newAliasForThis(@NotNull TranslationContext context, + @NotNull DeclarationDescriptor descriptor) { + JsExpression thisQualifier = getThisObject(context, descriptor); + TemporaryVariable aliasForThis = context.declareTemporary(thisQualifier); + context.aliaser().setAliasForThis(descriptor, aliasForThis.name()); + return aliasForThis; + } + + public static void removeAliasForThis(@NotNull TranslationContext context, + @NotNull DeclarationDescriptor descriptor) { + context.aliaser().removeAliasForThis(descriptor); + } + +} diff --git a/js/js.backend/src/org/jetbrains/k2js/utils/GenerationUtils.java b/js/js.backend/src/org/jetbrains/k2js/utils/GenerationUtils.java new file mode 100644 index 00000000000..ae96b635c59 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/utils/GenerationUtils.java @@ -0,0 +1,43 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.utils; + +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +//TODO: very thin class + +/** + * @author Pavel Talanov + */ +public final class GenerationUtils { + + @NotNull + public static String generateCallToMain(@NotNull String namespaceName, @NotNull List arguments) { + String constructArguments = "var args = [];\n"; + int index = 0; + for (String argument : arguments) { + constructArguments = constructArguments + "args[" + index + "]= \"" + argument + "\";\n"; + index++; + } + String callMain = namespaceName + ".main(args);\n"; + return constructArguments + callMain; + } + + +} diff --git a/js/js.backend/src/org/jetbrains/k2js/utils/JetFileUtils.java b/js/js.backend/src/org/jetbrains/k2js/utils/JetFileUtils.java new file mode 100644 index 00000000000..76d2f7ebba9 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/utils/JetFileUtils.java @@ -0,0 +1,89 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.utils; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.CharsetToolkit; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiFileFactory; +import com.intellij.psi.impl.PsiFileFactoryImpl; +import com.intellij.testFramework.LightVirtualFile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.JetFile; +import org.jetbrains.jet.plugin.JetLanguage; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Pavel Talanov + */ +public final class JetFileUtils { + + @NotNull + public static String loadFile(@NotNull String path) throws IOException { + return doLoadFile(path); + } + + @NotNull + private static String doLoadFile(@NotNull String path) throws IOException { + String text = FileUtil.loadFile(new File(path), CharsetToolkit.UTF8).trim(); + text = StringUtil.convertLineSeparators(text); + return text; + } + + @NotNull + public static JetFile createPsiFile(@NotNull String name, + @NotNull String text, + @NotNull Project project) { + return (JetFile) createFile(name + ".jet", text, project); + } + + @NotNull + public static JetFile loadPsiFile(@NotNull String name, @NotNull Project project) { + try { + return createPsiFile(name, loadFile(name), project); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @NotNull + private static PsiFile createFile(@NotNull String name, @NotNull String text, @NotNull Project project) { + LightVirtualFile virtualFile = new LightVirtualFile(name, JetLanguage.INSTANCE, text); + virtualFile.setCharset(CharsetToolkit.UTF8_CHARSET); + PsiFile result = ((PsiFileFactoryImpl) PsiFileFactory.getInstance(project)) + .trySetupPsiForFile(virtualFile, JetLanguage.INSTANCE, true, false); + assert result != null; + return result; + } + + + @NotNull + public static List createPsiFileList(@NotNull List inputFiles, + @NotNull Project project) { + List psiFiles = new ArrayList(); + for (String inputFile : inputFiles) { + psiFiles.add(JetFileUtils.loadPsiFile(inputFile, project)); + } + return psiFiles; + } +} diff --git a/js/js.backend/src/org/jetbrains/k2js/utils/JetTestUtils.java b/js/js.backend/src/org/jetbrains/k2js/utils/JetTestUtils.java new file mode 100644 index 00000000000..b9f3b05be11 --- /dev/null +++ b/js/js.backend/src/org/jetbrains/k2js/utils/JetTestUtils.java @@ -0,0 +1,178 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.utils; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.cfg.pseudocode.JetControlFlowDataTraceFactory; +import org.jetbrains.jet.lang.diagnostics.Diagnostic; +import org.jetbrains.jet.lang.diagnostics.Severity; +import org.jetbrains.jet.lang.diagnostics.UnresolvedReferenceDiagnostic; +import org.jetbrains.jet.lang.psi.JetFile; +import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.resolve.BindingTrace; +import org.jetbrains.jet.lang.resolve.java.AnalyzerFacade; +import org.jetbrains.jet.util.slicedmap.ReadOnlySlice; +import org.jetbrains.jet.util.slicedmap.SlicedMap; +import org.jetbrains.jet.util.slicedmap.WritableSlice; + +import java.io.File; +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; + +/** + * @author abreslav + */ +public class JetTestUtils { + public static final BindingTrace DUMMY_TRACE = new BindingTrace() { + + + @Override + public BindingContext getBindingContext() { + return new BindingContext() { + + @Override + public Collection getDiagnostics() { + throw new UnsupportedOperationException(); // TODO + } + + @Override + public V get(ReadOnlySlice slice, K key) { + return DUMMY_TRACE.get(slice, key); + } + + @NotNull + @Override + public Collection getKeys(WritableSlice slice) { + return DUMMY_TRACE.getKeys(slice); + } + }; + } + + @Override + public void record(WritableSlice slice, K key, V value) { + } + + @Override + public void record(WritableSlice slice, K key) { + } + + @Override + public V get(ReadOnlySlice slice, K key) { + if (slice == BindingContext.PROCESSED) return (V) Boolean.FALSE; + return SlicedMap.DO_NOTHING.get(slice, key); + } + + @NotNull + @Override + public Collection getKeys(WritableSlice slice) { + assert slice.isCollective(); + return Collections.emptySet(); + } + + @Override + public void report(@NotNull Diagnostic diagnostic) { + if (diagnostic instanceof UnresolvedReferenceDiagnostic) { + UnresolvedReferenceDiagnostic unresolvedReferenceDiagnostic = (UnresolvedReferenceDiagnostic) diagnostic; + throw new IllegalStateException("Unresolved: " + unresolvedReferenceDiagnostic.getPsiElement().getText()); + } + } + }; + + public static BindingTrace DUMMY_EXCEPTION_ON_ERROR_TRACE = new BindingTrace() { + @Override + public BindingContext getBindingContext() { + return new BindingContext() { + @Override + public Collection getDiagnostics() { + throw new UnsupportedOperationException(); + } + + @Override + public V get(ReadOnlySlice slice, K key) { + return DUMMY_EXCEPTION_ON_ERROR_TRACE.get(slice, key); + } + + @NotNull + @Override + public Collection getKeys(WritableSlice slice) { + return DUMMY_EXCEPTION_ON_ERROR_TRACE.getKeys(slice); + } + }; + } + + @Override + public void record(WritableSlice slice, K key, V value) { + } + + @Override + public void record(WritableSlice slice, K key) { + } + + @Override + public V get(ReadOnlySlice slice, K key) { + return null; + } + + @NotNull + @Override + public Collection getKeys(WritableSlice slice) { + assert slice.isCollective(); + return Collections.emptySet(); + } + + @Override + public void report(@NotNull Diagnostic diagnostic) { + if (diagnostic.getSeverity() == Severity.ERROR) { + throw new IllegalStateException(diagnostic.getMessage()); + } + } + }; + + public static BindingContext analyzeNamespace(@NotNull JetFile file, @NotNull JetControlFlowDataTraceFactory flowDataTraceFactory) { + return AnalyzerFacade.analyzeOneFileWithJavaIntegration(file, flowDataTraceFactory); + } + + + public static void mkdirs(File file) throws IOException { + if (file.isDirectory()) { + return; + } + if (!file.mkdirs()) { + throw new IOException(); + } + } + + public static void rmrf(File file) { + if (file != null) { + File[] children = file.listFiles(); + if (children != null) { + for (File child : children) { + rmrf(child); + } + } + file.delete(); + } + } + + public static void recreateDirectory(File file) throws IOException { + rmrf(file); + mkdirs(file); + } + + +} diff --git a/js/js.backend/testFiles/canvas_helper.js b/js/js.backend/testFiles/canvas_helper.js new file mode 100644 index 00000000000..90d9287df1f --- /dev/null +++ b/js/js.backend/testFiles/canvas_helper.js @@ -0,0 +1,27 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function getContext() { + return getCanvas().getContext('2d'); +} + +function getCanvas() { + return document.getElementsByTagName('canvas')[0]; +} + +function getKotlinLogo() { + return document.getElementsByTagName('img')[0]; +} \ No newline at end of file diff --git a/js/js.backend/testFiles/class/cases/classWithoutNamespace.kt b/js/js.backend/testFiles/class/cases/classWithoutNamespace.kt new file mode 100644 index 00000000000..4aceb233099 --- /dev/null +++ b/js/js.backend/testFiles/class/cases/classWithoutNamespace.kt @@ -0,0 +1,7 @@ +class A(var a : Int) { + { + $a=3 + } +} + +fun box() = (A(1).a == 3) \ No newline at end of file diff --git a/js/js.backend/testFiles/class/out/.gitignore b/js/js.backend/testFiles/class/out/.gitignore new file mode 100644 index 00000000000..86d0cb2726c --- /dev/null +++ b/js/js.backend/testFiles/class/out/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file diff --git a/js/js.backend/testFiles/examples/cases/closureWithParameter.jet b/js/js.backend/testFiles/examples/cases/closureWithParameter.jet new file mode 100644 index 00000000000..e0b5d616951 --- /dev/null +++ b/js/js.backend/testFiles/examples/cases/closureWithParameter.jet @@ -0,0 +1,7 @@ +fun box() : String { + return apply( "OK", {(arg: String) -> arg } ) +} + +fun apply(arg : String, f : (p:String) -> String) : String { + return f(arg) +} diff --git a/js/js.backend/testFiles/examples/cases/closureWithParameterAndBoxing.jet b/js/js.backend/testFiles/examples/cases/closureWithParameterAndBoxing.jet new file mode 100644 index 00000000000..2b64a547d29 --- /dev/null +++ b/js/js.backend/testFiles/examples/cases/closureWithParameterAndBoxing.jet @@ -0,0 +1,7 @@ +fun box() : String { + return if (apply( 5, {(arg: Int) -> arg + 13 } ) == 18) "OK" else "fail" +} + +fun apply(arg : Int, f : (p:Int) -> Int) : Int { + return f(arg) +} diff --git a/js/js.backend/testFiles/examples/cases/diamondInheritance.jet b/js/js.backend/testFiles/examples/cases/diamondInheritance.jet new file mode 100644 index 00000000000..210aecafedc --- /dev/null +++ b/js/js.backend/testFiles/examples/cases/diamondInheritance.jet @@ -0,0 +1,24 @@ +// Changed when traits were introduced. May not make sense any more + +open class Base() { + public var v : Int = 0 +} + +open class Left() : Base() {} +trait Right : Base {} + +class D() : Left(), Right + +fun vl(l : Left) : Int = l.v +fun vr(r : Right) : Int = r.v + +fun box() : String { + val d = D() + d.v = 42 + + if (d.v != 42) return "Fail #1" + if (vl(d) != 42) return "Fail #2" + if (vr(d) != 42) return "Fail #3" + + return "OK" +} \ No newline at end of file diff --git a/js/js.backend/testFiles/examples/cases/doubleEnclosedLocalVariable.jet b/js/js.backend/testFiles/examples/cases/doubleEnclosedLocalVariable.jet new file mode 100644 index 00000000000..96ac7f2ef19 --- /dev/null +++ b/js/js.backend/testFiles/examples/cases/doubleEnclosedLocalVariable.jet @@ -0,0 +1,8 @@ +fun box() : String { + val cl = 39 + return if (sum(200, { val ff = {cl}; ff() }) == 239) "OK" else "FAIL" +} + +fun sum(arg:Int, f : () -> Int) : Int { + return arg + f() +} diff --git a/js/js.backend/testFiles/examples/cases/enclosingLocalVariable.jet b/js/js.backend/testFiles/examples/cases/enclosingLocalVariable.jet new file mode 100644 index 00000000000..01e1a103ff2 --- /dev/null +++ b/js/js.backend/testFiles/examples/cases/enclosingLocalVariable.jet @@ -0,0 +1,8 @@ +fun box() : String { + val cl = 39 + return if (sum(200, { val m = { val r = { cl }; r() }; m() }) == 239) "OK" else "FAIL" +} + +fun sum(arg:Int, f : () -> Int) : Int { + return arg + f() +} diff --git a/js/js.backend/testFiles/examples/cases/extensionClosure.jet b/js/js.backend/testFiles/examples/cases/extensionClosure.jet new file mode 100644 index 00000000000..21307ea42ab --- /dev/null +++ b/js/js.backend/testFiles/examples/cases/extensionClosure.jet @@ -0,0 +1,13 @@ +class Point(val x : Int, val y : Int) + +fun box() : String { + val answer = apply(Point(3, 5), { Point.(scalar : Int) : Point -> + Point(x * scalar, y * scalar) + }) + + return if (answer.x == 6 && answer.y == 10) "OK" else "FAIL" +} + +fun apply(arg:Point, f : Point.(scalar : Int) -> Point) : Point { + return arg.f(2) +} diff --git a/js/js.backend/testFiles/examples/cases/funDelegation.jet b/js/js.backend/testFiles/examples/cases/funDelegation.jet new file mode 100644 index 00000000000..10a1f408b09 --- /dev/null +++ b/js/js.backend/testFiles/examples/cases/funDelegation.jet @@ -0,0 +1,17 @@ +open class Base() { + fun n(n : Int) : Int = n + 1 +} + +trait Abstract {} + +class Derived1() : Base(), Abstract {} +class Derived2() : Abstract, Base() {} + +fun test(s : Base) : Boolean = s.n(238) == 239 + +fun box() : String { + if (!test(Base())) return "Fail #1" + if (!test(Derived1())) return "Fail #2" + if (!test(Derived2())) return "Fail #3" + return "OK" +} \ No newline at end of file diff --git a/js/js.backend/testFiles/examples/cases/incrementProperty.kt b/js/js.backend/testFiles/examples/cases/incrementProperty.kt new file mode 100644 index 00000000000..29123dbcaf3 --- /dev/null +++ b/js/js.backend/testFiles/examples/cases/incrementProperty.kt @@ -0,0 +1,14 @@ +class Slot() { + var vitality: Int = 10000 + + fun increaseVitality(delta: Int) { + vitality = vitality + delta + if (vitality > 65535) vitality = 65535; + } +} + +fun box(): String { + val s = Slot() + s.increaseVitality(1000) + if (s.vitality == 11000) return "OK" else return "fail" +} diff --git a/js/js.backend/testFiles/examples/cases/inheritedMethod.jet b/js/js.backend/testFiles/examples/cases/inheritedMethod.jet new file mode 100644 index 00000000000..97d432077a8 --- /dev/null +++ b/js/js.backend/testFiles/examples/cases/inheritedMethod.jet @@ -0,0 +1,13 @@ +open class Foo() { + fun xyzzy(): String = "xyzzy" +} + +class Bar(): Foo() { + fun test(): String = xyzzy() +} + +fun box() : String { + val bar = Bar() + val f = bar.test() + return if (f == "xyzzy") "OK" else "fail" +} diff --git a/js/js.backend/testFiles/examples/cases/initializerBlock.jet b/js/js.backend/testFiles/examples/cases/initializerBlock.jet new file mode 100644 index 00000000000..c89deeda87d --- /dev/null +++ b/js/js.backend/testFiles/examples/cases/initializerBlock.jet @@ -0,0 +1,13 @@ +class C() { + public var f: Int + + { + $f = 610 + } +} + +fun box(): String { + val c = C() + if (c.f != 610) return "fail" + return "OK" +} diff --git a/js/js.backend/testFiles/examples/cases/kt242.jet b/js/js.backend/testFiles/examples/cases/kt242.jet new file mode 100644 index 00000000000..4e2de93cff3 --- /dev/null +++ b/js/js.backend/testFiles/examples/cases/kt242.jet @@ -0,0 +1,19 @@ +fun box() : String { + val i: Int? = 7 + val j: Int? = null + val k = 7 + + //verify errors + if (i == 7) {} + if (7 == i) {} + + if (j == 7) {} + if (7 == j) {} + + if (i == k) {} + if (k == i) {} + + if (j == k) {} + if (k == j) {} + return "OK" +} \ No newline at end of file diff --git a/js/js.backend/testFiles/examples/cases/newInstanceDefaultConstructor.jet b/js/js.backend/testFiles/examples/cases/newInstanceDefaultConstructor.jet new file mode 100644 index 00000000000..6cd0ebd954b --- /dev/null +++ b/js/js.backend/testFiles/examples/cases/newInstanceDefaultConstructor.jet @@ -0,0 +1,11 @@ +class SimpleClass() { + fun foo() = 610 +} + +fun box() : String { + val c = SimpleClass() + if (c.foo() == 610) { + return "OK" + } + return "FAIL" +} diff --git a/js/js.backend/testFiles/examples/cases/overloadBinaryOperator.jet b/js/js.backend/testFiles/examples/cases/overloadBinaryOperator.jet new file mode 100644 index 00000000000..bee66062979 --- /dev/null +++ b/js/js.backend/testFiles/examples/cases/overloadBinaryOperator.jet @@ -0,0 +1,25 @@ +import java.util.* + +class ArrayWrapper() { + val contents = ArrayList() + + fun add(item: T) { + contents.add(item) + } + + fun plus(b: ArrayWrapper): ArrayWrapper { + val result = ArrayWrapper() + result.contents.addAll(contents) + result.contents.addAll(b.contents) + return result + } +} + +fun box(): String { + val v1 = ArrayWrapper() + val v2 = ArrayWrapper() + v1.add("foo") + v2.add("bar") + val v3 = v1 + v2 + return if (v3.contents.size() == 2) "OK" else "fail" +} diff --git a/js/js.backend/testFiles/examples/cases/overloadPlusAssignReturn.jet b/js/js.backend/testFiles/examples/cases/overloadPlusAssignReturn.jet new file mode 100644 index 00000000000..b6d0c3c0282 --- /dev/null +++ b/js/js.backend/testFiles/examples/cases/overloadPlusAssignReturn.jet @@ -0,0 +1,27 @@ +import java.util.* + +class ArrayWrapper() { + val contents = ArrayList() + + fun add(item: T) { + contents.add(item) + } + + fun plusAssign(rhs: ArrayWrapper) { + contents.addAll(rhs.contents) + } + + fun get(index: Int): T { + return contents.get(index) + } +} + +fun box(): String { + var v1 = ArrayWrapper() + val v2 = ArrayWrapper() + v1.add("foo") + val v3 = v1 + v2.add("bar") + v1 += v2 + return if (v1.contents.size() == 2 && v3.contents.size() == 2) "OK" else "fail" +} diff --git a/js/js.backend/testFiles/examples/cases/overloadPlusToPlusAssign.jet b/js/js.backend/testFiles/examples/cases/overloadPlusToPlusAssign.jet new file mode 100644 index 00000000000..c5236b0ace4 --- /dev/null +++ b/js/js.backend/testFiles/examples/cases/overloadPlusToPlusAssign.jet @@ -0,0 +1,30 @@ +import java.util.* + +class ArrayWrapper() { + val contents = ArrayList() + + fun add(item: T) { + contents.add(item) + } + + fun plus(rhs: ArrayWrapper): ArrayWrapper { + val result = ArrayWrapper() + result.contents.addAll(contents) + result.contents.addAll(rhs.contents) + return result + } + + fun get(index: Int): T { + return contents.get(index) + } +} + +fun box(): String { + var v1 = ArrayWrapper() + val v2 = ArrayWrapper() + v1.add("foo") + val v3 = v1 + v2.add("bar") + v1 += v2 + return if (v1.contents.size() == 2 && v3.contents.size() == 1) "OK" else "fail" +} diff --git a/js/js.backend/testFiles/examples/cases/overloadUnaryOperator.jet b/js/js.backend/testFiles/examples/cases/overloadUnaryOperator.jet new file mode 100644 index 00000000000..2cbe1a27f3a --- /dev/null +++ b/js/js.backend/testFiles/examples/cases/overloadUnaryOperator.jet @@ -0,0 +1,31 @@ +import java.util.* + +class ArrayWrapper() { + val contents = ArrayList() + + fun add(item: T) { + contents.add(item) + } + + fun minus(): ArrayWrapper { + val result = ArrayWrapper() + result.contents.addAll(contents) + var i = contents.size(); + for (a in contents) { + result.contents[--i] = a; + } + return result + } + + fun get(index: Int): T { + return contents.get(index) + } +} + +fun box(): String { + val v1 = ArrayWrapper() + v1.add("foo") + v1.add("bar") + val v2 = -v1 + return if (v2[0] == "bar" && v2[1] == "foo") "OK" else "fail" +} diff --git a/js/js.backend/testFiles/examples/cases/propertyDelegation.jet b/js/js.backend/testFiles/examples/cases/propertyDelegation.jet new file mode 100644 index 00000000000..614c624f124 --- /dev/null +++ b/js/js.backend/testFiles/examples/cases/propertyDelegation.jet @@ -0,0 +1,33 @@ +open class Base() { + val plain = 239 + public val read : Int + get() = 239 + + public var readwrite : Int = 0 + get() = $readwrite + 1 + set(n : Int) { + $readwrite = n + } +} + +trait Abstract {} + +class Derived1() : Base(), Abstract {} +class Derived2() : Abstract, Base() {} + +fun code(s : Base) : Int { + if (s.plain != 239) return 1 + if (s.read != 239) return 2 + s.readwrite = 238 + if (s.readwrite != 239) return 3 + return 0 +} + +fun test(s : Base) : Boolean = code(s) == 0 + +fun box() : String { + if (!test(Base())) return "Fail #1" + if (!test(Derived1())) return "Fail #2" + if (!test(Derived2())) return "Fail #3" + return "OK" +} \ No newline at end of file diff --git a/js/js.backend/testFiles/examples/cases/rightHandOverride.jet b/js/js.backend/testFiles/examples/cases/rightHandOverride.jet new file mode 100644 index 00000000000..e99e60bd863 --- /dev/null +++ b/js/js.backend/testFiles/examples/cases/rightHandOverride.jet @@ -0,0 +1,20 @@ +// Changed when traits were introduced. May not make sense any more + +trait Left {} +open class Right() { + open fun f() = 42 +} + +class D() : Left, Right() { + override fun f() = 239 +} + +fun box() : String { + val r : Right = Right() + val d : D = D() + + if (r.f() != 42) return "Fail #1" + if (d.f() != 239) return "Fail #2" + + return "OK" +} \ No newline at end of file diff --git a/js/js.backend/testFiles/examples/cases/simplestClosure.jet b/js/js.backend/testFiles/examples/cases/simplestClosure.jet new file mode 100644 index 00000000000..7a9f3b439cf --- /dev/null +++ b/js/js.backend/testFiles/examples/cases/simplestClosure.jet @@ -0,0 +1,7 @@ +fun box() : String { + return invoker( {"OK"} ) +} + +fun invoker(gen : () -> String) : String { + return gen() +} diff --git a/js/js.backend/testFiles/examples/cases/simplestClosureAndBoxing.jet b/js/js.backend/testFiles/examples/cases/simplestClosureAndBoxing.jet new file mode 100644 index 00000000000..50f22fbeb8d --- /dev/null +++ b/js/js.backend/testFiles/examples/cases/simplestClosureAndBoxing.jet @@ -0,0 +1,7 @@ +fun box() : String { + return if (int_invoker( { 7 } ) == 7) "OK" else "fail" +} + +fun int_invoker(gen : () -> Int) : Int { + return gen() +} diff --git a/js/js.backend/testFiles/examples/out/.gitignore b/js/js.backend/testFiles/examples/out/.gitignore new file mode 100644 index 00000000000..86d0cb2726c --- /dev/null +++ b/js/js.backend/testFiles/examples/out/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file diff --git a/js/js.backend/testFiles/examples/sortedTests/unsupported/basicmethod.jet b/js/js.backend/testFiles/examples/sortedTests/unsupported/basicmethod.jet new file mode 100644 index 00000000000..9920bc3b69d --- /dev/null +++ b/js/js.backend/testFiles/examples/sortedTests/unsupported/basicmethod.jet @@ -0,0 +1,21 @@ +import java.util.ArrayList + +trait Tr { + fun extra() : String = "_" +} + +class N() : ArrayList(), Tr { + override fun add(el: Any) : Boolean { + super.add(el) + return super.add(el.toString() + super.extra() + el + extra()) + } + + override fun extra() : String = super.extra() + super.extra() +} + +fun box(): String { + val n = N() + n.add("239") + if (n.get(0) == "239" && n.get(1) == "239_239__") return "OK"; + return "fail"; +} \ No newline at end of file diff --git a/js/js.backend/testFiles/examples/sortedTests/unsupported/basicproperty.jet b/js/js.backend/testFiles/examples/sortedTests/unsupported/basicproperty.jet new file mode 100644 index 00000000000..ae1cfa5618b --- /dev/null +++ b/js/js.backend/testFiles/examples/sortedTests/unsupported/basicproperty.jet @@ -0,0 +1,22 @@ +open class M() { + open var b: Int = 0 +} + +class N() : M() { + val a : Int + get() { + super.b = super.b + 1 + return super.b + 1 + } + override var b: Int = a + 1 + + val superb : Int + get() = super.b +} + +fun box(): String { + val n = N() + println("a: " + n.a + " b: " + n.b + " superb: " + n.superb) + if (n.b == 3 && n.a == 4 && n.superb == 3) return "OK"; + return "fail"; +} \ No newline at end of file diff --git a/js/js.backend/testFiles/examples/sortedTests/unsupported/classObject.jet b/js/js.backend/testFiles/examples/sortedTests/unsupported/classObject.jet new file mode 100644 index 00000000000..76eff77c438 --- /dev/null +++ b/js/js.backend/testFiles/examples/sortedTests/unsupported/classObject.jet @@ -0,0 +1,11 @@ +class C() { + class object { + fun create() = C() + } +} + +fun box(): String { + val c = C.create() + return if (c is C) "OK" else "fail" +} + diff --git a/js/js.backend/testFiles/examples/sortedTests/unsupported/classObjectInterface.jet b/js/js.backend/testFiles/examples/sortedTests/unsupported/classObjectInterface.jet new file mode 100644 index 00000000000..432bb37bee0 --- /dev/null +++ b/js/js.backend/testFiles/examples/sortedTests/unsupported/classObjectInterface.jet @@ -0,0 +1,9 @@ +class C() { + fun getInstance(): Runnable = C + + class object: Runnable { + override fun run(): Unit { } + } +} + +fun foo() = C().getInstance() diff --git a/js/js.backend/testFiles/examples/sortedTests/unsupported/classObjectMethod.jet b/js/js.backend/testFiles/examples/sortedTests/unsupported/classObjectMethod.jet new file mode 100644 index 00000000000..d7d70a86d96 --- /dev/null +++ b/js/js.backend/testFiles/examples/sortedTests/unsupported/classObjectMethod.jet @@ -0,0 +1,13 @@ +abstract open class Default { + abstract fun defaultValue(): Int +} + +class MyInt() { + class object : Default { + override fun defaultValue(): Int = 610 + } +} + +fun toDefault(t: T) where class object T: Default = T.defaultValue() + +fun box(): String = if (toDefault(MyInt()) == 610) "OK" else "fail" diff --git a/js/js.backend/testFiles/examples/sortedTests/unsupported/enclosed.jet b/js/js.backend/testFiles/examples/sortedTests/unsupported/enclosed.jet new file mode 100644 index 00000000000..705e0b602c2 --- /dev/null +++ b/js/js.backend/testFiles/examples/sortedTests/unsupported/enclosed.jet @@ -0,0 +1,42 @@ +trait BK { + fun x() : Int = 50 +} + +trait K : BK { + override fun x() : Int = super.x() * 2 +} + +open class M() { + open fun x() : Int = 10 + + open var y = 500 +} + +open class N() : M(), K { + + override fun x() : Int = 20 + + override var y = 200 + + open class C() : K { + fun test1() = x() + fun test2() = super@N.x() + fun test3() = super@N.x() + fun test4() = super.x() + fun test5() = y + fun test6() : Int { + super@N.y += 200 + return super@N.y + } + } +} + +fun box(): String { + if (N().C().test1() != 100) return "test1 fail"; + if (N().C().test2() != 10) return "test2 fail"; + if (N().C().test3() != 100) return "test3 fail"; + if (N().C().test4() != 100) return "test4 fail"; + if (N().C().test5() != 200) return "test5 fail"; + if (N().C().test6() != 700) return "test6 fail"; + return "OK"; +} \ No newline at end of file diff --git a/js/js.backend/testFiles/examples/sortedTests/unsupported/forwardTypeParameter.jet b/js/js.backend/testFiles/examples/sortedTests/unsupported/forwardTypeParameter.jet new file mode 100644 index 00000000000..2e78cd3ae9c --- /dev/null +++ b/js/js.backend/testFiles/examples/sortedTests/unsupported/forwardTypeParameter.jet @@ -0,0 +1,12 @@ +class Foo() { } +class Bar() { } + +fun isInstance(obj: Any?) = obj is T + +fun isInstance2(obj: Any?) = isInstance(obj) + +fun box(): String { + if (! isInstance2(Foo())) return "fail 1" + if (isInstance2(Foo())) return "fail 2" + return "OK" +} diff --git a/js/js.backend/testFiles/examples/sortedTests/unsupported/inheritance.jet b/js/js.backend/testFiles/examples/sortedTests/unsupported/inheritance.jet new file mode 100644 index 00000000000..0bfdb4e497f --- /dev/null +++ b/js/js.backend/testFiles/examples/sortedTests/unsupported/inheritance.jet @@ -0,0 +1,41 @@ +// Changed when traits were introduced. May not make sense any more + +open class X(val x : Int) {} +trait Y { + abstract val y : Int +} + +class YImpl(override val y : Int) : Y {} + +class Point(x : Int, yy : Int) : X(x) , Y { + override val y : Int = yy +} + +trait Abstract {} + +class P1(x : Int, yy : Y) : Abstract, X(x), Y by yy {} +class P2(x : Int, yy : Y) : X(x), Abstract, Y by yy {} +class P3(x : Int, yy : Y) : X(x), Y by yy, Abstract {} +class P4(x : Int, yy : Y) : Y by yy, Abstract, X(x) {} + +fun box() : String { + if (X(239).x != 239) return "FAIL #1" + if (YImpl(239).y != 239) return "FAIL #2" + + val p = Point(240, -1) + if (p.x + p.y != 239) return "FAIL #3" + + val y = YImpl(-1) + val p1 = P1(240, y) + if (p1.x + p1.y != 239) return "FAIL #4" + val p2 = P2(240, y) + if (p2.x + p2.y != 239) return "FAIL #5" + + val p3 = P3(240, y) + if (p3.x + p3.y != 239) return "FAIL #6" + + val p4 = P4(240, y) + if (p4.x + p4.y != 239) return "FAIL #7" + + return "OK" +} \ No newline at end of file diff --git a/js/js.backend/testFiles/examples/sortedTests/unsupported/inheritedInnerClass.jet b/js/js.backend/testFiles/examples/sortedTests/unsupported/inheritedInnerClass.jet new file mode 100644 index 00000000000..4717bea55a8 --- /dev/null +++ b/js/js.backend/testFiles/examples/sortedTests/unsupported/inheritedInnerClass.jet @@ -0,0 +1,14 @@ +class Outer() { + open class InnerBase() { + } + + class InnerDerived(): InnerBase() { + } + + public val foo: InnerBase? = InnerDerived() +} + +fun box() : String { + val o = Outer() + return if (o.foo === null) "fail" else "OK" +} diff --git a/js/js.backend/testFiles/examples/sortedTests/unsupported/initializerBlockDImpl.jet b/js/js.backend/testFiles/examples/sortedTests/unsupported/initializerBlockDImpl.jet new file mode 100644 index 00000000000..aa3d227c80f --- /dev/null +++ b/js/js.backend/testFiles/examples/sortedTests/unsupported/initializerBlockDImpl.jet @@ -0,0 +1,20 @@ +import java.util.* +import java.io.* + +class World() { + public val items: ArrayList = ArrayList + + class Item() { + { + items.add(this) + } + } + + val foo = Item() +} + +fun box() : String { + val w = World() + if (w.items.size() != 1) return "fail" + return "OK" +} diff --git a/js/js.backend/testFiles/examples/sortedTests/unsupported/innerClass.jet b/js/js.backend/testFiles/examples/sortedTests/unsupported/innerClass.jet new file mode 100644 index 00000000000..df43f61e8bc --- /dev/null +++ b/js/js.backend/testFiles/examples/sortedTests/unsupported/innerClass.jet @@ -0,0 +1,19 @@ +class Outer(val foo: StringBuilder) { + class Inner() { + fun len() : Int { + return foo.length() + } + } + + fun test() : Inner { + return Inner() + } +} + +fun box() : String { + val sb = StringBuilder("xyzzy") + val o = Outer(sb) + val i = o.test() + val l = i.len() + return if (l != 5) "fail" else "OK" +} diff --git a/js/js.backend/testFiles/examples/sortedTests/unsupported/propertyInInitializer.jet b/js/js.backend/testFiles/examples/sortedTests/unsupported/propertyInInitializer.jet new file mode 100644 index 00000000000..f6e84b15afd --- /dev/null +++ b/js/js.backend/testFiles/examples/sortedTests/unsupported/propertyInInitializer.jet @@ -0,0 +1,16 @@ +class Outer() { + val s = "xyzzy" + + open class InnerBase(public val name: String) { + } + + class InnerDerived(): InnerBase(s) { + } + + val x = InnerDerived() +} + +fun box() : String { + val o = Outer() + return if (o.x.name != "xyzzy") "fail" else "OK" +} diff --git a/js/js.backend/testFiles/examples/sortedTests/unsupported/simpleBox.jet b/js/js.backend/testFiles/examples/sortedTests/unsupported/simpleBox.jet new file mode 100644 index 00000000000..be95d012d51 --- /dev/null +++ b/js/js.backend/testFiles/examples/sortedTests/unsupported/simpleBox.jet @@ -0,0 +1,8 @@ +class Box(t: T) { + var value = t +} + +fun box(): String { + val box: Box = Box(1) + return if (box.value == 1) "OK" else "fail" +} diff --git a/js/js.backend/testFiles/examples/sortedTests/unsupported/traitproperty.jet b/js/js.backend/testFiles/examples/sortedTests/unsupported/traitproperty.jet new file mode 100644 index 00000000000..715d8d73d8b --- /dev/null +++ b/js/js.backend/testFiles/examples/sortedTests/unsupported/traitproperty.jet @@ -0,0 +1,29 @@ +trait M { + var backingB : Int + var b : Int + get() = backingB + set(value: Int) { + backingB = value + } +} + +class N() : M { + override var backingB : Int = 0 + + val a : Int + get() { + super.b = super.b + 1 + return super.b + 1 + } + override var b: Int = a + 1 + + val superb : Int + get() = super.b +} + +fun box(): String { + val n = N() + println("a: " + n.a + " b: " + n.b + " superb: " + n.superb) + if (n.b == 3 && n.a == 4 && n.superb == 3) return "OK"; + return "fail"; +} \ No newline at end of file diff --git a/js/js.backend/testFiles/examples/sortedTests/usesJava/exceptionConstructor.jet b/js/js.backend/testFiles/examples/sortedTests/usesJava/exceptionConstructor.jet new file mode 100644 index 00000000000..ab6117e266e --- /dev/null +++ b/js/js.backend/testFiles/examples/sortedTests/usesJava/exceptionConstructor.jet @@ -0,0 +1,7 @@ +class GameError(msg: String): Exception(msg) { +} + +fun box(): String { + val e = GameError("foo") + return if (e.getMessage() == "foo") "OK" else "fail" +} diff --git a/js/js.backend/testFiles/expression/conditional/cases/ifAsExpressionWithThrow.kt b/js/js.backend/testFiles/expression/conditional/cases/ifAsExpressionWithThrow.kt new file mode 100644 index 00000000000..f73f96b80dd --- /dev/null +++ b/js/js.backend/testFiles/expression/conditional/cases/ifAsExpressionWithThrow.kt @@ -0,0 +1,3 @@ +package foo + +fun box() = if (true) throw Exception() else false \ No newline at end of file diff --git a/js/js.backend/testFiles/expression/conditional/out/.gitignore b/js/js.backend/testFiles/expression/conditional/out/.gitignore new file mode 100644 index 00000000000..86d0cb2726c --- /dev/null +++ b/js/js.backend/testFiles/expression/conditional/out/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file diff --git a/js/js.backend/testFiles/expression/for/cases/forIteratesOverArray.kt b/js/js.backend/testFiles/expression/for/cases/forIteratesOverArray.kt new file mode 100644 index 00000000000..0b0cad9557b --- /dev/null +++ b/js/js.backend/testFiles/expression/for/cases/forIteratesOverArray.kt @@ -0,0 +1,19 @@ +package foo + +val a1 = Array(10) + +fun box() : Boolean { + var c = 0 + var d = 0 + a1[3] = 3 + a1[5] = 5 + + for (var a : Int? in a1) { + if (a != null) { + c += 1; + } else { + d += 1 + } + } + return (c == 2) && (d == 8) +} \ No newline at end of file diff --git a/js/js.backend/testFiles/expression/for/cases/forOnEmptyArray.kt b/js/js.backend/testFiles/expression/for/cases/forOnEmptyArray.kt new file mode 100644 index 00000000000..951265396c3 --- /dev/null +++ b/js/js.backend/testFiles/expression/for/cases/forOnEmptyArray.kt @@ -0,0 +1,10 @@ +package foo + +val a1 = Array(0) + +fun box() : Boolean { + for (a in a1) { + return false; + } + return true; +} \ No newline at end of file diff --git a/js/js.backend/testFiles/expression/for/out/.gitignore b/js/js.backend/testFiles/expression/for/out/.gitignore new file mode 100644 index 00000000000..86d0cb2726c --- /dev/null +++ b/js/js.backend/testFiles/expression/for/out/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file diff --git a/js/js.backend/testFiles/expression/function/cases/KT-921.kt b/js/js.backend/testFiles/expression/function/cases/KT-921.kt new file mode 100644 index 00000000000..7c41a0fc0c0 --- /dev/null +++ b/js/js.backend/testFiles/expression/function/cases/KT-921.kt @@ -0,0 +1,67 @@ +import java.util.* + +class Lifetime() { + val attached = ArrayList< Function0 >() + + public fun attach(action : ()->Unit) + { + attached.add(action) + } + + fun close() + { + for(x in attached) x() + attached.clear() + } +} + +public class Viewable() +{ + val items = ArrayList() + + fun add(item:T) + { + items.add(item) + } + + fun remove(item:T) + { + items.remove(item) + } + + fun view(lifetime: Lifetime, viewer: (itemLifetime:Lifetime, item:T) -> Unit) + { + for(item in items) + viewer(lifetime, item) + } +} + +fun lifetime(body: (Lifetime)->Unit) +{ + val l = Lifetime() + body(l) + l.close() +} + +fun Dump(items:ArrayList) +{ + for(item in items) + print(item.toString() + ", ") + println() +} + +fun main(args:Array) +{ + val v = Viewable() + val x = ArrayList() + v.add(1) + v.add(2) + lifetime( + { + v.view(it, {(itemLifetime, item)-> + x.add(item) + Dump(x) + itemLifetime.attach { x.remove(item as Any); Dump(x) } + }) + }) +} \ No newline at end of file diff --git a/js/js.backend/testFiles/expression/function/cases/adderClosure.kt b/js/js.backend/testFiles/expression/function/cases/adderClosure.kt new file mode 100644 index 00000000000..d7c053cbb87 --- /dev/null +++ b/js/js.backend/testFiles/expression/function/cases/adderClosure.kt @@ -0,0 +1,9 @@ +package foo + +fun box() : Boolean { + var sum = 0 + val adder = {(a: Int) -> sum += a } + adder(3) + adder(2) + return sum == 5 +} \ No newline at end of file diff --git a/js/js.backend/testFiles/expression/function/cases/closureWithParameter.kt b/js/js.backend/testFiles/expression/function/cases/closureWithParameter.kt new file mode 100644 index 00000000000..31c8e3f07c0 --- /dev/null +++ b/js/js.backend/testFiles/expression/function/cases/closureWithParameter.kt @@ -0,0 +1,9 @@ +package foo + +fun box() : String { + return apply( "OK", {(arg: String) -> arg } ) +} + +fun apply(arg : String, f : (p:String) -> String) : String { + return f(arg) +} diff --git a/js/js.backend/testFiles/expression/function/cases/closureWithParameterAndBoxing.jet b/js/js.backend/testFiles/expression/function/cases/closureWithParameterAndBoxing.jet new file mode 100644 index 00000000000..e5602935931 --- /dev/null +++ b/js/js.backend/testFiles/expression/function/cases/closureWithParameterAndBoxing.jet @@ -0,0 +1,9 @@ +package foo + +fun box() : String { + return if (apply( 5, {(arg: Int) -> arg + 13 } ) == 18) "OK" else "fail" +} + +fun apply(arg : Int, f : (p:Int) -> Int) : Int { + return f(arg) +} diff --git a/js/js.backend/testFiles/expression/function/cases/defaultParameters.kt b/js/js.backend/testFiles/expression/function/cases/defaultParameters.kt new file mode 100644 index 00000000000..73945d0780a --- /dev/null +++ b/js/js.backend/testFiles/expression/function/cases/defaultParameters.kt @@ -0,0 +1,15 @@ +package foo + + +fun f(a : Int = 2, b : Int = 3) = a + b + +fun box() : Boolean +{ + if (f(1,2) != 3) return false; + if (f(1,3) != 4) return false; + if (f(3) != 6) return false; + if (f() != 5) return false; + + return true; +} + diff --git a/js/js.backend/testFiles/expression/function/cases/enclosingThis.kt b/js/js.backend/testFiles/expression/function/cases/enclosingThis.kt new file mode 100644 index 00000000000..d227b3a2cc9 --- /dev/null +++ b/js/js.backend/testFiles/expression/function/cases/enclosingThis.kt @@ -0,0 +1,12 @@ +class Point(val x:Int, val y:Int) { + fun mul() : (scalar:Int)->Point { + return { (scalar:Int):Point -> Point(x * scalar, y * scalar) } + } +} + +val m = Point(2, 3).mul() : (scalar:Int)->Point + +fun box() : String { + val answer = m(5) + return if (answer.x == 10 && answer.y == 15) "OK" else "FAIL" +} diff --git a/js/js.backend/testFiles/expression/function/cases/expressionAsFunction.kt b/js/js.backend/testFiles/expression/function/cases/expressionAsFunction.kt new file mode 100644 index 00000000000..a2f0685ae29 --- /dev/null +++ b/js/js.backend/testFiles/expression/function/cases/expressionAsFunction.kt @@ -0,0 +1,42 @@ +package foo + +import java.util.*; + +val d = {(a : Int) -> a + 1} +val p = {(a : Int) -> a * 3} + +val list = ArrayList>(); + +fun chain(start : Int) : Int { + var res = start; + for (func in list) { + res = (func)(res); + } + return res; +} + +fun box() : Boolean { + if (chain(0) != 0) { + return false; + } + list.add(d); + if (list.get(0)(0) != 1) { + return false; + } + list.add(p); + if (list.get(1)(10) != 30) { + return false; + } + if (chain(0) != 3) { + return false; + } + list.add({it * it}); + list.add({it - 100}); + if (chain(2) != -19) { + return false; + } + if (({(a : Int) -> a * a}(3)) != 9) { + return false; + } + return true; +} \ No newline at end of file diff --git a/js/js.backend/testFiles/expression/function/cases/functionInsideFunction.kt b/js/js.backend/testFiles/expression/function/cases/functionInsideFunction.kt new file mode 100644 index 00000000000..fcb2a69895f --- /dev/null +++ b/js/js.backend/testFiles/expression/function/cases/functionInsideFunction.kt @@ -0,0 +1,19 @@ +package foo + + +fun box() : Boolean{ + fun f() = 3 + + return (((f() + f()) == 6) && (b() == 24)) +} + + +fun b() : Int { + + fun a() : Int { + fun c() = 4 + return c() * 3 + } + val a = 2 + return a() * a +} \ No newline at end of file diff --git a/js/js.backend/testFiles/expression/function/cases/functionLiteral.kt b/js/js.backend/testFiles/expression/function/cases/functionLiteral.kt new file mode 100644 index 00000000000..e27b2e273f9 --- /dev/null +++ b/js/js.backend/testFiles/expression/function/cases/functionLiteral.kt @@ -0,0 +1,8 @@ +package foo + +fun box() : Boolean { + var sum = 0 + val addFive = {(a: Int) -> a + 5 } + sum = addFive(sum) + return sum == 5 +} \ No newline at end of file diff --git a/js/js.backend/testFiles/expression/function/cases/functionLiteralAsLastParameter.kt b/js/js.backend/testFiles/expression/function/cases/functionLiteralAsLastParameter.kt new file mode 100644 index 00000000000..6be5be64eb6 --- /dev/null +++ b/js/js.backend/testFiles/expression/function/cases/functionLiteralAsLastParameter.kt @@ -0,0 +1,14 @@ +package foo + +fun f(a: (Int) -> Int) = a(1) + +fun box() : Boolean { + + if (f() { + it + 2 + } != 3) return false + + if (f() {(a : Int) -> a * 300} != 300) return false; + + return true +} \ No newline at end of file diff --git a/js/js.backend/testFiles/expression/function/cases/functionLiteralAsParameter.kt b/js/js.backend/testFiles/expression/function/cases/functionLiteralAsParameter.kt new file mode 100644 index 00000000000..3fb7b7ae0e4 --- /dev/null +++ b/js/js.backend/testFiles/expression/function/cases/functionLiteralAsParameter.kt @@ -0,0 +1,10 @@ +package foo + +fun apply(f : (Int) -> Int, t : Int) : Int { + return f(t) +} + + +fun box() : Boolean { + return apply({(a: Int) -> a + 5 }, 3) == 8 +} \ No newline at end of file diff --git a/js/js.backend/testFiles/expression/function/cases/functionUsedBeforeDeclaration.kt b/js/js.backend/testFiles/expression/function/cases/functionUsedBeforeDeclaration.kt new file mode 100644 index 00000000000..a093c854230 --- /dev/null +++ b/js/js.backend/testFiles/expression/function/cases/functionUsedBeforeDeclaration.kt @@ -0,0 +1,13 @@ +package foo + + +fun box() : Boolean +{ + return f() +} + +fun f() : Boolean { + return true +} + + diff --git a/js/js.backend/testFiles/expression/function/cases/functionWithTwoParametersCall.kt b/js/js.backend/testFiles/expression/function/cases/functionWithTwoParametersCall.kt new file mode 100644 index 00000000000..4c82f64892f --- /dev/null +++ b/js/js.backend/testFiles/expression/function/cases/functionWithTwoParametersCall.kt @@ -0,0 +1,9 @@ +package foo + +fun sum(param1 : Int, param2 : Int) : Int { + return param1 + param2; +} + +fun box() : Boolean { + return (sum(1, 5) == 6) +} \ No newline at end of file diff --git a/js/js.backend/testFiles/expression/function/cases/implicitItParameter.kt b/js/js.backend/testFiles/expression/function/cases/implicitItParameter.kt new file mode 100644 index 00000000000..ecfbeb19844 --- /dev/null +++ b/js/js.backend/testFiles/expression/function/cases/implicitItParameter.kt @@ -0,0 +1,12 @@ +package foo + +fun test(f : (Int) -> Boolean, p : Int) = f(p) + +fun box() : Boolean { + if (!test({it + 1 == 2}, 1)) return false; + + if (!test({it > 1}, 3)) return false; + + return (test({((it < 1) == false)}, 1)) + +} \ No newline at end of file diff --git a/js/js.backend/testFiles/expression/function/cases/loopClosure.kt b/js/js.backend/testFiles/expression/function/cases/loopClosure.kt new file mode 100644 index 00000000000..1cf3f038528 --- /dev/null +++ b/js/js.backend/testFiles/expression/function/cases/loopClosure.kt @@ -0,0 +1,17 @@ +package foo + +var b = 0 + +fun loop(var times : Int) { + while(times > 0) { + val u = {(value : Int) -> + b = b + 1 + } + u(times--) + } +} + +fun box() : Boolean { + loop(5) + return b == 5 +} \ No newline at end of file diff --git a/js/js.backend/testFiles/expression/function/cases/namedArguments.kt b/js/js.backend/testFiles/expression/function/cases/namedArguments.kt new file mode 100644 index 00000000000..5511e10825f --- /dev/null +++ b/js/js.backend/testFiles/expression/function/cases/namedArguments.kt @@ -0,0 +1,16 @@ +package foo + +fun test(x: Int, y: Int) = y - x + +fun box() : Boolean { + if (test(1,2) != 1) { + return false; + } + if (test(x=1, y=2) != 1) { + return false; + } + if (test(y=2, x=1) != 1) { + return false; + } + return true +} \ No newline at end of file diff --git a/js/js.backend/testFiles/expression/function/cases/vararg.kt b/js/js.backend/testFiles/expression/function/cases/vararg.kt new file mode 100644 index 00000000000..d3ab09ec173 --- /dev/null +++ b/js/js.backend/testFiles/expression/function/cases/vararg.kt @@ -0,0 +1,17 @@ +package foo + +fun testSize(expectedSize : Int, vararg i : Int) : Boolean { + return (i.size == expectedSize) +} + +fun testSum(expectedSum : Int, vararg i : Int) : Boolean { + var sum = 0 + for (j in i) { + sum += j + } + + return (expectedSum == sum) +} + +fun box() = testSize(0) && testSum(0) && testSize(3, 1, 1, 1) && testSum(3, 1, 1, 1) && testSize(6, 1, 1, 1, 2, 3, 4) && + testSum(30, 10, 20, 0) \ No newline at end of file diff --git a/js/js.backend/testFiles/expression/function/out/.gitignore b/js/js.backend/testFiles/expression/function/out/.gitignore new file mode 100644 index 00000000000..86d0cb2726c --- /dev/null +++ b/js/js.backend/testFiles/expression/function/out/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file diff --git a/js/js.backend/testFiles/expression/misc/cases/intRange.kt b/js/js.backend/testFiles/expression/misc/cases/intRange.kt new file mode 100644 index 00000000000..73d578db692 --- /dev/null +++ b/js/js.backend/testFiles/expression/misc/cases/intRange.kt @@ -0,0 +1,112 @@ +package foo + +import js.* + +class RangeIterator(val start : Int, var count : Int, val reversed : Boolean) { + + var i = start + + fun next() : Int { + --count + if (reversed) { + i-- + return i + 1 + } else { + i++ + return i - 1 + } + } + + + fun hasNext() = (count > 0); +} + +class NumberRange(val start : Int, val size : Int, val reversed : Boolean) { + + val end : Int + get() = if (reversed) start - size + 1 else start + size - 1 + + fun contains(number : Int) : Boolean { + if (reversed) { + return (number <= start) && (number > start - size); + } else { + return (number >= start) && (number < start + size); + } + } + + fun iterator() = RangeIterator(start, size, reversed); +} + + + +fun box() = testRange() && testReversedRange(); + +fun testRange() : Boolean { + + val oneToFive = NumberRange(1, 4, false); + if (oneToFive.contains(5)) return false; + if (oneToFive.contains(0)) return false; + if (oneToFive.contains(-100)) return false; + if (oneToFive.contains(10)) return false; + if (!oneToFive.contains(1)) return false; + if (!oneToFive.contains(2)) return false; + if (!oneToFive.contains(3)) return false; + if (!oneToFive.contains(4)) return false; + if (!(oneToFive.start == 1)) return false; + if (!(oneToFive.size == 4)) return false; + if (!(oneToFive.end == 4)) return false; + + var sum = 0; + for (i in oneToFive) { + sum += i; + } + for (i in oneToFive) { + print(i) + } + + if (sum != 10) return false; + + return true; + +} + +fun testReversedRange() : Boolean { + + println("Testing reversed range."); + + val tenToFive = NumberRange(10, 5, true); + + if (tenToFive.contains(5)) return false; + if (tenToFive.contains(11)) return false; + if (tenToFive.contains(-100)) return false; + if (tenToFive.contains(1000)) return false; + if (!tenToFive.contains(6)) return false; + if (!tenToFive.contains(7)) return false; + if (!tenToFive.contains(8)) return false; + if (!tenToFive.contains(9)) return false; + if (!tenToFive.contains(10)) return false; + + if (!(tenToFive.start == 10)) return false; + if (!(tenToFive.size == 5)) return false; + if (!(tenToFive.end == 6)) return false; + + for (i in tenToFive) { + println(i) + } + + + var sum = 0; + for (i in tenToFive) { + sum += i; + } + + if (sum != 40) { + return false; + } + + return true; +} + +fun main(args : Array) { + println(box()) +} \ No newline at end of file diff --git a/js/js.backend/testFiles/expression/misc/cases/jquery.kt b/js/js.backend/testFiles/expression/misc/cases/jquery.kt new file mode 100644 index 00000000000..4b4c94c8273 --- /dev/null +++ b/js/js.backend/testFiles/expression/misc/cases/jquery.kt @@ -0,0 +1,8 @@ +package foo + +import jquery.*; + +fun box() : String { + val aa = jq("a").attr("a"); + return "OK" +} \ No newline at end of file diff --git a/js/js.backend/testFiles/expression/misc/cases/localProperty.jet b/js/js.backend/testFiles/expression/misc/cases/localProperty.jet new file mode 100644 index 00000000000..2f69f45afba --- /dev/null +++ b/js/js.backend/testFiles/expression/misc/cases/localProperty.jet @@ -0,0 +1,15 @@ +package foo + +val y = 3 + +fun f(a:Int) : Int { + val x = 42 + val y = 50 + + return y +} + +fun box() : Int +{ + return f(y) +} diff --git a/js/js.backend/testFiles/expression/misc/cases/safecallComputesExpressionOnlyOnce.kt b/js/js.backend/testFiles/expression/misc/cases/safecallComputesExpressionOnlyOnce.kt new file mode 100644 index 00000000000..e48f4d2da46 --- /dev/null +++ b/js/js.backend/testFiles/expression/misc/cases/safecallComputesExpressionOnlyOnce.kt @@ -0,0 +1,14 @@ +package foo + +var i = 0 + +fun test() : Int? = i++ + +fun box() : Boolean { + if (i != 0) return false + test()?.plus(1) + if (i != 1) return false + test()?.minus(2) + if (i != 2) return false + return true +} \ No newline at end of file diff --git a/js/js.backend/testFiles/expression/misc/out/.gitignore b/js/js.backend/testFiles/expression/misc/out/.gitignore new file mode 100644 index 00000000000..86d0cb2726c --- /dev/null +++ b/js/js.backend/testFiles/expression/misc/out/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file diff --git a/js/js.backend/testFiles/expression/string/cases/intInTemplate.kt b/js/js.backend/testFiles/expression/string/cases/intInTemplate.kt new file mode 100644 index 00000000000..2fa031bd831 --- /dev/null +++ b/js/js.backend/testFiles/expression/string/cases/intInTemplate.kt @@ -0,0 +1,7 @@ +package foo + +fun box() : String { + var number = 3 + return ("my age is $number"); +} + diff --git a/js/js.backend/testFiles/expression/string/cases/multipleExpressionsInTemplate.kt b/js/js.backend/testFiles/expression/string/cases/multipleExpressionsInTemplate.kt new file mode 100644 index 00000000000..cede8ae7ef9 --- /dev/null +++ b/js/js.backend/testFiles/expression/string/cases/multipleExpressionsInTemplate.kt @@ -0,0 +1,8 @@ +package foo + +fun box() : String { + var right = 2 + var left = 3 + return ("left = $left\nright = $right\nsum = ${left + right}\n"); +} + diff --git a/js/js.backend/testFiles/expression/string/cases/objectToStringCallInTemplate.kt b/js/js.backend/testFiles/expression/string/cases/objectToStringCallInTemplate.kt new file mode 100644 index 00000000000..7eab5902391 --- /dev/null +++ b/js/js.backend/testFiles/expression/string/cases/objectToStringCallInTemplate.kt @@ -0,0 +1,18 @@ +package foo + +class A(var i : Int) { + fun toString() = "a$i" +} + +fun box() : Boolean { + var p = A(2); + var n = A(1); + if ("$p$n" != "a2a1") { + return false; + } + if ("${A(10)}" != "a10") { + return false; + } + return true; +} + diff --git a/js/js.backend/testFiles/expression/string/cases/stringAssignment.kt b/js/js.backend/testFiles/expression/string/cases/stringAssignment.kt new file mode 100644 index 00000000000..9f3c327965f --- /dev/null +++ b/js/js.backend/testFiles/expression/string/cases/stringAssignment.kt @@ -0,0 +1,10 @@ +package foo + +fun box() : Boolean { + + val a = "bar"; + var b = "foo"; + b = a; + return (b == "bar"); +} + diff --git a/js/js.backend/testFiles/expression/string/cases/stringConstant.kt b/js/js.backend/testFiles/expression/string/cases/stringConstant.kt new file mode 100644 index 00000000000..9961104c902 --- /dev/null +++ b/js/js.backend/testFiles/expression/string/cases/stringConstant.kt @@ -0,0 +1,8 @@ +package foo + +fun box() : Boolean { + + val a = "String"; + return true; +} + diff --git a/js/js.backend/testFiles/expression/string/cases/stringInTemplate.kt b/js/js.backend/testFiles/expression/string/cases/stringInTemplate.kt new file mode 100644 index 00000000000..5a021f9aec2 --- /dev/null +++ b/js/js.backend/testFiles/expression/string/cases/stringInTemplate.kt @@ -0,0 +1,7 @@ +package foo + +fun box() : String { + var name = "Hello" + return ("o${name}o"); +} + diff --git a/js/js.backend/testFiles/expression/string/out/.gitignore b/js/js.backend/testFiles/expression/string/out/.gitignore new file mode 100644 index 00000000000..86d0cb2726c --- /dev/null +++ b/js/js.backend/testFiles/expression/string/out/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file diff --git a/js/js.backend/testFiles/extensionFunction/cases/extensionFunctionOnExpression.kt b/js/js.backend/testFiles/extensionFunction/cases/extensionFunctionOnExpression.kt new file mode 100644 index 00000000000..7b3c4b004da --- /dev/null +++ b/js/js.backend/testFiles/extensionFunction/cases/extensionFunctionOnExpression.kt @@ -0,0 +1,13 @@ +package foo + +fun Int.same() : Int { + return this +} + +fun Int.quadruple() : Int { + return same() * 4; +} + +fun box(): Boolean { + return ((3 + 4).quadruple() == 28) +} diff --git a/js/js.backend/testFiles/extensionFunction/cases/extensionInsideFunctionLiteral.kt b/js/js.backend/testFiles/extensionFunction/cases/extensionInsideFunctionLiteral.kt new file mode 100644 index 00000000000..9d69e2fd859 --- /dev/null +++ b/js/js.backend/testFiles/extensionFunction/cases/extensionInsideFunctionLiteral.kt @@ -0,0 +1,22 @@ +package foo + +class M() { + var m = 0 + + fun eval() { + var d = { + var c = {Int.() -> this + 3} + m += 3.c() + } + d(); + } +} + +fun box() : Boolean { + var a = M() + if (a.m != 0) return false; + a.eval() + if (a.m != 6) return false; + + return true; +} \ No newline at end of file diff --git a/js/js.backend/testFiles/extensionFunction/cases/extensionLiteralPassedToFunction.kt b/js/js.backend/testFiles/extensionFunction/cases/extensionLiteralPassedToFunction.kt new file mode 100644 index 00000000000..62bd16300a2 --- /dev/null +++ b/js/js.backend/testFiles/extensionFunction/cases/extensionLiteralPassedToFunction.kt @@ -0,0 +1,7 @@ +package foo + +fun apply(i : Int, f : Int.(Int) -> Int) = i.f(1); + +fun box() : Boolean { + return (apply(1, {i -> i + this})) == 2 +} \ No newline at end of file diff --git a/js/js.backend/testFiles/extensionFunction/cases/extensionUsedInsideClass.kt b/js/js.backend/testFiles/extensionFunction/cases/extensionUsedInsideClass.kt new file mode 100644 index 00000000000..7e345e3ad93 --- /dev/null +++ b/js/js.backend/testFiles/extensionFunction/cases/extensionUsedInsideClass.kt @@ -0,0 +1,15 @@ +package foo + +class A(var a : Int) { + fun eval() = f(); +} + +fun A.f() : Int { + a = 3 + return 10 +} + +fun box(): Boolean { + val a = A(4) + return (a.eval() == 10) && (a.a == 3) +} diff --git a/js/js.backend/testFiles/extensionFunction/cases/extensionWithImplicitReceiver.kt b/js/js.backend/testFiles/extensionFunction/cases/extensionWithImplicitReceiver.kt new file mode 100644 index 00000000000..ccab0e9f2f5 --- /dev/null +++ b/js/js.backend/testFiles/extensionFunction/cases/extensionWithImplicitReceiver.kt @@ -0,0 +1,13 @@ +package foo + +fun Int.same() : Int { + return this +} + +fun Int.quadruple() : Int { + return same() * 4; +} + +fun box(): Boolean { + return (3.quadruple() == 12) +} \ No newline at end of file diff --git a/js/js.backend/testFiles/extensionFunction/cases/generic.kt b/js/js.backend/testFiles/extensionFunction/cases/generic.kt new file mode 100644 index 00000000000..d13147d5d22 --- /dev/null +++ b/js/js.backend/testFiles/extensionFunction/cases/generic.kt @@ -0,0 +1,24 @@ +package foo + +import java.util.* + +fun ArrayList.findAll(predicate: (T) -> Boolean): ArrayList { + val result = ArrayList() + for(val t in this) { + if (predicate(t)) result.add(t) + } + return result +} + + +fun box(): String { + val list: ArrayList = ArrayList() + + list.add(2) + list.add(3) + list.add(5) + + + val m: ArrayList = list.findAll({(name: Int) -> name < 4}) + return if (m.size() == 2) "OK" else "fail" +} diff --git a/js/js.backend/testFiles/extensionFunction/cases/intExtension.kt b/js/js.backend/testFiles/extensionFunction/cases/intExtension.kt new file mode 100644 index 00000000000..884014c87aa --- /dev/null +++ b/js/js.backend/testFiles/extensionFunction/cases/intExtension.kt @@ -0,0 +1,9 @@ +package foo + +fun Int.quadruple() : Int { + return this * 4; +} + +fun box(): Boolean { + return (3.quadruple() == 12) +} diff --git a/js/js.backend/testFiles/extensionFunction/cases/virtualExtension.kt b/js/js.backend/testFiles/extensionFunction/cases/virtualExtension.kt new file mode 100644 index 00000000000..383c0191f5f --- /dev/null +++ b/js/js.backend/testFiles/extensionFunction/cases/virtualExtension.kt @@ -0,0 +1,15 @@ +package foo + +class A(var a : Int) { + + fun Int.modify() : Int { + return this * 3; + } + + fun eval() = a.modify(); +} + +fun box(): Boolean { + val a = A(4) + return (a.eval() == 12) +} diff --git a/js/js.backend/testFiles/extensionFunction/cases/virtualExtensionOverride.kt b/js/js.backend/testFiles/extensionFunction/cases/virtualExtensionOverride.kt new file mode 100644 index 00000000000..78646fb4b38 --- /dev/null +++ b/js/js.backend/testFiles/extensionFunction/cases/virtualExtensionOverride.kt @@ -0,0 +1,20 @@ +package foo + +open class A(var a : Int) { + + open fun Int.modify() : Int { + return this * 3; + } + + fun eval() = a.modify(); +} + +class B(a : Int) : A(a) { + override fun Int.modify() : Int { + return this - 2; + } +} + +fun box(): Boolean { + return (A(4).eval() == 12) && (A(2).eval() == 6) && (B(3).eval() == 1) +} diff --git a/js/js.backend/testFiles/extensionFunction/out/.gitignore b/js/js.backend/testFiles/extensionFunction/out/.gitignore new file mode 100644 index 00000000000..86d0cb2726c --- /dev/null +++ b/js/js.backend/testFiles/extensionFunction/out/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file diff --git a/js/js.backend/testFiles/extensionProperty/cases/absExtension.kt b/js/js.backend/testFiles/extensionProperty/cases/absExtension.kt new file mode 100644 index 00000000000..d0399758498 --- /dev/null +++ b/js/js.backend/testFiles/extensionProperty/cases/absExtension.kt @@ -0,0 +1,11 @@ +package foo; + +val Double.abs : Double + get() = if (this > 0) this else -this + +fun box() : Boolean { + if (4.0.abs != 4.0) return false; + if ((-5.2).abs != 5.2) return false; + + return true; +} diff --git a/js/js.backend/testFiles/extensionProperty/cases/propertyWithGetterAndSetter.kt b/js/js.backend/testFiles/extensionProperty/cases/propertyWithGetterAndSetter.kt new file mode 100644 index 00000000000..aba51251279 --- /dev/null +++ b/js/js.backend/testFiles/extensionProperty/cases/propertyWithGetterAndSetter.kt @@ -0,0 +1,23 @@ +package foo + +class Test() { + var a = 0 +} + +var Test.b : Int + get() = a * 3 + set(c : Int) { + a = c - 1 + } + +fun box() : Boolean { + val c = Test() + if (c.a != 0) return false; + if (c.b != 0) return false; + c.a = 3; + if (c.b != 9) return false; + c.b = 10; + if (c.a != 9) return false; + if (c.b != 27) return false; + return true; +} \ No newline at end of file diff --git a/js/js.backend/testFiles/extensionProperty/cases/simplePropertyWithGetter.kt b/js/js.backend/testFiles/extensionProperty/cases/simplePropertyWithGetter.kt new file mode 100644 index 00000000000..f073c5ce546 --- /dev/null +++ b/js/js.backend/testFiles/extensionProperty/cases/simplePropertyWithGetter.kt @@ -0,0 +1,17 @@ +package foo + +val String.size : Int + get() = length + +val Int.quadruple : Int + get() = this * 4 + +fun box() : Boolean +{ + if ("1".size != 1) return false; + if ("11".size != 2) return false; + if (("121" + "123").size != 6) return false; + if (1.quadruple != 4) return false; + if (0.quadruple != 0) return false; + return true; +} \ No newline at end of file diff --git a/js/js.backend/testFiles/extensionProperty/out/.gitignore b/js/js.backend/testFiles/extensionProperty/out/.gitignore new file mode 100644 index 00000000000..86d0cb2726c --- /dev/null +++ b/js/js.backend/testFiles/extensionProperty/out/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file diff --git a/js/js.backend/testFiles/inheritance/cases/baseClassDefinedAfterDerived.kt b/js/js.backend/testFiles/inheritance/cases/baseClassDefinedAfterDerived.kt new file mode 100644 index 00000000000..53a23fdc56e --- /dev/null +++ b/js/js.backend/testFiles/inheritance/cases/baseClassDefinedAfterDerived.kt @@ -0,0 +1,13 @@ +package foo + + +class A() : B() { + +} + +open class B() { + + val a = 3 +} + +fun box() = (A().a == 3) \ No newline at end of file diff --git a/js/js.backend/testFiles/inheritance/cases/complexInitializationOrder.kt b/js/js.backend/testFiles/inheritance/cases/complexInitializationOrder.kt new file mode 100644 index 00000000000..9004131ad03 --- /dev/null +++ b/js/js.backend/testFiles/inheritance/cases/complexInitializationOrder.kt @@ -0,0 +1,37 @@ +package foo + +open class A() { + + var order = "" + { + order = order + "A" + } +} + +open class B() : A() { + { + order = order + "B" + } +} + +class C() : B() { + { + order = order + "C" + } +} + +class D() : B() { + { + order = order + "D" + } +} + +class E() : A() { + { + order = order + "E" + } +} + +fun box() : Boolean { + return (C().order == "ABC") && (D().order == "ABD") && (E().order == "AE") +} \ No newline at end of file diff --git a/js/js.backend/testFiles/inheritance/cases/definitionOrder.kt b/js/js.backend/testFiles/inheritance/cases/definitionOrder.kt new file mode 100644 index 00000000000..12e3f7b045c --- /dev/null +++ b/js/js.backend/testFiles/inheritance/cases/definitionOrder.kt @@ -0,0 +1,37 @@ +package foo + +class C() : B() { + { + order = order + "C" + } +} + +class D() : B() { + { + order = order + "D" + } +} + +class E() : A() { + { + order = order + "E" + } +} + +open class B() : A() { + { + order = order + "B" + } +} + +open class A() { + + var order = "" + { + order = order + "A" + } +} + +fun box() : Boolean { + return (C().order == "ABC") && (D().order == "ABD") && (E().order == "AE") +} \ No newline at end of file diff --git a/js/js.backend/testFiles/inheritance/cases/initializationOrder.kt b/js/js.backend/testFiles/inheritance/cases/initializationOrder.kt new file mode 100644 index 00000000000..faf44b8ab6e --- /dev/null +++ b/js/js.backend/testFiles/inheritance/cases/initializationOrder.kt @@ -0,0 +1,26 @@ +package foo + +open class A() { + + var order = "" + { + order = order + "A" + } +} + +open class B() : A() { + { + order = order + "B" + } + +} + +class C() : B() { + { + order = order + "C" + } +} + +fun box() : Boolean { + return (C().order == "ABC") && (B().order == "AB") && (A().order == "A") +} \ No newline at end of file diff --git a/js/js.backend/testFiles/inheritance/cases/initializersOfBasicClassExecute.kt b/js/js.backend/testFiles/inheritance/cases/initializersOfBasicClassExecute.kt new file mode 100644 index 00000000000..52176e10fbe --- /dev/null +++ b/js/js.backend/testFiles/inheritance/cases/initializersOfBasicClassExecute.kt @@ -0,0 +1,13 @@ +package foo + +open class A() { + var a = 3; +} + +class B() : A() { + +} + +fun box() : Int { + return (B().a) +} diff --git a/js/js.backend/testFiles/inheritance/cases/methodOverride.kt b/js/js.backend/testFiles/inheritance/cases/methodOverride.kt new file mode 100644 index 00000000000..62feff89b9b --- /dev/null +++ b/js/js.backend/testFiles/inheritance/cases/methodOverride.kt @@ -0,0 +1,15 @@ +package foo + +open class C() { + open fun f(): Any = "C f" +} + +class D() : C() { + override fun f(): String = "D f" +} + +fun box(): Boolean { + val d : C = D() + if(d.f() != "D f") return false + return true +} \ No newline at end of file diff --git a/js/js.backend/testFiles/inheritance/cases/valuePassedToAncestorConstructor.kt b/js/js.backend/testFiles/inheritance/cases/valuePassedToAncestorConstructor.kt new file mode 100644 index 00000000000..97795d7210f --- /dev/null +++ b/js/js.backend/testFiles/inheritance/cases/valuePassedToAncestorConstructor.kt @@ -0,0 +1,12 @@ +package foo + +open class C(a : Int) { + val b = a +} + +class D(c : Int) : C(c + 2) { +} + +fun box(): Boolean { + return (D(0).b == 2) +} \ No newline at end of file diff --git a/js/js.backend/testFiles/inheritance/out/.gitignore b/js/js.backend/testFiles/inheritance/out/.gitignore new file mode 100644 index 00000000000..86d0cb2726c --- /dev/null +++ b/js/js.backend/testFiles/inheritance/out/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file diff --git a/js/js.backend/testFiles/java/arrayList/cases/access.kt b/js/js.backend/testFiles/java/arrayList/cases/access.kt new file mode 100644 index 00000000000..58c151430c2 --- /dev/null +++ b/js/js.backend/testFiles/java/arrayList/cases/access.kt @@ -0,0 +1,10 @@ +package foo + + import java.util.ArrayList; + + fun box() : Boolean { + val a = ArrayList(); + a.add(1) + a.add(2) + return (a.size() == 2) && (a.get(1) == 2) && (a.get(0) == 1); + } \ No newline at end of file diff --git a/js/js.backend/testFiles/java/arrayList/cases/arrayAccess.kt b/js/js.backend/testFiles/java/arrayList/cases/arrayAccess.kt new file mode 100644 index 00000000000..b47cfa6e040 --- /dev/null +++ b/js/js.backend/testFiles/java/arrayList/cases/arrayAccess.kt @@ -0,0 +1,11 @@ +package foo + +import java.util.ArrayList; + +fun box() : Boolean { + val a = ArrayList(); + a.add(1) + a.add(2) + a[1] = 100 + return (a.size() == 2) && (a[1] == 100) && (a[0] == 1); +} \ No newline at end of file diff --git a/js/js.backend/testFiles/java/arrayList/cases/emptyList.kt b/js/js.backend/testFiles/java/arrayList/cases/emptyList.kt new file mode 100644 index 00000000000..ec624c39ad9 --- /dev/null +++ b/js/js.backend/testFiles/java/arrayList/cases/emptyList.kt @@ -0,0 +1,8 @@ +package foo + +import java.util.ArrayList; + +fun box() : Boolean { + val a = ArrayList(); + return a.size() == 0; +} \ No newline at end of file diff --git a/js/js.backend/testFiles/java/arrayList/cases/indexOOB.kt b/js/js.backend/testFiles/java/arrayList/cases/indexOOB.kt new file mode 100644 index 00000000000..52d1f6936a7 --- /dev/null +++ b/js/js.backend/testFiles/java/arrayList/cases/indexOOB.kt @@ -0,0 +1,12 @@ +package foo + +import java.util.ArrayList; + +fun box() : Boolean { + val arr = ArrayList(); + var i = 0; + while (i++ < 10) { + arr.add(i); + } + return (arr[10] == 11) +} \ No newline at end of file diff --git a/js/js.backend/testFiles/java/arrayList/cases/isEmpty.kt b/js/js.backend/testFiles/java/arrayList/cases/isEmpty.kt new file mode 100644 index 00000000000..7f9c7283f87 --- /dev/null +++ b/js/js.backend/testFiles/java/arrayList/cases/isEmpty.kt @@ -0,0 +1,9 @@ +package foo + +import java.util.ArrayList; + +fun box() : Boolean { + val a = ArrayList(); + a.add(3) + return !(a.isEmpty()) && (ArrayList().isEmpty()); +} \ No newline at end of file diff --git a/js/js.backend/testFiles/java/arrayList/cases/iterate.kt b/js/js.backend/testFiles/java/arrayList/cases/iterate.kt new file mode 100644 index 00000000000..74da14b2f0a --- /dev/null +++ b/js/js.backend/testFiles/java/arrayList/cases/iterate.kt @@ -0,0 +1,16 @@ +package foo + +import java.util.ArrayList; + +fun box() : Boolean { + var i = 0 + val arr = ArrayList(); + while (i++ < 10) { + arr.add(i); + } + var sum = 0 + for (a in arr) { + sum += a; + } + return (sum == 55) && (arr.size() == 10) +} \ No newline at end of file diff --git a/js/js.backend/testFiles/java/arrayList/cases/max.kt b/js/js.backend/testFiles/java/arrayList/cases/max.kt new file mode 100644 index 00000000000..50098e82059 --- /dev/null +++ b/js/js.backend/testFiles/java/arrayList/cases/max.kt @@ -0,0 +1,8 @@ +package foo + +import java.util.Collection; +import java.util.Comparator. + +fun max(col :Collection, ) { + +} \ No newline at end of file diff --git a/js/js.backend/testFiles/java/arrayList/cases/remove.kt b/js/js.backend/testFiles/java/arrayList/cases/remove.kt new file mode 100644 index 00000000000..20095823895 --- /dev/null +++ b/js/js.backend/testFiles/java/arrayList/cases/remove.kt @@ -0,0 +1,17 @@ +package foo + +import java.util.ArrayList; + +fun box() : Boolean { + var i = 0 + val arr = ArrayList(); + while (i++ < 10) { + arr.add(i); + } + arr.remove(2) + var sum = 0 + for (a in arr) { + sum += a; + } + return ((sum == 52) && (arr[1] == 2) && (arr[2] == 4) && (arr[3] == 5) && (arr[4] == 6) && (arr[8] == 10)) +} \ No newline at end of file diff --git a/js/js.backend/testFiles/java/arrayList/out/.gitignore b/js/js.backend/testFiles/java/arrayList/out/.gitignore new file mode 100644 index 00000000000..86d0cb2726c --- /dev/null +++ b/js/js.backend/testFiles/java/arrayList/out/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file diff --git a/js/js.backend/testFiles/kotlinLib/cases/array.js b/js/js.backend/testFiles/kotlinLib/cases/array.js new file mode 100644 index 00000000000..0d2083c480c --- /dev/null +++ b/js/js.backend/testFiles/kotlinLib/cases/array.js @@ -0,0 +1,22 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function test() { + var a = Kotlin.arrayFromFun(3, function () { + return 3 + }); + return (a[0] == 3) && (a[2] == 3) && (a[1] == 3); +} \ No newline at end of file diff --git a/js/js.backend/testFiles/kotlinLib/cases/commaExpression.js b/js/js.backend/testFiles/kotlinLib/cases/commaExpression.js new file mode 100644 index 00000000000..f3990bf315d --- /dev/null +++ b/js/js.backend/testFiles/kotlinLib/cases/commaExpression.js @@ -0,0 +1,22 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +a = 0 +f = (t = a, t1 = t, a = ++t1, t) + +test = function () { + return (f == 0) && (a == 1) +} \ No newline at end of file diff --git a/js/js.backend/testFiles/kotlinLib/cases/hashMap.js b/js/js.backend/testFiles/kotlinLib/cases/hashMap.js new file mode 100644 index 00000000000..5b195c7600d --- /dev/null +++ b/js/js.backend/testFiles/kotlinLib/cases/hashMap.js @@ -0,0 +1,41 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var map = new Kotlin.HashMap() + +map.put(3, 4) +map.put(6, 3) + +function test() { + if (map.containsKey(4) || map.containsKey(5)) return false; + if (map.containsKey({}) || map.containsKey(11)) return false; + if (!map.containsKey(3) || !map.containsKey(6)) return false; + var obj = map.get(3); + if (obj !== 4) return false; + obj = map.get(6); + if (obj !== 3) return false; + if (map.size() !== 2) return false; + map.put(2, 3); + if (map.size() !== 3) return false; + if (!map.containsKey(2) || !map.containsKey(3) || !map.containsKey(6)) return false; + if (!map.containsValue(4) || !map.containsValue(3)) return false; + map.put(2, 1000); + if (map.size() !== 3) return false; + if (map.get(2)!== 1000) return false; + + + return true; +} \ No newline at end of file diff --git a/js/js.backend/testFiles/kotlinLib/cases/isAncestorType.js b/js/js.backend/testFiles/kotlinLib/cases/isAncestorType.js new file mode 100644 index 00000000000..255b5891326 --- /dev/null +++ b/js/js.backend/testFiles/kotlinLib/cases/isAncestorType.js @@ -0,0 +1,23 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var A = Kotlin.Class.create(); +var B = Kotlin.Class.create(A); +var b = new B; + +test = function() { + return (Kotlin.isType(b, A) && Kotlin.isType(b, B)); +} \ No newline at end of file diff --git a/js/js.backend/testFiles/kotlinLib/cases/isComplexTest.js b/js/js.backend/testFiles/kotlinLib/cases/isComplexTest.js new file mode 100644 index 00000000000..92a4a2f29ff --- /dev/null +++ b/js/js.backend/testFiles/kotlinLib/cases/isComplexTest.js @@ -0,0 +1,40 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var A = Kotlin.Class.create(); +var B = Kotlin.Class.create(A); +var b = new B; +var C = Kotlin.Class.create(B); +var c = new C; +var E = Kotlin.Class.create(A) +var e = new E; + +test1 = function() { + b2 = b + return (Kotlin.isType(b, A) && Kotlin.isType(b, B)); +} + +test2 = function() { + return (Kotlin.isType(c, C) && Kotlin.isType(c, B) && Kotlin.isType(c, A) && (!Kotlin.isType(c, E))); +} + +test3 = function() { + return Kotlin.isType(e, E) && !Kotlin.isType(e, B) && !Kotlin.isType(e, C) && Kotlin.isType(e, A) +} + +test = function() { + return test1() && test2() && test3() +} \ No newline at end of file diff --git a/js/js.backend/testFiles/kotlinLib/cases/isNotOtherType.js b/js/js.backend/testFiles/kotlinLib/cases/isNotOtherType.js new file mode 100644 index 00000000000..b33ad3afab2 --- /dev/null +++ b/js/js.backend/testFiles/kotlinLib/cases/isNotOtherType.js @@ -0,0 +1,24 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var A = Kotlin.Class.create(); +var B = Kotlin.Class.create(A); +var C = Kotlin.Class.create(); +var c = new C; + +test = function() { + return ((!isType(c, A)) && !isType(c, B)); +} \ No newline at end of file diff --git a/js/js.backend/testFiles/kotlinLib/cases/isSameType.js b/js/js.backend/testFiles/kotlinLib/cases/isSameType.js new file mode 100644 index 00000000000..cb333db25c5 --- /dev/null +++ b/js/js.backend/testFiles/kotlinLib/cases/isSameType.js @@ -0,0 +1,22 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var A = Kotlin.Class.create(); +var a = new A; + +test = function() { + return Kotlin.isType(a, A); +} \ No newline at end of file diff --git a/js/js.backend/testFiles/kotlinLib/cases/namespace.js b/js/js.backend/testFiles/kotlinLib/cases/namespace.js new file mode 100644 index 00000000000..eeb677033eb --- /dev/null +++ b/js/js.backend/testFiles/kotlinLib/cases/namespace.js @@ -0,0 +1,26 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +foo = Kotlin.Namespace.create({initialize:function(){ +} +, box:function(){ + return !false; +} +}); + +function test() { + return foo.box() +} diff --git a/js/js.backend/testFiles/kotlinLib/cases/namespaceWithClasses.js b/js/js.backend/testFiles/kotlinLib/cases/namespaceWithClasses.js new file mode 100644 index 00000000000..0571b39e660 --- /dev/null +++ b/js/js.backend/testFiles/kotlinLib/cases/namespaceWithClasses.js @@ -0,0 +1,59 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +{ + classes = function(){ + var A = Kotlin.Class.create({initialize:function(){ + this.$order = ''; + { + this.set_order(this.get_order() + 'A'); + } + } + , set_order:function(tmp$0){ + this.$order = tmp$0; + } + , get_order:function(){ + return this.$order; + } + }); + var B = Kotlin.Class.create(A, {initialize:function(){ + this.super_init(); + { + this.set_order(this.get_order() + 'B'); + } + } + }); + var C = Kotlin.Class.create(B, {initialize:function(){ + this.super_init(); + { + this.set_order(this.get_order() + 'C'); + } + } + }); + return {A:A, B:B, C:C}; + } + (); + foo = Kotlin.Namespace.create(classes, {initialize:function(){ + } + , box:function(){ + return (new foo.C).get_order() === 'ABC' && (new foo.B).get_order() === 'AB' && (new foo.A).get_order() === 'A'; + } + }); +} + +function test() { + return foo.box() +} diff --git a/js/js.backend/testFiles/kotlinLib/cases/trait.js b/js/js.backend/testFiles/kotlinLib/cases/trait.js new file mode 100644 index 00000000000..628dbbd52f2 --- /dev/null +++ b/js/js.backend/testFiles/kotlinLib/cases/trait.js @@ -0,0 +1,28 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +foo = {}; +(function(foo){ + foo.Test = Kotlin.Trait.create({addFoo:function(s){ + return s + 'FOO'; + } + }); + foo.ExtendedTest = Kotlin.Trait.create(foo.Test, {hooray:function(){ + return 'hooray'; + } + }); +} +(foo)); diff --git a/js/js.backend/testFiles/kotlin_lib.js b/js/js.backend/testFiles/kotlin_lib.js new file mode 100644 index 00000000000..2d155d5a78c --- /dev/null +++ b/js/js.backend/testFiles/kotlin_lib.js @@ -0,0 +1,1079 @@ +/** + * Copyright 2010 Tim Down. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Prototype JavaScript framework, version 1.6.1 + * (c) 2005-2009 Sam Stephenson + * + * Prototype is freely distributable under the terms of an MIT-style license. + * For details, see the Prototype web site: http://www.prototypejs.org/ + * + *--------------------------------------------------------------------------*/ +(function () { + + function $A(iterable) { + if (!iterable) return []; + if ('toArray' in Object(iterable)) return iterable.toArray(); + var length = iterable.length || 0, results = new Array(length); + while (length--) results[length] = iterable[length]; + return results; + } + + (function () { + + + function extend(destination, source) { + for (var property in source) + destination[property] = source[property]; + return destination; + } + + + function keys(object) { + if (Type(object) !== OBJECT_TYPE) { + throw new TypeError(); + } + var results = []; + for (var property in object) { + if (object.hasOwnProperty(property)) { + results.push(property); + } + } + return results; + } + + function values(object) { + var results = []; + for (var property in object) + results.push(object[property]); + return results; + } + + extend(Object, { + extend:extend, + keys:Object.keys || keys, + values:values + }); + })(); + + + Object.extend(Function.prototype, (function () { + var slice = Array.prototype.slice; + + function update(array, args) { + var arrayLength = array.length, length = args.length; + while (length--) array[arrayLength + length] = args[length]; + return array; + } + + function merge(array, args) { + array = slice.call(array, 0); + return update(array, args); + } + + function argumentNames() { + var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1] + .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '') + .replace(/\s+/g, '').split(','); + return names.length == 1 && !names[0] ? [] : names; + } + + function bind(context) { + if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this; + var __method = this, args = slice.call(arguments, 1); + return function () { + var a = merge(args, arguments); + return __method.apply(context, a); + } + } + + function bindAsEventListener(context) { + var __method = this, args = slice.call(arguments, 1); + return function (event) { + var a = update([event || window.event], args); + return __method.apply(context, a); + } + } + + function wrap(wrapper) { + var __method = this; + return function () { + var a = update([__method.bind(this)], arguments); + return wrapper.apply(this, a); + } + } + + return { + argumentNames:argumentNames, + bind:bind, + bindAsEventListener:bindAsEventListener, + wrap:wrap + } + })()); + + var isType = function (object, klass) { + current = object.get_class(); + while (current !== klass) { + if (current === null) { + return false; + } + current = current.superclass; + } + return true; + }; + + var emptyFunction = function () { + }; + + var Class = (function () { + + function subclass() { + } + ; + function create() { + var parent = null, properties = $A(arguments); + if (typeof (properties[0]) == "function") { + parent = properties.shift(); + } + + function klass() { + this.initializing = klass; + this.initialize.apply(this, arguments); + } + + Object.extend(klass, Class.Methods); + klass.superclass = parent; + klass.subclasses = []; + + if (parent) { + subclass.prototype = parent.prototype; + klass.prototype = new subclass; + parent.subclasses.push(klass); + } + + klass.addMethods( + { + get_class:function () { + return klass; + } + }); + + if (parent != null) { + klass.addMethods( + { + super_init:function () { + this.initializing = this.initializing.superclass; + this.initializing.prototype.initialize.apply(this, arguments) + } + }); + } + + for (var i = 0, length = properties.length; i < length; i++) + klass.addMethods(properties[i]); + + if (!klass.prototype.initialize) { + klass.prototype.initialize = emptyFunction; + } + + klass.prototype.constructor = klass; + return klass; + } + + function addMethods(source) { + var ancestor = this.superclass && this.superclass.prototype, + properties = Object.keys(source); + + + for (var i = 0, length = properties.length; i < length; i++) { + var property = properties[i], value = source[property]; + if (ancestor && (typeof (value) == "function") && + value.argumentNames()[0] == "$super") { + var method = value; + value = (function (m) { + return function () { + return ancestor[m].apply(this, arguments); + }; + })(property).wrap(method); + + } + this.prototype[property] = value; + } + + return this; + } + + return { + create:create, + Methods:{ + addMethods:addMethods + } + }; + })(); + + var Trait = (function () { + + function add(object, source) { + var properties = Object.keys(source); + for (var i = 0, length = properties.length; i < length; i++) { + var property = properties[i]; + var value = source[property]; + object[property] = value; + } + return this; + } + + function create() { + + result = {} + for (var i = 0, length = arguments.length; i < length; i++) { + add(result, arguments[i]); + } + return result; + } + + return { + create:create + }; + })(); + + + var Namespace = (function () { + + function create() { + return Trait.create.apply(Trait, arguments); + } + + return { + create:create + }; + })(); + + var object = (function () { + function create() { + var singletonClass = Class.create.apply(Class, arguments); + return new singletonClass; + } + return { + create:create + }; + })(); + + Kotlin = {}; + Kotlin.Class = Class; + Kotlin.Namespace = Namespace; + Kotlin.Trait = Trait; + Kotlin.isType = isType; + Kotlin.object = object; + + Kotlin.equals = function (obj1, obj2) { + if (typeof obj1 == "object") { + if (obj1.equals != undefined) { + return obj1.equals(obj2); + } + } + return (obj1 === obj2); + }; + + Kotlin.Exceptions = {} + Kotlin.Exception = Kotlin.Class.create(); + Kotlin.Exceptions.IndexOutOfBounds = {} + + + Kotlin.ArrayList = Class.create({ + initialize:function () { + this.array = []; + this.$size = 0; + }, + get:function (index) { + if ((index < 0) || (index >= this.$size)) { + throw Kotlin.Exceptions.IndexOutOfBounds; + } + return (this.array)[index]; + }, + set:function (index, value) { + if ((index < 0) || (index >= this.$size)) { + throw Kotlin.Exceptions.IndexOutOfBounds; + } + (this.array)[index] = value; + }, + size:function () { + return this.$size; + }, + iterator:function () { + return new Kotlin.ArrayIterator(this); + }, + isEmpty:function () { + return (this.$size == 0); + }, + add:function (element) { + this.array[this.$size++] = element; + }, + addAll:function (collection) { + var it = collection.iterator(); + while (it.hasNext()) { + this.add(it.next()); + } + }, + remove:function (index) { + for (var i = index; i < this.$size - 1; ++i) { + this.array[i] = this.array[i + 1]; + } + this.$size--; + }, + clear:function () { + this.array = []; + this.$size = 0; + }, + contains:function (obj) { + for (var i = 0; i < this.$size; ++i) { + if (Kotlin.equals(this.array[i], obj)) { + return true; + } + } + return false; + } + }); + + + Kotlin.parseInt = + function (str) { + return parseInt(str); + } + ; + + Kotlin.System = function () { + var output = ""; + + var print = function (obj) { + if (obj !== undefined) { + if (obj == null || typeof obj != "object") { + output += obj; + } + else { + output += obj.toString(); + } + } + }; + var println = function (obj) { + this.print(obj); + output += "\n"; + }; + + return { + out:function () { + return { + print:print, + println:println + }; + }, + output:function () { + return output; + }, + flush:function () { + output = ""; + } + }; + }(); + + Kotlin.println = function (s) { + Kotlin.System.out().println(s); + } + + Kotlin.print = function (s) { + Kotlin.System.out().print(s); + } + + Kotlin.AbstractFunctionInvokationError = Class.create(); + + Kotlin.Iterator = Class.create({ + initialize:function () { + }, + next:function () { + throw new Kotlin.AbstractFunctionInvokationError(); + }, + hasNext:function () { + throw new Kotlin.AbstractFunctionInvokationError(); + } + }); + + Kotlin.ArrayIterator = Class.create(Kotlin.Iterator, { + initialize:function (array) { + this.array = array; + this.index = 0; + }, + next:function () { + return this.array.get(this.index++); + }, + hasNext:function () { + return (this.array.size() > this.index); + }, + get_hasNext:function () { + return this.hasNext(); + } + }); + + Kotlin.RangeIterator = Kotlin.Class.create(Kotlin.Iterator, { + initialize:function (start, count, reversed) { + this.$start = start; + this.$count = count; + this.$reversed = reversed; + this.$i = this.get_start(); + }, get_start:function () { + return this.$start; + }, get_count:function () { + return this.$count; + }, set_count:function (tmp$0) { + this.$count = tmp$0; + }, get_reversed:function () { + return this.$reversed; + }, get_i:function () { + return this.$i; + }, set_i:function (tmp$0) { + this.$i = tmp$0; + }, next:function () { + this.set_count(this.get_count() - 1); + if (this.get_reversed()) { + this.set_i(this.get_i() - 1); + return this.get_i() + 1; + } + else { + this.set_i(this.get_i() + 1); + return this.get_i() - 1; + } + }, hasNext:function () { + return this.get_count() > 0; + }, get_hasNext:function () { + return this.hasNext(); + } + }); + + Kotlin.NumberRange = Kotlin.Class.create({initialize:function (start, size, reversed) { + this.$start = start; + this.$size = size; + this.$reversed = reversed; + }, get_start:function () { + return this.$start; + }, get_size:function () { + return this.$size; + }, get_reversed:function () { + return this.$reversed; + }, get_end:function () { + return this.get_reversed() ? this.get_start() - this.get_size() + 1 : this.get_start() + this.get_size() - 1; + }, contains:function (number) { + if (this.get_reversed()) { + return number <= this.get_start() && number > this.get_start() - this.get_size(); + } + else { + return number >= this.get_start() && number < this.get_start() + this.get_size(); + } + }, iterator:function () { + return new Kotlin.RangeIterator(this.get_start(), this.get_size(), this.get_reversed()); + } + }); + + Kotlin.Comparator = Kotlin.Class.create( + { + initialize:function () { + }, + compare:function (el1, el2) { + throw new Kotlin.AbstractFunctionInvokationError(); + } + } + ); + + Kotlin.comparator = function(f) { + var result = new Kotlin.Comparator; + result.compare = function(el1, el2) { + return f(el1, el2); + }; + return result; + }; + + Kotlin.collectionsMax = function (col, comp) { + var it = col.iterator(); + if (col.isEmpty()) { + //TODO: which exception? + throw Kotlin.Exception(); + } + var max = it.next(); + while (it.hasNext()) { + var el = it.next(); + if (comp.compare(max, el) < 0) { + max = el; + } + } + return max; + }; + + Kotlin.StringBuilder = Kotlin.Class.create( + { + initialize:function () { + this.string = ""; + }, + append:function (obj) { + this.string = this.string + obj.toString(); + }, + toString:function () { + return this.string; + } + } + ); + + Kotlin.splitString = function(str, regex) { + return str.split(regex); + }; + + Kotlin.nullArray = function (size) { + var res = []; + var i = size; + while (i > 0) { + res[--i] = null; + } + return res; + }; + + Kotlin.arrayFromFun = function (size, initFun) { + var res = []; + var i = size; + while (i > 0) { + res[--i] = initFun(i); + } + return res; + }; + + Kotlin.arrayIndices = function (arr) { + return new Kotlin.NumberRange(0, arr.length); + }; + + var intrinsicArrayIterator = Kotlin.Class.create( + Kotlin.Iterator, + { + initialize:function (arr) { + this.arr = arr; + this.len = arr.length; + this.i = 0; + }, + hasNext:function () { + return (this.i < this.len); + }, + next:function () { + return this.arr[this.i++]; + }, + get_hasNext:function () { + return this.hasNext() + } + } + ); + + Kotlin.arrayIterator = function (arr) { + return new intrinsicArrayIterator(arr); + }; + + Kotlin.toString = function (obj) { + return obj.toString(); + }; + + Kotlin.jsonFromTuples = function (pairArr) { + var i = pairArr.length; + var res = {}; + while (i > 0) { + --i; + res[pairArr[i][0]] = pairArr[i][1]; + } + return res; + }; + + Kotlin.jsonSet = function (obj, attrName, value) { + obj[attrName] = value; + }; + + Kotlin.jsonGet = function (obj, attrName) { + return obj[attrName]; + }; + + + Kotlin.sure = function(obj) { + return obj; + }; + + (function () { + var FUNCTION = "function"; + + var arrayRemoveAt = (typeof Array.prototype.splice == FUNCTION) ? + function (arr, idx) { + arr.splice(idx, 1); + } : + + function (arr, idx) { + var itemsAfterDeleted, i, len; + if (idx === arr.length - 1) { + arr.length = idx; + } + else { + itemsAfterDeleted = arr.slice(idx + 1); + arr.length = idx; + for (i = 0, len = itemsAfterDeleted.length; i < len; ++i) { + arr[idx + i] = itemsAfterDeleted[i]; + } + } + }; + + function hashObject(obj) { + var hashCode; + if (typeof obj == "string") { + return obj; + } else if (typeof obj.hashCode == FUNCTION) { + // Check the hashCode method really has returned a string + hashCode = obj.hashCode(); + return (typeof hashCode == "string") ? hashCode : hashObject(hashCode); + } else if (typeof obj.toString == FUNCTION) { + return obj.toString(); + } + else { + try { + return String(obj); + } catch (ex) { + // For host objects (such as ActiveObjects in IE) that have no toString() method and throw an error when + // passed to String() + return Object.prototype.toString.call(obj); + } + } + } + + function equals_fixedValueHasEquals(fixedValue, variableValue) { + return fixedValue.equals(variableValue); + } + + function equals_fixedValueNoEquals(fixedValue, variableValue) { + return (typeof variableValue.equals == FUNCTION) ? + variableValue.equals(fixedValue) : (fixedValue === variableValue); + } + + function createKeyValCheck(kvStr) { + return function (kv) { + if (kv === null) { + throw new Error("null is not a valid " + kvStr); + } else if (typeof kv == "undefined") { + throw new Error(kvStr + " must not be undefined"); + } + }; + } + + var checkKey = createKeyValCheck("key"), checkValue = createKeyValCheck("value"); + + /*----------------------------------------------------------------------------------------------------------------*/ + + function Bucket(hash, firstKey, firstValue, equalityFunction) { + this[0] = hash; + this.entries = []; + this.addEntry(firstKey, firstValue); + + if (equalityFunction !== null) { + this.getEqualityFunction = function () { + return equalityFunction; + }; + } + } + + var EXISTENCE = 0, ENTRY = 1, ENTRY_INDEX_AND_VALUE = 2; + + function createBucketSearcher(mode) { + return function (key) { + var i = this.entries.length, entry, equals = this.getEqualityFunction(key); + while (i--) { + entry = this.entries[i]; + if (equals(key, entry[0])) { + switch (mode) { + case EXISTENCE: + return true; + case ENTRY: + return entry; + case ENTRY_INDEX_AND_VALUE: + return [ i, entry[1] ]; + } + } + } + return false; + }; + } + + function createBucketLister(entryProperty) { + return function (aggregatedArr) { + var startIndex = aggregatedArr.length; + for (var i = 0, len = this.entries.length; i < len; ++i) { + aggregatedArr[startIndex + i] = this.entries[i][entryProperty]; + } + }; + } + + Bucket.prototype = { + getEqualityFunction:function (searchValue) { + return (typeof searchValue.equals == FUNCTION) ? equals_fixedValueHasEquals : equals_fixedValueNoEquals; + }, + + getEntryForKey:createBucketSearcher(ENTRY), + + getEntryAndIndexForKey:createBucketSearcher(ENTRY_INDEX_AND_VALUE), + + removeEntryForKey:function (key) { + var result = this.getEntryAndIndexForKey(key); + if (result) { + arrayRemoveAt(this.entries, result[0]); + return result[1]; + } + return null; + }, + + addEntry:function (key, value) { + this.entries[this.entries.length] = [key, value]; + }, + + keys:createBucketLister(0), + + values:createBucketLister(1), + + getEntries:function (entries) { + var startIndex = entries.length; + for (var i = 0, len = this.entries.length; i < len; ++i) { + // Clone the entry stored in the bucket before adding to array + entries[startIndex + i] = this.entries[i].slice(0); + } + }, + + containsKey:createBucketSearcher(EXISTENCE), + + containsValue:function (value) { + var i = this.entries.length; + while (i--) { + if (value === this.entries[i][1]) { + return true; + } + } + return false; + } + }; + + /*----------------------------------------------------------------------------------------------------------------*/ + + // Supporting functions for searching hashtable buckets + + function searchBuckets(buckets, hash) { + var i = buckets.length, bucket; + while (i--) { + bucket = buckets[i]; + if (hash === bucket[0]) { + return i; + } + } + return null; + } + + function getBucketForHash(bucketsByHash, hash) { + var bucket = bucketsByHash[hash]; + + // Check that this is a genuine bucket and not something inherited from the bucketsByHash's prototype + return ( bucket && (bucket instanceof Bucket) ) ? bucket : null; + } + + /*----------------------------------------------------------------------------------------------------------------*/ + + var Hashtable = function (hashingFunctionParam, equalityFunctionParam) { + var that = this; + var buckets = []; + var bucketsByHash = {}; + + var hashingFunction = (typeof hashingFunctionParam == FUNCTION) ? hashingFunctionParam : hashObject; + var equalityFunction = (typeof equalityFunctionParam == FUNCTION) ? equalityFunctionParam : null; + + this.put = function (key, value) { + checkKey(key); + checkValue(value); + var hash = hashingFunction(key), bucket, bucketEntry, oldValue = null; + + // Check if a bucket exists for the bucket key + bucket = getBucketForHash(bucketsByHash, hash); + if (bucket) { + // Check this bucket to see if it already contains this key + bucketEntry = bucket.getEntryForKey(key); + if (bucketEntry) { + // This bucket entry is the current mapping of key to value, so replace old value and we're done. + oldValue = bucketEntry[1]; + bucketEntry[1] = value; + } + else { + // The bucket does not contain an entry for this key, so add one + bucket.addEntry(key, value); + } + } + else { + // No bucket exists for the key, so create one and put our key/value mapping in + bucket = new Bucket(hash, key, value, equalityFunction); + buckets[buckets.length] = bucket; + bucketsByHash[hash] = bucket; + } + return oldValue; + }; + + this.get = function (key) { + checkKey(key); + + var hash = hashingFunction(key); + + // Check if a bucket exists for the bucket key + var bucket = getBucketForHash(bucketsByHash, hash); + if (bucket) { + // Check this bucket to see if it contains this key + var bucketEntry = bucket.getEntryForKey(key); + if (bucketEntry) { + // This bucket entry is the current mapping of key to value, so return the value. + return bucketEntry[1]; + } + } + return null; + }; + + this.containsKey = function (key) { + checkKey(key); + var bucketKey = hashingFunction(key); + + // Check if a bucket exists for the bucket key + var bucket = getBucketForHash(bucketsByHash, bucketKey); + + return bucket ? bucket.containsKey(key) : false; + }; + + this.containsValue = function (value) { + checkValue(value); + var i = buckets.length; + while (i--) { + if (buckets[i].containsValue(value)) { + return true; + } + } + return false; + }; + + this.clear = function () { + buckets.length = 0; + bucketsByHash = {}; + }; + + this.isEmpty = function () { + return !buckets.length; + }; + + var createBucketAggregator = function (bucketFuncName) { + return function () { + var aggregated = [], i = buckets.length; + while (i--) { + buckets[i][bucketFuncName](aggregated); + } + return aggregated; + }; + }; + + this.keys = createBucketAggregator("keys"); + this.values = createBucketAggregator("values"); + this.entries = createBucketAggregator("getEntries"); + + this.remove = function (key) { + checkKey(key); + + var hash = hashingFunction(key), bucketIndex, oldValue = null; + + // Check if a bucket exists for the bucket key + var bucket = getBucketForHash(bucketsByHash, hash); + + if (bucket) { + // Remove entry from this bucket for this key + oldValue = bucket.removeEntryForKey(key); + if (oldValue !== null) { + // Entry was removed, so check if bucket is empty + if (!bucket.entries.length) { + // Bucket is empty, so remove it from the bucket collections + bucketIndex = searchBuckets(buckets, hash); + arrayRemoveAt(buckets, bucketIndex); + delete bucketsByHash[hash]; + } + } + } + return oldValue; + }; + + this.size = function () { + var total = 0, i = buckets.length; + while (i--) { + total += buckets[i].entries.length; + } + return total; + }; + + this.each = function (callback) { + var entries = that.entries(), i = entries.length, entry; + while (i--) { + entry = entries[i]; + callback(entry[0], entry[1]); + } + }; + + + this.putAll = function (hashtable, conflictCallback) { + var entries = hashtable.entries(); + var entry, key, value, thisValue, i = entries.length; + var hasConflictCallback = (typeof conflictCallback == FUNCTION); + while (i--) { + entry = entries[i]; + key = entry[0]; + value = entry[1]; + + // Check for a conflict. The default behaviour is to overwrite the value for an existing key + if (hasConflictCallback && (thisValue = that.get(key))) { + value = conflictCallback(key, thisValue, value); + } + that.put(key, value); + } + }; + + this.clone = function () { + var clone = new Hashtable(hashingFunctionParam, equalityFunctionParam); + clone.putAll(that); + return clone; + }; + + this.keySet = function () { + var res = new Kotlin.HashSet(); + var keys = this.keys(); + var i = keys.length; + while (i--) { + res.add(keys[i]); + } + return res; + } + }; + + Kotlin.HashTable = Hashtable; + })(); + + Kotlin.HashMap = Kotlin.Class.create( + { + initialize:function () { + Kotlin.HashTable.call(this); + } + } + ); + + + + + (function () { + function HashSet(hashingFunction, equalityFunction) { + var hashTable = new Kotlin.HashTable(hashingFunction, equalityFunction); + + this.add = function (o) { + hashTable.put(o, true); + }; + + this.addAll = function (arr) { + var i = arr.length; + while (i--) { + hashTable.put(arr[i], true); + } + }; + + this.values = function () { + return hashTable.keys(); + }; + + this.iterator = function () { + var list = new Kotlin.ArrayList(); + var values = this.values(); + var i = values.length; + while (i--) { + list.add(values[i]); + } + return list.iterator(); + }; + + this.remove = function (o) { + return hashTable.remove(o) ? o : null; + }; + + this.contains = function (o) { + return hashTable.containsKey(o); + }; + + this.clear = function () { + hashTable.clear(); + }; + + this.size = function () { + return hashTable.size(); + }; + + this.isEmpty = function () { + return hashTable.isEmpty(); + }; + + this.clone = function () { + var h = new HashSet(hashingFunction, equalityFunction); + h.addAll(hashTable.keys()); + return h; + }; + + this.intersection = function (hashSet) { + var intersection = new HashSet(hashingFunction, equalityFunction); + var values = hashSet.values(), i = values.length, val; + while (i--) { + val = values[i]; + if (hashTable.containsKey(val)) { + intersection.add(val); + } + } + return intersection; + }; + + this.union = function (hashSet) { + var union = this.clone(); + var values = hashSet.values(), i = values.length, val; + while (i--) { + val = values[i]; + if (!hashTable.containsKey(val)) { + union.add(val); + } + } + return union; + }; + + this.isSubsetOf = function (hashSet) { + var values = hashTable.keys(), i = values.length; + while (i--) { + if (!hashSet.contains(values[i])) { + return false; + } + } + return true; + }; + } + + Kotlin.HashSet = Kotlin.Class.create( + { + initialize:function () { + HashSet.call(this); + } + } + ) + }()); + +})(); diff --git a/js/js.backend/testFiles/libImplementations/jQuery.js b/js/js.backend/testFiles/libImplementations/jQuery.js new file mode 100644 index 00000000000..3c9ea9e5649 --- /dev/null +++ b/js/js.backend/testFiles/libImplementations/jQuery.js @@ -0,0 +1,9300 @@ +/*! + * jQuery JavaScript Library v1.7 + * http://jquery.com/ + * + * Copyright 2011, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2011, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Thu Nov 3 16:18:21 2011 -0400 + */ +(function( window, undefined ) { + +// Use the correct document accordingly with window argument (sandbox) +var document = window.document, + navigator = window.navigator, + location = window.location; +var jQuery = (function() { + +// Define a local copy of jQuery +var jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context, rootjQuery ); + }, + + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + + // Map over the $ in case of overwrite + _$ = window.$, + + // A central reference to the root jQuery(document) + rootjQuery, + + // A simple way to check for HTML strings or ID strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + quickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/, + + // Check if a string has a non-whitespace character in it + rnotwhite = /\S/, + + // Used for trimming whitespace + trimLeft = /^\s+/, + trimRight = /\s+$/, + + // Check for digits + rdigit = /\d/, + + // Match a standalone tag + rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/, + + // JSON RegExp + rvalidchars = /^[\],:{}\s]*$/, + rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, + rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, + rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, + + // Useragent RegExp + rwebkit = /(webkit)[ \/]([\w.]+)/, + ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/, + rmsie = /(msie) ([\w.]+)/, + rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/, + + // Matches dashed string for camelizing + rdashAlpha = /-([a-z]|[0-9])/ig, + rmsPrefix = /^-ms-/, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return ( letter + "" ).toUpperCase(); + }, + + // Keep a UserAgent string for use with jQuery.browser + userAgent = navigator.userAgent, + + // For matching the engine and version of the browser + browserMatch, + + // The deferred used on DOM ready + readyList, + + // The ready event handler + DOMContentLoaded, + + // Save a reference to some core methods + toString = Object.prototype.toString, + hasOwn = Object.prototype.hasOwnProperty, + push = Array.prototype.push, + slice = Array.prototype.slice, + trim = String.prototype.trim, + indexOf = Array.prototype.indexOf, + + // [[Class]] -> type pairs + class2type = {}; + +jQuery.fn = jQuery.prototype = { + constructor: jQuery, + init: function( selector, context, rootjQuery ) { + var match, elem, ret, doc; + + // Handle $(""), $(null), or $(undefined) + if ( !selector ) { + return this; + } + + // Handle $(DOMElement) + if ( selector.nodeType ) { + this.context = this[0] = selector; + this.length = 1; + return this; + } + + // The body element only exists once, optimize finding it + if ( selector === "body" && !context && document.body ) { + this.context = document; + this[0] = document.body; + this.selector = selector; + this.length = 1; + return this; + } + + // Handle HTML strings + if ( typeof selector === "string" ) { + // Are we dealing with HTML string or an ID? + if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = quickExpr.exec( selector ); + } + + // Verify a match, and that no context was specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) { + context = context instanceof jQuery ? context[0] : context; + doc = ( context ? context.ownerDocument || context : document ); + + // If a single string is passed in and it's a single tag + // just do a createElement and skip the rest + ret = rsingleTag.exec( selector ); + + if ( ret ) { + if ( jQuery.isPlainObject( context ) ) { + selector = [ document.createElement( ret[1] ) ]; + jQuery.fn.attr.call( selector, context, true ); + + } else { + selector = [ doc.createElement( ret[1] ) ]; + } + + } else { + ret = jQuery.buildFragment( [ match[1] ], [ doc ] ); + selector = ( ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment ).childNodes; + } + + return jQuery.merge( this, selector ); + + // HANDLE: $("#id") + } else { + elem = document.getElementById( match[2] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id !== match[2] ) { + return rootjQuery.find( selector ); + } + + // Otherwise, we inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || rootjQuery ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return rootjQuery.ready( selector ); + } + + if ( selector.selector !== undefined ) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }, + + // Start with an empty selector + selector: "", + + // The current version of jQuery being used + jquery: "1.7", + + // The default length of a jQuery object is 0 + length: 0, + + // The number of elements contained in the matched element set + size: function() { + return this.length; + }, + + toArray: function() { + return slice.call( this, 0 ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num == null ? + + // Return a 'clean' array + this.toArray() : + + // Return just the object + ( num < 0 ? this[ this.length + num ] : this[ num ] ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems, name, selector ) { + // Build a new jQuery matched element set + var ret = this.constructor(); + + if ( jQuery.isArray( elems ) ) { + push.apply( ret, elems ); + + } else { + jQuery.merge( ret, elems ); + } + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + ret.context = this.context; + + if ( name === "find" ) { + ret.selector = this.selector + ( this.selector ? " " : "" ) + selector; + } else if ( name ) { + ret.selector = this.selector + "." + name + "(" + selector + ")"; + } + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + ready: function( fn ) { + // Attach the listeners + jQuery.bindReady(); + + // Add the callback + readyList.add( fn ); + + return this; + }, + + eq: function( i ) { + return i === -1 ? + this.slice( i ) : + this.slice( i, +i + 1 ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ), + "slice", slice.call(arguments).join(",") ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }, + + end: function() { + return this.prevObject || this.constructor(null); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: [].sort, + splice: [].splice +}; + +// Give the init function the jQuery prototype for later instantiation +jQuery.fn.init.prototype = jQuery.fn; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) { + target = {}; + } + + // extend jQuery itself if only one argument is passed + if ( length === i ) { + target = this; + --i; + } + + for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) { + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray(src) ? src : []; + + } else { + clone = src && jQuery.isPlainObject(src) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend({ + noConflict: function( deep ) { + if ( window.$ === jQuery ) { + window.$ = _$; + } + + if ( deep && window.jQuery === jQuery ) { + window.jQuery = _jQuery; + } + + return jQuery; + }, + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, + + // Handle when the DOM is ready + ready: function( wait ) { + // Either a released hold or an DOMready/load event and not yet ready + if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) { + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( !document.body ) { + return setTimeout( jQuery.ready, 1 ); + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.fireWith( document, [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.trigger ) { + jQuery( document ).trigger( "ready" ).unbind( "ready" ); + } + } + }, + + bindReady: function() { + if ( readyList ) { + return; + } + + readyList = jQuery.Callbacks( "once memory" ); + + // Catch cases where $(document).ready() is called after the + // browser event has already occurred. + if ( document.readyState === "complete" ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready + return setTimeout( jQuery.ready, 1 ); + } + + // Mozilla, Opera and webkit nightlies currently support this event + if ( document.addEventListener ) { + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", jQuery.ready, false ); + + // If IE event model is used + } else if ( document.attachEvent ) { + // ensure firing before onload, + // maybe late but safe also for iframes + document.attachEvent( "onreadystatechange", DOMContentLoaded ); + + // A fallback to window.onload, that will always work + window.attachEvent( "onload", jQuery.ready ); + + // If IE and not a frame + // continually check to see if the document is ready + var toplevel = false; + + try { + toplevel = window.frameElement == null; + } catch(e) {} + + if ( document.documentElement.doScroll && toplevel ) { + doScrollCheck(); + } + } + }, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return jQuery.type(obj) === "function"; + }, + + isArray: Array.isArray || function( obj ) { + return jQuery.type(obj) === "array"; + }, + + // A crude way of determining if an object is a window + isWindow: function( obj ) { + return obj && typeof obj === "object" && "setInterval" in obj; + }, + + isNumeric: function( obj ) { + return obj != null && rdigit.test( obj ) && !isNaN( obj ); + }, + + type: function( obj ) { + return obj == null ? + String( obj ) : + class2type[ toString.call(obj) ] || "object"; + }, + + isPlainObject: function( obj ) { + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + try { + // Not own constructor property must be Object + if ( obj.constructor && + !hasOwn.call(obj, "constructor") && + !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { + return false; + } + } catch ( e ) { + // IE8,9 Will throw exceptions on certain host objects #9897 + return false; + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + + var key; + for ( key in obj ) {} + + return key === undefined || hasOwn.call( obj, key ); + }, + + isEmptyObject: function( obj ) { + for ( var name in obj ) { + return false; + } + return true; + }, + + error: function( msg ) { + throw msg; + }, + + parseJSON: function( data ) { + if ( typeof data !== "string" || !data ) { + return null; + } + + // Make sure leading/trailing whitespace is removed (IE can't handle it) + data = jQuery.trim( data ); + + // Attempt to parse using the native JSON parser first + if ( window.JSON && window.JSON.parse ) { + return window.JSON.parse( data ); + } + + // Make sure the incoming data is actual JSON + // Logic borrowed from http://json.org/json2.js + if ( rvalidchars.test( data.replace( rvalidescape, "@" ) + .replace( rvalidtokens, "]" ) + .replace( rvalidbraces, "")) ) { + + return ( new Function( "return " + data ) )(); + + } + jQuery.error( "Invalid JSON: " + data ); + }, + + // Cross-browser xml parsing + parseXML: function( data ) { + var xml, tmp; + try { + if ( window.DOMParser ) { // Standard + tmp = new DOMParser(); + xml = tmp.parseFromString( data , "text/xml" ); + } else { // IE + xml = new ActiveXObject( "Microsoft.XMLDOM" ); + xml.async = "false"; + xml.loadXML( data ); + } + } catch( e ) { + xml = undefined; + } + if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; + }, + + noop: function() {}, + + // Evaluates a script in a global context + // Workarounds based on findings by Jim Driscoll + // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context + globalEval: function( data ) { + if ( data && rnotwhite.test( data ) ) { + // We use execScript on Internet Explorer + // We use an anonymous function so that context is window + // rather than jQuery in Firefox + ( window.execScript || function( data ) { + window[ "eval" ].call( window, data ); + } )( data ); + } + }, + + // Convert dashed to camelCase; used by the css and data modules + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); + }, + + // args is for internal usage only + each: function( object, callback, args ) { + var name, i = 0, + length = object.length, + isObj = length === undefined || jQuery.isFunction( object ); + + if ( args ) { + if ( isObj ) { + for ( name in object ) { + if ( callback.apply( object[ name ], args ) === false ) { + break; + } + } + } else { + for ( ; i < length; ) { + if ( callback.apply( object[ i++ ], args ) === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isObj ) { + for ( name in object ) { + if ( callback.call( object[ name ], name, object[ name ] ) === false ) { + break; + } + } + } else { + for ( ; i < length; ) { + if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) { + break; + } + } + } + } + + return object; + }, + + // Use native String.trim function wherever possible + trim: trim ? + function( text ) { + return text == null ? + "" : + trim.call( text ); + } : + + // Otherwise use our own trimming functionality + function( text ) { + return text == null ? + "" : + text.toString().replace( trimLeft, "" ).replace( trimRight, "" ); + }, + + // results is for internal usage only + makeArray: function( array, results ) { + var ret = results || []; + + if ( array != null ) { + // The window, strings (and functions) also have 'length' + // The extra typeof function check is to prevent crashes + // in Safari 2 (See: #3039) + // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930 + var type = jQuery.type( array ); + + if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) { + push.call( ret, array ); + } else { + jQuery.merge( ret, array ); + } + } + + return ret; + }, + + inArray: function( elem, array, i ) { + var len; + + if ( array ) { + if ( indexOf ) { + return indexOf.call( array, elem, i ); + } + + len = array.length; + i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; + + for ( ; i < len; i++ ) { + // Skip accessing in sparse arrays + if ( i in array && array[ i ] === elem ) { + return i; + } + } + } + + return -1; + }, + + merge: function( first, second ) { + var i = first.length, + j = 0; + + if ( typeof second.length === "number" ) { + for ( var l = second.length; j < l; j++ ) { + first[ i++ ] = second[ j ]; + } + + } else { + while ( second[j] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, inv ) { + var ret = [], retVal; + inv = !!inv; + + // Go through the array, only saving the items + // that pass the validator function + for ( var i = 0, length = elems.length; i < length; i++ ) { + retVal = !!callback( elems[ i ], i ); + if ( inv !== retVal ) { + ret.push( elems[ i ] ); + } + } + + return ret; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var value, key, ret = [], + i = 0, + length = elems.length, + // jquery objects are treated as arrays + isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ; + + // Go through the array, translating each of the items to their + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + + // Go through every key on the object, + } else { + for ( key in elems ) { + value = callback( elems[ key ], key, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + } + + // Flatten any nested arrays + return ret.concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + if ( typeof context === "string" ) { + var tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + var args = slice.call( arguments, 2 ), + proxy = function() { + return fn.apply( context, args.concat( slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++; + + return proxy; + }, + + // Mutifunctional method to get and set values to a collection + // The value/s can optionally be executed if it's a function + access: function( elems, key, value, exec, fn, pass ) { + var length = elems.length; + + // Setting many attributes + if ( typeof key === "object" ) { + for ( var k in key ) { + jQuery.access( elems, k, key[k], exec, fn, value ); + } + return elems; + } + + // Setting one attribute + if ( value !== undefined ) { + // Optionally, function values get executed if exec is true + exec = !pass && exec && jQuery.isFunction(value); + + for ( var i = 0; i < length; i++ ) { + fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass ); + } + + return elems; + } + + // Getting an attribute + return length ? fn( elems[0], key ) : undefined; + }, + + now: function() { + return ( new Date() ).getTime(); + }, + + // Use of jQuery.browser is frowned upon. + // More details: http://docs.jquery.com/Utilities/jQuery.browser + uaMatch: function( ua ) { + ua = ua.toLowerCase(); + + var match = rwebkit.exec( ua ) || + ropera.exec( ua ) || + rmsie.exec( ua ) || + ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) || + []; + + return { browser: match[1] || "", version: match[2] || "0" }; + }, + + sub: function() { + function jQuerySub( selector, context ) { + return new jQuerySub.fn.init( selector, context ); + } + jQuery.extend( true, jQuerySub, this ); + jQuerySub.superclass = this; + jQuerySub.fn = jQuerySub.prototype = this(); + jQuerySub.fn.constructor = jQuerySub; + jQuerySub.sub = this.sub; + jQuerySub.fn.init = function init( selector, context ) { + if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) { + context = jQuerySub( context ); + } + + return jQuery.fn.init.call( this, selector, context, rootjQuerySub ); + }; + jQuerySub.fn.init.prototype = jQuerySub.fn; + var rootjQuerySub = jQuerySub(document); + return jQuerySub; + }, + + browser: {} +}); + +// Populate the class2type map +jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +}); + +browserMatch = jQuery.uaMatch( userAgent ); +if ( browserMatch.browser ) { + jQuery.browser[ browserMatch.browser ] = true; + jQuery.browser.version = browserMatch.version; +} + +// Deprecated, use jQuery.browser.webkit instead +if ( jQuery.browser.webkit ) { + jQuery.browser.safari = true; +} + +// IE doesn't match non-breaking spaces with \s +if ( rnotwhite.test( "\xA0" ) ) { + trimLeft = /^[\s\xA0]+/; + trimRight = /[\s\xA0]+$/; +} + +// All jQuery objects should point back to these +rootjQuery = jQuery(document); + +// Cleanup functions for the document ready method +if ( document.addEventListener ) { + DOMContentLoaded = function() { + document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + jQuery.ready(); + }; + +} else if ( document.attachEvent ) { + DOMContentLoaded = function() { + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( document.readyState === "complete" ) { + document.detachEvent( "onreadystatechange", DOMContentLoaded ); + jQuery.ready(); + } + }; +} + +// The DOM ready check for Internet Explorer +function doScrollCheck() { + if ( jQuery.isReady ) { + return; + } + + try { + // If IE is used, use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + document.documentElement.doScroll("left"); + } catch(e) { + setTimeout( doScrollCheck, 1 ); + return; + } + + // and execute any waiting functions + jQuery.ready(); +} + +// Expose jQuery as an AMD module, but only for AMD loaders that +// understand the issues with loading multiple versions of jQuery +// in a page that all might call define(). The loader will indicate +// they have special allowances for multiple jQuery versions by +// specifying define.amd.jQuery = true. Register as a named module, +// since jQuery can be concatenated with other files that may use define, +// but not use a proper concatenation script that understands anonymous +// AMD modules. A named AMD is safest and most robust way to register. +// Lowercase jquery is used because AMD module names are derived from +// file names, and jQuery is normally delivered in a lowercase file name. +if ( typeof define === "function" && define.amd && define.amd.jQuery ) { + define( "jquery", [], function () { return jQuery; } ); +} + +return jQuery; + +})(); + + +// String to Object flags format cache +var flagsCache = {}; + +// Convert String-formatted flags into Object-formatted ones and store in cache +function createFlags( flags ) { + var object = flagsCache[ flags ] = {}, + i, length; + flags = flags.split( /\s+/ ); + for ( i = 0, length = flags.length; i < length; i++ ) { + object[ flags[i] ] = true; + } + return object; +} + +/* + * Create a callback list using the following parameters: + * + * flags: an optional list of space-separated flags that will change how + * the callback list behaves + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible flags: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( flags ) { + + // Convert flags from String-formatted to Object-formatted + // (we check in cache first) + flags = flags ? ( flagsCache[ flags ] || createFlags( flags ) ) : {}; + + var // Actual callback list + list = [], + // Stack of fire calls for repeatable lists + stack = [], + // Last fire value (for non-forgettable lists) + memory, + // Flag to know if list is currently firing + firing, + // First callback to fire (used internally by add and fireWith) + firingStart, + // End of the loop when firing + firingLength, + // Index of currently firing callback (modified by remove if needed) + firingIndex, + // Add one or several callbacks to the list + add = function( args ) { + var i, + length, + elem, + type, + actual; + for ( i = 0, length = args.length; i < length; i++ ) { + elem = args[ i ]; + type = jQuery.type( elem ); + if ( type === "array" ) { + // Inspect recursively + add( elem ); + } else if ( type === "function" ) { + // Add if not in unique mode and callback is not in + if ( !flags.unique || !self.has( elem ) ) { + list.push( elem ); + } + } + } + }, + // Fire callbacks + fire = function( context, args ) { + args = args || []; + memory = !flags.memory || [ context, args ]; + firing = true; + firingIndex = firingStart || 0; + firingStart = 0; + firingLength = list.length; + for ( ; list && firingIndex < firingLength; firingIndex++ ) { + if ( list[ firingIndex ].apply( context, args ) === false && flags.stopOnFalse ) { + memory = true; // Mark as halted + break; + } + } + firing = false; + if ( list ) { + if ( !flags.once ) { + if ( stack && stack.length ) { + memory = stack.shift(); + self.fireWith( memory[ 0 ], memory[ 1 ] ); + } + } else if ( memory === true ) { + self.disable(); + } else { + list = []; + } + } + }, + // Actual Callbacks object + self = { + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + var length = list.length; + add( arguments ); + // Do we need to add the callbacks to the + // current firing batch? + if ( firing ) { + firingLength = list.length; + // With memory, if we're not firing then + // we should call right away, unless previous + // firing was halted (stopOnFalse) + } else if ( memory && memory !== true ) { + firingStart = length; + fire( memory[ 0 ], memory[ 1 ] ); + } + } + return this; + }, + // Remove a callback from the list + remove: function() { + if ( list ) { + var args = arguments, + argIndex = 0, + argLength = args.length; + for ( ; argIndex < argLength ; argIndex++ ) { + for ( var i = 0; i < list.length; i++ ) { + if ( args[ argIndex ] === list[ i ] ) { + // Handle firingIndex and firingLength + if ( firing ) { + if ( i <= firingLength ) { + firingLength--; + if ( i <= firingIndex ) { + firingIndex--; + } + } + } + // Remove the element + list.splice( i--, 1 ); + // If we have some unicity property then + // we only need to do this once + if ( flags.unique ) { + break; + } + } + } + } + } + return this; + }, + // Control if a given callback is in the list + has: function( fn ) { + if ( list ) { + var i = 0, + length = list.length; + for ( ; i < length; i++ ) { + if ( fn === list[ i ] ) { + return true; + } + } + } + return false; + }, + // Remove all callbacks from the list + empty: function() { + list = []; + return this; + }, + // Have the list do nothing anymore + disable: function() { + list = stack = memory = undefined; + return this; + }, + // Is it disabled? + disabled: function() { + return !list; + }, + // Lock the list in its current state + lock: function() { + stack = undefined; + if ( !memory || memory === true ) { + self.disable(); + } + return this; + }, + // Is it locked? + locked: function() { + return !stack; + }, + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( stack ) { + if ( firing ) { + if ( !flags.once ) { + stack.push( [ context, args ] ); + } + } else if ( !( flags.once && memory ) ) { + fire( context, args ); + } + } + return this; + }, + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + // To know if the callbacks have already been called at least once + fired: function() { + return !!memory; + } + }; + + return self; +}; + + + + +var // Static reference to slice + sliceDeferred = [].slice; + +jQuery.extend({ + + Deferred: function( func ) { + var doneList = jQuery.Callbacks( "once memory" ), + failList = jQuery.Callbacks( "once memory" ), + progressList = jQuery.Callbacks( "memory" ), + state = "pending", + lists = { + resolve: doneList, + reject: failList, + notify: progressList + }, + promise = { + done: doneList.add, + fail: failList.add, + progress: progressList.add, + + state: function() { + return state; + }, + + // Deprecated + isResolved: doneList.fired, + isRejected: failList.fired, + + then: function( doneCallbacks, failCallbacks, progressCallbacks ) { + deferred.done( doneCallbacks ).fail( failCallbacks ).progress( progressCallbacks ); + return this; + }, + always: function() { + return deferred.done.apply( deferred, arguments ).fail.apply( deferred, arguments ); + }, + pipe: function( fnDone, fnFail, fnProgress ) { + return jQuery.Deferred(function( newDefer ) { + jQuery.each( { + done: [ fnDone, "resolve" ], + fail: [ fnFail, "reject" ], + progress: [ fnProgress, "notify" ] + }, function( handler, data ) { + var fn = data[ 0 ], + action = data[ 1 ], + returned; + if ( jQuery.isFunction( fn ) ) { + deferred[ handler ](function() { + returned = fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise().then( newDefer.resolve, newDefer.reject, newDefer.notify ); + } else { + newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] ); + } + }); + } else { + deferred[ handler ]( newDefer[ action ] ); + } + }); + }).promise(); + }, + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + if ( obj == null ) { + obj = promise; + } else { + for ( var key in promise ) { + obj[ key ] = promise[ key ]; + } + } + return obj; + } + }, + deferred = promise.promise({}), + key; + + for ( key in lists ) { + deferred[ key ] = lists[ key ].fire; + deferred[ key + "With" ] = lists[ key ].fireWith; + } + + // Handle state + deferred.done( function() { + state = "resolved"; + }, failList.disable, progressList.lock ).fail( function() { + state = "rejected"; + }, doneList.disable, progressList.lock ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( firstParam ) { + var args = sliceDeferred.call( arguments, 0 ), + i = 0, + length = args.length, + pValues = new Array( length ), + count = length, + pCount = length, + deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ? + firstParam : + jQuery.Deferred(), + promise = deferred.promise(); + function resolveFunc( i ) { + return function( value ) { + args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value; + if ( !( --count ) ) { + deferred.resolveWith( deferred, args ); + } + }; + } + function progressFunc( i ) { + return function( value ) { + pValues[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value; + deferred.notifyWith( promise, pValues ); + }; + } + if ( length > 1 ) { + for ( ; i < length; i++ ) { + if ( args[ i ] && args[ i ].promise && jQuery.isFunction( args[ i ].promise ) ) { + args[ i ].promise().then( resolveFunc(i), deferred.reject, progressFunc(i) ); + } else { + --count; + } + } + if ( !count ) { + deferred.resolveWith( deferred, args ); + } + } else if ( deferred !== firstParam ) { + deferred.resolveWith( deferred, length ? [ firstParam ] : [] ); + } + return promise; + } +}); + + + + +jQuery.support = (function() { + + var div = document.createElement( "div" ), + documentElement = document.documentElement, + all, + a, + select, + opt, + input, + marginDiv, + support, + fragment, + body, + testElementParent, + testElement, + testElementStyle, + tds, + events, + eventName, + i, + isSupported; + + // Preliminary tests + div.setAttribute("className", "t"); + div.innerHTML = "
a

"; + + + all = div.getElementsByTagName( "*" ); + a = div.getElementsByTagName( "a" )[ 0 ]; + + // Can't get basic test support + if ( !all || !all.length || !a ) { + return {}; + } + + // First batch of supports tests + select = document.createElement( "select" ); + opt = select.appendChild( document.createElement("option") ); + input = div.getElementsByTagName( "input" )[ 0 ]; + + support = { + // IE strips leading whitespace when .innerHTML is used + leadingWhitespace: ( div.firstChild.nodeType === 3 ), + + // Make sure that tbody elements aren't automatically inserted + // IE will insert them into empty tables + tbody: !div.getElementsByTagName( "tbody" ).length, + + // Make sure that link elements get serialized correctly by innerHTML + // This requires a wrapper element in IE + htmlSerialize: !!div.getElementsByTagName( "link" ).length, + + // Get the style information from getAttribute + // (IE uses .cssText instead) + style: /top/.test( a.getAttribute("style") ), + + // Make sure that URLs aren't manipulated + // (IE normalizes it by default) + hrefNormalized: ( a.getAttribute( "href" ) === "/a" ), + + // Make sure that element opacity exists + // (IE uses filter instead) + // Use a regex to work around a WebKit issue. See #5145 + opacity: /^0.55/.test( a.style.opacity ), + + // Verify style float existence + // (IE uses styleFloat instead of cssFloat) + cssFloat: !!a.style.cssFloat, + + // Make sure unknown elements (like HTML5 elems) are handled appropriately + unknownElems: !!div.getElementsByTagName( "nav" ).length, + + // Make sure that if no value is specified for a checkbox + // that it defaults to "on". + // (WebKit defaults to "" instead) + checkOn: ( input.value === "on" ), + + // Make sure that a selected-by-default option has a working selected property. + // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) + optSelected: opt.selected, + + // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) + getSetAttribute: div.className !== "t", + + // Tests for enctype support on a form(#6743) + enctype: !!document.createElement("form").enctype, + + // Will be defined later + submitBubbles: true, + changeBubbles: true, + focusinBubbles: false, + deleteExpando: true, + noCloneEvent: true, + inlineBlockNeedsLayout: false, + shrinkWrapBlocks: false, + reliableMarginRight: true + }; + + // Make sure checked status is properly cloned + input.checked = true; + support.noCloneChecked = input.cloneNode( true ).checked; + + // Make sure that the options inside disabled selects aren't marked as disabled + // (WebKit marks them as disabled) + select.disabled = true; + support.optDisabled = !opt.disabled; + + // Test to see if it's possible to delete an expando from an element + // Fails in Internet Explorer + try { + delete div.test; + } catch( e ) { + support.deleteExpando = false; + } + + if ( !div.addEventListener && div.attachEvent && div.fireEvent ) { + div.attachEvent( "onclick", function() { + // Cloning a node shouldn't copy over any + // bound event handlers (IE does this) + support.noCloneEvent = false; + }); + div.cloneNode( true ).fireEvent( "onclick" ); + } + + // Check if a radio maintains its value + // after being appended to the DOM + input = document.createElement("input"); + input.value = "t"; + input.setAttribute("type", "radio"); + support.radioValue = input.value === "t"; + + input.setAttribute("checked", "checked"); + div.appendChild( input ); + fragment = document.createDocumentFragment(); + fragment.appendChild( div.lastChild ); + + // WebKit doesn't clone checked state correctly in fragments + support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; + + div.innerHTML = ""; + + // Figure out if the W3C box model works as expected + div.style.width = div.style.paddingLeft = "1px"; + + // We don't want to do body-related feature tests on frameset + // documents, which lack a body. So we use + // document.getElementsByTagName("body")[0], which is undefined in + // frameset documents, while document.body isn’t. (7398) + body = document.getElementsByTagName("body")[ 0 ]; + // We use our own, invisible, body unless the body is already present + // in which case we use a div (#9239) + testElement = document.createElement( body ? "div" : "body" ); + testElementStyle = { + visibility: "hidden", + width: 0, + height: 0, + border: 0, + margin: 0, + background: "none" + }; + if ( body ) { + jQuery.extend( testElementStyle, { + position: "absolute", + left: "-999px", + top: "-999px" + }); + } + for ( i in testElementStyle ) { + testElement.style[ i ] = testElementStyle[ i ]; + } + testElement.appendChild( div ); + testElementParent = body || documentElement; + testElementParent.insertBefore( testElement, testElementParent.firstChild ); + + // Check if a disconnected checkbox will retain its checked + // value of true after appended to the DOM (IE6/7) + support.appendChecked = input.checked; + + support.boxModel = div.offsetWidth === 2; + + if ( "zoom" in div.style ) { + // Check if natively block-level elements act like inline-block + // elements when setting their display to 'inline' and giving + // them layout + // (IE < 8 does this) + div.style.display = "inline"; + div.style.zoom = 1; + support.inlineBlockNeedsLayout = ( div.offsetWidth === 2 ); + + // Check if elements with layout shrink-wrap their children + // (IE 6 does this) + div.style.display = ""; + div.innerHTML = "
"; + support.shrinkWrapBlocks = ( div.offsetWidth !== 2 ); + } + + div.innerHTML = "
t
"; + tds = div.getElementsByTagName( "td" ); + + // Check if table cells still have offsetWidth/Height when they are set + // to display:none and there are still other visible table cells in a + // table row; if so, offsetWidth/Height are not reliable for use when + // determining if an element has been hidden directly using + // display:none (it is still safe to use offsets if a parent element is + // hidden; don safety goggles and see bug #4512 for more information). + // (only IE 8 fails this test) + isSupported = ( tds[ 0 ].offsetHeight === 0 ); + + tds[ 0 ].style.display = ""; + tds[ 1 ].style.display = "none"; + + // Check if empty table cells still have offsetWidth/Height + // (IE < 8 fail this test) + support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); + div.innerHTML = ""; + + // Check if div with explicit width and no margin-right incorrectly + // gets computed margin-right based on width of container. For more + // info see bug #3333 + // Fails in WebKit before Feb 2011 nightlies + // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right + if ( document.defaultView && document.defaultView.getComputedStyle ) { + marginDiv = document.createElement( "div" ); + marginDiv.style.width = "0"; + marginDiv.style.marginRight = "0"; + div.appendChild( marginDiv ); + support.reliableMarginRight = + ( parseInt( ( document.defaultView.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0; + } + + // Technique from Juriy Zaytsev + // http://perfectionkills.com/detecting-event-support-without-browser-sniffing/ + // We only care about the case where non-standard event systems + // are used, namely in IE. Short-circuiting here helps us to + // avoid an eval call (in setAttribute) which can cause CSP + // to go haywire. See: https://developer.mozilla.org/en/Security/CSP + if ( div.attachEvent ) { + for( i in { + submit: 1, + change: 1, + focusin: 1 + } ) { + eventName = "on" + i; + isSupported = ( eventName in div ); + if ( !isSupported ) { + div.setAttribute( eventName, "return;" ); + isSupported = ( typeof div[ eventName ] === "function" ); + } + support[ i + "Bubbles" ] = isSupported; + } + } + + // Run fixed position tests at doc ready to avoid a crash + // related to the invisible body in IE8 + jQuery(function() { + var container, outer, inner, table, td, offsetSupport, + conMarginTop = 1, + ptlm = "position:absolute;top:0;left:0;width:1px;height:1px;margin:0;", + vb = "visibility:hidden;border:0;", + style = "style='" + ptlm + "border:5px solid #000;padding:0;'", + html = "
" + + "" + + "
"; + + // Reconstruct a container + body = document.getElementsByTagName("body")[0]; + if ( !body ) { + // Return for frameset docs that don't have a body + // These tests cannot be done + return; + } + + container = document.createElement("div"); + container.style.cssText = vb + "width:0;height:0;position:static;top:0;margin-top:" + conMarginTop + "px"; + body.insertBefore( container, body.firstChild ); + + // Construct a test element + testElement = document.createElement("div"); + testElement.style.cssText = ptlm + vb; + + testElement.innerHTML = html; + container.appendChild( testElement ); + outer = testElement.firstChild; + inner = outer.firstChild; + td = outer.nextSibling.firstChild.firstChild; + + offsetSupport = { + doesNotAddBorder: ( inner.offsetTop !== 5 ), + doesAddBorderForTableAndCells: ( td.offsetTop === 5 ) + }; + + inner.style.position = "fixed"; + inner.style.top = "20px"; + + // safari subtracts parent border width here which is 5px + offsetSupport.fixedPosition = ( inner.offsetTop === 20 || inner.offsetTop === 15 ); + inner.style.position = inner.style.top = ""; + + outer.style.overflow = "hidden"; + outer.style.position = "relative"; + + offsetSupport.subtractsBorderForOverflowNotVisible = ( inner.offsetTop === -5 ); + offsetSupport.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== conMarginTop ); + + body.removeChild( container ); + testElement = container = null; + + jQuery.extend( support, offsetSupport ); + }); + + testElement.innerHTML = ""; + testElementParent.removeChild( testElement ); + + // Null connected elements to avoid leaks in IE + testElement = fragment = select = opt = body = marginDiv = div = input = null; + + return support; +})(); + +// Keep track of boxModel +jQuery.boxModel = jQuery.support.boxModel; + + + + +var rbrace = /^(?:\{.*\}|\[.*\])$/, + rmultiDash = /([A-Z])/g; + +jQuery.extend({ + cache: {}, + + // Please use with caution + uuid: 0, + + // Unique for each copy of jQuery on the page + // Non-digits removed to match rinlinejQuery + expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ), + + // The following elements throw uncatchable exceptions if you + // attempt to add expando properties to them. + noData: { + "embed": true, + // Ban all objects except for Flash (which handle expandos) + "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000", + "applet": true + }, + + hasData: function( elem ) { + elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; + return !!elem && !isEmptyDataObject( elem ); + }, + + data: function( elem, name, data, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var privateCache, thisCache, ret, + internalKey = jQuery.expando, + getByName = typeof name === "string", + + // We have to handle DOM nodes and JS objects differently because IE6-7 + // can't GC object references properly across the DOM-JS boundary + isNode = elem.nodeType, + + // Only DOM nodes need the global jQuery cache; JS object data is + // attached directly to the object so GC can occur automatically + cache = isNode ? jQuery.cache : elem, + + // Only defining an ID for JS objects if its cache already exists allows + // the code to shortcut on the same path as a DOM node with no cache + id = isNode ? elem[ jQuery.expando ] : elem[ jQuery.expando ] && jQuery.expando, + isEvents = name === "events"; + + // Avoid doing any more work than we need to when trying to get data on an + // object that has no data at all + if ( (!id || !cache[id] || (!isEvents && !pvt && !cache[id].data)) && getByName && data === undefined ) { + return; + } + + if ( !id ) { + // Only DOM nodes need a new unique ID for each element since their data + // ends up in the global cache + if ( isNode ) { + elem[ jQuery.expando ] = id = ++jQuery.uuid; + } else { + id = jQuery.expando; + } + } + + if ( !cache[ id ] ) { + cache[ id ] = {}; + + // Avoids exposing jQuery metadata on plain JS objects when the object + // is serialized using JSON.stringify + if ( !isNode ) { + cache[ id ].toJSON = jQuery.noop; + } + } + + // An object can be passed to jQuery.data instead of a key/value pair; this gets + // shallow copied over onto the existing cache + if ( typeof name === "object" || typeof name === "function" ) { + if ( pvt ) { + cache[ id ] = jQuery.extend( cache[ id ], name ); + } else { + cache[ id ].data = jQuery.extend( cache[ id ].data, name ); + } + } + + privateCache = thisCache = cache[ id ]; + + // jQuery data() is stored in a separate object inside the object's internal data + // cache in order to avoid key collisions between internal data and user-defined + // data. + if ( !pvt ) { + if ( !thisCache.data ) { + thisCache.data = {}; + } + + thisCache = thisCache.data; + } + + if ( data !== undefined ) { + thisCache[ jQuery.camelCase( name ) ] = data; + } + + // Users should not attempt to inspect the internal events object using jQuery.data, + // it is undocumented and subject to change. But does anyone listen? No. + if ( isEvents && !thisCache[ name ] ) { + return privateCache.events; + } + + // Check for both converted-to-camel and non-converted data property names + // If a data property was specified + if ( getByName ) { + + // First Try to find as-is property data + ret = thisCache[ name ]; + + // Test for null|undefined property data + if ( ret == null ) { + + // Try to find the camelCased property + ret = thisCache[ jQuery.camelCase( name ) ]; + } + } else { + ret = thisCache; + } + + return ret; + }, + + removeData: function( elem, name, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var thisCache, i, l, + + // Reference to internal data cache key + internalKey = jQuery.expando, + + isNode = elem.nodeType, + + // See jQuery.data for more information + cache = isNode ? jQuery.cache : elem, + + // See jQuery.data for more information + id = isNode ? elem[ jQuery.expando ] : jQuery.expando; + + // If there is already no cache entry for this object, there is no + // purpose in continuing + if ( !cache[ id ] ) { + return; + } + + if ( name ) { + + thisCache = pvt ? cache[ id ] : cache[ id ].data; + + if ( thisCache ) { + + // Support space separated names + if ( jQuery.isArray( name ) ) { + name = name; + } else if ( name in thisCache ) { + name = [ name ]; + } else { + + // split the camel cased version by spaces + name = jQuery.camelCase( name ); + if ( name in thisCache ) { + name = [ name ]; + } else { + name = name.split( " " ); + } + } + + for ( i = 0, l = name.length; i < l; i++ ) { + delete thisCache[ name[i] ]; + } + + // If there is no data left in the cache, we want to continue + // and let the cache object itself get destroyed + if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) { + return; + } + } + } + + // See jQuery.data for more information + if ( !pvt ) { + delete cache[ id ].data; + + // Don't destroy the parent cache unless the internal data object + // had been the only thing left in it + if ( !isEmptyDataObject(cache[ id ]) ) { + return; + } + } + + // Browsers that fail expando deletion also refuse to delete expandos on + // the window, but it will allow it on all other JS objects; other browsers + // don't care + // Ensure that `cache` is not a window object #10080 + if ( jQuery.support.deleteExpando || !cache.setInterval ) { + delete cache[ id ]; + } else { + cache[ id ] = null; + } + + // We destroyed the cache and need to eliminate the expando on the node to avoid + // false lookups in the cache for entries that no longer exist + if ( isNode ) { + // IE does not allow us to delete expando properties from nodes, + // nor does it have a removeAttribute function on Document nodes; + // we must handle all of these cases + if ( jQuery.support.deleteExpando ) { + delete elem[ jQuery.expando ]; + } else if ( elem.removeAttribute ) { + elem.removeAttribute( jQuery.expando ); + } else { + elem[ jQuery.expando ] = null; + } + } + }, + + // For internal use only. + _data: function( elem, name, data ) { + return jQuery.data( elem, name, data, true ); + }, + + // A method for determining if a DOM node can handle the data expando + acceptData: function( elem ) { + if ( elem.nodeName ) { + var match = jQuery.noData[ elem.nodeName.toLowerCase() ]; + + if ( match ) { + return !(match === true || elem.getAttribute("classid") !== match); + } + } + + return true; + } +}); + +jQuery.fn.extend({ + data: function( key, value ) { + var parts, attr, name, + data = null; + + if ( typeof key === "undefined" ) { + if ( this.length ) { + data = jQuery.data( this[0] ); + + if ( this[0].nodeType === 1 && !jQuery._data( this[0], "parsedAttrs" ) ) { + attr = this[0].attributes; + for ( var i = 0, l = attr.length; i < l; i++ ) { + name = attr[i].name; + + if ( name.indexOf( "data-" ) === 0 ) { + name = jQuery.camelCase( name.substring(5) ); + + dataAttr( this[0], name, data[ name ] ); + } + } + jQuery._data( this[0], "parsedAttrs", true ); + } + } + + return data; + + } else if ( typeof key === "object" ) { + return this.each(function() { + jQuery.data( this, key ); + }); + } + + parts = key.split("."); + parts[1] = parts[1] ? "." + parts[1] : ""; + + if ( value === undefined ) { + data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]); + + // Try to fetch any internally stored data first + if ( data === undefined && this.length ) { + data = jQuery.data( this[0], key ); + data = dataAttr( this[0], key, data ); + } + + return data === undefined && parts[1] ? + this.data( parts[0] ) : + data; + + } else { + return this.each(function() { + var $this = jQuery( this ), + args = [ parts[0], value ]; + + $this.triggerHandler( "setData" + parts[1] + "!", args ); + jQuery.data( this, key, value ); + $this.triggerHandler( "changeData" + parts[1] + "!", args ); + }); + } + }, + + removeData: function( key ) { + return this.each(function() { + jQuery.removeData( this, key ); + }); + } +}); + +function dataAttr( elem, key, data ) { + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + + var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); + + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + jQuery.isNumeric( data ) ? parseFloat( data ) : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch( e ) {} + + // Make sure we set the data so it isn't changed later + jQuery.data( elem, key, data ); + + } else { + data = undefined; + } + } + + return data; +} + +// checks a cache object for emptiness +function isEmptyDataObject( obj ) { + for ( var name in obj ) { + + // if the public data object is empty, the private is still empty + if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { + continue; + } + if ( name !== "toJSON" ) { + return false; + } + } + + return true; +} + + + + +function handleQueueMarkDefer( elem, type, src ) { + var deferDataKey = type + "defer", + queueDataKey = type + "queue", + markDataKey = type + "mark", + defer = jQuery._data( elem, deferDataKey ); + if ( defer && + ( src === "queue" || !jQuery._data(elem, queueDataKey) ) && + ( src === "mark" || !jQuery._data(elem, markDataKey) ) ) { + // Give room for hard-coded callbacks to fire first + // and eventually mark/queue something else on the element + setTimeout( function() { + if ( !jQuery._data( elem, queueDataKey ) && + !jQuery._data( elem, markDataKey ) ) { + jQuery.removeData( elem, deferDataKey, true ); + defer.fire(); + } + }, 0 ); + } +} + +jQuery.extend({ + + _mark: function( elem, type ) { + if ( elem ) { + type = ( type || "fx" ) + "mark"; + jQuery._data( elem, type, (jQuery._data( elem, type ) || 0) + 1 ); + } + }, + + _unmark: function( force, elem, type ) { + if ( force !== true ) { + type = elem; + elem = force; + force = false; + } + if ( elem ) { + type = type || "fx"; + var key = type + "mark", + count = force ? 0 : ( (jQuery._data( elem, key ) || 1) - 1 ); + if ( count ) { + jQuery._data( elem, key, count ); + } else { + jQuery.removeData( elem, key, true ); + handleQueueMarkDefer( elem, type, "mark" ); + } + } + }, + + queue: function( elem, type, data ) { + var q; + if ( elem ) { + type = ( type || "fx" ) + "queue"; + q = jQuery._data( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !q || jQuery.isArray(data) ) { + q = jQuery._data( elem, type, jQuery.makeArray(data) ); + } else { + q.push( data ); + } + } + return q || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + fn = queue.shift(), + hooks = {}; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + } + + if ( fn ) { + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + jQuery._data( elem, type + ".run", hooks ); + fn.call( elem, function() { + jQuery.dequeue( elem, type ); + }, hooks ); + } + + if ( !queue.length ) { + jQuery.removeData( elem, type + "queue " + type + ".run", true ); + handleQueueMarkDefer( elem, type, "queue" ); + } + } +}); + +jQuery.fn.extend({ + queue: function( type, data ) { + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + } + + if ( data === undefined ) { + return jQuery.queue( this[0], type ); + } + return this.each(function() { + var queue = jQuery.queue( this, type, data ); + + if ( type === "fx" && queue[0] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + }); + }, + dequeue: function( type ) { + return this.each(function() { + jQuery.dequeue( this, type ); + }); + }, + // Based off of the plugin by Clint Helfers, with permission. + // http://blindsignals.com/index.php/2009/07/jquery-delay/ + delay: function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = setTimeout( next, time ); + hooks.stop = function() { + clearTimeout( timeout ); + }; + }); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, object ) { + if ( typeof type !== "string" ) { + object = type; + type = undefined; + } + type = type || "fx"; + var defer = jQuery.Deferred(), + elements = this, + i = elements.length, + count = 1, + deferDataKey = type + "defer", + queueDataKey = type + "queue", + markDataKey = type + "mark", + tmp; + function resolve() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + } + while( i-- ) { + if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) || + ( jQuery.data( elements[ i ], queueDataKey, undefined, true ) || + jQuery.data( elements[ i ], markDataKey, undefined, true ) ) && + jQuery.data( elements[ i ], deferDataKey, jQuery.Callbacks( "once memory" ), true ) )) { + count++; + tmp.add( resolve ); + } + } + resolve(); + return defer.promise(); + } +}); + + + + +var rclass = /[\n\t\r]/g, + rspace = /\s+/, + rreturn = /\r/g, + rtype = /^(?:button|input)$/i, + rfocusable = /^(?:button|input|object|select|textarea)$/i, + rclickable = /^a(?:rea)?$/i, + rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i, + getSetAttribute = jQuery.support.getSetAttribute, + nodeHook, boolHook, fixSpecified; + +jQuery.fn.extend({ + attr: function( name, value ) { + return jQuery.access( this, name, value, true, jQuery.attr ); + }, + + removeAttr: function( name ) { + return this.each(function() { + jQuery.removeAttr( this, name ); + }); + }, + + prop: function( name, value ) { + return jQuery.access( this, name, value, true, jQuery.prop ); + }, + + removeProp: function( name ) { + name = jQuery.propFix[ name ] || name; + return this.each(function() { + // try/catch handles cases where IE balks (such as removing a property on window) + try { + this[ name ] = undefined; + delete this[ name ]; + } catch( e ) {} + }); + }, + + addClass: function( value ) { + var classNames, i, l, elem, + setClass, c, cl; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).addClass( value.call(this, j, this.className) ); + }); + } + + if ( value && typeof value === "string" ) { + classNames = value.split( rspace ); + + for ( i = 0, l = this.length; i < l; i++ ) { + elem = this[ i ]; + + if ( elem.nodeType === 1 ) { + if ( !elem.className && classNames.length === 1 ) { + elem.className = value; + + } else { + setClass = " " + elem.className + " "; + + for ( c = 0, cl = classNames.length; c < cl; c++ ) { + if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) { + setClass += classNames[ c ] + " "; + } + } + elem.className = jQuery.trim( setClass ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classNames, i, l, elem, className, c, cl; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).removeClass( value.call(this, j, this.className) ); + }); + } + + if ( (value && typeof value === "string") || value === undefined ) { + classNames = ( value || "" ).split( rspace ); + + for ( i = 0, l = this.length; i < l; i++ ) { + elem = this[ i ]; + + if ( elem.nodeType === 1 && elem.className ) { + if ( value ) { + className = (" " + elem.className + " ").replace( rclass, " " ); + for ( c = 0, cl = classNames.length; c < cl; c++ ) { + className = className.replace(" " + classNames[ c ] + " ", " "); + } + elem.className = jQuery.trim( className ); + + } else { + elem.className = ""; + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isBool = typeof stateVal === "boolean"; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( i ) { + jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); + }); + } + + return this.each(function() { + if ( type === "string" ) { + // toggle individual class names + var className, + i = 0, + self = jQuery( this ), + state = stateVal, + classNames = value.split( rspace ); + + while ( (className = classNames[ i++ ]) ) { + // check each className given, space seperated list + state = isBool ? state : !self.hasClass( className ); + self[ state ? "addClass" : "removeClass" ]( className ); + } + + } else if ( type === "undefined" || type === "boolean" ) { + if ( this.className ) { + // store className if set + jQuery._data( this, "__className__", this.className ); + } + + // toggle whole className + this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || ""; + } + }); + }, + + hasClass: function( selector ) { + var className = " " + selector + " ", + i = 0, + l = this.length; + for ( ; i < l; i++ ) { + if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) { + return true; + } + } + + return false; + }, + + val: function( value ) { + var hooks, ret, isFunction, + elem = this[0]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.nodeName.toLowerCase() ] || jQuery.valHooks[ elem.type ]; + + if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { + return ret; + } + + ret = elem.value; + + return typeof ret === "string" ? + // handle most common string cases + ret.replace(rreturn, "") : + // handle cases where value is null/undef or number + ret == null ? "" : ret; + } + + return undefined; + } + + isFunction = jQuery.isFunction( value ); + + return this.each(function( i ) { + var self = jQuery(this), val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( isFunction ) { + val = value.call( this, i, self.val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + } else if ( typeof val === "number" ) { + val += ""; + } else if ( jQuery.isArray( val ) ) { + val = jQuery.map(val, function ( value ) { + return value == null ? "" : value + ""; + }); + } + + hooks = jQuery.valHooks[ this.nodeName.toLowerCase() ] || jQuery.valHooks[ this.type ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + }); + } +}); + +jQuery.extend({ + valHooks: { + option: { + get: function( elem ) { + // attributes.value is undefined in Blackberry 4.7 but + // uses .value. See #6932 + var val = elem.attributes.value; + return !val || val.specified ? elem.value : elem.text; + } + }, + select: { + get: function( elem ) { + var value, i, max, option, + index = elem.selectedIndex, + values = [], + options = elem.options, + one = elem.type === "select-one"; + + // Nothing was selected + if ( index < 0 ) { + return null; + } + + // Loop through all the selected options + i = one ? index : 0; + max = one ? index + 1 : options.length; + for ( ; i < max; i++ ) { + option = options[ i ]; + + // Don't return options that are disabled or in a disabled optgroup + if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) && + (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + // Fixes Bug #2551 -- select.val() broken in IE after form.reset() + if ( one && !values.length && options.length ) { + return jQuery( options[ index ] ).val(); + } + + return values; + }, + + set: function( elem, value ) { + var values = jQuery.makeArray( value ); + + jQuery(elem).find("option").each(function() { + this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0; + }); + + if ( !values.length ) { + elem.selectedIndex = -1; + } + return values; + } + } + }, + + attrFn: { + val: true, + css: true, + html: true, + text: true, + data: true, + width: true, + height: true, + offset: true + }, + + attr: function( elem, name, value, pass ) { + var ret, hooks, notxml, + nType = elem.nodeType; + + // don't get/set attributes on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return undefined; + } + + if ( pass && name in jQuery.attrFn ) { + return jQuery( elem )[ name ]( value ); + } + + // Fallback to prop when attributes are not supported + if ( !("getAttribute" in elem) ) { + return jQuery.prop( elem, name, value ); + } + + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + // All attributes are lowercase + // Grab necessary hook if one is defined + if ( notxml ) { + name = name.toLowerCase(); + hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook ); + } + + if ( value !== undefined ) { + + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return undefined; + + } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + elem.setAttribute( name, "" + value ); + return value; + } + + } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + + ret = elem.getAttribute( name ); + + // Non-existent attributes return null, we normalize to undefined + return ret === null ? + undefined : + ret; + } + }, + + removeAttr: function( elem, value ) { + var propName, attrNames, name, l, + i = 0; + + if ( elem.nodeType === 1 ) { + attrNames = ( value || "" ).split( rspace ); + l = attrNames.length; + + for ( ; i < l; i++ ) { + name = attrNames[ i ].toLowerCase(); + propName = jQuery.propFix[ name ] || name; + + // See #9699 for explanation of this approach (setting first, then removal) + jQuery.attr( elem, name, "" ); + elem.removeAttribute( getSetAttribute ? name : propName ); + + // Set corresponding property to false for boolean attributes + if ( rboolean.test( name ) && propName in elem ) { + elem[ propName ] = false; + } + } + } + }, + + attrHooks: { + type: { + set: function( elem, value ) { + // We can't allow the type property to be changed (since it causes problems in IE) + if ( rtype.test( elem.nodeName ) && elem.parentNode ) { + jQuery.error( "type property can't be changed" ); + } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { + // Setting the type on a radio button after the value resets the value in IE6-9 + // Reset value to it's default in case type is set after value + // This is for element creation + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + }, + // Use the value property for back compat + // Use the nodeHook for button elements in IE6/7 (#1954) + value: { + get: function( elem, name ) { + if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { + return nodeHook.get( elem, name ); + } + return name in elem ? + elem.value : + null; + }, + set: function( elem, value, name ) { + if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { + return nodeHook.set( elem, value, name ); + } + // Does not return so that setAttribute is also used + elem.value = value; + } + } + }, + + propFix: { + tabindex: "tabIndex", + readonly: "readOnly", + "for": "htmlFor", + "class": "className", + maxlength: "maxLength", + cellspacing: "cellSpacing", + cellpadding: "cellPadding", + rowspan: "rowSpan", + colspan: "colSpan", + usemap: "useMap", + frameborder: "frameBorder", + contenteditable: "contentEditable" + }, + + prop: function( elem, name, value ) { + var ret, hooks, notxml, + nType = elem.nodeType; + + // don't get/set properties on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return undefined; + } + + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + if ( notxml ) { + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + return ( elem[ name ] = value ); + } + + } else { + if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + return elem[ name ]; + } + } + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set + // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + var attributeNode = elem.getAttributeNode("tabindex"); + + return attributeNode && attributeNode.specified ? + parseInt( attributeNode.value, 10 ) : + rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? + 0 : + undefined; + } + } + } +}); + +// Add the tabIndex propHook to attrHooks for back-compat (different case is intentional) +jQuery.attrHooks.tabindex = jQuery.propHooks.tabIndex; + +// Hook for boolean attributes +boolHook = { + get: function( elem, name ) { + // Align boolean attributes with corresponding properties + // Fall back to attribute presence where some booleans are not supported + var attrNode, + property = jQuery.prop( elem, name ); + return property === true || typeof property !== "boolean" && ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ? + name.toLowerCase() : + undefined; + }, + set: function( elem, value, name ) { + var propName; + if ( value === false ) { + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + // value is true since we know at this point it's type boolean and not false + // Set boolean attributes to the same name and set the DOM property + propName = jQuery.propFix[ name ] || name; + if ( propName in elem ) { + // Only set the IDL specifically if it already exists on the element + elem[ propName ] = true; + } + + elem.setAttribute( name, name.toLowerCase() ); + } + return name; + } +}; + +// IE6/7 do not support getting/setting some attributes with get/setAttribute +if ( !getSetAttribute ) { + + fixSpecified = { + name: true, + id: true + }; + + // Use this for any attribute in IE6/7 + // This fixes almost every IE6/7 issue + nodeHook = jQuery.valHooks.button = { + get: function( elem, name ) { + var ret; + ret = elem.getAttributeNode( name ); + return ret && ( fixSpecified[ name ] ? ret.nodeValue !== "" : ret.specified ) ? + ret.nodeValue : + undefined; + }, + set: function( elem, value, name ) { + // Set the existing or create a new attribute node + var ret = elem.getAttributeNode( name ); + if ( !ret ) { + ret = document.createAttribute( name ); + elem.setAttributeNode( ret ); + } + return ( ret.nodeValue = value + "" ); + } + }; + + // Apply the nodeHook to tabindex + jQuery.attrHooks.tabindex.set = nodeHook.set; + + // Set width and height to auto instead of 0 on empty string( Bug #8150 ) + // This is for removals + jQuery.each([ "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + set: function( elem, value ) { + if ( value === "" ) { + elem.setAttribute( name, "auto" ); + return value; + } + } + }); + }); + + // Set contenteditable to false on removals(#10429) + // Setting to empty string throws an error as an invalid value + jQuery.attrHooks.contenteditable = { + get: nodeHook.get, + set: function( elem, value, name ) { + if ( value === "" ) { + value = "false"; + } + nodeHook.set( elem, value, name ); + } + }; +} + + +// Some attributes require a special call on IE +if ( !jQuery.support.hrefNormalized ) { + jQuery.each([ "href", "src", "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + get: function( elem ) { + var ret = elem.getAttribute( name, 2 ); + return ret === null ? undefined : ret; + } + }); + }); +} + +if ( !jQuery.support.style ) { + jQuery.attrHooks.style = { + get: function( elem ) { + // Return undefined in the case of empty string + // Normalize to lowercase since IE uppercases css property names + return elem.style.cssText.toLowerCase() || undefined; + }, + set: function( elem, value ) { + return ( elem.style.cssText = "" + value ); + } + }; +} + +// Safari mis-reports the default selected property of an option +// Accessing the parent's selectedIndex property fixes it +if ( !jQuery.support.optSelected ) { + jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, { + get: function( elem ) { + var parent = elem.parentNode; + + if ( parent ) { + parent.selectedIndex; + + // Make sure that it also works with optgroups, see #5701 + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + return null; + } + }); +} + +// IE6/7 call enctype encoding +if ( !jQuery.support.enctype ) { + jQuery.propFix.enctype = "encoding"; +} + +// Radios and checkboxes getter/setter +if ( !jQuery.support.checkOn ) { + jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + get: function( elem ) { + // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified + return elem.getAttribute("value") === null ? "on" : elem.value; + } + }; + }); +} +jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], { + set: function( elem, value ) { + if ( jQuery.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 ); + } + } + }); +}); + + + + +var rnamespaces = /\.(.*)$/, + rformElems = /^(?:textarea|input|select)$/i, + rperiod = /\./g, + rspaces = / /g, + rescape = /[^\w\s.|`]/g, + rtypenamespace = /^([^\.]*)?(?:\.(.+))?$/, + rhoverHack = /\bhover(\.\S+)?/, + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|contextmenu)|click/, + rquickIs = /^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/, + quickParse = function( selector ) { + var quick = rquickIs.exec( selector ); + if ( quick ) { + // 0 1 2 3 + // [ _, tag, id, class ] + quick[1] = ( quick[1] || "" ).toLowerCase(); + quick[3] = quick[3] && new RegExp( "(?:^|\\s)" + quick[3] + "(?:\\s|$)" ); + } + return quick; + }, + quickIs = function( elem, m ) { + return ( + (!m[1] || elem.nodeName.toLowerCase() === m[1]) && + (!m[2] || elem.id === m[2]) && + (!m[3] || m[3].test( elem.className )) + ); + }, + hoverHack = function( events ) { + return jQuery.event.special.hover ? events : events.replace( rhoverHack, "mouseenter$1 mouseleave$1" ); + }; + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + add: function( elem, types, handler, data, selector ) { + + var elemData, eventHandle, events, + t, tns, type, namespaces, handleObj, + handleObjIn, quick, handlers, special; + + // Don't attach events to noData or text/comment nodes (allow plain objects tho) + if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = jQuery._data( elem )) ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + events = elemData.events; + if ( !events ) { + elemData.events = events = {}; + } + eventHandle = elemData.handle; + if ( !eventHandle ) { + elemData.handle = eventHandle = function( e ) { + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ? + jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : + undefined; + }; + // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events + eventHandle.elem = elem; + } + + // Handle multiple events separated by a space + // jQuery(...).bind("mouseover mouseout", fn); + types = hoverHack(types).split( " " ); + for ( t = 0; t < types.length; t++ ) { + + tns = rtypenamespace.exec( types[t] ) || []; + type = tns[1]; + namespaces = ( tns[2] || "" ).split( "." ).sort(); + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend({ + type: type, + origType: tns[1], + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + namespace: namespaces.join(".") + }, handleObjIn ); + + // Delegated event; pre-analyze selector so it's processed quickly on event dispatch + if ( selector ) { + handleObj.quick = quickParse( selector ); + if ( !handleObj.quick && jQuery.expr.match.POS.test( selector ) ) { + handleObj.isPositional = true; + } + } + + // Init the event handler queue if we're the first + handlers = events[ type ]; + if ( !handlers ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener/attachEvent if the special events handler returns false + if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + // Bind the global event handler to the element + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + // Nullify elem to prevent memory leaks in IE + elem = null; + }, + + global: {}, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector ) { + + var elemData = jQuery.hasData( elem ) && jQuery._data( elem ), + t, tns, type, namespaces, origCount, + j, events, special, handle, eventType, handleObj; + + if ( !elemData || !(events = elemData.events) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = hoverHack( types || "" ).split(" "); + for ( t = 0; t < types.length; t++ ) { + tns = rtypenamespace.exec( types[t] ) || []; + type = tns[1]; + namespaces = tns[2]; + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + namespaces = namespaces? "." + namespaces : ""; + for ( j in events ) { + jQuery.event.remove( elem, j + namespaces, handler, selector ); + } + return; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector? special.delegateType : special.bindType ) || type; + eventType = events[ type ] || []; + origCount = eventType.length; + namespaces = namespaces ? new RegExp("(^|\\.)" + namespaces.split(".").sort().join("\\.(?:.*\\.)?") + "(\\.|$)") : null; + + // Only need to loop for special events or selective removal + if ( handler || namespaces || selector || special.remove ) { + for ( j = 0; j < eventType.length; j++ ) { + handleObj = eventType[ j ]; + + if ( !handler || handler.guid === handleObj.guid ) { + if ( !namespaces || namespaces.test( handleObj.namespace ) ) { + if ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) { + eventType.splice( j--, 1 ); + + if ( handleObj.selector ) { + eventType.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + } + } + } else { + // Removing all events + eventType.length = 0; + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( eventType.length === 0 && origCount !== eventType.length ) { + if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) { + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + handle = elemData.handle; + if ( handle ) { + handle.elem = null; + } + + // removeData also checks for emptiness and clears the expando if empty + // so use it instead of delete + jQuery.removeData( elem, [ "events", "handle" ], true ); + } + }, + + // Events that are safe to short-circuit if no handlers are attached. + // Native DOM events should not be added, they may have inline handlers. + customEvent: { + "getData": true, + "setData": true, + "changeData": true + }, + + trigger: function( event, data, elem, onlyHandlers ) { + // Don't do events on text and comment nodes + if ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) { + return; + } + + // Event object or event type + var type = event.type || event, + namespaces = [], + cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType; + + if ( type.indexOf( "!" ) >= 0 ) { + // Exclusive events trigger only for the exact event (no namespaces) + type = type.slice(0, -1); + exclusive = true; + } + + if ( type.indexOf( "." ) >= 0 ) { + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split("."); + type = namespaces.shift(); + namespaces.sort(); + } + + if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) { + // No jQuery handlers for this event type, and it can't have inline handlers + return; + } + + // Caller can pass in an Event, Object, or just an event type string + event = typeof event === "object" ? + // jQuery.Event object + event[ jQuery.expando ] ? event : + // Object literal + new jQuery.Event( type, event ) : + // Just the event type (string) + new jQuery.Event( type ); + + event.type = type; + event.isTrigger = true; + event.exclusive = exclusive; + event.namespace = namespaces.join( "." ); + event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)") : null; + ontype = type.indexOf( ":" ) < 0 ? "on" + type : ""; + + // triggerHandler() and global events don't bubble or run the default action + if ( onlyHandlers || !elem ) { + event.preventDefault(); + } + + // Handle a global trigger + if ( !elem ) { + + // TODO: Stop taunting the data cache; remove global events and always attach to document + cache = jQuery.cache; + for ( i in cache ) { + if ( cache[ i ].events && cache[ i ].events[ type ] ) { + jQuery.event.trigger( event, data, cache[ i ].handle.elem, true ); + } + } + return; + } + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data != null ? jQuery.makeArray( data ) : []; + data.unshift( event ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + eventPath = [[ elem, special.bindType || type ]]; + if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + old = null; + for ( cur = elem.parentNode; cur; cur = cur.parentNode ) { + eventPath.push([ cur, bubbleType ]); + old = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( old && old === elem.ownerDocument ) { + eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]); + } + } + + // Fire handlers on the event path + for ( i = 0; i < eventPath.length; i++ ) { + + cur = eventPath[i][0]; + event.type = eventPath[i][1]; + + handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + handle = ontype && cur[ ontype ]; + if ( handle && jQuery.acceptData( cur ) ) { + handle.apply( cur, data ); + } + + if ( event.isPropagationStopped() ) { + break; + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !event.isDefaultPrevented() ) { + + if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) && + !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name name as the event. + // Can't use an .isFunction() check here because IE6/7 fails that test. + // Don't do default actions on window, that's where global variables be (#6170) + // IE<9 dies on focus/blur to hidden element (#1486) + if ( ontype && elem[ type ] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + old = elem[ ontype ]; + + if ( old ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + elem[ type ](); + jQuery.event.triggered = undefined; + + if ( old ) { + elem[ ontype ] = old; + } + } + } + } + + return event.result; + }, + + dispatch: function( event ) { + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( event || window.event ); + + var handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []), + delegateCount = handlers.delegateCount, + args = [].slice.call( arguments, 0 ), + run_all = !event.exclusive && !event.namespace, + specialHandle = ( jQuery.event.special[ event.type ] || {} ).handle, + handlerQueue = [], + i, j, cur, ret, selMatch, matched, matches, handleObj, sel, hit, related; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[0] = event; + event.delegateTarget = this; + + // Determine handlers that should run if there are delegated events + // Avoid disabled elements in IE (#6911) and non-left-click bubbling in Firefox (#3861) + if ( delegateCount && !event.target.disabled && !(event.button && event.type === "click") ) { + + for ( cur = event.target; cur != this; cur = cur.parentNode || this ) { + selMatch = {}; + matches = []; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + sel = handleObj.selector; + hit = selMatch[ sel ]; + + if ( handleObj.isPositional ) { + // Since .is() does not work for positionals; see http://jsfiddle.net/eJ4yd/3/ + hit = ( hit || (selMatch[ sel ] = jQuery( sel )) ).index( cur ) >= 0; + } else if ( hit === undefined ) { + hit = selMatch[ sel ] = ( handleObj.quick ? quickIs( cur, handleObj.quick ) : jQuery( cur ).is( sel ) ); + } + if ( hit ) { + matches.push( handleObj ); + } + } + if ( matches.length ) { + handlerQueue.push({ elem: cur, matches: matches }); + } + } + } + + // Add the remaining (directly-bound) handlers + if ( handlers.length > delegateCount ) { + handlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) }); + } + + // Run delegates first; they may want to stop propagation beneath us + for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) { + matched = handlerQueue[ i ]; + event.currentTarget = matched.elem; + + for ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) { + handleObj = matched.matches[ j ]; + + // Triggered event must either 1) be non-exclusive and have no namespace, or + // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). + if ( run_all || (!event.namespace && !handleObj.namespace) || event.namespace_re && event.namespace_re.test( handleObj.namespace ) ) { + + event.data = handleObj.data; + event.handleObj = handleObj; + + ret = ( specialHandle || handleObj.handler ).apply( matched.elem, args ); + + if ( ret !== undefined ) { + event.result = ret; + if ( ret === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + return event.result; + }, + + // Includes some event props shared by KeyEvent and MouseEvent + // *** attrChange attrName relatedNode srcElement are not normalized, non-W3C, deprecated, will be removed in 1.8 *** + props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), + + fixHooks: {}, + + keyHooks: { + props: "char charCode key keyCode".split(" "), + filter: function( event, original ) { + + // Add which for key events + if ( event.which == null ) { + event.which = original.charCode != null ? original.charCode : original.keyCode; + } + + return event; + } + }, + + mouseHooks: { + props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement wheelDelta".split(" "), + filter: function( event, original ) { + var eventDoc, doc, body, + button = original.button, + fromElement = original.fromElement; + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && original.clientX != null ) { + eventDoc = event.target.ownerDocument || document; + doc = eventDoc.documentElement; + body = eventDoc.body; + + event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); + event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); + } + + // Add relatedTarget, if necessary + if ( !event.relatedTarget && fromElement ) { + event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && button !== undefined ) { + event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); + } + + return event; + } + }, + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // Create a writable copy of the event object and normalize some properties + var i, prop, + originalEvent = event, + fixHook = jQuery.event.fixHooks[ event.type ] || {}, + copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; + + event = jQuery.Event( originalEvent ); + + for ( i = copy.length; i; ) { + prop = copy[ --i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2) + if ( !event.target ) { + event.target = originalEvent.srcElement || document; + } + + // Target should not be a text node (#504, Safari) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + // For mouse/key events; add metaKey if it's not there (#3368, IE6/7/8) + if ( event.metaKey === undefined ) { + event.metaKey = event.ctrlKey; + } + + return fixHook.filter? fixHook.filter( event, originalEvent ) : event; + }, + + special: { + ready: { + // Make sure the ready event is setup + setup: jQuery.bindReady + }, + + focus: { + delegateType: "focusin", + noBubble: true + }, + blur: { + delegateType: "focusout", + noBubble: true + }, + + beforeunload: { + setup: function( data, namespaces, eventHandle ) { + // We only want to do this special case on windows + if ( jQuery.isWindow( this ) ) { + this.onbeforeunload = eventHandle; + } + }, + + teardown: function( namespaces, eventHandle ) { + if ( this.onbeforeunload === eventHandle ) { + this.onbeforeunload = null; + } + } + } + }, + + simulate: function( type, elem, event, bubble ) { + // Piggyback on a donor event to simulate a different one. + // Fake originalEvent to avoid donor's stopPropagation, but if the + // simulated event prevents default then we do the same on the donor. + var e = jQuery.extend( + new jQuery.Event(), + event, + { type: type, + isSimulated: true, + originalEvent: {} + } + ); + if ( bubble ) { + jQuery.event.trigger( e, null, elem ); + } else { + jQuery.event.dispatch.call( elem, e ); + } + if ( e.isDefaultPrevented() ) { + event.preventDefault(); + } + } +}; + +// Some plugins are using, but it's undocumented/deprecated and will be removed. +// The 1.7 special event interface should provide all the hooks needed now. +jQuery.event.handle = jQuery.event.dispatch; + +jQuery.removeEvent = document.removeEventListener ? + function( elem, type, handle ) { + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } + } : + function( elem, type, handle ) { + if ( elem.detachEvent ) { + elem.detachEvent( "on" + type, handle ); + } + }; + +jQuery.Event = function( src, props ) { + // Allow instantiation without the 'new' keyword + if ( !(this instanceof jQuery.Event) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false || + src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +function returnFalse() { + return false; +} +function returnTrue() { + return true; +} + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + preventDefault: function() { + this.isDefaultPrevented = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + + // if preventDefault exists run it on the original event + if ( e.preventDefault ) { + e.preventDefault(); + + // otherwise set the returnValue property of the original event to false (IE) + } else { + e.returnValue = false; + } + }, + stopPropagation: function() { + this.isPropagationStopped = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + // if stopPropagation exists run it on the original event + if ( e.stopPropagation ) { + e.stopPropagation(); + } + // otherwise set the cancelBubble property of the original event to true (IE) + e.cancelBubble = true; + }, + stopImmediatePropagation: function() { + this.isImmediatePropagationStopped = returnTrue; + this.stopPropagation(); + }, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse +}; + +// Create mouseenter/leave events using mouseover/out and event-time checks +jQuery.each({ + mouseenter: "mouseover", + mouseleave: "mouseout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = jQuery.event.special[ fix ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var target = this, + related = event.relatedTarget, + handleObj = event.handleObj, + selector = handleObj.selector, + oldType, ret; + + // For a real mouseover/out, always call the handler; for + // mousenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || handleObj.origType === event.type || (related !== target && !jQuery.contains( target, related )) ) { + oldType = event.type; + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = oldType; + } + return ret; + } + }; +}); + +// IE submit delegation +if ( !jQuery.support.submitBubbles ) { + + jQuery.event.special.submit = { + setup: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Lazy-add a submit handler when a descendant form may potentially be submitted + jQuery.event.add( this, "click._submit keypress._submit", function( e ) { + // Node name check avoids a VML-related crash in IE (#9807) + var elem = e.target, + form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; + if ( form && !form._submit_attached ) { + jQuery.event.add( form, "submit._submit", function( event ) { + // Form was submitted, bubble the event up the tree + if ( this.parentNode ) { + jQuery.event.simulate( "submit", this.parentNode, event, true ); + } + }); + form._submit_attached = true; + } + }); + // return undefined since we don't need an event listener + }, + + teardown: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Remove delegated handlers; cleanData eventually reaps submit handlers attached above + jQuery.event.remove( this, "._submit" ); + } + }; +} + +// IE change delegation and checkbox/radio fix +if ( !jQuery.support.changeBubbles ) { + + jQuery.event.special.change = { + + setup: function() { + + if ( rformElems.test( this.nodeName ) ) { + // IE doesn't fire change on a check/radio until blur; trigger it on click + // after a propertychange. Eat the blur-change in special.change.handle. + // This still fires onchange a second time for check/radio after blur. + if ( this.type === "checkbox" || this.type === "radio" ) { + jQuery.event.add( this, "propertychange._change", function( event ) { + if ( event.originalEvent.propertyName === "checked" ) { + this._just_changed = true; + } + }); + jQuery.event.add( this, "click._change", function( event ) { + if ( this._just_changed ) { + this._just_changed = false; + jQuery.event.simulate( "change", this, event, true ); + } + }); + } + return false; + } + // Delegated event; lazy-add a change handler on descendant inputs + jQuery.event.add( this, "beforeactivate._change", function( e ) { + var elem = e.target; + + if ( rformElems.test( elem.nodeName ) && !elem._change_attached ) { + jQuery.event.add( elem, "change._change", function( event ) { + if ( this.parentNode && !event.isSimulated ) { + jQuery.event.simulate( "change", this.parentNode, event, true ); + } + }); + elem._change_attached = true; + } + }); + }, + + handle: function( event ) { + var elem = event.target; + + // Swallow native change events from checkbox/radio, we already triggered them above + if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) { + return event.handleObj.handler.apply( this, arguments ); + } + }, + + teardown: function() { + jQuery.event.remove( this, "._change" ); + + return rformElems.test( this.nodeName ); + } + }; +} + +// Create "bubbling" focus and blur events +if ( !jQuery.support.focusinBubbles ) { + jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler while someone wants focusin/focusout + var attaches = 0, + handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + if ( attaches++ === 0 ) { + document.addEventListener( orig, handler, true ); + } + }, + teardown: function() { + if ( --attaches === 0 ) { + document.removeEventListener( orig, handler, true ); + } + } + }; + }); +} + +jQuery.fn.extend({ + + on: function( types, selector, data, fn, /*INTERNAL*/ one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + // ( types-Object, data ) + data = selector; + selector = undefined; + } + for ( type in types ) { + this.on( type, selector, data, types[ type ], one ); + } + return this; + } + + if ( data == null && fn == null ) { + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return this; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return this.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + }); + }, + one: function( types, selector, data, fn ) { + return this.on.call( this, types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + if ( types && types.preventDefault && types.handleObj ) { + // ( event ) dispatched jQuery.Event + var handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace? handleObj.type + "." + handleObj.namespace : handleObj.type, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + // ( types-object [, selector] ) + for ( var type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each(function() { + jQuery.event.remove( this, types, fn, selector ); + }); + }, + + bind: function( types, data, fn ) { + return this.on( types, null, data, fn ); + }, + unbind: function( types, fn ) { + return this.off( types, null, fn ); + }, + + live: function( types, data, fn ) { + jQuery( this.context ).on( types, this.selector, data, fn ); + return this; + }, + die: function( types, fn ) { + jQuery( this.context ).off( types, this.selector || "**", fn ); + return this; + }, + + delegate: function( selector, types, data, fn ) { + return this.on( types, selector, data, fn ); + }, + undelegate: function( selector, types, fn ) { + // ( namespace ) or ( selector, types [, fn] ) + return arguments.length == 1? this.off( selector, "**" ) : this.off( types, selector, fn ); + }, + + trigger: function( type, data ) { + return this.each(function() { + jQuery.event.trigger( type, data, this ); + }); + }, + triggerHandler: function( type, data ) { + if ( this[0] ) { + return jQuery.event.trigger( type, data, this[0], true ); + } + }, + + toggle: function( fn ) { + // Save reference to arguments for access in closure + var args = arguments, + guid = fn.guid || jQuery.guid++, + i = 0, + toggler = function( event ) { + // Figure out which function to execute + var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i; + jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 ); + + // Make sure that clicks stop + event.preventDefault(); + + // and execute the function + return args[ lastToggle ].apply( this, arguments ) || false; + }; + + // link all the functions, so any of them can unbind this click handler + toggler.guid = guid; + while ( i < args.length ) { + args[ i++ ].guid = guid; + } + + return this.click( toggler ); + }, + + hover: function( fnOver, fnOut ) { + return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); + } +}); + +jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + + "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) { + + // Handle event binding + jQuery.fn[ name ] = function( data, fn ) { + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; + + if ( jQuery.attrFn ) { + jQuery.attrFn[ name ] = true; + } + + if ( rkeyEvent.test( name ) ) { + jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks; + } + + if ( rmouseEvent.test( name ) ) { + jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks; + } +}); + + + +/*! + * Sizzle CSS Selector Engine + * Copyright 2011, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * More information: http://sizzlejs.com/ + */ +(function(){ + +var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, + expando = "sizcache" + (Math.random() + '').replace('.', ''), + done = 0, + toString = Object.prototype.toString, + hasDuplicate = false, + baseHasDuplicate = true, + rBackslash = /\\/g, + rReturn = /\r\n/g, + rNonWord = /\W/; + +// Here we check if the JavaScript engine is using some sort of +// optimization where it does not always call our comparision +// function. If that is the case, discard the hasDuplicate value. +// Thus far that includes Google Chrome. +[0, 0].sort(function() { + baseHasDuplicate = false; + return 0; +}); + +var Sizzle = function( selector, context, results, seed ) { + results = results || []; + context = context || document; + + var origContext = context; + + if ( context.nodeType !== 1 && context.nodeType !== 9 ) { + return []; + } + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + var m, set, checkSet, extra, ret, cur, pop, i, + prune = true, + contextXML = Sizzle.isXML( context ), + parts = [], + soFar = selector; + + // Reset the position of the chunker regexp (start from head) + do { + chunker.exec( "" ); + m = chunker.exec( soFar ); + + if ( m ) { + soFar = m[3]; + + parts.push( m[1] ); + + if ( m[2] ) { + extra = m[3]; + break; + } + } + } while ( m ); + + if ( parts.length > 1 && origPOS.exec( selector ) ) { + + if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { + set = posProcess( parts[0] + parts[1], context, seed ); + + } else { + set = Expr.relative[ parts[0] ] ? + [ context ] : + Sizzle( parts.shift(), context ); + + while ( parts.length ) { + selector = parts.shift(); + + if ( Expr.relative[ selector ] ) { + selector += parts.shift(); + } + + set = posProcess( selector, set, seed ); + } + } + + } else { + // Take a shortcut and set the context if the root selector is an ID + // (but not if it'll be faster if the inner selector is an ID) + if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML && + Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) { + + ret = Sizzle.find( parts.shift(), context, contextXML ); + context = ret.expr ? + Sizzle.filter( ret.expr, ret.set )[0] : + ret.set[0]; + } + + if ( context ) { + ret = seed ? + { expr: parts.pop(), set: makeArray(seed) } : + Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML ); + + set = ret.expr ? + Sizzle.filter( ret.expr, ret.set ) : + ret.set; + + if ( parts.length > 0 ) { + checkSet = makeArray( set ); + + } else { + prune = false; + } + + while ( parts.length ) { + cur = parts.pop(); + pop = cur; + + if ( !Expr.relative[ cur ] ) { + cur = ""; + } else { + pop = parts.pop(); + } + + if ( pop == null ) { + pop = context; + } + + Expr.relative[ cur ]( checkSet, pop, contextXML ); + } + + } else { + checkSet = parts = []; + } + } + + if ( !checkSet ) { + checkSet = set; + } + + if ( !checkSet ) { + Sizzle.error( cur || selector ); + } + + if ( toString.call(checkSet) === "[object Array]" ) { + if ( !prune ) { + results.push.apply( results, checkSet ); + + } else if ( context && context.nodeType === 1 ) { + for ( i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) { + results.push( set[i] ); + } + } + + } else { + for ( i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && checkSet[i].nodeType === 1 ) { + results.push( set[i] ); + } + } + } + + } else { + makeArray( checkSet, results ); + } + + if ( extra ) { + Sizzle( extra, origContext, results, seed ); + Sizzle.uniqueSort( results ); + } + + return results; +}; + +Sizzle.uniqueSort = function( results ) { + if ( sortOrder ) { + hasDuplicate = baseHasDuplicate; + results.sort( sortOrder ); + + if ( hasDuplicate ) { + for ( var i = 1; i < results.length; i++ ) { + if ( results[i] === results[ i - 1 ] ) { + results.splice( i--, 1 ); + } + } + } + } + + return results; +}; + +Sizzle.matches = function( expr, set ) { + return Sizzle( expr, null, null, set ); +}; + +Sizzle.matchesSelector = function( node, expr ) { + return Sizzle( expr, null, null, [node] ).length > 0; +}; + +Sizzle.find = function( expr, context, isXML ) { + var set, i, len, match, type, left; + + if ( !expr ) { + return []; + } + + for ( i = 0, len = Expr.order.length; i < len; i++ ) { + type = Expr.order[i]; + + if ( (match = Expr.leftMatch[ type ].exec( expr )) ) { + left = match[1]; + match.splice( 1, 1 ); + + if ( left.substr( left.length - 1 ) !== "\\" ) { + match[1] = (match[1] || "").replace( rBackslash, "" ); + set = Expr.find[ type ]( match, context, isXML ); + + if ( set != null ) { + expr = expr.replace( Expr.match[ type ], "" ); + break; + } + } + } + } + + if ( !set ) { + set = typeof context.getElementsByTagName !== "undefined" ? + context.getElementsByTagName( "*" ) : + []; + } + + return { set: set, expr: expr }; +}; + +Sizzle.filter = function( expr, set, inplace, not ) { + var match, anyFound, + type, found, item, filter, left, + i, pass, + old = expr, + result = [], + curLoop = set, + isXMLFilter = set && set[0] && Sizzle.isXML( set[0] ); + + while ( expr && set.length ) { + for ( type in Expr.filter ) { + if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) { + filter = Expr.filter[ type ]; + left = match[1]; + + anyFound = false; + + match.splice(1,1); + + if ( left.substr( left.length - 1 ) === "\\" ) { + continue; + } + + if ( curLoop === result ) { + result = []; + } + + if ( Expr.preFilter[ type ] ) { + match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); + + if ( !match ) { + anyFound = found = true; + + } else if ( match === true ) { + continue; + } + } + + if ( match ) { + for ( i = 0; (item = curLoop[i]) != null; i++ ) { + if ( item ) { + found = filter( item, match, i, curLoop ); + pass = not ^ found; + + if ( inplace && found != null ) { + if ( pass ) { + anyFound = true; + + } else { + curLoop[i] = false; + } + + } else if ( pass ) { + result.push( item ); + anyFound = true; + } + } + } + } + + if ( found !== undefined ) { + if ( !inplace ) { + curLoop = result; + } + + expr = expr.replace( Expr.match[ type ], "" ); + + if ( !anyFound ) { + return []; + } + + break; + } + } + } + + // Improper expression + if ( expr === old ) { + if ( anyFound == null ) { + Sizzle.error( expr ); + + } else { + break; + } + } + + old = expr; + } + + return curLoop; +}; + +Sizzle.error = function( msg ) { + throw "Syntax error, unrecognized expression: " + msg; +}; + +/** + * Utility function for retreiving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +var getText = Sizzle.getText = function( elem ) { + var i, node, + nodeType = elem.nodeType, + ret = ""; + + if ( nodeType ) { + if ( nodeType === 1 ) { + // Use textContent || innerText for elements + if ( typeof elem.textContent === 'string' ) { + return elem.textContent; + } else if ( typeof elem.innerText === 'string' ) { + // Replace IE's carriage returns + return elem.innerText.replace( rReturn, '' ); + } else { + // Traverse it's children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + } else { + + // If no nodeType, this is expected to be an array + for ( i = 0; (node = elem[i]); i++ ) { + // Do not traverse comment nodes + if ( node.nodeType !== 8 ) { + ret += getText( node ); + } + } + } + return ret; +}; + +var Expr = Sizzle.selectors = { + order: [ "ID", "NAME", "TAG" ], + + match: { + ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/, + ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/, + TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/, + CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/, + POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/, + PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/ + }, + + leftMatch: {}, + + attrMap: { + "class": "className", + "for": "htmlFor" + }, + + attrHandle: { + href: function( elem ) { + return elem.getAttribute( "href" ); + }, + type: function( elem ) { + return elem.getAttribute( "type" ); + } + }, + + relative: { + "+": function(checkSet, part){ + var isPartStr = typeof part === "string", + isTag = isPartStr && !rNonWord.test( part ), + isPartStrNotTag = isPartStr && !isTag; + + if ( isTag ) { + part = part.toLowerCase(); + } + + for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { + if ( (elem = checkSet[i]) ) { + while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} + + checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ? + elem || false : + elem === part; + } + } + + if ( isPartStrNotTag ) { + Sizzle.filter( part, checkSet, true ); + } + }, + + ">": function( checkSet, part ) { + var elem, + isPartStr = typeof part === "string", + i = 0, + l = checkSet.length; + + if ( isPartStr && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + + for ( ; i < l; i++ ) { + elem = checkSet[i]; + + if ( elem ) { + var parent = elem.parentNode; + checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false; + } + } + + } else { + for ( ; i < l; i++ ) { + elem = checkSet[i]; + + if ( elem ) { + checkSet[i] = isPartStr ? + elem.parentNode : + elem.parentNode === part; + } + } + + if ( isPartStr ) { + Sizzle.filter( part, checkSet, true ); + } + } + }, + + "": function(checkSet, part, isXML){ + var nodeCheck, + doneName = done++, + checkFn = dirCheck; + + if ( typeof part === "string" && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + nodeCheck = part; + checkFn = dirNodeCheck; + } + + checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML ); + }, + + "~": function( checkSet, part, isXML ) { + var nodeCheck, + doneName = done++, + checkFn = dirCheck; + + if ( typeof part === "string" && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + nodeCheck = part; + checkFn = dirNodeCheck; + } + + checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML ); + } + }, + + find: { + ID: function( match, context, isXML ) { + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [m] : []; + } + }, + + NAME: function( match, context ) { + if ( typeof context.getElementsByName !== "undefined" ) { + var ret = [], + results = context.getElementsByName( match[1] ); + + for ( var i = 0, l = results.length; i < l; i++ ) { + if ( results[i].getAttribute("name") === match[1] ) { + ret.push( results[i] ); + } + } + + return ret.length === 0 ? null : ret; + } + }, + + TAG: function( match, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( match[1] ); + } + } + }, + preFilter: { + CLASS: function( match, curLoop, inplace, result, not, isXML ) { + match = " " + match[1].replace( rBackslash, "" ) + " "; + + if ( isXML ) { + return match; + } + + for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { + if ( elem ) { + if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) { + if ( !inplace ) { + result.push( elem ); + } + + } else if ( inplace ) { + curLoop[i] = false; + } + } + } + + return false; + }, + + ID: function( match ) { + return match[1].replace( rBackslash, "" ); + }, + + TAG: function( match, curLoop ) { + return match[1].replace( rBackslash, "" ).toLowerCase(); + }, + + CHILD: function( match ) { + if ( match[1] === "nth" ) { + if ( !match[2] ) { + Sizzle.error( match[0] ); + } + + match[2] = match[2].replace(/^\+|\s*/g, ''); + + // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6' + var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec( + match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" || + !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); + + // calculate the numbers (first)n+(last) including if they are negative + match[2] = (test[1] + (test[2] || 1)) - 0; + match[3] = test[3] - 0; + } + else if ( match[2] ) { + Sizzle.error( match[0] ); + } + + // TODO: Move to normal caching system + match[0] = done++; + + return match; + }, + + ATTR: function( match, curLoop, inplace, result, not, isXML ) { + var name = match[1] = match[1].replace( rBackslash, "" ); + + if ( !isXML && Expr.attrMap[name] ) { + match[1] = Expr.attrMap[name]; + } + + // Handle if an un-quoted value was used + match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" ); + + if ( match[2] === "~=" ) { + match[4] = " " + match[4] + " "; + } + + return match; + }, + + PSEUDO: function( match, curLoop, inplace, result, not ) { + if ( match[1] === "not" ) { + // If we're dealing with a complex expression, or a simple one + if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) { + match[3] = Sizzle(match[3], null, null, curLoop); + + } else { + var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); + + if ( !inplace ) { + result.push.apply( result, ret ); + } + + return false; + } + + } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { + return true; + } + + return match; + }, + + POS: function( match ) { + match.unshift( true ); + + return match; + } + }, + + filters: { + enabled: function( elem ) { + return elem.disabled === false && elem.type !== "hidden"; + }, + + disabled: function( elem ) { + return elem.disabled === true; + }, + + checked: function( elem ) { + return elem.checked === true; + }, + + selected: function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + parent: function( elem ) { + return !!elem.firstChild; + }, + + empty: function( elem ) { + return !elem.firstChild; + }, + + has: function( elem, i, match ) { + return !!Sizzle( match[3], elem ).length; + }, + + header: function( elem ) { + return (/h\d/i).test( elem.nodeName ); + }, + + text: function( elem ) { + var attr = elem.getAttribute( "type" ), type = elem.type; + // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) + // use getAttribute instead to test this case + return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null ); + }, + + radio: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type; + }, + + checkbox: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type; + }, + + file: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "file" === elem.type; + }, + + password: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "password" === elem.type; + }, + + submit: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && "submit" === elem.type; + }, + + image: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "image" === elem.type; + }, + + reset: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && "reset" === elem.type; + }, + + button: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && "button" === elem.type || name === "button"; + }, + + input: function( elem ) { + return (/input|select|textarea|button/i).test( elem.nodeName ); + }, + + focus: function( elem ) { + return elem === elem.ownerDocument.activeElement; + } + }, + setFilters: { + first: function( elem, i ) { + return i === 0; + }, + + last: function( elem, i, match, array ) { + return i === array.length - 1; + }, + + even: function( elem, i ) { + return i % 2 === 0; + }, + + odd: function( elem, i ) { + return i % 2 === 1; + }, + + lt: function( elem, i, match ) { + return i < match[3] - 0; + }, + + gt: function( elem, i, match ) { + return i > match[3] - 0; + }, + + nth: function( elem, i, match ) { + return match[3] - 0 === i; + }, + + eq: function( elem, i, match ) { + return match[3] - 0 === i; + } + }, + filter: { + PSEUDO: function( elem, match, i, array ) { + var name = match[1], + filter = Expr.filters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + + } else if ( name === "contains" ) { + return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0; + + } else if ( name === "not" ) { + var not = match[3]; + + for ( var j = 0, l = not.length; j < l; j++ ) { + if ( not[j] === elem ) { + return false; + } + } + + return true; + + } else { + Sizzle.error( name ); + } + }, + + CHILD: function( elem, match ) { + var first, last, + doneName, parent, cache, + count, diff, + type = match[1], + node = elem; + + switch ( type ) { + case "only": + case "first": + while ( (node = node.previousSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + if ( type === "first" ) { + return true; + } + + node = elem; + + case "last": + while ( (node = node.nextSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + return true; + + case "nth": + first = match[2]; + last = match[3]; + + if ( first === 1 && last === 0 ) { + return true; + } + + doneName = match[0]; + parent = elem.parentNode; + + if ( parent && (parent[ expando ] !== doneName || !elem.nodeIndex) ) { + count = 0; + + for ( node = parent.firstChild; node; node = node.nextSibling ) { + if ( node.nodeType === 1 ) { + node.nodeIndex = ++count; + } + } + + parent[ expando ] = doneName; + } + + diff = elem.nodeIndex - last; + + if ( first === 0 ) { + return diff === 0; + + } else { + return ( diff % first === 0 && diff / first >= 0 ); + } + } + }, + + ID: function( elem, match ) { + return elem.nodeType === 1 && elem.getAttribute("id") === match; + }, + + TAG: function( elem, match ) { + return (match === "*" && elem.nodeType === 1) || !!elem.nodeName && elem.nodeName.toLowerCase() === match; + }, + + CLASS: function( elem, match ) { + return (" " + (elem.className || elem.getAttribute("class")) + " ") + .indexOf( match ) > -1; + }, + + ATTR: function( elem, match ) { + var name = match[1], + result = Sizzle.attr ? + Sizzle.attr( elem, name ) : + Expr.attrHandle[ name ] ? + Expr.attrHandle[ name ]( elem ) : + elem[ name ] != null ? + elem[ name ] : + elem.getAttribute( name ), + value = result + "", + type = match[2], + check = match[4]; + + return result == null ? + type === "!=" : + !type && Sizzle.attr ? + result != null : + type === "=" ? + value === check : + type === "*=" ? + value.indexOf(check) >= 0 : + type === "~=" ? + (" " + value + " ").indexOf(check) >= 0 : + !check ? + value && result !== false : + type === "!=" ? + value !== check : + type === "^=" ? + value.indexOf(check) === 0 : + type === "$=" ? + value.substr(value.length - check.length) === check : + type === "|=" ? + value === check || value.substr(0, check.length + 1) === check + "-" : + false; + }, + + POS: function( elem, match, i, array ) { + var name = match[2], + filter = Expr.setFilters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + } + } + } +}; + +var origPOS = Expr.match.POS, + fescape = function(all, num){ + return "\\" + (num - 0 + 1); + }; + +for ( var type in Expr.match ) { + Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) ); + Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) ); +} + +var makeArray = function( array, results ) { + array = Array.prototype.slice.call( array, 0 ); + + if ( results ) { + results.push.apply( results, array ); + return results; + } + + return array; +}; + +// Perform a simple check to determine if the browser is capable of +// converting a NodeList to an array using builtin methods. +// Also verifies that the returned array holds DOM nodes +// (which is not the case in the Blackberry browser) +try { + Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType; + +// Provide a fallback method if it does not work +} catch( e ) { + makeArray = function( array, results ) { + var i = 0, + ret = results || []; + + if ( toString.call(array) === "[object Array]" ) { + Array.prototype.push.apply( ret, array ); + + } else { + if ( typeof array.length === "number" ) { + for ( var l = array.length; i < l; i++ ) { + ret.push( array[i] ); + } + + } else { + for ( ; array[i]; i++ ) { + ret.push( array[i] ); + } + } + } + + return ret; + }; +} + +var sortOrder, siblingCheck; + +if ( document.documentElement.compareDocumentPosition ) { + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) { + return a.compareDocumentPosition ? -1 : 1; + } + + return a.compareDocumentPosition(b) & 4 ? -1 : 1; + }; + +} else { + sortOrder = function( a, b ) { + // The nodes are identical, we can exit early + if ( a === b ) { + hasDuplicate = true; + return 0; + + // Fallback to using sourceIndex (in IE) if it's available on both nodes + } else if ( a.sourceIndex && b.sourceIndex ) { + return a.sourceIndex - b.sourceIndex; + } + + var al, bl, + ap = [], + bp = [], + aup = a.parentNode, + bup = b.parentNode, + cur = aup; + + // If the nodes are siblings (or identical) we can do a quick check + if ( aup === bup ) { + return siblingCheck( a, b ); + + // If no parents were found then the nodes are disconnected + } else if ( !aup ) { + return -1; + + } else if ( !bup ) { + return 1; + } + + // Otherwise they're somewhere else in the tree so we need + // to build up a full list of the parentNodes for comparison + while ( cur ) { + ap.unshift( cur ); + cur = cur.parentNode; + } + + cur = bup; + + while ( cur ) { + bp.unshift( cur ); + cur = cur.parentNode; + } + + al = ap.length; + bl = bp.length; + + // Start walking down the tree looking for a discrepancy + for ( var i = 0; i < al && i < bl; i++ ) { + if ( ap[i] !== bp[i] ) { + return siblingCheck( ap[i], bp[i] ); + } + } + + // We ended someplace up the tree so do a sibling check + return i === al ? + siblingCheck( a, bp[i], -1 ) : + siblingCheck( ap[i], b, 1 ); + }; + + siblingCheck = function( a, b, ret ) { + if ( a === b ) { + return ret; + } + + var cur = a.nextSibling; + + while ( cur ) { + if ( cur === b ) { + return -1; + } + + cur = cur.nextSibling; + } + + return 1; + }; +} + +// Check to see if the browser returns elements by name when +// querying by getElementById (and provide a workaround) +(function(){ + // We're going to inject a fake input element with a specified name + var form = document.createElement("div"), + id = "script" + (new Date()).getTime(), + root = document.documentElement; + + form.innerHTML = ""; + + // Inject it into the root element, check its status, and remove it quickly + root.insertBefore( form, root.firstChild ); + + // The workaround has to do additional checks after a getElementById + // Which slows things down for other browsers (hence the branching) + if ( document.getElementById( id ) ) { + Expr.find.ID = function( match, context, isXML ) { + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + + return m ? + m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? + [m] : + undefined : + []; + } + }; + + Expr.filter.ID = function( elem, match ) { + var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); + + return elem.nodeType === 1 && node && node.nodeValue === match; + }; + } + + root.removeChild( form ); + + // release memory in IE + root = form = null; +})(); + +(function(){ + // Check to see if the browser returns only elements + // when doing getElementsByTagName("*") + + // Create a fake element + var div = document.createElement("div"); + div.appendChild( document.createComment("") ); + + // Make sure no comments are found + if ( div.getElementsByTagName("*").length > 0 ) { + Expr.find.TAG = function( match, context ) { + var results = context.getElementsByTagName( match[1] ); + + // Filter out possible comments + if ( match[1] === "*" ) { + var tmp = []; + + for ( var i = 0; results[i]; i++ ) { + if ( results[i].nodeType === 1 ) { + tmp.push( results[i] ); + } + } + + results = tmp; + } + + return results; + }; + } + + // Check to see if an attribute returns normalized href attributes + div.innerHTML = ""; + + if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && + div.firstChild.getAttribute("href") !== "#" ) { + + Expr.attrHandle.href = function( elem ) { + return elem.getAttribute( "href", 2 ); + }; + } + + // release memory in IE + div = null; +})(); + +if ( document.querySelectorAll ) { + (function(){ + var oldSizzle = Sizzle, + div = document.createElement("div"), + id = "__sizzle__"; + + div.innerHTML = "

"; + + // Safari can't handle uppercase or unicode characters when + // in quirks mode. + if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { + return; + } + + Sizzle = function( query, context, extra, seed ) { + context = context || document; + + // Only use querySelectorAll on non-XML documents + // (ID selectors don't work in non-HTML documents) + if ( !seed && !Sizzle.isXML(context) ) { + // See if we find a selector to speed up + var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query ); + + if ( match && (context.nodeType === 1 || context.nodeType === 9) ) { + // Speed-up: Sizzle("TAG") + if ( match[1] ) { + return makeArray( context.getElementsByTagName( query ), extra ); + + // Speed-up: Sizzle(".CLASS") + } else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) { + return makeArray( context.getElementsByClassName( match[2] ), extra ); + } + } + + if ( context.nodeType === 9 ) { + // Speed-up: Sizzle("body") + // The body element only exists once, optimize finding it + if ( query === "body" && context.body ) { + return makeArray( [ context.body ], extra ); + + // Speed-up: Sizzle("#ID") + } else if ( match && match[3] ) { + var elem = context.getElementById( match[3] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id === match[3] ) { + return makeArray( [ elem ], extra ); + } + + } else { + return makeArray( [], extra ); + } + } + + try { + return makeArray( context.querySelectorAll(query), extra ); + } catch(qsaError) {} + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + var oldContext = context, + old = context.getAttribute( "id" ), + nid = old || id, + hasParent = context.parentNode, + relativeHierarchySelector = /^\s*[+~]/.test( query ); + + if ( !old ) { + context.setAttribute( "id", nid ); + } else { + nid = nid.replace( /'/g, "\\$&" ); + } + if ( relativeHierarchySelector && hasParent ) { + context = context.parentNode; + } + + try { + if ( !relativeHierarchySelector || hasParent ) { + return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra ); + } + + } catch(pseudoError) { + } finally { + if ( !old ) { + oldContext.removeAttribute( "id" ); + } + } + } + } + + return oldSizzle(query, context, extra, seed); + }; + + for ( var prop in oldSizzle ) { + Sizzle[ prop ] = oldSizzle[ prop ]; + } + + // release memory in IE + div = null; + })(); +} + +(function(){ + var html = document.documentElement, + matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector; + + if ( matches ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9 fails this) + var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ), + pseudoWorks = false; + + try { + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( document.documentElement, "[test!='']:sizzle" ); + + } catch( pseudoError ) { + pseudoWorks = true; + } + + Sizzle.matchesSelector = function( node, expr ) { + // Make sure that attribute selectors are quoted + expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']"); + + if ( !Sizzle.isXML( node ) ) { + try { + if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) { + var ret = matches.call( node, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || !disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9, so check for that + node.document && node.document.nodeType !== 11 ) { + return ret; + } + } + } catch(e) {} + } + + return Sizzle(expr, null, null, [node]).length > 0; + }; + } +})(); + +(function(){ + var div = document.createElement("div"); + + div.innerHTML = "
"; + + // Opera can't find a second classname (in 9.6) + // Also, make sure that getElementsByClassName actually exists + if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) { + return; + } + + // Safari caches class attributes, doesn't catch changes (in 3.2) + div.lastChild.className = "e"; + + if ( div.getElementsByClassName("e").length === 1 ) { + return; + } + + Expr.order.splice(1, 0, "CLASS"); + Expr.find.CLASS = function( match, context, isXML ) { + if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { + return context.getElementsByClassName(match[1]); + } + }; + + // release memory in IE + div = null; +})(); + +function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + + if ( elem ) { + var match = false; + + elem = elem[dir]; + + while ( elem ) { + if ( elem[ expando ] === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 && !isXML ){ + elem[ expando ] = doneName; + elem.sizset = i; + } + + if ( elem.nodeName.toLowerCase() === cur ) { + match = elem; + break; + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + + if ( elem ) { + var match = false; + + elem = elem[dir]; + + while ( elem ) { + if ( elem[ expando ] === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 ) { + if ( !isXML ) { + elem[ expando ] = doneName; + elem.sizset = i; + } + + if ( typeof cur !== "string" ) { + if ( elem === cur ) { + match = true; + break; + } + + } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { + match = elem; + break; + } + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +if ( document.documentElement.contains ) { + Sizzle.contains = function( a, b ) { + return a !== b && (a.contains ? a.contains(b) : true); + }; + +} else if ( document.documentElement.compareDocumentPosition ) { + Sizzle.contains = function( a, b ) { + return !!(a.compareDocumentPosition(b) & 16); + }; + +} else { + Sizzle.contains = function() { + return false; + }; +} + +Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement; + + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +var posProcess = function( selector, context, seed ) { + var match, + tmpSet = [], + later = "", + root = context.nodeType ? [context] : context; + + // Position selectors must be done after the filter + // And so must :not(positional) so we move all PSEUDOs to the end + while ( (match = Expr.match.PSEUDO.exec( selector )) ) { + later += match[0]; + selector = selector.replace( Expr.match.PSEUDO, "" ); + } + + selector = Expr.relative[selector] ? selector + "*" : selector; + + for ( var i = 0, l = root.length; i < l; i++ ) { + Sizzle( selector, root[i], tmpSet, seed ); + } + + return Sizzle.filter( later, tmpSet ); +}; + +// EXPOSE +// Override sizzle attribute retrieval +Sizzle.attr = jQuery.attr; +Sizzle.selectors.attrMap = {}; +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[":"] = jQuery.expr.filters; +jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + + +})(); + + +var runtil = /Until$/, + rparentsprev = /^(?:parents|prevUntil|prevAll)/, + // Note: This RegExp should be improved, or likely pulled from Sizzle + rmultiselector = /,/, + isSimple = /^.[^:#\[\.,]*$/, + slice = Array.prototype.slice, + POS = jQuery.expr.match.POS, + // methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend({ + find: function( selector ) { + var self = this, + i, l; + + if ( typeof selector !== "string" ) { + return jQuery( selector ).filter(function() { + for ( i = 0, l = self.length; i < l; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + }); + } + + var ret = this.pushStack( "", "find", selector ), + length, n, r; + + for ( i = 0, l = this.length; i < l; i++ ) { + length = ret.length; + jQuery.find( selector, this[i], ret ); + + if ( i > 0 ) { + // Make sure that the results are unique + for ( n = length; n < ret.length; n++ ) { + for ( r = 0; r < length; r++ ) { + if ( ret[r] === ret[n] ) { + ret.splice(n--, 1); + break; + } + } + } + } + } + + return ret; + }, + + has: function( target ) { + var targets = jQuery( target ); + return this.filter(function() { + for ( var i = 0, l = targets.length; i < l; i++ ) { + if ( jQuery.contains( this, targets[i] ) ) { + return true; + } + } + }); + }, + + not: function( selector ) { + return this.pushStack( winnow(this, selector, false), "not", selector); + }, + + filter: function( selector ) { + return this.pushStack( winnow(this, selector, true), "filter", selector ); + }, + + is: function( selector ) { + return !!selector && ( + typeof selector === "string" ? + // If this is a positional selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + POS.test( selector ) ? + jQuery( selector, this.context ).index( this[0] ) >= 0 : + jQuery.filter( selector, this ).length > 0 : + this.filter( selector ).length > 0 ); + }, + + closest: function( selectors, context ) { + var ret = [], i, l, cur = this[0]; + + // Array (deprecated as of jQuery 1.7) + if ( jQuery.isArray( selectors ) ) { + var level = 1; + + while ( cur && cur.ownerDocument && cur !== context ) { + for ( i = 0; i < selectors.length; i++ ) { + + if ( jQuery( cur ).is( selectors[ i ] ) ) { + ret.push({ selector: selectors[ i ], elem: cur, level: level }); + } + } + + cur = cur.parentNode; + level++; + } + + return ret; + } + + // String + var pos = POS.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; + + for ( i = 0, l = this.length; i < l; i++ ) { + cur = this[i]; + + while ( cur ) { + if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) { + ret.push( cur ); + break; + + } else { + cur = cur.parentNode; + if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) { + break; + } + } + } + } + + ret = ret.length > 1 ? jQuery.unique( ret ) : ret; + + return this.pushStack( ret, "closest", selectors ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1; + } + + // index in selector + if ( typeof elem === "string" ) { + return jQuery.inArray( this[0], jQuery( elem ) ); + } + + // Locate the position of the desired element + return jQuery.inArray( + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[0] : elem, this ); + }, + + add: function( selector, context ) { + var set = typeof selector === "string" ? + jQuery( selector, context ) : + jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), + all = jQuery.merge( this.get(), set ); + + return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ? + all : + jQuery.unique( all ) ); + }, + + andSelf: function() { + return this.add( this.prevObject ); + } +}); + +// A painfully simple check to see if an element is disconnected +// from a document (should be improved, where feasible). +function isDisconnected( node ) { + return !node || !node.parentNode || node.parentNode.nodeType === 11; +} + +jQuery.each({ + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return jQuery.nth( elem, 2, "nextSibling" ); + }, + prev: function( elem ) { + return jQuery.nth( elem, 2, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( elem.parentNode.firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return jQuery.nodeName( elem, "iframe" ) ? + elem.contentDocument || elem.contentWindow.document : + jQuery.makeArray( elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var ret = jQuery.map( this, fn, until ), + // The variable 'args' was introduced in + // https://github.com/jquery/jquery/commit/52a0238 + // to work around a bug in Chrome 10 (Dev) and should be removed when the bug is fixed. + // http://code.google.com/p/v8/issues/detail?id=1050 + args = slice.call(arguments); + + if ( !runtil.test( name ) ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + ret = jQuery.filter( selector, ret ); + } + + ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret; + + if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) { + ret = ret.reverse(); + } + + return this.pushStack( ret, name, args.join(",") ); + }; +}); + +jQuery.extend({ + filter: function( expr, elems, not ) { + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 ? + jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] : + jQuery.find.matches(expr, elems); + }, + + dir: function( elem, dir, until ) { + var matched = [], + cur = elem[ dir ]; + + while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { + if ( cur.nodeType === 1 ) { + matched.push( cur ); + } + cur = cur[dir]; + } + return matched; + }, + + nth: function( cur, result, dir, elem ) { + result = result || 1; + var num = 0; + + for ( ; cur; cur = cur[dir] ) { + if ( cur.nodeType === 1 && ++num === result ) { + break; + } + } + + return cur; + }, + + sibling: function( n, elem ) { + var r = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + r.push( n ); + } + } + + return r; + } +}); + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, keep ) { + + // Can't pass null or undefined to indexOf in Firefox 4 + // Set to 0 to skip string check + qualifier = qualifier || 0; + + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep(elements, function( elem, i ) { + var retVal = !!qualifier.call( elem, i, elem ); + return retVal === keep; + }); + + } else if ( qualifier.nodeType ) { + return jQuery.grep(elements, function( elem, i ) { + return ( elem === qualifier ) === keep; + }); + + } else if ( typeof qualifier === "string" ) { + var filtered = jQuery.grep(elements, function( elem ) { + return elem.nodeType === 1; + }); + + if ( isSimple.test( qualifier ) ) { + return jQuery.filter(qualifier, filtered, !keep); + } else { + qualifier = jQuery.filter( qualifier, filtered ); + } + } + + return jQuery.grep(elements, function( elem, i ) { + return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep; + }); +} + + + + +function createSafeFragment( document ) { + var list = nodeNames.split( " " ), + safeFrag = document.createDocumentFragment(); + + if ( safeFrag.createElement ) { + while ( list.length ) { + safeFrag.createElement( + list.pop() + ); + } + } + return safeFrag; +} + +var nodeNames = "abbr article aside audio canvas datalist details figcaption figure footer " + + "header hgroup mark meter nav output progress section summary time video", + rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g, + rleadingWhitespace = /^\s+/, + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig, + rtagName = /<([\w:]+)/, + rtbody = /", "" ], + legend: [ 1, "
", "
" ], + thead: [ 1, "", "
" ], + tr: [ 2, "", "
" ], + td: [ 3, "", "
" ], + col: [ 2, "", "
" ], + area: [ 1, "", "" ], + _default: [ 0, "", "" ] + }, + safeFragment = createSafeFragment( document ); + +wrapMap.optgroup = wrapMap.option; +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +// IE can't serialize and + + + + + + \ No newline at end of file diff --git a/js/js.examples/src/html5/fancyLines/fancylines.js b/js/js.examples/src/html5/fancyLines/fancylines.js new file mode 100644 index 00000000000..27e7e39a3a3 --- /dev/null +++ b/js/js.examples/src/html5/fancyLines/fancylines.js @@ -0,0 +1,103 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +{ + var classes = function () { + var FancyLines = Kotlin.Class.create({initialize:function () { + this.$context = getContext(); + this.$height = getCanvas().height; + this.$width = getCanvas().width; + this.$x = this.get_width() * Math.random(); + this.$y = this.get_height() * Math.random(); + this.$hue = 0; + }, get_context:function () { + return this.$context; + }, get_height:function () { + return this.$height; + }, get_width:function () { + return this.$width; + }, get_x:function () { + return this.$x; + }, set_x:function (tmp$0) { + this.$x = tmp$0; + }, get_y:function () { + return this.$y; + }, set_y:function (tmp$0) { + this.$y = tmp$0; + }, get_hue:function () { + return this.$hue; + }, set_hue:function (tmp$0) { + this.$hue = tmp$0; + }, line:function () { + { + this.get_context().save(); + this.get_context().beginPath(); + this.get_context().lineWidth = 20 * Math.random(); + this.get_context().moveTo(this.get_x(), this.get_y()); + this.set_x(this.get_width() * Math.random()); + this.set_y(this.get_height() * Math.random()); + this.get_context().bezierCurveTo(this.get_width() * Math.random(), this.get_height() * Math.random(), this.get_width() * Math.random(), this.get_height() * Math.random(), this.get_x(), this.get_y()); + this.set_hue(this.get_hue() + Math.random() * 10); + this.get_context().strokeStyle = 'hsl(' + this.get_hue() + ', 50%, 50%)'; + this.get_context().shadowColor = 'white'; + this.get_context().shadowBlur = 10; + this.get_context().stroke(); + this.get_context().restore(); + } + }, blank:function () { + { + this.get_context().fillStyle = 'rgba(255,255,1,0.1)'; + this.get_context().fillRect(0, 0, this.get_width(), this.get_height()); + } + }, run:function () { + { + var tmp$1; + var tmp$0; + setInterval((tmp$0 = this , function () { + { + tmp$0.line(); + } + } + ), 40); + setInterval((tmp$1 = this , function () { + { + tmp$1.blank(); + } + } + ), 100); + } + } + }); + return {FancyLines_0:FancyLines}; + } + (); + var fancylines = Kotlin.Namespace.create({initialize:function () { + }, main:function () { + { + $(function () { + { + (new fancylines.FancyLines_0).run(); + } + } + ); + } + } + }, classes); + fancylines.initialize(); +} + +var args = []; +fancylines.main(args); diff --git a/js/js.examples/src/html5/fancyLines/template.html b/js/js.examples/src/html5/fancyLines/template.html new file mode 100644 index 00000000000..64a717ea926 --- /dev/null +++ b/js/js.examples/src/html5/fancyLines/template.html @@ -0,0 +1,30 @@ + + + + + + Canvas demo + + + + + + + + + \ No newline at end of file diff --git a/js/js.examples/src/html5/fancyLines/test.kt b/js/js.examples/src/html5/fancyLines/test.kt new file mode 100644 index 00000000000..892bc4695cc --- /dev/null +++ b/js/js.examples/src/html5/fancyLines/test.kt @@ -0,0 +1,64 @@ +/* +This example is based on example from html5 canvas2D docs: + http://www.w3.org/TR/2dcontext/ +Note that only a subset of the api is supported for now. +*/ + +package fancylines + +import js.*; +import html5.*; +import jquery.*; + +fun main() { + //jq is a name for JQuery function + jq { + FancyLines().run(); + } +} + +class FancyLines() { + // we use two 'magic' functions here getContext() and getCanvas() + val context = getContext(); + val height = getCanvas().height; + val width = getCanvas().width; + var x = width * Math.random(); + var y = height * Math.random(); + var hue = 0; + + fun line() { + context.save(); + + context.beginPath(); + + context.lineWidth = 20.0 * Math.random(); + context.moveTo(x, y); + + x = width * Math.random(); + y = height * Math.random(); + + context.bezierCurveTo(width * Math.random(), height * Math.random(), + width * Math.random(), height * Math.random(), x, y); + + hue += Math.random() * 10; + + context.strokeStyle = "hsl($hue, 50%, 50%)"; + + context.shadowColor = "white"; + context.shadowBlur = 10.0; + + context.stroke(); + + context.restore(); + } + + fun blank() { + context.fillStyle = "rgba(255,255,1,0.1)"; + context.fillRect(0.0, 0.0, width, height); + } + + fun run() { + setInterval({line()}, 40); + setInterval({blank()}, 100); + } +} \ No newline at end of file diff --git a/js/js.examples/src/html5/floatingText/hello.js b/js/js.examples/src/html5/floatingText/hello.js new file mode 100644 index 00000000000..289ebedd579 --- /dev/null +++ b/js/js.examples/src/html5/floatingText/hello.js @@ -0,0 +1,162 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +{ + var classes = function () { + var HelloKotlin = Kotlin.Class.create({initialize:function () { + this.$relX = 0.2 + 0.2 * Math.random(); + this.$relY = 0.4 + 0.2 * Math.random(); + this.$relXVelocity = this.randomVelocity(); + this.$relYVelocity = this.randomVelocity(); + this.$message = 'Hello, Kotlin!'; + this.$textHeightInPixels = 60; + { + hello.get_context().font = 'bold ' + this.get_textHeightInPixels() + 'px Georgia, serif'; + } + this.$textWidthInPixels = hello.get_context().measureText(this.get_message()).width; + }, get_relX:function () { + return this.$relX; + }, set_relX:function (tmp$0) { + this.$relX = tmp$0; + }, get_relY:function () { + return this.$relY; + }, set_relY:function (tmp$0) { + this.$relY = tmp$0; + }, get_absX:function () { + { + return this.get_relX() * hello.get_width(); + } + }, get_absY:function () { + { + return this.get_relY() * hello.get_height(); + } + }, get_relXVelocity:function () { + return this.$relXVelocity; + }, set_relXVelocity:function (tmp$0) { + this.$relXVelocity = tmp$0; + }, get_relYVelocity:function () { + return this.$relYVelocity; + }, set_relYVelocity:function (tmp$0) { + this.$relYVelocity = tmp$0; + }, get_message:function () { + return this.$message; + }, get_textHeightInPixels:function () { + return this.$textHeightInPixels; + }, get_textWidthInPixels:function () { + return this.$textWidthInPixels; + }, draw:function () { + { + hello.get_context().save(); + this.move(); + hello.get_context().shadowColor = '#000000'; + hello.get_context().shadowBlur = 5; + hello.get_context().shadowOffsetX = -4; + hello.get_context().shadowOffsetY = 4; + hello.get_context().fillStyle = 'rgb(242,160,110)'; + hello.get_context().fillText(this.get_message(), this.get_absX(), this.get_absY()); + hello.get_context().restore(); + } + }, move:function () { + { + var relTextWidth = this.get_textWidthInPixels() / hello.get_width(); + if (this.get_relX() > 1 - relTextWidth - this.get_abs(this.get_relXVelocity()) || this.get_relX() < this.get_abs(this.get_relXVelocity())) { + this.set_relXVelocity(this.get_relXVelocity() * -1); + } + var relTextHeight = this.get_textHeightInPixels() / hello.get_height(); + if (this.get_relY() > 1 - this.get_abs(this.get_relYVelocity()) || this.get_relY() < this.get_abs(this.get_relYVelocity()) + relTextHeight) { + this.set_relYVelocity(this.get_relYVelocity() * -1); + } + this.set_relX(this.get_relX() + this.get_relXVelocity()); + this.set_relY(this.get_relY() + this.get_relYVelocity()); + } + }, randomVelocity:function () { + var tmp$0; + if (Math.random() < 0.5) + tmp$0 = 1; + else + tmp$0 = -1; + { + return 0.03 * Math.random() * tmp$0; + } + }, get_abs:function (receiver) { + var tmp$0; + if (receiver > 0) + tmp$0 = receiver; + else + tmp$0 = -receiver; + { + return tmp$0; + } + } + }); + return {HelloKotlin_0:HelloKotlin}; + } + (); + var hello = Kotlin.Namespace.create({initialize:function () { + hello.$context = getContext(); + hello.$height = getCanvas().height; + hello.$width = getCanvas().width; + }, get_context:function () { + return hello.$context; + }, get_height:function () { + return hello.$height; + }, get_width:function () { + return hello.$width; + }, renderBackground:function () { + { + hello.get_context().save(); + hello.get_context().fillStyle = '#5C7EED'; + hello.get_context().fillRect(0, 0, hello.get_width(), hello.get_height()); + hello.get_context().restore(); + } + }, main:function () { + { + var interval = 50; + var logos = Kotlin.arrayFromFun(3, function (it) { + { + return new hello.HelloKotlin_0; + } + } + ); + $(function () { + { + setInterval(function () { + { + var tmp$0; + hello.renderBackground(); + { + tmp$0 = Kotlin.arrayIterator(logos); + while (tmp$0.hasNext()) { + var logo = tmp$0.next(); + { + logo.draw(); + } + } + } + } + } + , interval); + } + } + ); + } + } + }, classes); + hello.initialize(); +} + +var args = []; +hello.main(args); diff --git a/js/js.examples/src/html5/floatingText/template.html b/js/js.examples/src/html5/floatingText/template.html new file mode 100644 index 00000000000..557ab5c8b6f --- /dev/null +++ b/js/js.examples/src/html5/floatingText/template.html @@ -0,0 +1,30 @@ + + + + + + Canvas demo + + + + + + + + + \ No newline at end of file diff --git a/js/js.examples/src/html5/floatingText/test.kt b/js/js.examples/src/html5/floatingText/test.kt new file mode 100644 index 00000000000..e5bca3ea4d9 --- /dev/null +++ b/js/js.examples/src/html5/floatingText/test.kt @@ -0,0 +1,91 @@ +/* + This example is just simple text floating around. If u are using chrome, there is a bug that spoil the visuals. +*/ +package hello + +import js.* +import html5.* +import jquery.* + +val context = getContext() +val height = getCanvas().height +val width = getCanvas().width + +// class representing a floating text +class HelloKotlin() { + var relX = 0.2 + 0.2 * Math.random() + var relY = 0.4 + 0.2 * Math.random() + + val absX : Double + get() = (relX * width) + val absY : Double + get() = (relY * height) + + var relXVelocity = randomVelocity() + var relYVelocity = randomVelocity() + + + val message = "Hello, Kotlin!" + val textHeightInPixels = 60 + { + context.font = "bold ${textHeightInPixels}px Georgia, serif" + } + val textWidthInPixels = context.measureText(message).width + + fun draw() { + context.save() + move() + // if you using chrome chances are good you wont see the shadow + context.shadowColor = "#000000" + context.shadowBlur = 5.0 + context.shadowOffsetX = -4.0 + context.shadowOffsetY = 4.0 + context.fillStyle = "rgb(242,160,110)" + context.fillText(message, absX, absY) + context.restore() + } + + fun move() { + val relTextWidth = textWidthInPixels / width + if (relX > (1.0 - relTextWidth - relXVelocity.abs) || relX < relXVelocity.abs) { + relXVelocity *= -1 + } + val relTextHeight = textHeightInPixels / height + if (relY > (1.0 - relYVelocity.abs) || relY < relYVelocity.abs + relTextHeight) { + relYVelocity *= -1 + } + relX += relXVelocity + relY += relYVelocity + } + + fun randomVelocity() = 0.03 * Math.random() * (if (Math.random() < 0.5) 1 else -1) + + + val Double.abs : Double + get() = if (this > 0) this else -this +} + +fun renderBackground() { + context.save() + context.fillStyle = "#5C7EED" + context.fillRect(0.0, 0.0, width, height) + context.restore() +} + +fun main() { + val interval = 50 + // we pass a literal that constructs a new HelloKotlin object + val logos = Array(3) { + HelloKotlin() + } + jq { + setInterval({ + renderBackground() + for (logo in logos) { + logo.draw() + } + }, interval) + } +} + + diff --git a/js/js.examples/src/html5/interactive/interactive.js b/js/js.examples/src/html5/interactive/interactive.js new file mode 100644 index 00000000000..e87a2d66315 --- /dev/null +++ b/js/js.examples/src/html5/interactive/interactive.js @@ -0,0 +1,339 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +{ + var classes = function () { + var Vector = Kotlin.Class.create({initialize:function (x, y) { + this.$x = x; + this.$y = y; + }, get_x:function () { + return this.$x; + }, set_x:function (tmp$0) { + this.$x = tmp$0; + }, get_y:function () { + return this.$y; + }, set_y:function (tmp$0) { + this.$y = tmp$0; + }, plus:function (v) { + { + return interactive.v_0(this.get_x() + v.get_x(), this.get_y() + v.get_y()); + } + }, minus:function (v) { + { + return interactive.v_0(this.get_x() - v.get_x(), this.get_y() - v.get_y()); + } + }, times:function (coef) { + { + return interactive.v_0(this.get_x() * coef, this.get_y() * coef); + } + }, distanceTo:function (v) { + { + return Math.sqrt(this.minus(v).get_sqr()); + } + }, isInRect:function (topLeft, size) { + { + return this.get_x() >= topLeft.get_x() && this.get_x() <= topLeft.get_x() + size.get_x() && this.get_y() >= topLeft.get_y() && this.get_y() <= topLeft.get_y() + size.get_y(); + } + }, get_sqr:function () { + { + return this.get_x() * this.get_x() + this.get_y() * this.get_y(); + } + } + }); + var CanvasState = Kotlin.Class.create({initialize:function (canvas) { + this.$canvas = canvas; + this.$width = this.get_canvas().width; + this.$height = this.get_canvas().height; + this.$context = getContext(); + this.$valid = false; + this.$shapes = new Kotlin.ArrayList; + this.$selection = null; + this.$dragOff = new interactive.Vector_0(0, 0); + this.$interval = 1000 / 30; + this.$size = 20; + { + var tmp$4; + var tmp$3; + var tmp$2; + var tmp$1; + var tmp$0_0; + $(this.get_canvas()).mousedown((tmp$0_0 = this , function (it) { + { + var tmp$0; + tmp$0_0.set_valid(false); + tmp$0_0.set_selection(null); + var mousePos = tmp$0_0.mousePos_0(it); + { + tmp$0 = tmp$0_0.get_shapes().iterator(); + while (tmp$0.hasNext()) { + var shape = tmp$0.next(); + { + if (shape.contains(mousePos)) { + tmp$0_0.set_dragOff(mousePos.minus(shape.get_pos())); + shape.set_selected(true); + tmp$0_0.set_selection(shape); + break; + } + } + } + } + } + } + )); + $(this.get_canvas()).mousemove((tmp$1 = this , function (it) { + { + if (tmp$1.get_selection() != null) { + Kotlin.sure(tmp$1.get_selection()).set_pos(tmp$1.mousePos_0(it).minus(tmp$1.get_dragOff())); + tmp$1.set_valid(false); + } + } + } + )); + $(this.get_canvas()).mouseup((tmp$2 = this , function (it) { + { + if (tmp$2.get_selection() != null) { + Kotlin.sure(tmp$2.get_selection()).set_selected(false); + } + tmp$2.set_selection(null); + tmp$2.set_valid(false); + } + } + )); + $(this.get_canvas()).dblclick((tmp$3 = this , function (it) { + { + var redTransparentCircle = new interactive.Circle_0(tmp$3.mousePos_0(it), tmp$3.get_size(), 'rgba(200, 100, 100, 0.3)'); + tmp$3.addShape(redTransparentCircle); + tmp$3.set_valid(false); + } + } + )); + interactive.doWithPeriod(this.get_interval(), (tmp$4 = this , function () { + { + tmp$4.draw(); + } + } + )); + } + }, get_canvas:function () { + return this.$canvas; + }, get_width:function () { + return this.$width; + }, get_height:function () { + return this.$height; + }, get_context:function () { + return this.$context; + }, get_valid:function () { + return this.$valid; + }, set_valid:function (tmp$0) { + this.$valid = tmp$0; + }, get_shapes:function () { + return this.$shapes; + }, set_shapes:function (tmp$0) { + this.$shapes = tmp$0; + }, get_selection:function () { + return this.$selection; + }, set_selection:function (tmp$0) { + this.$selection = tmp$0; + }, get_dragOff:function () { + return this.$dragOff; + }, set_dragOff:function (tmp$0) { + this.$dragOff = tmp$0; + }, get_interval:function () { + return this.$interval; + }, get_size:function () { + return this.$size; + }, mousePos_0:function (e) { + { + var offset = new interactive.Vector_0(0, 0); + var element = this.get_canvas(); + while (element != null) { + var el = Kotlin.sure(element); + offset = offset.plus(new interactive.Vector_0(el.offsetLeft, el.offsetTop)); + element = el.offsetParent; + } + return (new interactive.Vector_0(e.pageX, e.pageY)).minus(offset); + } + }, addShape:function (shape) { + { + this.get_shapes().add(shape); + this.set_valid(false); + } + }, clear:function () { + { + this.get_context().fillStyle = '#FFFFFF'; + this.get_context().fillRect(0, 0, this.get_width(), this.get_height()); + this.get_context().strokeStyle = '#000000'; + this.get_context().lineWidth = 4; + this.get_context().strokeRect(0, 0, this.get_width(), this.get_height()); + } + }, draw:function () { + { + var tmp$0; + if (this.get_valid()) + return; + this.clear(); + { + tmp$0 = this.get_shapes().iterator(); + while (tmp$0.hasNext()) { + var shape = tmp$0.next(); + { + shape.draw(this); + } + } + } + this.set_valid(true); + } + } + }); + var Shape = Kotlin.Class.create({initialize:function () { + this.$selected = false; + }, draw:function (state) { + }, contains:function (mousePos) { + }, get_pos:function () { + return this.$pos; + }, set_pos:function (tmp$0) { + this.$pos = tmp$0; + }, get_selected:function () { + return this.$selected; + }, set_selected:function (tmp$0) { + this.$selected = tmp$0; + } + }); + var JB = Kotlin.Class.create(Shape, {initialize:function (pos, relSize) { + this.$pos = pos; + this.$relSize = relSize; + this.super_init(); + this.$imageSize = interactive.v_0(704, 254); + this.$canvasSize = this.get_imageSize().times(this.get_relSize()); + }, get_pos:function () { + return this.$pos; + }, set_pos:function (tmp$0) { + this.$pos = tmp$0; + }, get_relSize:function () { + return this.$relSize; + }, set_relSize:function (tmp$0) { + this.$relSize = tmp$0; + }, get_imageSize:function () { + return this.$imageSize; + }, get_canvasSize:function () { + return this.$canvasSize; + }, draw:function (state) { + { + var context = state.get_context(); + context.drawImage(getKotlinLogo(), 0, 0, this.get_imageSize().get_x(), this.get_imageSize().get_y(), this.get_pos().get_x(), this.get_pos().get_y(), this.get_canvasSize().get_x(), this.get_canvasSize().get_y()); + } + }, contains:function (mousePos) { + { + return mousePos.isInRect(this.get_pos(), this.get_canvasSize()); + } + } + }); + var Rectangle = Kotlin.Class.create(Shape, {initialize:function (pos, size) { + this.$pos = pos; + this.$size = size; + this.super_init(); + }, get_pos:function () { + return this.$pos; + }, set_pos:function (tmp$0) { + this.$pos = tmp$0; + }, get_size:function () { + return this.$size; + }, set_size:function (tmp$0) { + this.$size = tmp$0; + }, draw:function (state) { + { + var context = state.get_context(); + context.fillStyle = 'rgba(0,255,0,.6)'; + context.fillRect(this.get_pos().get_x(), this.get_pos().get_y(), this.get_size().get_x(), this.get_size().get_y()); + if (this.get_selected()) { + context.strokeStyle = '#FF0000'; + context.lineWidth = 2; + context.strokeRect(this.get_pos().get_x(), this.get_pos().get_y(), this.get_size().get_x(), this.get_size().get_y()); + } + } + }, contains:function (mousePos) { + { + return mousePos.isInRect(this.get_pos(), this.get_size()); + } + } + }); + var Circle = Kotlin.Class.create(Shape, {initialize:function (pos, radius, fillColor) { + this.$pos = pos; + this.$radius = radius; + this.$fillColor = fillColor; + this.super_init(); + }, get_pos:function () { + return this.$pos; + }, set_pos:function (tmp$0) { + this.$pos = tmp$0; + }, get_radius:function () { + return this.$radius; + }, set_radius:function (tmp$0) { + this.$radius = tmp$0; + }, get_fillColor:function () { + return this.$fillColor; + }, set_fillColor:function (tmp$0) { + this.$fillColor = tmp$0; + }, draw:function (state) { + { + var context = state.get_context(); + context.shadowColor = 'white'; + context.shadowBlur = 10; + context.fillStyle = this.get_fillColor(); + context.beginPath(); + context.arc(this.get_pos().get_x(), this.get_pos().get_y(), this.get_radius(), 0, 2 * Math.PI, false); + context.closePath(); + context.fill(); + } + }, contains:function (mousePos) { + { + return this.get_pos().distanceTo(mousePos) < this.get_radius(); + } + } + }); + return {Shape_0:Shape, JB_0:JB, Rectangle_0:Rectangle, Circle_0:Circle, CanvasState_0:CanvasState, Vector_0:Vector}; + } + (); + var interactive = Kotlin.Namespace.create({initialize:function () { + interactive.$state = new interactive.CanvasState_0(getCanvas()); + }, get_state:function () { + return interactive.$state; + }, doWithPeriod:function (period, f) { + { + setInterval(f, period); + } + }, v_0:function (x, y) { + { + return new interactive.Vector_0(x, y); + } + }, main:function () { + { + var state = new interactive.CanvasState_0(getCanvas()); + state.addShape(new interactive.JB_0(interactive.v_0(1, 1), 0.3)); + setTimeout(function () { + { + state.set_valid(false); + } + } + ); + } + } + }, classes); + interactive.initialize(); +} + +var args = []; +interactive.main(args); diff --git a/js/js.examples/src/html5/interactive/jbLogo.png b/js/js.examples/src/html5/interactive/jbLogo.png new file mode 100644 index 00000000000..759aa8ae05a Binary files /dev/null and b/js/js.examples/src/html5/interactive/jbLogo.png differ diff --git a/js/js.examples/src/html5/interactive/main.kt b/js/js.examples/src/html5/interactive/main.kt new file mode 100644 index 00000000000..86eb1c1fdfc --- /dev/null +++ b/js/js.examples/src/html5/interactive/main.kt @@ -0,0 +1,183 @@ +package interactive; + +import jquery.*; +import html5.*; +import java.util.ArrayList; +import js.*; +import js.Math; +import js.setInterval +import html5.getCanvas +import html5.getKotlinLogo +import jquery.jq +import js.setTimeout + +val state = CanvasState(getCanvas()); + +fun doWithPeriod(period : Int, f : ()->Unit) { + setInterval(f, period); +} + +abstract class Shape() { + abstract fun draw(state : CanvasState); + abstract fun contains(mousePos : Vector) : Boolean; + abstract var pos : Vector; + var selected : Boolean = false; +} + +class Circle(override var pos : Vector, + var radius : Double = 1.0, + var fillColor : String = "#AAAAAA") : Shape() { + + override fun draw(state : CanvasState) { + val context = state.context; + context.shadowColor = "white"; + context.shadowBlur = 10.0; + context.fillStyle = fillColor; + context.beginPath(); + context.arc(pos.x, pos.y, radius, 0.0, 2 * Math.PI, false); + context.closePath(); + context.fill(); + } + + override fun contains(mousePos : Vector) = pos.distanceTo(mousePos) < radius; +} + +class Rectangle(override var pos : Vector, + var size : Vector) : Shape() +{ + override fun draw(state : CanvasState) { + val context = state.context; + context.fillStyle = "rgba(0,255,0,.6)"; + context.fillRect(pos.x, pos.y, size.x, size.y); + if (selected) { + context.strokeStyle = "#FF0000" + context.lineWidth = 2.0; + context.strokeRect(pos.x, pos.y, size.x, size.y) + } + } + + override fun contains(mousePos : Vector) = mousePos.isInRect(pos, size); +} + +class JB(override var pos : Vector, + var relSize : Double = 0.3) : Shape() +{ + + val imageSize = v(704.0, 254.0) + val canvasSize = imageSize * relSize + + override fun draw(state : CanvasState) { + val context = state.context; + context.drawImage(getKotlinLogo(), 0.0, 0.0, imageSize.x, imageSize.y, pos.x, pos.y, canvasSize.x, canvasSize.y) + } + + override fun contains(mousePos: Vector): Boolean = mousePos.isInRect(pos, canvasSize) +} + +class CanvasState(val canvas : Canvas) { + val width = canvas.width; + val height = canvas.height; + val context = getContext(); + var valid = false; + var shapes = ArrayList(); + var selection : Shape? = null; + var dragOff = Vector(); + val interval = 1000 / 30; + val size = 20.0; + + { + jq(canvas).mousedown { + valid = false; + selection = null; + val mousePos = mousePos(it); + for (shape in shapes) { + if (mousePos in shape) { + dragOff = mousePos - shape.pos; + shape.selected = true; + selection = shape; + break; + } + } + } + + jq(canvas).mousemove { + if (selection != null) { + selection.sure().pos = mousePos(it) - dragOff; + valid = false; + } + } + + jq(canvas).mouseup { + if (selection != null) { + selection.sure().selected = false; + } + selection = null; + valid = false; + } + + jq(canvas).dblclick { + val redTransparentCircle = Circle(mousePos(it), size, "rgba(200, 100, 100, 0.3)"); + addShape(redTransparentCircle); + valid = false; + } + + doWithPeriod(interval) { + draw(); + } + } + + fun mousePos(e : MouseEvent) : Vector { + var offset = Vector(); + var element : DomElement? = canvas; + while (element != null) { + val el : DomElement = element.sure(); + offset += Vector(el.offsetLeft, el.offsetTop); + element = el.offsetParent; + } + return Vector(e.pageX, e.pageY) - offset; + } + + fun addShape(shape : Shape) { + shapes.add(shape); + valid = false; + } + + fun clear() { + context.fillStyle = "#FFFFFF" + context.fillRect(0.0, 0.0, width, height) + context.strokeStyle = "#000000" + context.lineWidth = 4.0; + context.strokeRect(0.0, 0.0, width, height) + } + + fun draw() { + if (valid) return + + clear(); + for (shape in shapes) { + shape.draw(this); + } + valid = true; + } +} + +fun v(x : Double, y : Double) = Vector(x, y); + +class Vector(var x : Double = 0.0, var y : Double = 0.0) { + fun plus(v : Vector) = v(x + v.x, y + v.y); + fun minus(v : Vector) = v(x - v.x, y - v.y); + fun times(coef : Double) = v(x * coef, y * coef); + fun distanceTo(v : Vector) = Math.sqrt((this - v).sqr); + fun isInRect(topLeft : Vector, size : Vector) = (x >= topLeft.x) && (x <= topLeft.x + size.x) && + (y >= topLeft.y) && (y <= topLeft.y + size.y) + val sqr : Double + get() = x * x + y * y; +} + +fun main() { + val state = CanvasState(getCanvas()); + state.addShape(JB(v(1.0, 1.0))); + setTimeout({ + state.valid = false + }) +} \ No newline at end of file diff --git a/js/js.examples/src/html5/interactive/template.html b/js/js.examples/src/html5/interactive/template.html new file mode 100644 index 00000000000..d976e3dafc2 --- /dev/null +++ b/js/js.examples/src/html5/interactive/template.html @@ -0,0 +1,31 @@ + + + + + + Canvas demo + + + + + + + + + + \ No newline at end of file diff --git a/js/js.examples/src/html5/interactive/template_copy.html b/js/js.examples/src/html5/interactive/template_copy.html new file mode 100644 index 00000000000..d3319ca6c00 --- /dev/null +++ b/js/js.examples/src/html5/interactive/template_copy.html @@ -0,0 +1,30 @@ + + + + + + Canvas demo + + + + + + + + + \ No newline at end of file diff --git a/js/js.examples/src/html5/interactive/tutorial.js b/js/js.examples/src/html5/interactive/tutorial.js new file mode 100644 index 00000000000..fb48f04ce7b --- /dev/null +++ b/js/js.examples/src/html5/interactive/tutorial.js @@ -0,0 +1,238 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// By Simon Sarris +// www.simonsarris.com +// sarris@acm.org +// +// Last update December 2011 +// +// Free to use and distribute at will +// So long as you are nice to people, etc + +// Constructor for Shape objects to hold data for all drawn objects. +// For now they will just be defined as rectangles. +function Shape(x, y, w, h, fill) { + // This is a very simple and unsafe constructor. All we're doing is checking if the values exist. + // "x || 0" just means "if there is a value for x, use that. Otherwise use 0." + // But we aren't checking anything else! We could put "Lalala" for the value of x + this.x = x || 0; + this.y = y || 0; + this.w = w || 1; + this.h = h || 1; + this.fill = fill || '#AAAAAA'; +} + +// Draws this shape to a given context +Shape.prototype.draw = function (ctx) { + ctx.fillStyle = this.fill; + ctx.fillRect(this.x, this.y, this.w, this.h); +} + +// Determine if a point is inside the shape's bounds +Shape.prototype.contains = function (mx, my) { + // All we have to do is make sure the Mouse X,Y fall in the area between + // the shape's X and (X + Height) and its Y and (Y + Height) + return (this.x <= mx) && (this.x + this.w >= mx) && + (this.y <= my) && (this.y + this.h >= my); +} + +function CanvasState(canvas) { + // **** First some setup! **** + + this.canvas = canvas; + this.width = canvas.width; + this.height = canvas.height; + this.ctx = canvas.getContext('2d'); + // This complicates things a little but but fixes mouse co-ordinate problems + // when there's a border or padding. See getMouse for more detail +// if (document.defaultView && document.defaultView.getComputedStyle) { +// this.stylePaddingLeft = parseInt(document.defaultView.getComputedStyle(canvas, null)['paddingLeft'], 10) || 0; +// this.stylePaddingTop = parseInt(document.defaultView.getComputedStyle(canvas, null)['paddingTop'], 10) || 0; +// this.styleBorderLeft = parseInt(document.defaultView.getComputedStyle(canvas, null)['borderLeftWidth'], 10) || 0; +// this.styleBorderTop = parseInt(document.defaultView.getComputedStyle(canvas, null)['borderTopWidth'], 10) || 0; +// } + // Some pages have fixed-position bars (like the stumbleupon bar) at the top or left of the page + // They will mess up mouse coordinates and this fixes that + var html = document.body.parentNode; + this.htmlTop = html.offsetTop; + this.htmlLeft = html.offsetLeft; + + // **** Keep track of state! **** + + this.valid = false; // when set to false, the canvas will redraw everything + this.shapes = []; // the collection of things to be drawn + this.dragging = false; // Keep track of when we are dragging + // the current selected object. In the future we could turn this into an array for multiple selection + this.selection = null; + this.dragoffx = 0; // See mousedown and mousemove events for explanation + this.dragoffy = 0; + + // **** Then events! **** + + // This is an example of a closure! + // Right here "this" means the CanvasState. But we are making events on the Canvas itself, + // and when the events are fired on the canvas the variable "this" is going to mean the canvas! + // Since we still want to use this particular CanvasState in the events we have to save a reference to it. + // This is our reference! + var myState = this; + + //fixes a problem where double clicking causes text to get selected on the canvas + canvas.addEventListener('selectstart', function (e) { + e.preventDefault(); + return false; + }, false); + // Up, down, and move are for dragging + canvas.addEventListener('mousedown', function (e) { + var mouse = myState.getMouse(e); + var mx = mouse.x; + var my = mouse.y; + var shapes = myState.shapes; + var l = shapes.length; + for (var i = l - 1; i >= 0; i--) { + if (shapes[i].contains(mx, my)) { + var mySel = shapes[i]; + // Keep track of where in the object we clicked + // so we can move it smoothly (see mousemove) + myState.dragoffx = mx - mySel.x; + myState.dragoffy = my - mySel.y; + myState.dragging = true; + myState.selection = mySel; + myState.valid = false; + return; + } + } + // havent returned means we have failed to select anything. + // If there was an object selected, we deselect it + if (myState.selection) { + myState.selection = null; + myState.valid = false; // Need to clear the old selection border + } + }, true); + canvas.addEventListener('mousemove', function (e) { + if (myState.dragging) { + var mouse = myState.getMouse(e); + // We don't want to drag the object by its top-left corner, we want to drag it + // from where we clicked. Thats why we saved the offset and use it here + myState.selection.x = mouse.x - myState.dragoffx; + myState.selection.y = mouse.y - myState.dragoffy; + myState.valid = false; // Something's dragging so we must redraw + } + }, true); + canvas.addEventListener('mouseup', function (e) { + myState.dragging = false; + }, true); + // double click for making new shapes + canvas.addEventListener('dblclick', function (e) { + var mouse = myState.getMouse(e); + myState.addShape(new Shape(mouse.x - 10, mouse.y - 10, 20, 20, 'rgba(0,255,0,.6)')); + }, true); + + // **** Options! **** + + this.selectionColor = '#CC0000'; + this.selectionWidth = 2; + this.interval = 30; + setInterval(function () { + myState.draw(); + }, myState.interval); +} + +CanvasState.prototype.addShape = function (shape) { + this.shapes.push(shape); + this.valid = false; +} + +CanvasState.prototype.clear = function () { + this.ctx.clearRect(0, 0, this.width, this.height); +} + +// While draw is called as often as the INTERVAL variable demands, +// It only ever does something if the canvas gets invalidated by our code +CanvasState.prototype.draw = function () { + // if our state is invalid, redraw and validate! + if (!this.valid) { + var ctx = this.ctx; + var shapes = this.shapes; + this.clear(); + + // ** Add stuff you want drawn in the background all the time here ** + + // draw all shapes + var l = shapes.length; + for (var i = 0; i < l; i++) { + var shape = shapes[i]; + // We can skip the drawing of elements that have moved off the screen: + if (shape.x > this.width || shape.y > this.height || + shape.x + shape.w < 0 || shape.y + shape.h < 0) continue; + shapes[i].draw(ctx); + } + + // draw selection + // right now this is just a stroke along the edge of the selected Shape + if (this.selection != null) { + ctx.strokeStyle = this.selectionColor; + ctx.lineWidth = this.selectionWidth; + var mySel = this.selection; + ctx.strokeRect(mySel.x, mySel.y, mySel.w, mySel.h); + } + + // ** Add stuff you want drawn on top all the time here ** + + this.valid = true; + } +} + + +// Creates an object with x and y defined, set to the mouse position relative to the state's canvas +// If you wanna be super-correct this can be tricky, we have to worry about padding and borders +CanvasState.prototype.getMouse = function (e) { + var element = this.canvas, offsetX = 0, offsetY = 0, mx, my; + + // Compute the total offset + if (element.offsetParent !== undefined) { + do { + offsetX += element.offsetLeft; + offsetY += element.offsetTop; + } while ((element = element.offsetParent)); + } + + // Add padding and border style widths to offset + // Also add the offsets in case there's a position:fixed bar +// offsetX += this.stylePaddingLeft + this.styleBorderLeft + this.htmlLeft; + // offsetY += this.stylePaddingTop + this.styleBorderTop + this.htmlTop; + + mx = e.pageX - offsetX; + my = e.pageY - offsetY; + + // We return a simple javascript object (a hash) with x and y defined + return {x:mx, y:my}; +} + +// If you dont want to use +// You could uncomment this init() reference and place the script reference inside the body tag +init(); + +function init() { + var s = new CanvasState(getCanvas()); + s.addShape(new Shape(40, 40, 50, 50)); // The default is gray + s.addShape(new Shape(60, 140, 40, 60, 'lightskyblue')); + // Lets make some partially transparent + s.addShape(new Shape(80, 150, 60, 30, 'rgba(127, 255, 212, .5)')); + s.addShape(new Shape(125, 80, 30, 80, 'rgba(245, 222, 179, .7)')); +} + +// Now go make something amazing! \ No newline at end of file diff --git a/js/js.examples/src/html5/interactive2/interactive2.js b/js/js.examples/src/html5/interactive2/interactive2.js new file mode 100644 index 00000000000..93de4ed144b --- /dev/null +++ b/js/js.examples/src/html5/interactive2/interactive2.js @@ -0,0 +1,475 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +{ + var classes = function () { + var Vector = Kotlin.Class.create({initialize:function (x, y) { + this.$x = x; + this.$y = y; + }, get_x:function () { + return this.$x; + }, get_y:function () { + return this.$y; + }, plus:function (v) { + { + return interactive2.v_0(this.get_x() + v.get_x(), this.get_y() + v.get_y()); + } + }, minus:function () { + { + return interactive2.v_0(-this.get_x(), -this.get_y()); + } + }, minus$0:function (v) { + { + return interactive2.v_0(this.get_x() - v.get_x(), this.get_y() - v.get_y()); + } + }, times:function (coef) { + { + return interactive2.v_0(this.get_x() * coef, this.get_y() * coef); + } + }, distanceTo:function (v) { + { + return Math.sqrt(this.minus$0(v).get_sqr()); + } + }, rotatedBy:function (theta) { + { + var sin = Math.sin(theta); + var cos = Math.cos(theta); + return interactive2.v_0(this.get_x() * cos - this.get_y() * sin, this.get_x() * sin + this.get_y() * cos); + } + }, isInRect:function (topLeft, size) { + { + return this.get_x() >= topLeft.get_x() && this.get_x() <= topLeft.get_x() + size.get_x() && this.get_y() >= topLeft.get_y() && this.get_y() <= topLeft.get_y() + size.get_y(); + } + }, get_sqr:function () { + { + return this.get_x() * this.get_x() + this.get_y() * this.get_y(); + } + }, get_normalized:function () { + { + return this.times(1 / Math.sqrt(this.get_sqr())); + } + } + }); + var RadialGradientGenerator = Kotlin.Class.create({initialize:function (context) { + this.$context = context; + this.$gradients = new Kotlin.ArrayList; + this.$current = 0; + { + this.newColorStops([ + [0, '#F59898'], + [0.5, '#F57373'], + [1, '#DB6B6B'] + ]); + this.newColorStops([ + [0.39, 'rgb(140,167,209)'], + [0.7, 'rgb(104,139,209)'], + [0.85, 'rgb(67,122,217)'] + ]); + this.newColorStops([ + [0, 'rgb(255,222,255)'], + [0.5, 'rgb(255,185,222)'], + [1, 'rgb(230,154,185)'] + ]); + this.newColorStops([ + [0, 'rgb(255,209,114)'], + [0.5, 'rgb(255,174,81)'], + [1, 'rgb(241,145,54)'] + ]); + this.newColorStops([ + [0, 'rgb(132,240,135)'], + [0.5, 'rgb(91,240,96)'], + [1, 'rgb(27,245,41)'] + ]); + this.newColorStops([ + [0, 'rgb(250,147,250)'], + [0.5, 'rgb(255,80,255)'], + [1, 'rgb(250,0,217)'] + ]); + } + }, get_context:function () { + return this.$context; + }, get_gradients:function () { + return this.$gradients; + }, get_current:function () { + return this.$current; + }, set_current:function (tmp$0) { + this.$current = tmp$0; + }, newColorStops:function (colorStops) { + { + this.get_gradients().add(colorStops); + } + }, getNext:function () { + { + var result = this.get_gradients().get(this.get_current()); + this.set_current((this.get_current() + 1) % this.get_gradients().size()); + return result; + } + } + }); + var CanvasState = Kotlin.Class.create({initialize:function (canvas) { + this.$canvas = canvas; + this.$width = this.get_canvas().width; + this.$height = this.get_canvas().height; + this.$context = getContext(); + this.$valid = false; + this.$shapes = new Kotlin.ArrayList; + this.$selection = null; + this.$dragOff = new interactive2.Vector_0(0, 0); + this.$interval = 1000 / 30; + { + var tmp$4; + var tmp$3; + var tmp$2; + var tmp$1; + var tmp$0_0; + $(this.get_canvas()).mousedown((tmp$0_0 = this , function (it) { + { + var tmp$0; + tmp$0_0.set_valid(false); + tmp$0_0.set_selection(null); + var mousePos = tmp$0_0.mousePos_0(it); + { + tmp$0 = tmp$0_0.get_shapes().iterator(); + while (tmp$0.hasNext()) { + var shape = tmp$0.next(); + { + if (shape.contains(mousePos)) { + tmp$0_0.set_dragOff(mousePos.minus$0(shape.get_pos())); + shape.set_selected(true); + tmp$0_0.set_selection(shape); + break; + } + } + } + } + } + } + )); + $(this.get_canvas()).mousemove((tmp$1 = this , function (it) { + { + if (tmp$1.get_selection() != null) { + Kotlin.sure(tmp$1.get_selection()).set_pos(tmp$1.mousePos_0(it).minus$0(tmp$1.get_dragOff())); + tmp$1.set_valid(false); + } + } + } + )); + $(this.get_canvas()).mouseup((tmp$2 = this , function (it) { + { + if (tmp$2.get_selection() != null) { + Kotlin.sure(tmp$2.get_selection()).set_selected(false); + } + tmp$2.set_selection(null); + tmp$2.set_valid(false); + } + } + )); + $(this.get_canvas()).dblclick((tmp$3 = this , function (it) { + { + var redTransparentCircle = new interactive2.Creature_0(tmp$3.mousePos_0(it), tmp$3); + tmp$3.addShape(redTransparentCircle); + tmp$3.set_valid(false); + } + } + )); + interactive2.doWithPeriod(this.get_interval(), (tmp$4 = this , function () { + { + tmp$4.draw(); + } + } + )); + } + }, get_canvas:function () { + return this.$canvas; + }, get_width:function () { + return this.$width; + }, get_height:function () { + return this.$height; + }, get_context:function () { + return this.$context; + }, get_valid:function () { + return this.$valid; + }, set_valid:function (tmp$0) { + this.$valid = tmp$0; + }, get_shapes:function () { + return this.$shapes; + }, set_shapes:function (tmp$0) { + this.$shapes = tmp$0; + }, get_selection:function () { + return this.$selection; + }, set_selection:function (tmp$0) { + this.$selection = tmp$0; + }, get_dragOff:function () { + return this.$dragOff; + }, set_dragOff:function (tmp$0) { + this.$dragOff = tmp$0; + }, get_interval:function () { + return this.$interval; + }, mousePos_0:function (e) { + { + var offset = new interactive2.Vector_0(0, 0); + var element = this.get_canvas(); + while (element != null) { + var el = Kotlin.sure(element); + offset = offset.plus(new interactive2.Vector_0(el.offsetLeft, el.offsetTop)); + element = el.offsetParent; + } + return (new interactive2.Vector_0(e.pageX, e.pageY)).minus$0(offset); + } + }, addShape:function (shape) { + { + this.get_shapes().add(shape); + this.set_valid(false); + } + }, clear:function () { + { + this.get_context().fillStyle = '#FFFFFF'; + this.get_context().fillRect(0, 0, this.get_width(), this.get_height()); + this.get_context().strokeStyle = '#000000'; + this.get_context().lineWidth = 4; + this.get_context().strokeRect(0, 0, this.get_width(), this.get_height()); + } + }, draw:function () { + { + var tmp$0; + if (this.get_valid()) + return; + this.clear(); + { + tmp$0 = this.get_shapes().iterator(); + while (tmp$0.hasNext()) { + var shape = tmp$0.next(); + { + shape.draw(this); + } + } + } + this.set_valid(true); + } + } + }); + var Shape = Kotlin.Class.create({initialize:function () { + this.$selected = false; + }, draw:function (state) { + }, contains:function (mousePos) { + }, get_pos:function () { + return this.$pos; + }, set_pos:function (tmp$0) { + this.$pos = tmp$0; + }, get_selected:function () { + return this.$selected; + }, set_selected:function (tmp$0) { + this.$selected = tmp$0; + } + }); + var Creature = Kotlin.Class.create(Shape, {initialize:function (pos, state) { + this.$pos = pos; + this.$state = state; + this.super_init(); + this.$shadowOffset = interactive2.v_0(-5, 5); + this.$colorStops = interactive2.get_gradientGenerator().getNext(); + this.$relSize = 0.05; + }, get_pos:function () { + return this.$pos; + }, set_pos:function (tmp$0) { + this.$pos = tmp$0; + }, get_state:function () { + return this.$state; + }, set_state:function (tmp$0) { + this.$state = tmp$0; + }, get_shadowOffset:function () { + return this.$shadowOffset; + }, get_colorStops:function () { + return this.$colorStops; + }, get_relSize:function () { + return this.$relSize; + }, get_radius:function () { + { + return this.get_state().get_width() * this.get_relSize(); + } + }, get_position:function () { + var tmp$0; + if (this.get_selected()) + tmp$0 = this.get_pos().minus$0(this.get_shadowOffset()); + else + tmp$0 = this.get_pos(); + { + return tmp$0; + } + }, get_directionToLogo:function () { + { + return interactive2.get_JB().get_centre().minus$0(this.get_position()).get_normalized(); + } + }, contains:function (mousePos) { + { + return this.get_pos().distanceTo(mousePos) < this.get_radius(); + } + }, fillCircle:function (receiver, position, rad) { + { + receiver.beginPath(); + receiver.arc(position.get_x(), position.get_y(), rad, 0, 2 * Math.PI, false); + receiver.closePath(); + receiver.fill(); + } + }, draw:function (state) { + { + var context = state.get_context(); + if (!this.get_selected()) { + this.drawCreature(context); + } + else { + this.drawCreatureWithShadow(context); + } + } + }, drawCreature:function (context) { + { + context.fillStyle = this.getGradient(context); + this.fillCircle(context, this.get_position(), this.get_radius()); + this.drawEye(context); + this.drawTail(context); + } + }, getGradient:function (context) { + { + var tmp$0; + var gradientCentre = this.get_position().plus(this.get_directionToLogo().times(this.get_radius() / 4)); + var gradient = context.createRadialGradient(gradientCentre.get_x(), gradientCentre.get_y(), 1, gradientCentre.get_x(), gradientCentre.get_y(), 2 * this.get_radius()); + { + tmp$0 = Kotlin.arrayIterator(this.get_colorStops()); + while (tmp$0.hasNext()) { + var colorStop = tmp$0.next(); + { + gradient.addColorStop(colorStop[0], colorStop[1]); + } + } + } + return gradient; + } + }, drawTail:function (context) { + { + var tailDirection = this.get_directionToLogo().minus(); + var tailPos = this.get_position().plus(tailDirection.times(this.get_radius()).times(0.7)); + var tailSize = this.get_radius() * 1.6; + var angle = Math.PI / 6; + var p1 = tailPos.plus(tailDirection.rotatedBy(angle).times(tailSize)); + var p2 = tailPos.plus(tailDirection.rotatedBy(-angle).times(tailSize)); + context.fillStyle = this.getGradient(context); + context.beginPath(); + context.moveTo(tailPos.get_x(), tailPos.get_y()); + context.lineTo(p1.get_x(), p1.get_y()); + var middlePoint = this.get_position().plus(tailDirection.times(this.get_radius()).times(1)); + context.quadraticCurveTo(middlePoint.get_x(), middlePoint.get_y(), p2.get_x(), p2.get_y()); + context.lineTo(tailPos.get_x(), tailPos.get_y()); + context.closePath(); + context.fill(); + } + }, drawEye:function (context) { + { + var eyePos = this.get_directionToLogo().times(this.get_radius()).times(0.6).plus(this.get_position()); + var eyeRadius = this.get_radius() / 3; + var eyeLidRadius = eyeRadius / 2; + context.fillStyle = '#FFFFFF'; + this.fillCircle(context, eyePos, eyeRadius); + context.fillStyle = '#000000'; + this.fillCircle(context, eyePos, eyeLidRadius); + } + }, drawCreatureWithShadow:function (context) { + { + context.save(); + this.setShadow(context); + context.fillStyle = this.getGradient(context); + this.fillCircle(context, this.get_position(), this.get_radius()); + context.restore(); + this.drawEye(context); + this.drawTail(context); + } + }, setShadow:function (context) { + { + context.shadowColor = 'rgba(100, 100, 100, 0.7)'; + context.shadowBlur = 5; + context.shadowOffsetX = this.get_shadowOffset().get_x(); + context.shadowOffsetY = this.get_shadowOffset().get_y(); + } + } + }); + var Logo = Kotlin.Class.create(Shape, {initialize:function (pos, relSize) { + this.$pos = pos; + this.$relSize = relSize; + this.super_init(); + this.$imageSize = interactive2.v_0(704, 254); + this.$canvasSize = this.get_imageSize().times(this.get_relSize()); + }, get_pos:function () { + return this.$pos; + }, set_pos:function (tmp$0) { + this.$pos = tmp$0; + }, get_relSize:function () { + return this.$relSize; + }, set_relSize:function (tmp$0) { + this.$relSize = tmp$0; + }, get_imageSize:function () { + return this.$imageSize; + }, get_canvasSize:function () { + return this.$canvasSize; + }, draw:function (state) { + { + var context = state.get_context(); + context.drawImage(getKotlinLogo(), 0, 0, this.get_imageSize().get_x(), this.get_imageSize().get_y(), this.get_pos().get_x(), this.get_pos().get_y(), this.get_canvasSize().get_x(), this.get_canvasSize().get_y()); + } + }, contains:function (mousePos) { + { + return mousePos.isInRect(this.get_pos(), this.get_canvasSize()); + } + }, get_centre:function () { + { + return this.get_pos().plus(this.get_canvasSize().times(0.5)); + } + } + }); + return {Shape_0:Shape, Logo_0:Logo, Creature_0:Creature, RadialGradientGenerator_0:RadialGradientGenerator, CanvasState_0:CanvasState, Vector_0:Vector}; + } + (); + var interactive2 = Kotlin.Namespace.create({initialize:function () { + interactive2.$gradientGenerator = new interactive2.RadialGradientGenerator_0(getContext()); + interactive2.$JB = new interactive2.Logo_0(interactive2.v_0(20, 20), 0.3); + }, get_gradientGenerator:function () { + return interactive2.$gradientGenerator; + }, get_JB:function () { + return interactive2.$JB; + }, doWithPeriod:function (period, f) { + { + setInterval(f, period); + } + }, v_0:function (x, y) { + { + return new interactive2.Vector_0(x, y); + } + }, main:function () { + { + var state = new interactive2.CanvasState_0(getCanvas()); + state.addShape(interactive2.get_JB()); + setTimeout(function () { + { + state.set_valid(false); + } + } + ); + } + } + }, classes); + interactive2.initialize(); +} + +var args = []; +interactive2.main(args); diff --git a/js/js.examples/src/html5/interactive2/jbLogo.png b/js/js.examples/src/html5/interactive2/jbLogo.png new file mode 100644 index 00000000000..759aa8ae05a Binary files /dev/null and b/js/js.examples/src/html5/interactive2/jbLogo.png differ diff --git a/js/js.examples/src/html5/interactive2/main.kt b/js/js.examples/src/html5/interactive2/main.kt new file mode 100644 index 00000000000..e4bacc02865 --- /dev/null +++ b/js/js.examples/src/html5/interactive2/main.kt @@ -0,0 +1,280 @@ +package interactive2; + +import jquery.*; +import html5.*; +import java.util.ArrayList; +import js.*; +import js.Math; +import js.setInterval +import html5.getCanvas +import html5.getKotlinLogo +import jquery.jq +import js.setTimeout + +val gradientGenerator = RadialGradientGenerator(getContext()) +val JB = Logo(v(20.0, 20.0)) + +fun doWithPeriod(period : Int, f : ()->Unit) { + setInterval(f, period); +} + +abstract class Shape() { + abstract fun draw(state : CanvasState); + abstract fun contains(mousePos : Vector) : Boolean; + abstract var pos : Vector; + var selected : Boolean = false; +} + +class Logo(override var pos : Vector, + var relSize : Double = 0.3) + : Shape() +{ + val imageSize = v(704.0, 254.0) + val canvasSize = imageSize * relSize + + override fun draw(state : CanvasState) { + val context = state.context; + context.drawImage(getKotlinLogo(), 0.0, 0.0, imageSize.x, imageSize.y, pos.x, pos.y, canvasSize.x, canvasSize.y) + } + + override fun contains(mousePos: Vector): Boolean = mousePos.isInRect(pos, canvasSize) + + val centre : Vector + get() = pos + canvasSize * 0.5 +} + +class Creature(override var pos : Vector, + var state : CanvasState) : Shape() { + + val shadowOffset = v(-5.0, 5.0) + val colorStops = gradientGenerator.getNext() + val relSize = 0.05; + val radius : Double + get() = state.width * relSize; + + val position : Vector + get() = if (selected) pos - shadowOffset else pos + + val directionToLogo : Vector + get() = (JB.centre - position).normalized + + override fun contains(mousePos : Vector) = pos.distanceTo(mousePos) < radius; + + fun Context.fillCircle(position : Vector, rad : Double) { + beginPath(); + arc(position.x, position.y, rad, 0.0, 2 * Math.PI, false); + closePath(); + fill() + } + + override fun draw(state : CanvasState) { + val context = state.context; + if (!selected) { + drawCreature(context) + } else { + drawCreatureWithShadow(context) + } + } + + fun drawCreature(context : Context) { + context.fillStyle = getGradient(context); + context.fillCircle(position, radius) + drawEye(context) + drawTail(context) + } + + fun getGradient(context : Context) : CanvasGradient { + val gradientCentre = position + directionToLogo * (radius / 4) + val gradient = context.createRadialGradient(gradientCentre.x, gradientCentre.y, 1.0, gradientCentre.x, gradientCentre.y, 2 * radius) + for (colorStop in colorStops) { + gradient.addColorStop(colorStop._1, colorStop._2) + } + return gradient; + } + + fun drawTail(context : Context) { + val tailDirection = -directionToLogo; + val tailPos = position + tailDirection * radius * 0.7 + val tailSize = radius * 1.6 + val angle = Math.PI / 6.0; + val p1 = tailPos + tailDirection.rotatedBy(angle) * tailSize + val p2 = tailPos + tailDirection.rotatedBy(-angle) * tailSize + context.fillStyle = getGradient(context) + context.beginPath() + context.moveTo(tailPos.x, tailPos.y) + context.lineTo(p1.x, p1.y) + val middlePoint = position + tailDirection * radius * 1.0 + context.quadraticCurveTo(middlePoint.x, middlePoint.y, p2.x, p2.y) + context.lineTo(tailPos.x, tailPos.y) + context.closePath() + context.fill() + } + + fun drawEye(context : Context) {; + val eyePos = directionToLogo * radius * 0.6 + position; + val eyeRadius = radius / 3; + val eyeLidRadius = eyeRadius / 2; + context.fillStyle = "#FFFFFF"; + context.fillCircle(eyePos, eyeRadius) + context.fillStyle = "#000000" + context.fillCircle(eyePos, eyeLidRadius) + } + + fun drawCreatureWithShadow(context : Context) { + context.save() + setShadow(context) + context.fillStyle = getGradient(context) + context.fillCircle(position, radius) + context.restore(); + drawEye(context) + drawTail(context) + } + + fun setShadow(context : Context) { + context.shadowColor = "rgba(100, 100, 100, 0.7)"; + context.shadowBlur = 5.0; + context.shadowOffsetX = shadowOffset.x; + context.shadowOffsetY = shadowOffset.y; + } +} + +class CanvasState(val canvas : Canvas) { + val width = canvas.width; + val height = canvas.height; + val context = getContext(); + var valid = false; + var shapes = ArrayList(); + var selection : Shape? = null; + var dragOff = Vector(); + val interval = 1000 / 30; + + { + jq(canvas).mousedown { + valid = false; + selection = null; + val mousePos = mousePos(it); + for (shape in shapes) { + if (mousePos in shape) { + dragOff = mousePos - shape.pos; + shape.selected = true; + selection = shape; + break; + } + } + } + + jq(canvas).mousemove { + if (selection != null) { + selection.sure().pos = mousePos(it) - dragOff; + valid = false; + } + } + + jq(canvas).mouseup { + if (selection != null) { + selection.sure().selected = false; + } + selection = null; + valid = false; + } + + jq(canvas).dblclick { + val redTransparentCircle = Creature(mousePos(it), this @CanvasState); + addShape(redTransparentCircle); + valid = false; + } + + doWithPeriod(interval) { + draw(); + } + } + + fun mousePos(e : MouseEvent) : Vector { + var offset = Vector(); + var element : DomElement? = canvas; + while (element != null) { + val el : DomElement = element.sure(); + offset += Vector(el.offsetLeft, el.offsetTop); + element = el.offsetParent; + } + return Vector(e.pageX, e.pageY) - offset; + } + + fun addShape(shape : Shape) { + shapes.add(shape); + valid = false; + } + + fun clear() { + context.fillStyle = "#FFFFFF" + context.fillRect(0.0, 0.0, width, height) + context.strokeStyle = "#000000" + context.lineWidth = 4.0; + context.strokeRect(0.0, 0.0, width, height) + } + + fun draw() { + if (valid) return + + clear(); + for (shape in shapes) { + shape.draw(this); + } + valid = true; + } +} + +class RadialGradientGenerator(val context : Context) { + val gradients = ArrayList>(); + var current = 0 + + fun newColorStops(vararg colorStops : #(Double, String)) { + gradients.add(colorStops) + } + + { + newColorStops(#(0.0, "#F59898"), #(0.5, "#F57373"), #(1.0, "#DB6B6B")) + newColorStops(#(0.39, "rgb(140,167,209)"), #(0.7, "rgb(104,139,209)"), #(0.85, "rgb(67,122,217)")) + newColorStops(#(0.0, "rgb(255,222,255)"), #(0.5, "rgb(255,185,222)"), #(1.0, "rgb(230,154,185)")) + newColorStops(#(0.0, "rgb(255,209,114)"), #(0.5, "rgb(255,174,81)"), #(1.0, "rgb(241,145,54)")) + newColorStops(#(0.0, "rgb(132,240,135)"), #(0.5, "rgb(91,240,96)"), #(1.0, "rgb(27,245,41)")) + newColorStops(#(0.0, "rgb(250,147,250)"), #(0.5, "rgb(255,80,255)"), #(1.0, "rgb(250,0,217)")) +} + + fun getNext() : Array<#(Double, String)> { + val result = gradients.get(current) + current = (current + 1) % gradients.size() + return result + } +} + +fun v(x : Double, y : Double) = Vector(x, y); + +class Vector(val x : Double = 0.0, val y : Double = 0.0) { + fun plus(v : Vector) = v(x + v.x, y + v.y); + fun minus() = v(-x, -y) + fun minus(v : Vector) = v(x - v.x, y - v.y) + fun times(coef : Double) = v(x * coef, y * coef) + fun distanceTo(v : Vector) = Math.sqrt((this - v).sqr) + fun rotatedBy(theta : Double) : Vector { + val sin = Math.sin(theta) + val cos = Math.cos(theta) + return v(x * cos - y * sin, x * sin + y * cos) + } + + fun isInRect(topLeft : Vector, size : Vector) = (x >= topLeft.x) && (x <= topLeft.x + size.x) && + (y >= topLeft.y) && (y <= topLeft.y + size.y) + + val sqr : Double + get() = x * x + y * y + val normalized : Vector + get() = this * (1.0 / Math.sqrt(sqr)) +} + +fun main() { + val state = CanvasState(getCanvas()); + state.addShape(JB); + setTimeout({ + state.valid = false + }) +} \ No newline at end of file diff --git a/js/js.examples/src/html5/interactive2/template.html b/js/js.examples/src/html5/interactive2/template.html new file mode 100644 index 00000000000..f7399d7ee00 --- /dev/null +++ b/js/js.examples/src/html5/interactive2/template.html @@ -0,0 +1,31 @@ + + + + + + Canvas demo + + + + + + + + + + \ No newline at end of file diff --git a/js/js.lib/jslib.iml b/js/js.lib/jslib.iml new file mode 100644 index 00000000000..d5c07432750 --- /dev/null +++ b/js/js.lib/jslib.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/js/js.lib/src/core/Dummy.java b/js/js.lib/src/core/Dummy.java new file mode 100644 index 00000000000..3a72c52114b --- /dev/null +++ b/js/js.lib/src/core/Dummy.java @@ -0,0 +1,23 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package core; + +/** + * @author Pavel Talanov + */ +public final class Dummy { +} diff --git a/js/js.lib/src/core/annotations.kt b/js/js.lib/src/core/annotations.kt new file mode 100644 index 00000000000..adafe8c4aa9 --- /dev/null +++ b/js/js.lib/src/core/annotations.kt @@ -0,0 +1,6 @@ +package js; + +native +annotation class native(name : String = "") {} +native +annotation class library(name : String = "") {} \ No newline at end of file diff --git a/js/js.lib/src/core/core.kt b/js/js.lib/src/core/core.kt new file mode 100644 index 00000000000..8335ca4fa2b --- /dev/null +++ b/js/js.lib/src/core/core.kt @@ -0,0 +1,40 @@ +package js; + +import js.library +import js.native +import java.util.*; +import java.lang.*; + + + +native +val noImpl : Nothing = throw Exception(); + +library("println") +fun println() {} +library("println") +fun println(s : Any?) {} +library("print") +fun print(s : Any?) {} +library("parseInt") +fun parseInt(s : String) : Int = 0 +library +open class Exception() : Throwable() {} +library +class NumberFormatException() : Exception() {} + +native +fun setTimeout(callback : ()-> Unit) {} + +native +fun setInterval(callback : ()-> Unit, ms : Int) {} +native +fun setInterval(callback : ()-> Unit) {} + + +native +open class DomElement() { + val offsetLeft = 0.0; + val offsetTop = 0.0; + val offsetParent : DomElement? = DomElement(); +} diff --git a/js/js.lib/src/core/date.kt b/js/js.lib/src/core/date.kt new file mode 100644 index 00000000000..cf551097b6a --- /dev/null +++ b/js/js.lib/src/core/date.kt @@ -0,0 +1,6 @@ +package js + +native +class Date() { + fun getTime() : Int = js.noImpl +} \ No newline at end of file diff --git a/js/js.lib/src/core/javalang.kt b/js/js.lib/src/core/javalang.kt new file mode 100644 index 00000000000..cb15d768496 --- /dev/null +++ b/js/js.lib/src/core/javalang.kt @@ -0,0 +1,12 @@ +package java.lang + +import java.util.Iterator; +import js.library + +library +trait Iterable { + fun iterator() : java.util.Iterator = js.noImpl +} + +library("splitString") +public fun String.split(regex : String) : Array = js.noImpl diff --git a/js/js.lib/src/core/javautil.kt b/js/js.lib/src/core/javautil.kt new file mode 100644 index 00000000000..954801cbcd6 --- /dev/null +++ b/js/js.lib/src/core/javautil.kt @@ -0,0 +1,175 @@ +package java.util + +import js.*; + + +library("collectionsMax") +public fun max(col : Collection, comp : Comparator) : T = js.noImpl + +library +public trait Comparator { + fun compare(obj1 : T, obj2 : T) : Int; +} + +library("comparator") +public fun comparator(f : (T, T) -> Int) : Comparator = js.noImpl + + +library +public open class Iterator() { + open fun next() : T = js.noImpl + open fun hasNext() : Boolean = js.noImpl +} + +library +val Collections = object { + library("collectionsMax") + public fun max(col : Collection, comp : Comparator) : T = js.noImpl +} + +library +public open class ArrayList() : java.util.List { + override public fun size() : Int = js.noImpl + override public fun isEmpty() : Boolean = js.noImpl + override public fun contains(o : Any?) : Boolean = js.noImpl + override public fun iterator() : Iterator = js.noImpl + // override public fun indexOf(o : Any?) : Int = js.noImpl + // override public fun lastIndexOf(o : Any?) : Int = js.noImpl + // override public fun toArray() : Array = js.noImpl + // override public fun toArray(a : Array) : Array = js.noImpl + override public fun get(index : Int) : E = js.noImpl + override public fun set(index : Int, element : E) : E = js.noImpl + override public fun add(e : E) : Boolean = js.noImpl + override public fun add(index : Int, element : E) : Unit = js.noImpl + override public fun remove(index : Int) : E = js.noImpl + override public fun remove(o : Any?) : Boolean = js.noImpl + override public fun clear() : Unit = js.noImpl + override public fun addAll(c : java.util.Collection) : Boolean = js.noImpl + // override public fun addAll(index : Int, c : java.util.Collection) : Boolean = js.noImpl +} + +library +public trait Collection : java.lang.Iterable { + open fun size() : Int + open fun isEmpty() : Boolean + open fun contains(o : Any?) : Boolean + override fun iterator() : java.util.Iterator + // open fun toArray() : Array + // open fun toArray(a : Array) : Array + open fun add(e : E) : Boolean + open fun remove(o : Any?) : Boolean + //open fun containsAll(c : java.util.Collection<*>) : Boolean + open fun addAll(c : java.util.Collection) : Boolean + //open fun removeAll(c : java.util.Collection<*>) : Boolean + //open fun retainAll(c : java.util.Collection<*>) : Boolean + open fun clear() : Unit +} + +library +public trait List : Collection { + override fun size() : Int + override fun isEmpty() : Boolean + override fun contains(o : Any?) : Boolean + override fun iterator() : java.util.Iterator + // override fun toArray() : Array + // Simulate Java's array covariance + // override fun toArray(a : Array) : Array + override fun add(e : E) : Boolean + override fun remove(o : Any?) : Boolean + // override fun containsAll(c : java.util.Collection<*>) : Boolean + override fun addAll(c : java.util.Collection) : Boolean + // open fun addAll(index : Int, c : java.util.Collection) : Boolean + // override fun removeAll(c : java.util.Collection<*>) : Boolean + // override fun retainAll(c : java.util.Collection<*>) : Boolean + override fun clear() : Unit + open fun get(index : Int) : E + open fun set(index : Int, element : E) : E + open fun add(index : Int, element : E) : Unit + open fun remove(index : Int) : E + // open fun indexOf(o : Any?) : Int + // open fun lastIndexOf(o : Any?) : Int +} + +library +public trait Set : Collection { + override fun size() : Int + override fun isEmpty() : Boolean + override fun contains(o : Any?) : Boolean + override fun iterator() : java.util.Iterator + // override fun toArray() : Array + // override fun toArray(a : Array) : Array + override fun add(e : E) : Boolean + override fun remove(o : Any?) : Boolean + //override fun containsAll(c : java.util.Collection<*>) : Boolean + override fun addAll(c : java.util.Collection) : Boolean + //override fun retainAll(c : java.util.Collection<*>) : Boolean + //override fun removeAll(c : java.util.Collection<*>) : Boolean + override fun clear() : Unit +} + +library +public open class HashSet() : java.util.Set { + override public fun iterator() : java.util.Iterator = js.noImpl + override public fun size() : Int = js.noImpl + override public fun isEmpty() : Boolean = js.noImpl + override public fun contains(o : Any?) : Boolean = js.noImpl + override public fun add(e : E) : Boolean = js.noImpl + override public fun remove(o : Any?) : Boolean = js.noImpl + override public fun clear() : Unit = js.noImpl + override fun addAll(c : java.util.Collection) : Boolean = js.noImpl +} + +library +public trait Map { + open fun size() : Int + open fun isEmpty() : Boolean + open fun containsKey(key : Any?) : Boolean + open fun containsValue(value : Any?) : Boolean + open fun get(key : Any?) : V? + open fun put(key : K, value : V) : V? + open fun remove(key : Any?) : V? + open fun putAll(m : java.util.Map) : Unit + open fun clear() : Unit + open fun keySet() : java.util.Set + open fun values() : java.util.Collection +} + +library +public open class HashMap() : java.util.Map { + override public fun size() : Int = js.noImpl + override public fun isEmpty() : Boolean = js.noImpl + override public fun get(key : Any?) : V = js.noImpl + override public fun containsKey(key : Any?) : Boolean = js.noImpl + override public fun put(key : K, value : V) : V = js.noImpl + override public fun putAll(m : java.util.Map) : Unit = js.noImpl + override public fun remove(key : Any?) : V? = js.noImpl + override public fun clear() : Unit = js.noImpl + override public fun containsValue(value : Any?) : Boolean = js.noImpl + override public fun keySet() : java.util.Set = js.noImpl + override public fun values() : java.util.Collection = js.noImpl +} + +library +public open class LinkedList() : List { + override public fun iterator() : java.util.Iterator = js.noImpl + override public fun isEmpty() : Boolean = js.noImpl + override public fun contains(o : Any?) : Boolean = js.noImpl + override public fun size() : Int = js.noImpl + override public fun add(e : E) : Boolean = js.noImpl + override public fun remove(o : Any?) : Boolean = js.noImpl + override public fun addAll(c : java.util.Collection) : Boolean = js.noImpl + override public fun clear() : Unit = js.noImpl + override public fun get(index : Int) : E = js.noImpl + override public fun set(index : Int, element : E) : E = js.noImpl + override public fun add(index : Int, element : E) : Unit = js.noImpl + override public fun remove(index : Int) : E = js.noImpl + public fun poll() : E? = js.noImpl + public fun peek() : E? = js.noImpl + public fun offer(e : E) : Boolean = js.noImpl +} + +library +public class StringBuilder() { + public fun append(obj : Any) : StringBuilder = js.noImpl + public fun toString() : String = js.noImpl +} \ No newline at end of file diff --git a/js/js.lib/src/core/json.kt b/js/js.lib/src/core/json.kt new file mode 100644 index 00000000000..df624aad0be --- /dev/null +++ b/js/js.lib/src/core/json.kt @@ -0,0 +1,18 @@ +package js + +import java.util.*; +import js.library + +native +class Json() { + +} + +library("jsonSet") +fun Json.set(paramName : String, value : Any?) : Unit {} + +library("jsonGet") +fun Json.get(paramName : String) : Any? = null + +library("jsonFromTuples") +fun json(vararg pairs : Tuple2) = Json() diff --git a/js/js.lib/src/core/math.kt b/js/js.lib/src/core/math.kt new file mode 100644 index 00000000000..3566b782aa6 --- /dev/null +++ b/js/js.lib/src/core/math.kt @@ -0,0 +1,32 @@ +package js; + +import js.native + +native +class MathClass() { + val PI : Double = 1.0; + fun random() : Double = 0.0; + fun abs(value : Double) = 0.0 + fun acos(value : Double) = 0.0 + fun asin(value : Double) = 0.0 + fun atan(value : Double) = 0.0 + fun atan2(x : Double, y : Double) = 0.0 + fun cos(value : Double) = 0.0 + fun sin(value : Double) = 0.0 + fun exp(value : Double) = 0.0 + fun max(vararg values : Double) = 0.0 + fun max(vararg values : Int) : Int = js.noImpl + fun min(vararg values : Int) : Int = js.noImpl + fun min(vararg values : Double) = 0.0 + fun sqrt(value : Double) = 0.0 + fun tan(value : Double) = 0.0 + fun log(value : Double) = 0.0 + fun pow(base : Double, exp : Double) = 0.0 + fun round(value : Double) = 0.0 + fun floor(value : Double) = 0.0 + fun ceil(value : Double) = 0.0 + +} + +native +val Math = MathClass(); \ No newline at end of file diff --git a/js/js.lib/src/html5/canvas.kt b/js/js.lib/src/html5/canvas.kt new file mode 100644 index 00000000000..014de6b0cb2 --- /dev/null +++ b/js/js.lib/src/html5/canvas.kt @@ -0,0 +1,130 @@ +package html5 + +import js.native +import js.DomElement + +native +class Context() { + fun save() { + } + fun restore() { + } + + fun scale(x : Double, y : Double) { + } + fun rotate(angle : Double) { + } + fun translate(x : Double, y : Double) { + } + + fun clearRect(x : Double, y : Double, w : Double, h : Double) { + } + fun fillRect(x : Double, y : Double, w : Double, h : Double) { + } + fun strokeRect(x : Double, y : Double, w : Double, h : Double) { + } + + var globalAlpha : Double = 1.0; + var strokeStyle : Any = "" + var fillStyle : Any = "" + var lineWidth : Double = 1.0 + var shadowOffsetX : Double = 0.0 + var shadowOffsetY : Double = 0.0 + var shadowBlur : Double = 0.0 + var shadowColor : String = "" + var font : String = "" + + fun beginPath() { + } + fun moveTo(x : Double, y : Double) { + } + fun closePath() { + } + + fun lineTo(x : Double, y : Double) { + } + fun quadraticCurveTo(cpx : Double, cpy : Double, x : Double, y : Double) { + } + fun bezierCurveTo(cp1x : Double, cp1y : Double, cp2x : Double, cp2y : Double, x : Double, y : Double) { + } + fun arcTo(x1 : Double, y1 : Double, x2 : Double, y2 : Double, radius : Double) { + } + fun arc(x : Double, y : Double, radius : Double, startAngle : Double, endAngle : Double, anticlockwise : Boolean) { + } + + fun rect(x : Double, y : Double, w : Double, h : Double) { + } + fun fill() { + } + fun stroke() { + } + + fun fillText(text : String, x : Double, y : Double) { + } + fun fillText(text : String, x : Double, y : Double, maxWidth : Double) { + } + fun strokeText(text : String, x : Double, y : Double) { + } + fun strokeText(text : String, x : Double, y : Double, maxWidth : Double) { + } + + fun measureText(text : String) : TextMetrics = TextMetrics(); + + fun drawImage(image : HTMLImageElement, dx : Double, dy : Double) { + } + fun drawImage(image : HTMLImageElement, dx: Double, dy: Double, dw: Double, dh: Double) { + } + fun drawImage(image : HTMLImageElement, sx: Double, sy: Double, + sw: Double, sh: Double, dx: Double, dy: Double, dw: Double, dh: Double) { + } + + fun createLinearGradient(x0 : Double, y0 : Double, x1 : Double, y1 : Double) : CanvasGradient = CanvasGradient() + fun createRadialGradient(x0 : Double, y0 : Double, r0 : Double, x1 : Double, y1 : Double, r1 : Double) : CanvasGradient = CanvasGradient(); + + fun getImageData(sx : Int, sy : Int, sw : Int, sh : Int) : ImageData = js.noImpl + fun putImageData(data : ImageData, dx : Int, dy : Int) : Unit = js.noImpl +} + +native +open class HTMLImageElement() : DomElement() { +} + +native +class CanvasGradient() { + fun addColorStop(offset : Double, color : String) { + } +} + +native +class ImageData() { + // readonly attribute unsigned long width; + // readonly attribute unsigned long height; + // readonly attribute Uint8ClampedArray data; + val width : Int = js.noImpl + val height : Int = js.noImpl + val data : Array = js.noImpl +} + + +native +class Canvas() : DomElement() { + var width : Int = js.noImpl; + var height : Int = js.noImpl; + + //DOMString toDataURL(in optional DOMString type, in any... args); + fun toDataURL() : String = js.noImpl + fun toDataURL(typ : String) : String = js.noImpl +} + +native +class TextMetrics() { + val width : Int = 0 +} + +/*custom helpers*/ +native +fun getContext() : Context = Context(); +native +fun getCanvas() : Canvas = Canvas(); +native +fun getKotlinLogo() : HTMLImageElement = HTMLImageElement(); \ No newline at end of file diff --git a/js/js.lib/src/html5/files.kt b/js/js.lib/src/html5/files.kt new file mode 100644 index 00000000000..8e05d2af4ef --- /dev/null +++ b/js/js.lib/src/html5/files.kt @@ -0,0 +1,37 @@ +package html5.files + +import js.native + +native +class FileReader() { + var onloadend : ((FileReaderEvent)->Unit)? = js.noImpl // + fun readAsDataURL(blob : Blob) = js.noImpl + val result : File = js.noImpl +} + +native +class FileReaderEvent() { + val target : FileReader = js.noImpl +} + +native +class FileList() { + fun item(index : Int) : File? = js.noImpl + val length : Int = js.noImpl +} + +native +class File() : Blob() { + val name : String = js.noImpl + // readonly attribute Date lastModifiedDate; +} + +native +open class Blob() { + val size : Int = js.noImpl + val `type` : String = js.noImpl + //Blob slice(optional long long start, + //optional long long end, + //optional DOMString contentType); + // +} diff --git a/js/js.lib/src/html5/image.kt b/js/js.lib/src/html5/image.kt new file mode 100644 index 00000000000..dc1ec502959 --- /dev/null +++ b/js/js.lib/src/html5/image.kt @@ -0,0 +1,25 @@ +package html5 + +import js.Exception +import js.native +import js.DomElement +import html5.files.FileList + +native +class HTMLInputElement() : DomElement() { + val files : FileList = js.noImpl + var onchange : (HTMLInputElementEvent)->Unit = js.noImpl +} + +native +class HTMLInputElementEvent() { + val target : HTMLInputElement = js.noImpl +} + +native +class Image() : HTMLImageElement() { + var height : Int = js.noImpl + var width : Int = js.noImpl + var src : String = js.noImpl + var onload : ()->Unit = js.noImpl +} \ No newline at end of file diff --git a/js/js.lib/src/jquery/common.kt b/js/js.lib/src/jquery/common.kt new file mode 100644 index 00000000000..54ad3feb9dd --- /dev/null +++ b/js/js.lib/src/jquery/common.kt @@ -0,0 +1,72 @@ +package jquery; + +import js.*; +import js.DomElement + +native +class JQuery() { + fun addClass(className : String) : JQuery = js.noImpl; + fun addClass(f : DomElement.(Int, String)->String) = js.noImpl; + + fun attr(attrName : String) = ""; + fun attr(attrName : String, value : String) = this; + + fun html() : String = ""; + fun html(s : String) = this; + fun html(f : DomElement.(Int, String)->String) = this; + + + fun hasClass(className : String) = true + fun removeClass(className : String) = this + fun height() = 0 + fun width() = 0 + + fun click() = this; + + fun mousedown(handler : DomElement.(MouseEvent)->Unit) = this; + fun mouseup(handler : DomElement.(MouseEvent)->Unit) = this; + fun mousemove(handler : DomElement.(MouseEvent)->Unit) = this; + + fun dblclick(handler : DomElement.(MouseClickEvent)->Unit) = this; + fun click(handler : DomElement.(MouseClickEvent)->Unit) = this; + + fun load(handler : DomElement.()->Unit) = this; + fun change(handler : DomElement.()->Unit) = this; + + fun append(str : String) = this; + fun ready(handler : ()->Unit) = this; + fun text(text : String) = this; + fun slideUp() = this; + fun hover(handlerInOut : DomElement.() -> Unit) = this; + fun hover(handlerIn : DomElement.() -> Unit, handlerOut : DomElement.() -> Unit) = this; + fun next() : JQuery = js.noImpl + fun parent() : JQuery = js.noImpl +} + +native +open class MouseEvent() { + val pageX : Double = 0.0; + val pageY : Double = 0.0; + fun preventDefault() {} + fun isDefaultPrevented() : Boolean = true; +} + +native +class MouseClickEvent() : MouseEvent() { + val which : Int = 0; +} + + + +native("$") +fun jq(selector : String) = JQuery(); +native("$") +fun jq(selector : String, context : DomElement) = JQuery(); +native("$") +fun jq(callback : () -> Unit) = JQuery(); +native("$") +fun jq(obj : JQuery) = JQuery(); +native("$") +fun jq(el : DomElement) = JQuery(); +native("$") +fun jq() = JQuery(); diff --git a/js/js.lib/src/jquery/ui.kt b/js/js.lib/src/jquery/ui.kt new file mode 100644 index 00000000000..ffccbe1f983 --- /dev/null +++ b/js/js.lib/src/jquery/ui.kt @@ -0,0 +1,20 @@ +package jquery.ui + + +//jquery UI +import jquery.JQuery +import js.native +import js.Json + +native +fun JQuery.buttonset() : JQuery = js.noImpl; +native +fun JQuery.dialog() : JQuery = js.noImpl; +native +fun JQuery.button() : JQuery = js.noImpl; +native +fun JQuery.accordion() : JQuery = js.noImpl +native +fun JQuery.draggable(params : Json) : JQuery = js.noImpl +native +fun JQuery.selectable() : JQuery = js.noImpl diff --git a/js/js.lib/src/raphael/raphael.kt b/js/js.lib/src/raphael/raphael.kt new file mode 100644 index 00000000000..ef74659206b --- /dev/null +++ b/js/js.lib/src/raphael/raphael.kt @@ -0,0 +1,63 @@ +package raphael + +import js.*; +import js.*; + +native +open class Element() { + + fun attr(name : String, value : Any?) : Element = this + fun attr(nameToValue : Json) : Element = this + + fun click(handler : Element.()-> Unit) = this; + + fun mouseover(handler : Element.() -> Unit) = this + fun mouseout(handler : Element.() -> Unit) = this + + + fun getTotalLength() : Double = 0.0 + fun getPointAtLength(length : Double) : Point = js.noImpl + + fun animate(params : Json, ms : Int, callback : ()-> Unit) : Element = js.noImpl + fun animate(params : Json, ms : Int, s : String) : Element = js.noImpl + fun animate(params : Json, ms : Int) : Element = js.noImpl + //fun mouse +} + +native +class Set() : Element() { + fun push(el : Element) = js.noImpl +} + +native +class Paper() { + fun path(pathString : String) : Element = Element(); + fun path() : Element = Element(); + + fun ellipse(x : Int, y : Int, rx : Int, ry : Int) : Element = js.noImpl + fun circle(x : Int, y : Int, r : Int) : Element = js.noImpl + fun set() : Set = js.noImpl + + val customAttributes : Json = Json(); +} + +native("Raphael") +fun Raphael(elementId : String, width : Int, height : Int) : Paper = Paper(); +native("Raphael") +fun Raphael(elementId : String, width : Int, height : Int, initFun : Paper.() -> Unit) : Unit = js.noImpl + +native +fun getColor() : Color = js.noImpl +native +fun resetColors() = js.noImpl + +native +class Color() { +} + +native +class Point() { + val x = 0 + val y = 0 + val alpha = 0 +} diff --git a/js/js.tests/js.tests.iml b/js/js.tests/js.tests.iml new file mode 100644 index 00000000000..e25eea390b5 --- /dev/null +++ b/js/js.tests/js.tests.iml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/js/js.tests/test/org/jetbrains/k2js/test/AbstractExpressionTest.java b/js/js.tests/test/org/jetbrains/k2js/test/AbstractExpressionTest.java new file mode 100644 index 00000000000..95f3eb9bbb0 --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/AbstractExpressionTest.java @@ -0,0 +1,30 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test; + +/** + * @author Pavel Talanov + */ +public abstract class AbstractExpressionTest extends TranslationTest { + + private final String SUITE = "expression/"; + + @Override + protected String suiteDirectoryName() { + return SUITE; + } +} diff --git a/js/js.tests/test/org/jetbrains/k2js/test/ArrayListTest.java b/js/js.tests/test/org/jetbrains/k2js/test/ArrayListTest.java new file mode 100644 index 00000000000..7e3f7506cab --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/ArrayListTest.java @@ -0,0 +1,65 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test; + +import org.mozilla.javascript.JavaScriptException; + +/** + * @author Pavel Talanov + */ +public final class ArrayListTest extends JavaClassesTest { + + final private static String MAIN = "arrayList/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + public void testEmptyList() throws Exception { + testFooBoxIsTrue("emptyList.kt"); + } + + public void testAccess() throws Exception { + testFooBoxIsTrue("access.kt"); + } + + public void testIsEmpty() throws Exception { + testFooBoxIsTrue("isEmpty.kt"); + } + + public void testArrayAccess() throws Exception { + testFooBoxIsTrue("arrayAccess.kt"); + } + + public void testIterate() throws Exception { + testFooBoxIsTrue("iterate.kt"); + } + + public void testRemove() throws Exception { + testFooBoxIsTrue("remove.kt"); + } + + public void testIndexOOB() throws Exception { + try { + testFooBoxIsTrue("indexOOB.kt"); + fail(); + } catch (JavaScriptException e) { + + } + } +} diff --git a/js/js.tests/test/org/jetbrains/k2js/test/BaseTest.java b/js/js.tests/test/org/jetbrains/k2js/test/BaseTest.java new file mode 100644 index 00000000000..71875def517 --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/BaseTest.java @@ -0,0 +1,51 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test; + +import com.intellij.openapi.project.Project; +import com.intellij.testFramework.UsefulTestCase; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.jet.JetTestUtils; +import org.jetbrains.jet.compiler.JetCoreEnvironment; + +/** + * @author Pavel Talanov + */ +public abstract class BaseTest extends UsefulTestCase { + @NonNls + protected JetCoreEnvironment myEnvironment; + + public Project getProject() { + return myEnvironment.getProject(); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + // createEnvironmentWithMockJdk(); + } + + @Override + protected void tearDown() throws Exception { + myEnvironment = null; + super.tearDown(); + } + + protected void createEnvironmentWithMockJdk() { + myEnvironment = JetTestUtils.createEnvironmentWithMockJdk(getTestRootDisposable()); + } +} diff --git a/js/js.tests/test/org/jetbrains/k2js/test/BasicClassTest.java b/js/js.tests/test/org/jetbrains/k2js/test/BasicClassTest.java new file mode 100644 index 00000000000..29df4e4922c --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/BasicClassTest.java @@ -0,0 +1,35 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test; + +/** + * @author Pavel Talanov + */ +//TODO: remove class +public class BasicClassTest extends TranslationTest { + + final private static String MAIN = "class/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + public void testClassWithoutNamespace() throws Exception { + testFunctionOutput("classWithoutNamespace.kt", "Anonymous", "box", true); + } +} diff --git a/js/js.tests/test/org/jetbrains/k2js/test/ClassInheritanceTest.java b/js/js.tests/test/org/jetbrains/k2js/test/ClassInheritanceTest.java new file mode 100644 index 00000000000..ed2d3369c68 --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/ClassInheritanceTest.java @@ -0,0 +1,60 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test; + +/** + * @author Pavel Talanov + */ +public final class ClassInheritanceTest extends TranslationTest { + + final private static String MAIN = "inheritance/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + public void testInitializersOfBasicClassExecute() throws Exception { + testFunctionOutput("initializersOfBasicClassExecute.kt", "foo", "box", 3); + } + + public void testMethodOverride() throws Exception { + testFooBoxIsTrue("methodOverride.kt"); + } + + public void testInitializationOrder() throws Exception { + testFooBoxIsTrue("initializationOrder.kt"); + } + + public void testComplexInitializationOrder() throws Exception { + testFooBoxIsTrue("complexInitializationOrder.kt"); + } + + public void testValuePassedToAncestorConstructor() throws Exception { + testFooBoxIsTrue("valuePassedToAncestorConstructor.kt"); + } + + public void testBaseClassDefinedAfterDerived() throws Exception { + testFooBoxIsTrue("baseClassDefinedAfterDerived.kt"); + } + + public void testDefinitionOrder() throws Exception { + testFooBoxIsTrue("definitionOrder.kt"); + } +} + + diff --git a/js/js.tests/test/org/jetbrains/k2js/test/ConditionalTest.java b/js/js.tests/test/org/jetbrains/k2js/test/ConditionalTest.java new file mode 100644 index 00000000000..8eaec5f933c --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/ConditionalTest.java @@ -0,0 +1,42 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test; + +import org.mozilla.javascript.JavaScriptException; + +/** + * @author Pavel Talanov + */ +//TODO: remove class +public class ConditionalTest extends AbstractExpressionTest { + + final private static String MAIN = "conditional/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + public void testIfElseAsExpressionWithThrow() throws Exception { + try { + testFooBoxIsTrue("ifAsExpressionWithThrow.kt"); + fail(); + } catch (JavaScriptException e) { + + } + } +} diff --git a/js/js.tests/test/org/jetbrains/k2js/test/ExampleTestSuite.java b/js/js.tests/test/org/jetbrains/k2js/test/ExampleTestSuite.java new file mode 100644 index 00000000000..329f749e1da --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/ExampleTestSuite.java @@ -0,0 +1,33 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test; + +import com.intellij.testFramework.UsefulTestCase; +import junit.framework.Test; +import org.jetbrains.annotations.NotNull; + +public final class ExampleTestSuite extends UsefulTestCase { + + public static Test suite() { + return Suite.suiteForDirectory("examples/", new Suite.SingleFileTester() { + @Override + public void performTest(@NotNull Suite test, @NotNull String filename) throws Exception { + test.testFunctionOutput(filename, "Anonymous", "box", "OK"); + } + }); + } +} diff --git a/js/js.tests/test/org/jetbrains/k2js/test/ExtensionFunctionTest.java b/js/js.tests/test/org/jetbrains/k2js/test/ExtensionFunctionTest.java new file mode 100644 index 00000000000..783d67f71ec --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/ExtensionFunctionTest.java @@ -0,0 +1,65 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test; + +/** + * @author Pavel Talanov + */ +public final class ExtensionFunctionTest extends TranslationTest { + final private static String MAIN = "extensionFunction/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + public void testIntExtension() throws Exception { + testFooBoxIsTrue("intExtension.kt"); + } + + public void testExtensionWithImplicitReceiver() throws Exception { + testFooBoxIsTrue("extensionWithImplicitReceiver.kt"); + } + + public void testExtensionFunctionOnExpression() throws Exception { + testFooBoxIsTrue("extensionFunctionOnExpression.kt"); + } + + public void testExtensionUsedInsideClass() throws Exception { + testFooBoxIsTrue("extensionUsedInsideClass.kt"); + } + + public void testVirtualExtension() throws Exception { + testFooBoxIsTrue("virtualExtension.kt"); + } + + public void testVirtualExtensionOverride() throws Exception { + testFooBoxIsTrue("virtualExtensionOverride.kt"); + } + + public void testExtensionLiteralPassedToFunction() throws Exception { + testFooBoxIsTrue("extensionLiteralPassedToFunction.kt"); + } + + public void testExtensionInsideFunctionLiteral() throws Exception { + testFooBoxIsTrue("extensionInsideFunctionLiteral.kt"); + } + + public void testGenericExtension() throws Exception { + testFooBoxIsOk("generic.kt"); + } +} diff --git a/js/js.tests/test/org/jetbrains/k2js/test/ExtensionPropertyTest.java b/js/js.tests/test/org/jetbrains/k2js/test/ExtensionPropertyTest.java new file mode 100644 index 00000000000..983dd8ab86f --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/ExtensionPropertyTest.java @@ -0,0 +1,41 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test; + +/** + * @author Pavel Talanov + */ +public final class ExtensionPropertyTest extends TranslationTest { + final private static String MAIN = "extensionProperty/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + public void testSimplePropertyWithGetter() throws Exception { + testFooBoxIsTrue("simplePropertyWithGetter.kt"); + } + + public void testPropertyWithGetterAndSetter() throws Exception { + testFooBoxIsTrue("propertyWithGetterAndSetter.kt"); + } + + public void testAbsExtension() throws Exception { + testFooBoxIsTrue("absExtension.kt"); + } +} diff --git a/js/js.tests/test/org/jetbrains/k2js/test/ForTest.java b/js/js.tests/test/org/jetbrains/k2js/test/ForTest.java new file mode 100644 index 00000000000..6c469fc070e --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/ForTest.java @@ -0,0 +1,38 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test; + +/** + * @author Pavel Talanov + */ +public final class ForTest extends AbstractExpressionTest { + + final private static String MAIN = "for/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + public void testForIteratesOverArray() throws Exception { + testFooBoxIsTrue("forIteratesOverArray.kt"); + } + + public void testForOnEmptyArray() throws Exception { + testFooBoxIsTrue("forOnEmptyArray.kt"); + } +} \ No newline at end of file diff --git a/js/js.tests/test/org/jetbrains/k2js/test/FunctionTest.java b/js/js.tests/test/org/jetbrains/k2js/test/FunctionTest.java new file mode 100644 index 00000000000..c339e7343af --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/FunctionTest.java @@ -0,0 +1,109 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test; + +/** + * @author Pavel Talanov + */ +public class FunctionTest extends AbstractExpressionTest { + + final private static String MAIN = "function/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + public void testFunctionUsedBeforeDeclaration() throws Exception { + testFooBoxIsTrue("functionUsedBeforeDeclaration.kt"); + } + + public void testFunctionWithTwoParametersCall() throws Exception { + testFooBoxIsTrue("functionWithTwoParametersCall.kt"); + } + + public void testFunctionLiteral() throws Exception { + testFooBoxIsTrue("functionLiteral.kt"); + } + + public void testAdderClosure() throws Exception { + testFooBoxIsTrue("adderClosure.kt"); + } + + public void testLoopClosure() throws Exception { + testFooBoxIsTrue("loopClosure.kt"); + } + + public void testFunctionLiteralAsParameter() throws Exception { + testFooBoxIsTrue("functionLiteralAsParameter.kt"); + } + + public void testClosureWithParameter() throws Exception { + testFooBoxIsOk("closureWithParameter.kt"); + } + + public void testClosureWithParameterAndBoxing() throws Exception { + testFooBoxIsOk("closureWithParameterAndBoxing.jet"); + } + + public void testEnclosingThis() throws Exception { + testFunctionOutput("enclosingThis.kt", "Anonymous", "box", "OK"); + } + + + public void testImplicitItParameter() throws Exception { + testFooBoxIsTrue("implicitItParameter.kt"); + } + + + public void testDefaultParameters() throws Exception { + testFooBoxIsTrue("defaultParameters.kt"); + } + + + public void testFunctionLiteralAsLastParameter() throws Exception { + testFooBoxIsTrue("functionLiteralAsLastParameter.kt"); + } + + + public void testNamedArguments() throws Exception { + testFooBoxIsTrue("namedArguments.kt"); + } + + + public void testExpressionAsFunction() throws Exception { + testFooBoxIsTrue("expressionAsFunction.kt"); + } + + + public void testVararg() throws Exception { + testFooBoxIsTrue("vararg.kt"); + } + + + public void testkt921() throws Exception { + try { + checkOutput("KT-921.kt", ""); + } catch (Throwable e) { + System.out.println(e); + } + } + + public void testFunctionInsideFunction() throws Exception { + testFooBoxIsTrue("functionInsideFunction.kt"); + } +} diff --git a/js/js.tests/test/org/jetbrains/k2js/test/JavaClassesTest.java b/js/js.tests/test/org/jetbrains/k2js/test/JavaClassesTest.java new file mode 100644 index 00000000000..7f66ee62d5c --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/JavaClassesTest.java @@ -0,0 +1,31 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test; + +/** + * @author Pavel Talanov + */ +@SuppressWarnings("FieldCanBeLocal") +public abstract class JavaClassesTest extends TranslationTest { + + private final String SUITE = "java/"; + + @Override + protected String suiteDirectoryName() { + return SUITE; + } +} diff --git a/js/js.tests/test/org/jetbrains/k2js/test/KotlinLibTest.java b/js/js.tests/test/org/jetbrains/k2js/test/KotlinLibTest.java new file mode 100644 index 00000000000..ea94e687ba9 --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/KotlinLibTest.java @@ -0,0 +1,118 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test; + +import org.mozilla.javascript.Context; +import org.mozilla.javascript.Scriptable; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * @author Pavel Talanov + */ +public final class KotlinLibTest extends TranslationTest { + + final private static String MAIN = "kotlinLib/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + private void runPropertyTypeCheck(String objectName, Map> propertyToType) + throws Exception { + runRhinoTest(Arrays.asList(kotlinLibraryPath()), new RhinoPropertyTypesChecker(objectName, propertyToType)); + } + + + public void testKotlinJsLibRunsWithRhino() throws Exception { + runRhinoTest(Arrays.asList(kotlinLibraryPath()), new RhinoResultChecker() { + @Override + public void runChecks(Context context, Scriptable scope) throws Exception { + //do nothing + } + }); + } + + //TODO: refactor + public void testCreatedTraitIsJSObject() throws Exception { + final Map> propertyToType + = new HashMap>(); + runRhinoTest(Arrays.asList(kotlinLibraryPath(), cases("trait.js")), + new RhinoPropertyTypesChecker("foo", propertyToType)); + } + + + public void testCreatedNamespaceIsJSObject() throws Exception { + final Map> propertyToType + = new HashMap>(); + runRhinoTest(Arrays.asList(kotlinLibraryPath(), cases("namespace.js")), + new RhinoPropertyTypesChecker("foo", propertyToType)); + } + + // + // TODO:Refactor calls to function result checker with test + public void testNamespaceHasDeclaredFunction() throws Exception { + runRhinoTest(Arrays.asList(kotlinLibraryPath(), cases("namespace.js")), + new RhinoFunctionResultChecker("test", true)); + } + + + public void testNamespaceHasDeclaredClasses() throws Exception { + runRhinoTest(Arrays.asList(kotlinLibraryPath(), cases("namespaceWithClasses.js")), + new RhinoFunctionResultChecker("test", true)); + } + + + public void testIsSameType() throws Exception { + runRhinoTest(Arrays.asList(kotlinLibraryPath(), cases("isSameType.js")), + new RhinoFunctionResultChecker("test", true)); + } + + + public void testIsAncestorType() throws Exception { + runRhinoTest(Arrays.asList(kotlinLibraryPath(), cases("isAncestorType.js")), + new RhinoFunctionResultChecker("test", true)); + } + + + public void testIsComplexTest() throws Exception { + runRhinoTest(Arrays.asList(kotlinLibraryPath(), cases("isComplexTest.js")), + new RhinoFunctionResultChecker("test", true)); + } + + + public void testCommaExpression() throws Exception { + runRhinoTest(Arrays.asList(kotlinLibraryPath(), cases("commaExpression.js")), + new RhinoFunctionResultChecker("test", true)); + } + + + public void testArray() throws Exception { + runRhinoTest(Arrays.asList(kotlinLibraryPath(), cases("array.js")), + new RhinoFunctionResultChecker("test", true)); + } + + + public void testHashMap() throws Exception { + runRhinoTest(Arrays.asList(kotlinLibraryPath(), cases("hashMap.js")), + new RhinoFunctionResultChecker("test", true)); + } + +} diff --git a/js/js.tests/test/org/jetbrains/k2js/test/MiscTest.java b/js/js.tests/test/org/jetbrains/k2js/test/MiscTest.java new file mode 100644 index 00000000000..d56a8371b78 --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/MiscTest.java @@ -0,0 +1,46 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test; + +/** + * @author Pavel Talanov + *

+ * This class contains tests that do not fall in any particular category + * most probably because that functionality has very little support + */ +public final class MiscTest extends AbstractExpressionTest { + final private static String MAIN = "misc/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + public void testLocalPropertys() throws Exception { + testFunctionOutput("localProperty.jet", "foo", "box", 50); + } + + public void testIntRange() throws Exception { + // checkOutput("intRange.kt", " "); + testFooBoxIsTrue("intRange.kt"); + } + + + public void testSafecallComputesExpressionOnlyOnce() throws Exception { + testFooBoxIsTrue("safecallComputesExpressionOnlyOnce.kt"); + } +} diff --git a/js/js.tests/test/org/jetbrains/k2js/test/MultiFileTest.java b/js/js.tests/test/org/jetbrains/k2js/test/MultiFileTest.java new file mode 100644 index 00000000000..76738d49285 --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/MultiFileTest.java @@ -0,0 +1,43 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test; + +/** + * @author Pavel Talanov + */ +public final class MultiFileTest extends TranslationTest { + + final private static String MAIN = "multiFile/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + public void testFunctionsVisibleFromOtherFile() throws Exception { + testFooBoxIsTrue("functionsVisibleFromOtherFile"); + } + + public void testClassesInheritedFromOtherFile() throws Exception { + testFooBoxIsTrue("classesInheritedFromOtherFile"); + } + + @Override + public void testFooBoxIsTrue(String dirName) throws Exception { + testMultiFile(dirName, "foo", "box", true); + } +} diff --git a/js/js.tests/test/org/jetbrains/k2js/test/MultiNamespaceTest.java b/js/js.tests/test/org/jetbrains/k2js/test/MultiNamespaceTest.java new file mode 100644 index 00000000000..8d7d473d636 --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/MultiNamespaceTest.java @@ -0,0 +1,43 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test; + +/** + * @author Pavel Talanov + */ +public class MultiNamespaceTest extends TranslationTest { + final private static String MAIN = "multiNamespace/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + public void testFunctionsVisibleFromOtherNamespace() throws Exception { + testFooBoxIsTrue("functionsVisibleFromOtherNamespace"); + } + + public void testClassesInheritedFromOtherNamespace() throws Exception { + testFooBoxIsTrue("classesInheritedFromOtherNamespace"); + } + + @Override + public void testFooBoxIsTrue(String dirName) throws Exception { + testMultiFile(dirName, "foo", "box", true); + } +} + diff --git a/js/js.tests/test/org/jetbrains/k2js/test/NameClashesTest.java b/js/js.tests/test/org/jetbrains/k2js/test/NameClashesTest.java new file mode 100644 index 00000000000..67f563d2430 --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/NameClashesTest.java @@ -0,0 +1,34 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test; + +/** + * @author Pavel Talanov + */ +public final class NameClashesTest extends TranslationTest { + + private static final String MAIN = "nameClashes/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + public void testMethodOverload() throws Exception { + testFooBoxIsTrue("methodOverload.kt"); + } +} \ No newline at end of file diff --git a/js/js.tests/test/org/jetbrains/k2js/test/ObjectTest.java b/js/js.tests/test/org/jetbrains/k2js/test/ObjectTest.java new file mode 100644 index 00000000000..f0824057fdc --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/ObjectTest.java @@ -0,0 +1,45 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test; + +/** + * @author Pavel Talanov + */ +public final class ObjectTest extends TranslationTest { + + private static final String MAIN = "object/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + + public void testObjectWithMethods() throws Exception { + testFooBoxIsTrue("objectWithMethods.kt"); + } + + + public void testObjectDeclaration() throws Exception { + testFooBoxIsTrue("objectDeclaration.kt"); + } + + + public void testObjectInMethod() throws Exception { + testFooBoxIsTrue("objectInMethod.kt"); + } +} \ No newline at end of file diff --git a/js/js.tests/test/org/jetbrains/k2js/test/OperatorOverloadingTest.java b/js/js.tests/test/org/jetbrains/k2js/test/OperatorOverloadingTest.java new file mode 100644 index 00000000000..d36e34ed211 --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/OperatorOverloadingTest.java @@ -0,0 +1,108 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test; + +/** + * @author Pavel Talanov + */ +public final class OperatorOverloadingTest extends TranslationTest { + + final private static String MAIN = "operatorOverloading/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + + public void testPlusOverload() throws Exception { + testFooBoxIsTrue("plusOverload.kt"); + } + + + public void testPostfixInc() throws Exception { + testFooBoxIsTrue("postfixIncOverload.kt"); + } + + + public void testPrefixDecOverload() throws Exception { + testFooBoxIsTrue("prefixDecOverload.kt"); + } + + + public void testPrefixIncReturnsCorrectValue() throws Exception { + testFooBoxIsTrue("prefixIncReturnsCorrectValue.kt"); + } + + + public void testOverloadedCallOnProperty() throws Exception { + testFooBoxIsTrue("overloadedCallOnProperty.kt"); + } + + + public void testPostfixOnProperty() throws Exception { + testFooBoxIsTrue("postfixOnProperty.kt"); + } + + + public void testOperatorOverloadOnPropertyCallGetterAndSetterOnlyOnce() throws Exception { + testFooBoxIsTrue("operatorOverloadOnPropertyCallGetterAndSetterOnlyOnce.kt"); + } + + + public void testUnaryOnIntProperty() throws Exception { + testFooBoxIsTrue("unaryOnIntProperty.kt"); + } + + + public void testUnaryOnIntPropertyAsStatement() throws Exception { + testFooBoxIsTrue("unaryOnIntProperty2.kt"); + } + + + public void testBinaryDivOverload() throws Exception { + testFooBoxIsTrue("binaryDivOverload.kt"); + } + + + public void testPlusAssignNoReassign() throws Exception { + testFooBoxIsTrue("plusAssignNoReassign.kt"); + } + + public void testNotOverload() throws Exception { + testFooBoxIsTrue("notOverload.kt"); + } + + + public void testCompareTo() throws Exception { + testFooBoxIsTrue("compareTo.kt"); + } + + + public void testPlusAndMinusAsAnExpression() throws Exception { + testFooBoxIsTrue("plusAndMinusAsAnExpression.kt"); + } + + + public void testUsingModInCaseModAssignNotAvailable() throws Exception { + testFooBoxIsTrue("usingModInCaseModAssignNotAvailable.kt"); + } + + public void testOverloadPlusAssignArrayList() throws Exception { + testFooBoxIsOk("overloadPlusAssignArrayList.kt"); + } +} diff --git a/js/js.tests/test/org/jetbrains/k2js/test/PatternMatchingTest.java b/js/js.tests/test/org/jetbrains/k2js/test/PatternMatchingTest.java new file mode 100644 index 00000000000..d9ff84c1b0e --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/PatternMatchingTest.java @@ -0,0 +1,90 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test; + +import org.mozilla.javascript.JavaScriptException; + +/** + * @author Pavel Talanov + */ +public final class PatternMatchingTest extends TranslationTest { + + final private static String MAIN = "patternMatching/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + + public void testWhenType() throws Exception { + testFooBoxIsTrue("whenType.kt"); + } + + + public void testWhenNotType() throws Exception { + testFooBoxIsTrue("whenNotType.kt"); + } + + + public void testWhenExecutesOnlyOnce() throws Exception { + testFooBoxIsTrue("whenExecutesOnlyOnce.kt"); + } + + + public void testWhenValue() throws Exception { + testFooBoxIsTrue("whenValue.kt"); + } + + + public void testWhenNotValue() throws Exception { + testFooBoxIsTrue("whenNotValue.kt"); + } + + + public void testWhenValueOrType() throws Exception { + testFooBoxIsTrue("whenValueOrType.kt"); + } + + + public void testWhenWithIf() throws Exception { + testFooBoxIsTrue("whenWithIf.kt"); + } + + + public void testMultipleCases() throws Exception { + testFunctionOutput("multipleCases.kt", "foo", "box", 2.0); + } + + + public void testMatchNullableType() throws Exception { + testFooBoxIsTrue("matchNullableType.kt"); + } + + + public void testWhenAsExpression() throws Exception { + testFooBoxIsTrue("whenAsExpression.kt"); + } + + public void whenAsExpressionWithThrow() throws Exception { + try { + testFooBoxIsTrue("whenAsExpressionWithThrow.kt"); + fail(); + } catch (JavaScriptException e) { + } + } +} \ No newline at end of file diff --git a/js/js.tests/test/org/jetbrains/k2js/test/PropertyAccessTest.java b/js/js.tests/test/org/jetbrains/k2js/test/PropertyAccessTest.java new file mode 100644 index 00000000000..d08b4b1dd52 --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/PropertyAccessTest.java @@ -0,0 +1,99 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test; + +/** + * @author Pavel Talanov + */ +public final class PropertyAccessTest extends TranslationTest { + + final private static String MAIN = "propertyAccess/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + + public void testAccessToInstanceProperty() throws Exception { + testFooBoxIsTrue("accessToInstanceProperty.kt"); + } + + + public void testTwoClassesWithProperties() throws Exception { + testFooBoxIsTrue("twoClassesWithProperties.kt"); + } + + + public void testSetter() throws Exception { + testFunctionOutput("setter.kt", "foo", "f", 99.0); + } + + + public void testCustomGetter() throws Exception { + testFooBoxIsTrue("customGetter.kt"); + } + + + public void testCustomSetter() throws Exception { + testFooBoxIsTrue("customSetter.kt"); + } + + + public void testSafeCall() throws Exception { + testFooBoxIsTrue("safeCall.kt"); + } + + + //TODO: place safecalls under distinkt category + public void testSafeCallReturnsNullIfFails() throws Exception { + testFooBoxIsTrue("safeCallReturnsNullIfFails.kt"); + } + + + public void testNamespacePropertyInitializer() throws Exception { + testFooBoxIsTrue("namespacePropertyInitializer.kt"); + } + + + public void testNamespacePropertySet() throws Exception { + testFooBoxIsTrue("namespacePropertySet.kt"); + } + + public void testNamespaceCustomAccessors() throws Exception { + testFooBoxIsTrue("namespaceCustomAccessors.kt"); + } + + + public void testClassUsesNamespaceProperties() throws Exception { + testFooBoxIsTrue("classUsesNamespaceProperties.kt"); + } + + + public void testSafeAccess() throws Exception { + testFooBoxIsTrue("safeAccess.kt"); + } + + public void testSafeExtensionFunctionCall() throws Exception { + testFooBoxIsOk("safeExtensionFunctionCall.kt"); + } + + public void testExtensionLiteralSafeCall() throws Exception { + testFooBoxIsTrue("extensionLiteralSafeCall.kt"); + } + +} diff --git a/js/js.tests/test/org/jetbrains/k2js/test/RTTITest.java b/js/js.tests/test/org/jetbrains/k2js/test/RTTITest.java new file mode 100644 index 00000000000..03ea3e85b9c --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/RTTITest.java @@ -0,0 +1,38 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test; + +/** + * @author Pavel Talanov + */ +public class RTTITest extends TranslationTest { + + final private static String MAIN = "rtti/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + public void testIsSameClass() throws Exception { + testFooBoxIsTrue("isSameClass.kt"); + } + + public void testNotIsOtherClass() throws Exception { + testFooBoxIsTrue("notIsOtherClass.kt"); + } +} diff --git a/js/js.tests/test/org/jetbrains/k2js/test/RangeTest.java b/js/js.tests/test/org/jetbrains/k2js/test/RangeTest.java new file mode 100644 index 00000000000..fe3b89d2756 --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/RangeTest.java @@ -0,0 +1,49 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test; + +/** + * @author Pavel Talanov + */ +public final class RangeTest extends TranslationTest { + + final private static String MAIN = "range/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + + public void testExplicitRange() throws Exception { + testFooBoxIsTrue("explicitRange.kt"); + } + + + public void testRangeSugarSyntax() throws Exception { + testFooBoxIsTrue("rangeSugarSyntax.kt"); + } + + + public void testIntInRange() throws Exception { + testFooBoxIsTrue("intInRange.kt"); + } + + public void testIteratingOverRanges() throws Exception { + testFooBoxIsTrue("iteratingOverRanges.kt"); + } +} diff --git a/js/js.tests/test/org/jetbrains/k2js/test/RhinoFunctionResultChecker.java b/js/js.tests/test/org/jetbrains/k2js/test/RhinoFunctionResultChecker.java new file mode 100644 index 00000000000..5bd63ab7f78 --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/RhinoFunctionResultChecker.java @@ -0,0 +1,64 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test; + +import org.jetbrains.annotations.Nullable; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.Scriptable; + +import static org.junit.Assert.assertTrue; + +/** + * @author Pavel Talanov + */ +public final class RhinoFunctionResultChecker implements RhinoResultChecker { + + private final String namespaceName; + private final String functionName; + private final Object expectedResult; + + public RhinoFunctionResultChecker(@Nullable String namespaceName, String functionName, Object expectedResult) { + this.namespaceName = namespaceName; + this.functionName = functionName; + this.expectedResult = expectedResult; + } + + public RhinoFunctionResultChecker(String functionName, Object expectedResult) { + this(null, functionName, expectedResult); + } + + @Override + public void runChecks(Context context, Scriptable scope) throws Exception { + Object result = evaluateFunction(context, scope); + assertTrue("Result is not what expected! Expected: " + expectedResult + " Evaluated : " + result, + result.equals(expectedResult)); + String report = namespaceName + "." + functionName + "() = " + Context.toString(result); + System.out.println(report); + } + + private Object evaluateFunction(Context cx, Scriptable scope) { + return cx.evaluateString(scope, functionCallString(), "function call", 0, null); + } + + private String functionCallString() { + String result = functionName + "()"; + if (namespaceName != null) { + result = namespaceName + "." + result; + } + return result; + } +} diff --git a/js/js.tests/test/org/jetbrains/k2js/test/RhinoPropertyTypesChecker.java b/js/js.tests/test/org/jetbrains/k2js/test/RhinoPropertyTypesChecker.java new file mode 100644 index 00000000000..4787fc0f8e0 --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/RhinoPropertyTypesChecker.java @@ -0,0 +1,57 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test; + +import org.mozilla.javascript.Context; +import org.mozilla.javascript.NativeObject; +import org.mozilla.javascript.Scriptable; + +import java.util.Map; + +import static org.junit.Assert.assertTrue; + +/** + * @author Pavel Talanov + */ +public final class RhinoPropertyTypesChecker implements RhinoResultChecker { + + final private String objectName; + final private Map> propertyToType; + + public RhinoPropertyTypesChecker(String objectName, Map> propertyToType) { + this.objectName = objectName; + this.propertyToType = propertyToType; + } + + @Override + public void runChecks(Context context, Scriptable scope) throws Exception { + NativeObject object = RhinoUtils.extractObject(objectName, scope); + verifyObjectHasExpectedPropertiesOfExpectedTypes(object, propertyToType); + } + + private void verifyObjectHasExpectedPropertiesOfExpectedTypes + (NativeObject object, Map> nameToClassMap) { + for (Map.Entry> entry : nameToClassMap.entrySet()) { + String name = entry.getKey(); + Class expectedClass = entry.getValue(); + assertTrue(object + " must contain key " + name, object.containsKey(name)); + assertTrue(object + "'s property " + name + " must be of type " + expectedClass, + expectedClass.isInstance(object.get(name))); + } + } + +} diff --git a/js/js.tests/test/org/jetbrains/k2js/test/RhinoResultChecker.java b/js/js.tests/test/org/jetbrains/k2js/test/RhinoResultChecker.java new file mode 100644 index 00000000000..273895ed8a8 --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/RhinoResultChecker.java @@ -0,0 +1,27 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test; + +import org.mozilla.javascript.Context; +import org.mozilla.javascript.Scriptable; + +/** + * @author Pavel Talanov + */ +public interface RhinoResultChecker { + public void runChecks(Context context, Scriptable scope) throws Exception; +} diff --git a/js/js.tests/test/org/jetbrains/k2js/test/RhinoSystemOutputChecker.java b/js/js.tests/test/org/jetbrains/k2js/test/RhinoSystemOutputChecker.java new file mode 100644 index 00000000000..388041c14f5 --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/RhinoSystemOutputChecker.java @@ -0,0 +1,73 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.k2js.utils.GenerationUtils; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.Scriptable; + +import java.util.List; + +import static org.junit.Assert.assertTrue; + +/** + * @author Pavel Talanov + */ +public final class RhinoSystemOutputChecker implements RhinoResultChecker { + + private final List arguments; + private final String expectedResult; + + public RhinoSystemOutputChecker(String expectedResult, List arguments) { + this.expectedResult = expectedResult; + this.arguments = arguments; + } + + @Override + public void runChecks(@NotNull Context context, @NotNull Scriptable scope) + throws Exception { + runMain(context, scope); + String result = getSystemOutput(context, scope); + String trimmedExpected = trimSpace(expectedResult); + String trimmedActual = trimSpace(result); + // System.out.println(trimmedActual); + // System.out.println(trimmedExpected); + assertTrue("Returned:\n" + trimmedActual + "END_OF_RETURNED\nExpected:\n" + trimmedExpected + + "END_OF_EXPECTED\n", trimmedExpected.equals(trimmedActual)); + } + + private String getSystemOutput(@NotNull Context context, @NotNull Scriptable scope) { + Object output = context.evaluateString(scope, "Kotlin.System.output()", "test", 0, null); + assertTrue("Output should be a string.", output instanceof String); + return (String) output; + } + + private void runMain(Context context, Scriptable scope) { + String callToMain = GenerationUtils.generateCallToMain("Anonymous", arguments); + context.evaluateString(scope, callToMain, "function call", 0, null); + } + + public String trimSpace(String s) { + String[] choppedUpString = s.trim().split("\\s"); + StringBuilder sb = new StringBuilder(); + for (String word : choppedUpString) { + sb.append(word); + } + return sb.toString(); + } +} diff --git a/js/js.tests/test/org/jetbrains/k2js/test/RhinoUtils.java b/js/js.tests/test/org/jetbrains/k2js/test/RhinoUtils.java new file mode 100644 index 00000000000..6ddf610539d --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/RhinoUtils.java @@ -0,0 +1,38 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test; + +import org.mozilla.javascript.NativeObject; +import org.mozilla.javascript.Scriptable; + +import static org.junit.Assert.assertTrue; + +/** + * @author Pavel Talanov + */ +public final class RhinoUtils { + + private RhinoUtils() { + + } + + public static NativeObject extractObject(String objectName, Scriptable scope) { + Object nativeObject = scope.get(objectName, scope); + assertTrue(objectName + " should be JSON Object", nativeObject instanceof NativeObject); + return (NativeObject) nativeObject; + } +} diff --git a/js/js.tests/test/org/jetbrains/k2js/test/SimpleTestSuite.java b/js/js.tests/test/org/jetbrains/k2js/test/SimpleTestSuite.java new file mode 100644 index 00000000000..6f8f741ca4f --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/SimpleTestSuite.java @@ -0,0 +1,36 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test; + +import com.intellij.testFramework.UsefulTestCase; +import junit.framework.Test; +import org.jetbrains.annotations.NotNull; + +/** + * @author Pavel Talanov + */ +public final class SimpleTestSuite extends UsefulTestCase { + + public static Test suite() { + return Suite.suiteForDirectory("simple/", new Suite.SingleFileTester() { + @Override + public void performTest(@NotNull Suite test, @NotNull String filename) throws Exception { + test.testFooBoxIsTrue(filename); + } + }); + } +} diff --git a/js/js.tests/test/org/jetbrains/k2js/test/StandardClassesTest.java b/js/js.tests/test/org/jetbrains/k2js/test/StandardClassesTest.java new file mode 100644 index 00000000000..95b2cbedcf1 --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/StandardClassesTest.java @@ -0,0 +1,66 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test; + +/** + * @author Pavel Talanov + */ +public class StandardClassesTest extends TranslationTest { + final private static String MAIN = "standardClasses/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + + public void testArray() throws Exception { + testFooBoxIsTrue("array.kt"); + } + + + public void testArrayAccess() throws Exception { + testFooBoxIsTrue("arrayAccess.kt"); + } + + + public void testArrayIsFilledWithNulls() throws Exception { + testFooBoxIsTrue("arrayIsFilledWithNulls.kt"); + } + + + public void testArrayFunctionConstructor() throws Exception { + testFooBoxIsTrue("arrayFunctionConstructor.kt"); + } + + + public void testArraySize() throws Exception { + testFooBoxIsTrue("arraySize.kt"); + } + + //TODO: this feature in not supported for some time + //TODO: support it. Probably configurable. +// (expected = JavaScriptException.class) +// public void arrayThrowsExceptionOnOOBaccess() throws Exception { +// testFooBoxIsTrue("arrayThrowsExceptionOnOOBaccess.kt"); +// } + + + public void testArraysIterator() throws Exception { + testFooBoxIsTrue("arraysIterator.kt"); + } +} diff --git a/js/js.tests/test/org/jetbrains/k2js/test/StringTest.java b/js/js.tests/test/org/jetbrains/k2js/test/StringTest.java new file mode 100644 index 00000000000..bf45441f9e7 --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/StringTest.java @@ -0,0 +1,54 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test; + +/** + * @author Pavel Talanov + */ +public class StringTest extends AbstractExpressionTest { + + final private static String MAIN = "string/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + public void testStringConstant() throws Exception { + testFooBoxIsTrue("stringConstant.kt"); + } + + public void testStringAssignment() throws Exception { + testFooBoxIsTrue("stringAssignment.kt"); + } + + public void testIntInTemplate() throws Exception { + testFunctionOutput("intInTemplate.kt", "foo", "box", "my age is 3"); + } + + public void testStringInTemplate() throws Exception { + testFunctionOutput("stringInTemplate.kt", "foo", "box", "oHelloo"); + } + + public void testMultipleExpressionInTemplate() throws Exception { + testFunctionOutput("multipleExpressionsInTemplate.kt", "foo", "box", "left = 3\nright = 2\nsum = 5\n"); + } + + public void testToStringMethod() throws Exception { + testFooBoxIsTrue("objectToStringCallInTemplate.kt"); + } +} diff --git a/js/js.tests/test/org/jetbrains/k2js/test/Suite.java b/js/js.tests/test/org/jetbrains/k2js/test/Suite.java new file mode 100644 index 00000000000..095557125af --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/Suite.java @@ -0,0 +1,77 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test; + +import junit.framework.Test; +import org.jetbrains.annotations.NotNull; + +/** + * @author Pavel Talanov + */ +//TODO: this class has strange behaviour. Should be refactored. +public final class Suite extends TranslationTest { + + private String name; + private final SingleFileTester tester; + private final String testMain; + + public Suite(@NotNull String testName, + @NotNull String suiteDirName, + @NotNull final SingleFileTester tester) { + this.name = testName; + this.tester = tester; + this.testMain = suiteDirName; + } + + public Suite() { + this("dummy", "dummy", new SingleFileTester() { + @Override + public void performTest(@NotNull Suite test, @NotNull String filename) throws Exception { + //do nothing + } + }); + } + + //NOTE: just to avoid warning + public void testNothing() { + } + + @Override + protected String mainDirectory() { + return testMain; + } + + public void runTest() throws Exception { + tester.performTest(this, name); + } + + public static Test suiteForDirectory(@NotNull final String mainName, @NotNull final SingleFileTester testMethod) { + + return TranslatorTestCaseBuilder.suiteForDirectory("translator\\testFiles\\", + mainName + casesDirectoryName(), true, new TranslatorTestCaseBuilder.NamedTestFactory() { + @NotNull + @Override + public Test createTest(@NotNull String name) { + return (new Suite(name, mainName, testMethod)); + } + }); + } + + protected static interface SingleFileTester { + void performTest(@NotNull Suite test, @NotNull String filename) throws Exception; + } +} diff --git a/js/js.tests/test/org/jetbrains/k2js/test/TraitTest.java b/js/js.tests/test/org/jetbrains/k2js/test/TraitTest.java new file mode 100644 index 00000000000..f58d18d038f --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/TraitTest.java @@ -0,0 +1,70 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test; + +/** + * @author Pavel Talanov + */ +public final class TraitTest extends TranslationTest { + + final private static String MAIN = "trait/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + + public void testTraitAddsFunctionsToClass() throws Exception { + testFooBoxIsTrue("traitAddsFunctionsToClass.kt"); + } + + + public void testClassDerivesFromClassAndTrait() throws Exception { + testFooBoxIsTrue("classDerivesFromClassAndTrait.kt"); + } + + + public void testClassDerivesFromTraitAndClass() throws Exception { + testFooBoxIsTrue("classDerivesFromTraitAndClass.kt"); + } + + + public void testExample() throws Exception { + testFooBoxIsTrue("example.kt"); + } + + + public void testTraitExtendsTrait() throws Exception { + testFooBoxIsTrue("traitExtendsTrait.kt"); + } + + + public void testTraitExtendsTwoTraits() throws Exception { + testFooBoxIsTrue("traitExtendsTwoTraits.kt"); + } + + + public void testFunDelegation() throws Exception { + testFooBoxIsOk("funDelegation.jet"); + } + + + public void testDefinitionOrder() throws Exception { + testFooBoxIsTrue("definitionOrder.kt"); + } +} diff --git a/js/js.tests/test/org/jetbrains/k2js/test/TranslationTest.java b/js/js.tests/test/org/jetbrains/k2js/test/TranslationTest.java new file mode 100644 index 00000000000..f46514ed2e4 --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/TranslationTest.java @@ -0,0 +1,188 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test; + +import org.jetbrains.k2js.facade.K2JSTranslator; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.Scriptable; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileReader; +import java.io.IOException; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +//TODO: spread the test* methods amongst classes that actually use them + +/** + * @author Pavel Talanov + */ +public abstract class TranslationTest extends BaseTest { + + private final static String TEST_FILES = "translator/testFiles/"; + private static final String CASES = "cases/"; + private static final String OUT = "out/"; + private static final String KOTLIN_JS_LIB = TEST_FILES + "kotlin_lib.js"; + private static final String EXPECTED = "expected/"; + + protected String kotlinLibraryPath() { + return KOTLIN_JS_LIB; + } + + protected static String casesDirectoryName() { + return CASES; + } + + private static String outDirectoryName() { + return OUT; + } + + protected abstract String mainDirectory(); + + private String testFilesPath() { + return TEST_FILES + suiteDirectoryName() + mainDirectory(); + } + + protected String suiteDirectoryName() { + return ""; + } + + private String getOutputPath() { + return testFilesPath() + outDirectoryName(); + } + + protected String getInputPath() { + return testFilesPath() + casesDirectoryName(); + } + + private String getExpectedPath() { + return testFilesPath() + expectedDirectoryName(); + } + + private String expectedDirectoryName() { + return EXPECTED; + } + + protected void testFunctionOutput(String filename, String namespaceName, + String functionName, Object expectedResult) throws Exception { + translateFile(filename); + runRhinoTest(generateFilenameList(getOutputFilePath(filename)), + new RhinoFunctionResultChecker(namespaceName, functionName, expectedResult)); + } + + protected void testMultiFile(String dirName, String namespaceName, + String functionName, Object expectedResult) throws Exception { + translateFilesInDir(dirName); + runRhinoTest(generateFilenameList(getOutputFilePath(dirName + ".kt")), + new RhinoFunctionResultChecker(namespaceName, functionName, expectedResult)); + } + + protected void translateFile(String filename) throws Exception { + K2JSTranslator.testTranslateFile(getInputFilePath(filename), + getOutputFilePath(filename)); + } + + protected void translateFilesInDir(String dirName) throws Exception { + String dirPath = getInputFilePath(dirName); + File dir = new File(dirPath); + List fullFilePaths = new ArrayList(); + for (String fileName : dir.list()) { + fullFilePaths.add(getInputFilePath(dirName) + "\\" + fileName); + } + assert dir.isDirectory(); + K2JSTranslator.testTranslateFiles(fullFilePaths, + getOutputFilePath(dirName + ".kt")); + } + + protected List generateFilenameList(String inputFile) { + return Arrays.asList(kotlinLibraryPath(), inputFile); + } + + //TODO: refactor filename generation logic + protected String getOutputFilePath(String filename) { + return getOutputPath() + convertToDotJsFile(filename); + } + + private String convertToDotJsFile(String filename) { + return filename.substring(0, filename.lastIndexOf('.')) + ".js"; + } + + private String getInputFilePath(String filename) { + return getInputPath() + filename; + } + + protected String cases(String filename) { + return getInputFilePath(filename); + } + + private String expected(String testName) { + return getExpectedPath() + testName + ".out"; + } + + protected void runFileWithRhino(String inputFile, Context context, Scriptable scope) throws Exception { + FileReader reader = new FileReader(inputFile); + context.evaluateReader(scope, reader, inputFile, 1, null); + reader.close(); + } + + protected void runRhinoTest(List fileNames, RhinoResultChecker checker) throws Exception { + Context context = Context.enter(); + Scriptable scope = context.initStandardObjects(); + for (String filename : fileNames) { + runFileWithRhino(filename, context, scope); + } + checker.runChecks(context, scope); + Context.exit(); + } + + public void testFooBoxIsTrue(String filename) throws Exception { + testFunctionOutput(filename, "foo", "box", true); + } + + public void testFooBoxIsOk(String filename) throws Exception { + testFunctionOutput(filename, "foo", "box", "OK"); + } + + protected void checkOutput(String filename, String expectedResult, String... args) throws Exception { + translateFile(filename); + runRhinoTest(generateFilenameList(getOutputFilePath(filename)), + new RhinoSystemOutputChecker(expectedResult, Arrays.asList(args))); + } + + protected void testWithMain(String testName, String testId, String... args) throws Exception { + checkOutput(testName + ".kt", readFile(expected(testName + testId)), args); + } + + private static String readFile(String path) throws IOException { + FileInputStream stream = new FileInputStream(new File(path)); + try { + FileChannel fc = stream.getChannel(); + MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()); + /* Instead of using default, pass in a decoder. */ + return Charset.defaultCharset().decode(bb).toString(); + } finally { + stream.close(); + } + } + + +} diff --git a/js/js.tests/test/org/jetbrains/k2js/test/TranslatorTestCaseBuilder.java b/js/js.tests/test/org/jetbrains/k2js/test/TranslatorTestCaseBuilder.java new file mode 100644 index 00000000000..ff9a8ed4c78 --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/TranslatorTestCaseBuilder.java @@ -0,0 +1,106 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test; + +import junit.framework.Test; +import junit.framework.TestSuite; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.FileFilter; +import java.io.FilenameFilter; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * @author Pavel Talanov + */ +public abstract class TranslatorTestCaseBuilder { + public static FilenameFilter emptyFilter = new FilenameFilter() { + @Override + public boolean accept(File file, String name) { + return true; + } + }; + + public interface NamedTestFactory { + @NotNull + Test createTest(@NotNull String filename); + } + + @NotNull + public static TestSuite suiteForDirectory(String baseDataDir, @NotNull final String dataPath, + boolean recursive, @NotNull NamedTestFactory factory) { + return suiteForDirectory(baseDataDir, dataPath, recursive, emptyFilter, factory); + } + + @NotNull + public static TestSuite suiteForDirectory(String baseDataDir, @NotNull final String dataPath, boolean recursive, + final FilenameFilter filter, @NotNull NamedTestFactory factory) { + TestSuite suite = new TestSuite(dataPath); + appendTestsInDirectory(baseDataDir, dataPath, recursive, filter, factory, suite); + return suite; + } + + public static void appendTestsInDirectory(String baseDataDir, String dataPath, boolean recursive, + final FilenameFilter filter, NamedTestFactory factory, TestSuite suite) { + final String extensionJet = ".jet"; + final String extensionKt = ".kt"; + final FilenameFilter extensionFilter = new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return name.endsWith(extensionJet) || name.endsWith(extensionKt); + } + }; + FilenameFilter resultFilter; + if (filter != emptyFilter) { + resultFilter = new FilenameFilter() { + @Override + public boolean accept(File file, String s) { + return extensionFilter.accept(file, s) && filter.accept(file, s); + } + }; + } else { + resultFilter = extensionFilter; + } + File dir = new File(baseDataDir + dataPath); + FileFilter dirFilter = new FileFilter() { + @Override + public boolean accept(File pathname) { + return pathname.isDirectory(); + } + }; + if (recursive) { + File[] files = dir.listFiles(dirFilter); + assert files != null : dir; + List subdirs = Arrays.asList(files); + Collections.sort(subdirs); + for (File subdir : subdirs) { + suite.addTest(suiteForDirectory(baseDataDir, dataPath + "/" + subdir.getName(), recursive, filter, factory)); + } + } + List files = Arrays.asList(dir.listFiles(resultFilter)); + Collections.sort(files); + for (File file : files) { + String fileName = file.getName(); + assert fileName != null; + String extension = fileName.endsWith(extensionJet) ? extensionJet : extensionKt; + suite.addTest(factory.createTest(fileName)); + } + } +} diff --git a/js/js.tests/test/org/jetbrains/k2js/test/TupleTest.java b/js/js.tests/test/org/jetbrains/k2js/test/TupleTest.java new file mode 100644 index 00000000000..d40b350d90f --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/TupleTest.java @@ -0,0 +1,41 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test; + +/** + * @author Pavel Talanov + */ +public final class TupleTest extends TranslationTest { + + final private static String MAIN = "tuple/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + public void testTwoElements() throws Exception { + testFooBoxIsTrue("twoElements.kt"); + } + + public void testMultipleMembers() throws Exception { + testFooBoxIsTrue("multipleMembers.kt"); + } + + +} + diff --git a/js/js.tests/test/org/jetbrains/k2js/test/WebDemoExamples1Test.java b/js/js.tests/test/org/jetbrains/k2js/test/WebDemoExamples1Test.java new file mode 100644 index 00000000000..7490e89ca94 --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/WebDemoExamples1Test.java @@ -0,0 +1,100 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test; + +/** + * @author Pavel Talanov + */ +public final class WebDemoExamples1Test extends TranslationTest { + + final private static String MAIN = "webDemoExamples1/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + + public void testPrintArg() throws Exception { + checkOutput("printArg.kt", "Hello, world!", "Hello, world!"); + } + + + public void testWhileLoop() throws Exception { + checkOutput("whileLoop.kt", "guest1\nguest2\nguest3\nguest4\n", "guest1", "guest2", "guest3", "guest4"); + } + + + public void testIfAsExpression() throws Exception { + checkOutput("ifAsExpression.kt", "20\n", "10", "20"); + } + + + public void testObjectOrientedHello() throws Exception { + checkOutput("objectOrientedHello.kt", "Hello, Pavel!\n", "Pavel"); + } + + + public void testMultiLanguageHello() throws Exception { + checkOutput("multiLanguageHello.kt", "Salut!\n", "FR"); + } + + + public void testNullChecks() throws Exception { + checkOutput("nullChecks.kt", "No number supplied"); + checkOutput("nullChecks.kt", "6", "2", "3"); + } + + + public void testRanges() throws Exception { + checkOutput("ranges.kt", "OK\n" + + " 1 2 3 4 5\n" + + "Out: array has only 3 elements. x = 4\n" + + "Yes: array contains aaa\n" + + "No: array doesn't contains ddd\n", "4"); + + checkOutput("ranges.kt", " 1 2 3 4 5\n" + + "Out: array has only 3 elements. x = 10\n" + + "Yes: array contains aaa\n" + + "No: array doesn't contains ddd\n", "10"); + } + + + public void testForLoop() throws Exception { + checkOutput("forLoop.kt", "a\n" + + "b\n" + + "c\n" + + "\n" + + "a\n" + + "b\n" + + "c\n", "a", "b", "c"); + checkOutput("forLoop.kt", "123\n\n123\n", "123"); + } + + + public void testIsCheck() throws Exception { + checkOutput("isCheck.kt", "3\nnull\n"); + } + + + public void testPatternMatching() throws Exception { + checkOutput("patternMatching.kt", "Greeting\n" + + "One\n" + + "Not a string\n" + + "Unknown\n"); + } +} diff --git a/js/js.tests/test/org/jetbrains/k2js/test/WebDemoExamples2Test.java b/js/js.tests/test/org/jetbrains/k2js/test/WebDemoExamples2Test.java new file mode 100644 index 00000000000..1eb409c2b8a --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/WebDemoExamples2Test.java @@ -0,0 +1,50 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test; + +/** + * @author Pavel Talanov + */ +public final class WebDemoExamples2Test extends TranslationTest { + + final private static String MAIN = "webDemoExamples2/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + public void testBottles() throws Exception { + testWithMain("bottles", "2", "2"); + testWithMain("bottles", ""); + } + + public void testLife() throws Exception { + testWithMain("life", "", "2"); + } + + public void testBuilder() throws Exception { + testWithMain("builder", ""); + testWithMain("builder", "1", "over9000"); + } + + //TODO: comparator LinkedList dependencies +// @Test +// public void maze() throws Exception { +// testWithMain("maze", ""); +// } +} diff --git a/js/k2js.iml b/js/k2js.iml new file mode 100644 index 00000000000..af012ea065d --- /dev/null +++ b/js/k2js.iml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + +