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 }