prepare for multiple classfiles per class generation.

This commit is contained in:
Maxim Shafirov
2011-04-11 01:13:22 +04:00
parent fc2d29b85b
commit 54050f9de5
9 changed files with 220 additions and 74 deletions
@@ -0,0 +1,65 @@
package org.jetbrains.jet.codegen;
import com.intellij.openapi.project.Project;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.types.ClassDescriptor;
import org.jetbrains.jet.lang.types.JetStandardLibrary;
import org.jetbrains.jet.lang.types.JetType;
import org.objectweb.asm.ClassVisitor;
import java.util.List;
/**
* @author max
*/
public class ClassCodegen {
private final Project project;
private final ClassVisitor v;
private final BindingContext bindingContext;
public ClassCodegen(Project project, Codegens v, JetClass jetClass, BindingContext bindingContext) {
this.project = project;
this.v = null;
this.bindingContext = bindingContext;
ClassDescriptor descriptor = bindingContext.getClassDescriptor(jetClass);
String fqName = CodeGenUtil.getInternalInterfaceName(descriptor);
List<JetDelegationSpecifier> delegationSpecifiers = jetClass.getDelegationSpecifiers();
for (JetDelegationSpecifier specifier : delegationSpecifiers) {
JetType superType = bindingContext.resolveTypeReference(specifier.getTypeReference());
String superClassFQN = CodeGenUtil.getInternalInterfaceName((ClassDescriptor) superType.getConstructor().getDeclarationDescriptor());
}
//descriptor.
/*
v.visit(Opcodes.V1_6,
Opcodes.ACC_PUBLIC,
fqName.replace('.', '/'),
null,
//"jet/lang/Namespace",
"java/lang/Object",
new String[0]
);
*/
}
public void generate(JetNamespace namespace) {
final PropertyCodegen propertyCodegen = new PropertyCodegen(v);
final FunctionCodegen functionCodegen = new FunctionCodegen(v, JetStandardLibrary.getJetStandardLibrary(project), bindingContext);
for (JetDeclaration declaration : namespace.getDeclarations()) {
if (declaration instanceof JetProperty) {
propertyCodegen.gen((JetProperty) declaration, namespace);
}
else if (declaration instanceof JetFunction) {
functionCodegen.gen((JetFunction) declaration, namespace);
}
}
}
public ClassVisitor getVisitor() {
return v;
}
}
@@ -1,17 +0,0 @@
package org.jetbrains.jet.codegen;
import org.jetbrains.jet.lang.psi.JetNamespace;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.util.TraceClassVisitor;
import java.io.PrintWriter;
import java.io.StringWriter;
/**
* @author max
*/
public class ClassVisitorFactory {
public ClassVisitor visitorForClassIn(JetNamespace namespace) {
return new TraceClassVisitor(new PrintWriter(new StringWriter()));
}
}
@@ -0,0 +1,21 @@
package org.jetbrains.jet.codegen;
import org.jetbrains.jet.lang.types.ClassDescriptor;
import org.jetbrains.jet.resolve.DescriptorUtil;
/**
* @author max
*/
public class CodeGenUtil {
public static String getInternalInterfaceName(ClassDescriptor descriptor) {
return DescriptorUtil.getFQName(descriptor).replace('.', '/');
}
public static String getInternalImplementationName(ClassDescriptor descriptor) {
return getInternalInterfaceName(descriptor) + "$$Impl";
}
public static String getInternalDelegatingImplementationName(ClassDescriptor descriptor) {
return getInternalInterfaceName(descriptor) + "$$DImpl";
}
}
@@ -0,0 +1,91 @@
package org.jetbrains.jet.codegen;
import com.intellij.openapi.project.Project;
import org.jetbrains.jet.lang.psi.JetNamespace;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.util.TraceClassVisitor;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author max
*/
public class Codegens {
private final Project project;
private final boolean isText;
private final Map<String, NamespaceCodegen> ns2codegen = new HashMap<String, NamespaceCodegen>();
private final Map<String, ClassVisitor> generators = new HashMap<String, ClassVisitor>();
private boolean isDone = false;
public Codegens(Project project, boolean text) {
this.project = project;
isText = text;
}
public ClassVisitor newVisitor(String filePath) {
ClassVisitor visitor;
if (isText) {
visitor = new TraceClassVisitor(new PrintWriter(new StringWriter()));
}
else {
visitor = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
}
generators.put(filePath, visitor);
return visitor;
}
public NamespaceCodegen forNamespace(JetNamespace namespace) {
assert !isDone : "Already done!";
String fqName = namespace.getFQName();
NamespaceCodegen codegen = ns2codegen.get(fqName);
if (codegen == null) {
codegen = new NamespaceCodegen(project, newVisitor(NamespaceCodegen.getJVMClassName(fqName) + ".class"), fqName);
ns2codegen.put(fqName, codegen);
}
return codegen;
}
private void done() {
if (!isDone) {
isDone = true;
for (NamespaceCodegen codegen : ns2codegen.values()) {
codegen.done();
}
}
}
public String asText(String file) {
assert isText : "Need to create with text=true";
done();
TraceClassVisitor visitor = (TraceClassVisitor) generators.get(file);
StringWriter writer = new StringWriter();
visitor.print(new PrintWriter(writer));
return writer.toString();
}
public byte[] asBytes(String file) {
assert !isText : "This is debug stuff, only produces texts.";
done();
ClassWriter visitor = (ClassWriter) generators.get(file);
return visitor.toByteArray();
}
public List<String> files() {
done();
return new ArrayList<String>(generators.keySet());
}
}
@@ -28,6 +28,14 @@ public class FunctionCodegen {
}
public void gen(JetFunction f, JetNamespace owner) {
gen(f);
}
public void gen(JetFunction f, JetClass owner) {
}
private void gen(JetFunction f) {
final List<JetParameter> parameters = f.getValueParameters();
Type[] parameterTypes = new Type[parameters.size()];
for (int i = 0; i < parameters.size(); i++) {
@@ -83,8 +91,4 @@ public class FunctionCodegen {
}
return false;
}
public void gen(JetFunction f, JetClass owner) {
}
}
@@ -83,18 +83,9 @@ public class JetTypeMapper {
}
if (descriptor instanceof ClassDescriptor) {
return Type.getObjectType(getFQName(descriptor).replace('.', '/'));
return Type.getObjectType(CodeGenUtil.getInternalInterfaceName((ClassDescriptor) descriptor));
}
throw new UnsupportedOperationException("Unknown type " + jetType);
}
private static String getFQName(DeclarationDescriptor descriptor) {
DeclarationDescriptor container = descriptor.getContainingDeclaration();
if (container != null && !(container instanceof ModuleDescriptor)) {
return getFQName(container) + "." + descriptor.getName();
}
return descriptor.getName();
}
}
@@ -12,15 +12,16 @@ import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.util.Chunk;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.codegen.Codegens;
import org.jetbrains.jet.codegen.NamespaceCodegen;
import org.jetbrains.jet.lang.JetFileType;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.psi.JetNamespace;
import org.objectweb.asm.ClassWriter;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
@@ -64,7 +65,7 @@ public class JetCompiler implements TranslatingCompiler {
}
private static class ModuleCompileState {
private final Map<String, NamespaceCodegen> ns2codegen = new HashMap<String, NamespaceCodegen>();
private final Codegens codegens;
private final CompileContext compileContext;
private final Module module;
private final OutputSink outputSink;
@@ -73,6 +74,7 @@ public class JetCompiler implements TranslatingCompiler {
this.compileContext = compileContext;
this.module = module;
this.outputSink = outputSink;
codegens = new Codegens(compileContext.getProject(), false);
}
public void compile(final VirtualFile virtualFile) {
@@ -82,13 +84,7 @@ public class JetCompiler implements TranslatingCompiler {
PsiFile psiFile = PsiManager.getInstance(module.getProject()).findFile(virtualFile);
if (psiFile instanceof JetFile) {
final JetNamespace namespace = ((JetFile) psiFile).getRootNamespace();
String fqName = namespace.getFQName();
NamespaceCodegen codegen = ns2codegen.get(fqName);
if (codegen == null) {
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
codegen = new NamespaceCodegen(compileContext.getProject(), writer, fqName);
ns2codegen.put(fqName, codegen);
}
NamespaceCodegen codegen = codegens.forNamespace(namespace);
codegen.generate(namespace);
}
}
@@ -97,28 +93,15 @@ public class JetCompiler implements TranslatingCompiler {
public void done() {
VirtualFile outputDir = compileContext.getModuleOutputDirectory(module);
for (Map.Entry<String, NamespaceCodegen> entry : ns2codegen.entrySet()) {
NamespaceCodegen codegen = entry.getValue();
codegen.done();
List<String> files = codegens.files();
for (String file : files) {
File target = new File(outputDir.getPath(), file);
try {
File nsOutputDir = dirForPackage(outputDir, entry.getKey());
ClassWriter writer = (ClassWriter) codegen.getVisitor();
File output = new File(nsOutputDir, "namespace.class");
FileUtil.writeToFile(output, writer.toByteArray());
FileUtil.writeToFile(target, codegens.asBytes(file));
} catch (IOException e) {
compileContext.addMessage(CompilerMessageCategory.ERROR, e.getMessage(), null, 0, 0);
}
}
}
private static File dirForPackage(VirtualFile root, String fqName) throws IOException {
File result = new File(root.getPath(), fqName.replace(".", "/"));
if (!result.exists() && !result.mkdirs()) {
throw new IOException("Failed to create directory for package");
}
return result;
}
}
}
@@ -125,4 +125,13 @@ public class DescriptorUtil {
}
}
}
public static String getFQName(DeclarationDescriptor descriptor) {
DeclarationDescriptor container = descriptor.getContainingDeclaration();
if (container != null && !(container instanceof ModuleDescriptor)) {
return getFQName(container) + "." + descriptor.getName();
}
return descriptor.getName();
}
}
@@ -8,13 +8,10 @@ import org.jetbrains.jet.lang.JetFileType;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.psi.JetNamespace;
import org.jetbrains.jet.parsing.JetParsingTest;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.util.TraceClassVisitor;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
/**
* @author yole
@@ -497,29 +494,31 @@ public class NamespaceGenTest extends LightCodeInsightFixtureTestCase {
}
private String generateToText() {
StringWriter writer = new StringWriter();
Codegens state = new Codegens(getProject(), true);
JetFile jetFile = (JetFile) myFixture.getFile();
JetNamespace namespace = jetFile.getRootNamespace();
NamespaceCodegen codegen = new NamespaceCodegen(getProject(),
new TraceClassVisitor(new PrintWriter(writer)),
namespace.getFQName());
NamespaceCodegen codegen = state.forNamespace(namespace);
codegen.generate(namespace);
codegen.done();
return writer.toString();
List<String> files = state.files();
assertEquals("This test only supposed to generate single class file", 1, files.size());
return state.asText(files.get(0));
}
private Class generateToClass() {
Codegens state = new Codegens(getProject(), false);
JetFile jetFile = (JetFile) myFixture.getFile();
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
final JetNamespace namespace = jetFile.getRootNamespace();
NamespaceCodegen codegen = new NamespaceCodegen(getProject(),
writer,
namespace.getFQName());
NamespaceCodegen codegen = state.forNamespace(namespace);
codegen.generate(namespace);
final byte[] data = writer.toByteArray();
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());
final Class aClass = classLoader.doDefineClass(NamespaceCodegen.getJVMClassName(namespace.getFQName()).replace("/", "."), data);
return aClass;
return classLoader.doDefineClass(NamespaceCodegen.getJVMClassName(namespace.getFQName()).replace("/", "."), data);
}
private Method generateFunction() {