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