From 60ab760d4a8715ef794930a63e1b2c4451670998 Mon Sep 17 00:00:00 2001 From: Alex Tkachman Date: Mon, 12 Sep 2011 09:26:50 +0300 Subject: [PATCH] 1st test for traits work --- .idea/runConfigurations/All_Tests.xml | 6 ++ .../jet/codegen/ClassBodyCodegen.java | 8 ++- .../jetbrains/jet/codegen/ClassCodegen.java | 6 ++ .../jet/codegen/ExpressionCodegen.java | 4 +- .../jet/codegen/FunctionCodegen.java | 6 +- .../jet/codegen/GenerationState.java | 4 +- .../codegen/ImplementationBodyCodegen.java | 68 +++++++++++++++++-- .../jetbrains/jet/codegen/JetTypeMapper.java | 29 +++++--- .../org/jetbrains/jet/codegen/OwnerKind.java | 2 +- .../jet/codegen/PropertyCodegen.java | 18 ----- .../jet/codegen/TraitImplBodyCodegen.java | 39 +++++++++++ .../jet/codegen/intrinsics/PsiMethodCall.java | 2 +- idea/testData/codegen/traits/simple.jet | 12 ++++ .../org/jetbrains/jet/codegen/TraitsTest.java | 13 ++++ 14 files changed, 174 insertions(+), 43 deletions(-) create mode 100644 compiler/backend/src/org/jetbrains/jet/codegen/TraitImplBodyCodegen.java create mode 100644 idea/testData/codegen/traits/simple.jet create mode 100644 idea/tests/org/jetbrains/jet/codegen/TraitsTest.java diff --git a/.idea/runConfigurations/All_Tests.xml b/.idea/runConfigurations/All_Tests.xml index 6ebba66deda..85e2275839b 100644 --- a/.idea/runConfigurations/All_Tests.xml +++ b/.idea/runConfigurations/All_Tests.xml @@ -23,10 +23,16 @@ + + + diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ClassBodyCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ClassBodyCodegen.java index fd4e66256a3..6ffc688431f 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ClassBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ClassBodyCodegen.java @@ -71,13 +71,17 @@ public abstract class ClassBodyCodegen { } else if (declaration instanceof JetNamedFunction) { try { - functionCodegen.gen((JetNamedFunction) declaration); + genNamedFunction((JetNamedFunction) declaration, functionCodegen); } catch (RuntimeException e) { throw new RuntimeException("Error generating method " + myClass.getName() + "." + declaration.getName() + " in " + context, e); } } } + protected void genNamedFunction(JetNamedFunction declaration, FunctionCodegen functionCodegen) { + functionCodegen.gen(declaration); + } + private void generatePrimaryConstructorProperties(PropertyCodegen propertyCodegen) { OwnerKind kind = context.getContextKind(); for (JetParameter p : getPrimaryConstructorParameters()) { @@ -90,7 +94,7 @@ public abstract class ClassBodyCodegen { } //noinspection ConstantConditions - if (!(kind instanceof OwnerKind.DelegateKind) && kind != OwnerKind.INTERFACE && state.getBindingContext().get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor)) { + if (!(kind instanceof OwnerKind.DelegateKind) && state.getBindingContext().get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor)) { v.visitField(Opcodes.ACC_PRIVATE, p.getName(), state.getTypeMapper().mapType(propertyDescriptor.getOutType()).getDescriptor(), null, null); } } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ClassCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ClassCodegen.java index 5c117ce1962..1933acff885 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ClassCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ClassCodegen.java @@ -9,6 +9,7 @@ import org.objectweb.asm.commons.InstructionAdapter; /** * @author max + * @author alex.tkachman */ public class ClassCodegen { private final GenerationState state; @@ -35,6 +36,11 @@ public class ClassCodegen { ClassDescriptor descriptor = state.getBindingContext().get(BindingContext.CLASS, aClass); ClassVisitor v = state.forClassImplementation(descriptor); new ImplementationBodyCodegen(aClass, parentContext.intoClass(descriptor, kind), v, state).generate(); + + if(aClass instanceof JetClass && ((JetClass)aClass).isTrait()) { + v = state.forTraitImplementation(descriptor); + new TraitImplBodyCodegen(aClass, parentContext.intoClass(descriptor, OwnerKind.TRAIT_IMPL), v, state).generate(); + } } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java index 3bf85426225..df479228c83 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java @@ -914,7 +914,7 @@ public class ExpressionCodegen extends JetVisitor { CallableMethod callableMethod; if (declarationPsiElement instanceof PsiMethod || declarationPsiElement instanceof JetNamedFunction) { - callableMethod = typeMapper.mapToCallableMethod((PsiNamedElement) declarationPsiElement); + callableMethod = typeMapper.mapToCallableMethod((PsiNamedElement) declarationPsiElement, null); } else if (fd instanceof FunctionDescriptor) { callableMethod = ClosureCodegen.asCallableMethod((FunctionDescriptor) fd); @@ -1708,7 +1708,7 @@ public class ExpressionCodegen extends JetVisitor { accessor = JetTypeMapper.mapToCallableMethod((PsiMethod) declaration); } else if (declaration instanceof JetNamedFunction) { - accessor = typeMapper.mapToCallableMethod((JetNamedFunction) declaration); + accessor = typeMapper.mapToCallableMethod((JetNamedFunction) declaration, null); } else { throw new UnsupportedOperationException("unknown accessor type: " + declaration); diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java index 3c7e3abe22a..b42ac8f0e48 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java @@ -35,7 +35,7 @@ public class FunctionCodegen { } public void gen(JetNamedFunction f) { - Method method = state.getTypeMapper().mapToCallableMethod(f).getSignature(); + Method method = state.getTypeMapper().mapToCallableMethod(f, owner.getContextKind()).getSignature(); final FunctionDescriptor functionDescriptor = state.getBindingContext().get(BindingContext.FUNCTION, f); generateMethod(f, method, functionDescriptor); } @@ -86,10 +86,10 @@ public class FunctionCodegen { OwnerKind kind = context.getContextKind(); - boolean isStatic = kind == OwnerKind.NAMESPACE; + boolean isStatic = kind == OwnerKind.NAMESPACE || kind == OwnerKind.TRAIT_IMPL; if (isStatic) flags |= Opcodes.ACC_STATIC; - boolean isAbstract = bodyExpressions == null; + boolean isAbstract = !isStatic && (bodyExpressions == null || TypeUtils.isInterface(functionDescriptor.getContainingDeclaration(), state.getBindingContext())); if (isAbstract) flags |= Opcodes.ACC_ABSTRACT; final MethodVisitor mv = v.visitMethod(flags, jvmSignature.getName(), jvmSignature.getDescriptor(), null, null); diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/GenerationState.java b/compiler/backend/src/org/jetbrains/jet/codegen/GenerationState.java index c047f642d57..8e342f3ef52 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/GenerationState.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/GenerationState.java @@ -70,8 +70,8 @@ public class GenerationState { return factory.newVisitor(typeMapper.jvmName(aClass, OwnerKind.IMPLEMENTATION) + ".class"); } - public ClassVisitor forClassDelegatingImplementation(ClassDescriptor aClass) { - return factory.newVisitor(JetTypeMapper.jvmNameForDelegatingImplementation(aClass) + ".class"); + public ClassVisitor forTraitImplementation(ClassDescriptor aClass) { + return factory.newVisitor(typeMapper.jvmName(aClass, OwnerKind.TRAIT_IMPL) + ".class"); } public Pair forAnonymousSubclass(JetExpression expression) { diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java index a81e6c116da..e9f872046a6 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java @@ -7,6 +7,7 @@ import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant; +import org.jetbrains.jet.lang.types.JetStandardClasses; import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.Variance; import org.jetbrains.jet.lexer.JetTokens; @@ -215,7 +216,6 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { return; ConstructorDescriptor constructorDescriptor = state.getBindingContext().get(BindingContext.CONSTRUCTOR, myClass); -// if (constructorDescriptor == null && !(myClass instanceof JetObjectDeclaration) && !isEnum(myClass)) return; Method method; CallableMethod callableMethod; @@ -302,6 +302,8 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { generateInitializers(codegen, iv); + generateTraitMethods(codegen); + int curParam = 0; List constructorParameters = getPrimaryConstructorParameters(); for (JetParameter parameter : constructorParameters) { @@ -320,9 +322,67 @@ 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); + private void generateTraitMethods(ExpressionCodegen codegen) { + if(!(myClass instanceof JetClass) || ((JetClass)myClass).isTrait() || ((JetClass)myClass).hasModifier(JetTokens.ABSTRACT_KEYWORD)) + return; + + HashSet set = new HashSet(); + getAbstractMethods(descriptor.getDefaultType(), set); + for(FunctionDescriptor fun: set) { + int flags = Opcodes.ACC_PUBLIC; // TODO. + + Method function = state.getTypeMapper().mapSignature(fun.getName(), fun); + + final MethodVisitor mv = v.visitMethod(flags, function.getName(), function.getDescriptor(), null, null); + mv.visitCode(); + + codegen.generateThisOrOuter(descriptor); + + Type[] argTypes = function.getArgumentTypes(); + InstructionAdapter iv = new InstructionAdapter(mv); + iv.load(0, JetTypeMapper.TYPE_OBJECT); + for (int i = 0, reg = 1; i < argTypes.length; i++) { + Type argType = argTypes[i]; + iv.load(reg, argType); + //noinspection AssignmentToForLoopParameter + reg += argType.getSize(); + } + + String fdescriptor = function.getDescriptor().replace("(","(L" + state.getTypeMapper().jvmName((ClassDescriptor) fun.getContainingDeclaration(),OwnerKind.IMPLEMENTATION) + ";"); + iv.invokestatic(state.getTypeMapper().jvmName((ClassDescriptor) fun.getContainingDeclaration(), OwnerKind.TRAIT_IMPL), function.getName(), fdescriptor); + iv.areturn(function.getReturnType()); + mv.visitMaxs(0, 0); + mv.visitEnd(); + } + } + + private void getAbstractMethods(JetType type, Set set) { + if(type.equals(JetStandardClasses.getAny())) { + return; + } + + for(JetType superType : type.getConstructor().getSupertypes()) { + getAbstractMethods(superType, set); + } + + PsiElement psiElement = state.getBindingContext().get(BindingContext.DESCRIPTOR_TO_DECLARATION, type.getConstructor().getDeclarationDescriptor()); + if(psiElement instanceof JetClass) { + JetClass jetClass = (JetClass) psiElement; + for(JetDeclaration decl : jetClass.getDeclarations()) { + if(decl instanceof JetNamedFunction) { + JetNamedFunction jetNamedFunction = (JetNamedFunction) decl; + FunctionDescriptor funDescriptor = state.getBindingContext().get(BindingContext.FUNCTION, decl); + set.removeAll(funDescriptor.getOverriddenDescriptors()); + + if(jetNamedFunction.getBodyExpression() == null || jetClass.isTrait()) { + set.add(funDescriptor); + } + } + } + } + else if(psiElement instanceof PsiClass) { + PsiClass psiClass = (PsiClass) psiElement; + } } @Nullable diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/JetTypeMapper.java b/compiler/backend/src/org/jetbrains/jet/codegen/JetTypeMapper.java index 430458dfec5..5896a36e70b 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/JetTypeMapper.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/JetTypeMapper.java @@ -218,15 +218,15 @@ public class JetTypeMapper { public String jvmName(JetClassObject classObject) { final ClassDescriptor descriptor = bindingContext.get(BindingContext.CLASS, classObject.getObjectDeclaration()); - return jvmName(descriptor, OwnerKind.IMPLEMENTATION); + return jvmName(descriptor, OwnerKind.IMPLEMENTATION); } - public boolean isInterface(ClassDescriptor jetClass, OwnerKind kind) { + public boolean isInterface(ClassDescriptor jetClass) { PsiElement declaration = bindingContext.get(BindingContext.DESCRIPTOR_TO_DECLARATION, jetClass); if (declaration instanceof JetObjectDeclaration) { return false; } - return jetClass instanceof JetClass ? ((JetClass)jetClass).isTrait() : false; + return declaration instanceof JetClass && ((JetClass) declaration).isTrait(); } private static String jetJvmName(ClassDescriptor jetClass, OwnerKind kind) { @@ -236,6 +236,9 @@ public class JetTypeMapper { else if (kind == OwnerKind.IMPLEMENTATION) { return jvmNameForImplementation(jetClass); } + else if (kind == OwnerKind.TRAIT_IMPL) { + return jvmNameForTraitImpl(jetClass); + } else { assert false : "Unsuitable kind"; return "java/lang/Object"; @@ -277,8 +280,8 @@ public class JetTypeMapper { return jvmNameForInterface(descriptor); } - public static String jvmNameForDelegatingImplementation(ClassDescriptor descriptor) { - return jvmNameForInterface(descriptor) + "$$DImpl"; + private static String jvmNameForTraitImpl(ClassDescriptor descriptor) { + return jvmNameForInterface(descriptor) + "$$TImpl"; } public String getOwner(DeclarationDescriptor descriptor, OwnerKind kind) { @@ -426,7 +429,7 @@ public class JetTypeMapper { } - private Method mapSignature(JetNamedFunction f, List valueParameterTypes) { + private Method mapSignature(JetNamedFunction f, List valueParameterTypes, OwnerKind kind) { final JetTypeReference receiverTypeRef = f.getReceiverTypeRef(); final JetType receiverType = receiverTypeRef == null ? null : bindingContext.get(BindingContext.TYPE, receiverTypeRef); final List parameters = f.getValueParameters(); @@ -434,6 +437,13 @@ public class JetTypeMapper { if (receiverType != null) { parameterTypes.add(mapType(receiverType)); } + FunctionDescriptor functionDescriptor = bindingContext.get(BindingContext.FUNCTION, f); + if(kind == OwnerKind.TRAIT_IMPL) { + JetType jetType = ((ClassDescriptor)functionDescriptor.getContainingDeclaration()).getDefaultType(); + final Type type = mapType(jetType); + valueParameterTypes.add(type); + parameterTypes.add(type); + } for (JetParameter parameter : parameters) { final Type type = mapType(bindingContext.get(BindingContext.TYPE, parameter.getTypeReference())); valueParameterTypes.add(type); @@ -445,7 +455,6 @@ public class JetTypeMapper { final JetTypeReference returnTypeRef = f.getReturnTypeRef(); Type returnType; if (returnTypeRef == null) { - final FunctionDescriptor functionDescriptor = bindingContext.get(BindingContext.FUNCTION, f); assert functionDescriptor != null; final JetType type = functionDescriptor.getReturnType(); returnType = mapReturnType(type); @@ -456,7 +465,7 @@ public class JetTypeMapper { return new Method(f.getName(), returnType, parameterTypes.toArray(new Type[parameterTypes.size()])); } - public CallableMethod mapToCallableMethod(PsiNamedElement declaration) { + public CallableMethod mapToCallableMethod(PsiNamedElement declaration, OwnerKind kind) { if (declaration instanceof PsiMethod) { return mapToCallableMethod((PsiMethod) declaration); } @@ -468,7 +477,7 @@ public class JetTypeMapper { assert functionDescriptor != null; final DeclarationDescriptor functionParent = functionDescriptor.getContainingDeclaration(); final List valueParameterTypes = new ArrayList(); - Method descriptor = mapSignature(f, valueParameterTypes); + Method descriptor = mapSignature(f, valueParameterTypes, kind); String owner; int invokeOpcode; boolean needsReceiver; @@ -481,7 +490,7 @@ public class JetTypeMapper { else if (functionParent instanceof ClassDescriptor) { ClassDescriptor containingClass = (ClassDescriptor) functionParent; owner = jvmName(containingClass, OwnerKind.IMPLEMENTATION); - invokeOpcode = isInterface(containingClass, OwnerKind.INTERFACE) + invokeOpcode = isInterface(containingClass) ? Opcodes.INVOKEINTERFACE : Opcodes.INVOKEVIRTUAL; needsReceiver = true; diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/OwnerKind.java b/compiler/backend/src/org/jetbrains/jet/codegen/OwnerKind.java index efb642c0096..f5259b2098b 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/OwnerKind.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/OwnerKind.java @@ -11,8 +11,8 @@ public class OwnerKind { } public static final OwnerKind NAMESPACE = new OwnerKind("namespace"); - public static final OwnerKind INTERFACE = new OwnerKind("interface"); public static final OwnerKind IMPLEMENTATION = new OwnerKind("implementation"); + public static final OwnerKind TRAIT_IMPL = new OwnerKind("trait implementation"); public static class DelegateKind extends OwnerKind { private final StackValue delegate; diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/PropertyCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/PropertyCodegen.java index 686fe404c38..0de28e5e0fe 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/PropertyCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/PropertyCodegen.java @@ -42,24 +42,6 @@ public class PropertyCodegen { generateGetter(p, propertyDescriptor); generateSetter(p, propertyDescriptor); } - else if (kind == OwnerKind.INTERFACE) { - final JetPropertyAccessor getter = p.getGetter(); - if ((getter != null && !getter.hasModifier(JetTokens.PRIVATE_KEYWORD) || - (getter == null && isExternallyAccessible(p)))) { - v.visitMethod(Opcodes.ACC_ABSTRACT | Opcodes.ACC_PUBLIC, - getterName(p.getName()), - state.getTypeMapper().mapGetterSignature(propertyDescriptor).getDescriptor(), - null, null); - } - final JetPropertyAccessor setter = p.getSetter(); - if ((setter != null && !setter.hasModifier(JetTokens.PRIVATE_KEYWORD) || - (setter == null && isExternallyAccessible(p) && p.isVar()))) { - v.visitMethod(Opcodes.ACC_ABSTRACT | Opcodes.ACC_PUBLIC, - setterName(p.getName()), - state.getTypeMapper().mapSetterSignature(propertyDescriptor).getDescriptor(), - null, null); - } - } else if (kind instanceof OwnerKind.DelegateKind) { generateDefaultGetter(propertyDescriptor, Opcodes.ACC_PUBLIC); if (propertyDescriptor.isVar()) { diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/TraitImplBodyCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/TraitImplBodyCodegen.java new file mode 100644 index 00000000000..8c87865c55b --- /dev/null +++ b/compiler/backend/src/org/jetbrains/jet/codegen/TraitImplBodyCodegen.java @@ -0,0 +1,39 @@ +package org.jetbrains.jet.codegen; + +import org.jetbrains.jet.lang.psi.JetClass; +import org.jetbrains.jet.lang.psi.JetClassOrObject; +import org.jetbrains.jet.lang.psi.JetDeclaration; +import org.jetbrains.jet.lang.psi.JetNamedFunction; +import org.jetbrains.jet.lexer.JetTokens; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.Opcodes; + +import java.util.ArrayList; +import java.util.List; + +public class TraitImplBodyCodegen extends ClassBodyCodegen { + public TraitImplBodyCodegen(JetClassOrObject aClass, ClassContext context, ClassVisitor v, GenerationState state) { + super(aClass, context, v, state); + } + + @Override + protected void generateDeclaration() { + v.visit(Opcodes.V1_6, + Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, + jvmName(), + null, + "java/lang/Object", + new String[0] + ); + v.visitSource(myClass.getContainingFile().getName(), null); + } + + private String jvmName() { + return state.getTypeMapper().jvmName(descriptor, OwnerKind.TRAIT_IMPL); + } + + @Override + protected void genNamedFunction(JetNamedFunction declaration, FunctionCodegen functionCodegen) { + super.genNamedFunction(declaration, functionCodegen); //To change body of overridden methods use File | Settings | File Templates. + } +} diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/intrinsics/PsiMethodCall.java b/compiler/backend/src/org/jetbrains/jet/codegen/intrinsics/PsiMethodCall.java index 496f610df9a..1025f23c5a6 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/intrinsics/PsiMethodCall.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/intrinsics/PsiMethodCall.java @@ -25,7 +25,7 @@ public class PsiMethodCall implements IntrinsicMethod { @Override public StackValue generate(ExpressionCodegen codegen, InstructionAdapter v, Type expectedType, PsiElement element, List arguments, StackValue receiver) { - final CallableMethod callableMethod = codegen.getTypeMapper().mapToCallableMethod(myMethod); + final CallableMethod callableMethod = codegen.getTypeMapper().mapToCallableMethod(myMethod, null); codegen.invokeMethodWithArguments(callableMethod, (JetCallExpression) element, receiver); return StackValue.onStack(callableMethod.getSignature().getReturnType()); } diff --git a/idea/testData/codegen/traits/simple.jet b/idea/testData/codegen/traits/simple.jet new file mode 100644 index 00000000000..f8119547d2d --- /dev/null +++ b/idea/testData/codegen/traits/simple.jet @@ -0,0 +1,12 @@ +trait SimpleClass : java.lang.Object { + fun foo() : String = "239 " + toString () +} + +class SimpleClassImpl() : SimpleClass { + override fun toString() = "SimpleClassImpl" +} + +fun box() : String { + val c = SimpleClassImpl() + return if("239 SimpleClassImpl" == c.foo()) "OK" else "fail" +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/codegen/TraitsTest.java b/idea/tests/org/jetbrains/jet/codegen/TraitsTest.java new file mode 100644 index 00000000000..3558c42e455 --- /dev/null +++ b/idea/tests/org/jetbrains/jet/codegen/TraitsTest.java @@ -0,0 +1,13 @@ +package org.jetbrains.jet.codegen; + +public class TraitsTest extends CodegenTestCase { + @Override + protected String getPrefix() { + return "traits"; + } + + public void testSimple () throws Exception { + blackBoxFile("traits/simple.jet"); + System.out.println(generateToText()); + } +}