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