Multiple classfiles generated by test supported, starting work on interfaces list for jet classes

This commit is contained in:
Maxim Shafirov
2011-04-19 22:07:00 +04:00
parent d9c08e38da
commit 7dec1ce492
8 changed files with 126 additions and 62 deletions
+1
View File
@@ -1,6 +1,7 @@
<component name="ProjectDictionaryState">
<dictionary name="max">
<words>
<w>classfiles</w>
<w>codegen</w>
</words>
</dictionary>
@@ -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<JetDelegationSpecifier> delegationSpecifiers = aClass.getDelegationSpecifiers();
for (JetDelegationSpecifier specifier : delegationSpecifiers) {
JetType superType = bindingContext.resolveTypeReference(specifier.getTypeReference());
String superClassFQN = CodeGenUtil.getInternalInterfaceName((ClassDescriptor) superType.getConstructor().getDeclarationDescriptor());
}
Set<String> 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<String> getSuperInterfaces(JetClass aClass) {
List<JetDelegationSpecifier> delegationSpecifiers = aClass.getDelegationSpecifiers();
String superClassName = null;
Set<String> superInterfaces = new LinkedHashSet<String>();
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();
}
}
@@ -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);
}
@@ -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;
@@ -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);
}
}
}
@@ -0,0 +1 @@
class Foo : java.util.ArrayList
@@ -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");
}
}
@@ -33,52 +33,47 @@ public abstract class CodegenTestCase extends LightCodeInsightFixtureTestCase {
NamespaceCodegen codegen = state.forNamespace(namespace);
codegen.generate(namespace);
List<String> 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<String> 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<String> 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<Class> result = new Ref<Class>();
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<String> 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() {