diff --git a/.idea/dictionaries/max.xml b/.idea/dictionaries/max.xml index 59b317d1177..d8e764b26e2 100644 --- a/.idea/dictionaries/max.xml +++ b/.idea/dictionaries/max.xml @@ -1,6 +1,7 @@ + classfiles codegen diff --git a/idea/src/org/jetbrains/jet/codegen/ClassCodegen.java b/idea/src/org/jetbrains/jet/codegen/ClassCodegen.java index fef9847f70f..16ee7fd3222 100644 --- a/idea/src/org/jetbrains/jet/codegen/ClassCodegen.java +++ b/idea/src/org/jetbrains/jet/codegen/ClassCodegen.java @@ -1,6 +1,8 @@ package org.jetbrains.jet.codegen; import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElement; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.types.ClassDescriptor; @@ -9,7 +11,10 @@ import org.jetbrains.jet.lang.types.JetType; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.Opcodes; +import java.util.ArrayList; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Set; /** * @author max @@ -26,37 +31,78 @@ public class ClassCodegen { } public void generate(JetClass aClass) { + generateInterface(aClass); + } + + private void generateInterface(JetClass aClass) { ClassDescriptor descriptor = bindingContext.getClassDescriptor(aClass); - String fqName = CodeGenUtil.getInternalImplementationName(descriptor); - List delegationSpecifiers = aClass.getDelegationSpecifiers(); - for (JetDelegationSpecifier specifier : delegationSpecifiers) { - JetType superType = bindingContext.resolveTypeReference(specifier.getTypeReference()); - String superClassFQN = CodeGenUtil.getInternalInterfaceName((ClassDescriptor) superType.getConstructor().getDeclarationDescriptor()); - } + Set superInterfaces = getSuperInterfaces(aClass); - ClassVisitor v = factory.forClassImplementation(descriptor); + String fqName = CodeGenUtil.getInternalInterfaceName(descriptor); + ClassVisitor v = factory.forClassInterface(descriptor); v.visit(Opcodes.V1_6, - Opcodes.ACC_PUBLIC, + Opcodes.ACC_PUBLIC | Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT, fqName.replace('.', '/'), null, - //"jet/lang/Namespace", "java/lang/Object", - new String[0] - ); + superInterfaces.toArray(new String[superInterfaces.size()]) + ); + generateClassBody(aClass, v); + + v.visitEnd(); + } + + private Set getSuperInterfaces(JetClass aClass) { + List delegationSpecifiers = aClass.getDelegationSpecifiers(); + String superClassName = null; + Set superInterfaces = new LinkedHashSet(); + for (JetDelegationSpecifier specifier : delegationSpecifiers) { + JetType superType = bindingContext.resolveTypeReference(specifier.getTypeReference()); + ClassDescriptor superClassDescriptor = (ClassDescriptor) superType.getConstructor().getDeclarationDescriptor(); + PsiElement superPsi = bindingContext.getDeclarationPsiElement(superClassDescriptor); + + if (superPsi instanceof PsiClass) { + PsiClass psiClass = (PsiClass) superPsi; + String fqn = psiClass.getQualifiedName(); + if (psiClass.isInterface()) { + superInterfaces.add(fqn); + } + else { + if (superClassName == null) { + superClassName = fqn.replace('.', '/'); + + while (psiClass != null) { + for (PsiClass ifs : psiClass.getInterfaces()) { + superInterfaces.add(ifs.getQualifiedName().replace('.', '/')); + } + psiClass = psiClass.getSuperClass(); + } + } + else { + throw new RuntimeException("Cannot determine single class to inherit from"); + } + } + } + else { + superInterfaces.add(CodeGenUtil.getInternalInterfaceName(superClassDescriptor)); + } + } + return superInterfaces; + } + + private void generateClassBody(JetClass aClass, ClassVisitor v) { final PropertyCodegen propertyCodegen = new PropertyCodegen(v); final FunctionCodegen functionCodegen = new FunctionCodegen(v, JetStandardLibrary.getJetStandardLibrary(project), bindingContext); for (JetDeclaration declaration : aClass.getDeclarations()) { if (declaration instanceof JetProperty) { - propertyCodegen.genInInterface((JetProperty) declaration); + propertyCodegen.gen((JetProperty) declaration, OwnerKind.INTERFACE); } else if (declaration instanceof JetFunction) { - functionCodegen.genInInterface((JetFunction) declaration); + functionCodegen.gen((JetFunction) declaration, OwnerKind.INTERFACE); } } - - v.visitEnd(); } } diff --git a/idea/src/org/jetbrains/jet/codegen/Codegens.java b/idea/src/org/jetbrains/jet/codegen/Codegens.java index acb6b5545d7..44b6cdf3a4f 100644 --- a/idea/src/org/jetbrains/jet/codegen/Codegens.java +++ b/idea/src/org/jetbrains/jet/codegen/Codegens.java @@ -45,7 +45,7 @@ public class Codegens { } public ClassVisitor forClassInterface(ClassDescriptor aClass) { - return newVisitor(CodeGenUtil.getInternalInterfaceName(aClass)); + return newVisitor(CodeGenUtil.getInternalInterfaceName(aClass) + ".class"); } public ClassCodegen forClass(BindingContext bindingContext) { @@ -53,11 +53,11 @@ public class Codegens { } public ClassVisitor forClassImplementation(ClassDescriptor aClass) { - return newVisitor(CodeGenUtil.getInternalImplementationName(aClass)); + return newVisitor(CodeGenUtil.getInternalImplementationName(aClass) + ".class"); } public ClassVisitor forClassDelegatingImplementation(ClassDescriptor aClass) { - return newVisitor(CodeGenUtil.getInternalDelegatingImplementationName(aClass)); + return newVisitor(CodeGenUtil.getInternalDelegatingImplementationName(aClass) + ".class"); } public NamespaceCodegen forNamespace(JetNamespace namespace) { @@ -65,7 +65,7 @@ public class Codegens { String fqName = namespace.getFQName(); NamespaceCodegen codegen = ns2codegen.get(fqName); if (codegen == null) { - codegen = new NamespaceCodegen(project, newVisitor(NamespaceCodegen.getJVMClassName(fqName) + ".class"), fqName); + codegen = new NamespaceCodegen(project, newVisitor(NamespaceCodegen.getJVMClassName(fqName) + ".class"), fqName, this); ns2codegen.put(fqName, codegen); } diff --git a/idea/src/org/jetbrains/jet/codegen/FunctionCodegen.java b/idea/src/org/jetbrains/jet/codegen/FunctionCodegen.java index 87b040a1020..5fb6ab67194 100644 --- a/idea/src/org/jetbrains/jet/codegen/FunctionCodegen.java +++ b/idea/src/org/jetbrains/jet/codegen/FunctionCodegen.java @@ -43,7 +43,7 @@ public class FunctionCodegen { } - private void gen(JetFunction f, OwnerKind kind) { + public void gen(JetFunction f, OwnerKind kind) { int flags = Opcodes.ACC_PUBLIC; // TODO. boolean isStatic = kind == OwnerKind.NAMESPACE; diff --git a/idea/src/org/jetbrains/jet/codegen/NamespaceCodegen.java b/idea/src/org/jetbrains/jet/codegen/NamespaceCodegen.java index 2706bf3e288..407ff9f02c2 100644 --- a/idea/src/org/jetbrains/jet/codegen/NamespaceCodegen.java +++ b/idea/src/org/jetbrains/jet/codegen/NamespaceCodegen.java @@ -2,10 +2,7 @@ package org.jetbrains.jet.codegen; import com.intellij.openapi.project.Project; import org.jetbrains.jet.lang.ErrorHandler; -import org.jetbrains.jet.lang.psi.JetDeclaration; -import org.jetbrains.jet.lang.psi.JetFunction; -import org.jetbrains.jet.lang.psi.JetNamespace; -import org.jetbrains.jet.lang.psi.JetProperty; +import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.AnalyzingUtils; import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.types.JetStandardLibrary; @@ -18,10 +15,12 @@ import org.objectweb.asm.Opcodes; public class NamespaceCodegen { private final Project project; private final ClassVisitor v; + private final Codegens codegens; - public NamespaceCodegen(Project project, ClassVisitor v, String fqName) { + public NamespaceCodegen(Project project, ClassVisitor v, String fqName, Codegens codegens) { this.project = project; this.v = v; + this.codegens = codegens; v.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC, @@ -38,6 +37,7 @@ public class NamespaceCodegen { final PropertyCodegen propertyCodegen = new PropertyCodegen(v); final FunctionCodegen functionCodegen = new FunctionCodegen(v, JetStandardLibrary.getJetStandardLibrary(project), bindingContext); + final ClassCodegen classCodegen = codegens.forClass(bindingContext); for (JetDeclaration declaration : namespace.getDeclarations()) { if (declaration instanceof JetProperty) { @@ -46,6 +46,9 @@ public class NamespaceCodegen { else if (declaration instanceof JetFunction) { functionCodegen.genInNamespace((JetFunction) declaration); } + else if (declaration instanceof JetClass) { + classCodegen.generate((JetClass) declaration); + } } } diff --git a/idea/testData/codegen/inheritingFromArrayList.jet b/idea/testData/codegen/inheritingFromArrayList.jet new file mode 100644 index 00000000000..8caa9823b36 --- /dev/null +++ b/idea/testData/codegen/inheritingFromArrayList.jet @@ -0,0 +1 @@ +class Foo : java.util.ArrayList \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/codegen/ClassGenTest.java b/idea/tests/org/jetbrains/jet/codegen/ClassGenTest.java index 37939f133cf..7cebefb5923 100644 --- a/idea/tests/org/jetbrains/jet/codegen/ClassGenTest.java +++ b/idea/tests/org/jetbrains/jet/codegen/ClassGenTest.java @@ -1,6 +1,7 @@ package org.jetbrains.jet.codegen; import java.lang.reflect.Method; +import java.util.List; /** * @author yole @@ -8,8 +9,25 @@ import java.lang.reflect.Method; public class ClassGenTest extends CodegenTestCase { public void testPSVMClass() throws Exception { loadFile("simpleClass.jet"); - final Class aClass = generateClass(); + System.out.println(generateToText()); + + final Class aClass = loadClass("SimpleClass", generateClassesInFile()); final Method[] methods = aClass.getDeclaredMethods(); assertEquals(1, methods.length); } + + public void testArrayListInheritance() throws Exception { + loadFile("inheritingFromArrayList.jet"); + System.out.println(generateToText()); + + final Class aClass = loadClass("Foo", generateClassesInFile()); + checkInterface(aClass, List.class); + } + + private void checkInterface(Class aClass, Class ifs) { + for (Class anInterface : aClass.getInterfaces()) { + if (anInterface == ifs) return; + } + fail(aClass.getName() + " must have " + ifs.getName() + " in its interfaces"); + } } diff --git a/idea/tests/org/jetbrains/jet/codegen/CodegenTestCase.java b/idea/tests/org/jetbrains/jet/codegen/CodegenTestCase.java index e58c918912b..b069e9b4240 100644 --- a/idea/tests/org/jetbrains/jet/codegen/CodegenTestCase.java +++ b/idea/tests/org/jetbrains/jet/codegen/CodegenTestCase.java @@ -33,52 +33,47 @@ public abstract class CodegenTestCase extends LightCodeInsightFixtureTestCase { NamespaceCodegen codegen = state.forNamespace(namespace); codegen.generate(namespace); - List files = state.files(); - assertEquals("This test only supposed to generate single class file", 1, files.size()); + StringBuilder answer = new StringBuilder(); - return state.asText(files.get(0)); + List files = state.files(); + for (String file : files) { + answer.append("@").append(file).append('\n'); + answer.append(state.asText(file)); + } + + return answer.toString(); } private Class generateNamespaceClass() { + JetFile jetFile = (JetFile) myFixture.getFile(); + final JetNamespace namespace = jetFile.getRootNamespace(); + String fqName = NamespaceCodegen.getJVMClassName(namespace.getFQName()); + Codegens state = generateClassesInFile(); + return loadClass(fqName, state); + } + + protected Class loadClass(String fqName, Codegens state) { + List files = state.files(); + for (String file : files) { + if (file.equals(fqName.replace('.', '/') + ".class")) { + final byte[] data = state.asBytes(file); + MyClassLoader classLoader = new MyClassLoader(NamespaceGenTest.class.getClassLoader()); + return classLoader.doDefineClass(fqName, data); + } + } + + fail("No classfile was generated for: " + fqName); + return null; + } + + protected Codegens generateClassesInFile() { Codegens state = new Codegens(getProject(), false); JetFile jetFile = (JetFile) myFixture.getFile(); final JetNamespace namespace = jetFile.getRootNamespace(); NamespaceCodegen codegen = state.forNamespace(namespace); codegen.generate(namespace); - - return getJVMClass(state, NamespaceCodegen.getJVMClassName(namespace.getFQName()).replace("/", ".")); - } - - protected Class generateClass() { - final Codegens state = new Codegens(getProject(), false); - JetFile jetFile = (JetFile) myFixture.getFile(); - final Ref result = new Ref(); - final ClassCodegen codegen = state.forClass(AnalyzingUtils.analyzeFile(jetFile, ErrorHandler.THROW_EXCEPTION)); - jetFile.acceptChildren(new JetVisitor() { - @Override - public void visitJetElement(JetElement elem) { - elem.acceptChildren(this); - } - - @Override - public void visitClass(JetClass klass) { - codegen.generate(klass); - result.set(getJVMClass(state, klass.getName() + "$$Impl")); - } - }); - if (result.isNull()) { - throw new IllegalArgumentException("found no classes in the file to generate"); - } - return result.get(); - } - - private static Class getJVMClass(Codegens state, final String fqName) { - List files = state.files(); - assertEquals("This test only supposed to generate single class file", 1, files.size()); - final byte[] data = state.asBytes(files.get(0)); - MyClassLoader classLoader = new MyClassLoader(NamespaceGenTest.class.getClassLoader()); - return classLoader.doDefineClass(fqName, data); + return state; } protected Method generateFunction() {