JS backend: added enum class support.

This commit is contained in:
Erokhin Stanislav
2013-08-12 20:26:15 +04:00
parent 3b5d5a4a95
commit adcb359c38
11 changed files with 279 additions and 9 deletions
@@ -0,0 +1,34 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.k2js.test.semantics;
import org.jetbrains.k2js.test.SingleFileTranslationTest;
public class EnumTest extends SingleFileTranslationTest {
public EnumTest() {
super("enum/");
}
public void testSimple() throws Exception {
checkFooBoxIsOk();
}
public void testStandardMethods() throws Exception {
checkFooBoxIsOk();
}
}
@@ -40,6 +40,7 @@ public final class Namer {
private static final String CLASS_OBJECT_NAME = "createClass";
private static final String TRAIT_OBJECT_NAME = "createTrait";
private static final String OBJECT_OBJECT_NAME = "createObject";
private static final String ENUM_ENTRIES_NAME = "createEnumEntries";
private static final String SETTER_PREFIX = "set_";
private static final String GETTER_PREFIX = "get_";
private static final String BACKING_FIELD_PREFIX = "$";
@@ -142,6 +143,8 @@ public final class Namer {
private final JsExpression definePackage;
@NotNull
private final JsName objectName;
@NotNull
private final JsName enumEntriesName;
@NotNull
private final JsName isTypeName;
@@ -154,6 +157,7 @@ public final class Namer {
definePackage = kotlin("definePackage");
className = kotlinScope.declareName(CLASS_OBJECT_NAME);
enumEntriesName = kotlinScope.declareName(ENUM_ENTRIES_NAME);
objectName = kotlinScope.declareName(OBJECT_OBJECT_NAME);
isTypeName = kotlinScope.declareName("isType");
@@ -164,6 +168,11 @@ public final class Namer {
return kotlin(className);
}
@NotNull
public JsExpression enumEntriesCreationMethodReference() {
return kotlin(enumEntriesName);
}
@NotNull
public JsExpression traitCreationMethodReference() {
return kotlin(traitName);
@@ -224,13 +233,19 @@ public final class Namer {
switch (descriptor.getKind()) {
case TRAIT:
return new JsInvocation(traitCreationMethodReference());
case OBJECT:
return new JsInvocation(objectCreationMethodReference());
case CLASS_OBJECT:
case ENUM_ENTRY:
return new JsInvocation(objectCreationMethodReference());
default:
return new JsInvocation(classCreationMethodReference());
}
}
@NotNull
public JsInvocation enumEntriesObjectCreateInvocation() {
return new JsInvocation(enumEntriesCreationMethodReference());
}
}
@@ -99,6 +99,8 @@ public final class StandardClasses {
standardClasses.declare().forFQ("jet.IntProgression").kotlinClass("NumberProgression")
.methods("iterator", "contains").properties("start", "end", "increment");
standardClasses.declare().forFQ("jet.Enum").kotlinClass("Enum");
}
@@ -43,11 +43,13 @@ import java.util.List;
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.*;
import static org.jetbrains.k2js.translate.expression.LiteralFunctionTranslator.createPlace;
import static org.jetbrains.k2js.translate.initializer.InitializerUtils.createPropertyInitializer;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getClassDescriptor;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getPropertyDescriptorForConstructorParameter;
import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getContainingClass;
import static org.jetbrains.k2js.translate.utils.PsiUtils.getPrimaryConstructorParameters;
import static org.jetbrains.k2js.translate.utils.TranslationUtils.getQualifiedReference;
import static org.jetbrains.k2js.translate.utils.TranslationUtils.simpleReturnFunction;
/**
* Generates a definition of a single class.
@@ -173,7 +175,9 @@ public final class ClassTranslator extends AbstractTranslator {
}
translatePropertiesAsConstructorParameters(declarationContext, properties);
new DeclarationBodyVisitor(properties, staticProperties).traverseContainer(classDeclaration, declarationContext);
DeclarationBodyVisitor bodyVisitor = new DeclarationBodyVisitor(properties, staticProperties);
bodyVisitor.traverseContainer(classDeclaration, declarationContext);
mayBeAddEnumEntry(bodyVisitor.getEnumEntryList(), staticProperties, declarationContext);
if (isTopLevelDeclaration) {
declarationContext.literalFunctionTranslator().setDefinitionPlace(null);
@@ -198,6 +202,21 @@ public final class ClassTranslator extends AbstractTranslator {
}
}
private void mayBeAddEnumEntry(@NotNull List<JsPropertyInitializer> enumEntryList,
@NotNull List<JsPropertyInitializer> staticProperties,
@NotNull TranslationContext declarationContext
) {
if (descriptor.getKind() == ClassKind.ENUM_CLASS) {
JsInvocation invocation = context().namer().enumEntriesObjectCreateInvocation();
invocation.getArguments().add(new JsObjectLiteral(enumEntryList, true));
JsFunction fun = simpleReturnFunction(declarationContext.getScopeForDescriptor(descriptor), invocation);
staticProperties.add(createPropertyInitializer(Namer.getNamedForClassObjectInitializer(), fun, declarationContext));
} else {
assert enumEntryList.isEmpty(): "Only enum class may have enum entry. Class kind is: " + descriptor.getKind();
}
}
private void addSuperclassReferences(@NotNull JsInvocation jsClassDeclaration) {
List<JsExpression> superClassReferences = getSupertypesNameReferences();
if (superClassReferences.isEmpty()) {
@@ -244,6 +263,9 @@ public final class ClassTranslator extends AbstractTranslator {
}
list.add(getClassReference(result));
break;
case ENUM_CLASS:
base = getClassReference(result);
break;
default:
throw new UnsupportedOperationException("unsupported super class kind " + result.getKind().name());
@@ -21,13 +21,18 @@ import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.k2js.translate.context.Namer;
import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.general.Translation;
import org.jetbrains.k2js.translate.general.TranslatorVisitor;
import org.jetbrains.k2js.translate.initializer.ClassInitializerTranslator;
import org.jetbrains.k2js.translate.utils.BindingUtils;
import org.jetbrains.k2js.translate.utils.JsAstUtils;
import org.jetbrains.k2js.translate.utils.TranslationUtils;
import java.util.Collection;
import java.util.List;
import static org.jetbrains.k2js.translate.initializer.InitializerUtils.createPropertyInitializer;
@@ -36,6 +41,7 @@ import static org.jetbrains.k2js.translate.utils.BindingUtils.*;
public class DeclarationBodyVisitor extends TranslatorVisitor<Void> {
protected final List<JsPropertyInitializer> result;
protected final List<JsPropertyInitializer> staticResult;
protected final List<JsPropertyInitializer> enumEntryList = new SmartList<JsPropertyInitializer>();
public DeclarationBodyVisitor() {
this(new SmartList<JsPropertyInitializer>(), new SmartList<JsPropertyInitializer>());
@@ -51,11 +57,41 @@ public class DeclarationBodyVisitor extends TranslatorVisitor<Void> {
return result;
}
public List<JsPropertyInitializer> getEnumEntryList() {
return enumEntryList;
}
@Override
public Void visitClass(@NotNull JetClass expression, @NotNull TranslationContext context) {
return null;
}
@Override
public Void visitEnumEntry(
final JetEnumEntry enumEntry, TranslationContext data
) {
JsExpression jsEnumEntryCreation;
ClassDescriptor descriptor = getClassDescriptor(data.bindingContext(), enumEntry);
Collection<JetType> supertypes = descriptor.getTypeConstructor().getSupertypes();
if (enumEntry.getBody() != null || supertypes.size() > 1) {
jsEnumEntryCreation = ClassTranslator.generateClassCreation(enumEntry, descriptor, data);
} else {
assert supertypes.size() == 1 : "Simple Enum entry must have one supertype";
jsEnumEntryCreation = new ClassInitializerTranslator(enumEntry, data).generateEnumEntryInstanceCreation(supertypes.iterator().next());
}
Named named = new Named() {
@NotNull
@Override
public Name getName() {
String name = enumEntry.getName();
assert name != null : "Enum entry name must be not null";
return Name.identifier(name);
}
};
enumEntryList.add(new JsPropertyInitializer(data.nameToLiteral(named), jsEnumEntryCreation));
return null;
}
@Override
public Void visitClassObject(
JetClassObject classObject, TranslationContext context
@@ -65,8 +101,7 @@ public class DeclarationBodyVisitor extends TranslatorVisitor<Void> {
ClassDescriptor descriptor = getClassDescriptor(context.bindingContext(), declaration);
JsExpression value = ClassTranslator.generateClassCreation(declaration, descriptor, context);
JsFunction fun = new JsFunction(context.getScopeForDescriptor(descriptor.getContainingDeclaration()));
fun.setBody(new JsBlock(new JsReturn(value)));
JsFunction fun = TranslationUtils.simpleReturnFunction(context.getScopeForDescriptor(descriptor), value);
staticResult.add(createPropertyInitializer(Namer.getNamedForClassObjectInitializer(), fun, context));
return null;
}
@@ -27,16 +27,21 @@ import org.jetbrains.jet.lang.psi.JetClassOrObject;
import org.jetbrains.jet.lang.psi.JetDelegationSpecifier;
import org.jetbrains.jet.lang.psi.JetDelegatorToSuperCall;
import org.jetbrains.jet.lang.psi.JetParameter;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lexer.JetTokens;
import org.jetbrains.k2js.translate.context.Namer;
import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.general.AbstractTranslator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getClassDescriptorForType;
import static org.jetbrains.k2js.translate.utils.BindingUtils.*;
import static org.jetbrains.k2js.translate.utils.JsAstUtils.convertToStatement;
import static org.jetbrains.k2js.translate.utils.PsiUtils.getPrimaryConstructorParameters;
import static org.jetbrains.k2js.translate.utils.TranslationUtils.getQualifiedReference;
import static org.jetbrains.k2js.translate.utils.TranslationUtils.translateArgumentList;
public final class ClassInitializerTranslator extends AbstractTranslator {
@@ -75,18 +80,38 @@ public final class ClassInitializerTranslator extends AbstractTranslator {
return result;
}
@NotNull
public JsExpression generateEnumEntryInstanceCreation(@NotNull JetType enumClassType) {
JetDelegatorToSuperCall superCall = getSuperCall();
List<JsExpression> arguments;
if (superCall != null) {
arguments = translateArguments(superCall);
} else {
arguments = Collections.emptyList();
}
JsNameRef reference = getQualifiedReference(context(), getClassDescriptorForType(enumClassType));
if(context().isEcma5()) {
return new JsInvocation(reference, arguments);
} else {
return new JsNew(reference, arguments);
}
}
private void mayBeAddCallToSuperMethod(JsFunction initializer) {
if (classDeclaration.hasModifier(JetTokens.ENUM_KEYWORD)) {
addCallToSuperMethod(Collections.<JsExpression>emptyList(), initializer);
return;
}
if (hasAncestorClass(bindingContext(), classDeclaration)) {
JetDelegatorToSuperCall superCall = getSuperCall();
if (superCall == null) {
return;
}
addCallToSuperMethod(superCall, initializer);
addCallToSuperMethod(translateArguments(superCall), initializer);
}
}
private void addCallToSuperMethod(@NotNull JetDelegatorToSuperCall superCall, JsFunction initializer) {
List<JsExpression> arguments = translateArguments(superCall);
private void addCallToSuperMethod(@NotNull List<JsExpression> arguments, JsFunction initializer) {
if (context().isEcma5()) {
JsName ref = context().scope().declareName(Namer.CALLEE_NAME);
initializer.setName(ref);
@@ -59,7 +59,7 @@ public final class JsDescriptorUtils {
@Nullable
public static ClassDescriptor findAncestorClass(@NotNull List<ClassDescriptor> superclassDescriptors) {
for (ClassDescriptor descriptor : superclassDescriptors) {
if (descriptor.getKind() == ClassKind.CLASS) {
if (descriptor.getKind() == ClassKind.CLASS || descriptor.getKind() == ClassKind.ENUM_CLASS) {
return descriptor;
}
}
@@ -56,6 +56,11 @@ public final class TranslationUtils {
}
}
@NotNull
public static JsFunction simpleReturnFunction(@NotNull JsScope functionScope, @NotNull JsExpression returnExpression) {
return new JsFunction(functionScope, new JsBlock(new JsReturn(returnExpression)));
}
@NotNull
private static JsPropertyInitializer translateExtensionFunctionAsEcma5DataDescriptor(@NotNull JsFunction function,
@NotNull FunctionDescriptor descriptor, @NotNull TranslationContext context) {
@@ -0,0 +1,43 @@
package foo
enum class B(open val bar: Int) {
val x = 1
var y = 12;
a : B(0) {
override val bar = 3
{
y = 0
}
}
b : B(4){}
c : B(5)
}
trait X {
val foo: Int
fun bar(): Int {
return foo;
}
}
enum class Y(override val foo: Int) : X {
m:Y(3)
n:Y(6)
}
fun box(): String {
if (B.a.x != 1) return "B.a.x != 1"
if (B.a.y != 0) return "B.a.y != 0"
if (B.a.bar != 3) return "B.a.bar != 3"
if (B.b.bar != 4) return "B.b.bar != 4"
if (B.b.y != 12) return "B.b.y != 12"
if (B.c.bar != 5) return "B.c.bar != 5"
if (B.c.y != 12) return "B.c.y != 12"
if (Y.m.bar() != 3) return "Y.m.bar() != 3"
if (Y.n.bar() != 6) return "Y.n.bar() != 6"
return "OK"
}
@@ -0,0 +1,47 @@
package foo
enum class EmptyEnum
enum class Simple {
OK
}
enum class A {
a : A() {}
b : A()
c
}
enum class B {
c
}
fun box(): String {
if (Simple.OK.name() != "OK") return "Simple.OK.name() != OK"
val ok = Simple.OK
if (ok.ordinal() != 0) return "ok = Simple.Ok; ok.ordinal() != 0"
val ok2 = Simple.valueOf("OK")
if(!ok2.equals(ok)) return "ok2 not equal ok"
if(!ok2.identityEquals(ok)) return "ok2 not identity equal ok"
if (EmptyEnum.values().size != 0) return "EmptyEnum.values().size != 0"
if (A.values() != array(A.a, A.b, A.c)) return "Wrong A.values(): " + A.values().toString()
if (A.c.toString() != "c") return "A.c.toString() != c"
if (A.valueOf("b") != A.b) return "A.valueOf('b') != A.b"
if (A.a == A.b) return "A.a == A.b"
if (A.a.name() != "a") return "A.a.name() != a"
if (A.b.name() != "b") return "A.b.name() != b"
if (A.c.name() != "c") return "A.c.name() != c"
if (A.a.ordinal() != 0) return "A.a.ordinal() != 0"
if (A.b.ordinal() != 1) return "A.b.ordinal() != 1"
if (A.c.ordinal() != 2) return "A.c.ordinal() != 2"
if (A.c.equals(B.c)) return "A.c.equals(B.c)"
return "OK"
}
+43 -1
View File
@@ -45,7 +45,7 @@ String.prototype.contains = function (s) {
return obj1 === obj2;
};
Kotlin.toString = function (o) {
if (o == null) {
return "null";
@@ -130,6 +130,48 @@ String.prototype.contains = function (s) {
Kotlin.Collection = Kotlin.$createClass();
Kotlin.Enum = Kotlin.$createClass(null, {
initialize: function () {
this.name$ = undefined;
this.ordinal$ = undefined;
},
name: function () {
return this.name$;
},
ordinal: function () {
return this.ordinal$;
},
toString: function () {
return this.name();
}
});
(function (){
function valueOf(name) {
return this[name];
}
function getValues() {
return this.values$;
}
Kotlin.createEnumEntries = function(enumEntryList) {
var i = 0;
var values = [];
for (var entryName in enumEntryList) {
if (enumEntryList.hasOwnProperty(entryName)) {
var entryObject = enumEntryList[entryName];
values[i] = entryObject;
entryObject.ordinal$ = i;
entryObject.name$ = entryName;
i++;
}
}
enumEntryList.values$ = values;
enumEntryList.valueOf = valueOf;
enumEntryList.values = getValues;
return enumEntryList;
};
})();
Kotlin.AbstractCollection = Kotlin.$createClass(Kotlin.Collection, {
size: function () {
return this.$size;