initial, barely working implementation of inner classes

This commit is contained in:
Dmitry Jemerov
2011-05-29 18:16:58 +04:00
parent a83d6ed8f6
commit 9e4567ffc3
6 changed files with 99 additions and 20 deletions
@@ -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<JetDelegationSpecifier> 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, "<init>", /* 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<JetDelegationSpecifier> 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, "<init>", /* TODO super constructor descriptor */"()V");
}
HashSet<FunctionDescriptor> overriden = new HashSet<FunctionDescriptor>();
for (JetDeclaration declaration : aClass.getDeclarations()) {
if (declaration instanceof JetFunction) {
@@ -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), "<init>", method.getDescriptor());
}
}
@@ -1336,6 +1346,14 @@ public class ExpressionCodegen extends JetVisitor {
myStack.push(StackValue.onStack(type));
}
private void pushOuterClassArguments(ClassDescriptor classDecl) {
final List<ClassDescriptor> 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);
@@ -253,6 +253,9 @@ public class JetTypeMapper {
List<ValueParameterDescriptor> parameters = descriptor.getUnsubstitutedValueParameters();
List<Type> parameterTypes = new ArrayList<Type>();
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("<init>", Type.VOID_TYPE, parameterTypes.toArray(new Type[parameterTypes.size()]));
}
public static List<ClassDescriptor> getOuterClassDescriptors(ClassDescriptor classDescriptor) {
List<ClassDescriptor> result = new ArrayList<ClassDescriptor>();
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)) {
@@ -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"
}
@@ -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");
}
}
@@ -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 {