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.inline; 018 019 import com.intellij.openapi.components.ServiceManager; 020 import com.intellij.openapi.project.Project; 021 import com.intellij.openapi.vfs.VirtualFile; 022 import com.intellij.psi.PsiElement; 023 import com.intellij.psi.PsiFile; 024 import org.jetbrains.annotations.NotNull; 025 import org.jetbrains.annotations.Nullable; 026 import org.jetbrains.jet.codegen.binding.CodegenBinding; 027 import org.jetbrains.jet.codegen.context.CodegenContext; 028 import org.jetbrains.jet.codegen.context.PackageContext; 029 import org.jetbrains.jet.codegen.state.GenerationState; 030 import org.jetbrains.jet.codegen.state.JetTypeMapper; 031 import org.jetbrains.jet.descriptors.serialization.JavaProtoBuf; 032 import org.jetbrains.jet.descriptors.serialization.ProtoBuf; 033 import org.jetbrains.jet.descriptors.serialization.descriptors.DeserializedSimpleFunctionDescriptor; 034 import org.jetbrains.jet.lang.descriptors.*; 035 import org.jetbrains.jet.lang.psi.JetFile; 036 import org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils; 037 import org.jetbrains.jet.lang.resolve.DescriptorUtils; 038 import org.jetbrains.jet.lang.resolve.java.JvmAbi; 039 import org.jetbrains.jet.lang.resolve.java.PackageClassUtils; 040 import org.jetbrains.jet.lang.resolve.kotlin.DeserializedResolverUtils; 041 import org.jetbrains.jet.lang.resolve.kotlin.PackagePartClassUtils; 042 import org.jetbrains.jet.lang.resolve.kotlin.VirtualFileFinder; 043 import org.jetbrains.jet.lang.resolve.name.FqName; 044 import org.jetbrains.jet.lang.resolve.name.Name; 045 import org.jetbrains.org.objectweb.asm.*; 046 import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter; 047 import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode; 048 import org.jetbrains.org.objectweb.asm.tree.InsnList; 049 import org.jetbrains.org.objectweb.asm.tree.MethodNode; 050 051 import java.io.IOException; 052 import java.io.InputStream; 053 import java.util.Arrays; 054 import java.util.ListIterator; 055 056 import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getFqName; 057 import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isTrait; 058 059 public class InlineCodegenUtil { 060 public static final int API = Opcodes.ASM5; 061 public static final String INVOKE = "invoke"; 062 063 public static final String CAPTURED_FIELD_PREFIX = "$"; 064 065 public static final String THIS$0 = "this$0"; 066 067 public static final String RECEIVER$0 = "receiver$0"; 068 069 public static final String NON_LOCAL_RETURN = "$$$$$NON_LOCAL_RETURN$$$$$"; 070 071 public static final String ROOT_LABEL = "$$$$$ROOT$$$$$"; 072 073 @Nullable 074 public static MethodNode getMethodNode( 075 InputStream classData, 076 final String methodName, 077 final String methodDescriptor 078 ) throws ClassNotFoundException, IOException { 079 ClassReader cr = new ClassReader(classData); 080 final MethodNode[] methodNode = new MethodNode[1]; 081 cr.accept(new ClassVisitor(API) { 082 083 @Override 084 public MethodVisitor visitMethod(int access, @NotNull String name, @NotNull String desc, String signature, String[] exceptions) { 085 if (methodName.equals(name) && methodDescriptor.equals(desc)) { 086 return methodNode[0] = new MethodNode(access, name, desc, signature, exceptions); 087 } 088 return null; 089 } 090 }, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); 091 092 return methodNode[0]; 093 } 094 095 096 @NotNull 097 public static VirtualFile getVirtualFileForCallable(@NotNull DeserializedSimpleFunctionDescriptor deserializedDescriptor, @NotNull GenerationState state) { 098 VirtualFile file; 099 DeclarationDescriptor parentDeclaration = deserializedDescriptor.getContainingDeclaration(); 100 if (parentDeclaration instanceof PackageFragmentDescriptor) { 101 ProtoBuf.Callable proto = deserializedDescriptor.getProto(); 102 if (!proto.hasExtension(JavaProtoBuf.implClassName)) { 103 throw new IllegalStateException("Function in namespace should have implClassName property in proto: " + deserializedDescriptor); 104 } 105 Name name = deserializedDescriptor.getNameResolver().getName(proto.getExtension(JavaProtoBuf.implClassName)); 106 FqName packagePartFqName = 107 PackageClassUtils.getPackageClassFqName(((PackageFragmentDescriptor) parentDeclaration).getFqName()).parent().child( 108 name); 109 file = findVirtualFileWithHeader(state.getProject(), packagePartFqName); 110 } else { 111 file = findVirtualFileContainingDescriptor(state.getProject(), deserializedDescriptor); 112 } 113 114 if (file == null) { 115 throw new IllegalStateException("Couldn't find declaration file for " + deserializedDescriptor.getName()); 116 } 117 118 return file; 119 } 120 121 @Nullable 122 public static VirtualFile findVirtualFileWithHeader(@NotNull Project project, @NotNull FqName containerFqName) { 123 VirtualFileFinder fileFinder = ServiceManager.getService(project, VirtualFileFinder.class); 124 return fileFinder.findVirtualFileWithHeader(containerFqName); 125 } 126 127 @Nullable 128 public static VirtualFile findVirtualFile(@NotNull Project project, @NotNull String internalName) { 129 VirtualFileFinder fileFinder = ServiceManager.getService(project, VirtualFileFinder.class); 130 return fileFinder.findVirtualFile(internalName); 131 } 132 133 //TODO: navigate to inner classes 134 @Nullable 135 public static FqName getContainerFqName(@NotNull DeclarationDescriptor referencedDescriptor) { 136 ClassOrPackageFragmentDescriptor 137 containerDescriptor = DescriptorUtils.getParentOfType(referencedDescriptor, ClassOrPackageFragmentDescriptor.class, false); 138 if (containerDescriptor instanceof PackageFragmentDescriptor) { 139 return PackageClassUtils.getPackageClassFqName(getFqName(containerDescriptor).toSafe()); 140 } 141 if (containerDescriptor instanceof ClassDescriptor) { 142 FqName fqName = DeserializedResolverUtils.kotlinFqNameToJavaFqName(getFqName(containerDescriptor)); 143 if (isTrait(containerDescriptor)) { 144 return fqName.parent().child(Name.identifier(fqName.shortName() + JvmAbi.TRAIT_IMPL_SUFFIX)); 145 } 146 return fqName; 147 } 148 return null; 149 } 150 151 public static String getInlineName(@NotNull CodegenContext codegenContext, @NotNull JetTypeMapper typeMapper) { 152 return getInlineName(codegenContext, codegenContext.getContextDescriptor(), typeMapper); 153 } 154 155 private static String getInlineName(@NotNull CodegenContext codegenContext, @NotNull DeclarationDescriptor currentDescriptor, @NotNull JetTypeMapper typeMapper) { 156 if (currentDescriptor instanceof PackageFragmentDescriptor) { 157 PsiFile file = getContainingFile(codegenContext); 158 159 Type packagePartType; 160 if (file == null) { 161 //in case package fragment clinit 162 assert codegenContext instanceof PackageContext : "Expected package context but " + codegenContext; 163 packagePartType = ((PackageContext) codegenContext).getPackagePartType(); 164 } else { 165 packagePartType = PackagePartClassUtils.getPackagePartType((JetFile) file); 166 } 167 168 if (packagePartType == null) { 169 DeclarationDescriptor contextDescriptor = codegenContext.getContextDescriptor(); 170 //noinspection ConstantConditions 171 throw new RuntimeException("Couldn't find declaration for " + contextDescriptor.getContainingDeclaration().getName() + "." + contextDescriptor.getName() ); 172 } 173 174 return packagePartType.getInternalName(); 175 } 176 else if (currentDescriptor instanceof ClassifierDescriptor) { 177 Type type = typeMapper.mapType((ClassifierDescriptor) currentDescriptor); 178 return type.getInternalName(); 179 } else if (currentDescriptor instanceof FunctionDescriptor) { 180 ClassDescriptor descriptor = 181 typeMapper.getBindingContext().get(CodegenBinding.CLASS_FOR_FUNCTION, (FunctionDescriptor) currentDescriptor); 182 if (descriptor != null) { 183 Type type = typeMapper.mapType(descriptor); 184 return type.getInternalName(); 185 } 186 } 187 188 //TODO: add suffix for special case 189 String suffix = currentDescriptor.getName().isSpecial() ? "" : currentDescriptor.getName().asString(); 190 191 //noinspection ConstantConditions 192 return getInlineName(codegenContext, currentDescriptor.getContainingDeclaration(), typeMapper) + "$" + suffix; 193 } 194 195 @Nullable 196 private static VirtualFile findVirtualFileContainingDescriptor( 197 @NotNull Project project, 198 @NotNull DeclarationDescriptor referencedDescriptor 199 ) { 200 FqName containerFqName = getContainerFqName(referencedDescriptor); 201 if (containerFqName == null) { 202 return null; 203 } 204 return findVirtualFileWithHeader(project, containerFqName); 205 } 206 207 208 public static boolean isInvokeOnLambda(String owner, String name) { 209 if (!INVOKE.equals(name)) { 210 return false; 211 } 212 213 for (String prefix : Arrays.asList("kotlin/Function", "kotlin/ExtensionFunction")) { 214 if (owner.startsWith(prefix)) { 215 String suffix = owner.substring(prefix.length()); 216 if (isInteger(suffix)) { 217 return true; 218 } 219 } 220 } 221 return false; 222 } 223 224 public static boolean isAnonymousConstructorCall(@NotNull String internalName, @NotNull String methodName) { 225 return "<init>".equals(methodName) && isAnonymousClass(internalName); 226 } 227 228 public static boolean isAnonymousClass(String internalName) { 229 String shortName = getLastNamePart(internalName); 230 int index = shortName.lastIndexOf("$"); 231 232 if (index < 0) { 233 return false; 234 } 235 236 String suffix = shortName.substring(index + 1); 237 return isInteger(suffix); 238 } 239 240 @NotNull 241 private static String getLastNamePart(@NotNull String internalName) { 242 int index = internalName.lastIndexOf("/"); 243 return index < 0 ? internalName : internalName.substring(index + 1); 244 } 245 246 @Nullable 247 public static PsiFile getContainingFile(CodegenContext codegenContext) { 248 DeclarationDescriptor contextDescriptor = codegenContext.getContextDescriptor(); 249 PsiElement psiElement = DescriptorToSourceUtils.descriptorToDeclaration(contextDescriptor); 250 if (psiElement != null) { 251 return psiElement.getContainingFile(); 252 } 253 return null; 254 } 255 256 @NotNull 257 public static MaxCalcNode wrapWithMaxLocalCalc(@NotNull MethodNode methodNode) { 258 return new MaxCalcNode(methodNode); 259 } 260 261 private static boolean isInteger(@NotNull String string) { 262 if (string.isEmpty()) { 263 return false; 264 } 265 266 for (int i = 0; i < string.length(); i++) { 267 if (!Character.isDigit(string.charAt(i))) { 268 return false; 269 } 270 } 271 272 return true; 273 } 274 275 public static boolean isCapturedFieldName(@NotNull String fieldName) { 276 // TODO: improve this heuristic 277 return (fieldName.startsWith(CAPTURED_FIELD_PREFIX) && !fieldName.equals(JvmAbi.KOTLIN_CLASS_FIELD_NAME)) || 278 THIS$0.equals(fieldName) || 279 RECEIVER$0.equals(fieldName); 280 } 281 282 public static boolean isReturnOpcode(int opcode) { 283 return opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN; 284 } 285 286 public static void generateGlobalReturnFlag(@NotNull InstructionAdapter iv, @NotNull String labelName) { 287 iv.invokestatic(NON_LOCAL_RETURN, labelName, "()V", false); 288 } 289 290 public static Type getReturnType(int opcode) { 291 switch (opcode) { 292 case Opcodes.RETURN: return Type.VOID_TYPE; 293 case Opcodes.IRETURN: return Type.INT_TYPE; 294 case Opcodes.DRETURN: return Type.DOUBLE_TYPE; 295 case Opcodes.FRETURN: return Type.FLOAT_TYPE; 296 case Opcodes.LRETURN: return Type.LONG_TYPE; 297 default: return Type.getObjectType("object"); 298 } 299 } 300 301 public static void insertNodeBefore(@NotNull MethodNode from, @NotNull MethodNode to, @NotNull AbstractInsnNode afterNode) { 302 InsnList instructions = to.instructions; 303 ListIterator<AbstractInsnNode> iterator = from.instructions.iterator(); 304 while (iterator.hasNext()) { 305 AbstractInsnNode next = iterator.next(); 306 instructions.insertBefore(afterNode, next); 307 } 308 } 309 310 311 public static MethodNode createEmptyMethodNode() { 312 return new MethodNode(API, 0, "fake", "()V", null, null); 313 } 314 }