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.asm4.*; 027 import org.jetbrains.asm4.tree.MethodNode; 028 import org.jetbrains.jet.codegen.PackageCodegen; 029 import org.jetbrains.jet.codegen.binding.CodegenBinding; 030 import org.jetbrains.jet.codegen.context.CodegenContext; 031 import org.jetbrains.jet.codegen.context.PackageContext; 032 import org.jetbrains.jet.codegen.state.GenerationState; 033 import org.jetbrains.jet.codegen.state.JetTypeMapper; 034 import org.jetbrains.jet.descriptors.serialization.JavaProtoBuf; 035 import org.jetbrains.jet.descriptors.serialization.ProtoBuf; 036 import org.jetbrains.jet.descriptors.serialization.descriptors.DeserializedSimpleFunctionDescriptor; 037 import org.jetbrains.jet.lang.descriptors.*; 038 import org.jetbrains.jet.lang.resolve.BindingContextUtils; 039 import org.jetbrains.jet.lang.resolve.DescriptorUtils; 040 import org.jetbrains.jet.lang.resolve.java.PackageClassUtils; 041 import org.jetbrains.jet.lang.resolve.kotlin.DeserializedResolverUtils; 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 046 import java.io.IOException; 047 import java.io.InputStream; 048 import java.util.Arrays; 049 050 import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getFqName; 051 import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isTrait; 052 053 public class InlineCodegenUtil { 054 055 public final static int API = Opcodes.ASM4; 056 057 public final static String INVOKE = "invoke"; 058 059 @Nullable 060 public static MethodNode getMethodNode( 061 InputStream classData, 062 final String methodName, 063 final String methodDescriptor 064 ) throws ClassNotFoundException, IOException { 065 ClassReader cr = new ClassReader(classData); 066 final MethodNode[] methodNode = new MethodNode[1]; 067 cr.accept(new ClassVisitor(API) { 068 069 @Override 070 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { 071 if (methodName.equals(name) && methodDescriptor.equals(desc)) { 072 return methodNode[0] = new MethodNode(access, name, desc, signature, exceptions); 073 } 074 return null; 075 } 076 }, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); 077 078 return methodNode[0]; 079 } 080 081 082 @NotNull 083 public static VirtualFile getVirtualFileForCallable(@NotNull DeserializedSimpleFunctionDescriptor deserializedDescriptor, @NotNull GenerationState state) { 084 VirtualFile file; 085 DeclarationDescriptor parentDeclaration = deserializedDescriptor.getContainingDeclaration(); 086 if (parentDeclaration instanceof PackageFragmentDescriptor) { 087 ProtoBuf.Callable proto = deserializedDescriptor.getFunctionProto(); 088 if (!proto.hasExtension(JavaProtoBuf.implClassName)) { 089 throw new IllegalStateException("Function in namespace should have implClassName property in proto: " + deserializedDescriptor); 090 } 091 Name name = deserializedDescriptor.getNameResolver().getName(proto.getExtension(JavaProtoBuf.implClassName)); 092 FqName packagePartFqName = 093 PackageClassUtils.getPackageClassFqName(((PackageFragmentDescriptor) parentDeclaration).getFqName()).parent().child( 094 name); 095 file = findVirtualFileWithHeader(state.getProject(), packagePartFqName); 096 } else { 097 file = findVirtualFileContainingDescriptor(state.getProject(), deserializedDescriptor); 098 } 099 100 if (file == null) { 101 throw new IllegalStateException("Couldn't find declaration file for " + deserializedDescriptor.getName()); 102 } 103 104 return file; 105 } 106 107 @Nullable 108 public static VirtualFile findVirtualFileWithHeader(@NotNull Project project, @NotNull FqName containerFqName) { 109 VirtualFileFinder fileFinder = ServiceManager.getService(project, VirtualFileFinder.class); 110 return fileFinder.findVirtualFileWithHeader(containerFqName); 111 } 112 113 @Nullable 114 public static VirtualFile findVirtualFile(@NotNull Project project, @NotNull String internalName) { 115 VirtualFileFinder fileFinder = ServiceManager.getService(project, VirtualFileFinder.class); 116 return fileFinder.findVirtualFile(internalName); 117 } 118 119 //TODO: navigate to inner classes 120 @Nullable 121 public static FqName getContainerFqName(@NotNull DeclarationDescriptor referencedDescriptor) { 122 ClassOrPackageFragmentDescriptor 123 containerDescriptor = DescriptorUtils.getParentOfType(referencedDescriptor, ClassOrPackageFragmentDescriptor.class, false); 124 if (containerDescriptor instanceof PackageFragmentDescriptor) { 125 return PackageClassUtils.getPackageClassFqName(getFqName(containerDescriptor).toSafe()); 126 } 127 if (containerDescriptor instanceof ClassDescriptor) { 128 return DeserializedResolverUtils.kotlinFqNameToJavaFqName(getFqName(containerDescriptor), isTrait(containerDescriptor)); 129 } 130 return null; 131 } 132 133 public static String getInlineName(@NotNull CodegenContext codegenContext, @NotNull JetTypeMapper typeMapper) { 134 return getInlineName(codegenContext, codegenContext.getContextDescriptor(), typeMapper); 135 } 136 137 private static String getInlineName(@NotNull CodegenContext codegenContext, @NotNull DeclarationDescriptor currentDescriptor, @NotNull JetTypeMapper typeMapper) { 138 if (currentDescriptor instanceof PackageFragmentDescriptor) { 139 PsiFile file = getContainingFile(codegenContext, typeMapper); 140 141 Type packagePartType; 142 if (file == null) { 143 //in case package fragment clinit 144 assert codegenContext instanceof PackageContext : "Expected package context but " + codegenContext; 145 packagePartType = ((PackageContext) codegenContext).getPackagePartType(); 146 } else { 147 packagePartType = 148 PackageCodegen.getPackagePartType(PackageClassUtils.getPackageClassFqName(getFqName(currentDescriptor).toSafe()), 149 file.getVirtualFile()); 150 } 151 152 if (packagePartType == null) { 153 DeclarationDescriptor contextDescriptor = codegenContext.getContextDescriptor(); 154 //noinspection ConstantConditions 155 throw new RuntimeException("Couldn't find declaration for " + contextDescriptor.getContainingDeclaration().getName() + "." + contextDescriptor.getName() ); 156 } 157 158 return packagePartType.getInternalName(); 159 } 160 else if (currentDescriptor instanceof ClassifierDescriptor) { 161 Type type = typeMapper.mapType((ClassifierDescriptor) currentDescriptor); 162 return type.getInternalName(); 163 } else if (currentDescriptor instanceof FunctionDescriptor) { 164 ClassDescriptor descriptor = 165 typeMapper.getBindingContext().get(CodegenBinding.CLASS_FOR_FUNCTION, (FunctionDescriptor) currentDescriptor); 166 if (descriptor != null) { 167 Type type = typeMapper.mapType(descriptor); 168 return type.getInternalName(); 169 } 170 } 171 172 //TODO: add suffix for special case 173 String suffix = currentDescriptor.getName().isSpecial() ? "" : currentDescriptor.getName().asString(); 174 175 //noinspection ConstantConditions 176 return getInlineName(codegenContext, currentDescriptor.getContainingDeclaration(), typeMapper) + "$" + suffix; 177 } 178 179 @Nullable 180 private static VirtualFile findVirtualFileContainingDescriptor( 181 @NotNull Project project, 182 @NotNull DeclarationDescriptor referencedDescriptor 183 ) { 184 FqName containerFqName = getContainerFqName(referencedDescriptor); 185 if (containerFqName == null) { 186 return null; 187 } 188 return findVirtualFileWithHeader(project, containerFqName); 189 } 190 191 192 public static boolean isInvokeOnLambda(String owner, String name) { 193 if (!INVOKE.equals(name)) { 194 return false; 195 } 196 197 for (String prefix : Arrays.asList("kotlin/Function", "kotlin/ExtensionFunction")) { 198 if (owner.startsWith(prefix)) { 199 String suffix = owner.substring(prefix.length()); 200 if (isInteger(suffix)) { 201 return true; 202 } 203 } 204 } 205 return false; 206 } 207 208 public static boolean isLambdaConstructorCall(@NotNull String internalName, @NotNull String methodName) { 209 return "<init>".equals(methodName) && isLambdaClass(internalName); 210 } 211 212 public static boolean isLambdaClass(String internalName) { 213 String shortName = getLastNamePart(internalName); 214 int index = shortName.lastIndexOf("$"); 215 216 if (index < 0) { 217 return false; 218 } 219 220 String suffix = shortName.substring(index + 1); 221 return isInteger(suffix); 222 } 223 224 @NotNull 225 private static String getLastNamePart(@NotNull String internalName) { 226 int index = internalName.lastIndexOf("/"); 227 return index < 0 ? internalName : internalName.substring(index + 1); 228 } 229 230 @Nullable 231 public static PsiFile getContainingFile(CodegenContext codegenContext, JetTypeMapper typeMapper) { 232 DeclarationDescriptor contextDescriptor = codegenContext.getContextDescriptor(); 233 PsiElement psiElement = BindingContextUtils.descriptorToDeclaration(typeMapper.getBindingContext(), contextDescriptor); 234 if (psiElement != null) { 235 return psiElement.getContainingFile(); 236 } 237 return null; 238 } 239 240 @NotNull 241 public static MaxCalcNode wrapWithMaxLocalCalc(@NotNull MethodNode methodNode) { 242 return new MaxCalcNode(methodNode); 243 } 244 245 private static boolean isInteger(@NotNull String string) { 246 if (string.isEmpty()) { 247 return false; 248 } 249 250 for (int i = 0; i < string.length(); i++) { 251 if (!Character.isDigit(string.charAt(i))) { 252 return false; 253 } 254 } 255 256 return true; 257 } 258 }