Jet files can be compiled from within IDEA

This commit is contained in:
Dmitry Jemerov
2011-03-24 17:59:53 +01:00
parent af4c777197
commit 3b30500055
5 changed files with 176 additions and 18 deletions
+3 -2
View File
@@ -5,11 +5,12 @@
<vendor>JetBrains</vendor>
<application-components>
<!-- Add your application components here -->
</application-components>
<project-components>
<!-- Add your project components here -->
<component>
<implementation-class>org.jetbrains.jet.compiler.JetCompilerManager</implementation-class>
</component>
</project-components>
<actions>
@@ -16,22 +16,28 @@ import org.objectweb.asm.Opcodes;
* @author max
*/
public class NamespaceCodegen {
public NamespaceCodegen() {
}
private final Project project;
private final ClassVisitor v;
public void generate(JetNamespace namespace, ClassVisitor v, Project project) {
BindingContext bindingContext = AnalyzingUtils.analyzeNamespace(namespace, ErrorHandler.THROW_EXCEPTION);
public NamespaceCodegen(Project project, ClassVisitor v, String fqName) {
this.project = project;
this.v = v;
final PropertyCodegen propertyCodegen = new PropertyCodegen(v);
final FunctionCodegen functionCodegen = new FunctionCodegen(v, JetStandardLibrary.getJetStandardLibrary(project), bindingContext);
v.visit(Opcodes.V1_6,
Opcodes.ACC_PUBLIC,
getJVMClassName(namespace),
getJVMClassName(fqName),
null,
//"jet/lang/Namespace",
"java/lang/Object",
new String[0]
);
}
public void generate(JetNamespace namespace) {
BindingContext bindingContext = AnalyzingUtils.analyzeNamespace(namespace, ErrorHandler.THROW_EXCEPTION);
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) {
@@ -41,12 +47,17 @@ public class NamespaceCodegen {
functionCodegen.gen((JetFunction) declaration, namespace);
}
}
}
public void done() {
v.visitEnd();
}
public static String getJVMClassName(JetNamespace namespace) {
return namespace.getFQName().replace('.', '/') + "/namespace";
public static String getJVMClassName(String fqName) {
return fqName.replace('.', '/') + "/namespace";
}
public ClassVisitor getVisitor() {
return v;
}
}
@@ -0,0 +1,124 @@
package org.jetbrains.jet.compiler;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.compiler.CompileContext;
import com.intellij.openapi.compiler.CompileScope;
import com.intellij.openapi.compiler.CompilerMessageCategory;
import com.intellij.openapi.compiler.TranslatingCompiler;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
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.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.Map;
/**
* @author yole
*/
public class JetCompiler implements TranslatingCompiler {
@Override
public boolean isCompilableFile(VirtualFile virtualFile, CompileContext compileContext) {
return virtualFile.getFileType() instanceof JetFileType;
}
@NotNull
@Override
public String getDescription() {
return "Jet Language Compiler";
}
@Override
public boolean validateConfiguration(CompileScope compileScope) {
return true;
}
@Override
public void compile(CompileContext compileContext, Chunk<Module> moduleChunk, VirtualFile[] virtualFiles, OutputSink outputSink) {
Map<Module, ModuleCompileState> moduleMap = new HashMap<Module, ModuleCompileState>();
for (VirtualFile virtualFile : virtualFiles) {
Module module = compileContext.getModuleByFile(virtualFile);
ModuleCompileState state = moduleMap.get(module);
if (state == null) {
state = new ModuleCompileState(compileContext, module, outputSink);
moduleMap.put(module, state);
}
state.compile(virtualFile);
}
for (ModuleCompileState state : moduleMap.values()) {
state.done();
}
}
private static class ModuleCompileState {
private final Map<String, NamespaceCodegen> ns2codegen = new HashMap<String, NamespaceCodegen>();
private final CompileContext compileContext;
private final Module module;
private final OutputSink outputSink;
public ModuleCompileState(CompileContext compileContext, Module module, OutputSink outputSink) {
this.compileContext = compileContext;
this.module = module;
this.outputSink = outputSink;
}
public void compile(final VirtualFile virtualFile) {
ApplicationManager.getApplication().runReadAction(new Runnable() {
@Override
public void run() {
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);
}
codegen.generate(namespace);
}
}
});
}
public void done() {
VirtualFile outputDir = compileContext.getModuleOutputDirectory(module);
for (Map.Entry<String, NamespaceCodegen> entry : ns2codegen.entrySet()) {
NamespaceCodegen codegen = entry.getValue();
codegen.done();
try {
File nsOutputDir = dirForPackage(outputDir, entry.getKey());
ClassWriter writer = (ClassWriter) codegen.getVisitor();
File output = new File(nsOutputDir, "namespace.class");
FileUtil.writeToFile(output, writer.toByteArray());
} 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.mkdirs()) {
throw new IOException("Failed to create directory for package");
}
return result;
}
}
}
@@ -0,0 +1,19 @@
package org.jetbrains.jet.compiler;
import com.intellij.openapi.compiler.CompilerManager;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.StdFileTypes;
import org.jetbrains.jet.lang.JetFileType;
import java.util.Collections;
/**
* @author yole
*/
public class JetCompilerManager {
public JetCompilerManager(CompilerManager manager) {
manager.addTranslatingCompiler(new JetCompiler(),
Collections.<FileType>singleton(JetFileType.INSTANCE),
Collections.singleton(StdFileTypes.CLASS));
}
}
@@ -18,12 +18,9 @@ import java.lang.reflect.Method;
* @author yole
*/
public class NamespaceGenTest extends LightCodeInsightFixtureTestCase {
private NamespaceCodegen codegen;
@Override
protected void setUp() throws Exception {
super.setUp();
codegen = new NamespaceCodegen();
}
public void testPSVM() throws Exception {
@@ -114,7 +111,10 @@ public class NamespaceGenTest extends LightCodeInsightFixtureTestCase {
StringWriter writer = new StringWriter();
JetFile jetFile = (JetFile) myFixture.getFile();
JetNamespace namespace = jetFile.getRootNamespace();
codegen.generate(namespace, new TraceClassVisitor(new PrintWriter(writer)), getProject());
NamespaceCodegen codegen = new NamespaceCodegen(getProject(),
new TraceClassVisitor(new PrintWriter(writer)),
namespace.getFQName());
codegen.generate(namespace);
return writer.toString();
}
@@ -122,10 +122,13 @@ public class NamespaceGenTest extends LightCodeInsightFixtureTestCase {
JetFile jetFile = (JetFile) myFixture.getFile();
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
final JetNamespace namespace = jetFile.getRootNamespace();
codegen.generate(namespace, writer, getProject());
NamespaceCodegen codegen = new NamespaceCodegen(getProject(),
writer,
namespace.getFQName());
codegen.generate(namespace);
final byte[] data = writer.toByteArray();
MyClassLoader classLoader = new MyClassLoader(NamespaceGenTest.class.getClassLoader());
final Class aClass = classLoader.doDefineClass(NamespaceCodegen.getJVMClassName(namespace).replace("/", "."), data);
final Class aClass = classLoader.doDefineClass(NamespaceCodegen.getJVMClassName(namespace.getFQName()).replace("/", "."), data);
return aClass;
}