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 }