initial basic implementation of object literals; move anonymous class name mapping to JetTypeMapper; cleanup many usages of JetTypeMapper

This commit is contained in:
Dmitry Jemerov
2011-06-29 17:10:47 +02:00
parent f26d0b4af7
commit 47bb3d6a2c
12 changed files with 140 additions and 62 deletions
@@ -1,10 +1,7 @@
package org.jetbrains.jet.codegen;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.jetbrains.jet.lang.psi.JetClass;
import org.jetbrains.jet.lang.psi.JetClassOrObject;
import org.jetbrains.jet.lang.psi.JetDeclaration;
import org.jetbrains.jet.lang.psi.JetObjectDeclaration;
import org.jetbrains.jet.lang.psi.*;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
@@ -20,6 +17,8 @@ public class ClassCodegen {
}
public void generate(JetClassOrObject aClass) {
prepareAnonymousClasses(aClass);
if (aClass instanceof JetObjectDeclaration) {
generateImplementation(aClass, OwnerKind.IMPLEMENTATION);
}
@@ -36,6 +35,21 @@ public class ClassCodegen {
}
}
private void prepareAnonymousClasses(JetClassOrObject aClass) {
aClass.acceptChildren(new JetVisitor() {
@Override
public void visitJetElement(JetElement element) {
super.visitJetElement(element);
element.acceptChildren(this);
}
@Override
public void visitObjectLiteralExpression(JetObjectLiteralExpression expression) {
state.getTypeMapper().classNameForAnonymousClass(expression.getObjectDeclaration());
}
});
}
private void generateInterface(JetClassOrObject aClass) {
final ClassVisitor visitor = state.forClassInterface(state.getBindingContext().getClassDescriptor(aClass));
new InterfaceBodyCodegen(aClass, visitor, state).generate();
@@ -1,7 +1,6 @@
package org.jetbrains.jet.codegen;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import org.jetbrains.jet.lang.psi.JetNamespace;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
@@ -19,7 +18,6 @@ public class ClassFileFactory {
private final boolean isText;
private final Map<String, NamespaceCodegen> ns2codegen = new HashMap<String, NamespaceCodegen>();
private final Map<String, ClassVisitor> generators = new LinkedHashMap<String, ClassVisitor>();
private final Map<String, Integer> closuresCount = new HashMap<String, Integer>();
private boolean isDone = false;
public final GenerationState state;
@@ -42,14 +40,8 @@ public class ClassFileFactory {
return visitor;
}
Pair<String, ClassVisitor> forClosureIn(String baseName) {
Integer count = closuresCount.get(baseName);
if (count == null) count = 0;
closuresCount.put(baseName, count + 1);
final String className = baseName + "$" + (count + 1);
return new Pair<String, ClassVisitor>(className, newVisitor(className + ".class"));
ClassVisitor forAnonymousSubclass(String className) {
return newVisitor(className + ".class");
}
NamespaceCodegen forNamespace(JetNamespace namespace) {
@@ -4,7 +4,6 @@
package org.jetbrains.jet.codegen;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
@@ -78,17 +77,8 @@ public class ClosureCodegen {
return null;
}
public GeneratedClosureDescriptor gen(JetFunctionLiteralExpression fun) {
JetNamedDeclaration container = PsiTreeUtil.getParentOfType(fun, JetNamespace.class, JetClass.class, JetObjectDeclaration.class);
final Pair<String, ClassVisitor> nameAndVisitor;
if (container instanceof JetNamespace) {
nameAndVisitor = state.forClosureIn((JetNamespace) container);
}
else {
nameAndVisitor = state.forClosureIn(state.getBindingContext().getClassDescriptor((JetClassOrObject) container));
}
public GeneratedAnonymousClassDescriptor gen(JetFunctionLiteralExpression fun) {
final Pair<String, ClassVisitor> nameAndVisitor = state.forAnonymousSubclass(fun);
final FunctionDescriptor funDescriptor = (FunctionDescriptor) state.getBindingContext().getDeclarationDescriptor(fun);
@@ -123,7 +113,7 @@ public class ClosureCodegen {
cv.visitEnd();
final GeneratedClosureDescriptor answer = new GeneratedClosureDescriptor(name, constructor);
final GeneratedAnonymousClassDescriptor answer = new GeneratedAnonymousClassDescriptor(name, constructor);
for (DeclarationDescriptor descriptor : closure.keySet()) {
final EnclosedValueDescriptor valueDescriptor = closure.get(descriptor);
answer.addArg(valueDescriptor.getOuterValue());
@@ -94,8 +94,8 @@ public class ExpressionCodegen extends JetVisitor {
outerThisExpressions.put(outer, expression);
}
static void loadTypeInfo(ClassDescriptor descriptor, InstructionAdapter v) {
String owner = JetTypeMapper.jvmNameForImplementation(descriptor);
static void loadTypeInfo(JetTypeMapper typeMapper, ClassDescriptor descriptor, InstructionAdapter v) {
String owner = typeMapper.jvmName(descriptor, OwnerKind.IMPLEMENTATION);
if (descriptor.getTypeConstructor().getParameters().size() > 0) {
v.load(0, JetTypeMapper.TYPE_OBJECT);
v.getfield(owner, "$typeInfo", "Ljet/typeinfo/TypeInfo;");
@@ -482,7 +482,7 @@ public class ExpressionCodegen extends JetVisitor {
generateBlock(expression.getFunctionLiteral().getBodyExpression().getStatements());
}
else {
final GeneratedClosureDescriptor closure = state.generateClosure(expression, this);
final GeneratedAnonymousClassDescriptor closure = state.generateClosure(expression, this);
v.anew(Type.getObjectType(closure.getClassname()));
v.dup();
@@ -498,6 +498,14 @@ public class ExpressionCodegen extends JetVisitor {
}
}
@Override
public void visitObjectLiteralExpression(JetObjectLiteralExpression expression) {
GeneratedAnonymousClassDescriptor descriptor = state.generateObjectLiteral(expression, this);
v.anew(Type.getObjectType(descriptor.getClassname()));
v.dup();
v.invokespecial(descriptor.getClassname(), "<init>", descriptor.getConstructor().getDescriptor());
}
private void generateBlock(List<JetElement> statements) {
Label blockStart = new Label();
v.mark(blockStart);
@@ -778,7 +786,7 @@ public class ExpressionCodegen extends JetVisitor {
methodDescriptor.getName(),
methodDescriptor.getDescriptor());
}
else if(declarationPsiElement instanceof JetNamedFunction) {
else if (declarationPsiElement instanceof JetNamedFunction) {
final JetNamedFunction jetFunction = (JetNamedFunction) declarationPsiElement;
methodDescriptor = typeMapper.mapSignature(jetFunction);
if (functionParent instanceof NamespaceDescriptorImpl) {
@@ -790,10 +798,14 @@ public class ExpressionCodegen extends JetVisitor {
v.invokestatic(owner, methodDescriptor.getName(), methodDescriptor.getDescriptor());
}
else if (functionParent instanceof ClassDescriptor) {
ensureReceiverOnStack(expression, (ClassDescriptor) functionParent);
ClassDescriptor containingClass = (ClassDescriptor) functionParent;
ensureReceiverOnStack(expression, containingClass);
pushMethodArguments(expression, methodDescriptor);
final String owner = JetTypeMapper.jvmNameForInterface((ClassDescriptor) functionParent);
v.invokeinterface(owner, methodDescriptor.getName(), methodDescriptor.getDescriptor());
final String owner = typeMapper.jvmName(containingClass, OwnerKind.INTERFACE);
int opcode = typeMapper.isInterface(containingClass, OwnerKind.INTERFACE)
? Opcodes.INVOKEINTERFACE
: Opcodes.INVOKEVIRTUAL;
v.visitMethodInsn(opcode,owner, methodDescriptor.getName(), methodDescriptor.getDescriptor());
}
else {
throw new UnsupportedOperationException("don't know how to generate call to " + declarationPsiElement);
@@ -1682,7 +1694,7 @@ public class ExpressionCodegen extends JetVisitor {
if (declarationDescriptor instanceof TypeParameterDescriptor) {
DeclarationDescriptor containingDeclaration = declarationDescriptor.getContainingDeclaration();
if (containingDeclaration == contextType && contextType instanceof ClassDescriptor) {
loadTypeInfo((ClassDescriptor) contextType, v);
loadTypeInfo(typeMapper, (ClassDescriptor) contextType, v);
v.iconst(((TypeParameterDescriptor) declarationDescriptor).getIndex());
v.invokevirtual("jet/typeinfo/TypeInfo", "getTypeParameter", "(I)Ljet/typeinfo/TypeInfo;");
return;
@@ -8,12 +8,12 @@ import org.objectweb.asm.commons.Method;
import java.util.ArrayList;
import java.util.List;
public class GeneratedClosureDescriptor {
public class GeneratedAnonymousClassDescriptor {
private final String classname;
private Method constructor;
private List<StackValue> args = new ArrayList<StackValue>();
public GeneratedClosureDescriptor(String classname, Method constructor) {
public GeneratedAnonymousClassDescriptor(String classname, Method constructor) {
this.classname = classname;
this.constructor = constructor;
}
@@ -10,13 +10,12 @@ import org.jetbrains.jet.lang.ErrorHandler;
import org.jetbrains.jet.lang.cfg.pseudocode.JetControlFlowDataTraceFactory;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.psi.JetFunctionLiteralExpression;
import org.jetbrains.jet.lang.psi.JetNamespace;
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;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.commons.Method;
public class GenerationState {
private final ClassFileFactory factory;
@@ -69,12 +68,10 @@ public class GenerationState {
return factory.newVisitor(JetTypeMapper.jvmNameForDelegatingImplementation(aClass) + ".class");
}
public Pair<String, ClassVisitor> forClosureIn(ClassDescriptor aClass) {
return factory.forClosureIn(JetTypeMapper.jvmNameForInterface(aClass));
}
public Pair<String, ClassVisitor> forClosureIn(JetNamespace namespace) {
return factory.forClosureIn(NamespaceCodegen.getJVMClassName(namespace.getFQName()));
public Pair<String, ClassVisitor> forAnonymousSubclass(JetExpression expression) {
String className = typeMapper.classNameForAnonymousClass(expression);
ClassVisitor visitor = factory.forAnonymousSubclass(className);
return Pair.create(className, visitor);
}
public NamespaceCodegen forNamespace(JetNamespace namespace) {
@@ -97,18 +94,24 @@ public class GenerationState {
}
}
public GeneratedClosureDescriptor generateClosure(JetFunctionLiteralExpression literal, ExpressionCodegen context) {
public GeneratedAnonymousClassDescriptor generateClosure(JetFunctionLiteralExpression literal, ExpressionCodegen context) {
final ClosureCodegen codegen = new ClosureCodegen(this, context);
closureContexts.push(codegen);
try {
return codegen.gen(literal);
}
finally {
final ClosureCodegen pooped = closureContexts.pop();
assert pooped == codegen;
final ClosureCodegen popped = closureContexts.pop();
assert popped == codegen;
}
}
public GeneratedAnonymousClassDescriptor generateObjectLiteral(JetObjectLiteralExpression literal, ExpressionCodegen context) {
Pair<String, ClassVisitor> nameAndVisitor = forAnonymousSubclass(literal.getObjectDeclaration());
new ImplementationBodyCodegen(literal.getObjectDeclaration(), OwnerKind.IMPLEMENTATION, nameAndVisitor.getSecond(), this).generate();
return new GeneratedAnonymousClassDescriptor(nameAndVisitor.first, new Method("<init>", "()V"));
}
public StackValue lookupInContext(DeclarationDescriptor d) {
final ClosureCodegen top = closureContexts.peek();
return top != null ? top.lookupInContext(d) : null;
@@ -42,7 +42,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
}
private String jvmName() {
return JetTypeMapper.jetJvmName(descriptor, kind);
return state.getTypeMapper().jvmName(descriptor, kind);
}
protected String getSuperClass() {
@@ -65,7 +65,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
int typeinfoStatic = descriptor.getTypeConstructor().getParameters().size() > 0 ? 0 : Opcodes.ACC_STATIC;
v.visitField(Opcodes.ACC_PRIVATE | typeinfoStatic, "$typeInfo", "Ljet/typeinfo/TypeInfo;", null, null);
if (myClass instanceof JetObjectDeclaration) {
if (isNonLiteralObject()) {
Type type = JetTypeMapper.jetImplementationType(descriptor);
v.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "$instance", type.getDescriptor(), null, null);
}
@@ -381,7 +381,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
private void generateStaticInitializer() {
boolean needTypeInfo = descriptor.getTypeConstructor().getParameters().size() == 0;
boolean needInstance = myClass instanceof JetObjectDeclaration;
boolean needInstance = isNonLiteralObject();
if (!needTypeInfo && !needInstance) {
// we will have a dynamic type info field
return;
@@ -393,8 +393,9 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
InstructionAdapter v = new InstructionAdapter(mv);
if (needTypeInfo) {
ClassCodegen.newTypeInfo(v, state.getTypeMapper().jvmType(descriptor, OwnerKind.INTERFACE));
v.putstatic(JetTypeMapper.jvmNameForImplementation(descriptor), "$typeInfo", "Ljet/typeinfo/TypeInfo;");
JetTypeMapper typeMapper = state.getTypeMapper();
ClassCodegen.newTypeInfo(v, typeMapper.jvmType(descriptor, OwnerKind.INTERFACE));
v.putstatic(typeMapper.jvmName(descriptor, kind), "$typeInfo", "Ljet/typeinfo/TypeInfo;");
}
if (needInstance) {
String name = jvmName();
@@ -410,6 +411,10 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
mv.visitEnd();
}
private boolean isNonLiteralObject() {
return myClass instanceof JetObjectDeclaration && !((JetObjectDeclaration) myClass).isObjectLiteral();
}
private void generateGetTypeInfo() {
final MethodVisitor mv = v.visitMethod(Opcodes.ACC_PUBLIC,
"getTypeInfo",
@@ -418,7 +423,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
null);
mv.visitCode();
InstructionAdapter v = new InstructionAdapter(mv);
ExpressionCodegen.loadTypeInfo(descriptor, v);
ExpressionCodegen.loadTypeInfo(state.getTypeMapper(), descriptor, v);
v.areturn(JetTypeMapper.TYPE_TYPEINFO);
mv.visitMaxs(0, 0);
mv.visitEnd();
@@ -2,6 +2,7 @@ package org.jetbrains.jet.codegen;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import jet.typeinfo.TypeInfo;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.*;
@@ -15,7 +16,9 @@ import org.objectweb.asm.Type;
import org.objectweb.asm.commons.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author yole
@@ -26,6 +29,8 @@ public class JetTypeMapper {
private final JetStandardLibrary standardLibrary;
private final BindingContext bindingContext;
private final Map<JetExpression, String> classNamesForAnonymousClasses = new HashMap<JetExpression, String>();
private final Map<String, Integer> anonymousSubclassesCount = new HashMap<String, Integer>();
public JetTypeMapper(JetStandardLibrary standardLibrary, BindingContext bindingContext) {
this.standardLibrary = standardLibrary;
@@ -49,10 +54,25 @@ public class JetTypeMapper {
if (declaration instanceof PsiClass) {
return jvmName((PsiClass) declaration);
}
if (declaration instanceof JetObjectDeclaration && ((JetObjectDeclaration) declaration).isObjectLiteral()) {
String className = classNamesForAnonymousClasses.get(declaration);
if (className == null) {
throw new UnsupportedOperationException("Unexpected forward reference to anonymous class " + declaration);
}
return className;
}
return jetJvmName(jetClass, kind);
}
public static String jetJvmName(ClassDescriptor jetClass, OwnerKind kind) {
public boolean isInterface(ClassDescriptor jetClass, OwnerKind kind) {
PsiElement declaration = bindingContext.getDeclarationPsiElement(jetClass);
if (declaration instanceof JetObjectDeclaration && ((JetObjectDeclaration) declaration).isObjectLiteral()) {
return false;
}
return kind == OwnerKind.INTERFACE;
}
private static String jetJvmName(ClassDescriptor jetClass, OwnerKind kind) {
if (jetClass.isObject()) {
return jvmNameForImplementation(jetClass);
}
@@ -209,11 +229,7 @@ public class JetTypeMapper {
}
if (descriptor instanceof ClassDescriptor) {
final PsiElement declaration = bindingContext.getDeclarationPsiElement(descriptor);
if (declaration instanceof PsiClass) {
return psiClassType((PsiClass) declaration);
}
return Type.getObjectType(jetJvmName((ClassDescriptor) descriptor, kind));
return Type.getObjectType(jvmName((ClassDescriptor) descriptor, kind));
}
throw new UnsupportedOperationException("Unknown type " + jetType);
@@ -370,4 +386,31 @@ public class JetTypeMapper {
}
return flags;
}
String classNameForAnonymousClass(JetExpression expression) {
String name = classNamesForAnonymousClasses.get(expression);
if (name != null) {
return name;
}
JetNamedDeclaration container = PsiTreeUtil.getParentOfType(expression, JetNamespace.class, JetClass.class, JetObjectDeclaration.class);
String baseName;
if (container instanceof JetNamespace) {
baseName = NamespaceCodegen.getJVMClassName(((JetNamespace) container).getFQName());
}
else {
ClassDescriptor aClass = bindingContext.getClassDescriptor((JetClassOrObject) container);
baseName = JetTypeMapper.jvmNameForInterface(aClass);
}
Integer count = anonymousSubclassesCount.get(baseName);
if (count == null) count = 0;
anonymousSubclassesCount.put(baseName, count + 1);
final String className = baseName + "$" + (count + 1);
classNamesForAnonymousClasses.put(expression, className);
return className;
}
}
@@ -1,5 +1,6 @@
package org.jetbrains.jet.lang.psi;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -8,7 +9,7 @@ import java.util.List;
/**
* @author max
*/
public interface JetClassOrObject {
public interface JetClassOrObject extends PsiElement {
List<JetDeclaration> getDeclarations();
@Nullable
@@ -82,4 +82,8 @@ public class JetObjectDeclaration extends JetNamedDeclaration implements JetClas
public void accept(JetVisitor visitor) {
visitor.visitObjectDeclaration(this);
}
public boolean isObjectLiteral() {
return getNameAsDeclaration() == null;
}
}
@@ -0,0 +1,10 @@
class C() {
val child = object {
fun toString(): String = "child"
}
}
fun box(): String {
val c = C()
return if (c.child.toString() == "child") "OK" else "fail"
}
@@ -7,4 +7,8 @@ public class ObjectGenTest extends CodegenTestCase {
public void testSimpleObject() throws Exception {
blackBoxFile("objects/simpleObject.jet");
}
public void testObjectLiteral() throws Exception {
blackBoxFile("objects/objectLiteral.jet");
}
}