diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 5318e951e73..e35a91b9c12 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -6,12 +6,14 @@ - + + + + + - - - + @@ -49,7 +51,20 @@ - + - - - - - - - - - - - - - - - - - - - - + + @@ -132,26 +129,17 @@ - - + + - + - - - - - - - - - - - + + @@ -159,37 +147,64 @@ - - + + - + - - + + - + - - + + - + - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -199,82 +214,82 @@ - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + @@ -310,22 +325,22 @@ @@ -400,6 +415,50 @@ @@ -726,32 +803,6 @@ - - - - - - - - - - + + + + + + + + + + - - - - - + + + + + localhost @@ -981,9 +1058,9 @@ - + - + @@ -1021,7 +1098,7 @@ @@ -1065,116 +1145,114 @@ - + + + + + + + + - + - + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - - - - - - - - - - + - + - + - + - + - + - + - + - + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/translator/src/org/jetbrains/k2js/translate/BindingUtils.java b/translator/src/org/jetbrains/k2js/translate/BindingUtils.java index 5e936dca416..ead6477d4e1 100644 --- a/translator/src/org/jetbrains/k2js/translate/BindingUtils.java +++ b/translator/src/org/jetbrains/k2js/translate/BindingUtils.java @@ -101,12 +101,20 @@ public final class BindingUtils { return superClassDescriptors; } - static public boolean hasAncestor(@NotNull BindingContext context, @NotNull JetClass classDeclaration) { - return !getSuperclassDescriptors(context, classDeclaration).isEmpty(); + static public boolean hasAncestorClass(@NotNull BindingContext context, @NotNull JetClass classDeclaration) { + List superclassDescriptors = getSuperclassDescriptors(context, classDeclaration); + for (ClassDescriptor descriptor : superclassDescriptors) { + if (descriptor.getKind() == ClassKind.CLASS) { + return true; + } + } + return false; } static public boolean isStatement(@NotNull BindingContext context, @NotNull JetExpression expression) { - return context.get(BindingContext.STATEMENT, expression); + Boolean isStatement = context.get(BindingContext.STATEMENT, expression); + assert isStatement != null : "Invalid behaviour of get(BindingContext.STATEMENT)"; + return isStatement; } //TODO better implementation? diff --git a/translator/src/org/jetbrains/k2js/translate/ClassTranslator.java b/translator/src/org/jetbrains/k2js/translate/ClassTranslator.java index 0695a0e8577..c9e9901d099 100644 --- a/translator/src/org/jetbrains/k2js/translate/ClassTranslator.java +++ b/translator/src/org/jetbrains/k2js/translate/ClassTranslator.java @@ -30,13 +30,43 @@ public final class ClassTranslator extends AbstractTranslator { @NotNull public JsStatement translateClass(@NotNull JetClass classDeclaration) { + if (!classDeclaration.isTrait()) { + return translateAsClassWithState(classDeclaration); + } else { + return translateAsStatelessTrait(classDeclaration); + } + } + + private JsStatement translateAsStatelessTrait(@NotNull JetClass classDeclaration) { + JsObjectLiteral traitLiteral = translateClassDeclarations(classDeclaration); + return AstUtil.convertToStatement + (AstUtil.newAssignment(namespaceQualifiedClassNameReference(classDeclaration), traitLiteral)); + } + + private JsStatement translateAsClassWithState(JetClass jetClassDeclaration) { + JsInvocation jsClassDeclaration = createMethodInvocation(); + addSuperclassReferences(jetClassDeclaration, jsClassDeclaration); + addClassOwnDeclarations(jetClassDeclaration, jsClassDeclaration); + return classDeclarationStatement(jetClassDeclaration, jsClassDeclaration); + } + + @NotNull + private JsInvocation createMethodInvocation() { JsInvocation jsClassDeclaration = new JsInvocation(); jsClassDeclaration.setQualifier(Namer.creationMethodReference()); - addSuperclassReferences(classDeclaration, jsClassDeclaration); - addClassOwnDeclarations(classDeclaration, jsClassDeclaration); - JsName jsClassName = translationContext().getNameForElement(classDeclaration); - return AstUtil.convertToStatement( - AstUtil.newAssignment(translationContext().getNamespaceQualifiedReference(jsClassName), jsClassDeclaration)); + return jsClassDeclaration; + } + + @NotNull + private JsStatement classDeclarationStatement(@NotNull JetClass classDeclaration, + @NotNull JsInvocation jsClassDeclaration) { + return AstUtil.convertToStatement(AstUtil.newAssignment + (namespaceQualifiedClassNameReference(classDeclaration), jsClassDeclaration)); + } + + private JsNameRef namespaceQualifiedClassNameReference(JetClass classDeclaration) { + return translationContext().getNamespaceQualifiedReference + (translationContext().getNameForElement(classDeclaration)); } private void addClassOwnDeclarations(@NotNull JetClass classDeclaration, @@ -67,7 +97,9 @@ public final class ClassTranslator extends AbstractTranslator { @NotNull private JsObjectLiteral translateClassDeclarations(@NotNull JetClass classDeclaration) { List propertyList = new ArrayList(); - propertyList.add(generateInitializeMethod(classDeclaration)); + if (!classDeclaration.isTrait()) { + propertyList.add(generateInitializeMethod(classDeclaration)); + } propertyList.addAll(classDeclaration.accept(classBodyVisitor, translationContext().newClass(classDeclaration))); return new JsObjectLiteral(propertyList); @@ -88,4 +120,5 @@ public final class ClassTranslator extends AbstractTranslator { translationContext().newClass(classDeclaration)); return initializerVisitor.generateInitializeMethod(); } + } diff --git a/translator/src/org/jetbrains/k2js/translate/ExpressionVisitor.java b/translator/src/org/jetbrains/k2js/translate/ExpressionVisitor.java index b9e58407427..4c84e827a89 100644 --- a/translator/src/org/jetbrains/k2js/translate/ExpressionVisitor.java +++ b/translator/src/org/jetbrains/k2js/translate/ExpressionVisitor.java @@ -93,11 +93,11 @@ public final class ExpressionVisitor extends TranslatorVisitor { // by name JsName referencedName = getReferencedName(expression, context); if (referencedName != null) { - if (context.namespaceScope().ownsName(referencedName)) { + if (requiresNamespaceQualifier(context, referencedName)) { return context.getNamespaceQualifiedReference(referencedName); } JsNameRef nameRef = referencedName.makeRef(); - if (expression.getReferencedNameElementType() == JetTokens.FIELD_IDENTIFIER) { + if (requiresThisQualifier(expression, context)) { nameRef.setQualifier(new JsThisRef()); } return nameRef; @@ -110,6 +110,17 @@ public final class ExpressionVisitor extends TranslatorVisitor { throw new AssertionError("Undefined name in this scope: " + expression.getReferencedName()); } + private boolean requiresNamespaceQualifier(TranslationContext context, JsName referencedName) { + return context.namespaceScope().ownsName(referencedName); + } + + private boolean requiresThisQualifier(@NotNull JetSimpleNameExpression expression, + @NotNull TranslationContext context) { + boolean isClassMember = context.classScope().ownsName(getReferencedName(expression, context)); + boolean isBackingFieldAccess = expression.getReferencedNameElementType() == JetTokens.FIELD_IDENTIFIER; + return isClassMember || isBackingFieldAccess; + } + @Nullable private JsName getReferencedName(@NotNull JetSimpleNameExpression expression, @NotNull TranslationContext context) { String referencedName = expression.getReferencedName(); diff --git a/translator/src/org/jetbrains/k2js/translate/InitializerVisitor.java b/translator/src/org/jetbrains/k2js/translate/InitializerVisitor.java index b4c69297a3c..b244dc5cec3 100644 --- a/translator/src/org/jetbrains/k2js/translate/InitializerVisitor.java +++ b/translator/src/org/jetbrains/k2js/translate/InitializerVisitor.java @@ -53,7 +53,7 @@ public class InitializerVisitor extends TranslatorVisitor> { private List generateCallToSuperMethod(@NotNull JetClass classDeclaration, @NotNull TranslationContext context) { List result = new ArrayList(); - if (BindingUtils.hasAncestor(context.bindingContext(), classDeclaration)) { + if (BindingUtils.hasAncestorClass(context.bindingContext(), classDeclaration)) { JsName superMethodName = initializerMethodScope.declareName(Namer.SUPER_METHOD_NAME); List arguments = translateArguments(classDeclaration, context); result.add(AstUtil.convertToStatement diff --git a/translator/test/org/jetbrains/k2js/test/TraitTest.java b/translator/test/org/jetbrains/k2js/test/TraitTest.java new file mode 100644 index 00000000000..4fb3576c8d1 --- /dev/null +++ b/translator/test/org/jetbrains/k2js/test/TraitTest.java @@ -0,0 +1,26 @@ +package org.jetbrains.k2js.test; + +import org.junit.Test; + +/** + * @author Talanov Pavel + */ +public final class TraitTest extends AbstractClassTest { + + final private static String MAIN = "trait/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + @Test + public void traitCompiles() throws Exception { + testFooBoxIsTrue("traitCompiles.kt"); + } + + @Test + public void traitAddsFunctionsToClass() throws Exception { + testFooBoxIsTrue("traitAddsFunctionsToClass.kt"); + } +} diff --git a/translator/testFiles/trait/cases/traitAddsFunctionsToClass.kt b/translator/testFiles/trait/cases/traitAddsFunctionsToClass.kt new file mode 100644 index 00000000000..bba3fe75695 --- /dev/null +++ b/translator/testFiles/trait/cases/traitAddsFunctionsToClass.kt @@ -0,0 +1,21 @@ +namespace foo + +trait Test { + fun addFoo(s:String) : String { + return s + "FOO" + } + + fun addBar(s:String) : String { + return s + "BAR" + } +} + + +class A() : Test { + val string = "TEST" + fun value() : String { + return addBar(addFoo(string)) + } +} + +fun box() = A().value() == "TESTFOOBAR" \ No newline at end of file diff --git a/translator/testFiles/trait/cases/traitCompiles.kt b/translator/testFiles/trait/cases/traitCompiles.kt new file mode 100644 index 00000000000..93f090310d2 --- /dev/null +++ b/translator/testFiles/trait/cases/traitCompiles.kt @@ -0,0 +1,9 @@ +namespace foo + +trait Test { + fun addFoo(s:String) : String { + return s + "FOO" + } +} + +fun box() = true \ No newline at end of file