001/*
002 * Copyright 2010-2013 JetBrains s.r.o.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.jetbrains.jet.codegen;
018
019import org.jetbrains.annotations.NotNull;
020import org.jetbrains.annotations.Nullable;
021import org.jetbrains.asm4.MethodVisitor;
022import org.jetbrains.asm4.Type;
023import org.jetbrains.jet.codegen.context.ClassContext;
024import org.jetbrains.jet.codegen.state.GenerationState;
025import org.jetbrains.jet.lang.descriptors.*;
026import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
027import org.jetbrains.jet.lang.descriptors.impl.SimpleFunctionDescriptorImpl;
028import org.jetbrains.jet.lang.psi.*;
029import org.jetbrains.jet.lang.resolve.BindingContext;
030import org.jetbrains.jet.lang.resolve.DescriptorUtils;
031import org.jetbrains.jet.lang.resolve.name.Name;
032
033import java.util.Collections;
034import java.util.List;
035
036import static org.jetbrains.asm4.Opcodes.*;
037import static org.jetbrains.jet.codegen.AsmUtil.*;
038import static org.jetbrains.jet.codegen.binding.CodegenBinding.enumEntryNeedSubclass;
039
040public abstract class ClassBodyCodegen extends MemberCodegen {
041    protected final JetClassOrObject myClass;
042    protected final OwnerKind kind;
043    protected final ClassDescriptor descriptor;
044    protected final ClassBuilder v;
045    protected final ClassContext context;
046
047    private MethodVisitor clInitMethod;
048
049    private ExpressionCodegen clInitCodegen;
050
051    protected ClassBodyCodegen(
052            @NotNull JetClassOrObject aClass,
053            @NotNull ClassContext context,
054            @NotNull ClassBuilder v,
055            @NotNull GenerationState state,
056            @Nullable MemberCodegen parentCodegen
057    ) {
058        super(state, parentCodegen);
059        descriptor = state.getBindingContext().get(BindingContext.CLASS, aClass);
060        myClass = aClass;
061        this.context = context;
062        this.kind = context.getContextKind();
063        this.v = v;
064    }
065
066    public void generate() {
067        generateDeclaration();
068
069        generateClassBody();
070
071        generateSyntheticParts();
072
073        generateStaticInitializer();
074
075        generateRemoveInIterator();
076    }
077
078    protected abstract void generateDeclaration();
079
080    protected void generateSyntheticParts() {
081    }
082
083    private void generateClassBody() {
084        FunctionCodegen functionCodegen = new FunctionCodegen(context, v, state);
085        PropertyCodegen propertyCodegen = new PropertyCodegen(context, v, functionCodegen, this);
086
087        if (kind != OwnerKind.TRAIT_IMPL) {
088            //generate nested classes first and only then generate class body. It necessary to access to nested CodegenContexts
089            for (JetDeclaration declaration : myClass.getDeclarations()) {
090                if (shouldProcessFirst(declaration)) {
091                    generateDeclaration(propertyCodegen, declaration);
092                }
093            }
094        }
095
096        for (JetDeclaration declaration : myClass.getDeclarations()) {
097            if (!shouldProcessFirst(declaration)) {
098                generateDeclaration(propertyCodegen, declaration);
099            }
100        }
101
102        generatePrimaryConstructorProperties(propertyCodegen, myClass);
103    }
104
105    private boolean shouldProcessFirst(JetDeclaration declaration) {
106        return false == (declaration instanceof JetProperty || declaration instanceof JetNamedFunction);
107    }
108
109
110    protected void generateDeclaration(PropertyCodegen propertyCodegen, JetDeclaration declaration) {
111        if (declaration instanceof JetProperty || declaration instanceof JetNamedFunction) {
112            genFunctionOrProperty(context, (JetTypeParameterListOwner) declaration, v);
113        }
114        else if (declaration instanceof JetClassOrObject) {
115            if (declaration instanceof JetEnumEntry && !enumEntryNeedSubclass(
116                    state.getBindingContext(), (JetEnumEntry) declaration)) {
117                return;
118            }
119
120            genClassOrObject(context, (JetClassOrObject) declaration);
121        }
122        else if (declaration instanceof JetClassObject) {
123            genClassOrObject(context, ((JetClassObject) declaration).getObjectDeclaration());
124        }
125    }
126
127    private void generatePrimaryConstructorProperties(PropertyCodegen propertyCodegen, JetClassOrObject origin) {
128        boolean isAnnotation = origin instanceof JetClass && ((JetClass) origin).isAnnotation();
129        for (JetParameter p : getPrimaryConstructorParameters()) {
130            if (p.getValOrVarNode() != null) {
131                PropertyDescriptor propertyDescriptor = state.getBindingContext().get(BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, p);
132                if (propertyDescriptor != null) {
133                    if (!isAnnotation) {
134                        propertyCodegen.generatePrimaryConstructorProperty(p, propertyDescriptor);
135                    }
136                    else {
137                        Type type = state.getTypeMapper().mapType(propertyDescriptor);
138                        v.newMethod(p, ACC_PUBLIC | ACC_ABSTRACT, p.getName(), "()" + type.getDescriptor(), null, null);
139                    }
140                }
141            }
142        }
143    }
144
145    protected @NotNull List<JetParameter> getPrimaryConstructorParameters() {
146        if (myClass instanceof JetClass) {
147            return ((JetClass) myClass).getPrimaryConstructorParameters();
148        }
149        return Collections.emptyList();
150    }
151
152    private void generateStaticInitializer() {
153        if (clInitMethod != null) {
154            createOrGetClInitMethod();
155
156            if (state.getClassBuilderMode() == ClassBuilderMode.FULL) {
157                ExpressionCodegen codegen = createOrGetClInitCodegen();
158
159                createOrGetClInitMethod().visitInsn(RETURN);
160                FunctionCodegen.endVisit(codegen.v, "static initializer", myClass);
161            }
162        }
163    }
164
165    @Nullable
166    protected MethodVisitor createOrGetClInitMethod() {
167        if (clInitMethod == null) {
168            clInitMethod = v.newMethod(null, ACC_STATIC, "<clinit>", "()V", null, null);
169        }
170        return clInitMethod;
171    }
172
173    @Nullable
174    protected ExpressionCodegen createOrGetClInitCodegen() {
175        assert state.getClassBuilderMode() == ClassBuilderMode.FULL;
176        if (state.getClassBuilderMode() == ClassBuilderMode.FULL) {
177            if (clInitCodegen == null) {
178                MethodVisitor method = createOrGetClInitMethod();
179                method.visitCode();
180                SimpleFunctionDescriptorImpl clInit =
181                        new SimpleFunctionDescriptorImpl(descriptor, Collections.<AnnotationDescriptor>emptyList(),
182                                                         Name.special("<clinit>"),
183                                                         CallableMemberDescriptor.Kind.SYNTHESIZED);
184                clInit.initialize(null, null, Collections.<TypeParameterDescriptor>emptyList(),
185                                  Collections.<ValueParameterDescriptor>emptyList(), null, null, Visibilities.PRIVATE, false);
186
187                clInitCodegen = new ExpressionCodegen(method, new FrameMap(), Type.VOID_TYPE, context.intoFunction(clInit), state);
188            }
189        }
190        return clInitCodegen;
191    }
192
193    private void generateRemoveInIterator() {
194        // generates stub 'remove' function for subclasses of Iterator to be compatible with java.util.Iterator
195        if (DescriptorUtils.isIteratorWithoutRemoveImpl(descriptor)) {
196            MethodVisitor mv = v.getVisitor().visitMethod(ACC_PUBLIC, "remove", "()V", null, null);
197            genMethodThrow(mv, "java/lang/UnsupportedOperationException", "Mutating method called on a Kotlin Iterator");
198        }
199    }
200}