From 4e48e527b100f4d0fed3dd1d20fe79218f2a9b14 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Thu, 7 Jul 2011 16:13:25 +0200 Subject: [PATCH] initial implementation of class objects --- .../jet/codegen/ExpressionCodegen.java | 13 +++++- .../jet/codegen/GenerationState.java | 2 +- .../codegen/ImplementationBodyCodegen.java | 45 +++++++++++++++++-- .../jetbrains/jet/codegen/JetTypeMapper.java | 11 +++++ .../org/jetbrains/jet/lang/psi/JetClass.java | 7 +++ .../jetbrains/jet/lang/psi/JetClassBody.java | 6 +++ idea/testData/codegen/classes/classObject.jet | 11 +++++ .../jetbrains/jet/codegen/ClassGenTest.java | 4 ++ 8 files changed, 93 insertions(+), 6 deletions(-) create mode 100644 idea/testData/codegen/classes/classObject.jet diff --git a/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java b/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java index fc1751cafd1..009598833df 100644 --- a/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java +++ b/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java @@ -628,7 +628,7 @@ public class ExpressionCodegen extends JetVisitor { //TODO: hack, will not need if resolve goes to right descriptor itself if (declaration instanceof JetParameter) { - if (PsiTreeUtil.getParentOfType(expression, JetDelegationSpecifier.class) != null) { + if (PsiTreeUtil.getParentOfType(expression, JetDelegationSpecifier.class) != null) { JetClass aClass = PsiTreeUtil.getParentOfType(expression, JetClass.class); ConstructorDescriptor constructorDescriptor = bindingContext.getConstructorDescriptor(aClass); List parameters = constructorDescriptor.getValueParameters(); @@ -667,6 +667,17 @@ public class ExpressionCodegen extends JetVisitor { myStack.push(iValue); } } + else if (descriptor instanceof ClassDescriptor) { + final JetClassObject classObject = ((JetClass) declaration).getClassObject(); + if (classObject == null) { + throw new UnsupportedOperationException("trying to reference a class which doesn't have a class object"); + } + final String type = typeMapper.jvmName(classObject); + myStack.push(StackValue.field(Type.getObjectType(type), + typeMapper.jvmName((ClassDescriptor) descriptor, OwnerKind.IMPLEMENTATION), + "$classobj", + true)); + } else { // receiver StackValue.local(0, JetTypeMapper.TYPE_OBJECT).put(JetTypeMapper.TYPE_OBJECT, v); diff --git a/idea/src/org/jetbrains/jet/codegen/GenerationState.java b/idea/src/org/jetbrains/jet/codegen/GenerationState.java index 7077ec4cc3b..a935133f248 100644 --- a/idea/src/org/jetbrains/jet/codegen/GenerationState.java +++ b/idea/src/org/jetbrains/jet/codegen/GenerationState.java @@ -59,7 +59,7 @@ public class GenerationState { } public ClassVisitor forClassImplementation(ClassDescriptor aClass) { - return factory.newVisitor(JetTypeMapper.jvmNameForImplementation(aClass) + ".class"); + return factory.newVisitor(typeMapper.jvmName(aClass, OwnerKind.IMPLEMENTATION) + ".class"); } public ClassVisitor forClassDelegatingImplementation(ClassDescriptor aClass) { diff --git a/idea/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java b/idea/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java index df41ee20250..fc5da7d85d3 100644 --- a/idea/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java +++ b/idea/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java @@ -2,6 +2,7 @@ package org.jetbrains.jet.codegen; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.types.JetType; @@ -73,6 +74,11 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { 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(); @@ -133,7 +139,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { iv.invokespecial(superClass, "", /* TODO super constructor descriptor */"()V"); } - final DeclarationDescriptor outerDescriptor = descriptor.getContainingDeclaration(); + final DeclarationDescriptor outerDescriptor = getOuterClassDescriptor(); if (outerDescriptor instanceof ClassDescriptor) { final ClassDescriptor outerClassDescriptor = (ClassDescriptor) outerDescriptor; final Type type = JetTypeMapper.jetImplementationType(outerClassDescriptor); @@ -220,6 +226,15 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { mv.visitEnd(); } + @Nullable + private ClassDescriptor getOuterClassDescriptor() { + if (myClass.getParent() instanceof JetClassObject) { + return null; + } + final DeclarationDescriptor outerDescriptor = descriptor.getContainingDeclaration(); + return outerDescriptor instanceof ClassDescriptor ? (ClassDescriptor) outerDescriptor : null; + } + private void generateDelegatorToConstructorCall(InstructionAdapter iv, ExpressionCodegen codegen, JetCall constructorCall, ConstructorDescriptor constructorDescriptor, boolean isJavaSuperclass, ConstructorFrameMap frameMap) { @@ -272,6 +287,9 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { if (declaration instanceof JetConstructor) { generateSecondaryConstructor((JetConstructor) declaration); } + else if (declaration instanceof JetClassObject) { + generateClassObject((JetClassObject) declaration); + } else { super.generateDeclaration(propertyCodegen, declaration, functionCodegen); } @@ -387,8 +405,8 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { private void generateStaticInitializer() { boolean needTypeInfo = descriptor.getTypeConstructor().getParameters().size() == 0; boolean needInstance = isNonLiteralObject(); - if (!needTypeInfo && !needInstance) { - // we will have a dynamic type info field + JetClassObject classObject = getClassObject(); + if (!needTypeInfo && !needInstance && classObject == null) { return; } final MethodVisitor mv = v.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, @@ -409,6 +427,15 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { 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); @@ -416,8 +443,14 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { mv.visitEnd(); } + @Nullable + private JetClassObject getClassObject() { + return myClass instanceof JetClass ? ((JetClass) myClass).getClassObject() : null; + } + private boolean isNonLiteralObject() { - return myClass instanceof JetObjectDeclaration && !((JetObjectDeclaration) myClass).isObjectLiteral(); + return myClass instanceof JetObjectDeclaration && !((JetObjectDeclaration) myClass).isObjectLiteral() && + !(myClass.getParent() instanceof JetClassObject); } private void generateGetTypeInfo() { @@ -433,4 +466,8 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { mv.visitMaxs(0, 0); mv.visitEnd(); } + + private void generateClassObject(JetClassObject declaration) { + state.forClass().generate(context, declaration.getObjectDeclaration()); + } } diff --git a/idea/src/org/jetbrains/jet/codegen/JetTypeMapper.java b/idea/src/org/jetbrains/jet/codegen/JetTypeMapper.java index 6ede21ad9ed..9cdf479a8c4 100644 --- a/idea/src/org/jetbrains/jet/codegen/JetTypeMapper.java +++ b/idea/src/org/jetbrains/jet/codegen/JetTypeMapper.java @@ -141,6 +141,12 @@ public class JetTypeMapper { return jvmName((PsiClass) declaration); } if (declaration instanceof JetObjectDeclaration && ((JetObjectDeclaration) declaration).isObjectLiteral()) { + final PsiElement parent = declaration.getParent(); + if (parent instanceof JetClassObject) { + JetClass containingClass = PsiTreeUtil.getParentOfType(parent, JetClass.class); + final ClassDescriptor containingClassDescriptor = bindingContext.getClassDescriptor(containingClass); + return jvmName(containingClassDescriptor, OwnerKind.INTERFACE) + "$$ClassObj"; + } String className = classNamesForAnonymousClasses.get(declaration); if (className == null) { throw new UnsupportedOperationException("Unexpected forward reference to anonymous class " + declaration); @@ -150,6 +156,11 @@ public class JetTypeMapper { return jetJvmName(jetClass, kind); } + public String jvmName(JetClassObject classObject) { + final ClassDescriptor descriptor = bindingContext.getClassDescriptor(classObject.getObjectDeclaration()); + return jvmName(descriptor, OwnerKind.IMPLEMENTATION); + } + public boolean isInterface(ClassDescriptor jetClass, OwnerKind kind) { PsiElement declaration = bindingContext.getDeclarationPsiElement(jetClass); if (declaration instanceof JetObjectDeclaration && ((JetObjectDeclaration) declaration).isObjectLiteral()) { diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetClass.java b/idea/src/org/jetbrains/jet/lang/psi/JetClass.java index 665acd30dae..a679f330fc8 100644 --- a/idea/src/org/jetbrains/jet/lang/psi/JetClass.java +++ b/idea/src/org/jetbrains/jet/lang/psi/JetClass.java @@ -85,6 +85,13 @@ public class JetClass extends JetTypeParameterListOwner implements JetClassOrObj return (JetObjectDeclarationName) findChildByType(JetNodeTypes.OBJECT_DECLARATION_NAME); } + @Nullable + public JetClassObject getClassObject() { + JetClassBody body = (JetClassBody) findChildByType(JetNodeTypes.CLASS_BODY); + if (body == null) return null; + return body.getClassObject(); + } + public List getProperties() { JetClassBody body = (JetClassBody) findChildByType(JetNodeTypes.CLASS_BODY); if (body == null) return Collections.emptyList(); diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetClassBody.java b/idea/src/org/jetbrains/jet/lang/psi/JetClassBody.java index a24af493a04..3278c17f679 100644 --- a/idea/src/org/jetbrains/jet/lang/psi/JetClassBody.java +++ b/idea/src/org/jetbrains/jet/lang/psi/JetClassBody.java @@ -3,6 +3,7 @@ package org.jetbrains.jet.lang.psi; import com.intellij.lang.ASTNode; import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.JetNodeTypes; import java.util.List; @@ -37,4 +38,9 @@ public class JetClassBody extends JetElement { public List getProperties() { return findChildrenByType(JetNodeTypes.PROPERTY); } + + @Nullable + public JetClassObject getClassObject() { + return (JetClassObject) findChildByType(JetNodeTypes.CLASS_OBJECT); + } } diff --git a/idea/testData/codegen/classes/classObject.jet b/idea/testData/codegen/classes/classObject.jet new file mode 100644 index 00000000000..76eff77c438 --- /dev/null +++ b/idea/testData/codegen/classes/classObject.jet @@ -0,0 +1,11 @@ +class C() { + class object { + fun create() = C() + } +} + +fun box(): String { + val c = C.create() + return if (c is C) "OK" else "fail" +} + diff --git a/idea/tests/org/jetbrains/jet/codegen/ClassGenTest.java b/idea/tests/org/jetbrains/jet/codegen/ClassGenTest.java index c68cfab8461..4dbcc712247 100644 --- a/idea/tests/org/jetbrains/jet/codegen/ClassGenTest.java +++ b/idea/tests/org/jetbrains/jet/codegen/ClassGenTest.java @@ -115,4 +115,8 @@ public class ClassGenTest extends CodegenTestCase { final Class aClass = loadAllClasses(generateClassesInFile()).get("SimpleClass$$Impl"); assertTrue((aClass.getModifiers() & Modifier.ABSTRACT) != 0); } + + public void testClassObject() throws Exception { + blackBoxFile("classes/classObject.jet"); + } }