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