From 9e4567ffc39ba65d781856a561f20eb8dc63ac39 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Sun, 29 May 2011 18:16:58 +0400 Subject: [PATCH] initial, barely working implementation of inner classes --- .../jetbrains/jet/codegen/ClassCodegen.java | 39 ++++++++++++++----- .../jet/codegen/ExpressionCodegen.java | 34 ++++++++++++---- .../jetbrains/jet/codegen/JetTypeMapper.java | 13 +++++++ idea/testData/codegen/classes/innerClass.jet | 19 +++++++++ .../jetbrains/jet/codegen/ClassGenTest.java | 4 ++ .../jet/codegen/CodegenTestCase.java | 10 ++++- 6 files changed, 99 insertions(+), 20 deletions(-) create mode 100644 idea/testData/codegen/classes/innerClass.jet diff --git a/idea/src/org/jetbrains/jet/codegen/ClassCodegen.java b/idea/src/org/jetbrains/jet/codegen/ClassCodegen.java index 0ca6b3a33dc..b739270eee1 100644 --- a/idea/src/org/jetbrains/jet/codegen/ClassCodegen.java +++ b/idea/src/org/jetbrains/jet/codegen/ClassCodegen.java @@ -42,6 +42,12 @@ public class ClassCodegen { generateInterface(aClass); generateImplementation(aClass, OwnerKind.IMPLEMENTATION); generateImplementation(aClass, OwnerKind.DELEGATING_IMPLEMENTATION); + + for (JetDeclaration declaration : aClass.getDeclarations()) { + if (declaration instanceof JetClass) { + generate((JetClass) declaration); + } + } } private void generateInterface(JetClass aClass) { @@ -70,12 +76,13 @@ public class ClassCodegen { String superClass = getSuperClass(aClass, kind); ClassVisitor v = kind == OwnerKind.IMPLEMENTATION ? factory.forClassImplementation(descriptor) : factory.forClassDelegatingImplementation(descriptor); + final String defaultInterfaceName = JetTypeMapper.jvmNameForInterface(descriptor); v.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC, JetTypeMapper.jetJvmName(descriptor, kind), null, superClass, - new String[] { "jet/JetObject", JetTypeMapper.jvmNameForInterface(descriptor) } + new String[] { "jet/JetObject", defaultInterfaceName} ); int typeinfoStatic = descriptor.getTypeConstructor().getParameters().size() > 0 ? 0 : Opcodes.ACC_STATIC; @@ -180,9 +187,29 @@ public class ClassCodegen { String classname = typeMapper.jvmName(classDescriptor, kind); final Type classType = Type.getType("L" + classname + ";"); + List specifiers = aClass.getDelegationSpecifiers(); + + if (specifiers.isEmpty() || !(specifiers.get(0) instanceof JetDelegatorToSuperCall)) { + String superClass = getSuperClass(aClass, kind); + iv.load(0, Type.getType("L" + superClass + ";")); + iv.invokespecial(superClass, "", /* TODO super constructor descriptor */"()V"); + } + + int index = 0; + for (ClassDescriptor outerClassDescriptor : JetTypeMapper.getOuterClassDescriptors(classDescriptor)) { + final Type type = JetTypeMapper.jetInterfaceType(outerClassDescriptor); + String interfaceDesc = type.getDescriptor(); + final String fieldName = "this$" + index; + v.visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL, fieldName, interfaceDesc, null, null); + iv.load(0, classType); + iv.load(index + 1, type); + iv.putfield(classname, fieldName, interfaceDesc); + frameMap.enterTemp(); + } + if (kind == OwnerKind.DELEGATING_IMPLEMENTATION) { String interfaceDesc = JetTypeMapper.jetInterfaceType(classDescriptor).getDescriptor(); - v.visitField(Opcodes.ACC_PRIVATE, "$this", interfaceDesc, /*TODO*/null, null); + v.visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL, "$this", interfaceDesc, /*TODO*/null, null); iv.load(1, argTypes[0]); iv.putfield(classname, "$this", interfaceDesc); frameMap.enterTemp(); @@ -204,14 +231,6 @@ public class ClassCodegen { } } - List specifiers = aClass.getDelegationSpecifiers(); - - if (specifiers.isEmpty() || !(specifiers.get(0) instanceof JetDelegatorToSuperCall)) { - String superClass = getSuperClass(aClass, kind); - iv.load(0, Type.getType("L" + superClass + ";")); - iv.invokespecial(superClass, "", /* TODO super constructor descriptor */"()V"); - } - HashSet overriden = new HashSet(); for (JetDeclaration declaration : aClass.getDeclarations()) { if (declaration instanceof JetFunction) { diff --git a/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java b/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java index 6d72385558c..0fb9ca4fa47 100644 --- a/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java +++ b/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java @@ -561,7 +561,7 @@ public class ExpressionCodegen extends JetVisitor { final Type fieldType = psiTypeToAsm(psiField.getType()); final boolean isStatic = psiField.hasModifierProperty(PsiModifier.STATIC); if (!isStatic) { - ensureReceiverOnStack(expression); + ensureReceiverOnStack(expression, null); } myStack.push(StackValue.field(fieldType, owner, psiField.getName(), isStatic)); } @@ -591,7 +591,7 @@ public class ExpressionCodegen extends JetVisitor { } if (isClass(container, "Array") && propertyDescriptor.getName().equals("size")) { - ensureReceiverOnStack(expression); + ensureReceiverOnStack(expression, null); v.arraylength(); myStack.push(StackValue.onStack(Type.INT_TYPE)); } @@ -600,7 +600,7 @@ public class ExpressionCodegen extends JetVisitor { final boolean directToField = expression.getReferencedNameElementType() == JetTokens.FIELD_IDENTIFIER; final StackValue iValue = intermediateValueForProperty(propertyDescriptor, directToField); if (!isStatic) { - ensureReceiverOnStack(expression); + ensureReceiverOnStack(expression, container instanceof ClassDescriptor ? (ClassDescriptor) container : null); } myStack.push(iValue); } @@ -709,7 +709,7 @@ public class ExpressionCodegen extends JetVisitor { final boolean isStatic = psiMethod.hasModifierProperty(PsiModifier.STATIC); if (!isStatic) { - ensureReceiverOnStack(expression); + ensureReceiverOnStack(expression, null); if (expression.getParent() instanceof JetQualifiedExpression) { final JetExpression receiver = ((JetQualifiedExpression) expression.getParent()).getReceiverExpression(); owner = expressionType(receiver).getInternalName(); @@ -727,14 +727,14 @@ public class ExpressionCodegen extends JetVisitor { methodDescriptor = typeMapper.mapSignature(jetFunction); if (functionParent instanceof NamespaceDescriptorImpl) { if (jetFunction.getReceiverTypeRef() != null) { - ensureReceiverOnStack(expression); + ensureReceiverOnStack(expression, null); } pushMethodArguments(expression, methodDescriptor); final String owner = NamespaceCodegen.getJVMClassName(DescriptorRenderer.getFQName(functionParent)); v.invokestatic(owner, methodDescriptor.getName(), methodDescriptor.getDescriptor()); } else if (functionParent instanceof ClassDescriptor) { - ensureReceiverOnStack(expression); + ensureReceiverOnStack(expression, (ClassDescriptor) functionParent); pushMethodArguments(expression, methodDescriptor); final String owner = JetTypeMapper.jvmNameForInterface((ClassDescriptor) functionParent); v.invokeinterface(owner, methodDescriptor.getName(), methodDescriptor.getDescriptor()); @@ -756,7 +756,7 @@ public class ExpressionCodegen extends JetVisitor { } } - private void ensureReceiverOnStack(JetElement expression) { + private void ensureReceiverOnStack(JetElement expression, @Nullable ClassDescriptor calleeContainingClass) { if (expression.getParent() instanceof JetDotQualifiedExpression && !isReceiver(expression)) { final JetDotQualifiedExpression parent = (JetDotQualifiedExpression) expression.getParent(); if (!resolvesToClassOrPackage(parent.getReceiverExpression())) { @@ -769,6 +769,12 @@ public class ExpressionCodegen extends JetVisitor { } else if (!(expression.getParent() instanceof JetSafeQualifiedExpression)) { v.load(0, JetTypeMapper.TYPE_OBJECT); // TODO hope it works; really need more checks here :) + if (calleeContainingClass != null && contextType instanceof ClassDescriptor && + calleeContainingClass == contextType.getContainingDeclaration()) { + v.getfield(typeMapper.jvmName((ClassDescriptor) contextType, OwnerKind.IMPLEMENTATION), + "this$0", typeMapper.jvmType(calleeContainingClass, OwnerKind.INTERFACE).getDescriptor()); + // TODO handle more levels of class nestng + } } } @@ -1314,9 +1320,14 @@ public class ExpressionCodegen extends JetVisitor { generateNewArray(expression, type); } else { + ClassDescriptor classDecl = (ClassDescriptor) constructorDescriptor.getContainingDeclaration(); + v.anew(type); v.dup(); + // TODO typechecker must verify that we're the outer class of the instance being created + pushOuterClassArguments(classDecl); + Method method = typeMapper.mapConstructorSignature((ConstructorDescriptor) constructorDescriptor, OwnerKind.IMPLEMENTATION); pushMethodArguments(expression, method); @@ -1326,7 +1337,6 @@ public class ExpressionCodegen extends JetVisitor { ClassCodegen.newTypeInfo(v, typeMapper.mapType(TypeUtils.makeNullable(typeArgument))); } - ClassDescriptor classDecl = (ClassDescriptor) constructorDescriptor.getContainingDeclaration(); v.invokespecial(JetTypeMapper.jvmNameForImplementation(classDecl), "", method.getDescriptor()); } } @@ -1336,6 +1346,14 @@ public class ExpressionCodegen extends JetVisitor { myStack.push(StackValue.onStack(type)); } + private void pushOuterClassArguments(ClassDescriptor classDecl) { + final List outerClassDescriptors = JetTypeMapper.getOuterClassDescriptors(classDecl); + if (outerClassDescriptors.size() > 0) { + v.load(0, JetTypeMapper.jetImplementationType(classDecl)); + } + // TODO push further outer classes + } + private Type generateJavaConstructorCall(JetNewExpression expression, PsiMethod constructor) { PsiClass javaClass = constructor.getContainingClass(); Type type = JetTypeMapper.psiClassType(javaClass); diff --git a/idea/src/org/jetbrains/jet/codegen/JetTypeMapper.java b/idea/src/org/jetbrains/jet/codegen/JetTypeMapper.java index db398232569..38f8f828a31 100644 --- a/idea/src/org/jetbrains/jet/codegen/JetTypeMapper.java +++ b/idea/src/org/jetbrains/jet/codegen/JetTypeMapper.java @@ -253,6 +253,9 @@ public class JetTypeMapper { List parameters = descriptor.getUnsubstitutedValueParameters(); List parameterTypes = new ArrayList(); ClassDescriptor classDescriptor = descriptor.getContainingDeclaration(); + for (ClassDescriptor outerDescriptor : getOuterClassDescriptors(classDescriptor)) { + parameterTypes.add(jvmType(outerDescriptor, OwnerKind.IMPLEMENTATION)); + } if (delegate) { parameterTypes.add(jetInterfaceType(classDescriptor)); } @@ -268,6 +271,16 @@ public class JetTypeMapper { return new Method("", Type.VOID_TYPE, parameterTypes.toArray(new Type[parameterTypes.size()])); } + public static List getOuterClassDescriptors(ClassDescriptor classDescriptor) { + List result = new ArrayList(); + DeclarationDescriptor outerClass = classDescriptor.getContainingDeclaration(); + while (outerClass instanceof ClassDescriptor) { + result.add((ClassDescriptor) outerClass); + outerClass = outerClass.getContainingDeclaration(); + } + return result; + } + static int getAccessModifiers(JetDeclaration p, int defaultFlags) { int flags = 0; if (p.hasModifier(JetTokens.PUBLIC_KEYWORD)) { diff --git a/idea/testData/codegen/classes/innerClass.jet b/idea/testData/codegen/classes/innerClass.jet new file mode 100644 index 00000000000..52adef612a7 --- /dev/null +++ b/idea/testData/codegen/classes/innerClass.jet @@ -0,0 +1,19 @@ +class Outer(val foo: StringBuilder) { + class Inner() { + fun len() { + return foo.length() + } + } + + fun test() { + return new Inner() + } +} + +fun box(): String { + val sb = new StringBuilder("xyzzy") + val o = new Outer(sb) + val i = o.test() + val l = i.len() + return if (l != 5) "fail" else "OK" +} diff --git a/idea/tests/org/jetbrains/jet/codegen/ClassGenTest.java b/idea/tests/org/jetbrains/jet/codegen/ClassGenTest.java index e856c5e7ea0..25932d776f9 100644 --- a/idea/tests/org/jetbrains/jet/codegen/ClassGenTest.java +++ b/idea/tests/org/jetbrains/jet/codegen/ClassGenTest.java @@ -58,4 +58,8 @@ public class ClassGenTest extends CodegenTestCase { final Integer returnValue = (Integer) method.invoke(null); assertEquals(610, returnValue.intValue()); } + + public void testInnerClass() throws Exception { + blackBoxFile("classes/innerClass.jet"); + } } diff --git a/idea/tests/org/jetbrains/jet/codegen/CodegenTestCase.java b/idea/tests/org/jetbrains/jet/codegen/CodegenTestCase.java index 10aeafe3fb4..a697ead1946 100644 --- a/idea/tests/org/jetbrains/jet/codegen/CodegenTestCase.java +++ b/idea/tests/org/jetbrains/jet/codegen/CodegenTestCase.java @@ -65,8 +65,14 @@ public abstract class CodegenTestCase extends LightCodeInsightFixtureTestCase { protected void blackBoxFile(String filename) throws Exception { loadFile(filename); - //System.out.println(generateToText()); - assertEquals("OK", blackBox()); + String actual; + try { + actual = blackBox(); + } catch (Exception e) { + System.out.println(generateToText()); + throw e; + } + assertEquals("OK", actual); } protected String blackBox() throws Exception {