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