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 }