From dca537ce70245c78ef404ec252a9129a184fa0d2 Mon Sep 17 00:00:00 2001 From: "Natalia.Ukhorskaya" Date: Wed, 23 Nov 2011 14:55:42 +0400 Subject: [PATCH] Help at rigth part --- .idea/workspace.xml | 967 +++++++++++------- .../google/dart/compiler/util/AstUtil.java | 13 + ...rationExtractor.java => Declarations.java} | 15 +- .../k2js/declarations/ExtractionVisitor.java | 66 +- .../k2js/translate/AbstractTranslator.java | 14 +- .../k2js/translate/BindingUtils.java | 47 +- .../k2js/translate/ClassBodyVisitor.java | 18 - .../k2js/translate/ClassTranslator.java | 34 +- .../k2js/translate/ExpressionVisitor.java | 173 ++-- .../k2js/translate/FunctionTranslator.java | 99 +- .../k2js/translate/GenerationState.java | 11 +- .../k2js/translate/InitializerVisitor.java | 8 +- .../org/jetbrains/k2js/translate/Namer.java | 14 +- ...va => NamespaceDeclarationTranslator.java} | 10 +- ....java => NamespaceDeclarationVisitor.java} | 4 +- .../k2js/translate/NamespaceTranslator.java | 11 +- .../k2js/translate/OperationTranslator.java | 33 +- .../k2js/translate/OperatorTable.java | 36 +- .../k2js/translate/PatternTranslator.java | 83 ++ .../translate/PropertyAccessTranslator.java | 13 +- .../k2js/translate/ReferenceTranslator.java | 102 ++ .../jetbrains/k2js/translate/Translation.java | 33 +- .../k2js/translate/TranslationContext.java | 48 +- .../k2js/translate/TranslatorVisitor.java | 6 +- .../k2js/translate/WhenTranslator.java | 175 ++++ .../k2js/test/AbstractExpressionTest.java | 22 + .../jetbrains/k2js/test/BasicClassTest.java | 4 +- .../k2js/test/ClassInheritanceTest.java | 2 +- .../jetbrains/k2js/test/ConditionalTest.java | 31 + .../jetbrains/k2js/test/ExpressionTest.java | 122 --- .../org/jetbrains/k2js/test/FunctionTest.java | 57 ++ ...ClassTest.java => IncludeLibraryTest.java} | 2 +- .../jetbrains/k2js/test/KotlinLibTest.java | 78 +- .../org/jetbrains/k2js/test/MiscTest.java | 23 + .../jetbrains/k2js/test/OperationTest.java | 45 + .../k2js/test/PatternMatchingTest.java | 52 + .../k2js/test/PropertyAccessorTest.java | 12 +- .../org/jetbrains/k2js/test/RTTITest.java | 26 + .../k2js/test/RhinoFunctionResultChecker.java | 43 +- .../k2js/test/RhinoPropertyTypesChecker.java | 41 + .../org/jetbrains/k2js/test/StringTest.java | 26 + .../org/jetbrains/k2js/test/TraitTest.java | 13 +- .../jetbrains/k2js/test/TranslationTest.java | 58 +- .../org/jetbrains/k2js/test/TupleTest.java | 22 + .../org/jetbrains/k2js/test/WhileTest.java | 55 + translator/testFiles/expression/cases/test.kt | 9 - .../{ => conditional}/cases/elseif.kt | 0 .../expression/{ => conditional}/cases/if.kt | 0 .../cases/ifElseAsExpression.kt | 0 .../expression/function/cases/adderClosure.kt | 9 + .../function/cases/closureWithParameter.jet | 9 + .../cases/closureWithParameterAndBoxing.jet | 9 + .../function/cases/functionLiteral.kt | 8 + .../cases/functionLiteralAsParameter.kt | 10 + .../cases/functionUsedBeforeDeclaration.kt | 0 .../cases/functionWithTwoParametersCall.kt | 0 .../expression/function/cases/loopClosure.kt | 17 + .../{ => misc}/cases/localProperty.jet | 0 .../{ => operation}/cases/assign.jet | 0 .../{ => operation}/cases/comparison.kt | 0 .../{ => operation}/cases/notBoolean.kt | 0 .../cases/positiveAndNegativeNumbers.kt | 0 .../cases/postfixIntOperations.kt | 0 .../cases/prefixIntOperations.kt | 0 .../{ => string}/cases/stringAssignment.kt | 0 .../{ => string}/cases/stringConstant.kt | 0 .../expression/while/cases/breakDoWhile.kt | 13 + .../expression/while/cases/breakWhile.kt | 13 + .../expression/while/cases/continueDoWhile.kt | 15 + .../expression/while/cases/continueWhile.kt | 15 + .../expression/{ => while}/cases/doWhile.kt | 0 .../expression/{ => while}/cases/doWhile2.kt | 0 .../expression/{ => while}/cases/while.kt | 0 .../expression/{ => while}/cases/while2.kt | 0 .../kotlinLib/cases/isAncestorType.js | 7 + .../kotlinLib/cases/isComplexTest.js | 24 + .../kotlinLib/cases/isNotOtherType.js | 8 + .../testFiles/kotlinLib/cases/isSameType.js | 6 + translator/testFiles/kotlinLib/cases/trait.js | 12 + translator/testFiles/kotlin_lib.js | 41 +- .../patternMatching/cases/multipleCases.kt | 17 + .../cases/whenExecutesOnlyOnce.kt | 15 + .../patternMatching/cases/whenNotType.kt | 12 + .../patternMatching/cases/whenNotValue.kt | 10 + .../patternMatching/cases/whenType.kt | 12 + .../patternMatching/cases/whenValue.kt | 10 + .../patternMatching/cases/whenValueOrType.kt | 25 + .../propertyAccess/cases/safeCall.kt | 13 + .../cases/safeCallReturnsNullIfFails.kt | 10 + translator/testFiles/prototype.js | 388 +++++++ .../testFiles/rtti/cases/isSameClass.kt | 7 + .../testFiles/rtti/cases/notIsOtherClass.kt | 11 + .../testFiles/trait/cases/funDelegation.jet | 19 + .../trait/cases/traitExtendsTwoTraits.kt | 25 + .../testFiles/tuple/cases/twoElements.kt | 6 + 95 files changed, 2760 insertions(+), 812 deletions(-) rename translator/src/org/jetbrains/k2js/declarations/{DeclarationExtractor.java => Declarations.java} (74%) rename translator/src/org/jetbrains/k2js/translate/{DeclarationTranslator.java => NamespaceDeclarationTranslator.java} (52%) rename translator/src/org/jetbrains/k2js/translate/{DeclarationVisitor.java => NamespaceDeclarationVisitor.java} (92%) create mode 100644 translator/src/org/jetbrains/k2js/translate/PatternTranslator.java create mode 100644 translator/src/org/jetbrains/k2js/translate/ReferenceTranslator.java create mode 100644 translator/src/org/jetbrains/k2js/translate/WhenTranslator.java create mode 100644 translator/test/org/jetbrains/k2js/test/AbstractExpressionTest.java create mode 100644 translator/test/org/jetbrains/k2js/test/ConditionalTest.java delete mode 100644 translator/test/org/jetbrains/k2js/test/ExpressionTest.java create mode 100644 translator/test/org/jetbrains/k2js/test/FunctionTest.java rename translator/test/org/jetbrains/k2js/test/{AbstractClassTest.java => IncludeLibraryTest.java} (80%) create mode 100644 translator/test/org/jetbrains/k2js/test/MiscTest.java create mode 100644 translator/test/org/jetbrains/k2js/test/OperationTest.java create mode 100644 translator/test/org/jetbrains/k2js/test/PatternMatchingTest.java create mode 100644 translator/test/org/jetbrains/k2js/test/RTTITest.java create mode 100644 translator/test/org/jetbrains/k2js/test/RhinoPropertyTypesChecker.java create mode 100644 translator/test/org/jetbrains/k2js/test/StringTest.java create mode 100644 translator/test/org/jetbrains/k2js/test/TupleTest.java create mode 100644 translator/test/org/jetbrains/k2js/test/WhileTest.java delete mode 100644 translator/testFiles/expression/cases/test.kt rename translator/testFiles/expression/{ => conditional}/cases/elseif.kt (100%) rename translator/testFiles/expression/{ => conditional}/cases/if.kt (100%) rename translator/testFiles/expression/{ => conditional}/cases/ifElseAsExpression.kt (100%) create mode 100644 translator/testFiles/expression/function/cases/adderClosure.kt create mode 100644 translator/testFiles/expression/function/cases/closureWithParameter.jet create mode 100644 translator/testFiles/expression/function/cases/closureWithParameterAndBoxing.jet create mode 100644 translator/testFiles/expression/function/cases/functionLiteral.kt create mode 100644 translator/testFiles/expression/function/cases/functionLiteralAsParameter.kt rename translator/testFiles/expression/{ => function}/cases/functionUsedBeforeDeclaration.kt (100%) rename translator/testFiles/expression/{ => function}/cases/functionWithTwoParametersCall.kt (100%) create mode 100644 translator/testFiles/expression/function/cases/loopClosure.kt rename translator/testFiles/expression/{ => misc}/cases/localProperty.jet (100%) rename translator/testFiles/expression/{ => operation}/cases/assign.jet (100%) rename translator/testFiles/expression/{ => operation}/cases/comparison.kt (100%) rename translator/testFiles/expression/{ => operation}/cases/notBoolean.kt (100%) rename translator/testFiles/expression/{ => operation}/cases/positiveAndNegativeNumbers.kt (100%) rename translator/testFiles/expression/{ => operation}/cases/postfixIntOperations.kt (100%) rename translator/testFiles/expression/{ => operation}/cases/prefixIntOperations.kt (100%) rename translator/testFiles/expression/{ => string}/cases/stringAssignment.kt (100%) rename translator/testFiles/expression/{ => string}/cases/stringConstant.kt (100%) create mode 100644 translator/testFiles/expression/while/cases/breakDoWhile.kt create mode 100644 translator/testFiles/expression/while/cases/breakWhile.kt create mode 100644 translator/testFiles/expression/while/cases/continueDoWhile.kt create mode 100644 translator/testFiles/expression/while/cases/continueWhile.kt rename translator/testFiles/expression/{ => while}/cases/doWhile.kt (100%) rename translator/testFiles/expression/{ => while}/cases/doWhile2.kt (100%) rename translator/testFiles/expression/{ => while}/cases/while.kt (100%) rename translator/testFiles/expression/{ => while}/cases/while2.kt (100%) create mode 100644 translator/testFiles/kotlinLib/cases/isAncestorType.js create mode 100644 translator/testFiles/kotlinLib/cases/isComplexTest.js create mode 100644 translator/testFiles/kotlinLib/cases/isNotOtherType.js create mode 100644 translator/testFiles/kotlinLib/cases/isSameType.js create mode 100644 translator/testFiles/kotlinLib/cases/trait.js create mode 100644 translator/testFiles/patternMatching/cases/multipleCases.kt create mode 100644 translator/testFiles/patternMatching/cases/whenExecutesOnlyOnce.kt create mode 100644 translator/testFiles/patternMatching/cases/whenNotType.kt create mode 100644 translator/testFiles/patternMatching/cases/whenNotValue.kt create mode 100644 translator/testFiles/patternMatching/cases/whenType.kt create mode 100644 translator/testFiles/patternMatching/cases/whenValue.kt create mode 100644 translator/testFiles/patternMatching/cases/whenValueOrType.kt create mode 100644 translator/testFiles/propertyAccess/cases/safeCall.kt create mode 100644 translator/testFiles/propertyAccess/cases/safeCallReturnsNullIfFails.kt create mode 100644 translator/testFiles/prototype.js create mode 100644 translator/testFiles/rtti/cases/isSameClass.kt create mode 100644 translator/testFiles/rtti/cases/notIsOtherClass.kt create mode 100644 translator/testFiles/trait/cases/funDelegation.jet create mode 100644 translator/testFiles/trait/cases/traitExtendsTwoTraits.kt create mode 100644 translator/testFiles/tuple/cases/twoElements.kt diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 2b6e648cae1..423f58ca895 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -1,13 +1,14 @@ - + + + - @@ -58,7 +59,33 @@ - + - - + + - + + + + + + + + + + + + - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -135,70 +227,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -208,26 +237,53 @@ - - + + - + - - + + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -235,64 +291,37 @@ - - + + - + - - + + - + - - + + - + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -319,22 +348,22 @@ @@ -377,22 +406,6 @@ - - - - - - - - - - - - @@ -425,6 +438,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -555,106 +800,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + localhost @@ -802,23 +1043,23 @@ + + - + - + - - @@ -852,7 +1093,7 @@ @@ -899,108 +1140,122 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/js/src/com/google/dart/compiler/util/AstUtil.java b/js/src/com/google/dart/compiler/util/AstUtil.java index 2ea618c8bea..b238bd4e9b1 100644 --- a/js/src/com/google/dart/compiler/util/AstUtil.java +++ b/js/src/com/google/dart/compiler/util/AstUtil.java @@ -268,4 +268,17 @@ public class AstUtil { 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 exprssion form statement: " + statement; + return (((JsExprStmt) statement).getExpression()); + } } diff --git a/translator/src/org/jetbrains/k2js/declarations/DeclarationExtractor.java b/translator/src/org/jetbrains/k2js/declarations/Declarations.java similarity index 74% rename from translator/src/org/jetbrains/k2js/declarations/DeclarationExtractor.java rename to translator/src/org/jetbrains/k2js/declarations/Declarations.java index c9d9dd2c13c..4aa19b139ce 100644 --- a/translator/src/org/jetbrains/k2js/declarations/DeclarationExtractor.java +++ b/translator/src/org/jetbrains/k2js/declarations/Declarations.java @@ -11,19 +11,22 @@ import java.util.Map; /** * @author Talanov Pavel */ -public final class DeclarationExtractor { +public final class Declarations { private final Map descriptorToScopeMap = new HashMap(); private final Map descriptorToNameMap = new HashMap(); - public DeclarationExtractor() { + private Declarations() { } - public void extractDeclarations(@NotNull DeclarationDescriptor descriptor, JsScope rootScope) { - ExtractionVisitor visitor = new ExtractionVisitor(this); + @NotNull + static public Declarations extractDeclarations(@NotNull DeclarationDescriptor descriptor, JsScope rootScope) { + Declarations declarations = new Declarations(); + ExtractionVisitor visitor = new ExtractionVisitor(declarations); descriptor.accept(visitor, rootScope); + return declarations; } @NotNull @@ -40,6 +43,10 @@ public final class DeclarationExtractor { return name; } + public boolean isDeclared(@NotNull DeclarationDescriptor descriptor) { + return descriptorToNameMap.containsKey(descriptor); + } + /*package*/ void putScope(@NotNull DeclarationDescriptor descriptor, @NotNull JsScope scope) { descriptorToScopeMap.put(descriptor, scope); } diff --git a/translator/src/org/jetbrains/k2js/declarations/ExtractionVisitor.java b/translator/src/org/jetbrains/k2js/declarations/ExtractionVisitor.java index 02880fa7949..4e16bf9dea6 100644 --- a/translator/src/org/jetbrains/k2js/declarations/ExtractionVisitor.java +++ b/translator/src/org/jetbrains/k2js/declarations/ExtractionVisitor.java @@ -13,32 +13,58 @@ import org.jetbrains.k2js.translate.Namer; public final class ExtractionVisitor extends DeclarationDescriptorVisitor { @NotNull - private final DeclarationExtractor extractor; + private final Declarations declarations; - /*package*/ ExtractionVisitor(@NotNull DeclarationExtractor extractor) { - this.extractor = extractor; + /*package*/ ExtractionVisitor(@NotNull Declarations declarations) { + this.declarations = declarations; } - @Override public Void visitClassDescriptor(@NotNull ClassDescriptor descriptor, @NotNull JsScope enclosingScope) { + JsScope classScope = extractClassDeclarations(descriptor, enclosingScope); + visitClassMembers(descriptor, classScope); + return null; + } + + @NotNull + private JsScope extractClassDeclarations(@NotNull ClassDescriptor descriptor, @NotNull JsScope enclosingScope) { String className = descriptor.getName(); - extractor.putName(descriptor, enclosingScope.declareName(className)); + declarations.putName(descriptor, enclosingScope.declareName(className)); JsScope classScope = new JsScope(enclosingScope, "class " + className); - extractor.putScope(descriptor, classScope); + declarations.putScope(descriptor, classScope); + return classScope; + } + + private void visitClassMembers(@NotNull ClassDescriptor descriptor, @NotNull JsScope classScope) { + visitClassConstructor(descriptor, classScope); for (DeclarationDescriptor memberDescriptor : descriptor.getDefaultType().getMemberScope().getAllDescriptors()) { memberDescriptor.accept(this, classScope); } + } + + private void visitClassConstructor(@NotNull ClassDescriptor descriptor, @NotNull JsScope classScope) { + for (ConstructorDescriptor constructorDescriptor : descriptor.getConstructors()) { + constructorDescriptor.accept(this, classScope); + } + } + + //TODO: think about the ways to make this less hacky + //For now constructor just references the name of the class. Initialize method scope is defined independently. + @Override + public Void visitConstructorDescriptor(@NotNull ConstructorDescriptor descriptor, @NotNull JsScope enclosingScope) { + String className = descriptor.getContainingDeclaration().getName(); + JsName alreadyDeclaredClassName = enclosingScope.findExistingName(className); + declarations.putName(descriptor, alreadyDeclaredClassName); return null; } @Override public Void visitFunctionDescriptor(@NotNull FunctionDescriptor descriptor, @NotNull JsScope enclosingScope) { String functionName = descriptor.getName(); - extractor.putName(descriptor, enclosingScope.declareName(functionName)); + declarations.putName(descriptor, enclosingScope.declareName(functionName)); JsScope functionScope = new JsScope(enclosingScope, "function " + functionName); - extractor.putScope(descriptor, functionScope); + declarations.putScope(descriptor, functionScope); return null; } @@ -59,23 +85,35 @@ public final class ExtractionVisitor extends DeclarationDescriptorVisitor superclassTypes = classDescriptor.getTypeConstructor().getSupertypes(); List superClassDescriptors = new ArrayList(); for (JetType type : superclassTypes) { - DeclarationDescriptor superClassDescriptor = - type.getConstructor().getDeclarationDescriptor(); - assert superClassDescriptor instanceof ClassDescriptor - : "Superclass descriptor of a type should be of type ClassDescriptor"; - if (isNotAny(superClassDescriptor)) { - superClassDescriptors.add((ClassDescriptor) superClassDescriptor); + ClassDescriptor result = getClassDescriptorForType(type); + if (isNotAny(result)) { + superClassDescriptors.add(result); } } return superClassDescriptors; } + @NotNull + static public 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; + } + static public boolean hasAncestorClass(@NotNull BindingContext context, @NotNull JetClass classDeclaration) { List superclassDescriptors = getSuperclassDescriptors(context, classDeclaration); return (findAncestorClass(superclassDescriptors) != null); @@ -113,8 +112,28 @@ public final class BindingUtils { return isStatement; } + @NotNull + static public 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 + static public ClassDescriptor getClassDescriptorForTypeReference(@NotNull BindingContext context, + @NotNull JetTypeReference typeReference) { + return getClassDescriptorForType(getTypeByReference(context, typeReference)); + } + + @Nullable + static public DeclarationDescriptor getDescriptorForReferenceExpression(@NotNull BindingContext context, + @NotNull JetReferenceExpression reference) { + return context.get(BindingContext.REFERENCE_TARGET, reference); + } + //TODO better implementation? - private static boolean isNotAny(@NotNull DeclarationDescriptor superClassDescriptor) { + static private boolean isNotAny(@NotNull DeclarationDescriptor superClassDescriptor) { return !superClassDescriptor.getName().equals("Any"); } diff --git a/translator/src/org/jetbrains/k2js/translate/ClassBodyVisitor.java b/translator/src/org/jetbrains/k2js/translate/ClassBodyVisitor.java index 750ac52aecc..57798ae81e9 100644 --- a/translator/src/org/jetbrains/k2js/translate/ClassBodyVisitor.java +++ b/translator/src/org/jetbrains/k2js/translate/ClassBodyVisitor.java @@ -153,22 +153,4 @@ public final class ClassBodyVisitor extends TranslatorVisitor no additional actions are needed return new ArrayList(); } - -// @NotNull -// JsName getNameForGetter(@NotNull String propertyName, @NotNull TranslationContext context) { -// return getNameForAccessor(propertyName, true, context); -// } -// -// @NotNull -// JsName getNameForSetter(@NotNull String propertyName, @NotNull TranslationContext context) { -// return getNameForAccessor(propertyName, false, context); -// } -// -// @NotNull -// JsName getNameForAccessor(@NotNull String propertyName, boolean isGetter, -// @NotNull TranslationContext context) { -// return context.classScope().findExistingName(Namer.getNameForAccessor(propertyName, isGetter)); -// } - - } diff --git a/translator/src/org/jetbrains/k2js/translate/ClassTranslator.java b/translator/src/org/jetbrains/k2js/translate/ClassTranslator.java index 3857087688e..44e6e51ec58 100644 --- a/translator/src/org/jetbrains/k2js/translate/ClassTranslator.java +++ b/translator/src/org/jetbrains/k2js/translate/ClassTranslator.java @@ -31,41 +31,27 @@ public final class ClassTranslator extends AbstractTranslator { } @NotNull - public JsStatement translateClass(@NotNull JetClass classDeclaration) { - if (!classDeclaration.isTrait()) { - return translateAsClassWithState(classDeclaration); - } else { - return translateAsStatelessTrait(classDeclaration); - } - } - - @NotNull - private JsStatement translateAsStatelessTrait(@NotNull JetClass classDeclaration) { - JsObjectLiteral traitLiteral = translateClassDeclarations(classDeclaration); - return AstUtil.convertToStatement - (AstUtil.newAssignment(namespaceQualifiedClassNameReference(classDeclaration), traitLiteral)); - } - - @NotNull - private JsStatement translateAsClassWithState(@NotNull JetClass jetClassDeclaration) { - JsInvocation jsClassDeclaration = createMethodInvocation(); + public JsStatement translateClass(@NotNull JetClass jetClassDeclaration) { + JsInvocation jsClassDeclaration = classCreateMethodInvocation(jetClassDeclaration); addSuperclassReferences(jetClassDeclaration, jsClassDeclaration); addClassOwnDeclarations(jetClassDeclaration, jsClassDeclaration); return classDeclarationStatement(jetClassDeclaration, jsClassDeclaration); } @NotNull - private JsInvocation createMethodInvocation() { - JsInvocation jsClassDeclaration = new JsInvocation(); - jsClassDeclaration.setQualifier(Namer.creationMethodReference()); - return jsClassDeclaration; + private JsInvocation classCreateMethodInvocation(@NotNull JetClass jetClassDeclaration) { + if (jetClassDeclaration.isTrait()) { + return AstUtil.newInvocation(Namer.traitCreationMethodReference()); + } else { + return AstUtil.newInvocation(Namer.classCreationMethodReference()); + } } @NotNull private JsStatement classDeclarationStatement(@NotNull JetClass classDeclaration, @NotNull JsInvocation jsClassDeclaration) { - return AstUtil.convertToStatement(AstUtil.newAssignment - (namespaceQualifiedClassNameReference(classDeclaration), jsClassDeclaration)); + return AstUtil.newAssignmentStatement + (namespaceQualifiedClassNameReference(classDeclaration), jsClassDeclaration); } @NotNull diff --git a/translator/src/org/jetbrains/k2js/translate/ExpressionVisitor.java b/translator/src/org/jetbrains/k2js/translate/ExpressionVisitor.java index a72ad7771ea..b05204f6ede 100644 --- a/translator/src/org/jetbrains/k2js/translate/ExpressionVisitor.java +++ b/translator/src/org/jetbrains/k2js/translate/ExpressionVisitor.java @@ -11,7 +11,6 @@ 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.resolve.constants.NullValue; -import org.jetbrains.jet.lexer.JetTokens; import java.util.List; @@ -61,7 +60,7 @@ public final class ExpressionVisitor extends TranslatorVisitor { @NotNull TranslationContext context) { JetExpression returnedExpression = jetReturnExpression.getReturnedExpression(); if (returnedExpression != null) { - JsExpression jsExpression = AstUtil.convertToExpression(returnedExpression.accept(this, context)); + JsExpression jsExpression = translateAsExpression(returnedExpression, context); return new JsReturn(jsExpression); } return new JsReturn(); @@ -85,52 +84,6 @@ public final class ExpressionVisitor extends TranslatorVisitor { return Translation.operationTranslator(context).translate(expression); } - - //TODO support other cases - @Override - @NotNull - public JsNode visitSimpleNameExpression(@NotNull JetSimpleNameExpression expression, - @NotNull TranslationContext context) { - - //TODO: this is only a hack for now - // Problem is that namespace properties do not generate getters and setter actually so they must be referenced - // by name - JsName referencedName = getReferencedName(expression, context); - if (referencedName != null) { - if (requiresNamespaceQualifier(context, referencedName)) { - return context.getNamespaceQualifiedReference(referencedName); - } - JsNameRef nameRef = referencedName.makeRef(); - if (requiresThisQualifier(expression, context)) { - nameRef.setQualifier(new JsThisRef()); - } - return nameRef; - } - - JsInvocation getterCall = Translation.propertyAccessTranslator(context).resolveAsPropertyGet(expression); - if (getterCall != null) { - return getterCall; - } - 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(); - return context.enclosingScope().findExistingName(referencedName); - } - @Override @NotNull // assume it is a local variable declaration @@ -143,7 +96,7 @@ public final class ExpressionVisitor extends TranslatorVisitor { @Override @NotNull public JsNode visitCallExpression(JetCallExpression expression, TranslationContext context) { - JsExpression callee = getCallee(expression, context); + JsExpression callee = translateCallee(expression, context); List arguments = translateArgumentList(expression.getValueArguments(), context); if (isConstructorInvocation(expression, context)) { JsNew constructorCall = new JsNew(callee); @@ -165,13 +118,12 @@ public final class ExpressionVisitor extends TranslatorVisitor { } @NotNull - private JsExpression getCallee(@NotNull JetCallExpression expression, @NotNull TranslationContext context) { - JetExpression jetCallee = expression.getCalleeExpression(); - if (jetCallee == null) { + private JsExpression translateCallee(@NotNull JetCallExpression expression, @NotNull TranslationContext context) { + JetExpression callee = expression.getCalleeExpression(); + if (callee == null) { throw new AssertionError("Call expression with no callee encountered!"); } - JsNode jsCallee = jetCallee.accept(this, context); - return AstUtil.convertToExpression(jsCallee); + return translateAsExpression(callee, context); } @Override @@ -183,9 +135,16 @@ public final class ExpressionVisitor extends TranslatorVisitor { } else { return translateAsConditionalExpression(expression, context); } - } + @Override + @NotNull + public JsNode visitSimpleNameExpression(@NotNull JetSimpleNameExpression expression, + @NotNull TranslationContext context) { + return Translation.referenceTranslator(context).translateSimpleName(expression); + } + + @NotNull private JsIf translateAsIfStatement(@NotNull JetIfExpression expression, @NotNull TranslationContext context) { @@ -227,9 +186,9 @@ public final class ExpressionVisitor extends TranslatorVisitor { @NotNull private JsExpression translateConditionExpression(@Nullable JetExpression expression, @NotNull TranslationContext context) { - JsNode jsCondition = translateNullableExpression(expression, context); - if (jsCondition == context.program().getEmptyStmt()) { - throw new AssertionError("Empty condition clause!"); + JsExpression jsCondition = translateNullableExpression(expression, context); + if (jsCondition == null) { + throw new AssertionError("Empty condition!"); } return AstUtil.convertToExpression(jsCondition); } @@ -240,6 +199,10 @@ public final class ExpressionVisitor extends TranslatorVisitor { if (expression == null) { return null; } + return translateAsExpression(expression, context); + } + + private JsExpression translateAsExpression(JetExpression expression, TranslationContext context) { return AstUtil.convertToExpression(expression.accept(this, context)); } @@ -286,31 +249,34 @@ public final class ExpressionVisitor extends TranslatorVisitor { return null; } - //TODO method too long @Override @NotNull public JsNode visitDotQualifiedExpression(@NotNull JetDotQualifiedExpression expression, @NotNull TranslationContext context) { + return translateQualifiedExpression(expression, context); + } + + @NotNull + private JsExpression translateQualifiedExpression(@NotNull JetQualifiedExpression expression, + @NotNull TranslationContext context) { JsInvocation getterCall = translateAsGetterCall(expression, context); if (getterCall != null) { return getterCall; } - return translateAsQualifiedExpression(expression, context); + return translateAsQualifiedAccess(expression, context); } @Nullable - private JsInvocation translateAsGetterCall(@NotNull JetDotQualifiedExpression expression, + private JsInvocation translateAsGetterCall(@NotNull JetQualifiedExpression expression, @NotNull TranslationContext context) { return Translation.propertyAccessTranslator(context).resolveAsPropertyGet(expression); } @NotNull - private JsNode translateAsQualifiedExpression(@NotNull JetDotQualifiedExpression expression, - @NotNull TranslationContext context) { - JsExpression receiver = AstUtil.convertToExpression(expression.getReceiverExpression().accept(this, context)); - JetExpression jetSelector = expression.getSelectorExpression(); - assert jetSelector != null : "Selector should not be null in dot qualified expression."; - JsExpression selector = AstUtil.convertToExpression(jetSelector.accept(this, context)); + private JsExpression translateAsQualifiedAccess(@NotNull JetQualifiedExpression expression, + @NotNull TranslationContext context) { + JsExpression receiver = translateReceiver(expression, context); + JsExpression selector = translateSelector(expression, context); assert (selector instanceof JsNameRef || selector instanceof JsInvocation) : "Selector should be a name reference or a method invocation in dot qualified expression."; if (selector instanceof JsInvocation) { @@ -321,13 +287,27 @@ public final class ExpressionVisitor extends TranslatorVisitor { } @NotNull - private JsNode translateAsQualifiedNameReference(@NotNull JsExpression receiver, @NotNull JsNameRef selector) { + private JsExpression translateSelector(@NotNull JetQualifiedExpression expression, + @NotNull TranslationContext context) { + JetExpression jetSelector = expression.getSelectorExpression(); + assert jetSelector != null : "Selector should not be null in dot qualified expression."; + return translateAsExpression(jetSelector, context); + } + + @NotNull + private JsExpression translateReceiver(@NotNull JetQualifiedExpression expression, + @NotNull TranslationContext context) { + return translateAsExpression(expression.getReceiverExpression(), context); + } + + @NotNull + private JsExpression translateAsQualifiedNameReference(@NotNull JsExpression receiver, @NotNull JsNameRef selector) { selector.setQualifier(receiver); return selector; } @NotNull - private JsNode translateAsQualifiedInvocation(@NotNull JsExpression receiver, @NotNull JsInvocation selector) { + private JsExpression translateAsQualifiedInvocation(@NotNull JsExpression receiver, @NotNull JsInvocation selector) { JsExpression qualifier = selector.getQualifier(); JsNameRef nameRef = (JsNameRef) qualifier; nameRef.setQualifier(receiver); @@ -347,7 +327,62 @@ public final class ExpressionVisitor extends TranslatorVisitor { public JsNode visitPostfixExpression(@NotNull JetPostfixExpression expression, @NotNull TranslationContext context) { return Translation.operationTranslator(context).translatePostfixOperation(expression); + } + @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) { + JsExpression receiver = translateReceiver(expression, context); + JsNullLiteral nullLiteral = context.program().getNullLiteral(); + JsBinaryOperation nullCheck = new JsBinaryOperation + (JsBinaryOperator.NEQ, receiver, nullLiteral); + JsExpression thenExpression = translateQualifiedExpression(expression, context); + return new JsConditional(nullCheck, thenExpression, nullLiteral); + } + + @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(context).translateAsLiteral(expression.getFunctionLiteral()); } diff --git a/translator/src/org/jetbrains/k2js/translate/FunctionTranslator.java b/translator/src/org/jetbrains/k2js/translate/FunctionTranslator.java index ee513c04409..9d39c124d24 100644 --- a/translator/src/org/jetbrains/k2js/translate/FunctionTranslator.java +++ b/translator/src/org/jetbrains/k2js/translate/FunctionTranslator.java @@ -2,10 +2,8 @@ package org.jetbrains.k2js.translate; import com.google.dart.compiler.backend.js.ast.*; import com.google.dart.compiler.util.AstUtil; -import com.sun.istack.internal.NotNull; -import org.jetbrains.jet.lang.psi.JetExpression; -import org.jetbrains.jet.lang.psi.JetNamedFunction; -import org.jetbrains.jet.lang.psi.JetParameter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.*; import java.util.ArrayList; import java.util.List; @@ -25,42 +23,87 @@ public final class FunctionTranslator extends AbstractTranslator { } @NotNull - public JsStatement translateAsFunction(@NotNull JetNamedFunction jetFunction) { - JsName functionName = translationContext().namespaceScope().declareFreshName(jetFunction.getName()); - JsFunction function = generateFunctionObject(jetFunction); - return AstUtil.convertToStatement(AstUtil.newAssignment - (translationContext().getNamespaceQualifiedReference(functionName), function)); + public JsStatement translateAsFunctionDeclaration(@NotNull JetNamedFunction expression) { + JsName functionName = translationContext().getNameForElement(expression); + JsFunction function = generateFunctionObject(expression); + return AstUtil.newAssignmentStatement + (translationContext().getNamespaceQualifiedReference(functionName), function); } @NotNull - JsPropertyInitializer translateAsMethod(@NotNull JetNamedFunction jetFunction) { - JsName functionName = translationContext().namespaceScope().declareFreshName(jetFunction.getName()); - JsFunction function = generateFunctionObject(jetFunction); + JsPropertyInitializer translateAsMethod(@NotNull JetNamedFunction expression) { + JsName functionName = translationContext().getNameForElement(expression); + JsFunction function = generateFunctionObject(expression); return new JsPropertyInitializer(functionName.makeRef(), function); } @NotNull - private JsFunction generateFunctionObject(@NotNull JetNamedFunction jetFunction) { - JsFunction result = JsFunction.getAnonymousFunctionWithScope - (translationContext().getScopeForElement(jetFunction)); - List jsParameters = translateParameters(jetFunction.getValueParameters(), result.getScope()); - JsNode jsBody = translateBody(jetFunction); - result.setParameters(jsParameters); - result.setBody(AstUtil.convertToBlock(jsBody)); - return result; + JsFunction translateAsLiteral(@NotNull JetFunctionLiteral expression) { + return generateFunctionObject(expression); } @NotNull - private JsNode translateBody(@NotNull JetNamedFunction jetFunction) { - JetExpression jetBodyExpression = jetFunction.getBodyExpression(); - //TODO support them ffs - assert jetBodyExpression != null : "Function without body not supported at the moment"; - JsNode body = Translation.expressionTranslator - (translationContext().newFunction(jetFunction)).translate(jetBodyExpression); - if (jetFunction.hasBlockBody()) { + private JsFunction generateFunctionObject(@NotNull JetFunction jetFunction) { + JsFunction result = createFunctionObject(jetFunction); + result.setParameters(translateParameters(jetFunction.getValueParameters(), result.getScope())); + result.setBody(translateBody(jetFunction, result.getScope())); + return result; + } + + private JsFunction createFunctionObject(JetFunction function) { + if (function instanceof JetNamedFunction) { + return JsFunction.getAnonymousFunctionWithScope + (translationContext().getScopeForElement(function)); + } + if (function instanceof JetFunctionLiteral) { + return new JsFunction(translationContext().enclosingScope()); + } + throw new AssertionError("Unsupported type of function."); + } + + @NotNull + private JsBlock translateBody(@NotNull JetFunction function, @NotNull JsScope functionScope) { + JetExpression jetBodyExpression = function.getBodyExpression(); + //TODO decide if there are cases where this assert is illegal + assert jetBodyExpression != null : "Function without body not supported"; + JsNode body = Translation.translateExpression(jetBodyExpression, functionBodyContext(function, functionScope)); + return wrapWithReturnIfNeeded(body, !function.hasBlockBody()); + } + + @NotNull + private JsBlock wrapWithReturnIfNeeded(@NotNull JsNode body, boolean needsReturn) { + if (!needsReturn) { return AstUtil.convertToBlock(body); } - return AstUtil.convertToBlock(new JsReturn(AstUtil.convertToExpression((body)))); + if (body instanceof JsExpression) { + return AstUtil.convertToBlock(new JsReturn(AstUtil.convertToExpression(body))); + } + if (body instanceof JsBlock) { + JsBlock bodyBlock = (JsBlock) body; + addReturnToBlockStatement(bodyBlock); + return bodyBlock; + } + throw new AssertionError("Invalid node as function body"); + } + + private void addReturnToBlockStatement(@NotNull JsBlock bodyBlock) { + List statements = bodyBlock.getStatements(); + int lastIndex = statements.size() - 1; + JsStatement lastStatement = statements.get(lastIndex); + statements.set(lastIndex, + new JsReturn(AstUtil.extractExpressionFromStatement(lastStatement))); + } + + @NotNull + private TranslationContext functionBodyContext(@NotNull JetFunction function, + @NotNull JsScope functionScope) { + if (function instanceof JetNamedFunction) { + return translationContext().newFunctionDeclaration((JetNamedFunction) function); + } + if (function instanceof JetFunctionLiteral) { + return translationContext().newFunctionLiteral(functionScope); + } + throw new AssertionError("Unsupported type of function."); } @NotNull diff --git a/translator/src/org/jetbrains/k2js/translate/GenerationState.java b/translator/src/org/jetbrains/k2js/translate/GenerationState.java index a04e6143a95..453369144ec 100644 --- a/translator/src/org/jetbrains/k2js/translate/GenerationState.java +++ b/translator/src/org/jetbrains/k2js/translate/GenerationState.java @@ -4,10 +4,11 @@ package org.jetbrains.k2js.translate; import com.google.dart.compiler.backend.js.ast.JsProgram; +import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor; import org.jetbrains.jet.lang.psi.JetNamespace; import org.jetbrains.jet.lang.resolve.BindingContext; -import org.jetbrains.k2js.declarations.DeclarationExtractor; +import org.jetbrains.k2js.declarations.Declarations; import java.util.List; @@ -18,14 +19,14 @@ public final class GenerationState { } //TODO method too long - public JsProgram compileCorrectNamespaces(BindingContext bindingContext, List namespaces) { + @NotNull + public JsProgram compileCorrectNamespaces(@NotNull BindingContext bindingContext, @NotNull List namespaces) { //TODO hardcoded JsProgram result = new JsProgram("main"); JetNamespace namespace = namespaces.get(0); NamespaceDescriptor descriptor = BindingUtils.getNamespaceDescriptor(bindingContext, namespace); - DeclarationExtractor extractor = new DeclarationExtractor(); - extractor.extractDeclarations(descriptor, result.getRootScope()); - Translation.namespaceTranslator(TranslationContext.rootContext(result, bindingContext, extractor)) + Declarations declarations = Declarations.extractDeclarations(descriptor, result.getRootScope()); + Translation.namespaceTranslator(TranslationContext.rootContext(result, bindingContext, declarations)) .generateAst(namespace); return result; } diff --git a/translator/src/org/jetbrains/k2js/translate/InitializerVisitor.java b/translator/src/org/jetbrains/k2js/translate/InitializerVisitor.java index b244dc5cec3..56bb69f26ab 100644 --- a/translator/src/org/jetbrains/k2js/translate/InitializerVisitor.java +++ b/translator/src/org/jetbrains/k2js/translate/InitializerVisitor.java @@ -121,8 +121,7 @@ public class InitializerVisitor extends TranslatorVisitor> { @NotNull private JsStatement translateInitializer(@NotNull JetProperty property, @NotNull TranslationContext context, @NotNull JetExpression initializer) { - JsExpression initExpression = AstUtil.convertToExpression(Translation.expressionTranslator(context) - .translate(initializer)); + JsExpression initExpression = Translation.translateAsExpression(initializer, context); return assignmentToBackingField(property, initExpression, context); } @@ -134,15 +133,14 @@ public class InitializerVisitor extends TranslatorVisitor> { JsName backingFieldName = getBackingFieldName(propertyName, context); JsNameRef backingFieldRef = backingFieldName.makeRef(); backingFieldRef.setQualifier(new JsThisRef()); - return AstUtil.convertToStatement(AstUtil.newAssignment(backingFieldRef, initExpression)); + return AstUtil.newAssignmentStatement(backingFieldRef, initExpression); } @Override @NotNull public List visitAnonymousInitializer(@NotNull JetClassInitializer initializer, @NotNull TranslationContext context) { - return Arrays.asList(AstUtil.convertToStatement - (Translation.expressionTranslator(context).translate(initializer.getBody()))); + return Arrays.asList(Translation.translateAsStatement(initializer.getBody(), context)); } @Override diff --git a/translator/src/org/jetbrains/k2js/translate/Namer.java b/translator/src/org/jetbrains/k2js/translate/Namer.java index c792d03fd70..533bfa6a411 100644 --- a/translator/src/org/jetbrains/k2js/translate/Namer.java +++ b/translator/src/org/jetbrains/k2js/translate/Namer.java @@ -8,7 +8,7 @@ import com.google.dart.compiler.util.AstUtil; */ /* - * This class is a complete dummy and needs a lot of work + * This class is a complete dummy and should completely change in the future */ public final class Namer { @@ -58,14 +58,22 @@ public final class Namer { return name; } + //TODO: dummy public static JsNameRef classObjectReference() { - //TODO dummy return AstUtil.newQualifiedNameRef("Class"); } - public static JsNameRef creationMethodReference() { + public static JsNameRef classCreationMethodReference() { return AstUtil.newQualifiedNameRef("Class.create"); } + public static JsNameRef traitCreationMethodReference() { + return AstUtil.newQualifiedNameRef("Trait.create"); + } + + public static JsNameRef isOperationReference() { + return AstUtil.newQualifiedNameRef("isType"); + } + } diff --git a/translator/src/org/jetbrains/k2js/translate/DeclarationTranslator.java b/translator/src/org/jetbrains/k2js/translate/NamespaceDeclarationTranslator.java similarity index 52% rename from translator/src/org/jetbrains/k2js/translate/DeclarationTranslator.java rename to translator/src/org/jetbrains/k2js/translate/NamespaceDeclarationTranslator.java index 620ca020498..4ef42250b6e 100644 --- a/translator/src/org/jetbrains/k2js/translate/DeclarationTranslator.java +++ b/translator/src/org/jetbrains/k2js/translate/NamespaceDeclarationTranslator.java @@ -7,16 +7,16 @@ import org.jetbrains.jet.lang.psi.JetDeclaration; /** * @author Talanov Pavel */ -public final class DeclarationTranslator extends AbstractTranslator { +public final class NamespaceDeclarationTranslator extends AbstractTranslator { - private DeclarationVisitor visitor = new DeclarationVisitor(); + private NamespaceDeclarationVisitor visitor = new NamespaceDeclarationVisitor(); @NotNull - public static DeclarationTranslator newInstance(@NotNull TranslationContext context) { - return new DeclarationTranslator(context); + public static NamespaceDeclarationTranslator newInstance(@NotNull TranslationContext context) { + return new NamespaceDeclarationTranslator(context); } - private DeclarationTranslator(TranslationContext context) { + private NamespaceDeclarationTranslator(TranslationContext context) { super(context); } diff --git a/translator/src/org/jetbrains/k2js/translate/DeclarationVisitor.java b/translator/src/org/jetbrains/k2js/translate/NamespaceDeclarationVisitor.java similarity index 92% rename from translator/src/org/jetbrains/k2js/translate/DeclarationVisitor.java rename to translator/src/org/jetbrains/k2js/translate/NamespaceDeclarationVisitor.java index 2231ddf9a75..be783500ff6 100644 --- a/translator/src/org/jetbrains/k2js/translate/DeclarationVisitor.java +++ b/translator/src/org/jetbrains/k2js/translate/NamespaceDeclarationVisitor.java @@ -14,7 +14,7 @@ import org.jetbrains.jet.lang.psi.JetProperty; * @author Talanov Pavel */ //TODO: rework the class -public class DeclarationVisitor extends TranslatorVisitor { +public class NamespaceDeclarationVisitor extends TranslatorVisitor { @NotNull @Override @@ -41,7 +41,7 @@ public class DeclarationVisitor extends TranslatorVisitor { @NotNull @Override public JsStatement visitNamedFunction(@NotNull JetNamedFunction expression, @NotNull TranslationContext context) { - return AstUtil.convertToStatement(Translation.functionTranslator(context).translateAsFunction(expression)); + return AstUtil.convertToStatement(Translation.functionTranslator(context).translateAsFunctionDeclaration(expression)); } } diff --git a/translator/src/org/jetbrains/k2js/translate/NamespaceTranslator.java b/translator/src/org/jetbrains/k2js/translate/NamespaceTranslator.java index 492d9402374..511e3392928 100644 --- a/translator/src/org/jetbrains/k2js/translate/NamespaceTranslator.java +++ b/translator/src/org/jetbrains/k2js/translate/NamespaceTranslator.java @@ -33,29 +33,30 @@ public final class NamespaceTranslator extends AbstractTranslator { public JsBlock translate(@NotNull JetNamespace namespace) { // TODO support multiple namespaces JsBlock block = program().getFragmentBlock(0); - JsName namespaceName = scope().declareName(Namer.getNameForNamespace(namespace.getName())); + 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); - block.addStatement(AstUtil.convertToStatement(newNamespace(namespaceName, namespaceDeclarations, dummyFunction))); + JsInvocation namespaceExpression = newNamespace(namespaceName, namespaceDeclarations, dummyFunction); + block.addStatement(AstUtil.convertToStatement(namespaceExpression)); return block; } @NotNull private JsBlock translateDeclarations(@NotNull JetNamespace namespace, @NotNull TranslationContext newContext) { - DeclarationTranslator declarationTranslator = Translation.declarationTranslator(newContext); + NamespaceDeclarationTranslator namespaceDeclarationTranslator = Translation.declarationTranslator(newContext); JsBlock namespaceDeclarations = new JsBlock(); for (JetDeclaration declaration : namespace.getDeclarations()) { - namespaceDeclarations.addStatement(declarationTranslator.translateDeclaration(declaration)); + namespaceDeclarations.addStatement(namespaceDeclarationTranslator.translateDeclaration(declaration)); } return namespaceDeclarations; } @NotNull private JsStatement namespaceInitStatement(@NotNull JsName namespaceName) { - return AstUtil.convertToStatement(AstUtil.newAssignment(namespaceName.makeRef(), new JsObjectLiteral())); + return AstUtil.newAssignmentStatement(namespaceName.makeRef(), new JsObjectLiteral()); } @NotNull diff --git a/translator/src/org/jetbrains/k2js/translate/OperationTranslator.java b/translator/src/org/jetbrains/k2js/translate/OperationTranslator.java index 9fd3af25a71..b725ffbc856 100644 --- a/translator/src/org/jetbrains/k2js/translate/OperationTranslator.java +++ b/translator/src/org/jetbrains/k2js/translate/OperationTranslator.java @@ -1,7 +1,6 @@ package org.jetbrains.k2js.translate; import com.google.dart.compiler.backend.js.ast.*; -import com.google.dart.compiler.util.AstUtil; import com.intellij.psi.tree.IElementType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -42,8 +41,7 @@ public final class OperationTranslator extends AbstractTranslator { private JsExpression translateBaseExpression(@NotNull JetUnaryExpression expression) { JetExpression baseExpression = expression.getBaseExpression(); assert baseExpression != null : "Unary operation should have a base expression"; - return AstUtil.convertToExpression - (Translation.translateExpression(baseExpression, translationContext())); + return Translation.translateAsExpression(baseExpression, translationContext()); } @NotNull @@ -63,6 +61,7 @@ public final class OperationTranslator extends AbstractTranslator { return translateAsBinaryOperation(expression); } + //TODO: think about the ways to improve logic here @Nullable public JsInvocation translateAsSetterCall(@NotNull JetBinaryExpression expression) { JetToken jetOperationToken = getOperationToken(expression); @@ -80,26 +79,32 @@ public final class OperationTranslator extends AbstractTranslator { return setterCall; } - @NotNull - private JetToken getOperationToken(@NotNull JetBinaryExpression expression) { - return (JetToken) expression.getOperationToken(); - } - @NotNull private JsExpression translateAsBinaryOperation(@NotNull JetBinaryExpression expression) { - JsExpression left = AstUtil.convertToExpression - (Translation.translateExpression(expression.getLeft(), translationContext())); + JsExpression left = Translation.translateAsExpression(expression.getLeft(), translationContext()); JsExpression right = translateRightExpression(expression); - JetToken jetOperationToken = getOperationToken(expression); - return new JsBinaryOperation(OperatorTable.getBinaryOperator(jetOperationToken), left, right); + JetToken token = getOperationToken(expression); + if (OperatorTable.hasCorrespondingBinaryOperator(token)) { + return new JsBinaryOperation(OperatorTable.getBinaryOperator(token), left, right); + } + if (OperatorTable.hasCorrespondingFunctionInvocation(token)) { + JsInvocation functionInvocation = OperatorTable.getCorrespondingFunctionInvocation(token); + functionInvocation.setArguments(Arrays.asList(left, right)); + return functionInvocation; + } + throw new AssertionError("Unsupported token encountered: " + token.toString()); } @NotNull private JsExpression translateRightExpression(@NotNull JetBinaryExpression expression) { JetExpression rightExpression = expression.getRight(); assert rightExpression != null : "Binary expression should have a right expression"; - return AstUtil.convertToExpression - (Translation.translateExpression(rightExpression, translationContext())); + return Translation.translateAsExpression(rightExpression, translationContext()); + } + + @NotNull + private JetToken getOperationToken(@NotNull JetBinaryExpression expression) { + return (JetToken) expression.getOperationToken(); } diff --git a/translator/src/org/jetbrains/k2js/translate/OperatorTable.java b/translator/src/org/jetbrains/k2js/translate/OperatorTable.java index d9970e7122a..90c2010255d 100644 --- a/translator/src/org/jetbrains/k2js/translate/OperatorTable.java +++ b/translator/src/org/jetbrains/k2js/translate/OperatorTable.java @@ -1,7 +1,10 @@ package org.jetbrains.k2js.translate; import com.google.dart.compiler.backend.js.ast.JsBinaryOperator; +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.JsUnaryOperator; +import com.google.dart.compiler.util.AstUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lexer.JetToken; import org.jetbrains.jet.lexer.JetTokens; @@ -14,9 +17,9 @@ import java.util.Map; */ public final class OperatorTable { - private static Map binaryOperatorsMap = new HashMap(); private static Map unaryOperatorsMap = new HashMap(); + private static Map operatorToFunctionNameReference = new HashMap(); static { unaryOperatorsMap.put(JetTokens.PLUSPLUS, JsUnaryOperator.INC); //++ @@ -41,22 +44,45 @@ public final class OperatorTable { binaryOperatorsMap.put(JetTokens.ANDAND, JsBinaryOperator.AND); 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); + } + + static { + operatorToFunctionNameReference.put(JetTokens.IS_KEYWORD, Namer.isOperationReference()); + } + + public static boolean hasCorrespondingBinaryOperator(@NotNull JetToken token) { + return binaryOperatorsMap.containsKey(token); + } + + static boolean hasCorrespondingFunctionInvocation(@NotNull JetToken token) { + return operatorToFunctionNameReference.containsKey(token); } @NotNull - public static JsBinaryOperator getBinaryOperator(@NotNull JetToken token) { + static public JsInvocation getCorrespondingFunctionInvocation(@NotNull JetToken token) { + JsNameRef functionReference = operatorToFunctionNameReference.get(token); + assert functionReference != null : "Token should represent a function call!"; + return AstUtil.newInvocation(functionReference); + } + + @NotNull + static public JsBinaryOperator getBinaryOperator(@NotNull JetToken token) { assert JetTokens.OPERATIONS.contains(token) : "Token should represent an operation!"; return binaryOperatorsMap.get(token); } @NotNull - public static JsUnaryOperator getUnaryOperator(@NotNull JetToken token) { + static public JsUnaryOperator getUnaryOperator(@NotNull JetToken token) { assert JetTokens.OPERATIONS.contains(token) : "Token should represent an operation!"; return unaryOperatorsMap.get(token); } - public static boolean isAssignment(JetToken token) { + static public boolean isAssignment(JetToken token) { return (token == JetTokens.EQ); } - } diff --git a/translator/src/org/jetbrains/k2js/translate/PatternTranslator.java b/translator/src/org/jetbrains/k2js/translate/PatternTranslator.java new file mode 100644 index 00000000000..2de2defcff8 --- /dev/null +++ b/translator/src/org/jetbrains/k2js/translate/PatternTranslator.java @@ -0,0 +1,83 @@ +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.descriptors.ClassDescriptor; +import org.jetbrains.jet.lang.psi.*; + +/** + * @author Talanov Pavel + */ +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(), translationContext()); + 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) { + return AstUtil.newInvocation(Namer.isOperationReference(), expressionToMatch, getClassReference(pattern)); + } + + @NotNull + private JsExpression getClassReference(@NotNull JetTypePattern pattern) { + JetTypeReference typeReference = pattern.getTypeReference(); + assert typeReference != null : "Type pattern should contain a type reference"; + return getClassNameReferenceForTypeReference(typeReference); + } + + @NotNull + private JsNameRef getClassNameReferenceForTypeReference(@NotNull JetTypeReference typeReference) { + ClassDescriptor referencedClass = BindingUtils.getClassDescriptorForTypeReference + (translationContext().bindingContext(), typeReference); + //TODO should reference class by full name here + JsName className = translationContext().getNameForDescriptor(referencedClass); + return translationContext().getNamespaceQualifiedReference(className); + } + + @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, translationContext()); + //TODO: should call equals method here + return new JsBinaryOperation(JsBinaryOperator.REF_EQ, expressionToMatch, expressionToMatchAgainst); + } +} diff --git a/translator/src/org/jetbrains/k2js/translate/PropertyAccessTranslator.java b/translator/src/org/jetbrains/k2js/translate/PropertyAccessTranslator.java index f77e35fecb6..048b2a11fc1 100644 --- a/translator/src/org/jetbrains/k2js/translate/PropertyAccessTranslator.java +++ b/translator/src/org/jetbrains/k2js/translate/PropertyAccessTranslator.java @@ -1,9 +1,9 @@ package org.jetbrains.k2js.translate; +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.backend.js.ast.JsNode; import com.google.dart.compiler.util.AstUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -13,6 +13,7 @@ import org.jetbrains.jet.lang.descriptors.PropertyGetterDescriptor; import org.jetbrains.jet.lang.descriptors.PropertySetterDescriptor; import org.jetbrains.jet.lang.psi.JetDotQualifiedExpression; 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.BindingContext; import org.jetbrains.jet.lang.resolve.calls.ResolvedCall; @@ -33,7 +34,7 @@ public final class PropertyAccessTranslator extends AbstractTranslator { } @Nullable - public JsInvocation resolveAsPropertyGet(@NotNull JetDotQualifiedExpression expression) { + public JsInvocation resolveAsPropertyGet(@NotNull JetQualifiedExpression expression) { JetExpression selectorExpression = expression.getSelectorExpression(); assert selectorExpression != null : "Selector should not be null."; JsName getterName = getPropertyGetterName(selectorExpression); @@ -54,11 +55,11 @@ public final class PropertyAccessTranslator extends AbstractTranslator { @NotNull private JsInvocation translateReceiverAndReturnAccessorInvocation - (@NotNull JetDotQualifiedExpression dotQualifiedExpression, @NotNull JsName accessorName) { - JsNode node = Translation.expressionTranslator(translationContext()) - .translate(dotQualifiedExpression.getReceiverExpression()); + (@NotNull JetQualifiedExpression qualifiedExpression, @NotNull JsName accessorName) { + JsExpression qualifier = Translation.translateAsExpression + (qualifiedExpression.getReceiverExpression(), translationContext()); JsNameRef result = accessorName.makeRef(); - result.setQualifier(AstUtil.convertToExpression(node)); + result.setQualifier(qualifier); return AstUtil.newInvocation(result); } diff --git a/translator/src/org/jetbrains/k2js/translate/ReferenceTranslator.java b/translator/src/org/jetbrains/k2js/translate/ReferenceTranslator.java new file mode 100644 index 00000000000..12cda448210 --- /dev/null +++ b/translator/src/org/jetbrains/k2js/translate/ReferenceTranslator.java @@ -0,0 +1,102 @@ +package org.jetbrains.k2js.translate; + +import com.google.dart.compiler.backend.js.ast.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; +import org.jetbrains.jet.lang.psi.JetSimpleNameExpression; +import org.jetbrains.jet.lexer.JetTokens; + +/** + * @author Talanov Pavel + */ +public class ReferenceTranslator extends AbstractTranslator { + + @NotNull + static public ReferenceTranslator newInstance(@NotNull TranslationContext context) { + return new ReferenceTranslator(context); + } + + private ReferenceTranslator(@NotNull TranslationContext context) { + super(context); + } + + @NotNull + JsExpression translateSimpleName(@NotNull JetSimpleNameExpression expression) { + //TODO: this is only a hack for now + // Problem is that namespace properties do not generate getters and setter actually so they must be referenced + // by name + JsExpression result; + result = resolveAsGlobalReference(expression); + if (result != null) { + return result; + } + result = resolveAsLocalReference(expression); + if (result != null) { + return result; + } + JsInvocation getterCall = + Translation.propertyAccessTranslator(translationContext()).resolveAsPropertyGet(expression); + if (getterCall != null) { + return getterCall; + } + throw new AssertionError("Undefined name in this scope: " + expression.getReferencedName()); + + } + + @Nullable + private JsExpression resolveAsGlobalReference(@NotNull JetSimpleNameExpression expression) { + DeclarationDescriptor referencedDescriptor = + BindingUtils.getDescriptorForReferenceExpression(translationContext().bindingContext(), expression); + if (referencedDescriptor == null) { + return null; + } + if (!translationContext().isDeclared(referencedDescriptor)) { + return null; + } + JsName referencedName = translationContext().getNameForDescriptor(referencedDescriptor); + return generateCorrectReference(expression, referencedName); + } + + @Nullable + private JsExpression resolveAsLocalReference(@NotNull JetSimpleNameExpression expression) { + JsName localReferencedName = getLocalReferencedName(expression); + if (localReferencedName == null) { + return null; + } + return generateCorrectReference(expression, localReferencedName); + } + + @NotNull + private JsNameRef generateCorrectReference(@NotNull JetSimpleNameExpression expression, + @NotNull JsName referencedName) { + JsNameRef result; + if (requiresNamespaceQualifier(referencedName)) { + result = translationContext().getNamespaceQualifiedReference(referencedName); + } else { + result = referencedName.makeRef(); + if (requiresThisQualifier(expression)) { + result.setQualifier(new JsThisRef()); + } + } + return result; + } + + private boolean requiresNamespaceQualifier(@NotNull JsName referencedName) { + return translationContext().namespaceScope().ownsName(referencedName); + } + + private boolean requiresThisQualifier(@NotNull JetSimpleNameExpression expression) { + boolean isClassMember = translationContext().classScope().ownsName(getLocalReferencedName(expression)); + boolean isBackingFieldAccess = expression.getReferencedNameElementType() == JetTokens.FIELD_IDENTIFIER; + return isClassMember || isBackingFieldAccess; + } + + @Nullable + private JsName getLocalReferencedName(@NotNull JetSimpleNameExpression expression) { + String referencedName = expression.getReferencedName(); + return translationContext().enclosingScope().findExistingName(referencedName); + } + + +} diff --git a/translator/src/org/jetbrains/k2js/translate/Translation.java b/translator/src/org/jetbrains/k2js/translate/Translation.java index 5c9d7749b18..ee9cf594979 100644 --- a/translator/src/org/jetbrains/k2js/translate/Translation.java +++ b/translator/src/org/jetbrains/k2js/translate/Translation.java @@ -1,8 +1,12 @@ 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.util.AstUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.psi.JetExpression; +import org.jetbrains.jet.lang.psi.JetWhenExpression; /** * @author Talanov Pavel @@ -42,12 +46,37 @@ public final class Translation { } @NotNull - static public DeclarationTranslator declarationTranslator(@NotNull TranslationContext context) { - return DeclarationTranslator.newInstance(context); + static public NamespaceDeclarationTranslator declarationTranslator(@NotNull TranslationContext context) { + return NamespaceDeclarationTranslator.newInstance(context); + } + + @NotNull + static public PatternTranslator patternTranslator(@NotNull TranslationContext context) { + return PatternTranslator.newInstance(context); + } + + @NotNull + static public ReferenceTranslator referenceTranslator(@NotNull TranslationContext context) { + return ReferenceTranslator.newInstance(context); } @NotNull static public JsNode translateExpression(@NotNull JetExpression expression, @NotNull TranslationContext context) { return expressionTranslator(context).translate(expression); } + + @NotNull + static public JsExpression translateAsExpression(@NotNull JetExpression expression, @NotNull TranslationContext context) { + return AstUtil.convertToExpression(translateExpression(expression, context)); + } + + @NotNull + static public JsStatement translateAsStatement(@NotNull JetExpression expression, @NotNull TranslationContext context) { + return AstUtil.convertToStatement(translateExpression(expression, context)); + } + + @NotNull + static public JsNode translateWhenExpression(@NotNull JetWhenExpression expression, @NotNull TranslationContext context) { + return WhenTranslator.translateWhenExpression(expression, context); + } } diff --git a/translator/src/org/jetbrains/k2js/translate/TranslationContext.java b/translator/src/org/jetbrains/k2js/translate/TranslationContext.java index 7ae20bc999b..76244da762f 100644 --- a/translator/src/org/jetbrains/k2js/translate/TranslationContext.java +++ b/translator/src/org/jetbrains/k2js/translate/TranslationContext.java @@ -10,7 +10,7 @@ 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.k2js.declarations.DeclarationExtractor; +import org.jetbrains.k2js.declarations.Declarations; /** * @author Talanov Pavel @@ -35,7 +35,7 @@ public final class TranslationContext { @NotNull public static TranslationContext rootContext(@NotNull JsProgram program, @NotNull BindingContext bindingContext, - @NotNull DeclarationExtractor extractor) { + @NotNull Declarations extractor) { JsScope rootScope = program.getRootScope(); Scopes scopes = new Scopes(rootScope, rootScope, rootScope); return new TranslationContext(null, program, bindingContext, scopes, extractor); @@ -50,17 +50,17 @@ public final class TranslationContext { @Nullable private final JsName namespaceName; @NotNull - private final DeclarationExtractor extractor; + private final Declarations declarations; private TranslationContext(@Nullable JsName namespaceName, @NotNull JsProgram program, @NotNull BindingContext bindingContext, @NotNull Scopes scopes, - @NotNull DeclarationExtractor extractor) { + @NotNull Declarations declarations) { this.program = program; this.bindingContext = bindingContext; this.namespaceName = namespaceName; this.scopes = scopes; - this.extractor = extractor; + this.declarations = declarations; } @NotNull @@ -70,17 +70,17 @@ public final class TranslationContext { @NotNull public TranslationContext newNamespace(@NotNull NamespaceDescriptor descriptor) { - JsScope namespaceScope = extractor.getScope(descriptor); + JsScope namespaceScope = declarations.getScope(descriptor); JsName namespaceName = scopes.enclosingScope.findExistingName(descriptor.getName()); Scopes newScopes = new Scopes(namespaceScope, namespaceScope, namespaceScope); - return new TranslationContext(namespaceName, program, bindingContext, newScopes, extractor); + return new TranslationContext(namespaceName, program, bindingContext, newScopes, declarations); } @NotNull public TranslationContext newBlock() { Scopes newScopes = new Scopes(new JsScope (scopes.enclosingScope, "Scope for a block"), scopes.classScope, scopes.namespaceScope); - return new TranslationContext(namespaceName, program, bindingContext, newScopes, extractor); + return new TranslationContext(namespaceName, program, bindingContext, newScopes, declarations); } @NotNull @@ -90,9 +90,9 @@ public final class TranslationContext { @NotNull public TranslationContext newClass(@NotNull ClassDescriptor descriptor) { - JsScope classScope = extractor.getScope(descriptor); + JsScope classScope = declarations.getScope(descriptor); Scopes newScopes = new Scopes(classScope, classScope, scopes.namespaceScope); - return new TranslationContext(namespaceName, program, bindingContext, newScopes, extractor); + return new TranslationContext(namespaceName, program, bindingContext, newScopes, declarations); } @NotNull @@ -102,26 +102,32 @@ public final class TranslationContext { @NotNull public TranslationContext newPropertyAccess(@NotNull PropertyAccessorDescriptor descriptor) { - return newFunction(descriptor); + return newFunctionDeclaration(descriptor); } @NotNull - public TranslationContext newFunction(@NotNull JetNamedFunction declaration) { - return newFunction(BindingUtils.getFunctionDescriptor(bindingContext, declaration)); + public TranslationContext newFunctionDeclaration(@NotNull JetNamedFunction declaration) { + return newFunctionDeclaration(BindingUtils.getFunctionDescriptor(bindingContext, declaration)); } @NotNull - public TranslationContext newFunction(@NotNull FunctionDescriptor descriptor) { - JsScope functionScope = extractor.getScope(descriptor); + public TranslationContext newFunctionDeclaration(@NotNull FunctionDescriptor descriptor) { + JsScope functionScope = declarations.getScope(descriptor); Scopes newScopes = new Scopes(functionScope, scopes.classScope, scopes.namespaceScope); - return new TranslationContext(namespaceName, program, bindingContext, newScopes, extractor); + return new TranslationContext(namespaceName, program, bindingContext, newScopes, declarations); + } + + @NotNull + public TranslationContext newFunctionLiteral(@NotNull JsScope correspondingScope) { + Scopes newScopes = new Scopes(correspondingScope, scopes.classScope, scopes.namespaceScope); + return new TranslationContext(namespaceName, program, bindingContext, newScopes, declarations); } // Note: Should be used if and only if scope has no corresponding descriptor @NotNull public TranslationContext newEnclosingScope(@NotNull JsScope enclosingScope) { Scopes newScopes = new Scopes(enclosingScope, scopes.classScope, scopes.namespaceScope); - return new TranslationContext(namespaceName, program, bindingContext, newScopes, extractor); + return new TranslationContext(namespaceName, program, bindingContext, newScopes, declarations); } @@ -165,7 +171,7 @@ public final class TranslationContext { @NotNull public JsScope getScopeForDescriptor(@NotNull DeclarationDescriptor descriptor) { - return extractor.getScope(descriptor); + return declarations.getScope(descriptor); } @NotNull @@ -176,7 +182,7 @@ public final class TranslationContext { @NotNull public JsName getNameForDescriptor(@NotNull DeclarationDescriptor descriptor) { - return extractor.getName(descriptor); + return declarations.getName(descriptor); } @NotNull @@ -191,4 +197,8 @@ public final class TranslationContext { assert descriptor != null : "Element should have a descriptor"; return descriptor; } + + public boolean isDeclared(@NotNull DeclarationDescriptor descriptor) { + return declarations.isDeclared(descriptor); + } } diff --git a/translator/src/org/jetbrains/k2js/translate/TranslatorVisitor.java b/translator/src/org/jetbrains/k2js/translate/TranslatorVisitor.java index aacc912ce4f..26c34290dc2 100644 --- a/translator/src/org/jetbrains/k2js/translate/TranslatorVisitor.java +++ b/translator/src/org/jetbrains/k2js/translate/TranslatorVisitor.java @@ -2,7 +2,6 @@ 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.util.AstUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.psi.*; @@ -45,8 +44,7 @@ public class TranslatorVisitor extends JetVisitor { JsExpression jsInitExpression = null; JetExpression initializer = declaration.getInitializer(); if (initializer != null) { - jsInitExpression = AstUtil.convertToExpression(Translation.expressionTranslator(context) - .translate(initializer)); + jsInitExpression = Translation.translateAsExpression(initializer, context); } return jsInitExpression; } @@ -67,6 +65,6 @@ public class TranslatorVisitor extends JetVisitor { if (jetExpression == null) { throw new AssertionError("Argument with no expression encountered!"); } - return AstUtil.convertToExpression(Translation.translateExpression(jetExpression, context)); + return Translation.translateAsExpression(jetExpression, context); } } diff --git a/translator/src/org/jetbrains/k2js/translate/WhenTranslator.java b/translator/src/org/jetbrains/k2js/translate/WhenTranslator.java new file mode 100644 index 00000000000..9f71f74af2e --- /dev/null +++ b/translator/src/org/jetbrains/k2js/translate/WhenTranslator.java @@ -0,0 +1,175 @@ +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.*; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Talanov Pavel + */ +//TODO: fix members order +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 JsName dummyCounterName; + private int currentEntryNumber = 0; + + private WhenTranslator(@NotNull JetWhenExpression expression, @NotNull TranslationContext context) { + super(context); + this.whenExpression = expression; + this.expressionToMatch = translateExpressionToMatch(whenExpression); + this.dummyCounterName = context.enclosingScope().declareTemporary(); + } + + @NotNull + JsNode translate() { + JsFor resultingFor = generateDummyFor(); + List entries = translateEntries(); + resultingFor.setBody(AstUtil.newBlock(entries)); + return resultingFor; + } + + @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, + dummyCounterName.makeRef(), translationContext().program().getNumberLiteral(currentEntryNumber)); + currentEntryNumber++; + return new JsIf(stepNumberEqualsCurrentEntryNumber, entryStatement, null); + } + + @NotNull + private JsFor generateDummyFor() { + JsFor result = new JsFor(); + result.setInitVars(generateInitStatement()); + result.setIncrExpr(generateIncrementStatement()); + result.setCondition(generateConditionStatement()); + return result; + } + + @NotNull + private JsBinaryOperation generateConditionStatement() { + JsNumberLiteral entriesNumber = program().getNumberLiteral(whenExpression.getEntries().size()); + return new JsBinaryOperation(JsBinaryOperator.LT, dummyCounterName.makeRef(), entriesNumber); + } + + @NotNull + private JsPrefixOperation generateIncrementStatement() { + return new JsPrefixOperation(JsUnaryOperator.INC, dummyCounterName.makeRef()); + } + + @NotNull + private JsVars generateInitStatement() { + return AstUtil.newVar(dummyCounterName, translationContext().program().getNumberLiteral(0)); + } + + @NotNull + private JsStatement translateEntry(@NotNull JetWhenEntry entry) { + JsStatement statementToExecute = translateExpressionToExecute(entry); + if (entry.isElse()) { + return statementToExecute; + } + JsExpression condition = translateConditions(entry); + return new JsIf(condition, addDummyBreak(statementToExecute), null); + } + + @NotNull + private JsStatement translateExpressionToExecute(@NotNull JetWhenEntry entry) { + JetExpression expressionToExecute = entry.getExpression(); + assert expressionToExecute != null : "WhenEntry should have whenExpression to execute."; + return Translation.translateAsStatement(expressionToExecute, translationContext()); + } + + //TODO: ask what these conditions mean + @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) { + current = condition; + } else { + current = new JsBinaryOperation(JsBinaryOperator.OR, current, condition); + } + return current; + } + + + @NotNull + private JsExpression translateCondition(@NotNull JetWhenCondition condition) { + if (condition instanceof JetWhenConditionIsPattern) { + return translatePatternCondition((JetWhenConditionIsPattern) 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 JetWhenConditionIsPattern condition) { + JsExpression patternMatchExpression = Translation.patternTranslator(translationContext()). + translatePattern(getPattern(condition), expressionToMatch); + if (condition.isNegated()) { + return AstUtil.negated(patternMatchExpression); + } + return patternMatchExpression; + } + + @NotNull + private JetPattern getPattern(@NotNull JetWhenConditionIsPattern condition) { + JetPattern pattern = condition.getPattern(); + assert pattern != null : "Is 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, translationContext()); + } +} diff --git a/translator/test/org/jetbrains/k2js/test/AbstractExpressionTest.java b/translator/test/org/jetbrains/k2js/test/AbstractExpressionTest.java new file mode 100644 index 00000000000..bb96c99a92a --- /dev/null +++ b/translator/test/org/jetbrains/k2js/test/AbstractExpressionTest.java @@ -0,0 +1,22 @@ +package org.jetbrains.k2js.test; + +import java.util.Arrays; +import java.util.List; + +/** + * @author Talanov Pavel + */ +public abstract class AbstractExpressionTest extends TranslationTest { + + 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/BasicClassTest.java b/translator/test/org/jetbrains/k2js/test/BasicClassTest.java index c05c9c072f4..efcad3444ce 100644 --- a/translator/test/org/jetbrains/k2js/test/BasicClassTest.java +++ b/translator/test/org/jetbrains/k2js/test/BasicClassTest.java @@ -5,7 +5,7 @@ import org.junit.Test; /** * @author Talanov Pavel */ -public class BasicClassTest extends AbstractClassTest { +public class BasicClassTest extends IncludeLibraryTest { final private static String MAIN = "class/"; @@ -24,7 +24,7 @@ public class BasicClassTest extends AbstractClassTest { testFooBoxIsTrue("methodDeclarationAndCall.kt"); } - //TODO: test excluded. Wait for bugfix and implement functionality + //TODO: wait for bugfix and implement properties as consructor parameter declaration @Test public void constructorWithParameter() throws Exception { testFooBoxIsTrue("constructorWithParameter.kt"); diff --git a/translator/test/org/jetbrains/k2js/test/ClassInheritanceTest.java b/translator/test/org/jetbrains/k2js/test/ClassInheritanceTest.java index 0d2f901d00b..247cf7e1b39 100644 --- a/translator/test/org/jetbrains/k2js/test/ClassInheritanceTest.java +++ b/translator/test/org/jetbrains/k2js/test/ClassInheritanceTest.java @@ -5,7 +5,7 @@ import org.junit.Test; /** * @author Talanov Pavel */ -public final class ClassInheritanceTest extends AbstractClassTest { +public final class ClassInheritanceTest extends IncludeLibraryTest { final private static String MAIN = "inheritance/"; diff --git a/translator/test/org/jetbrains/k2js/test/ConditionalTest.java b/translator/test/org/jetbrains/k2js/test/ConditionalTest.java new file mode 100644 index 00000000000..a7124ad6923 --- /dev/null +++ b/translator/test/org/jetbrains/k2js/test/ConditionalTest.java @@ -0,0 +1,31 @@ +package org.jetbrains.k2js.test; + +import org.junit.Test; + +/** + * @author Talanov Pavel + */ +public class ConditionalTest extends AbstractExpressionTest { + final private static String MAIN = "conditional/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + @Test + public void ifElseAsExpression() throws Exception { + testFooBoxIsTrue("ifElseAsExpression.kt"); + } + + @Test + public void ifElse() throws Exception { + testFunctionOutput("if.kt", "foo", "box", 5); + } + + //TODO: test fails due to isStatement issue, include when issue is solved or implement another solution +// @Test +// public void ifElseIf() throws Exception { +// testFunctionOutput("elseif.kt", "foo", "box", 5); +// } +} diff --git a/translator/test/org/jetbrains/k2js/test/ExpressionTest.java b/translator/test/org/jetbrains/k2js/test/ExpressionTest.java deleted file mode 100644 index 2415c9a6ef7..00000000000 --- a/translator/test/org/jetbrains/k2js/test/ExpressionTest.java +++ /dev/null @@ -1,122 +0,0 @@ -package org.jetbrains.k2js.test; - -import org.junit.Test; - -import java.util.Arrays; -import java.util.List; - -/** - * @author Talanov Pavel - *

- * This class tests basic language features and constructs such as constants, local variables, simple loops, - * conditional clauses etc. - */ -public final class ExpressionTest extends TranslationTest { - - private static final String MAIN = "expression/"; - - protected String mainDirectory() { - return MAIN; - } - - @Override - protected List generateFilenameList(String inputFile) { - return Arrays.asList(inputFile); - } - - @Test - public void currentTest() throws Exception { - testFooBoxIsTrue("test.kt"); - } - - @Test - public void testAssign() throws Exception { - testFunctionOutput("assign.jet", "foo", "f", 2.0); - } - - @Test - public void namespaceProperties() throws Exception { - testFunctionOutput("localProperty.jet", "foo", "box", 50); - } - - @Test - public void comparison() throws Exception { - testFooBoxIsTrue("comparison.kt"); - } - - @Test - public void ifElse() throws Exception { - testFunctionOutput("if.kt", "foo", "box", 5); - } - //TODO: test fails due to isStatement issue, include when issue is solved or implement another solution -// @Test -// public void ifElseIf() throws Exception { -// testFunctionOutput("elseif.kt", "foo", "box", 5); -// } - - @Test - public void whileSimpleTest() throws Exception { - testFooBoxIsTrue("while.kt"); - } - - @Test - public void doWhileSimpleTest() throws Exception { - testFooBoxIsTrue("doWhile.kt"); - } - - @Test - public void doWhileExecutesAtLeastOnce() throws Exception { - testFooBoxIsTrue("doWhile2.kt"); - } - - @Test - public void whileDoesntExecuteEvenOnceIfConditionIsFalse() throws Exception { - testFooBoxIsTrue("while2.kt"); - } - - @Test - public void stringConstant() throws Exception { - testFooBoxIsTrue("stringConstant.kt"); - } - - @Test - public void stringAssignment() throws Exception { - testFooBoxIsTrue("stringAssignment.kt"); - } - - @Test - public void functionUsedBeforeDeclaration() throws Exception { - testFooBoxIsTrue("functionUsedBeforeDeclaration.kt"); - } - - @Test - public void functionWithTwoParametersCall() throws Exception { - testFooBoxIsTrue("functionWithTwoParametersCall.kt"); - } - - @Test - public void prefixIntOperations() throws Exception { - testFooBoxIsTrue("prefixIntOperations.kt"); - } - - @Test - public void postfixIntOperations() throws Exception { - testFooBoxIsTrue("postfixIntOperations.kt"); - } - - @Test - public void notBoolean() throws Exception { - testFooBoxIsTrue("notBoolean.kt"); - } - - @Test - public void positiveAndNegativeNumbers() throws Exception { - testFooBoxIsTrue("positiveAndNegativeNumbers.kt"); - } - - @Test - public void ifElseAsExpression() throws Exception { - testFooBoxIsTrue("ifElseAsExpression.kt"); - } - -} diff --git a/translator/test/org/jetbrains/k2js/test/FunctionTest.java b/translator/test/org/jetbrains/k2js/test/FunctionTest.java new file mode 100644 index 00000000000..3b41d6d0a41 --- /dev/null +++ b/translator/test/org/jetbrains/k2js/test/FunctionTest.java @@ -0,0 +1,57 @@ +package org.jetbrains.k2js.test; + +import org.junit.Test; + +/** + * @author Talanov Pavel + */ +public class FunctionTest extends AbstractExpressionTest { + + final private static String MAIN = "function/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + @Test + public void functionUsedBeforeDeclaration() throws Exception { + testFooBoxIsTrue("functionUsedBeforeDeclaration.kt"); + } + + @Test + public void functionWithTwoParametersCall() throws Exception { + testFooBoxIsTrue("functionWithTwoParametersCall.kt"); + } + + @Test + public void functionLiteral() throws Exception { + testFooBoxIsTrue("functionLiteral.kt"); + } + + @Test + public void adderClosure() throws Exception { + testFooBoxIsTrue("adderClosure.kt"); + } + + @Test + public void loopClosure() throws Exception { + testFooBoxIsTrue("loopClosure.kt"); + } + + @Test + public void functionLiteralAsParameter() throws Exception { + testFooBoxIsTrue("functionLiteralAsParameter.kt"); + } + + @Test + public void closureWithParameter() throws Exception { + testFooBoxIsOk("closureWithParameter.jet"); + } + + @Test + public void closureWithParameterAndBoxing() throws Exception { + testFooBoxIsOk("closureWithParameterAndBoxing.jet"); + } + +} diff --git a/translator/test/org/jetbrains/k2js/test/AbstractClassTest.java b/translator/test/org/jetbrains/k2js/test/IncludeLibraryTest.java similarity index 80% rename from translator/test/org/jetbrains/k2js/test/AbstractClassTest.java rename to translator/test/org/jetbrains/k2js/test/IncludeLibraryTest.java index a5214f7271f..4b87608a5f6 100644 --- a/translator/test/org/jetbrains/k2js/test/AbstractClassTest.java +++ b/translator/test/org/jetbrains/k2js/test/IncludeLibraryTest.java @@ -6,7 +6,7 @@ import java.util.List; /** * @author Talanov Pavel */ -public abstract class AbstractClassTest extends TranslationTest { +public abstract class IncludeLibraryTest extends TranslationTest { @Override protected List generateFilenameList(String inputFile) { diff --git a/translator/test/org/jetbrains/k2js/test/KotlinLibTest.java b/translator/test/org/jetbrains/k2js/test/KotlinLibTest.java index 894c8000bd1..9fd8b52ce4f 100644 --- a/translator/test/org/jetbrains/k2js/test/KotlinLibTest.java +++ b/translator/test/org/jetbrains/k2js/test/KotlinLibTest.java @@ -3,7 +3,6 @@ package org.jetbrains.k2js.test; import org.junit.Test; import org.mozilla.javascript.Context; import org.mozilla.javascript.Function; -import org.mozilla.javascript.NativeObject; import org.mozilla.javascript.Scriptable; import java.util.Arrays; @@ -11,8 +10,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import static org.junit.Assert.assertTrue; - /** * @author Talanov Pavel */ @@ -30,39 +27,62 @@ public class KotlinLibTest extends TranslationTest { return MAIN; } - protected 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))); - } + private void runPropertyTypeCheck(String objectName, Map> propertyToType) + throws Exception { + runRhinoTest(Arrays.asList(kotlinLibraryPath()), new RhinoPropertyTypesChecker(objectName, propertyToType)); } @Test public void kotlinJsLibRunsWithRhino() throws Exception { - Context context = Context.enter(); - Scriptable scope = context.initStandardObjects(); - // ss - runFileWithRhino(kotlinLibraryPath(), context, scope); - Context.exit(); + runRhinoTest(Arrays.asList(kotlinLibraryPath()), new RhinoResultChecker() { + @Override + public void runChecks(Context context, Scriptable scope) throws Exception { + //do nothing + } + }); } @Test public void classObjectHasCreateMethod() throws Exception { - final Map> nameToClassMap = - new HashMap>(); - nameToClassMap.put("create", Function.class); - - runRhinoTest(Arrays.asList(kotlinLibraryPath()), - new RhinoResultChecker() { - @Override - public void runChecks(Context context, Scriptable scope) throws Exception { - NativeObject object = RhinoUtils.extractObject("Class", scope); - verifyObjectHasExpectedPropertiesOfExpectedTypes(object, nameToClassMap); - } - }); + final Map> propertyToType + = new HashMap>(); + propertyToType.put("create", Function.class); + runPropertyTypeCheck("Class", propertyToType); } + + @Test + public void traitObjectHasCreateMethod() throws Exception { + final Map> propertyToType + = new HashMap>(); + propertyToType.put("create", Function.class); + runPropertyTypeCheck("Trait", propertyToType); + } + + @Test + public void createdTraitIsJSObject() throws Exception { + final Map> propertyToType + = new HashMap>(); + runRhinoTest(Arrays.asList(kotlinLibraryPath(), cases("trait.js")), + new RhinoPropertyTypesChecker("foo", propertyToType)); + } + + @Test + public void isSameType() throws Exception { + runRhinoTest(Arrays.asList(kotlinLibraryPath(), cases("isSameType.js")), + new RhinoFunctionResultChecker("test", true)); + } + + @Test + public void isAncestorType() throws Exception { + runRhinoTest(Arrays.asList(kotlinLibraryPath(), cases("isAncestorType.js")), + new RhinoFunctionResultChecker("test", true)); + } + + @Test + public void isComplexTest() throws Exception { + runRhinoTest(Arrays.asList(kotlinLibraryPath(), cases("isComplexTest.js")), + new RhinoFunctionResultChecker("test", true)); + } + + } diff --git a/translator/test/org/jetbrains/k2js/test/MiscTest.java b/translator/test/org/jetbrains/k2js/test/MiscTest.java new file mode 100644 index 00000000000..da01678566a --- /dev/null +++ b/translator/test/org/jetbrains/k2js/test/MiscTest.java @@ -0,0 +1,23 @@ +package org.jetbrains.k2js.test; + +import org.junit.Test; + +/** + * @author Talanov Pavel + *

+ * This class contains tests that do not fall in any particular category + * most probably because that functionality has very little support + */ +public class MiscTest extends AbstractExpressionTest { + final private static String MAIN = "misc/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + @Test + public void namespaceProperties() throws Exception { + testFunctionOutput("localProperty.jet", "foo", "box", 50); + } +} diff --git a/translator/test/org/jetbrains/k2js/test/OperationTest.java b/translator/test/org/jetbrains/k2js/test/OperationTest.java new file mode 100644 index 00000000000..92be83c7be0 --- /dev/null +++ b/translator/test/org/jetbrains/k2js/test/OperationTest.java @@ -0,0 +1,45 @@ +package org.jetbrains.k2js.test; + +import org.junit.Test; + +/** + * @author Talanov Pavel + */ +public class OperationTest extends AbstractExpressionTest { + final private static String MAIN = "operation/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + @Test + public void prefixIntOperations() throws Exception { + testFooBoxIsTrue("prefixIntOperations.kt"); + } + + @Test + public void postfixIntOperations() throws Exception { + testFooBoxIsTrue("postfixIntOperations.kt"); + } + + @Test + public void notBoolean() throws Exception { + testFooBoxIsTrue("notBoolean.kt"); + } + + @Test + public void positiveAndNegativeNumbers() throws Exception { + testFooBoxIsTrue("positiveAndNegativeNumbers.kt"); + } + + @Test + public void assign() throws Exception { + testFunctionOutput("assign.jet", "foo", "f", 2.0); + } + + @Test + public void comparison() throws Exception { + testFooBoxIsTrue("comparison.kt"); + } +} diff --git a/translator/test/org/jetbrains/k2js/test/PatternMatchingTest.java b/translator/test/org/jetbrains/k2js/test/PatternMatchingTest.java new file mode 100644 index 00000000000..74d0996ba70 --- /dev/null +++ b/translator/test/org/jetbrains/k2js/test/PatternMatchingTest.java @@ -0,0 +1,52 @@ +package org.jetbrains.k2js.test; + +import org.junit.Test; + +/** + * @author Talanov Pavel + */ +public final class PatternMatchingTest extends IncludeLibraryTest { + + final private static String MAIN = "patternMatching/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + @Test + public void whenType() throws Exception { + testFooBoxIsTrue("whenType.kt"); + } + + @Test + public void whenNotType() throws Exception { + testFooBoxIsTrue("whenNotType.kt"); + } + + @Test + public void whenExecutesOnlyOnce() throws Exception { + testFooBoxIsTrue("whenExecutesOnlyOnce.kt"); + } + + @Test + public void whenValue() throws Exception { + testFooBoxIsTrue("whenValue.kt"); + } + + @Test + public void whenNotValue() throws Exception { + testFooBoxIsTrue("whenNotValue.kt"); + } + + @Test + public void whenValueOrType() throws Exception { + testFooBoxIsTrue("whenValueOrType.kt"); + } + + @Test + public void multipleCases() throws Exception { + testFunctionOutput("multipleCases.kt", "foo", "box", 2.0); + } + +} diff --git a/translator/test/org/jetbrains/k2js/test/PropertyAccessorTest.java b/translator/test/org/jetbrains/k2js/test/PropertyAccessorTest.java index aa410782026..3052216e426 100644 --- a/translator/test/org/jetbrains/k2js/test/PropertyAccessorTest.java +++ b/translator/test/org/jetbrains/k2js/test/PropertyAccessorTest.java @@ -5,7 +5,7 @@ import org.junit.Test; /** * @author Talanov Pavel */ -public final class PropertyAccessorTest extends AbstractClassTest { +public final class PropertyAccessorTest extends IncludeLibraryTest { final private static String MAIN = "propertyAccess/"; @@ -39,6 +39,16 @@ public final class PropertyAccessorTest extends AbstractClassTest { testFooBoxIsTrue("customSetter.kt"); } + @Test + public void safeCall() throws Exception { + testFooBoxIsTrue("safeCall.kt"); + } + + @Test + public void safeCallReturnsNullIfFails() throws Exception { + testFooBoxIsTrue("safeCallReturnsNullIfFails.kt"); + } + //TODO test // @Test // public void namespaceCustomAccessors() throws Exception { diff --git a/translator/test/org/jetbrains/k2js/test/RTTITest.java b/translator/test/org/jetbrains/k2js/test/RTTITest.java new file mode 100644 index 00000000000..0fa4ddc4d0f --- /dev/null +++ b/translator/test/org/jetbrains/k2js/test/RTTITest.java @@ -0,0 +1,26 @@ +package org.jetbrains.k2js.test; + +import org.junit.Test; + +/** + * @author Talanov Pavel + */ +public class RTTITest extends IncludeLibraryTest { + + final private static String MAIN = "rtti/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + @Test + public void isSameClass() throws Exception { + testFooBoxIsTrue("isSameClass.kt"); + } + + @Test + public void notIsOtherClass() throws Exception { + testFooBoxIsTrue("notIsOtherClass.kt"); + } +} diff --git a/translator/test/org/jetbrains/k2js/test/RhinoFunctionResultChecker.java b/translator/test/org/jetbrains/k2js/test/RhinoFunctionResultChecker.java index 5b0fb36bd09..8be5474e5bc 100644 --- a/translator/test/org/jetbrains/k2js/test/RhinoFunctionResultChecker.java +++ b/translator/test/org/jetbrains/k2js/test/RhinoFunctionResultChecker.java @@ -1,5 +1,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; @@ -16,27 +17,55 @@ public final class RhinoFunctionResultChecker implements RhinoResultChecker { final String functionName; final Object expectedResult; - public RhinoFunctionResultChecker(String namespaceName, String functionName, 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 = extractAndCallFunctionObject(namespaceName, functionName, context, scope); - assertTrue("Result is not what expected!", result.equals(expectedResult)); + 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) { - NativeObject namespaceObject = RhinoUtils.extractObject(namespaceName, scope); - Object box = namespaceObject.get(functionName, namespaceObject); - assertTrue("Function " + functionName + " not defined in namespace " + namespaceName, box instanceof Function); + Object functionObject; + if (namespaceName != null) { + functionObject = extractFunctionFromObject(namespaceName, functionName, scope); + } else { + functionObject = extractFunctionFromGlobalScope(functionName, scope); + } + return callFunctionAndCheckResults(cx, scope, (Function) functionObject); + } + + private Object callFunctionAndCheckResults(Context cx, Scriptable scope, Function functionObject) { Object functionArgs[] = {}; - Function function = (Function) box; - return function.call(cx, scope, scope, 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; } } diff --git a/translator/test/org/jetbrains/k2js/test/RhinoPropertyTypesChecker.java b/translator/test/org/jetbrains/k2js/test/RhinoPropertyTypesChecker.java new file mode 100644 index 00000000000..a9e1ab2a723 --- /dev/null +++ b/translator/test/org/jetbrains/k2js/test/RhinoPropertyTypesChecker.java @@ -0,0 +1,41 @@ +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 Talanov Pavel + */ +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/translator/test/org/jetbrains/k2js/test/StringTest.java b/translator/test/org/jetbrains/k2js/test/StringTest.java new file mode 100644 index 00000000000..fa8a9b09d02 --- /dev/null +++ b/translator/test/org/jetbrains/k2js/test/StringTest.java @@ -0,0 +1,26 @@ +package org.jetbrains.k2js.test; + +import org.junit.Test; + +/** + * @author Talanov Pavel + */ +public class StringTest extends AbstractExpressionTest { + + final private static String MAIN = "string/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + @Test + public void stringConstant() throws Exception { + testFooBoxIsTrue("stringConstant.kt"); + } + + @Test + public void stringAssignment() throws Exception { + testFooBoxIsTrue("stringAssignment.kt"); + } +} diff --git a/translator/test/org/jetbrains/k2js/test/TraitTest.java b/translator/test/org/jetbrains/k2js/test/TraitTest.java index b08ef053be9..d8346238200 100644 --- a/translator/test/org/jetbrains/k2js/test/TraitTest.java +++ b/translator/test/org/jetbrains/k2js/test/TraitTest.java @@ -5,7 +5,7 @@ import org.junit.Test; /** * @author Talanov Pavel */ -public final class TraitTest extends AbstractClassTest { +public final class TraitTest extends IncludeLibraryTest { final private static String MAIN = "trait/"; @@ -39,4 +39,15 @@ public final class TraitTest extends AbstractClassTest { testFooBoxIsTrue("traitExtendsTrait.kt"); } + @Test + public void traitExtendsTwoTraits() throws Exception { + testFooBoxIsTrue("traitExtendsTwoTraits.kt"); + } + + @Test + public void funDelegation() throws Exception { + testFooBoxIsOk("funDelegation.jet"); + } + + } diff --git a/translator/test/org/jetbrains/k2js/test/TranslationTest.java b/translator/test/org/jetbrains/k2js/test/TranslationTest.java index 600b54cc3b4..ae1ddbd0b58 100644 --- a/translator/test/org/jetbrains/k2js/test/TranslationTest.java +++ b/translator/test/org/jetbrains/k2js/test/TranslationTest.java @@ -1,7 +1,6 @@ package org.jetbrains.k2js.test; import org.jetbrains.k2js.K2JSTranslator; -import org.junit.Before; import org.mozilla.javascript.Context; import org.mozilla.javascript.Scriptable; @@ -19,45 +18,55 @@ public abstract class TranslationTest { final private static String OUT = "out/"; final private String KOTLIN_JS_LIB = TEST_FILES + "kotlin_lib.js"; - protected String testFilesDirectory; - protected String testCasesDirectory; - protected String outputDirectory; - - @Before - public void setUpClass() { - testCasesDirectory = CASES; - outputDirectory = OUT; - testFilesDirectory = TEST_FILES + mainDirectory(); - } - protected abstract String mainDirectory(); protected String kotlinLibraryPath() { return KOTLIN_JS_LIB; } - private String getOutputDirectory() { - return testFilesDirectory + outputDirectory; + private String casesDirectoryName() { + return CASES; } - private String getInputDirectory() { - return testFilesDirectory + testCasesDirectory; + private String outDirectoryName() { + return OUT; + } + + private String testFilesPath() { + return TEST_FILES + suiteDirectoryName() + mainDirectory(); + } + + protected String suiteDirectoryName() { + return ""; + } + + private String getOutputPath() { + return testFilesPath() + outDirectoryName(); + } + + private String getInputPath() { + return testFilesPath() + casesDirectoryName(); } protected void testFunctionOutput(String filename, String namespaceName, String functionName, Object expectedResult) throws Exception { + translateFile(filename); + runRhinoTest(generateFilenameList(getOutputFilePath(filename)), + new RhinoFunctionResultChecker(namespaceName, functionName, expectedResult)); + } + + private void translateFile(String filename) { K2JSTranslator.Arguments args = new K2JSTranslator.Arguments(); args.src = getInputFilePath(filename); args.outputDir = getOutputFilePath(filename); K2JSTranslator.translate(args); - runRhinoTest(generateFilenameList(args.outputDir), - new RhinoFunctionResultChecker(namespaceName, functionName, expectedResult)); } abstract protected List generateFilenameList(String inputfile); + //TODO: refactor filename generation logic private String getOutputFilePath(String filename) { - return getOutputDirectory() + convertToDotJsFile(filename); + return getOutputPath() + convertToDotJsFile(filename); } private String convertToDotJsFile(String filename) { @@ -65,7 +74,11 @@ public abstract class TranslationTest { } private String getInputFilePath(String filename) { - return getInputDirectory() + filename; + return getInputPath() + filename; + } + + protected String cases(String filename) { + return getInputFilePath(filename); } protected void runFileWithRhino(String inputFile, Context context, Scriptable scope) throws Exception { @@ -87,4 +100,9 @@ public abstract class TranslationTest { protected void testFooBoxIsTrue(String filename) throws Exception { testFunctionOutput(filename, "foo", "box", true); } + + protected void testFooBoxIsOk(String filename) throws Exception { + testFunctionOutput(filename, "foo", "box", "OK"); + } + } diff --git a/translator/test/org/jetbrains/k2js/test/TupleTest.java b/translator/test/org/jetbrains/k2js/test/TupleTest.java new file mode 100644 index 00000000000..4e8fab2ad21 --- /dev/null +++ b/translator/test/org/jetbrains/k2js/test/TupleTest.java @@ -0,0 +1,22 @@ +package org.jetbrains.k2js.test; + +/** + * @author Talanov Pavel + */ +public class TupleTest extends IncludeLibraryTest { + + final private static String MAIN = "tuple/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + //TODO: excluded because Tuples are not implemented +// @Test +// public void twoElements() throws Exception { +// testFooBoxIsTrue("twoElements.kt"); +// } + +} + diff --git a/translator/test/org/jetbrains/k2js/test/WhileTest.java b/translator/test/org/jetbrains/k2js/test/WhileTest.java new file mode 100644 index 00000000000..d31f87aaf99 --- /dev/null +++ b/translator/test/org/jetbrains/k2js/test/WhileTest.java @@ -0,0 +1,55 @@ +package org.jetbrains.k2js.test; + +import org.junit.Test; + +/** + * @author Talanov Pavel + */ +public final class WhileTest extends AbstractExpressionTest { + + private static final String MAIN = "while/"; + + protected String mainDirectory() { + return MAIN; + } + + @Test + public void whileSimpleTest() throws Exception { + testFooBoxIsTrue("while.kt"); + } + + @Test + public void doWhileSimpleTest() throws Exception { + testFooBoxIsTrue("doWhile.kt"); + } + + @Test + public void doWhileExecutesAtLeastOnce() throws Exception { + testFooBoxIsTrue("doWhile2.kt"); + } + + @Test + public void whileDoesntExecuteEvenOnceIfConditionIsFalse() throws Exception { + testFooBoxIsTrue("while2.kt"); + } + + @Test + public void breakWhile() throws Exception { + testFooBoxIsTrue("breakWhile.kt"); + } + + @Test + public void breakDoWhile() throws Exception { + testFooBoxIsTrue("breakDoWhile.kt"); + } + + @Test + public void continueWhile() throws Exception { + testFooBoxIsTrue("continueWhile.kt"); + } + + @Test + public void continueDoWhile() throws Exception { + testFooBoxIsTrue("continueDoWhile.kt"); + } +} diff --git a/translator/testFiles/expression/cases/test.kt b/translator/testFiles/expression/cases/test.kt deleted file mode 100644 index 98ee81ba1ea..00000000000 --- a/translator/testFiles/expression/cases/test.kt +++ /dev/null @@ -1,9 +0,0 @@ -namespace foo - -fun box() : Boolean { - val a = 2; - val b = 3; - var c = 4; - return (a < c) -} - diff --git a/translator/testFiles/expression/cases/elseif.kt b/translator/testFiles/expression/conditional/cases/elseif.kt similarity index 100% rename from translator/testFiles/expression/cases/elseif.kt rename to translator/testFiles/expression/conditional/cases/elseif.kt diff --git a/translator/testFiles/expression/cases/if.kt b/translator/testFiles/expression/conditional/cases/if.kt similarity index 100% rename from translator/testFiles/expression/cases/if.kt rename to translator/testFiles/expression/conditional/cases/if.kt diff --git a/translator/testFiles/expression/cases/ifElseAsExpression.kt b/translator/testFiles/expression/conditional/cases/ifElseAsExpression.kt similarity index 100% rename from translator/testFiles/expression/cases/ifElseAsExpression.kt rename to translator/testFiles/expression/conditional/cases/ifElseAsExpression.kt diff --git a/translator/testFiles/expression/function/cases/adderClosure.kt b/translator/testFiles/expression/function/cases/adderClosure.kt new file mode 100644 index 00000000000..06e2ea8f116 --- /dev/null +++ b/translator/testFiles/expression/function/cases/adderClosure.kt @@ -0,0 +1,9 @@ +namespace 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/translator/testFiles/expression/function/cases/closureWithParameter.jet b/translator/testFiles/expression/function/cases/closureWithParameter.jet new file mode 100644 index 00000000000..8674e078051 --- /dev/null +++ b/translator/testFiles/expression/function/cases/closureWithParameter.jet @@ -0,0 +1,9 @@ +namespace foo + +fun box() : String { + return apply( "OK", {(arg: String) => arg } ) +} + +fun apply(arg : String, f : fun (p:String) : String) : String { + return f(arg) +} diff --git a/translator/testFiles/expression/function/cases/closureWithParameterAndBoxing.jet b/translator/testFiles/expression/function/cases/closureWithParameterAndBoxing.jet new file mode 100644 index 00000000000..3b05060982d --- /dev/null +++ b/translator/testFiles/expression/function/cases/closureWithParameterAndBoxing.jet @@ -0,0 +1,9 @@ +namespace foo + +fun box() : String { + return if (apply( 5, {(arg: Int) => arg + 13 } ) == 18) "OK" else "fail" +} + +fun apply(arg : Int, f : fun (p:Int) : Int) : Int { + return f(arg) +} diff --git a/translator/testFiles/expression/function/cases/functionLiteral.kt b/translator/testFiles/expression/function/cases/functionLiteral.kt new file mode 100644 index 00000000000..b0eefc30f42 --- /dev/null +++ b/translator/testFiles/expression/function/cases/functionLiteral.kt @@ -0,0 +1,8 @@ +namespace 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/translator/testFiles/expression/function/cases/functionLiteralAsParameter.kt b/translator/testFiles/expression/function/cases/functionLiteralAsParameter.kt new file mode 100644 index 00000000000..3ec397a9127 --- /dev/null +++ b/translator/testFiles/expression/function/cases/functionLiteralAsParameter.kt @@ -0,0 +1,10 @@ +namespace foo + +fun apply(f : fun(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/translator/testFiles/expression/cases/functionUsedBeforeDeclaration.kt b/translator/testFiles/expression/function/cases/functionUsedBeforeDeclaration.kt similarity index 100% rename from translator/testFiles/expression/cases/functionUsedBeforeDeclaration.kt rename to translator/testFiles/expression/function/cases/functionUsedBeforeDeclaration.kt diff --git a/translator/testFiles/expression/cases/functionWithTwoParametersCall.kt b/translator/testFiles/expression/function/cases/functionWithTwoParametersCall.kt similarity index 100% rename from translator/testFiles/expression/cases/functionWithTwoParametersCall.kt rename to translator/testFiles/expression/function/cases/functionWithTwoParametersCall.kt diff --git a/translator/testFiles/expression/function/cases/loopClosure.kt b/translator/testFiles/expression/function/cases/loopClosure.kt new file mode 100644 index 00000000000..5c5a1e5615d --- /dev/null +++ b/translator/testFiles/expression/function/cases/loopClosure.kt @@ -0,0 +1,17 @@ +namespace foo + +var b = 0 + +fun loop(var times : Int) { + while(times > 0) { + val u : fun(value : Int) : Unit = { + b++ + } + u(times--) + } +} + +fun box() : Boolean { + loop(5) + return b == 5 +} \ No newline at end of file diff --git a/translator/testFiles/expression/cases/localProperty.jet b/translator/testFiles/expression/misc/cases/localProperty.jet similarity index 100% rename from translator/testFiles/expression/cases/localProperty.jet rename to translator/testFiles/expression/misc/cases/localProperty.jet diff --git a/translator/testFiles/expression/cases/assign.jet b/translator/testFiles/expression/operation/cases/assign.jet similarity index 100% rename from translator/testFiles/expression/cases/assign.jet rename to translator/testFiles/expression/operation/cases/assign.jet diff --git a/translator/testFiles/expression/cases/comparison.kt b/translator/testFiles/expression/operation/cases/comparison.kt similarity index 100% rename from translator/testFiles/expression/cases/comparison.kt rename to translator/testFiles/expression/operation/cases/comparison.kt diff --git a/translator/testFiles/expression/cases/notBoolean.kt b/translator/testFiles/expression/operation/cases/notBoolean.kt similarity index 100% rename from translator/testFiles/expression/cases/notBoolean.kt rename to translator/testFiles/expression/operation/cases/notBoolean.kt diff --git a/translator/testFiles/expression/cases/positiveAndNegativeNumbers.kt b/translator/testFiles/expression/operation/cases/positiveAndNegativeNumbers.kt similarity index 100% rename from translator/testFiles/expression/cases/positiveAndNegativeNumbers.kt rename to translator/testFiles/expression/operation/cases/positiveAndNegativeNumbers.kt diff --git a/translator/testFiles/expression/cases/postfixIntOperations.kt b/translator/testFiles/expression/operation/cases/postfixIntOperations.kt similarity index 100% rename from translator/testFiles/expression/cases/postfixIntOperations.kt rename to translator/testFiles/expression/operation/cases/postfixIntOperations.kt diff --git a/translator/testFiles/expression/cases/prefixIntOperations.kt b/translator/testFiles/expression/operation/cases/prefixIntOperations.kt similarity index 100% rename from translator/testFiles/expression/cases/prefixIntOperations.kt rename to translator/testFiles/expression/operation/cases/prefixIntOperations.kt diff --git a/translator/testFiles/expression/cases/stringAssignment.kt b/translator/testFiles/expression/string/cases/stringAssignment.kt similarity index 100% rename from translator/testFiles/expression/cases/stringAssignment.kt rename to translator/testFiles/expression/string/cases/stringAssignment.kt diff --git a/translator/testFiles/expression/cases/stringConstant.kt b/translator/testFiles/expression/string/cases/stringConstant.kt similarity index 100% rename from translator/testFiles/expression/cases/stringConstant.kt rename to translator/testFiles/expression/string/cases/stringConstant.kt diff --git a/translator/testFiles/expression/while/cases/breakDoWhile.kt b/translator/testFiles/expression/while/cases/breakDoWhile.kt new file mode 100644 index 00000000000..b69bec71763 --- /dev/null +++ b/translator/testFiles/expression/while/cases/breakDoWhile.kt @@ -0,0 +1,13 @@ +namespace foo + +fun box() : Boolean { + var i = 0 + do { + if (i == 3) { + break; + } + ++i; + } while ( i < 100) + + return i == 3 +} \ No newline at end of file diff --git a/translator/testFiles/expression/while/cases/breakWhile.kt b/translator/testFiles/expression/while/cases/breakWhile.kt new file mode 100644 index 00000000000..c0b2a84a810 --- /dev/null +++ b/translator/testFiles/expression/while/cases/breakWhile.kt @@ -0,0 +1,13 @@ +namespace foo + +fun box() : Boolean { + var i = 0 + while ( i < 100) { + if (i == 3) { + break; + } + ++i; + } + + return i == 3 +} \ No newline at end of file diff --git a/translator/testFiles/expression/while/cases/continueDoWhile.kt b/translator/testFiles/expression/while/cases/continueDoWhile.kt new file mode 100644 index 00000000000..a514a8e70ca --- /dev/null +++ b/translator/testFiles/expression/while/cases/continueDoWhile.kt @@ -0,0 +1,15 @@ +namespace foo + +fun box() : Boolean { + var i = 0 + var b = true + do { + ++i; + if (i >= 1) { + continue; + } + b =false; + } while (i < 100) + + return b +} \ No newline at end of file diff --git a/translator/testFiles/expression/while/cases/continueWhile.kt b/translator/testFiles/expression/while/cases/continueWhile.kt new file mode 100644 index 00000000000..0094978a9b5 --- /dev/null +++ b/translator/testFiles/expression/while/cases/continueWhile.kt @@ -0,0 +1,15 @@ +namespace foo + +fun box() : Boolean { + var i = 0 + var b = true + while (i < 100) { + ++i; + if (i >= 1) { + continue; + } + b =false; + } + + return b +} \ No newline at end of file diff --git a/translator/testFiles/expression/cases/doWhile.kt b/translator/testFiles/expression/while/cases/doWhile.kt similarity index 100% rename from translator/testFiles/expression/cases/doWhile.kt rename to translator/testFiles/expression/while/cases/doWhile.kt diff --git a/translator/testFiles/expression/cases/doWhile2.kt b/translator/testFiles/expression/while/cases/doWhile2.kt similarity index 100% rename from translator/testFiles/expression/cases/doWhile2.kt rename to translator/testFiles/expression/while/cases/doWhile2.kt diff --git a/translator/testFiles/expression/cases/while.kt b/translator/testFiles/expression/while/cases/while.kt similarity index 100% rename from translator/testFiles/expression/cases/while.kt rename to translator/testFiles/expression/while/cases/while.kt diff --git a/translator/testFiles/expression/cases/while2.kt b/translator/testFiles/expression/while/cases/while2.kt similarity index 100% rename from translator/testFiles/expression/cases/while2.kt rename to translator/testFiles/expression/while/cases/while2.kt diff --git a/translator/testFiles/kotlinLib/cases/isAncestorType.js b/translator/testFiles/kotlinLib/cases/isAncestorType.js new file mode 100644 index 00000000000..29fea2ff1b1 --- /dev/null +++ b/translator/testFiles/kotlinLib/cases/isAncestorType.js @@ -0,0 +1,7 @@ +var A = Class.create(); +var B = Class.create(A); +var b = new B; + +test = function() { + return (isType(b, A) && isType(b, B)); +} \ No newline at end of file diff --git a/translator/testFiles/kotlinLib/cases/isComplexTest.js b/translator/testFiles/kotlinLib/cases/isComplexTest.js new file mode 100644 index 00000000000..c935e77afc0 --- /dev/null +++ b/translator/testFiles/kotlinLib/cases/isComplexTest.js @@ -0,0 +1,24 @@ +var A = Class.create(); +var B = Class.create(A); +var b = new B; +var C = Class.create(B); +var c = new C; +var E = Class.create(A) +var e = new E; + +test1 = function() { + b2 = b + return (isType(b, A) && isType(b, B)); +} + +test2 = function() { + return (isType(c, C) && isType(c, B) && isType(c, A) && (!isType(c, E))); +} + +test3 = function() { + return isType(e, E) && !isType(e, B) && !isType(e, C) && isType(e, A) +} + +test = function() { + return test1() && test2() && test3() +} \ No newline at end of file diff --git a/translator/testFiles/kotlinLib/cases/isNotOtherType.js b/translator/testFiles/kotlinLib/cases/isNotOtherType.js new file mode 100644 index 00000000000..624f1584c89 --- /dev/null +++ b/translator/testFiles/kotlinLib/cases/isNotOtherType.js @@ -0,0 +1,8 @@ +var A = Class.create(); +var B = Class.create(A); +var C = Class.create(); +var c = new C; + +test = function() { + return ((!isType(c, A)) && !isType(c, B)); +} \ No newline at end of file diff --git a/translator/testFiles/kotlinLib/cases/isSameType.js b/translator/testFiles/kotlinLib/cases/isSameType.js new file mode 100644 index 00000000000..bb8dfb4e7db --- /dev/null +++ b/translator/testFiles/kotlinLib/cases/isSameType.js @@ -0,0 +1,6 @@ +var A = Class.create(); +var a = new A; + +test = function() { + return isType(a, A); +} \ No newline at end of file diff --git a/translator/testFiles/kotlinLib/cases/trait.js b/translator/testFiles/kotlinLib/cases/trait.js new file mode 100644 index 00000000000..f1efec21a20 --- /dev/null +++ b/translator/testFiles/kotlinLib/cases/trait.js @@ -0,0 +1,12 @@ +foo = {}; +(function(foo){ + foo.Test = Trait.create({addFoo:function(s){ + return s + 'FOO'; + } + }); + foo.ExtendedTest = Trait.create(foo.Test, {hooray:function(){ + return 'hooray'; + } + }); +} +(foo)); diff --git a/translator/testFiles/kotlin_lib.js b/translator/testFiles/kotlin_lib.js index 8c0e002e123..9eccea4f81b 100644 --- a/translator/testFiles/kotlin_lib.js +++ b/translator/testFiles/kotlin_lib.js @@ -6,6 +6,17 @@ function $A(iterable) { return results; } +var isType = function(object, class) { + current = object.get_class(); + while (current !== class) { + if (current === null) { + return false; + } + current = current.superclass; + } + return true; +} + var emptyFunction = function() {} var Class = (function() { @@ -32,10 +43,17 @@ var Class = (function() { } + klass.addMethods( + { + get_class : function () { + return klass; + } + }); + if (parent != null) { klass.addMethods( { - 'super_init' : function () { + super_init : function () { this.initializing = this.initializing.superclass; this.initializing.prototype.initialize.apply(this, arguments) } @@ -66,8 +84,6 @@ var Class = (function() { return function() { return ancestor[m].apply(this, arguments); }; })(property).wrap(method); - // value.valueOf = method.valueOf.bind(method); - // value.toString = method.toString.bind(method); } this.prototype[property] = value; } @@ -85,8 +101,25 @@ var Class = (function() { var Trait = (function() { + + function add(object, source) { + 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() { - return new Class.create(arguments); + + result = {} + for (var i = 0, length = arguments.length; i < length; i++) + { + add(result, arguments[i]); + } + return result; } return { diff --git a/translator/testFiles/patternMatching/cases/multipleCases.kt b/translator/testFiles/patternMatching/cases/multipleCases.kt new file mode 100644 index 00000000000..b75ebe8d8d3 --- /dev/null +++ b/translator/testFiles/patternMatching/cases/multipleCases.kt @@ -0,0 +1,17 @@ +namespace foo + +fun box() : Int { + val c = 3 + val d = 5 + var z = 0 + when(c) { + is 5, is 3 => z++; + else => z = -1000; + } + + when(d) { + is 5, is 3 => z++; + else => z = -1000; + } + return z +} \ No newline at end of file diff --git a/translator/testFiles/patternMatching/cases/whenExecutesOnlyOnce.kt b/translator/testFiles/patternMatching/cases/whenExecutesOnlyOnce.kt new file mode 100644 index 00000000000..3ba142f334c --- /dev/null +++ b/translator/testFiles/patternMatching/cases/whenExecutesOnlyOnce.kt @@ -0,0 +1,15 @@ +namespace foo + +class A() { + +} + +fun box() : Boolean { + var a = 0 + when(A()) { + is A => a++; + is A => a++; + else => a++; + } + return (a == 1) +} \ No newline at end of file diff --git a/translator/testFiles/patternMatching/cases/whenNotType.kt b/translator/testFiles/patternMatching/cases/whenNotType.kt new file mode 100644 index 00000000000..be7d077a352 --- /dev/null +++ b/translator/testFiles/patternMatching/cases/whenNotType.kt @@ -0,0 +1,12 @@ +namespace foo + +class A() { + +} + +fun box() : Boolean { + when(A()) { + !is A => return false; + else => return true; + } +} \ No newline at end of file diff --git a/translator/testFiles/patternMatching/cases/whenNotValue.kt b/translator/testFiles/patternMatching/cases/whenNotValue.kt new file mode 100644 index 00000000000..949ef0b068d --- /dev/null +++ b/translator/testFiles/patternMatching/cases/whenNotValue.kt @@ -0,0 +1,10 @@ +namespace foo + +fun box() : Boolean { + var a = 4 + when(a) { + !is 3 => a = 10; + !is 4 => a = 20; + } + return (a == 10) +} \ No newline at end of file diff --git a/translator/testFiles/patternMatching/cases/whenType.kt b/translator/testFiles/patternMatching/cases/whenType.kt new file mode 100644 index 00000000000..87cfccc929f --- /dev/null +++ b/translator/testFiles/patternMatching/cases/whenType.kt @@ -0,0 +1,12 @@ +namespace foo + +class A() { + +} + +fun box() : Boolean { + when(A()) { + is A => return true; + else => return false; + } +} \ No newline at end of file diff --git a/translator/testFiles/patternMatching/cases/whenValue.kt b/translator/testFiles/patternMatching/cases/whenValue.kt new file mode 100644 index 00000000000..52093a79b4c --- /dev/null +++ b/translator/testFiles/patternMatching/cases/whenValue.kt @@ -0,0 +1,10 @@ +namespace foo + +fun box() : Boolean { + var a = 4 + when(a) { + is 3 => a = 10; + is 4 => a = 20; + } + return (a == 20) +} \ No newline at end of file diff --git a/translator/testFiles/patternMatching/cases/whenValueOrType.kt b/translator/testFiles/patternMatching/cases/whenValueOrType.kt new file mode 100644 index 00000000000..71afe50d300 --- /dev/null +++ b/translator/testFiles/patternMatching/cases/whenValueOrType.kt @@ -0,0 +1,25 @@ +namespace foo + +class A() { + +} + +class B() { + +} + +fun box() : Boolean { + var c : Int = 0 + var a = A() : Any? + var b = null : Any? + when(a) { + is null => c = 10; + is B => c = 10000 + is A => c = 20; + } + when(b) { + is null => c += 5 + is B => c += 100 + } + return (c == 25) +} \ No newline at end of file diff --git a/translator/testFiles/propertyAccess/cases/safeCall.kt b/translator/testFiles/propertyAccess/cases/safeCall.kt new file mode 100644 index 00000000000..2a918b76ff8 --- /dev/null +++ b/translator/testFiles/propertyAccess/cases/safeCall.kt @@ -0,0 +1,13 @@ +namespace foo + +class A() { + fun doSomething() { + + } +} + +fun box() : Boolean { + var a : A? = null; + a?.doSomething() + return true; +} \ No newline at end of file diff --git a/translator/testFiles/propertyAccess/cases/safeCallReturnsNullIfFails.kt b/translator/testFiles/propertyAccess/cases/safeCallReturnsNullIfFails.kt new file mode 100644 index 00000000000..b5d44bb29bb --- /dev/null +++ b/translator/testFiles/propertyAccess/cases/safeCallReturnsNullIfFails.kt @@ -0,0 +1,10 @@ +namespace foo + +class A() { + val x = 4 +} + +fun box() : Boolean { + var a : A? = null; + return (a?.x == null); +} \ No newline at end of file diff --git a/translator/testFiles/prototype.js b/translator/testFiles/prototype.js new file mode 100644 index 00000000000..8de2bd31b17 --- /dev/null +++ b/translator/testFiles/prototype.js @@ -0,0 +1,388 @@ +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; +} + +var emptyFunction = function() {} + +var Class = (function() { + + var IS_DONTENUM_BUGGY = (function(){ + for (var p in { toString: 1 }) { + if (p === 'toString') return false; + } + return true; + })(); + + function subclass() {}; + function create() { + var parent = null, properties = $A(arguments); + if (Object.isFunction(properties[0])) + parent = properties.shift(); + + function 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); + } + + 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); + + if (IS_DONTENUM_BUGGY) { + if (source.toString != Object.prototype.toString) + properties.push("toString"); + if (source.valueOf != Object.prototype.valueOf) + properties.push("valueOf"); + } + + for (var i = 0, length = properties.length; i < length; i++) { + var property = properties[i], value = source[property]; + if (ancestor && Object.isFunction(value) && + value.argumentNames()[0] == "$super") { + var method = value; + value = (function(m) { + return function() { return ancestor[m].apply(this, arguments); }; + })(property).wrap(method); + + value.valueOf = method.valueOf.bind(method); + value.toString = method.toString.bind(method); + } + this.prototype[property] = value; + } + + return this; + } + + return { + create: create, + Methods: { + addMethods: addMethods + } + }; +})(); + +var Trait = (function() { + + function create() { + var traitClass = Class.create.apply(Class, arguments) + return new traitClass; + } + + return { + create: create + }; +})(); + +(function() { + + var _toString = Object.prototype.toString, + NULL_TYPE = 'Null', + UNDEFINED_TYPE = 'Undefined', + BOOLEAN_TYPE = 'Boolean', + NUMBER_TYPE = 'Number', + STRING_TYPE = 'String', + OBJECT_TYPE = 'Object', + FUNCTION_CLASS = '[object Function]', + BOOLEAN_CLASS = '[object Boolean]', + NUMBER_CLASS = '[object Number]', + STRING_CLASS = '[object String]', + ARRAY_CLASS = '[object Array]', + DATE_CLASS = '[object Date]'; + + function Type(o) { + switch(o) { + case null: return NULL_TYPE; + case (void 0): return UNDEFINED_TYPE; + } + var type = typeof o; + switch(type) { + case 'boolean': return BOOLEAN_TYPE; + case 'number': return NUMBER_TYPE; + case 'string': return STRING_TYPE; + } + return OBJECT_TYPE; + } + + function extend(destination, source) { + for (var property in source) + destination[property] = source[property]; + return destination; + } + + function inspect(object) { + try { + if (isUndefined(object)) return 'undefined'; + if (object === null) return 'null'; + return object.inspect ? object.inspect() : String(object); + } catch (e) { + if (e instanceof RangeError) return '...'; + throw e; + } + } + + function toJSON(value) { + return Str('', { '': value }, []); + } + + function Str(key, holder, stack) { + var value = holder[key], + type = typeof value; + + if (Type(value) === OBJECT_TYPE && typeof value.toJSON === 'function') { + value = value.toJSON(key); + } + + var _class = _toString.call(value); + + switch (_class) { + case NUMBER_CLASS: + case BOOLEAN_CLASS: + case STRING_CLASS: + value = value.valueOf(); + } + + switch (value) { + case null: return 'null'; + case true: return 'true'; + case false: return 'false'; + } + + type = typeof value; + switch (type) { + case 'string': + return value.inspect(true); + case 'number': + return isFinite(value) ? String(value) : 'null'; + case 'object': + + for (var i = 0, length = stack.length; i < length; i++) { + if (stack[i] === value) { throw new TypeError(); } + } + stack.push(value); + + var partial = []; + if (_class === ARRAY_CLASS) { + for (var i = 0, length = value.length; i < length; i++) { + var str = Str(i, value, stack); + partial.push(typeof str === 'undefined' ? 'null' : str); + } + partial = '[' + partial.join(',') + ']'; + } else { + var keys = Object.keys(value); + for (var i = 0, length = keys.length; i < length; i++) { + var key = keys[i], str = Str(key, value, stack); + if (typeof str !== "undefined") { + partial.push(key.inspect(true)+ ':' + str); + } + } + partial = '{' + partial.join(',') + '}'; + } + stack.pop(); + return partial; + } + } + + function stringify(object) { + return JSON.stringify(object); + } + + function toQueryString(object) { + return $H(object).toQueryString(); + } + + function toHTML(object) { + return object && object.toHTML ? object.toHTML() : String.interpret(object); + } + + 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; + } + + function clone(object) { + return extend({ }, object); + } + + function isElement(object) { + return !!(object && object.nodeType == 1); + } + + function isArray(object) { + return _toString.call(object) === ARRAY_CLASS; + } + + var hasNativeIsArray = (typeof Array.isArray == 'function') + && Array.isArray([]) && !Array.isArray({}); + + if (hasNativeIsArray) { + isArray = Array.isArray; + } + + function isHash(object) { + return object instanceof Hash; + } + + function isFunction(object) { + return _toString.call(object) === FUNCTION_CLASS; + } + + function isString(object) { + return _toString.call(object) === STRING_CLASS; + } + + function isNumber(object) { + return _toString.call(object) === NUMBER_CLASS; + } + + function isDate(object) { + return _toString.call(object) === DATE_CLASS; + } + + function isUndefined(object) { + return typeof object === "undefined"; + } + + extend(Object, { + extend: extend, + inspect: inspect, + toQueryString: toQueryString, + toHTML: toHTML, + keys: Object.keys || keys, + values: values, + clone: clone, + isElement: isElement, + isArray: isArray, + isHash: isHash, + isFunction: isFunction, + isString: isString, + isNumber: isNumber, + isDate: isDate, + isUndefined: isUndefined + }); +})(); + + +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 curry() { + if (!arguments.length) return this; + var __method = this, args = slice.call(arguments, 0); + return function() { + var a = merge(args, arguments); + return __method.apply(this, a); + } + } + + function delay(timeout) { + var __method = this, args = slice.call(arguments, 1); + timeout = timeout * 1000; + return window.setTimeout(function() { + return __method.apply(__method, args); + }, timeout); + } + + function defer() { + var args = update([0.01], arguments); + return this.delay.apply(this, args); + } + + function wrap(wrapper) { + var __method = this; + return function() { + var a = update([__method.bind(this)], arguments); + return wrapper.apply(this, a); + } + } + + function methodize() { + if (this._methodized) return this._methodized; + var __method = this; + return this._methodized = function() { + var a = update([this], arguments); + return __method.apply(null, a); + }; + } + + return { + argumentNames: argumentNames, + bind: bind, + bindAsEventListener: bindAsEventListener, + curry: curry, + delay: delay, + defer: defer, + wrap: wrap, + methodize: methodize + } +})()); diff --git a/translator/testFiles/rtti/cases/isSameClass.kt b/translator/testFiles/rtti/cases/isSameClass.kt new file mode 100644 index 00000000000..49517d74b05 --- /dev/null +++ b/translator/testFiles/rtti/cases/isSameClass.kt @@ -0,0 +1,7 @@ +namespace foo + +class A() { + +} + +fun box() = (A() is A) \ No newline at end of file diff --git a/translator/testFiles/rtti/cases/notIsOtherClass.kt b/translator/testFiles/rtti/cases/notIsOtherClass.kt new file mode 100644 index 00000000000..9aed1fe8aa9 --- /dev/null +++ b/translator/testFiles/rtti/cases/notIsOtherClass.kt @@ -0,0 +1,11 @@ +namespace foo + +open class A() { + +} + +class B() : A() { + +} + +fun box() = (A() !is B) \ No newline at end of file diff --git a/translator/testFiles/trait/cases/funDelegation.jet b/translator/testFiles/trait/cases/funDelegation.jet new file mode 100644 index 00000000000..8a5258d2cee --- /dev/null +++ b/translator/testFiles/trait/cases/funDelegation.jet @@ -0,0 +1,19 @@ +namespace foo + +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/translator/testFiles/trait/cases/traitExtendsTwoTraits.kt b/translator/testFiles/trait/cases/traitExtendsTwoTraits.kt new file mode 100644 index 00000000000..fa28a2a94c9 --- /dev/null +++ b/translator/testFiles/trait/cases/traitExtendsTwoTraits.kt @@ -0,0 +1,25 @@ +namespace foo + +trait A { + fun addFoo(s:String) : String { + return s + "FOO" + } +} + +trait B { + fun hooray() : String { + return "hooray" + } +} + +trait AD : A, B { + +} + +class Test() : AD { + fun eval() : String { + return addFoo(hooray()); + } +} + +fun box() = (Test().eval() == "hoorayFOO") \ No newline at end of file diff --git a/translator/testFiles/tuple/cases/twoElements.kt b/translator/testFiles/tuple/cases/twoElements.kt new file mode 100644 index 00000000000..8a8524ca4d4 --- /dev/null +++ b/translator/testFiles/tuple/cases/twoElements.kt @@ -0,0 +1,6 @@ +namespace foo + +fun box() : Boolean { + val a : (Int, Int) = (1, 2) + return (a._1 == 1) + } \ No newline at end of file