Merge remote branch 'origin/master'
This commit is contained in:
@@ -4,8 +4,11 @@ import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.PropertyDescriptor;
|
||||
import org.jetbrains.jet.lang.psi.*;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.commons.InstructionAdapter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@@ -22,6 +25,8 @@ public abstract class ClassBodyCodegen {
|
||||
protected final ClassVisitor v;
|
||||
protected final ClassContext context;
|
||||
|
||||
protected final List<CodeChunk> staticInitializerChunks = new ArrayList<CodeChunk>();
|
||||
|
||||
public ClassBodyCodegen(JetClassOrObject aClass, ClassContext context, ClassVisitor v, GenerationState state) {
|
||||
this.state = state;
|
||||
descriptor = state.getBindingContext().getClassDescriptor(aClass);
|
||||
@@ -38,6 +43,8 @@ public abstract class ClassBodyCodegen {
|
||||
|
||||
generateClassBody();
|
||||
|
||||
generateStaticInitializer();
|
||||
|
||||
v.visitEnd();
|
||||
}
|
||||
|
||||
@@ -95,4 +102,23 @@ public abstract class ClassBodyCodegen {
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private void generateStaticInitializer() {
|
||||
if (staticInitializerChunks.size() > 0) {
|
||||
final MethodVisitor mv = v.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC,
|
||||
"<clinit>", "()V", null, null);
|
||||
mv.visitCode();
|
||||
|
||||
InstructionAdapter v = new InstructionAdapter(mv);
|
||||
|
||||
for (CodeChunk chunk : staticInitializerChunks) {
|
||||
chunk.generate(v);
|
||||
}
|
||||
|
||||
mv.visitInsn(Opcodes.RETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
|
||||
mv.visitEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,13 +25,15 @@ public class ClassCodegen {
|
||||
else {
|
||||
generateInterface(parentContext, aClass);
|
||||
generateImplementation(parentContext, aClass, OwnerKind.IMPLEMENTATION);
|
||||
generateImplementation(parentContext, aClass, OwnerKind.DELEGATING_IMPLEMENTATION);
|
||||
if (!ImplementationBodyCodegen.isEnum(aClass)) {
|
||||
generateImplementation(parentContext, aClass, OwnerKind.DELEGATING_IMPLEMENTATION);
|
||||
}
|
||||
}
|
||||
|
||||
ClassDescriptor descriptor = state.getBindingContext().getClassDescriptor(aClass);
|
||||
final ClassContext contextForInners = parentContext.intoClass(descriptor, OwnerKind.IMPLEMENTATION);
|
||||
for (JetDeclaration declaration : aClass.getDeclarations()) {
|
||||
if (declaration instanceof JetClass) {
|
||||
if (declaration instanceof JetClass && !(declaration instanceof JetEnumEntry)) {
|
||||
generate(contextForInners, (JetClass) declaration);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.jetbrains.jet.codegen;
|
||||
|
||||
import org.objectweb.asm.commons.InstructionAdapter;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
public interface CodeChunk {
|
||||
void generate(InstructionAdapter v);
|
||||
}
|
||||
@@ -896,11 +896,11 @@ public class ExpressionCodegen extends JetVisitor {
|
||||
return funDescriptor;
|
||||
}
|
||||
|
||||
private void invokeMethodWithArguments(CallableMethod callableMethod, JetCallExpression expression) {
|
||||
public void invokeMethodWithArguments(CallableMethod callableMethod, JetCall expression) {
|
||||
invokeMethodWithArguments(callableMethod, expression, false);
|
||||
}
|
||||
|
||||
private void invokeMethodWithArguments(CallableMethod callableMethod, JetCallExpression expression, final boolean haveReceiver) {
|
||||
private void invokeMethodWithArguments(CallableMethod callableMethod, JetCall expression, final boolean haveReceiver) {
|
||||
if (callableMethod.isOwnerFromCall()) {
|
||||
setOwnerFromCall(callableMethod, expression);
|
||||
}
|
||||
@@ -914,7 +914,7 @@ public class ExpressionCodegen extends JetVisitor {
|
||||
callableMethod.invoke(v);
|
||||
}
|
||||
|
||||
private void setOwnerFromCall(CallableMethod callableMethod, JetCallExpression expression) {
|
||||
private void setOwnerFromCall(CallableMethod callableMethod, JetCall expression) {
|
||||
if (expression.getParent() instanceof JetQualifiedExpression) {
|
||||
final JetExpression receiver = ((JetQualifiedExpression) expression.getParent()).getReceiverExpression();
|
||||
JetType expressionType = bindingContext.getExpressionType(receiver);
|
||||
@@ -926,7 +926,7 @@ public class ExpressionCodegen extends JetVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
private JetExpression getReceiverForSelector(JetElement expression) {
|
||||
private static JetExpression getReceiverForSelector(PsiElement expression) {
|
||||
if (expression.getParent() instanceof JetDotQualifiedExpression && !isReceiver(expression)) {
|
||||
final JetDotQualifiedExpression parent = (JetDotQualifiedExpression) expression.getParent();
|
||||
return parent.getReceiverExpression();
|
||||
@@ -934,7 +934,7 @@ public class ExpressionCodegen extends JetVisitor {
|
||||
return null;
|
||||
}
|
||||
|
||||
private void ensureReceiverOnStack(JetElement expression, @Nullable ClassDescriptor calleeContainingClass) {
|
||||
private void ensureReceiverOnStack(PsiElement expression, @Nullable ClassDescriptor calleeContainingClass) {
|
||||
JetExpression receiver = getReceiverForSelector(expression);
|
||||
if (receiver != null) {
|
||||
if (!resolvesToClassOrPackage(receiver)) {
|
||||
@@ -1013,7 +1013,7 @@ public class ExpressionCodegen extends JetVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isReceiver(JetElement expression) {
|
||||
private static boolean isReceiver(PsiElement expression) {
|
||||
final PsiElement parent = expression.getParent();
|
||||
if (parent instanceof JetQualifiedExpression) {
|
||||
final JetExpression receiverExpression = ((JetQualifiedExpression) parent).getReceiverExpression();
|
||||
|
||||
@@ -74,20 +74,9 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
|
||||
@Override
|
||||
protected void generateSyntheticParts() {
|
||||
int typeinfoStatic = descriptor.getTypeConstructor().getParameters().size() > 0 ? 0 : Opcodes.ACC_STATIC;
|
||||
v.visitField(Opcodes.ACC_PRIVATE | typeinfoStatic, "$typeInfo", "Ljet/typeinfo/TypeInfo;", null, null);
|
||||
|
||||
if (isNonLiteralObject()) {
|
||||
Type type = JetTypeMapper.jetImplementationType(descriptor);
|
||||
v.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "$instance", type.getDescriptor(), null, null);
|
||||
}
|
||||
final JetClassObject classObject = getClassObject();
|
||||
if (classObject != null) {
|
||||
Type type = Type.getObjectType(state.getTypeMapper().jvmName(classObject));
|
||||
v.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "$classobj", type.getDescriptor(), null, null);
|
||||
}
|
||||
|
||||
generateStaticInitializer();
|
||||
generateFieldForTypeInfo();
|
||||
generateFieldForObjectInstance();
|
||||
generateFieldForClassObject();
|
||||
|
||||
try {
|
||||
generatePrimaryConstructor();
|
||||
@@ -99,13 +88,69 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
generateGetTypeInfo();
|
||||
}
|
||||
|
||||
private void generateFieldForTypeInfo() {
|
||||
final boolean typeInfoIsStatic = descriptor.getTypeConstructor().getParameters().size() == 0;
|
||||
v.visitField(Opcodes.ACC_PRIVATE | (typeInfoIsStatic ? Opcodes.ACC_STATIC : 0), "$typeInfo",
|
||||
"Ljet/typeinfo/TypeInfo;", null, null);
|
||||
if (typeInfoIsStatic) {
|
||||
staticInitializerChunks.add(new CodeChunk() {
|
||||
@Override
|
||||
public void generate(InstructionAdapter v) {
|
||||
JetTypeMapper typeMapper = state.getTypeMapper();
|
||||
ClassCodegen.newTypeInfo(v, false, typeMapper.jvmType(descriptor, OwnerKind.INTERFACE));
|
||||
v.putstatic(typeMapper.jvmName(descriptor, kind), "$typeInfo", "Ljet/typeinfo/TypeInfo;");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void generateFieldForObjectInstance() {
|
||||
if (isNonLiteralObject()) {
|
||||
Type type = JetTypeMapper.jetImplementationType(descriptor);
|
||||
v.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "$instance", type.getDescriptor(), null, null);
|
||||
|
||||
staticInitializerChunks.add(new CodeChunk() {
|
||||
@Override
|
||||
public void generate(InstructionAdapter v) {
|
||||
String name = jvmName();
|
||||
v.anew(Type.getObjectType(name));
|
||||
v.dup();
|
||||
v.invokespecial(name, "<init>", "()V");
|
||||
v.putstatic(name, "$instance", JetTypeMapper.jetImplementationType(descriptor).getDescriptor());
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void generateFieldForClassObject() {
|
||||
final JetClassObject classObject = getClassObject();
|
||||
if (classObject != null) {
|
||||
Type type = Type.getObjectType(state.getTypeMapper().jvmName(classObject));
|
||||
v.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "$classobj", type.getDescriptor(), null, null);
|
||||
|
||||
staticInitializerChunks.add(new CodeChunk() {
|
||||
@Override
|
||||
public void generate(InstructionAdapter v) {
|
||||
String name = state.getTypeMapper().jvmName(classObject);
|
||||
final Type classObjectType = Type.getObjectType(name);
|
||||
v.anew(classObjectType);
|
||||
v.dup();
|
||||
v.invokespecial(name, "<init>", "()V");
|
||||
v.putstatic(state.getTypeMapper().jvmName(descriptor, OwnerKind.IMPLEMENTATION), "$classobj",
|
||||
classObjectType.getDescriptor());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected void generatePrimaryConstructor() {
|
||||
ConstructorDescriptor constructorDescriptor = state.getBindingContext().getConstructorDescriptor((JetElement) myClass);
|
||||
if (constructorDescriptor == null && !(myClass instanceof JetObjectDeclaration)) return;
|
||||
if (constructorDescriptor == null && !(myClass instanceof JetObjectDeclaration) && !isEnum(myClass)) return;
|
||||
|
||||
Method method;
|
||||
CallableMethod callableMethod;
|
||||
if (myClass instanceof JetObjectDeclaration) {
|
||||
if (constructorDescriptor == null) {
|
||||
method = new Method("<init>", Type.VOID_TYPE, new Type[0]);
|
||||
callableMethod = new CallableMethod("", method, Opcodes.INVOKESPECIAL, Collections.<Type>emptyList());
|
||||
}
|
||||
@@ -233,6 +278,11 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
static boolean isEnum(JetClassOrObject myClass) {
|
||||
final JetModifierList modifierList = myClass.getModifierList();
|
||||
return modifierList != null && modifierList.hasModifier(JetTokens.ENUM_KEYWORD);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private ClassDescriptor getOuterClassDescriptor() {
|
||||
if (myClass.getParent() instanceof JetClassObject) {
|
||||
@@ -280,13 +330,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
}
|
||||
|
||||
CallableMethod method = state.getTypeMapper().mapToCallableMethod(constructorDescriptor, kind);
|
||||
List<JetArgument> args = constructorCall.getValueArguments();
|
||||
for (int i = 0, argsSize = args.size(); i < argsSize; i++) {
|
||||
JetArgument arg = args.get(i);
|
||||
codegen.gen(arg.getArgumentExpression(), method.getValueParameterTypes().get(i));
|
||||
}
|
||||
|
||||
method.invoke(iv);
|
||||
codegen.invokeMethodWithArguments(method, constructorCall);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -409,47 +453,6 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
}
|
||||
}
|
||||
|
||||
private void generateStaticInitializer() {
|
||||
boolean needTypeInfo = descriptor.getTypeConstructor().getParameters().size() == 0;
|
||||
boolean needInstance = isNonLiteralObject();
|
||||
JetClassObject classObject = getClassObject();
|
||||
if (!needTypeInfo && !needInstance && classObject == null) {
|
||||
return;
|
||||
}
|
||||
final MethodVisitor mv = v.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC,
|
||||
"<clinit>", "()V", null, null);
|
||||
mv.visitCode();
|
||||
|
||||
InstructionAdapter v = new InstructionAdapter(mv);
|
||||
|
||||
if (needTypeInfo) {
|
||||
JetTypeMapper typeMapper = state.getTypeMapper();
|
||||
ClassCodegen.newTypeInfo(v, false, typeMapper.jvmType(descriptor, OwnerKind.INTERFACE));
|
||||
v.putstatic(typeMapper.jvmName(descriptor, kind), "$typeInfo", "Ljet/typeinfo/TypeInfo;");
|
||||
}
|
||||
if (needInstance) {
|
||||
String name = jvmName();
|
||||
v.anew(Type.getObjectType(name));
|
||||
v.dup();
|
||||
v.invokespecial(name, "<init>", "()V");
|
||||
v.putstatic(name, "$instance", JetTypeMapper.jetImplementationType(descriptor).getDescriptor());
|
||||
}
|
||||
if (classObject != null) {
|
||||
String name = state.getTypeMapper().jvmName(classObject);
|
||||
final Type classObjectType = Type.getObjectType(name);
|
||||
v.anew(classObjectType);
|
||||
v.dup();
|
||||
v.invokespecial(name, "<init>", "()V");
|
||||
v.putstatic(state.getTypeMapper().jvmName(descriptor, OwnerKind.IMPLEMENTATION), "$classobj",
|
||||
classObjectType.getDescriptor());
|
||||
}
|
||||
|
||||
mv.visitInsn(Opcodes.RETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private JetClassObject getClassObject() {
|
||||
return myClass instanceof JetClass ? ((JetClass) myClass).getClassObject() : null;
|
||||
|
||||
@@ -3,13 +3,16 @@ package org.jetbrains.jet.codegen;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
|
||||
import org.jetbrains.jet.lang.psi.JetClassOrObject;
|
||||
import org.jetbrains.jet.lang.psi.JetDelegationSpecifier;
|
||||
import org.jetbrains.jet.lang.descriptors.ConstructorDescriptor;
|
||||
import org.jetbrains.jet.lang.psi.*;
|
||||
import org.jetbrains.jet.lang.resolve.BindingContext;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.commons.InstructionAdapter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@@ -18,6 +21,8 @@ import java.util.Set;
|
||||
* @author yole
|
||||
*/
|
||||
public class InterfaceBodyCodegen extends ClassBodyCodegen {
|
||||
private final List<JetEnumEntry> myEnumConstants = new ArrayList<JetEnumEntry>();
|
||||
|
||||
public InterfaceBodyCodegen(JetClassOrObject aClass, ClassContext context, ClassVisitor v, GenerationState state) {
|
||||
super(aClass, context, v, state);
|
||||
assert context.getContextKind() == OwnerKind.INTERFACE;
|
||||
@@ -73,4 +78,59 @@ public class InterfaceBodyCodegen extends ClassBodyCodegen {
|
||||
}
|
||||
return superInterfaces;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void generateDeclaration(PropertyCodegen propertyCodegen, JetDeclaration declaration, FunctionCodegen functionCodegen) {
|
||||
if (declaration instanceof JetEnumEntry && !((JetEnumEntry) declaration).hasPrimaryConstructor()) {
|
||||
String name = declaration.getName();
|
||||
final String desc = "L" + state.getTypeMapper().jvmName(descriptor, OwnerKind.INTERFACE) + ";";
|
||||
v.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL, name, desc, null, null);
|
||||
if (myEnumConstants.isEmpty()) {
|
||||
staticInitializerChunks.add(new CodeChunk() {
|
||||
@Override
|
||||
public void generate(InstructionAdapter v) {
|
||||
initializeEnumConstants(v);
|
||||
}
|
||||
});
|
||||
}
|
||||
myEnumConstants.add((JetEnumEntry) declaration);
|
||||
}
|
||||
else {
|
||||
super.generateDeclaration(propertyCodegen, declaration, functionCodegen);
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeEnumConstants(InstructionAdapter v) {
|
||||
ExpressionCodegen codegen = new ExpressionCodegen(v, new FrameMap(), Type.VOID_TYPE, context, state);
|
||||
for (JetEnumEntry enumConstant : myEnumConstants) {
|
||||
// TODO type and constructor parameters
|
||||
String intfClass = state.getTypeMapper().jvmName(descriptor, OwnerKind.INTERFACE);
|
||||
String implClass = state.getTypeMapper().jvmName(descriptor, OwnerKind.IMPLEMENTATION);
|
||||
|
||||
final List<JetDelegationSpecifier> delegationSpecifiers = enumConstant.getDelegationSpecifiers();
|
||||
if (delegationSpecifiers.size() > 1) {
|
||||
throw new UnsupportedOperationException("multiple delegation specifiers for enum constant not supported");
|
||||
}
|
||||
|
||||
v.anew(Type.getObjectType(implClass));
|
||||
v.dup();
|
||||
|
||||
if (delegationSpecifiers.size() == 1) {
|
||||
final JetDelegationSpecifier specifier = delegationSpecifiers.get(0);
|
||||
if (specifier instanceof JetDelegatorToSuperCall) {
|
||||
final JetDelegatorToSuperCall superCall = (JetDelegatorToSuperCall) specifier;
|
||||
ConstructorDescriptor constructorDescriptor = state.getBindingContext().resolveSuperConstructor(superCall);
|
||||
CallableMethod method = state.getTypeMapper().mapToCallableMethod(constructorDescriptor, OwnerKind.IMPLEMENTATION);
|
||||
codegen.invokeMethodWithArguments(method, superCall);
|
||||
}
|
||||
else {
|
||||
throw new UnsupportedOperationException("unsupported type of enum constant initializer: " + specifier);
|
||||
}
|
||||
}
|
||||
else {
|
||||
v.invokespecial(implClass, "<init>", "()V");
|
||||
}
|
||||
v.putstatic(intfClass, enumConstant.getName(), "L" + intfClass + ";");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 abreslav
|
||||
*/
|
||||
public interface JetCall {
|
||||
public interface JetCall extends PsiElement {
|
||||
@Nullable
|
||||
JetArgumentList getValueArgumentList();
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.jetbrains.jet.codegen;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.List;
|
||||
@@ -130,4 +131,21 @@ public class ClassGenTest extends CodegenTestCase {
|
||||
Object result = method.invoke(null);
|
||||
assertInstanceOf(result, Runnable.class);
|
||||
}
|
||||
|
||||
public void testEnumClass() throws Exception {
|
||||
loadText("enum class Direction { NORTH; SOUTH; EAST; WEST }");
|
||||
final Class direction = loadAllClasses(generateClassesInFile()).get("Direction");
|
||||
final Field north = direction.getField("NORTH");
|
||||
assertEquals(direction, north.getType());
|
||||
assertInstanceOf(north.get(null), direction);
|
||||
}
|
||||
|
||||
public void testEnumConstantConstructors() throws Exception {
|
||||
loadText("enum class Color(val rgb: Int) { RED: Color(0xFF0000); GREEN: Color(0x00FF00); }");
|
||||
final Class colorClass = loadAllClasses(generateClassesInFile()).get("Color");
|
||||
final Field redField = colorClass.getField("RED");
|
||||
final Object redValue = redField.get(null);
|
||||
final Method rgbMethod = colorClass.getMethod("getRgb");
|
||||
assertEquals(0xFF0000, rgbMethod.invoke(redValue));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user