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_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> myBreakTargets = new Stack<Label>();
private final Stack<StackValue> myStack = new Stack<StackValue>();
@@ -372,21 +370,8 @@ public class ExpressionCodegen extends JetVisitor {
}
else if (descriptor instanceof PropertyDescriptor) {
final PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
String owner;
boolean isStatic;
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());
}
boolean isStatic = descriptor.getContainingDeclaration() instanceof NamespaceDescriptor;
String owner = JetTypeMapper.getOwner(descriptor);
final JetType outType = ((VariableDescriptor) descriptor).getOutType();
Method getter;
Method setter;
@@ -395,8 +380,8 @@ public class ExpressionCodegen extends JetVisitor {
setter = null;
}
else {
getter = typeMapper.mapGetterSignature(propertyDescriptor);
setter = typeMapper.mapSetterSignature(propertyDescriptor);
getter = propertyDescriptor.getGetter() == null ? null : typeMapper.mapGetterSignature(propertyDescriptor);
setter = propertyDescriptor.getSetter() == null ? null : typeMapper.mapSetterSignature(propertyDescriptor);
}
if (!isStatic) {
ensureReceiverOnStack(expression);
@@ -497,11 +482,11 @@ public class ExpressionCodegen extends JetVisitor {
final JetDotQualifiedExpression parent = (JetDotQualifiedExpression) expression.getParent();
if (!resolvesToClassOrPackage(parent.getReceiverExpression())) {
// 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)) {
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);
gen(expr, exprType);
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());
}
@@ -2,14 +2,12 @@ package org.jetbrains.jet.codegen;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.Nullable;
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.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.types.*;
import org.jetbrains.jet.lexer.JetTokens;
import org.jetbrains.jet.resolve.DescriptorUtil;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.Method;
@@ -19,6 +17,8 @@ import java.util.List;
* @author yole
*/
public class JetTypeMapper {
static final Type TYPE_OBJECT = Type.getObjectType("java/lang/Object");
private final JetStandardLibrary standardLibrary;
private final BindingContext bindingContext;
@@ -39,6 +39,10 @@ public class JetTypeMapper {
return NamespaceCodegen.getJVMClassName(namespace.getFQName());
}
static String jvmName(NamespaceDescriptor namespace) {
return NamespaceCodegen.getJVMClassName(DescriptorUtil.getFQName(namespace));
}
public static String jvmNameForInterface(ClassDescriptor descriptor) {
return DescriptorUtil.getFQName(descriptor).replace('.', '/');
}
@@ -51,6 +55,21 @@ public class JetTypeMapper {
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) {
if (jetType.equals(JetStandardClasses.getUnitType())) {
return Type.VOID_TYPE;
@@ -150,21 +169,27 @@ public class JetTypeMapper {
return new Method(f.getName(), returnType, parameterTypes);
}
@Nullable
public Method mapGetterSignature(PropertyDescriptor descriptor) {
if (descriptor.getGetter() == null) {
return null;
}
Type returnType = mapType(descriptor.getOutType());
return new Method(PropertyCodegen.getterName(descriptor.getName()), returnType, new Type[0]);
}
@Nullable
public Method mapSetterSignature(PropertyDescriptor descriptor) {
if (descriptor.getSetter() == null) {
return null;
}
Type paramType = mapType(descriptor.getInType());
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.resolve.BindingContext;
import org.jetbrains.jet.lang.types.*;
import org.jetbrains.jet.lexer.JetTokens;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
import java.util.Collections;
@@ -63,15 +67,16 @@ public class PropertyCodegen {
if (kind == OwnerKind.NAMESPACE) {
modifiers |= Opcodes.ACC_STATIC;
}
v.visitField(modifiers, p.getName(),
mapper.mapType(descriptor.getOutType()).getDescriptor(),
null, value);
v.visitField(modifiers, p.getName(), mapper.mapType(descriptor.getOutType()).getDescriptor(), null, value);
}
final JetPropertyAccessor getter = p.getGetter();
if (getter != null) {
functionCodegen.generateMethod(getter, kind, mapper.mapGetterSignature(propertyDescriptor),
Collections.<ValueParameterDescriptor>emptyList());
}
else if (p.hasModifier(JetTokens.PUBLIC_KEYWORD)) {
generateDefaultGetter(p, kind);
}
final JetPropertyAccessor setter = p.getSetter();
if (setter != null) {
final PropertySetterDescriptor setterDescriptor = propertyDescriptor.getSetter();
@@ -79,9 +84,60 @@ public class PropertyCodegen {
functionCodegen.generateMethod(setter, kind, mapper.mapSetterSignature(propertyDescriptor),
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) {
return "get" + StringUtil.capitalizeWithJavaBeanConvention(propertyName);
}
@@ -29,6 +29,17 @@ public class PropertyGenTest extends CodegenTestCase {
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 {
loadText("private val x = 239");
final Class nsClass = generateNamespaceClass();