001    /*
002     * Copyright 2010-2016 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    
017    package org.jetbrains.kotlin.codegen;
018    
019    import kotlin.jvm.functions.Function0;
020    import org.jetbrains.annotations.NotNull;
021    import org.jetbrains.kotlin.codegen.context.CodegenContext;
022    import org.jetbrains.kotlin.codegen.context.MethodContext;
023    import org.jetbrains.kotlin.codegen.context.ScriptContext;
024    import org.jetbrains.kotlin.codegen.state.GenerationState;
025    import org.jetbrains.kotlin.descriptors.ClassDescriptor;
026    import org.jetbrains.kotlin.descriptors.ConstructorDescriptor;
027    import org.jetbrains.kotlin.descriptors.ScriptDescriptor;
028    import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor;
029    import org.jetbrains.kotlin.psi.*;
030    import org.jetbrains.kotlin.resolve.BindingContext;
031    import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
032    import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
033    import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt;
034    import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
035    import org.jetbrains.org.objectweb.asm.MethodVisitor;
036    import org.jetbrains.org.objectweb.asm.Type;
037    import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
038    
039    import java.util.Collections;
040    import java.util.List;
041    
042    import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE;
043    import static org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin.NO_ORIGIN;
044    import static org.jetbrains.org.objectweb.asm.Opcodes.*;
045    
046    public class ScriptCodegen extends MemberCodegen<KtScript> {
047    
048        public static ScriptCodegen createScriptCodegen(
049                @NotNull KtScript declaration,
050                @NotNull GenerationState state,
051                @NotNull CodegenContext parentContext
052        ) {
053            BindingContext bindingContext = state.getBindingContext();
054            ScriptDescriptor scriptDescriptor = bindingContext.get(BindingContext.SCRIPT, declaration);
055            assert scriptDescriptor != null;
056    
057            Type classType = state.getTypeMapper().mapType(scriptDescriptor);
058    
059            ClassBuilder builder = state.getFactory().newVisitor(JvmDeclarationOriginKt.OtherOrigin(declaration, scriptDescriptor),
060                                                                 classType, declaration.getContainingFile());
061            List<ScriptDescriptor> earlierScripts = state.getReplSpecific().getEarlierScriptsForReplInterpreter();
062            ScriptContext scriptContext = parentContext.intoScript(
063                    scriptDescriptor,
064                    earlierScripts == null ? Collections.<ScriptDescriptor>emptyList() : earlierScripts,
065                    scriptDescriptor,
066                    state.getTypeMapper()
067            );
068            return new ScriptCodegen(declaration, state, scriptContext, builder);
069        }
070    
071        private final KtScript scriptDeclaration;
072        private final ScriptContext context;
073        private final ScriptDescriptor scriptDescriptor;
074        private final Type classAsmType;
075    
076        private ScriptCodegen(
077                @NotNull KtScript scriptDeclaration,
078                @NotNull GenerationState state,
079                @NotNull ScriptContext context,
080                @NotNull ClassBuilder builder
081        ) {
082            super(state, null, context, scriptDeclaration, builder);
083            this.scriptDeclaration = scriptDeclaration;
084            this.context = context;
085            this.scriptDescriptor = context.getScriptDescriptor();
086            classAsmType = typeMapper.mapClass(context.getContextDescriptor());
087        }
088    
089        @Override
090        protected void generateDeclaration() {
091            v.defineClass(scriptDeclaration,
092                          state.getClassFileVersion(),
093                          ACC_PUBLIC | ACC_SUPER,
094                          classAsmType.getInternalName(),
095                          null,
096                          typeMapper.mapSupertype(DescriptorUtilsKt.getSuperClassOrAny(scriptDescriptor).getDefaultType(), null).getInternalName(),
097                          CodegenUtilKt.mapSupertypesNames(typeMapper, DescriptorUtilsKt.getSuperInterfaces(scriptDescriptor), null));
098        }
099    
100        @Override
101        protected void generateBody() {
102            genMembers();
103            genFieldsForParameters(v);
104            genConstructor(scriptDescriptor, v,
105                           context.intoFunction(scriptDescriptor.getUnsubstitutedPrimaryConstructor()));
106        }
107    
108        @Override
109        protected void generateSyntheticParts() {
110            generatePropertyMetadataArrayFieldIfNeeded(classAsmType);
111        }
112    
113        @Override
114        protected void generateKotlinMetadataAnnotation() {
115            generateKotlinClassMetadataAnnotation(scriptDescriptor, true);
116        }
117    
118        private void genConstructor(
119                @NotNull ScriptDescriptor scriptDescriptor,
120                @NotNull ClassBuilder classBuilder,
121                @NotNull MethodContext methodContext
122        ) {
123            JvmMethodSignature jvmSignature = typeMapper.mapScriptSignature(scriptDescriptor, context.getEarlierScripts());
124    
125            if (state.getReplSpecific().getShouldGenerateScriptResultValue()) {
126                FieldInfo resultFieldInfo = context.getResultFieldInfo();
127                classBuilder.newField(
128                        JvmDeclarationOrigin.NO_ORIGIN,
129                        ACC_PUBLIC | ACC_FINAL,
130                        resultFieldInfo.getFieldName(),
131                        resultFieldInfo.getFieldType().getDescriptor(),
132                        null,
133                        null
134                );
135            }
136    
137            MethodVisitor mv = classBuilder.newMethod(
138                    JvmDeclarationOriginKt.OtherOrigin(scriptDeclaration, scriptDescriptor.getUnsubstitutedPrimaryConstructor()),
139                    ACC_PUBLIC, jvmSignature.getAsmMethod().getName(), jvmSignature.getAsmMethod().getDescriptor(),
140                    null, null);
141    
142            if (state.getClassBuilderMode().generateBodies) {
143                mv.visitCode();
144    
145                InstructionAdapter iv = new InstructionAdapter(mv);
146    
147                Type classType = typeMapper.mapType(scriptDescriptor);
148    
149                ClassDescriptor superclass = DescriptorUtilsKt.getSuperClassNotAny(scriptDescriptor);
150                // TODO: throw if class is not found)
151    
152                if (superclass == null) {
153                    iv.load(0, classType);
154                    iv.invokespecial("java/lang/Object", "<init>", "()V", false);
155                }
156                else {
157                    ConstructorDescriptor ctorDesc = superclass.getUnsubstitutedPrimaryConstructor();
158                    if (ctorDesc == null) throw new RuntimeException("Primary constructor not found for script template " + superclass.toString());
159    
160                    iv.load(0, classType);
161    
162                    int valueParamStart = context.getEarlierScripts().size() + 1;
163    
164                    List<ValueParameterDescriptor> valueParameters = scriptDescriptor.getUnsubstitutedPrimaryConstructor().getValueParameters();
165                    for (ValueParameterDescriptor superclassParam: ctorDesc.getValueParameters()) {
166                        ValueParameterDescriptor valueParam = null;
167                        for (ValueParameterDescriptor vpd: valueParameters) {
168                            if (vpd.getName().equals(superclassParam.getName())) {
169                                valueParam = vpd;
170                                break;
171                            }
172                        }
173                        assert valueParam != null;
174                        iv.load(valueParam.getIndex() + valueParamStart, typeMapper.mapType(valueParam.getType()));
175                    }
176    
177                    CallableMethod ctorMethod = typeMapper.mapToCallableMethod(ctorDesc, false);
178                    String sig = ctorMethod.getAsmMethod().getDescriptor();
179    
180                    iv.invokespecial(
181                            typeMapper.mapSupertype(superclass.getDefaultType(), null).getInternalName(),
182                            "<init>", sig, false);
183                }
184                iv.load(0, classType);
185    
186                FrameMap frameMap = new FrameMap();
187                frameMap.enterTemp(OBJECT_TYPE);
188    
189                for (ScriptDescriptor importedScript : context.getEarlierScripts()) {
190                    frameMap.enter(importedScript, OBJECT_TYPE);
191                }
192    
193                int offset = 1;
194    
195                for (ScriptDescriptor earlierScript : context.getEarlierScripts()) {
196                    Type earlierClassType = typeMapper.mapClass(earlierScript);
197                    iv.load(0, classType);
198                    iv.load(offset, earlierClassType);
199                    offset += earlierClassType.getSize();
200                    iv.putfield(classType.getInternalName(), context.getScriptFieldName(earlierScript), earlierClassType.getDescriptor());
201                }
202    
203                final ExpressionCodegen codegen = new ExpressionCodegen(mv, frameMap, Type.VOID_TYPE, methodContext, state, this);
204    
205                generateInitializers(new Function0<ExpressionCodegen>() {
206                    @Override
207                    public ExpressionCodegen invoke() {
208                        return codegen;
209                    }
210                });
211    
212                iv.areturn(Type.VOID_TYPE);
213            }
214    
215            mv.visitMaxs(-1, -1);
216            mv.visitEnd();
217        }
218    
219        private void genFieldsForParameters(@NotNull ClassBuilder classBuilder) {
220            for (ScriptDescriptor earlierScript : context.getEarlierScripts()) {
221                Type earlierClassName = typeMapper.mapType(earlierScript);
222                int access = ACC_PUBLIC | ACC_FINAL;
223                classBuilder.newField(NO_ORIGIN, access, context.getScriptFieldName(earlierScript), earlierClassName.getDescriptor(), null, null);
224            }
225        }
226    
227        private void genMembers() {
228            for (KtDeclaration declaration : scriptDeclaration.getDeclarations()) {
229                if (declaration instanceof KtProperty || declaration instanceof KtNamedFunction || declaration instanceof KtTypeAlias) {
230                    genSimpleMember(declaration);
231                }
232                else if (declaration instanceof KtClassOrObject) {
233                    genClassOrObject((KtClassOrObject) declaration);
234                }
235            }
236        }
237    }