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