namespace properties are mostly working
This commit is contained in:
@@ -11,6 +11,7 @@ import org.jetbrains.jet.lang.types.JetType;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@@ -151,8 +152,9 @@ public class ClassCodegen {
|
||||
}
|
||||
|
||||
private void generateClassBody(JetClass aClass, ClassVisitor v, OwnerKind kind) {
|
||||
final PropertyCodegen propertyCodegen = new PropertyCodegen(v);
|
||||
final FunctionCodegen functionCodegen = new FunctionCodegen(v, JetStandardLibrary.getJetStandardLibrary(project), bindingContext);
|
||||
final JetStandardLibrary standardLibrary = JetStandardLibrary.getJetStandardLibrary(project);
|
||||
final FunctionCodegen functionCodegen = new FunctionCodegen(v, standardLibrary, bindingContext);
|
||||
final PropertyCodegen propertyCodegen = new PropertyCodegen(v, standardLibrary, bindingContext, functionCodegen);
|
||||
|
||||
for (JetDeclaration declaration : aClass.getDeclarations()) {
|
||||
if (declaration instanceof JetProperty) {
|
||||
|
||||
@@ -370,6 +370,28 @@ public class ExpressionCodegen extends JetVisitor {
|
||||
final JetType outType = ((VariableDescriptor) descriptor).getOutType();
|
||||
myStack.push(StackValue.local(index, typeMapper.mapType(outType)));
|
||||
}
|
||||
else if (descriptor instanceof PropertyDescriptor) {
|
||||
final PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
|
||||
if (descriptor.getContainingDeclaration() instanceof NamespaceDescriptor) {
|
||||
JetNamespace ns = (JetNamespace) bindingContext.getDeclarationPsiElement(descriptor.getContainingDeclaration());
|
||||
String owner = JetTypeMapper.jvmName(ns);
|
||||
final JetType outType = ((VariableDescriptor) descriptor).getOutType();
|
||||
Method getter;
|
||||
Method setter;
|
||||
if (expression.getReferencedNameElementType() == JetTokens.FIELD_IDENTIFIER) {
|
||||
getter = null;
|
||||
setter = null;
|
||||
}
|
||||
else {
|
||||
getter = typeMapper.mapGetterSignature(propertyDescriptor);
|
||||
setter = typeMapper.mapSetterSignature(propertyDescriptor);
|
||||
}
|
||||
myStack.push(StackValue.property(descriptor.getName(), owner, typeMapper.mapType(outType), getter, setter));
|
||||
}
|
||||
else {
|
||||
throw new UnsupportedOperationException("don't know how to generate non-namespace property reference " + descriptor);
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new UnsupportedOperationException("don't know how to generate reference " + descriptor);
|
||||
}
|
||||
@@ -887,10 +909,7 @@ public class ExpressionCodegen extends JetVisitor {
|
||||
int increment = op.getName().equals("inc") ? 1 : -1;
|
||||
if (operand instanceof JetReferenceExpression) {
|
||||
final int index = indexOfLocal((JetReferenceExpression) operand);
|
||||
if (index < 0) {
|
||||
throw new UnsupportedOperationException("don't know how to increment or decrement something which is not a local var");
|
||||
}
|
||||
if (isIntPrimitive(asmType)) {
|
||||
if (index >= 0 && isIntPrimitive(asmType)) {
|
||||
v.iinc(index, increment);
|
||||
return StackValue.local(index, asmType);
|
||||
}
|
||||
|
||||
@@ -44,6 +44,12 @@ public class FunctionCodegen {
|
||||
}
|
||||
|
||||
public void gen(JetFunction f, OwnerKind kind) {
|
||||
Method method = typeMapper.mapSignature(f);
|
||||
List<ValueParameterDescriptor> paramDescrs = bindingContext.getFunctionDescriptor(f).getUnsubstitutedValueParameters();
|
||||
generateMethod(f, kind, method, paramDescrs);
|
||||
}
|
||||
|
||||
public void generateMethod(JetDeclarationWithBody f, OwnerKind kind, Method jvmSignature, List<ValueParameterDescriptor> paramDescrs) {
|
||||
int flags = Opcodes.ACC_PUBLIC; // TODO.
|
||||
|
||||
boolean isStatic = kind == OwnerKind.NAMESPACE;
|
||||
@@ -53,8 +59,7 @@ public class FunctionCodegen {
|
||||
boolean isAbstract = kind == OwnerKind.INTERFACE || bodyExpression == null;
|
||||
if (isAbstract) flags |= Opcodes.ACC_ABSTRACT;
|
||||
|
||||
Method method = typeMapper.mapSignature(f);
|
||||
final MethodVisitor mv = v.visitMethod(flags, method.getName(), method.getDescriptor(), null, null);
|
||||
final MethodVisitor mv = v.visitMethod(flags, jvmSignature.getName(), jvmSignature.getDescriptor(), null, null);
|
||||
if (kind != OwnerKind.INTERFACE) {
|
||||
mv.visitCode();
|
||||
FrameMap frameMap = new FrameMap();
|
||||
@@ -63,15 +68,13 @@ public class FunctionCodegen {
|
||||
frameMap.enterTemp(); // 0 slot for this
|
||||
}
|
||||
|
||||
List<ValueParameterDescriptor> parameDescrs = bindingContext.getFunctionDescriptor(f).getUnsubstitutedValueParameters();
|
||||
|
||||
Type[] argTypes = method.getArgumentTypes();
|
||||
for (int i = 0; i < parameDescrs.size(); i++) {
|
||||
ValueParameterDescriptor parameter = parameDescrs.get(i);
|
||||
Type[] argTypes = jvmSignature.getArgumentTypes();
|
||||
for (int i = 0; i < paramDescrs.size(); i++) {
|
||||
ValueParameterDescriptor parameter = paramDescrs.get(i);
|
||||
frameMap.enter(parameter, argTypes[i].getSize());
|
||||
}
|
||||
|
||||
ExpressionCodegen codegen = new ExpressionCodegen(mv, bindingContext, frameMap, typeMapper, method.getReturnType());
|
||||
ExpressionCodegen codegen = new ExpressionCodegen(mv, bindingContext, frameMap, typeMapper, jvmSignature.getReturnType());
|
||||
bodyExpression.accept(codegen);
|
||||
generateReturn(mv, bodyExpression, codegen);
|
||||
mv.visitMaxs(0, 0);
|
||||
|
||||
@@ -2,7 +2,9 @@ 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.resolve.BindingContext;
|
||||
@@ -32,6 +34,10 @@ public class JetTypeMapper {
|
||||
return Type.getType("L" + jvmName(psiClass) + ";");
|
||||
}
|
||||
|
||||
static String jvmName(JetNamespace namespace) {
|
||||
return NamespaceCodegen.getJVMClassName(namespace.getFQName());
|
||||
}
|
||||
|
||||
public Type mapType(final JetType jetType) {
|
||||
if (jetType.equals(JetStandardClasses.getUnitType())) {
|
||||
return Type.VOID_TYPE;
|
||||
@@ -130,4 +136,22 @@ 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 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,8 +35,9 @@ public class NamespaceCodegen {
|
||||
public void generate(JetNamespace namespace) {
|
||||
BindingContext bindingContext = AnalyzingUtils.analyzeNamespace(namespace, ErrorHandler.THROW_EXCEPTION);
|
||||
|
||||
final PropertyCodegen propertyCodegen = new PropertyCodegen(v);
|
||||
final FunctionCodegen functionCodegen = new FunctionCodegen(v, JetStandardLibrary.getJetStandardLibrary(project), bindingContext);
|
||||
final JetStandardLibrary standardLibrary = JetStandardLibrary.getJetStandardLibrary(project);
|
||||
final FunctionCodegen functionCodegen = new FunctionCodegen(v, standardLibrary, bindingContext);
|
||||
final PropertyCodegen propertyCodegen = new PropertyCodegen(v, standardLibrary, bindingContext, functionCodegen);
|
||||
final ClassCodegen classCodegen = codegens.forClass(bindingContext);
|
||||
|
||||
for (JetDeclaration declaration : namespace.getDeclarations()) {
|
||||
|
||||
@@ -1,16 +1,31 @@
|
||||
package org.jetbrains.jet.codegen;
|
||||
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import org.jetbrains.jet.lang.psi.JetConstantExpression;
|
||||
import org.jetbrains.jet.lang.psi.JetExpression;
|
||||
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.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* @author max
|
||||
*/
|
||||
public class PropertyCodegen {
|
||||
private final BindingContext context;
|
||||
private final FunctionCodegen functionCodegen;
|
||||
private final ClassVisitor v;
|
||||
private final JetTypeMapper mapper;
|
||||
|
||||
public PropertyCodegen(ClassVisitor v) {
|
||||
public PropertyCodegen(ClassVisitor v, JetStandardLibrary standardLibrary, BindingContext context, FunctionCodegen functionCodegen) {
|
||||
this.v = v;
|
||||
this.context = context;
|
||||
this.functionCodegen = functionCodegen;
|
||||
this.mapper = new JetTypeMapper(standardLibrary, context);
|
||||
}
|
||||
|
||||
public void genInNamespace(JetProperty p) {
|
||||
@@ -30,6 +45,45 @@ public class PropertyCodegen {
|
||||
}
|
||||
|
||||
public void gen(JetProperty p, OwnerKind kind) {
|
||||
if (kind == OwnerKind.NAMESPACE) {
|
||||
final VariableDescriptor descriptor = context.getVariableDescriptor(p);
|
||||
if (!(descriptor instanceof PropertyDescriptor)) {
|
||||
throw new UnsupportedOperationException("expect a property to have a property descriptor");
|
||||
}
|
||||
final PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
|
||||
if (context.hasBackingField(propertyDescriptor)) {
|
||||
Object value = null;
|
||||
final JetExpression initializer = p.getInitializer();
|
||||
if (initializer != null) {
|
||||
if (initializer instanceof JetConstantExpression) {
|
||||
value = ((JetConstantExpression) initializer).getValue();
|
||||
}
|
||||
}
|
||||
v.visitField(Opcodes.ACC_STATIC | Opcodes.ACC_PRIVATE,
|
||||
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());
|
||||
}
|
||||
final JetPropertyAccessor setter = p.getSetter();
|
||||
if (setter != null) {
|
||||
final PropertySetterDescriptor setterDescriptor = propertyDescriptor.getSetter();
|
||||
assert setterDescriptor != null;
|
||||
functionCodegen.generateMethod(setter, kind, mapper.mapSetterSignature(propertyDescriptor),
|
||||
setterDescriptor.getUnsubstitutedValueParameters());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String getterName(String propertyName) {
|
||||
return "get" + StringUtil.capitalizeWithJavaBeanConvention(propertyName);
|
||||
}
|
||||
|
||||
public static String setterName(String propertyName) {
|
||||
return "set" + StringUtil.capitalizeWithJavaBeanConvention(propertyName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.commons.InstructionAdapter;
|
||||
import org.objectweb.asm.commons.Method;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
@@ -70,6 +71,10 @@ public abstract class StackValue {
|
||||
return new Field(type, owner, name, isStatic);
|
||||
}
|
||||
|
||||
public static StackValue property(String name, String owner, Type type, Method getter, Method setter) {
|
||||
return new Property(name, owner, getter, setter, type);
|
||||
}
|
||||
|
||||
private static void box(final Type type, InstructionAdapter v) {
|
||||
if (type == Type.INT_TYPE) {
|
||||
v.invokestatic("java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;");
|
||||
@@ -368,4 +373,39 @@ public abstract class StackValue {
|
||||
v.visitFieldInsn(isStatic ? Opcodes.PUTSTATIC : Opcodes.PUTFIELD, owner, name, this.type.getDescriptor());
|
||||
}
|
||||
}
|
||||
|
||||
private static class Property extends StackValue {
|
||||
private final String name;
|
||||
private final String owner;
|
||||
private final Method getter;
|
||||
private final Method setter;
|
||||
|
||||
public Property(String name, String owner, Method getter, Method setter, Type type) {
|
||||
super(type);
|
||||
this.name = name;
|
||||
this.owner = owner;
|
||||
this.getter = getter;
|
||||
this.setter = setter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(Type type, InstructionAdapter v) {
|
||||
if (getter == null) {
|
||||
v.visitFieldInsn(Opcodes.GETSTATIC, owner, name, type.getDescriptor());
|
||||
}
|
||||
else {
|
||||
v.invokestatic(owner, getter.getName(), getter.getDescriptor());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void store(InstructionAdapter v) {
|
||||
if (setter == null) {
|
||||
v.visitFieldInsn(Opcodes.PUTSTATIC, owner, name, type.getDescriptor());
|
||||
}
|
||||
else {
|
||||
v.invokestatic(owner, setter.getName(), setter.getDescriptor());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
private var x = 0;
|
||||
|
||||
fun increment(): Int {
|
||||
return ++x;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
var collector: String = ""
|
||||
set(it) { $collector = $collector + it }
|
||||
|
||||
fun append(s: String): String {
|
||||
collector = s;
|
||||
return collector;
|
||||
}
|
||||
@@ -42,7 +42,7 @@ public abstract class CodegenTestCase extends LightCodeInsightFixtureTestCase {
|
||||
return answer.toString();
|
||||
}
|
||||
|
||||
private Class generateNamespaceClass() {
|
||||
protected Class generateNamespaceClass() {
|
||||
JetFile jetFile = (JetFile) myFixture.getFile();
|
||||
final JetNamespace namespace = jetFile.getRootNamespace();
|
||||
String fqName = NamespaceCodegen.getJVMClassName(namespace.getFQName()).replace("/", ".");
|
||||
@@ -89,6 +89,11 @@ public abstract class CodegenTestCase extends LightCodeInsightFixtureTestCase {
|
||||
throw new IllegalArgumentException("couldn't find method " + name);
|
||||
}
|
||||
|
||||
protected void assertIsCurrentTime(long returnValue) {
|
||||
long currentTime = System.currentTimeMillis();
|
||||
assertTrue(Math.abs(returnValue - currentTime) <= 1L);
|
||||
}
|
||||
|
||||
private static class MyClassLoader extends ClassLoader {
|
||||
public MyClassLoader(ClassLoader parent) {
|
||||
super(parent);
|
||||
|
||||
@@ -56,8 +56,7 @@ public class NamespaceGenTest extends CodegenTestCase {
|
||||
System.out.println(generateToText());
|
||||
final Method main = generateFunction();
|
||||
final long returnValue = (Long) main.invoke(null);
|
||||
long currentTime = System.currentTimeMillis();
|
||||
assertTrue(Math.abs(returnValue - currentTime) <= 1L);
|
||||
assertIsCurrentTime(returnValue);
|
||||
}
|
||||
|
||||
public void testIdentityHashCode() throws Exception {
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package org.jetbrains.jet.codegen;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
@@ -8,6 +12,39 @@ public class PropertyGenTest extends CodegenTestCase {
|
||||
loadFile("privateVal.jet");
|
||||
System.out.println(generateToText());
|
||||
// TODO
|
||||
}
|
||||
|
||||
public void testPropertyInNamespace() throws Exception {
|
||||
loadText("private val x = 239");
|
||||
final Class nsClass = generateNamespaceClass();
|
||||
final Field[] fields = nsClass.getDeclaredFields();
|
||||
assertEquals(1, fields.length);
|
||||
final Field field = fields[0];
|
||||
field.setAccessible(true);
|
||||
assertEquals("x", field.getName());
|
||||
assertEquals(Modifier.PRIVATE | Modifier.STATIC, field.getModifiers());
|
||||
assertEquals(239, field.get(null));
|
||||
}
|
||||
|
||||
public void testFieldPropertyAccess() throws Exception {
|
||||
loadFile("fieldPropertyAccess.jet");
|
||||
final Method method = generateFunction();
|
||||
assertEquals(1, method.invoke(null));
|
||||
assertEquals(2, method.invoke(null));
|
||||
}
|
||||
|
||||
public void testFieldGetter() throws Exception {
|
||||
loadText("val now: Long get() = System.currentTimeMillis(); fun foo() = now");
|
||||
final Method method = generateFunction("foo");
|
||||
assertIsCurrentTime((Long) method.invoke(null));
|
||||
}
|
||||
|
||||
public void testFieldSetter() throws Exception {
|
||||
loadFile("fieldSetter.jet");
|
||||
System.out.println(generateToText());
|
||||
final Method method = generateFunction("append");
|
||||
method.invoke(null, "IntelliJ ");
|
||||
String value = (String) method.invoke(null, "IDEA");
|
||||
assertEquals(value, "IntelliJ IDEA");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user