Merge branch 'master' of git.labs.intellij.net:jet
This commit is contained in:
Generated
+7
@@ -0,0 +1,7 @@
|
||||
<component name="ProjectDictionaryState">
|
||||
<dictionary name="max">
|
||||
<words>
|
||||
<w>codegen</w>
|
||||
</words>
|
||||
</dictionary>
|
||||
</component>
|
||||
Generated
+1
@@ -5,6 +5,7 @@
|
||||
<value>
|
||||
<option name="LINE_SEPARATOR" value=" " />
|
||||
<option name="ELSE_ON_NEW_LINE" value="true" />
|
||||
<option name="ALIGN_MULTILINE_BINARY_OPERATION" value="true" />
|
||||
<option name="ALIGN_GROUP_FIELD_DECLARATIONS" value="true" />
|
||||
<option name="IF_BRACE_FORCE" value="1" />
|
||||
<ADDITIONAL_INDENT_OPTIONS fileType="groovy">
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
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,256 @@
|
||||
package org.jetbrains.jet.codegen;
|
||||
|
||||
import gnu.trove.TObjectIntHashMap;
|
||||
import org.jetbrains.jet.lang.psi.*;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.commons.InstructionAdapter;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
|
||||
/**
|
||||
* @author max
|
||||
*/
|
||||
public class ExpressionCodegen extends JetVisitor {
|
||||
private final Stack<Label> myLoopStarts = new Stack<Label>();
|
||||
private final Stack<Label> myLoopEnds = new Stack<Label>();
|
||||
|
||||
private final InstructionAdapter v;
|
||||
private final TObjectIntHashMap<JetProperty> myVarIndex = new TObjectIntHashMap<JetProperty>();
|
||||
private int myMaxVarIndex = 0;
|
||||
|
||||
public ExpressionCodegen(MethodVisitor v) {
|
||||
this.v = new InstructionAdapter(v);
|
||||
}
|
||||
|
||||
private void gen(JetElement expr) {
|
||||
if (expr == null) throw new CompilationException();
|
||||
expr.accept(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitExpression(JetExpression expression) {
|
||||
throw new UnsupportedOperationException("Codegen for " + expression + " is not yet implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitParenthesizedExpression(JetParenthesizedExpression expression) {
|
||||
gen(expression.getExpression());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitAnnotatedExpression(JetAnnotatedExpression expression) {
|
||||
gen(expression.getBaseExpression());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitIfExpression(JetIfExpression expression) {
|
||||
gen(expression.getCondition());
|
||||
unboxBoolean();
|
||||
|
||||
JetExpression thenExpression = expression.getThen();
|
||||
JetExpression elseExpression = expression.getElse();
|
||||
|
||||
if (thenExpression == null && elseExpression == null) {
|
||||
throw new CompilationException();
|
||||
}
|
||||
|
||||
if (thenExpression == null) {
|
||||
generateSingleBranchIf(elseExpression, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (elseExpression == null) {
|
||||
generateSingleBranchIf(thenExpression, true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Label elseLabel = new Label();
|
||||
v.ifeq(elseLabel); // == 0, i.e. false
|
||||
|
||||
gen(thenExpression);
|
||||
|
||||
Label endLabel = new Label();
|
||||
v.goTo(endLabel);
|
||||
v.mark(elseLabel);
|
||||
|
||||
gen(elseExpression);
|
||||
|
||||
v.mark(endLabel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitWhileExpression(JetWhileExpression expression) {
|
||||
Label condition = new Label();
|
||||
myLoopStarts.push(condition);
|
||||
v.mark(condition);
|
||||
|
||||
Label end = new Label();
|
||||
myLoopEnds.push(end);
|
||||
|
||||
gen(expression.getCondition());
|
||||
unboxBoolean();
|
||||
v.ifeq(end);
|
||||
|
||||
genToUnit(expression.getBody());
|
||||
v.goTo(condition);
|
||||
|
||||
v.mark(end);
|
||||
myLoopEnds.pop();
|
||||
myLoopStarts.pop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitDoWhileExpression(JetDoWhileExpression expression) {
|
||||
Label condition = new Label();
|
||||
v.mark(condition);
|
||||
myLoopStarts.push(condition);
|
||||
|
||||
Label end = new Label();
|
||||
myLoopEnds.push(end);
|
||||
|
||||
genToUnit(expression.getBody());
|
||||
|
||||
gen(expression.getCondition());
|
||||
unboxBoolean();
|
||||
v.ifne(condition);
|
||||
|
||||
v.mark(end);
|
||||
|
||||
myLoopEnds.pop();
|
||||
myLoopStarts.pop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitBreakExpression(JetBreakExpression expression) {
|
||||
String labelName = expression.getLabelName();
|
||||
|
||||
Label label = labelName == null ? myLoopEnds.peek() : null; // TODO:
|
||||
|
||||
v.goTo(label);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitContinueExpression(JetContinueExpression expression) {
|
||||
String labelName = expression.getLabelName();
|
||||
|
||||
Label label = labelName == null ? myLoopStarts.peek() : null; // TODO:
|
||||
|
||||
v.goTo(label);
|
||||
}
|
||||
|
||||
private void unboxBoolean() {
|
||||
v.invokevirtual("java/lang/Boolean", "booleanValue", "()Z");
|
||||
}
|
||||
|
||||
private void generateSingleBranchIf(JetExpression expression, boolean inverse) {
|
||||
Label endLabel = new Label();
|
||||
|
||||
if (inverse) {
|
||||
v.ifeq(endLabel);
|
||||
}
|
||||
else {
|
||||
v.ifne(endLabel);
|
||||
}
|
||||
|
||||
genToUnit(expression);
|
||||
|
||||
v.mark(endLabel);
|
||||
}
|
||||
|
||||
private void genToUnit(JetExpression expression) {
|
||||
gen(expression);
|
||||
|
||||
if (!isUnitType(expression)) {
|
||||
v.pop();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitConstantExpression(JetConstantExpression expression) {
|
||||
Object value = expression.getValue();
|
||||
v.aconst(value);
|
||||
|
||||
if (value instanceof Integer) {
|
||||
v.invokestatic("java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;");
|
||||
}
|
||||
else if (value instanceof Boolean) {
|
||||
v.invokestatic("java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;");
|
||||
}
|
||||
else if (value instanceof Character) {
|
||||
v.invokestatic("java/lang/Character", "valueOf", "(C)Ljava/lang/Character;");
|
||||
}
|
||||
else if (value instanceof Short) {
|
||||
v.invokestatic("java/lang/Short", "valueOf", "(S)Ljava/lang/Short;");
|
||||
}
|
||||
else if (value instanceof Long) {
|
||||
v.invokestatic("java/lang/Long", "valueOf", "(J)Ljava/lang/Long;");
|
||||
}
|
||||
else if (value instanceof Byte) {
|
||||
v.invokestatic("java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;");
|
||||
}
|
||||
else if (value instanceof Float) {
|
||||
v.invokestatic("java/lang/Float", "valueOf", "(F)Ljava/lang/Float;");
|
||||
}
|
||||
else if (value instanceof Double) {
|
||||
v.invokestatic("java/lang/Double", "valueOf", "(D)Ljava/lang/Double;");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitBlockExpression(JetBlockExpression expression) {
|
||||
Label blockStart = new Label();
|
||||
v.mark(blockStart);
|
||||
|
||||
List<JetElement> statements = expression.getStatements();
|
||||
for (JetElement statement : statements) {
|
||||
if (statement instanceof JetProperty) {
|
||||
myVarIndex.put((JetProperty) statement, myMaxVarIndex++);
|
||||
}
|
||||
}
|
||||
|
||||
for (JetElement statement : statements) {
|
||||
gen(statement);
|
||||
}
|
||||
|
||||
Label blockEnd = new Label();
|
||||
v.mark(blockEnd);
|
||||
|
||||
for (JetElement statement : statements) {
|
||||
if (statement instanceof JetProperty) {
|
||||
JetProperty var = (JetProperty) statement;
|
||||
int index = myVarIndex.remove(var);
|
||||
v.visitLocalVariable(var.getName(), getType(var).getDescriptor(), null, blockStart, blockEnd, index);
|
||||
myMaxVarIndex--;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Type getType(JetProperty var) {
|
||||
return InstructionAdapter.OBJECT_TYPE; // TODO:
|
||||
}
|
||||
|
||||
private boolean isUnitType(JetExpression e) {
|
||||
return false; // TODO:!!!
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitProperty(JetProperty property) {
|
||||
int index = myVarIndex.get(property);
|
||||
|
||||
assert index >= 0;
|
||||
|
||||
JetExpression initializer = property.getInitializer();
|
||||
if (initializer != null) {
|
||||
gen(initializer);
|
||||
v.store(index, getType(property));
|
||||
}
|
||||
}
|
||||
|
||||
private static class CompilationException extends RuntimeException {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.jetbrains.jet.codegen;
|
||||
|
||||
import org.jetbrains.jet.lang.psi.JetClass;
|
||||
import org.jetbrains.jet.lang.psi.JetFunction;
|
||||
import org.jetbrains.jet.lang.psi.JetNamespace;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
|
||||
/**
|
||||
* @author max
|
||||
*/
|
||||
public class FunctionCodegen {
|
||||
private final ClassVisitor v;
|
||||
|
||||
public FunctionCodegen(ClassVisitor v) {
|
||||
this.v = v;
|
||||
}
|
||||
|
||||
public void gen(JetFunction f, JetNamespace owner) {
|
||||
|
||||
}
|
||||
|
||||
public void gen(JetFunction f, JetClass owner) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package org.jetbrains.jet.codegen;
|
||||
|
||||
/**
|
||||
* @author max
|
||||
*/
|
||||
public class MethodCodeGen {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package org.jetbrains.jet.codegen;
|
||||
|
||||
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.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
/**
|
||||
* @author max
|
||||
*/
|
||||
public class NamespaceCodegen {
|
||||
private final ClassVisitorFactory factory;
|
||||
|
||||
public NamespaceCodegen(ClassVisitorFactory factory) {
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
public void generate(JetNamespace namespace) {
|
||||
final ClassVisitor v = factory.visitorForClassIn(namespace);
|
||||
|
||||
final PropertyCodegen propertyCodegen = new PropertyCodegen(v);
|
||||
final FunctionCodegen functionCodegen = new FunctionCodegen(v);
|
||||
v.visit(Opcodes.V1_6,
|
||||
Opcodes.ACC_PUBLIC,
|
||||
namespace.getFQName().replace('.', '/') + "/namespace",
|
||||
null,
|
||||
"jet/lang/Namespace",
|
||||
new String[0]
|
||||
);
|
||||
|
||||
for (JetDeclaration declaration : namespace.getDeclarations()) {
|
||||
if (declaration instanceof JetProperty) {
|
||||
propertyCodegen.gen((JetProperty) declaration, namespace);
|
||||
}
|
||||
else if (declaration instanceof JetFunction) {
|
||||
functionCodegen.gen((JetFunction) declaration, namespace);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
v.visitEnd();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.jetbrains.jet.codegen;
|
||||
|
||||
import org.jetbrains.jet.lang.psi.JetClass;
|
||||
import org.jetbrains.jet.lang.psi.JetNamespace;
|
||||
import org.jetbrains.jet.lang.psi.JetProperty;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
|
||||
/**
|
||||
* @author max
|
||||
*/
|
||||
public class PropertyCodegen {
|
||||
private final ClassVisitor v;
|
||||
|
||||
public PropertyCodegen(ClassVisitor v) {
|
||||
this.v = v;
|
||||
}
|
||||
|
||||
public void gen(JetProperty p, JetNamespace owner) {
|
||||
|
||||
}
|
||||
|
||||
public void gen(JetProperty p, JetClass owner) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
package org.jetbrains.jet.lang.psi;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.JetNodeTypes;
|
||||
|
||||
/**
|
||||
* @author max
|
||||
@@ -15,4 +17,45 @@ public class JetConstantExpression extends JetExpression {
|
||||
public void accept(JetVisitor visitor) {
|
||||
visitor.visitConstantExpression(this);
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
IElementType elementType = getNode().getElementType();
|
||||
String nodeText = getNode().getText();
|
||||
if (elementType == JetNodeTypes.INTEGER_CONSTANT) {
|
||||
return Integer.parseInt(nodeText);
|
||||
}
|
||||
else if (elementType == JetNodeTypes.LONG_CONSTANT) {
|
||||
return Long.parseLong(nodeText);
|
||||
}
|
||||
else if (elementType == JetNodeTypes.FLOAT_CONSTANT) {
|
||||
assert nodeText.length() > 0;
|
||||
char lastChar = nodeText.charAt(nodeText.length() - 1);
|
||||
if (lastChar == 'f' || lastChar == 'F') {
|
||||
return Float.parseFloat(nodeText.substring(0, nodeText.length() - 1));
|
||||
}
|
||||
else if (lastChar == 'd' || lastChar == 'D') {
|
||||
return Double.parseDouble(nodeText.substring(0, nodeText.length() - 1));
|
||||
}
|
||||
else {
|
||||
return Double.parseDouble(nodeText);
|
||||
}
|
||||
}
|
||||
else if (elementType == JetNodeTypes.BOOLEAN_CONSTANT) {
|
||||
return Boolean.parseBoolean(nodeText);
|
||||
}
|
||||
else if (elementType == JetNodeTypes.CHARACTER_CONSTANT) {
|
||||
return nodeText.charAt(1);
|
||||
}
|
||||
else if (elementType == JetNodeTypes.STRING_CONSTANT) {
|
||||
int tail = nodeText.length();
|
||||
if (nodeText.endsWith("\"")) tail--;
|
||||
return nodeText.substring(1, tail);
|
||||
}
|
||||
else if (elementType == JetNodeTypes.NULL) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Unsupported constant: " + this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,4 +35,8 @@ public class JetNamespace extends JetDeclaration {
|
||||
PsiElement nameNode = findChildByType(JetNodeTypes.NAMESPACE_NAME);
|
||||
return nameNode != null ? nameNode.getText() : "";
|
||||
}
|
||||
|
||||
public String getFQName() {
|
||||
return getName(); // TODO: Must include container namespace names, module root namespace
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package org.jetbrains.jet.codegen;
|
||||
|
||||
import com.intellij.codeInsight.daemon.LightDaemonAnalyzerTestCase;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import org.jetbrains.jet.lang.psi.JetProperty;
|
||||
import org.objectweb.asm.util.TraceMethodVisitor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
|
||||
/**
|
||||
* @author max
|
||||
*/
|
||||
public class ExpressionGenTest extends LightDaemonAnalyzerTestCase {
|
||||
public void testIntegerZeroExpression() throws Exception {
|
||||
checkCode("0",
|
||||
"LDC 0\n" +
|
||||
"INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;");
|
||||
}
|
||||
|
||||
public void testIf() throws Exception {
|
||||
checkCode("if (false) 15 else 20",
|
||||
"LDC false\n" +
|
||||
"INVOKESTATIC java/lang/Boolean.valueOf (Z)Ljava/lang/Boolean;\n" +
|
||||
"INVOKEVIRTUAL java/lang/Boolean.booleanValue ()Z\n" +
|
||||
"IFEQ L0\n" +
|
||||
"LDC 15\n" +
|
||||
"INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;\n" +
|
||||
"GOTO L1\n" +
|
||||
"L0\n" +
|
||||
"LDC 20\n" +
|
||||
"INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;\n" +
|
||||
"L1");
|
||||
|
||||
checkCode("if (false) 15",
|
||||
"LDC false\n" +
|
||||
"INVOKESTATIC java/lang/Boolean.valueOf (Z)Ljava/lang/Boolean;\n" +
|
||||
"INVOKEVIRTUAL java/lang/Boolean.booleanValue ()Z\n" +
|
||||
"IFEQ L0\n" +
|
||||
"LDC 15\n" +
|
||||
"INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;\n" +
|
||||
"POP\n" +
|
||||
"L0");
|
||||
|
||||
checkCode("if (false) else 20",
|
||||
"LDC false\n" +
|
||||
"INVOKESTATIC java/lang/Boolean.valueOf (Z)Ljava/lang/Boolean;\n" +
|
||||
"INVOKEVIRTUAL java/lang/Boolean.booleanValue ()Z\n" +
|
||||
"IFNE L0\n" +
|
||||
"LDC 20\n" +
|
||||
"INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;\n" +
|
||||
"POP\n" +
|
||||
"L0");
|
||||
}
|
||||
|
||||
private void checkCode(String expr, String expectation) throws IOException {
|
||||
configureFromFileText("test.jet", "val x = " + expr);
|
||||
|
||||
JetProperty p = PsiTreeUtil.getParentOfType(getFile().findElementAt(0), JetProperty.class);
|
||||
|
||||
TraceMethodVisitor trace = new TraceMethodVisitor();
|
||||
p.getInitializer().accept(new ExpressionCodegen(trace));
|
||||
|
||||
StringWriter out = new StringWriter();
|
||||
trace.print(new PrintWriter(out));
|
||||
assertEquals(trimLines(expectation), trimLines(out.getBuffer().toString()));
|
||||
}
|
||||
|
||||
private static String trimLines(String s) {
|
||||
StringBuilder b = new StringBuilder();
|
||||
for (String line : s.split("\\n")) {
|
||||
b.append(line.trim()).append('\n');
|
||||
}
|
||||
return b.toString();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user