generate public accessor methods for public properties
This commit is contained in:
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user