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 017package org.jetbrains.jet.codegen; 018 019import com.google.common.collect.Lists; 020import com.intellij.openapi.util.Pair; 021import org.jetbrains.annotations.NotNull; 022import org.jetbrains.asm4.MethodVisitor; 023import org.jetbrains.asm4.Type; 024import org.jetbrains.asm4.commons.InstructionAdapter; 025import org.jetbrains.asm4.commons.Method; 026import org.jetbrains.jet.codegen.context.CodegenContext; 027import org.jetbrains.jet.codegen.context.FieldOwnerContext; 028import org.jetbrains.jet.codegen.context.MethodContext; 029import org.jetbrains.jet.codegen.context.ScriptContext; 030import org.jetbrains.jet.codegen.signature.JvmMethodSignature; 031import org.jetbrains.jet.codegen.state.GenerationState; 032import org.jetbrains.jet.lang.descriptors.ClassDescriptor; 033import org.jetbrains.jet.lang.descriptors.ScriptDescriptor; 034import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor; 035import org.jetbrains.jet.lang.psi.*; 036import org.jetbrains.jet.lang.resolve.BindingContext; 037import org.jetbrains.jet.lang.resolve.ScriptNameUtil; 038import org.jetbrains.jet.lang.resolve.java.JvmClassName; 039 040import javax.inject.Inject; 041import java.util.Collections; 042import java.util.List; 043 044import static org.jetbrains.asm4.Opcodes.*; 045import static org.jetbrains.jet.codegen.binding.CodegenBinding.*; 046import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.OBJECT_TYPE; 047 048public 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}