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 }