JS backend: added enum class support.
This commit is contained in:
@@ -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());
|
||||
|
||||
+37
-2
@@ -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;
|
||||
}
|
||||
|
||||
+28
-3
@@ -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"
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user