generate public accessor methods for public properties

This commit is contained in:
Dmitry Jemerov
2011-04-21 17:18:15 +02:00
parent d6611e495b
commit 11f8ed8c28
4 changed files with 115 additions and 38 deletions
@@ -27,8 +27,6 @@ public class ExpressionCodegen extends JetVisitor {
private static final String CLASS_STRING_BUILDER = "java/lang/StringBuilder"; private static final String CLASS_STRING_BUILDER = "java/lang/StringBuilder";
private static final String CLASS_COMPARABLE = "java/lang/Comparable"; private static final String CLASS_COMPARABLE = "java/lang/Comparable";
private static final Type TYPE_OBJECT = Type.getObjectType(CLASS_OBJECT);
private final Stack<Label> myContinueTargets = new Stack<Label>(); private final Stack<Label> myContinueTargets = new Stack<Label>();
private final Stack<Label> myBreakTargets = new Stack<Label>(); private final Stack<Label> myBreakTargets = new Stack<Label>();
private final Stack<StackValue> myStack = new Stack<StackValue>(); private final Stack<StackValue> myStack = new Stack<StackValue>();
@@ -372,21 +370,8 @@ public class ExpressionCodegen extends JetVisitor {
} }
else if (descriptor instanceof PropertyDescriptor) { else if (descriptor instanceof PropertyDescriptor) {
final PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor; final PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
String owner; boolean isStatic = descriptor.getContainingDeclaration() instanceof NamespaceDescriptor;
boolean isStatic; String owner = JetTypeMapper.getOwner(descriptor);
if (descriptor.getContainingDeclaration() instanceof NamespaceDescriptor) {
JetNamespace ns = (JetNamespace) bindingContext.getDeclarationPsiElement(descriptor.getContainingDeclaration());
owner = JetTypeMapper.jvmName(ns);
isStatic = true;
}
else if (descriptor.getContainingDeclaration() instanceof ClassDescriptor) {
ClassDescriptor classDescriptor = (ClassDescriptor) descriptor.getContainingDeclaration();
owner = JetTypeMapper.jvmNameForImplementation(classDescriptor);
isStatic = false;
}
else {
throw new UnsupportedOperationException("don't know how to generate property reference with parent " + descriptor.getContainingDeclaration());
}
final JetType outType = ((VariableDescriptor) descriptor).getOutType(); final JetType outType = ((VariableDescriptor) descriptor).getOutType();
Method getter; Method getter;
Method setter; Method setter;
@@ -395,8 +380,8 @@ public class ExpressionCodegen extends JetVisitor {
setter = null; setter = null;
} }
else { else {
getter = typeMapper.mapGetterSignature(propertyDescriptor); getter = propertyDescriptor.getGetter() == null ? null : typeMapper.mapGetterSignature(propertyDescriptor);
setter = typeMapper.mapSetterSignature(propertyDescriptor); setter = propertyDescriptor.getSetter() == null ? null : typeMapper.mapSetterSignature(propertyDescriptor);
} }
if (!isStatic) { if (!isStatic) {
ensureReceiverOnStack(expression); ensureReceiverOnStack(expression);
@@ -497,11 +482,11 @@ public class ExpressionCodegen extends JetVisitor {
final JetDotQualifiedExpression parent = (JetDotQualifiedExpression) expression.getParent(); final JetDotQualifiedExpression parent = (JetDotQualifiedExpression) expression.getParent();
if (!resolvesToClassOrPackage(parent.getReceiverExpression())) { if (!resolvesToClassOrPackage(parent.getReceiverExpression())) {
// we have a receiver on stack // we have a receiver on stack
myStack.pop().put(TYPE_OBJECT, v); myStack.pop().put(JetTypeMapper.TYPE_OBJECT, v);
} }
} }
else if (!(expression.getParent() instanceof JetSafeQualifiedExpression)) { else if (!(expression.getParent() instanceof JetSafeQualifiedExpression)) {
v.load(0, TYPE_OBJECT); // TODO hope it works; really need more checks here :) v.load(0, JetTypeMapper.TYPE_OBJECT); // TODO hope it works; really need more checks here :)
} }
} }
@@ -856,7 +841,7 @@ public class ExpressionCodegen extends JetVisitor {
Type exprType = expressionType(expr); Type exprType = expressionType(expr);
gen(expr, exprType); gen(expr, exprType);
Method appendDescriptor = new Method("append", Type.getObjectType(CLASS_STRING_BUILDER), Method appendDescriptor = new Method("append", Type.getObjectType(CLASS_STRING_BUILDER),
new Type[] { exprType.getSort() == Type.OBJECT ? TYPE_OBJECT : exprType}); new Type[] { exprType.getSort() == Type.OBJECT ? JetTypeMapper.TYPE_OBJECT : exprType});
v.invokevirtual(CLASS_STRING_BUILDER, "append", appendDescriptor.getDescriptor()); v.invokevirtual(CLASS_STRING_BUILDER, "append", appendDescriptor.getDescriptor());
} }
@@ -2,14 +2,12 @@ package org.jetbrains.jet.codegen;
import com.intellij.psi.PsiClass; import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.psi.JetFunction;
import org.jetbrains.jet.lang.psi.JetNamespace;
import org.jetbrains.jet.lang.psi.JetParameter;
import org.jetbrains.jet.lang.psi.JetTypeReference;
import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.types.*; import org.jetbrains.jet.lang.types.*;
import org.jetbrains.jet.lexer.JetTokens;
import org.jetbrains.jet.resolve.DescriptorUtil; import org.jetbrains.jet.resolve.DescriptorUtil;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type; import org.objectweb.asm.Type;
import org.objectweb.asm.commons.Method; import org.objectweb.asm.commons.Method;
@@ -19,6 +17,8 @@ import java.util.List;
* @author yole * @author yole
*/ */
public class JetTypeMapper { public class JetTypeMapper {
static final Type TYPE_OBJECT = Type.getObjectType("java/lang/Object");
private final JetStandardLibrary standardLibrary; private final JetStandardLibrary standardLibrary;
private final BindingContext bindingContext; private final BindingContext bindingContext;
@@ -39,6 +39,10 @@ public class JetTypeMapper {
return NamespaceCodegen.getJVMClassName(namespace.getFQName()); return NamespaceCodegen.getJVMClassName(namespace.getFQName());
} }
static String jvmName(NamespaceDescriptor namespace) {
return NamespaceCodegen.getJVMClassName(DescriptorUtil.getFQName(namespace));
}
public static String jvmNameForInterface(ClassDescriptor descriptor) { public static String jvmNameForInterface(ClassDescriptor descriptor) {
return DescriptorUtil.getFQName(descriptor).replace('.', '/'); return DescriptorUtil.getFQName(descriptor).replace('.', '/');
} }
@@ -51,6 +55,21 @@ public class JetTypeMapper {
return jvmNameForInterface(descriptor) + "$$DImpl"; return jvmNameForInterface(descriptor) + "$$DImpl";
} }
static String getOwner(DeclarationDescriptor descriptor) {
String owner;
if (descriptor.getContainingDeclaration() instanceof NamespaceDescriptor) {
owner = jvmName((NamespaceDescriptor) descriptor.getContainingDeclaration());
}
else if (descriptor.getContainingDeclaration() instanceof ClassDescriptor) {
ClassDescriptor classDescriptor = (ClassDescriptor) descriptor.getContainingDeclaration();
owner = jvmNameForImplementation(classDescriptor);
}
else {
throw new UnsupportedOperationException("don't know how to generate owner for parent " + descriptor.getContainingDeclaration());
}
return owner;
}
public Type mapType(final JetType jetType) { public Type mapType(final JetType jetType) {
if (jetType.equals(JetStandardClasses.getUnitType())) { if (jetType.equals(JetStandardClasses.getUnitType())) {
return Type.VOID_TYPE; return Type.VOID_TYPE;
@@ -150,21 +169,27 @@ public class JetTypeMapper {
return new Method(f.getName(), returnType, parameterTypes); return new Method(f.getName(), returnType, parameterTypes);
} }
@Nullable
public Method mapGetterSignature(PropertyDescriptor descriptor) { public Method mapGetterSignature(PropertyDescriptor descriptor) {
if (descriptor.getGetter() == null) {
return null;
}
Type returnType = mapType(descriptor.getOutType()); Type returnType = mapType(descriptor.getOutType());
return new Method(PropertyCodegen.getterName(descriptor.getName()), returnType, new Type[0]); return new Method(PropertyCodegen.getterName(descriptor.getName()), returnType, new Type[0]);
} }
@Nullable
public Method mapSetterSignature(PropertyDescriptor descriptor) { public Method mapSetterSignature(PropertyDescriptor descriptor) {
if (descriptor.getSetter() == null) {
return null;
}
Type paramType = mapType(descriptor.getInType()); Type paramType = mapType(descriptor.getInType());
return new Method(PropertyCodegen.setterName(descriptor.getName()), Type.VOID_TYPE, new Type[] { paramType }); return new Method(PropertyCodegen.setterName(descriptor.getName()), Type.VOID_TYPE, new Type[] { paramType });
} }
static int getAccessModifiers(JetDeclaration p, int defaultFlags) {
int flags = 0;
if (p.hasModifier(JetTokens.PUBLIC_KEYWORD)) {
flags |= Opcodes.ACC_PUBLIC;
}
else if (p.hasModifier(JetTokens.PRIVATE_KEYWORD)) {
flags |= Opcodes.ACC_PRIVATE;
}
else {
flags |= defaultFlags;
}
return flags;
}
} }
@@ -7,8 +7,12 @@ import org.jetbrains.jet.lang.psi.JetProperty;
import org.jetbrains.jet.lang.psi.JetPropertyAccessor; import org.jetbrains.jet.lang.psi.JetPropertyAccessor;
import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.types.*; import org.jetbrains.jet.lang.types.*;
import org.jetbrains.jet.lexer.JetTokens;
import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes; import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
import java.util.Collections; import java.util.Collections;
@@ -63,15 +67,16 @@ public class PropertyCodegen {
if (kind == OwnerKind.NAMESPACE) { if (kind == OwnerKind.NAMESPACE) {
modifiers |= Opcodes.ACC_STATIC; modifiers |= Opcodes.ACC_STATIC;
} }
v.visitField(modifiers, p.getName(), v.visitField(modifiers, p.getName(), mapper.mapType(descriptor.getOutType()).getDescriptor(), null, value);
mapper.mapType(descriptor.getOutType()).getDescriptor(),
null, value);
} }
final JetPropertyAccessor getter = p.getGetter(); final JetPropertyAccessor getter = p.getGetter();
if (getter != null) { if (getter != null) {
functionCodegen.generateMethod(getter, kind, mapper.mapGetterSignature(propertyDescriptor), functionCodegen.generateMethod(getter, kind, mapper.mapGetterSignature(propertyDescriptor),
Collections.<ValueParameterDescriptor>emptyList()); Collections.<ValueParameterDescriptor>emptyList());
} }
else if (p.hasModifier(JetTokens.PUBLIC_KEYWORD)) {
generateDefaultGetter(p, kind);
}
final JetPropertyAccessor setter = p.getSetter(); final JetPropertyAccessor setter = p.getSetter();
if (setter != null) { if (setter != null) {
final PropertySetterDescriptor setterDescriptor = propertyDescriptor.getSetter(); final PropertySetterDescriptor setterDescriptor = propertyDescriptor.getSetter();
@@ -79,9 +84,60 @@ public class PropertyCodegen {
functionCodegen.generateMethod(setter, kind, mapper.mapSetterSignature(propertyDescriptor), functionCodegen.generateMethod(setter, kind, mapper.mapSetterSignature(propertyDescriptor),
setterDescriptor.getUnsubstitutedValueParameters()); setterDescriptor.getUnsubstitutedValueParameters());
} }
else if (p.hasModifier(JetTokens.PUBLIC_KEYWORD) && p.isVar()) {
generateDefaultSetter(p, kind);
}
} }
} }
private void generateDefaultGetter(JetProperty p, OwnerKind kind) {
final PropertyDescriptor propertyDescriptor = (PropertyDescriptor) context.getVariableDescriptor(p);
int flags = JetTypeMapper.getAccessModifiers(p, Opcodes.ACC_PUBLIC);
if (kind == OwnerKind.NAMESPACE) {
flags |= Opcodes.ACC_STATIC;
}
final String signature = mapper.mapGetterSignature(propertyDescriptor).getDescriptor();
MethodVisitor mv = v.visitMethod(flags, getterName(p.getName()), signature, null, null);
mv.visitCode();
InstructionAdapter iv = new InstructionAdapter(mv);
if (kind != OwnerKind.NAMESPACE) {
iv.load(0, JetTypeMapper.TYPE_OBJECT);
}
final Type type = mapper.mapType(propertyDescriptor.getOutType());
iv.visitFieldInsn(kind == OwnerKind.NAMESPACE ? Opcodes.GETSTATIC : Opcodes.GETFIELD,
JetTypeMapper.getOwner(propertyDescriptor), propertyDescriptor.getName(),
type.getDescriptor());
iv.areturn(type);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
private void generateDefaultSetter(JetProperty p, OwnerKind kind) {
final PropertyDescriptor propertyDescriptor = (PropertyDescriptor) context.getVariableDescriptor(p);
int flags = JetTypeMapper.getAccessModifiers(p, Opcodes.ACC_PUBLIC);
if (kind == OwnerKind.NAMESPACE) {
flags |= Opcodes.ACC_STATIC;
}
final String signature = mapper.mapSetterSignature(propertyDescriptor).getDescriptor();
MethodVisitor mv = v.visitMethod(flags, setterName(p.getName()), signature, null, null);
mv.visitCode();
InstructionAdapter iv = new InstructionAdapter(mv);
final Type type = mapper.mapType(propertyDescriptor.getOutType());
if (kind != OwnerKind.NAMESPACE) {
iv.load(0, JetTypeMapper.TYPE_OBJECT);
iv.load(1, type);
}
else {
iv.load(0, type);
}
iv.visitFieldInsn(kind == OwnerKind.NAMESPACE ? Opcodes.PUTSTATIC : Opcodes.PUTFIELD,
JetTypeMapper.getOwner(propertyDescriptor), propertyDescriptor.getName(),
type.getDescriptor());
iv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
public static String getterName(String propertyName) { public static String getterName(String propertyName) {
return "get" + StringUtil.capitalizeWithJavaBeanConvention(propertyName); return "get" + StringUtil.capitalizeWithJavaBeanConvention(propertyName);
} }
@@ -29,6 +29,17 @@ public class PropertyGenTest extends CodegenTestCase {
assertEquals(239, ((Integer) getter.invoke(instance)).intValue()); assertEquals(239, ((Integer) getter.invoke(instance)).intValue());
} }
public void testPublicVar() throws Exception {
loadText("class PublicVar { public var foo = 0; }");
System.out.println(generateToText());
final Class aClass = loadImplementationClass(generateClassesInFile(), "PublicVar");
final Object instance = aClass.newInstance();
Method setter = findMethodByName(aClass, "setFoo");
setter.invoke(instance, 239);
Method getter = findMethodByName(aClass, "getFoo");
assertEquals(239, ((Integer) getter.invoke(instance)).intValue());
}
public void testPropertyInNamespace() throws Exception { public void testPropertyInNamespace() throws Exception {
loadText("private val x = 239"); loadText("private val x = 239");
final Class nsClass = generateNamespaceClass(); final Class nsClass = generateNamespaceClass();