From f806af48293ed2e0a1a347e3efcd1337af161cdc Mon Sep 17 00:00:00 2001 From: Pavel Talanov Date: Mon, 21 Nov 2011 22:13:32 +0400 Subject: [PATCH] Work on namespaces in progress. --- .../translate/AbstractInitializerVisitor.java | 74 +++++++++++++ .../translate/ClassDeclarationTranslator.java | 102 ++++++++++++++++++ ...itor.java => ClassInitializerVisitor.java} | 65 ++--------- .../k2js/translate/ClassTranslator.java | 51 +++------ ...sitor.java => DeclarationBodyVisitor.java} | 33 ++++-- .../k2js/translate/GenerationState.java | 3 +- .../k2js/translate/InitializerGenerator.java | 52 +++++++++ .../org/jetbrains/k2js/translate/Namer.java | 4 + .../NamespaceDeclarationTranslator.java | 29 ----- .../NamespaceDeclarationVisitor.java | 47 -------- .../NamespaceInitializerVisitor.java | 31 ++++++ .../k2js/translate/NamespaceTranslator.java | 65 +++++------ .../k2js/translate/ReferenceProvider.java | 10 +- .../jetbrains/k2js/translate/Translation.java | 19 ++-- .../k2js/test/AbstractExpressionTest.java | 10 +- .../jetbrains/k2js/test/KotlinLibTest.java | 35 ++++-- .../k2js/test/PropertyAccessorTest.java | 10 ++ .../k2js/test/RhinoFunctionResultChecker.java | 41 ++----- .../expression/function/cases/loopClosure.kt | 2 +- .../testFiles/kotlinLib/cases/namespace.js | 10 ++ .../testFiles/kotlinLib/cases/namespace2.js | 35 ++++++ .../kotlinLib/cases/namespaceWithClasses.js | 45 ++++++++ translator/testFiles/kotlin_lib.js | 8 +- .../cases/namespacePropertyInitializer.kt | 5 + .../cases/namespacePropertySet.kt | 8 ++ 25 files changed, 512 insertions(+), 282 deletions(-) create mode 100644 translator/src/org/jetbrains/k2js/translate/AbstractInitializerVisitor.java create mode 100644 translator/src/org/jetbrains/k2js/translate/ClassDeclarationTranslator.java rename translator/src/org/jetbrains/k2js/translate/{InitializerVisitor.java => ClassInitializerVisitor.java} (57%) rename translator/src/org/jetbrains/k2js/translate/{ClassBodyVisitor.java => DeclarationBodyVisitor.java} (87%) create mode 100644 translator/src/org/jetbrains/k2js/translate/InitializerGenerator.java delete mode 100644 translator/src/org/jetbrains/k2js/translate/NamespaceDeclarationTranslator.java delete mode 100644 translator/src/org/jetbrains/k2js/translate/NamespaceDeclarationVisitor.java create mode 100644 translator/src/org/jetbrains/k2js/translate/NamespaceInitializerVisitor.java create mode 100644 translator/testFiles/kotlinLib/cases/namespace.js create mode 100644 translator/testFiles/kotlinLib/cases/namespace2.js create mode 100644 translator/testFiles/kotlinLib/cases/namespaceWithClasses.js create mode 100644 translator/testFiles/propertyAccess/cases/namespacePropertyInitializer.kt create mode 100644 translator/testFiles/propertyAccess/cases/namespacePropertySet.kt diff --git a/translator/src/org/jetbrains/k2js/translate/AbstractInitializerVisitor.java b/translator/src/org/jetbrains/k2js/translate/AbstractInitializerVisitor.java new file mode 100644 index 00000000000..e2a4044a211 --- /dev/null +++ b/translator/src/org/jetbrains/k2js/translate/AbstractInitializerVisitor.java @@ -0,0 +1,74 @@ +package org.jetbrains.k2js.translate; + +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsFunction; +import com.google.dart.compiler.backend.js.ast.JsScope; +import com.google.dart.compiler.backend.js.ast.JsStatement; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.JetClassInitializer; +import org.jetbrains.jet.lang.psi.JetDeclaration; +import org.jetbrains.jet.lang.psi.JetExpression; +import org.jetbrains.jet.lang.psi.JetProperty; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * @author Talanov Pavel + */ +public abstract class AbstractInitializerVisitor extends TranslatorVisitor> { + + @NotNull + protected final JsScope initializerMethodScope; + + @NotNull + protected final TranslationContext initializerMethodContext; + + protected AbstractInitializerVisitor(@NotNull TranslationContext enclosingContext, @NotNull JsScope newScope) { + this.initializerMethodContext = enclosingContext.newEnclosingScope(newScope); + this.initializerMethodScope = newScope; + } + + @NotNull + abstract protected JsFunction generate(); + + @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); + } + + @NotNull + JsStatement assignmentToBackingField(@NotNull JetProperty property, @NotNull JsExpression initExpression, + @NotNull TranslationContext context) { + + return AstUtil.newAssignmentStatement(backingFieldReference(property, context), 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 new ArrayList(); + } +} diff --git a/translator/src/org/jetbrains/k2js/translate/ClassDeclarationTranslator.java b/translator/src/org/jetbrains/k2js/translate/ClassDeclarationTranslator.java new file mode 100644 index 00000000000..fe5cc6fd541 --- /dev/null +++ b/translator/src/org/jetbrains/k2js/translate/ClassDeclarationTranslator.java @@ -0,0 +1,102 @@ +package org.jetbrains.k2js.translate; + +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.JetClass; +import org.jetbrains.jet.lang.psi.JetDeclaration; +import org.jetbrains.jet.lang.psi.JetNamespace; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author Talanov Pavel + */ +//TODO: implement +public final class ClassDeclarationTranslator extends AbstractTranslator { + + @NotNull + private final JetNamespace namespace; + @NotNull + private final Map localToGlobalClassName; + @NotNull + private final JsScope dummyFunctionScope; + @Nullable + private JsName declarationsObject = null; + @Nullable + private JsStatement declarationsStatement = null; + + public ClassDeclarationTranslator(@NotNull TranslationContext context, @NotNull JetNamespace namespace) { + super(context); + this.namespace = namespace; + this.localToGlobalClassName = new HashMap(); + this.dummyFunctionScope = new JsScope(translationContext().enclosingScope(), "class declaration function"); + } + + public void generateDeclarations() { + //TODO: hardcoded string + declarationsObject = translationContext().enclosingScope().declareName("classes"); + declarationsStatement = + AstUtil.newAssignmentStatement(declarationsObject.makeRef(), 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 = getClassDeclarationStatements(); + 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 getClassDeclarationStatements() { + List classDeclarations = new ArrayList(); + for (JetDeclaration declaration : namespace.getDeclarations()) { + if (declaration instanceof JetClass) { + classDeclarations.add(generateDeclaration((JetClass) declaration)); + } + } + return classDeclarations; + } + + @NotNull + private JsStatement generateDeclaration(@NotNull JetClass declaration) { + JsName globalClassName = translationContext().getNameForElement(declaration); + JsName localClassName = dummyFunctionScope.declareName(globalClassName.getIdent()); + localToGlobalClassName.put(localClassName, globalClassName); + JsInvocation classDeclarationExpression = + Translation.classTranslator(translationContext()).translateClass(declaration); + return AstUtil.newVar(localClassName, classDeclarationExpression); + } +} diff --git a/translator/src/org/jetbrains/k2js/translate/InitializerVisitor.java b/translator/src/org/jetbrains/k2js/translate/ClassInitializerVisitor.java similarity index 57% rename from translator/src/org/jetbrains/k2js/translate/InitializerVisitor.java rename to translator/src/org/jetbrains/k2js/translate/ClassInitializerVisitor.java index 0ca9b05165b..d5fc56f3f40 100644 --- a/translator/src/org/jetbrains/k2js/translate/InitializerVisitor.java +++ b/translator/src/org/jetbrains/k2js/translate/ClassInitializerVisitor.java @@ -6,34 +6,25 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.psi.*; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; /** * @author Talanov Pavel - *

