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 }