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    
017    package org.jetbrains.jet.codegen;
018    
019    import com.google.common.collect.Lists;
020    import com.intellij.openapi.util.Pair;
021    import org.jetbrains.annotations.NotNull;
022    import org.jetbrains.asm4.MethodVisitor;
023    import org.jetbrains.asm4.Type;
024    import org.jetbrains.asm4.commons.InstructionAdapter;
025    import org.jetbrains.asm4.commons.Method;
026    import org.jetbrains.jet.codegen.context.CodegenContext;
027    import org.jetbrains.jet.codegen.context.FieldOwnerContext;
028    import org.jetbrains.jet.codegen.context.MethodContext;
029    import org.jetbrains.jet.codegen.context.ScriptContext;
030    import org.jetbrains.jet.codegen.signature.JvmMethodSignature;
031    import org.jetbrains.jet.codegen.state.GenerationState;
032    import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
033    import org.jetbrains.jet.lang.descriptors.ScriptDescriptor;
034    import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
035    import org.jetbrains.jet.lang.psi.*;
036    import org.jetbrains.jet.lang.resolve.BindingContext;
037    import org.jetbrains.jet.lang.resolve.java.JvmClassName;
038    
039    import javax.inject.Inject;
040    import java.util.Collections;
041    import java.util.List;
042    
043    import static org.jetbrains.asm4.Opcodes.*;
044    import static org.jetbrains.jet.codegen.binding.CodegenBinding.*;
045    import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.OBJECT_TYPE;
046    
047    public class ScriptCodegen extends MemberCodegen {
048    
049        @NotNull
050        private ClassFileFactory classFileFactory;
051    
052        private List<ScriptDescriptor> earlierScripts;
053        private Method scriptConstructorMethod;
054    
055        public ScriptCodegen(@NotNull GenerationState state) {
056            super(state, null);
057        }
058    
059        @Inject
060        public void setClassFileFactory(@NotNull ClassFileFactory classFileFactory) {
061            this.classFileFactory = classFileFactory;
062        }
063    
064        public void generate(JetScript scriptDeclaration) {
065    
066            ScriptDescriptor scriptDescriptor = state.getBindingContext().get(BindingContext.SCRIPT, scriptDeclaration);
067    
068            assert scriptDescriptor != null;
069            ClassDescriptor classDescriptorForScript = bindingContext.get(CLASS_FOR_SCRIPT, scriptDescriptor);
070            assert classDescriptorForScript != null;
071    
072            ScriptContext context =
073                    (ScriptContext) CodegenContext.STATIC
074                            .intoScript(scriptDescriptor, classDescriptorForScript);
075    
076            JvmClassName className = bindingContext.get(FQN, classDescriptorForScript);
077            assert className != null;
078    
079            ClassBuilder classBuilder = classFileFactory.newVisitor(className, scriptDeclaration.getContainingFile());
080            classBuilder.defineClass(scriptDeclaration,
081                                     V1_6,
082                                     ACC_PUBLIC,
083                                     className.getInternalName(),
084                                     null,
085                                     "java/lang/Object",
086                                     new String[0]);
087    
088            genMembers(scriptDeclaration, context, classBuilder);
089            genFieldsForParameters(scriptDescriptor, classBuilder);
090            genConstructor(scriptDeclaration, scriptDescriptor, classDescriptorForScript, classBuilder,
091                           context.intoFunction(scriptDescriptor.getScriptCodeDescriptor()),
092                           earlierScripts);
093    
094            classBuilder.done();
095        }
096    
097        private void genConstructor(
098                @NotNull JetScript scriptDeclaration,
099                @NotNull ScriptDescriptor scriptDescriptor,
100                @NotNull ClassDescriptor classDescriptorForScript,
101                @NotNull ClassBuilder classBuilder,
102                @NotNull MethodContext context,
103                @NotNull List<ScriptDescriptor> importedScripts
104        ) {
105    
106            Type blockType = typeMapper.mapType(scriptDescriptor.getReturnType());
107    
108            classBuilder.newField(null, ACC_PUBLIC | ACC_FINAL, ScriptDescriptor.LAST_EXPRESSION_VALUE_FIELD_NAME,
109                                  blockType.getDescriptor(), null, null);
110    
111            JvmMethodSignature jvmSignature = typeMapper.mapScriptSignature(scriptDescriptor, importedScripts);
112    
113            state.getScriptCodegen().setScriptConstructorMethod(jvmSignature.getAsmMethod());
114    
115            MethodVisitor mv = classBuilder.newMethod(
116                    scriptDeclaration, ACC_PUBLIC, jvmSignature.getAsmMethod().getName(), jvmSignature.getAsmMethod().getDescriptor(),
117                    null, null);
118    
119            mv.visitCode();
120    
121            InstructionAdapter instructionAdapter = new InstructionAdapter(mv);
122    
123            JvmClassName className = bindingContext.get(FQN, classDescriptorForScript);
124            assert className != null;
125    
126            instructionAdapter.load(0, className.getAsmType());
127            instructionAdapter.invokespecial("java/lang/Object", "<init>", "()V");
128    
129            instructionAdapter.load(0, className.getAsmType());
130    
131            FrameMap frameMap = context.prepareFrame(typeMapper);
132    
133            for (ScriptDescriptor importedScript : importedScripts) {
134                frameMap.enter(importedScript, OBJECT_TYPE);
135            }
136    
137            Type[] argTypes = jvmSignature.getAsmMethod().getArgumentTypes();
138            int add = 0;
139    
140            for (int i = 0; i < scriptDescriptor.getValueParameters().size(); i++) {
141                ValueParameterDescriptor parameter = scriptDescriptor.getValueParameters().get(i);
142                frameMap.enter(parameter, argTypes[i + add]);
143            }
144    
145            ImplementationBodyCodegen.generateInitializers(
146                    new ExpressionCodegen(instructionAdapter, frameMap, Type.VOID_TYPE, context, state),
147                    scriptDeclaration.getDeclarations(),
148                    bindingContext,
149                    state);
150    
151            int offset = 1;
152    
153            for (ScriptDescriptor earlierScript : importedScripts) {
154                JvmClassName earlierClassName = classNameForScriptDescriptor(bindingContext, earlierScript);
155                instructionAdapter.load(0, className.getAsmType());
156                instructionAdapter.load(offset, earlierClassName.getAsmType());
157                offset += earlierClassName.getAsmType().getSize();
158                instructionAdapter.putfield(className.getInternalName(), getScriptFieldName(earlierScript),
159                                            earlierClassName.getAsmType().getDescriptor());
160            }
161    
162            for (ValueParameterDescriptor parameter : scriptDescriptor.getValueParameters()) {
163                Type parameterType = typeMapper.mapType(parameter.getType());
164                instructionAdapter.load(0, className.getAsmType());
165                instructionAdapter.load(offset, parameterType);
166                offset += parameterType.getSize();
167                instructionAdapter.putfield(className.getInternalName(), parameter.getName().getIdentifier(), parameterType.getDescriptor());
168            }
169    
170            StackValue stackValue =
171                    new ExpressionCodegen(mv, frameMap, Type.VOID_TYPE, context, state).gen(scriptDeclaration.getBlockExpression());
172            if (stackValue.type != Type.VOID_TYPE) {
173                stackValue.put(stackValue.type, instructionAdapter);
174                instructionAdapter
175                        .putfield(className.getInternalName(), ScriptDescriptor.LAST_EXPRESSION_VALUE_FIELD_NAME, blockType.getDescriptor());
176            }
177    
178            instructionAdapter.areturn(Type.VOID_TYPE);
179            mv.visitMaxs(-1, -1);
180            mv.visitEnd();
181        }
182    
183        private void genFieldsForParameters(@NotNull ScriptDescriptor script, @NotNull ClassBuilder classBuilder) {
184            for (ScriptDescriptor earlierScript : earlierScripts) {
185                JvmClassName earlierClassName;
186                earlierClassName = classNameForScriptDescriptor(bindingContext, earlierScript);
187                int access = ACC_PRIVATE | ACC_FINAL;
188                classBuilder.newField(null, access, getScriptFieldName(earlierScript), earlierClassName.getDescriptor(), null, null);
189            }
190    
191            for (ValueParameterDescriptor parameter : script.getValueParameters()) {
192                Type parameterType = typeMapper.mapType(parameter);
193                int access = ACC_PUBLIC | ACC_FINAL;
194                classBuilder.newField(null, access, parameter.getName().getIdentifier(), parameterType.getDescriptor(), null, null);
195            }
196        }
197    
198        private void genMembers(@NotNull JetScript scriptDeclaration, @NotNull FieldOwnerContext context, @NotNull ClassBuilder classBuilder) {
199            for (JetDeclaration decl : scriptDeclaration.getDeclarations()) {
200                genFunctionOrProperty(context, (JetTypeParameterListOwner) decl, classBuilder);
201            }
202        }
203    
204        public void registerEarlierScripts(List<Pair<ScriptDescriptor, JvmClassName>> earlierScripts) {
205            for (Pair<ScriptDescriptor, JvmClassName> t : earlierScripts) {
206                ScriptDescriptor earlierDescriptor = t.first;
207                JvmClassName earlierClassName = t.second;
208    
209                registerClassNameForScript(state.getBindingTrace(), earlierDescriptor, earlierClassName);
210            }
211    
212            List<ScriptDescriptor> earlierScriptDescriptors = Lists.newArrayList();
213            for (Pair<ScriptDescriptor, JvmClassName> t : earlierScripts) {
214                ScriptDescriptor earlierDescriptor = t.first;
215                earlierScriptDescriptors.add(earlierDescriptor);
216            }
217            this.earlierScripts = earlierScriptDescriptors;
218        }
219    
220        protected int getScriptIndex(@NotNull ScriptDescriptor scriptDescriptor) {
221            int index = earlierScripts.indexOf(scriptDescriptor);
222            if (index < 0) {
223                throw new IllegalStateException("Unregistered script: " + scriptDescriptor);
224            }
225            return index + 1;
226        }
227    
228        public String getScriptFieldName(@NotNull ScriptDescriptor scriptDescriptor) {
229            return "script$" + getScriptIndex(scriptDescriptor);
230        }
231    
232        public void setScriptConstructorMethod(Method scriptConstructorMethod) {
233            this.scriptConstructorMethod = scriptConstructorMethod;
234        }
235    
236        public Method getScriptConstructorMethod() {
237            return scriptConstructorMethod;
238        }
239    
240        public void compileScript(
241                @NotNull JetScript script,
242                @NotNull JvmClassName className,
243                @NotNull List<Pair<ScriptDescriptor, JvmClassName>> earlierScripts,
244                @NotNull CompilationErrorHandler errorHandler
245        ) {
246            registerEarlierScripts(earlierScripts);
247            registerClassNameForScript(state.getBindingTrace(), script, className);
248    
249            state.beforeCompile();
250            KotlinCodegenFacade.generateNamespace(
251                    state,
252                    JetPsiUtil.getFQName((JetFile) script.getContainingFile()),
253                    Collections.singleton((JetFile) script.getContainingFile()),
254                    errorHandler);
255        }
256    }