- * This visitor collects all initializers from a given class into JsFunction object. - * Note: we do use visitor pattern to preserve the order in which initializers are executed. */ -public class InitializerVisitor extends TranslatorVisitor> { +public final class ClassInitializerVisitor extends AbstractInitializerVisitor { - @NotNull - private final JsScope initializerMethodScope; @NotNull private final JetClass classDeclaration; - @NotNull - private final TranslationContext initializerMethodContext; - public InitializerVisitor(@NotNull JetClass classDeclaration, @NotNull TranslationContext context) { - this.initializerMethodScope = new JsScope(context.getScopeForElement(classDeclaration), - "initializer " + classDeclaration.getName()); + public ClassInitializerVisitor(@NotNull JetClass classDeclaration, @NotNull TranslationContext context) { + super(context, new JsScope(context.getScopeForElement(classDeclaration), + "initializer " + classDeclaration.getName())); this.classDeclaration = classDeclaration; - this.initializerMethodContext = context.newEnclosingScope(initializerMethodScope); - } + @Override @NotNull - public JsFunction generateInitializeMethod() { + public JsFunction generate() { JsFunction result = JsFunction.getAnonymousFunctionWithScope(initializerMethodScope); result.setParameters(translatePrimaryConstructorParameters(classDeclaration)); result.setBody(generateInitializerMethodBody(classDeclaration, initializerMethodContext)); @@ -102,48 +93,4 @@ public class InitializerVisitor extends TranslatorVisitor> { } return result; } - - @Override - @NotNull - public List visitProperty(@NotNull JetProperty expression, @NotNull TranslationContext context) { - JetExpression initializer = expression.getInitializer(); - declareBackingFieldName(expression); - if (initializer == null) { - return new ArrayList(); - } - return Arrays.asList(translateInitializer(expression, context, initializer)); - } - - private void declareBackingFieldName(@NotNull JetProperty property) { - initializerMethodScope.declareName(Namer.getKotlinBackingFieldName(property.getName())); - } - - @NotNull - private JsStatement translateInitializer(@NotNull JetProperty property, @NotNull TranslationContext context, - @NotNull JetExpression initializer) { - JsExpression initExpression = Translation.translateAsExpression(initializer, context); - return assignmentToBackingField(property, initExpression, context); - } - - @NotNull - JsStatement assignmentToBackingField(@NotNull JetProperty property, @NotNull JsExpression initExpression, - @NotNull TranslationContext context) { - - return AstUtil.newAssignmentStatement(backingFieldReference(property, context), 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 new ArrayList(); - } - } diff --git a/translator/src/org/jetbrains/k2js/translate/ClassTranslator.java b/translator/src/org/jetbrains/k2js/translate/ClassTranslator.java index 44e6e51ec58..9b4bfa6efb3 100644 --- a/translator/src/org/jetbrains/k2js/translate/ClassTranslator.java +++ b/translator/src/org/jetbrains/k2js/translate/ClassTranslator.java @@ -1,6 +1,9 @@ package org.jetbrains.k2js.translate; -import com.google.dart.compiler.backend.js.ast.*; +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.JsObjectLiteral; +import com.google.dart.compiler.backend.js.ast.JsPropertyInitializer; import com.google.dart.compiler.util.AstUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -19,7 +22,7 @@ import java.util.List; public final class ClassTranslator extends AbstractTranslator { @NotNull - private final ClassBodyVisitor classBodyVisitor = new ClassBodyVisitor(); + private final DeclarationBodyVisitor declarationBodyVisitor = new DeclarationBodyVisitor(); @NotNull public static ClassTranslator newInstance(@NotNull TranslationContext context) { @@ -31,11 +34,11 @@ public final class ClassTranslator extends AbstractTranslator { } @NotNull - public JsStatement translateClass(@NotNull JetClass jetClassDeclaration) { + public JsInvocation translateClass(@NotNull JetClass jetClassDeclaration) { JsInvocation jsClassDeclaration = classCreateMethodInvocation(jetClassDeclaration); addSuperclassReferences(jetClassDeclaration, jsClassDeclaration); addClassOwnDeclarations(jetClassDeclaration, jsClassDeclaration); - return classDeclarationStatement(jetClassDeclaration, jsClassDeclaration); + return jsClassDeclaration; } @NotNull @@ -47,18 +50,13 @@ public final class ClassTranslator extends AbstractTranslator { } } - @NotNull - private JsStatement classDeclarationStatement(@NotNull JetClass classDeclaration, - @NotNull JsInvocation jsClassDeclaration) { - return AstUtil.newAssignmentStatement - (namespaceQualifiedClassNameReference(classDeclaration), jsClassDeclaration); - } - - @NotNull - private JsNameRef namespaceQualifiedClassNameReference(@NotNull JetClass classDeclaration) { - return translationContext().getNamespaceQualifiedReference - (translationContext().getNameForElement(classDeclaration)); - } + //TODO +// @NotNull +// private JsStatement classDeclarationStatement(@NotNull JetClass classDeclaration, +// @NotNull JsInvocation jsClassDeclaration) { +// JsNameRef unqualifiedClassNameReference = translationContext().getNameForElement(classDeclaration).makeRef(); +// return AstUtil.newAssignmentStatement(unqualifiedClassNameReference, jsClassDeclaration); +// } private void addClassOwnDeclarations(@NotNull JetClass classDeclaration, @NotNull JsInvocation jsClassDeclaration) { @@ -119,27 +117,10 @@ public final class ClassTranslator extends AbstractTranslator { private JsObjectLiteral translateClassDeclarations(@NotNull JetClass classDeclaration) { List propertyList = new ArrayList(); if (!classDeclaration.isTrait()) { - propertyList.add(generateInitializeMethod(classDeclaration)); + propertyList.add(InitializerGenerator.generateInitializeMethod(classDeclaration, translationContext())); } - propertyList.addAll(classDeclaration.accept(classBodyVisitor, + propertyList.addAll(declarationBodyVisitor.traverseClass(classDeclaration, translationContext().newClass(classDeclaration))); return new JsObjectLiteral(propertyList); } - - // TODO: names are inconsistent - @NotNull - private JsPropertyInitializer generateInitializeMethod(@NotNull JetClass classDeclaration) { - JsPropertyInitializer initializer = new JsPropertyInitializer(); - initializer.setLabelExpr(program().getStringLiteral(Namer.INITIALIZE_METHOD_NAME)); - initializer.setValueExpr(generateInitializeMethodBody(classDeclaration)); - return initializer; - } - - @NotNull - private JsFunction generateInitializeMethodBody(@NotNull JetClass classDeclaration) { - InitializerVisitor initializerVisitor = new InitializerVisitor(classDeclaration, - translationContext().newClass(classDeclaration)); - return initializerVisitor.generateInitializeMethod(); - } - } diff --git a/translator/src/org/jetbrains/k2js/translate/ClassBodyVisitor.java b/translator/src/org/jetbrains/k2js/translate/DeclarationBodyVisitor.java similarity index 87% rename from translator/src/org/jetbrains/k2js/translate/ClassBodyVisitor.java rename to translator/src/org/jetbrains/k2js/translate/DeclarationBodyVisitor.java index 0efaf5f30bc..056269c259d 100644 --- a/translator/src/org/jetbrains/k2js/translate/ClassBodyVisitor.java +++ b/translator/src/org/jetbrains/k2js/translate/DeclarationBodyVisitor.java @@ -9,16 +9,18 @@ import org.jetbrains.jet.lang.psi.*; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; /** * @author Talanov Pavel */ -public final class ClassBodyVisitor extends TranslatorVisitor> { +public final class DeclarationBodyVisitor extends TranslatorVisitor> { + - @Override @NotNull - public List visitClass(@NotNull JetClass expression, @NotNull TranslationContext context) { + public List traverseClass(@NotNull JetClass expression, + @NotNull TranslationContext context) { List properties = new ArrayList(); for (JetDeclaration declaration : expression.getDeclarations()) { properties.addAll(declaration.accept(this, context)); @@ -26,13 +28,28 @@ public final class ClassBodyVisitor extends TranslatorVisitor traverseNamespace(@NotNull JetNamespace expression, + @NotNull TranslationContext context) { + List properties = new ArrayList(); + for (JetDeclaration declaration : expression.getDeclarations()) { + properties.addAll(declaration.accept(this, context)); + } + return properties; + } + + //TODO + @Override + @NotNull + public List visitClass(@NotNull JetClass expression, @NotNull TranslationContext context) { + //return Arrays.asList(Translation.classTranslator(context).translateClass(expression)); + return Collections.emptyList(); + } + @Override @NotNull - // method declaration public List visitNamedFunction(@NotNull JetNamedFunction expression, @NotNull TranslationContext context) { - List properties = new ArrayList(); - properties.add(Translation.functionTranslator(context).translateAsMethod(expression)); - return properties; + return Arrays.asList(Translation.functionTranslator(context).translateAsMethod(expression)); } @Override @@ -145,6 +162,6 @@ public final class ClassBodyVisitor extends TranslatorVisitor visitAnonymousInitializer(@NotNull JetClassInitializer expression, @NotNull TranslationContext context) { // parsed it in initializer visitor => no additional actions are needed - return new ArrayList(); + return Collections.emptyList(); } } diff --git a/translator/src/org/jetbrains/k2js/translate/GenerationState.java b/translator/src/org/jetbrains/k2js/translate/GenerationState.java index 453369144ec..da6fe418b62 100644 --- a/translator/src/org/jetbrains/k2js/translate/GenerationState.java +++ b/translator/src/org/jetbrains/k2js/translate/GenerationState.java @@ -26,8 +26,7 @@ public final class GenerationState { JetNamespace namespace = namespaces.get(0); NamespaceDescriptor descriptor = BindingUtils.getNamespaceDescriptor(bindingContext, namespace); Declarations declarations = Declarations.extractDeclarations(descriptor, result.getRootScope()); - Translation.namespaceTranslator(TranslationContext.rootContext(result, bindingContext, declarations)) - .generateAst(namespace); + Translation.generateAst(result, bindingContext, declarations, namespace); return result; } diff --git a/translator/src/org/jetbrains/k2js/translate/InitializerGenerator.java b/translator/src/org/jetbrains/k2js/translate/InitializerGenerator.java new file mode 100644 index 00000000000..99bc7f5f0f3 --- /dev/null +++ b/translator/src/org/jetbrains/k2js/translate/InitializerGenerator.java @@ -0,0 +1,52 @@ +package org.jetbrains.k2js.translate; + +import com.google.dart.compiler.backend.js.ast.JsFunction; +import com.google.dart.compiler.backend.js.ast.JsPropertyInitializer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.JetClass; +import org.jetbrains.jet.lang.psi.JetNamedDeclaration; +import org.jetbrains.jet.lang.psi.JetNamespace; + +public class InitializerGenerator { + + @NotNull + private final TranslationContext context; + @NotNull + private final JetNamedDeclaration declaration; + + static public JsPropertyInitializer generateInitializeMethod(@NotNull JetNamedDeclaration declaration, + @NotNull TranslationContext context) { + return (new InitializerGenerator(context, declaration)).generateInitializeMethod(); + } + + private InitializerGenerator(@NotNull TranslationContext context, @NotNull JetNamedDeclaration declaration) { + this.context = context; + this.declaration = declaration; + assert (declaration instanceof JetClass) || (declaration instanceof JetNamespace) : + "Can create initializers for classes or namespaces only"; + } + + @NotNull + public JsPropertyInitializer generateInitializeMethod() { + JsPropertyInitializer initializer = new JsPropertyInitializer(); + initializer.setLabelExpr(context.program().getStringLiteral(Namer.INITIALIZE_METHOD_NAME)); + initializer.setValueExpr(generateInitializeFunction()); + return initializer; + } + + @NotNull + private JsFunction generateInitializeFunction() { + AbstractInitializerVisitor visitor; + if (declaration instanceof JetNamespace) { + JetNamespace namespaceDeclaration = (JetNamespace) declaration; + visitor = new NamespaceInitializerVisitor(namespaceDeclaration, + context.newNamespace(namespaceDeclaration)); + } else { + assert declaration instanceof JetClass; + JetClass classDeclaration = (JetClass) declaration; + visitor = new ClassInitializerVisitor(classDeclaration, + context.newClass(classDeclaration)); + } + return visitor.generate(); + } +} \ No newline at end of file diff --git a/translator/src/org/jetbrains/k2js/translate/Namer.java b/translator/src/org/jetbrains/k2js/translate/Namer.java index 533bfa6a411..3744f452a0a 100644 --- a/translator/src/org/jetbrains/k2js/translate/Namer.java +++ b/translator/src/org/jetbrains/k2js/translate/Namer.java @@ -71,6 +71,10 @@ public final class Namer { return AstUtil.newQualifiedNameRef("Trait.create"); } + public static JsNameRef namespaceCreationMethodReference() { + return AstUtil.newQualifiedNameRef("Namespace.create"); + } + public static JsNameRef isOperationReference() { return AstUtil.newQualifiedNameRef("isType"); } diff --git a/translator/src/org/jetbrains/k2js/translate/NamespaceDeclarationTranslator.java b/translator/src/org/jetbrains/k2js/translate/NamespaceDeclarationTranslator.java deleted file mode 100644 index 4ef42250b6e..00000000000 --- a/translator/src/org/jetbrains/k2js/translate/NamespaceDeclarationTranslator.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.jetbrains.k2js.translate; - -import com.google.dart.compiler.backend.js.ast.JsStatement; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.jet.lang.psi.JetDeclaration; - -/** - * @author Talanov Pavel - */ -public final class NamespaceDeclarationTranslator extends AbstractTranslator { - - private NamespaceDeclarationVisitor visitor = new NamespaceDeclarationVisitor(); - - @NotNull - public static NamespaceDeclarationTranslator newInstance(@NotNull TranslationContext context) { - return new NamespaceDeclarationTranslator(context); - } - - private NamespaceDeclarationTranslator(TranslationContext context) { - super(context); - } - - @NotNull - JsStatement translateDeclaration(JetDeclaration declaration) { - return declaration.accept(visitor, translationContext()); - } - - -} diff --git a/translator/src/org/jetbrains/k2js/translate/NamespaceDeclarationVisitor.java b/translator/src/org/jetbrains/k2js/translate/NamespaceDeclarationVisitor.java deleted file mode 100644 index be783500ff6..00000000000 --- a/translator/src/org/jetbrains/k2js/translate/NamespaceDeclarationVisitor.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.jetbrains.k2js.translate; - -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.backend.js.ast.JsStatement; -import com.google.dart.compiler.util.AstUtil; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.jet.lang.psi.JetClass; -import org.jetbrains.jet.lang.psi.JetNamedFunction; -import org.jetbrains.jet.lang.psi.JetProperty; - -/** - * @author Talanov Pavel - */ -//TODO: rework the class -public class NamespaceDeclarationVisitor extends TranslatorVisitor { - - @NotNull - @Override - //TODO method too long - public JsStatement visitProperty(@NotNull JetProperty expression, @NotNull TranslationContext context) { - JsName propertyName = context.declareLocalName(getPropertyName(expression)); - JsNameRef jsPropertyNameReference = context.getNamespaceQualifiedReference(propertyName); - JsExpression jsInitExpression = translateInitializerForProperty(expression, context); - JsExpression result; - if (jsInitExpression != null) { - result = AstUtil.newAssignment(jsPropertyNameReference, jsInitExpression); - } else { - result = jsPropertyNameReference; - } - return AstUtil.convertToStatement(result); - } - - @NotNull - @Override - public JsStatement visitClass(@NotNull JetClass expression, @NotNull TranslationContext context) { - return Translation.classTranslator(context).translateClass(expression); - } - - @NotNull - @Override - public JsStatement visitNamedFunction(@NotNull JetNamedFunction expression, @NotNull TranslationContext context) { - return AstUtil.convertToStatement(Translation.functionTranslator(context).translateAsFunctionDeclaration(expression)); - } - -} diff --git a/translator/src/org/jetbrains/k2js/translate/NamespaceInitializerVisitor.java b/translator/src/org/jetbrains/k2js/translate/NamespaceInitializerVisitor.java new file mode 100644 index 00000000000..eb5a32e12fd --- /dev/null +++ b/translator/src/org/jetbrains/k2js/translate/NamespaceInitializerVisitor.java @@ -0,0 +1,31 @@ +package org.jetbrains.k2js.translate; + +import com.google.dart.compiler.backend.js.ast.JsFunction; +import com.google.dart.compiler.backend.js.ast.JsScope; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.JetNamespace; + +/** + * @author Talanov Pavel + */ +public final class NamespaceInitializerVisitor extends AbstractInitializerVisitor { + + @NotNull + private final JetNamespace namespace; + + public NamespaceInitializerVisitor(@NotNull JetNamespace namespace, @NotNull TranslationContext context) { + super(context, new JsScope(context.getScopeForElement(namespace), + "initializer " + namespace.getName())); + this.namespace = namespace; + } + + @Override + @NotNull + public JsFunction generate() { + JsFunction result = JsFunction.getAnonymousFunctionWithScope(initializerMethodScope); + result.setBody(AstUtil.newBlock(namespace.accept(this, initializerMethodContext))); + return result; + } + +} diff --git a/translator/src/org/jetbrains/k2js/translate/NamespaceTranslator.java b/translator/src/org/jetbrains/k2js/translate/NamespaceTranslator.java index 511e3392928..910f98776d2 100644 --- a/translator/src/org/jetbrains/k2js/translate/NamespaceTranslator.java +++ b/translator/src/org/jetbrains/k2js/translate/NamespaceTranslator.java @@ -3,7 +3,6 @@ package org.jetbrains.k2js.translate; 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.JetDeclaration; import org.jetbrains.jet.lang.psi.JetNamespace; import java.util.ArrayList; @@ -23,50 +22,44 @@ public final class NamespaceTranslator extends AbstractTranslator { super(context); } - public JsProgram generateAst(@NotNull JetNamespace namespace) { - translate(namespace); - return program(); - } - - //TODO logic unclear @NotNull - public JsBlock translate(@NotNull JetNamespace namespace) { - // TODO support multiple namespaces - JsBlock block = program().getFragmentBlock(0); - JsName namespaceName = translationContext().enclosingScope().declareName(Namer.getNameForNamespace(namespace.getName())); - block.addStatement(namespaceInitStatement(namespaceName)); - TranslationContext newContext = translationContext().newNamespace(namespace); - JsFunction dummyFunction = JsFunction.getAnonymousFunctionWithScope - (translationContext().getScopeForElement(namespace)); - JsBlock namespaceDeclarations = translateDeclarations(namespace, newContext); - JsInvocation namespaceExpression = newNamespace(namespaceName, namespaceDeclarations, dummyFunction); - block.addStatement(AstUtil.convertToStatement(namespaceExpression)); - return block; + public JsStatement translateNamespace(@NotNull JetNamespace namespace) { + ClassDeclarationTranslator translator = new ClassDeclarationTranslator(translationContext(), namespace); + translator.generateDeclarations(); + JsName declarationsObjectName = translator.getDeclarationsObjectName(); + JsBlock result = new JsBlock(); + JsInvocation namespaceDeclaration = namespaceCreateMethodInvocation(namespace); + namespaceDeclaration.getArguments().add(declarationsObjectName.makeRef()); + addMemberDeclarations(namespace, namespaceDeclaration); + result.addStatement(translator.getDeclarationsStatement()); + result.addStatement(namespaceDeclarationStatement(namespace, namespaceDeclaration)); + return result; } @NotNull - private JsBlock translateDeclarations(@NotNull JetNamespace namespace, @NotNull TranslationContext newContext) { - NamespaceDeclarationTranslator namespaceDeclarationTranslator = Translation.declarationTranslator(newContext); - JsBlock namespaceDeclarations = new JsBlock(); - for (JetDeclaration declaration : namespace.getDeclarations()) { - namespaceDeclarations.addStatement(namespaceDeclarationTranslator.translateDeclaration(declaration)); - } - return namespaceDeclarations; + private JsInvocation namespaceCreateMethodInvocation(@NotNull JetNamespace namespace) { + return AstUtil.newInvocation(Namer.namespaceCreationMethodReference()); } @NotNull - private JsStatement namespaceInitStatement(@NotNull JsName namespaceName) { - return AstUtil.newAssignmentStatement(namespaceName.makeRef(), new JsObjectLiteral()); + private JsStatement namespaceDeclarationStatement(@NotNull JetNamespace namespace, + @NotNull JsInvocation namespaceDeclaration) { + return AstUtil.newAssignmentStatement + (translationContext().getNameForElement(namespace).makeRef(), namespaceDeclaration); + } + + private void addMemberDeclarations(@NotNull JetNamespace namespace, + @NotNull JsInvocation jsNamespace) { + JsObjectLiteral jsClassDescription = translateNamespaceMemberDeclarations(namespace); + jsNamespace.getArguments().add(jsClassDescription); } @NotNull - private JsInvocation newNamespace(@NotNull JsName name, @NotNull JsBlock namespaceDeclarations, - @NotNull JsFunction dummyFunction) { - List params = new ArrayList(); - params.add(new JsParameter(name)); - dummyFunction.setParameters(params); - JsExpression invocationParam = name.makeRef(); - dummyFunction.setBody(namespaceDeclarations); - return AstUtil.newInvocation(dummyFunction, invocationParam); + private JsObjectLiteral translateNamespaceMemberDeclarations(@NotNull JetNamespace namespace) { + List propertyList = new ArrayList(); + propertyList.add(InitializerGenerator.generateInitializeMethod(namespace, translationContext())); + propertyList.addAll(new DeclarationBodyVisitor().traverseNamespace(namespace, + translationContext().newNamespace(namespace))); + return new JsObjectLiteral(propertyList); } } diff --git a/translator/src/org/jetbrains/k2js/translate/ReferenceProvider.java b/translator/src/org/jetbrains/k2js/translate/ReferenceProvider.java index 3d53b06d604..2b98dbee769 100644 --- a/translator/src/org/jetbrains/k2js/translate/ReferenceProvider.java +++ b/translator/src/org/jetbrains/k2js/translate/ReferenceProvider.java @@ -29,12 +29,13 @@ public final class ReferenceProvider { this.requiresNamespaceQualifier = requiresNamespaceQualifier(); } + //TODO @NotNull public JsNameRef generateCorrectReference() { - if (requiresNamespaceQualifier) { - return context.getNamespaceQualifiedReference(referencedName); - } else if (requiresThisQualifier) { + if (requiresThisQualifier) { return AstUtil.thisQualifiedReference(referencedName); + } else if (requiresNamespaceQualifier) { + return context.getNamespaceQualifiedReference(referencedName); } return referencedName.makeRef(); } @@ -45,8 +46,9 @@ public final class ReferenceProvider { private boolean requiresThisQualifier(@NotNull JetSimpleNameExpression expression) { JsName name = context.enclosingScope().findExistingName(referencedName.getIdent()); + boolean isNamespaceMember = requiresNamespaceQualifier; boolean isClassMember = context.classScope().ownsName(name); boolean isBackingFieldAccess = expression.getReferencedNameElementType() == JetTokens.FIELD_IDENTIFIER; - return isClassMember || isBackingFieldAccess; + return (isNamespaceMember && !isClassMember) || isClassMember || isBackingFieldAccess; } } diff --git a/translator/src/org/jetbrains/k2js/translate/Translation.java b/translator/src/org/jetbrains/k2js/translate/Translation.java index ee9cf594979..8d049e8b3e6 100644 --- a/translator/src/org/jetbrains/k2js/translate/Translation.java +++ b/translator/src/org/jetbrains/k2js/translate/Translation.java @@ -1,12 +1,13 @@ package org.jetbrains.k2js.translate; -import com.google.dart.compiler.backend.js.ast.JsExpression; -import com.google.dart.compiler.backend.js.ast.JsNode; -import com.google.dart.compiler.backend.js.ast.JsStatement; +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.JetNamespace; import org.jetbrains.jet.lang.psi.JetWhenExpression; +import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.k2js.declarations.Declarations; /** * @author Talanov Pavel @@ -45,11 +46,6 @@ public final class Translation { return OperationTranslator.newInstance(context); } - @NotNull - static public NamespaceDeclarationTranslator declarationTranslator(@NotNull TranslationContext context) { - return NamespaceDeclarationTranslator.newInstance(context); - } - @NotNull static public PatternTranslator patternTranslator(@NotNull TranslationContext context) { return PatternTranslator.newInstance(context); @@ -79,4 +75,11 @@ public final class Translation { static public JsNode translateWhenExpression(@NotNull JetWhenExpression expression, @NotNull TranslationContext context) { return WhenTranslator.translateWhenExpression(expression, context); } + + public static void generateAst(@NotNull JsProgram result, @NotNull BindingContext bindingContext, + @NotNull Declarations declarations, @NotNull JetNamespace namespace) { + JsBlock block = result.getFragmentBlock(0); + TranslationContext context = TranslationContext.rootContext(result, bindingContext, declarations); + block.addStatement(Translation.namespaceTranslator(context).translateNamespace(namespace)); + } } diff --git a/translator/test/org/jetbrains/k2js/test/AbstractExpressionTest.java b/translator/test/org/jetbrains/k2js/test/AbstractExpressionTest.java index bb96c99a92a..6d4c92bf6d3 100644 --- a/translator/test/org/jetbrains/k2js/test/AbstractExpressionTest.java +++ b/translator/test/org/jetbrains/k2js/test/AbstractExpressionTest.java @@ -1,20 +1,12 @@ package org.jetbrains.k2js.test; -import java.util.Arrays; -import java.util.List; - /** * @author Talanov Pavel */ -public abstract class AbstractExpressionTest extends TranslationTest { +public abstract class AbstractExpressionTest extends IncludeLibraryTest { private final String SUITE = "expression/"; - @Override - protected List generateFilenameList(String inputFile) { - return Arrays.asList(inputFile); - } - @Override protected String suiteDirectoryName() { return SUITE; diff --git a/translator/test/org/jetbrains/k2js/test/KotlinLibTest.java b/translator/test/org/jetbrains/k2js/test/KotlinLibTest.java index 9d932360064..b1b2fd616c4 100644 --- a/translator/test/org/jetbrains/k2js/test/KotlinLibTest.java +++ b/translator/test/org/jetbrains/k2js/test/KotlinLibTest.java @@ -7,21 +7,15 @@ import org.mozilla.javascript.Scriptable; import java.util.Arrays; import java.util.HashMap; -import java.util.List; import java.util.Map; /** * @author Talanov Pavel */ -public class KotlinLibTest extends TranslationTest { +public class KotlinLibTest extends IncludeLibraryTest { final private static String MAIN = "kotlinLib/"; - @Override - protected List generateFilenameList(String inputFile) { - return Arrays.asList(kotlinLibraryPath()); - } - @Override protected String mainDirectory() { return MAIN; @@ -74,6 +68,33 @@ public class KotlinLibTest extends TranslationTest { new RhinoPropertyTypesChecker("foo", propertyToType)); } + @Test + public void createdNamespaceIsJSObject() throws Exception { + final Map> propertyToType + = new HashMap>(); + runRhinoTest(Arrays.asList(kotlinLibraryPath(), cases("namespace.js")), + new RhinoPropertyTypesChecker("foo", propertyToType)); + } + + @Test + public void namespaceHasDeclaredFunction() throws Exception { + runRhinoTest(Arrays.asList(kotlinLibraryPath(), cases("namespace.js")), + new RhinoFunctionResultChecker("test", true)); + } + + + @Test + public void namespaceHasDeclaredClasses() throws Exception { + runRhinoTest(Arrays.asList(kotlinLibraryPath(), cases("namespaceWithClasses.js")), + new RhinoFunctionResultChecker("test", true)); + } + + @Test + public void namespacePropertyAccess() throws Exception { + runRhinoTest(Arrays.asList(kotlinLibraryPath(), cases("namespace2.js")), + new RhinoFunctionResultChecker("test", true)); + } + @Test public void isSameType() throws Exception { runRhinoTest(Arrays.asList(kotlinLibraryPath(), cases("isSameType.js")), diff --git a/translator/test/org/jetbrains/k2js/test/PropertyAccessorTest.java b/translator/test/org/jetbrains/k2js/test/PropertyAccessorTest.java index 3052216e426..de06377e759 100644 --- a/translator/test/org/jetbrains/k2js/test/PropertyAccessorTest.java +++ b/translator/test/org/jetbrains/k2js/test/PropertyAccessorTest.java @@ -49,6 +49,16 @@ public final class PropertyAccessorTest extends IncludeLibraryTest { testFooBoxIsTrue("safeCallReturnsNullIfFails.kt"); } + @Test + public void namespacePropertyInitializer() throws Exception { + testFooBoxIsTrue("namespacePropertyInitializer.kt"); + } + + @Test + public void namespacePropertySet() throws Exception { + testFooBoxIsTrue("namespacePropertySet.kt"); + } + //TODO test // @Test // public void namespaceCustomAccessors() throws Exception { diff --git a/translator/test/org/jetbrains/k2js/test/RhinoFunctionResultChecker.java b/translator/test/org/jetbrains/k2js/test/RhinoFunctionResultChecker.java index 8be5474e5bc..115a46173aa 100644 --- a/translator/test/org/jetbrains/k2js/test/RhinoFunctionResultChecker.java +++ b/translator/test/org/jetbrains/k2js/test/RhinoFunctionResultChecker.java @@ -2,8 +2,6 @@ package org.jetbrains.k2js.test; import org.jetbrains.annotations.Nullable; import org.mozilla.javascript.Context; -import org.mozilla.javascript.Function; -import org.mozilla.javascript.NativeObject; import org.mozilla.javascript.Scriptable; import static org.junit.Assert.assertTrue; @@ -29,43 +27,22 @@ public final class RhinoFunctionResultChecker implements RhinoResultChecker { @Override public void runChecks(Context context, Scriptable scope) throws Exception { - Object result = extractAndCallFunctionObject(namespaceName, functionName, context, scope); + 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 extractAndCallFunctionObject(String namespaceName, String functionName, - Context cx, Scriptable scope) { - Object functionObject; + 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) { - functionObject = extractFunctionFromObject(namespaceName, functionName, scope); - } else { - functionObject = extractFunctionFromGlobalScope(functionName, scope); + result = namespaceName + "." + result; } - return callFunctionAndCheckResults(cx, scope, (Function) functionObject); - } - - private Object callFunctionAndCheckResults(Context cx, Scriptable scope, Function functionObject) { - Object functionArgs[] = {}; - return functionObject.call(cx, scope, scope, functionArgs); - } - - private Object extractFunctionFromGlobalScope(String functionName, Scriptable scope) { - Object functionObject; - functionObject = scope.get(functionName, scope); - assertTrue("Function " + functionName + " is not defined in global scope", - functionObject instanceof Function); - return functionObject; - } - - private Object extractFunctionFromObject(String namespaceName, String functionName, Scriptable scope) { - Object functionObject; - NativeObject namespaceObject = RhinoUtils.extractObject(namespaceName, scope); - functionObject = namespaceObject.get(functionName, namespaceObject); - assertTrue("Function " + functionName + " is not defined in namespace " + namespaceName, - functionObject instanceof Function); - return functionObject; + return result; } } diff --git a/translator/testFiles/expression/function/cases/loopClosure.kt b/translator/testFiles/expression/function/cases/loopClosure.kt index 5c5a1e5615d..fcffa4a0f85 100644 --- a/translator/testFiles/expression/function/cases/loopClosure.kt +++ b/translator/testFiles/expression/function/cases/loopClosure.kt @@ -5,7 +5,7 @@ var b = 0 fun loop(var times : Int) { while(times > 0) { val u : fun(value : Int) : Unit = { - b++ + b = b + 1 } u(times--) } diff --git a/translator/testFiles/kotlinLib/cases/namespace.js b/translator/testFiles/kotlinLib/cases/namespace.js new file mode 100644 index 00000000000..4e028424b2f --- /dev/null +++ b/translator/testFiles/kotlinLib/cases/namespace.js @@ -0,0 +1,10 @@ +foo = Namespace.create({initialize:function(){ +} +, box:function(){ + return !false; +} +}); + +function test() { + return foo.box() +} diff --git a/translator/testFiles/kotlinLib/cases/namespace2.js b/translator/testFiles/kotlinLib/cases/namespace2.js new file mode 100644 index 00000000000..67f1f393144 --- /dev/null +++ b/translator/testFiles/kotlinLib/cases/namespace2.js @@ -0,0 +1,35 @@ +{ + classes = function(){ + return {}; + } + (); + foo = Namespace.create(classes, {initialize:function(){ + this.$b = 0 + } + , set_b:function(tmp$0){ + this.$b = tmp$0; + } + , get_b:function(){ + return this.$b; + } + , loop:function(times){ + while (times > 0) { + var u = function(){ + return this.set_b(this.get_b() + 1); + } + ; + u(times--); + } + } + , box:function(){ + this.loop(5); + return this.get_b() === 5; + } + }); +} + + + +function test() { + return foo.box() +} diff --git a/translator/testFiles/kotlinLib/cases/namespaceWithClasses.js b/translator/testFiles/kotlinLib/cases/namespaceWithClasses.js new file mode 100644 index 00000000000..4d85b782583 --- /dev/null +++ b/translator/testFiles/kotlinLib/cases/namespaceWithClasses.js @@ -0,0 +1,45 @@ +{ + classes = function(){ + var A = 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 = Class.create(A, {initialize:function(){ + this.super_init(); + { + this.set_order(this.get_order() + 'B'); + } + } + }); + var C = Class.create(B, {initialize:function(){ + this.super_init(); + { + this.set_order(this.get_order() + 'C'); + } + } + }); + return {A:A, B:B, C:C}; + } + (); + foo = 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/translator/testFiles/kotlin_lib.js b/translator/testFiles/kotlin_lib.js index e525c5d3f83..dbcfa033e93 100644 --- a/translator/testFiles/kotlin_lib.js +++ b/translator/testFiles/kotlin_lib.js @@ -6,9 +6,9 @@ function $A(iterable) { return results; } -var isType = function(object, class) { +var isType = function(object, klass) { current = object.get_class(); - while (current !== class) { + while (current !== klass) { if (current === null) { return false; } @@ -101,7 +101,6 @@ var Class = (function() { var Trait = (function() { - function add(object, source) { properties = Object.keys(source); for (var i = 0, length = properties.length; i < length; i++) { @@ -132,9 +131,8 @@ var Trait = (function() { var Namespace = (function() { function create() { - return Class.create.apply(this, arguments); + return Trait.create.apply(Trait, arguments); } - return { create: create }; diff --git a/translator/testFiles/propertyAccess/cases/namespacePropertyInitializer.kt b/translator/testFiles/propertyAccess/cases/namespacePropertyInitializer.kt new file mode 100644 index 00000000000..01314357101 --- /dev/null +++ b/translator/testFiles/propertyAccess/cases/namespacePropertyInitializer.kt @@ -0,0 +1,5 @@ +namespace foo + +val b = 3 + +fun box() = (b == 3) \ No newline at end of file diff --git a/translator/testFiles/propertyAccess/cases/namespacePropertySet.kt b/translator/testFiles/propertyAccess/cases/namespacePropertySet.kt new file mode 100644 index 00000000000..1b602320603 --- /dev/null +++ b/translator/testFiles/propertyAccess/cases/namespacePropertySet.kt @@ -0,0 +1,8 @@ +namespace foo + +var b = 3 + +fun box() : Boolean { + b = 2 + return (b == 2) +} \ No newline at end of file