diff --git a/idea/src/org/jetbrains/jet/codegen/ClassBodyCodegen.java b/idea/src/org/jetbrains/jet/codegen/ClassBodyCodegen.java index ed0735204e8..58a6fa5d7d4 100644 --- a/idea/src/org/jetbrains/jet/codegen/ClassBodyCodegen.java +++ b/idea/src/org/jetbrains/jet/codegen/ClassBodyCodegen.java @@ -4,8 +4,11 @@ import org.jetbrains.jet.lang.descriptors.ClassDescriptor; import org.jetbrains.jet.lang.descriptors.PropertyDescriptor; import org.jetbrains.jet.lang.psi.*; import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; +import org.objectweb.asm.commons.InstructionAdapter; +import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -22,6 +25,8 @@ public abstract class ClassBodyCodegen { protected final ClassVisitor v; protected final ClassContext context; + protected final List staticInitializerChunks = new ArrayList(); + public ClassBodyCodegen(JetClassOrObject aClass, ClassContext context, ClassVisitor v, GenerationState state) { this.state = state; descriptor = state.getBindingContext().getClassDescriptor(aClass); @@ -38,6 +43,8 @@ public abstract class ClassBodyCodegen { generateClassBody(); + generateStaticInitializer(); + v.visitEnd(); } @@ -95,4 +102,23 @@ public abstract class ClassBodyCodegen { } return Collections.emptyList(); } + + private void generateStaticInitializer() { + if (staticInitializerChunks.size() > 0) { + final MethodVisitor mv = v.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, + "", "()V", null, null); + mv.visitCode(); + + InstructionAdapter v = new InstructionAdapter(mv); + + for (CodeChunk chunk : staticInitializerChunks) { + chunk.generate(v); + } + + mv.visitInsn(Opcodes.RETURN); + mv.visitMaxs(0, 0); + + mv.visitEnd(); + } + } } diff --git a/idea/src/org/jetbrains/jet/codegen/ClassCodegen.java b/idea/src/org/jetbrains/jet/codegen/ClassCodegen.java index 2ba069000ab..07dee13f7da 100644 --- a/idea/src/org/jetbrains/jet/codegen/ClassCodegen.java +++ b/idea/src/org/jetbrains/jet/codegen/ClassCodegen.java @@ -25,13 +25,15 @@ public class ClassCodegen { else { generateInterface(parentContext, aClass); generateImplementation(parentContext, aClass, OwnerKind.IMPLEMENTATION); - generateImplementation(parentContext, aClass, OwnerKind.DELEGATING_IMPLEMENTATION); + if (!ImplementationBodyCodegen.isEnum(aClass)) { + generateImplementation(parentContext, aClass, OwnerKind.DELEGATING_IMPLEMENTATION); + } } ClassDescriptor descriptor = state.getBindingContext().getClassDescriptor(aClass); final ClassContext contextForInners = parentContext.intoClass(descriptor, OwnerKind.IMPLEMENTATION); for (JetDeclaration declaration : aClass.getDeclarations()) { - if (declaration instanceof JetClass) { + if (declaration instanceof JetClass && !(declaration instanceof JetEnumEntry)) { generate(contextForInners, (JetClass) declaration); } } diff --git a/idea/src/org/jetbrains/jet/codegen/CodeChunk.java b/idea/src/org/jetbrains/jet/codegen/CodeChunk.java new file mode 100644 index 00000000000..eb0aa420884 --- /dev/null +++ b/idea/src/org/jetbrains/jet/codegen/CodeChunk.java @@ -0,0 +1,10 @@ +package org.jetbrains.jet.codegen; + +import org.objectweb.asm.commons.InstructionAdapter; + +/** + * @author yole + */ +public interface CodeChunk { + void generate(InstructionAdapter v); +} diff --git a/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java b/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java index 9eac2586d1c..2f7d11653f7 100644 --- a/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java +++ b/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java @@ -896,11 +896,11 @@ public class ExpressionCodegen extends JetVisitor { return funDescriptor; } - private void invokeMethodWithArguments(CallableMethod callableMethod, JetCallExpression expression) { + public void invokeMethodWithArguments(CallableMethod callableMethod, JetCall expression) { invokeMethodWithArguments(callableMethod, expression, false); } - private void invokeMethodWithArguments(CallableMethod callableMethod, JetCallExpression expression, final boolean haveReceiver) { + private void invokeMethodWithArguments(CallableMethod callableMethod, JetCall expression, final boolean haveReceiver) { if (callableMethod.isOwnerFromCall()) { setOwnerFromCall(callableMethod, expression); } @@ -914,7 +914,7 @@ public class ExpressionCodegen extends JetVisitor { callableMethod.invoke(v); } - private void setOwnerFromCall(CallableMethod callableMethod, JetCallExpression expression) { + private void setOwnerFromCall(CallableMethod callableMethod, JetCall expression) { if (expression.getParent() instanceof JetQualifiedExpression) { final JetExpression receiver = ((JetQualifiedExpression) expression.getParent()).getReceiverExpression(); JetType expressionType = bindingContext.getExpressionType(receiver); @@ -926,7 +926,7 @@ public class ExpressionCodegen extends JetVisitor { } } - private JetExpression getReceiverForSelector(JetElement expression) { + private static JetExpression getReceiverForSelector(PsiElement expression) { if (expression.getParent() instanceof JetDotQualifiedExpression && !isReceiver(expression)) { final JetDotQualifiedExpression parent = (JetDotQualifiedExpression) expression.getParent(); return parent.getReceiverExpression(); @@ -934,7 +934,7 @@ public class ExpressionCodegen extends JetVisitor { return null; } - private void ensureReceiverOnStack(JetElement expression, @Nullable ClassDescriptor calleeContainingClass) { + private void ensureReceiverOnStack(PsiElement expression, @Nullable ClassDescriptor calleeContainingClass) { JetExpression receiver = getReceiverForSelector(expression); if (receiver != null) { if (!resolvesToClassOrPackage(receiver)) { @@ -1013,7 +1013,7 @@ public class ExpressionCodegen extends JetVisitor { } } - private static boolean isReceiver(JetElement expression) { + private static boolean isReceiver(PsiElement expression) { final PsiElement parent = expression.getParent(); if (parent instanceof JetQualifiedExpression) { final JetExpression receiverExpression = ((JetQualifiedExpression) parent).getReceiverExpression(); diff --git a/idea/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java b/idea/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java index f013f20e2d9..6cfa5fdf0d8 100644 --- a/idea/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java +++ b/idea/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java @@ -74,20 +74,9 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { @Override protected void generateSyntheticParts() { - int typeinfoStatic = descriptor.getTypeConstructor().getParameters().size() > 0 ? 0 : Opcodes.ACC_STATIC; - v.visitField(Opcodes.ACC_PRIVATE | typeinfoStatic, "$typeInfo", "Ljet/typeinfo/TypeInfo;", null, null); - - if (isNonLiteralObject()) { - Type type = JetTypeMapper.jetImplementationType(descriptor); - v.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "$instance", type.getDescriptor(), null, null); - } - final JetClassObject classObject = getClassObject(); - if (classObject != null) { - Type type = Type.getObjectType(state.getTypeMapper().jvmName(classObject)); - v.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "$classobj", type.getDescriptor(), null, null); - } - - generateStaticInitializer(); + generateFieldForTypeInfo(); + generateFieldForObjectInstance(); + generateFieldForClassObject(); try { generatePrimaryConstructor(); @@ -99,13 +88,69 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { generateGetTypeInfo(); } + private void generateFieldForTypeInfo() { + final boolean typeInfoIsStatic = descriptor.getTypeConstructor().getParameters().size() == 0; + v.visitField(Opcodes.ACC_PRIVATE | (typeInfoIsStatic ? Opcodes.ACC_STATIC : 0), "$typeInfo", + "Ljet/typeinfo/TypeInfo;", null, null); + if (typeInfoIsStatic) { + staticInitializerChunks.add(new CodeChunk() { + @Override + public void generate(InstructionAdapter v) { + JetTypeMapper typeMapper = state.getTypeMapper(); + ClassCodegen.newTypeInfo(v, false, typeMapper.jvmType(descriptor, OwnerKind.INTERFACE)); + v.putstatic(typeMapper.jvmName(descriptor, kind), "$typeInfo", "Ljet/typeinfo/TypeInfo;"); + } + }); + } + } + + private void generateFieldForObjectInstance() { + if (isNonLiteralObject()) { + Type type = JetTypeMapper.jetImplementationType(descriptor); + v.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "$instance", type.getDescriptor(), null, null); + + staticInitializerChunks.add(new CodeChunk() { + @Override + public void generate(InstructionAdapter v) { + String name = jvmName(); + v.anew(Type.getObjectType(name)); + v.dup(); + v.invokespecial(name, "", "()V"); + v.putstatic(name, "$instance", JetTypeMapper.jetImplementationType(descriptor).getDescriptor()); + } + }); + + } + } + + private void generateFieldForClassObject() { + final JetClassObject classObject = getClassObject(); + if (classObject != null) { + Type type = Type.getObjectType(state.getTypeMapper().jvmName(classObject)); + v.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "$classobj", type.getDescriptor(), null, null); + + staticInitializerChunks.add(new CodeChunk() { + @Override + public void generate(InstructionAdapter v) { + String name = state.getTypeMapper().jvmName(classObject); + final Type classObjectType = Type.getObjectType(name); + v.anew(classObjectType); + v.dup(); + v.invokespecial(name, "", "()V"); + v.putstatic(state.getTypeMapper().jvmName(descriptor, OwnerKind.IMPLEMENTATION), "$classobj", + classObjectType.getDescriptor()); + } + }); + } + } + protected void generatePrimaryConstructor() { ConstructorDescriptor constructorDescriptor = state.getBindingContext().getConstructorDescriptor((JetElement) myClass); - if (constructorDescriptor == null && !(myClass instanceof JetObjectDeclaration)) return; + if (constructorDescriptor == null && !(myClass instanceof JetObjectDeclaration) && !isEnum(myClass)) return; Method method; CallableMethod callableMethod; - if (myClass instanceof JetObjectDeclaration) { + if (constructorDescriptor == null) { method = new Method("", Type.VOID_TYPE, new Type[0]); callableMethod = new CallableMethod("", method, Opcodes.INVOKESPECIAL, Collections.emptyList()); } @@ -233,6 +278,11 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { mv.visitEnd(); } + static boolean isEnum(JetClassOrObject myClass) { + final JetModifierList modifierList = myClass.getModifierList(); + return modifierList != null && modifierList.hasModifier(JetTokens.ENUM_KEYWORD); + } + @Nullable private ClassDescriptor getOuterClassDescriptor() { if (myClass.getParent() instanceof JetClassObject) { @@ -280,13 +330,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { } CallableMethod method = state.getTypeMapper().mapToCallableMethod(constructorDescriptor, kind); - List args = constructorCall.getValueArguments(); - for (int i = 0, argsSize = args.size(); i < argsSize; i++) { - JetArgument arg = args.get(i); - codegen.gen(arg.getArgumentExpression(), method.getValueParameterTypes().get(i)); - } - - method.invoke(iv); + codegen.invokeMethodWithArguments(method, constructorCall); } @Override @@ -409,47 +453,6 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { } } - private void generateStaticInitializer() { - boolean needTypeInfo = descriptor.getTypeConstructor().getParameters().size() == 0; - boolean needInstance = isNonLiteralObject(); - JetClassObject classObject = getClassObject(); - if (!needTypeInfo && !needInstance && classObject == null) { - return; - } - final MethodVisitor mv = v.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, - "", "()V", null, null); - mv.visitCode(); - - InstructionAdapter v = new InstructionAdapter(mv); - - if (needTypeInfo) { - JetTypeMapper typeMapper = state.getTypeMapper(); - ClassCodegen.newTypeInfo(v, false, typeMapper.jvmType(descriptor, OwnerKind.INTERFACE)); - v.putstatic(typeMapper.jvmName(descriptor, kind), "$typeInfo", "Ljet/typeinfo/TypeInfo;"); - } - if (needInstance) { - String name = jvmName(); - v.anew(Type.getObjectType(name)); - v.dup(); - v.invokespecial(name, "", "()V"); - v.putstatic(name, "$instance", JetTypeMapper.jetImplementationType(descriptor).getDescriptor()); - } - if (classObject != null) { - String name = state.getTypeMapper().jvmName(classObject); - final Type classObjectType = Type.getObjectType(name); - v.anew(classObjectType); - v.dup(); - v.invokespecial(name, "", "()V"); - v.putstatic(state.getTypeMapper().jvmName(descriptor, OwnerKind.IMPLEMENTATION), "$classobj", - classObjectType.getDescriptor()); - } - - mv.visitInsn(Opcodes.RETURN); - mv.visitMaxs(0, 0); - - mv.visitEnd(); - } - @Nullable private JetClassObject getClassObject() { return myClass instanceof JetClass ? ((JetClass) myClass).getClassObject() : null; diff --git a/idea/src/org/jetbrains/jet/codegen/InterfaceBodyCodegen.java b/idea/src/org/jetbrains/jet/codegen/InterfaceBodyCodegen.java index 2c7fb25aa0b..52f31470129 100644 --- a/idea/src/org/jetbrains/jet/codegen/InterfaceBodyCodegen.java +++ b/idea/src/org/jetbrains/jet/codegen/InterfaceBodyCodegen.java @@ -3,13 +3,16 @@ package org.jetbrains.jet.codegen; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; import org.jetbrains.jet.lang.descriptors.ClassDescriptor; -import org.jetbrains.jet.lang.psi.JetClassOrObject; -import org.jetbrains.jet.lang.psi.JetDelegationSpecifier; +import org.jetbrains.jet.lang.descriptors.ConstructorDescriptor; +import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.types.JetType; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.InstructionAdapter; +import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; @@ -18,6 +21,8 @@ import java.util.Set; * @author yole */ public class InterfaceBodyCodegen extends ClassBodyCodegen { + private final List myEnumConstants = new ArrayList(); + public InterfaceBodyCodegen(JetClassOrObject aClass, ClassContext context, ClassVisitor v, GenerationState state) { super(aClass, context, v, state); assert context.getContextKind() == OwnerKind.INTERFACE; @@ -73,4 +78,59 @@ public class InterfaceBodyCodegen extends ClassBodyCodegen { } return superInterfaces; } + + @Override + protected void generateDeclaration(PropertyCodegen propertyCodegen, JetDeclaration declaration, FunctionCodegen functionCodegen) { + if (declaration instanceof JetEnumEntry && !((JetEnumEntry) declaration).hasPrimaryConstructor()) { + String name = declaration.getName(); + final String desc = "L" + state.getTypeMapper().jvmName(descriptor, OwnerKind.INTERFACE) + ";"; + v.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL, name, desc, null, null); + if (myEnumConstants.isEmpty()) { + staticInitializerChunks.add(new CodeChunk() { + @Override + public void generate(InstructionAdapter v) { + initializeEnumConstants(v); + } + }); + } + myEnumConstants.add((JetEnumEntry) declaration); + } + else { + super.generateDeclaration(propertyCodegen, declaration, functionCodegen); + } + } + + private void initializeEnumConstants(InstructionAdapter v) { + ExpressionCodegen codegen = new ExpressionCodegen(v, new FrameMap(), Type.VOID_TYPE, context, state); + for (JetEnumEntry enumConstant : myEnumConstants) { + // TODO type and constructor parameters + String intfClass = state.getTypeMapper().jvmName(descriptor, OwnerKind.INTERFACE); + String implClass = state.getTypeMapper().jvmName(descriptor, OwnerKind.IMPLEMENTATION); + + final List delegationSpecifiers = enumConstant.getDelegationSpecifiers(); + if (delegationSpecifiers.size() > 1) { + throw new UnsupportedOperationException("multiple delegation specifiers for enum constant not supported"); + } + + v.anew(Type.getObjectType(implClass)); + v.dup(); + + if (delegationSpecifiers.size() == 1) { + final JetDelegationSpecifier specifier = delegationSpecifiers.get(0); + if (specifier instanceof JetDelegatorToSuperCall) { + final JetDelegatorToSuperCall superCall = (JetDelegatorToSuperCall) specifier; + ConstructorDescriptor constructorDescriptor = state.getBindingContext().resolveSuperConstructor(superCall); + CallableMethod method = state.getTypeMapper().mapToCallableMethod(constructorDescriptor, OwnerKind.IMPLEMENTATION); + codegen.invokeMethodWithArguments(method, superCall); + } + else { + throw new UnsupportedOperationException("unsupported type of enum constant initializer: " + specifier); + } + } + else { + v.invokespecial(implClass, "", "()V"); + } + v.putstatic(intfClass, enumConstant.getName(), "L" + intfClass + ";"); + } + } } diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetCall.java b/idea/src/org/jetbrains/jet/lang/psi/JetCall.java index 66169083301..937ef578915 100644 --- a/idea/src/org/jetbrains/jet/lang/psi/JetCall.java +++ b/idea/src/org/jetbrains/jet/lang/psi/JetCall.java @@ -1,5 +1,6 @@ package org.jetbrains.jet.lang.psi; +import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -8,7 +9,7 @@ import java.util.List; /** * @author abreslav */ -public interface JetCall { +public interface JetCall extends PsiElement { @Nullable JetArgumentList getValueArgumentList(); diff --git a/idea/tests/org/jetbrains/jet/codegen/ClassGenTest.java b/idea/tests/org/jetbrains/jet/codegen/ClassGenTest.java index b051e854244..8800d0aae20 100644 --- a/idea/tests/org/jetbrains/jet/codegen/ClassGenTest.java +++ b/idea/tests/org/jetbrains/jet/codegen/ClassGenTest.java @@ -1,5 +1,6 @@ package org.jetbrains.jet.codegen; +import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.List; @@ -130,4 +131,21 @@ public class ClassGenTest extends CodegenTestCase { Object result = method.invoke(null); assertInstanceOf(result, Runnable.class); } + + public void testEnumClass() throws Exception { + loadText("enum class Direction { NORTH; SOUTH; EAST; WEST }"); + final Class direction = loadAllClasses(generateClassesInFile()).get("Direction"); + final Field north = direction.getField("NORTH"); + assertEquals(direction, north.getType()); + assertInstanceOf(north.get(null), direction); + } + + public void testEnumConstantConstructors() throws Exception { + loadText("enum class Color(val rgb: Int) { RED: Color(0xFF0000); GREEN: Color(0x00FF00); }"); + final Class colorClass = loadAllClasses(generateClassesInFile()).get("Color"); + final Field redField = colorClass.getField("RED"); + final Object redValue = redField.get(null); + final Method rgbMethod = colorClass.getMethod("getRgb"); + assertEquals(0xFF0000, rgbMethod.invoke(redValue)); + } }