class file parsing for non-static inner class constructor fixed.
[fedora-idea.git] / source / com / intellij / psi / impl / compiled / ClsStubBuilder.java
blob84c988040e7683879645b621176fd0dcad83ea2b
1 /*
2 * @author max
3 */
4 package com.intellij.psi.impl.compiled;
6 import com.intellij.openapi.util.Comparing;
7 import com.intellij.openapi.util.text.StringUtil;
8 import com.intellij.openapi.vfs.VirtualFile;
9 import com.intellij.pom.java.LanguageLevel;
10 import com.intellij.psi.PsiNameHelper;
11 import com.intellij.psi.PsiReferenceList;
12 import com.intellij.psi.impl.cache.ModifierFlags;
13 import com.intellij.psi.impl.cache.TypeInfo;
14 import com.intellij.psi.impl.java.stubs.JavaStubElementTypes;
15 import com.intellij.psi.impl.java.stubs.PsiClassStub;
16 import com.intellij.psi.impl.java.stubs.PsiFieldStub;
17 import com.intellij.psi.impl.java.stubs.PsiModifierListStub;
18 import com.intellij.psi.impl.java.stubs.impl.*;
19 import com.intellij.psi.stubs.PsiFileStub;
20 import com.intellij.psi.stubs.StubElement;
21 import com.intellij.util.cls.ClsFormatException;
22 import com.intellij.util.io.StringRef;
23 import org.jetbrains.annotations.NonNls;
24 import org.jetbrains.annotations.Nullable;
25 import org.objectweb.asm.*;
26 import org.objectweb.asm.commons.EmptyVisitor;
28 import java.io.IOException;
29 import java.text.CharacterIterator;
30 import java.text.StringCharacterIterator;
31 import java.util.ArrayList;
32 import java.util.List;
34 @SuppressWarnings({"HardCodedStringLiteral"})
35 public class ClsStubBuilder {
36 private ClsStubBuilder() {
39 @Nullable
40 public static PsiFileStub build(final VirtualFile vFile, byte[] bytes) throws ClsFormatException {
41 final PsiJavaFileStubImpl file = new PsiJavaFileStubImpl("dont.know.yet", true);
42 try {
43 final PsiClassStub result = buildClass(vFile, bytes, file, 0);
44 if (result == null) return null;
46 file.setPackageName(getPackageName(result));
48 catch (Exception e) {
49 throw new ClsFormatException();
51 return file;
54 private static PsiClassStub buildClass(final VirtualFile vFile, final byte[] bytes, final StubElement parent, final int access) {
55 ClassReader reader = new ClassReader(bytes);
57 final MyClassVisitor classVisitor = new MyClassVisitor(vFile, parent, access);
58 reader.accept(classVisitor, ClassReader.SKIP_CODE);
59 return classVisitor.getResult();
62 private static String getPackageName(final PsiClassStub result) {
63 final String fqn = result.getQualifiedName();
64 final String shortName = result.getName();
65 if (fqn == null || Comparing.equal(shortName, fqn)) {
66 return "";
69 return fqn.substring(0, fqn.lastIndexOf('.'));
72 private static class MyClassVisitor implements ClassVisitor {
73 private final StubElement myParent;
74 private final int myAccess;
75 private final VirtualFile myVFile;
76 private PsiModifierListStub myModlist;
77 private PsiClassStub myResult;
78 private static final String[] EMPTY_STRINGS = new String[0];
79 @NonNls private static final String SYNTHETIC_CLINIT_METHOD = "<clinit>";
80 @NonNls private static final String SYNTHETIC_INIT_METHOD = "<init>";
83 private MyClassVisitor(final VirtualFile vFile, final StubElement parent, final int access) {
84 myVFile = vFile;
85 myParent = parent;
86 myAccess = access;
89 public PsiClassStub getResult() {
90 return myResult;
93 public void visit(final int version,
94 final int access,
95 final String name,
96 final String signature,
97 final String superName,
98 final String[] interfaces) {
99 String fqn = getClassName(name);
101 final String shortName = PsiNameHelper.getShortClassName(fqn);
103 final int flags = myAccess != 0 ? myAccess : access;
105 boolean isDeprecated = (flags & Opcodes.ACC_DEPRECATED) != 0;
106 boolean isInterface = (flags & Opcodes.ACC_INTERFACE) != 0;
107 boolean isEnum = (flags & Opcodes.ACC_ENUM) != 0;
108 boolean isAnnotationType = (flags & Opcodes.ACC_ANNOTATION) != 0;
110 final byte stubFlags = PsiClassStubImpl.packFlags(isDeprecated, isInterface, isEnum, false, false, isAnnotationType, false, false);
112 myResult = new PsiClassStubImpl(JavaStubElementTypes.CLASS, myParent, fqn, shortName, null, stubFlags);
114 ((PsiClassStubImpl)myResult).setLanguageLevel(convertFromVersion(version));
115 myModlist = new PsiModifierListStubImpl(myResult, packModlistFlags(flags));
117 CharacterIterator signatureIterator = signature != null ? new StringCharacterIterator(signature) : null;
118 if (signatureIterator != null) {
119 try {
120 SignatureParsing.parseTypeParametersDeclaration(signatureIterator, myResult);
122 catch (ClsFormatException e) {
123 signatureIterator = null;
126 else {
127 new PsiTypeParameterListStubImpl(myResult);
130 String convertedSuper;
131 List<String> convertedInterfaces = new ArrayList<String>();
132 if (signatureIterator == null) {
133 convertedSuper = parseClassDescription(superName, interfaces, convertedInterfaces);
135 else {
136 try {
137 convertedSuper = parseClassSignature(signatureIterator, convertedInterfaces);
139 catch (ClsFormatException e) {
140 new PsiTypeParameterListStubImpl(myResult);
141 convertedSuper = parseClassDescription(superName, interfaces, convertedInterfaces);
145 String[] interfacesArray = convertedInterfaces.toArray(new String[convertedInterfaces.size()]);
146 if (isInterface) {
147 new PsiClassReferenceListStubImpl(JavaStubElementTypes.EXTENDS_LIST, myResult, interfacesArray, PsiReferenceList.Role.EXTENDS_LIST);
148 new PsiClassReferenceListStubImpl(JavaStubElementTypes.IMPLEMENTS_LIST, myResult, EMPTY_STRINGS, PsiReferenceList.Role.IMPLEMENTS_LIST);
150 else {
151 if (convertedSuper != null && !"java.lang.Object".equals(convertedSuper)) {
152 new PsiClassReferenceListStubImpl(JavaStubElementTypes.EXTENDS_LIST, myResult, new String[] {convertedSuper}, PsiReferenceList.Role.EXTENDS_LIST);
154 else {
155 new PsiClassReferenceListStubImpl(JavaStubElementTypes.EXTENDS_LIST, myResult, EMPTY_STRINGS, PsiReferenceList.Role.EXTENDS_LIST);
157 new PsiClassReferenceListStubImpl(JavaStubElementTypes.IMPLEMENTS_LIST, myResult, interfacesArray, PsiReferenceList.Role.IMPLEMENTS_LIST);
161 @Nullable
162 private static String parseClassDescription(final String superName, final String[] interfaces, final List<String> convertedInterfaces) {
163 final String convertedSuper;
164 convertedSuper = superName != null ? getClassName(superName) : null;
165 for (String anInterface : interfaces) {
166 convertedInterfaces.add(getClassName(anInterface));
168 return convertedSuper;
171 @Nullable
172 private static String parseClassSignature(final CharacterIterator signatureIterator, final List<String> convertedInterfaces)
173 throws ClsFormatException {
174 final String convertedSuper;
175 convertedSuper = SignatureParsing.parseToplevelClassRefSignature(signatureIterator);
176 while (signatureIterator.current() != CharacterIterator.DONE) {
177 final String ifs = SignatureParsing.parseToplevelClassRefSignature(signatureIterator);
178 if (ifs == null) throw new ClsFormatException();
180 convertedInterfaces.add(ifs);
182 return convertedSuper;
185 private static LanguageLevel convertFromVersion(final int version) {
186 if (version == Opcodes.V1_1 || version == Opcodes.V1_2 || version == Opcodes.V1_3) {
187 return LanguageLevel.JDK_1_3;
190 if (version == Opcodes.V1_4) {
191 return LanguageLevel.JDK_1_4;
194 if (version == Opcodes.V1_5 || version == Opcodes.V1_6) {
195 return LanguageLevel.JDK_1_5;
198 return LanguageLevel.HIGHEST;
201 private static int packModlistFlags(final int access) {
202 int flags = 0;
204 if ((access & Opcodes.ACC_PRIVATE) != 0) {
205 flags |= ModifierFlags.PRIVATE_MASK;
207 else if ((access & Opcodes.ACC_PROTECTED) != 0) {
208 flags |= ModifierFlags.PROTECTED_MASK;
210 else if ((access & Opcodes.ACC_PUBLIC) != 0) {
211 flags |= ModifierFlags.PUBLIC_MASK;
213 else {
214 flags |= ModifierFlags.PACKAGE_LOCAL_MASK;
217 if ((access & Opcodes.ACC_ABSTRACT) != 0) {
218 flags |= ModifierFlags.ABSTRACT_MASK;
220 if ((access & Opcodes.ACC_FINAL) != 0) {
221 flags |= ModifierFlags.FINAL_MASK;
223 if ((access & Opcodes.ACC_NATIVE) != 0) {
224 flags |= ModifierFlags.NATIVE_MASK;
226 if ((access & Opcodes.ACC_STATIC) != 0) {
227 flags |= ModifierFlags.STATIC_MASK;
229 if ((access & Opcodes.ACC_TRANSIENT) != 0) {
230 flags |= ModifierFlags.TRANSIENT_MASK;
232 if ((access & Opcodes.ACC_VOLATILE) != 0) {
233 flags |= ModifierFlags.VOLATILE_MASK;
235 if ((access & Opcodes.ACC_STRICT) != 0) {
236 flags |= ModifierFlags.STRICTFP_MASK;
239 return flags;
242 public void visitSource(final String source, final String debug) {
243 ((PsiClassStubImpl)myResult).setSourceFileName(source);
246 public void visitOuterClass(final String owner, final String name, final String desc) {
249 public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) {
250 return new AnnotationTextCollector(desc, new AnnotationResultCallback() {
251 public void callback(final String text) {
252 new PsiAnnotationStubImpl(myModlist, text);
257 public void visitAttribute(final Attribute attr) {
260 public void visitInnerClass(final String name, final String outerName, final String innerName, final int access) {
261 if (innerName != null && outerName != null && getClassName(outerName).equals(myResult.getQualifiedName())) {
262 final String basename = myVFile.getNameWithoutExtension();
263 final VirtualFile dir = myVFile.getParent();
264 assert dir != null;
266 final VirtualFile innerFile = dir.findChild(basename + "$" + innerName + ".class");
267 if (innerFile != null) {
268 try {
269 buildClass(innerFile, innerFile.contentsToByteArray(), myResult, access);
271 catch (IOException e) {
272 // No inner class file found, ignore.
278 public FieldVisitor visitField(final int access, final String name, final String desc, final String signature, final Object value) {
279 final byte flags = PsiFieldStubImpl.packFlags((access & Opcodes.ACC_ENUM) != 0, (access & Opcodes.ACC_DEPRECATED) != 0, false);
280 PsiFieldStub stub = new PsiFieldStubImpl(myResult, name, fieldType(desc, signature), constToString(value), flags);
281 final PsiModifierListStub modlist = new PsiModifierListStubImpl(stub, packModlistFlags(access));
282 return new AnnotationCollectingVisitor(stub, modlist);
285 private static TypeInfo fieldType(String desc, String signature) {
286 if (signature != null) {
287 try {
288 return TypeInfo.fromString(SignatureParsing.parseTypeString(new StringCharacterIterator(signature, 0)));
290 catch (ClsFormatException e) {
291 return fieldTypeViaDescription(desc);
294 else {
295 return fieldTypeViaDescription(desc);
299 private static TypeInfo fieldTypeViaDescription(final String desc) {
300 Type type = Type.getType(desc);
301 final int dim = type.getSort() == Type.ARRAY ? type.getDimensions() : 0;
302 if (dim > 0) {
303 type = type.getElementType();
305 final TypeInfo info = new TypeInfo();
306 info.arrayCount = (byte)dim;
307 info.text = StringRef.fromString(getTypeText(type));
308 info.isEllipsis = false;
309 return info;
313 @Nullable
314 public MethodVisitor visitMethod(final int access,
315 final String name,
316 final String desc,
317 final String signature,
318 final String[] exceptions) {
319 if ((access & Opcodes.ACC_SYNTHETIC) != 0) return null;
320 if ((access & Opcodes.ACC_BRIDGE) != 0) return null;
321 if (SYNTHETIC_CLINIT_METHOD.equals(name)) return null;
323 boolean isDeprecated = (access & Opcodes.ACC_DEPRECATED) != 0;
324 boolean isConstructor = SYNTHETIC_INIT_METHOD.equals(name);
325 boolean isVarargs = (access & Opcodes.ACC_VARARGS) != 0;
326 boolean isAnnotationMethod = myResult.isAnnotationType();
328 final byte flags = PsiMethodStubImpl.packFlags(isConstructor, isAnnotationMethod, isVarargs, isDeprecated, false);
330 String canonicalMethodName = isConstructor ? myResult.getName() : name;
331 PsiMethodStubImpl stub = new PsiMethodStubImpl(myResult, canonicalMethodName, null, flags, null);
332 PsiModifierListStub modlist = new PsiModifierListStubImpl(stub, packMethodFlags(access));
334 String returnType;
335 List<String> args = new ArrayList<String>();
336 if (signature == null) {
337 returnType = parseMethodViaDescription(desc, stub, args);
339 else {
340 try {
341 returnType = parseMethodViaGenericSignature(signature, stub, args);
343 catch (ClsFormatException e) {
344 returnType = parseMethodViaDescription(desc, stub, args);
347 stub.setReturnType(TypeInfo.fromString(returnType));
349 boolean nonStaticInnerClassConstructor = isConstructor && !(myParent instanceof PsiFileStub) && (myModlist.getModifiersMask() & Opcodes.ACC_STATIC) == 0;
351 final PsiParameterListStubImpl parameterList = new PsiParameterListStubImpl(stub);
352 final int paramCount = args.size();
353 for (int i = 0; i < paramCount; i++) {
354 if (nonStaticInnerClassConstructor && i == 0) continue;
356 String arg = args.get(i);
357 boolean isEllipsisParam = isVarargs && i == (paramCount - 1);
358 final TypeInfo typeInfo = TypeInfo.fromString(arg);
359 if (isEllipsisParam) {
360 typeInfo.isEllipsis = true;
363 PsiParameterStubImpl parameterStub = new PsiParameterStubImpl(parameterList, "p" + (i + 1), typeInfo, isEllipsisParam);
364 new PsiModifierListStubImpl(parameterStub, 0);
367 if (exceptions != null) {
368 String[] converted = new String[exceptions.length];
369 for (int i = 0; i < converted.length; i++) {
370 converted[i] = getClassName(exceptions[i]);
372 new PsiClassReferenceListStubImpl(JavaStubElementTypes.THROWS_LIST, stub, converted, PsiReferenceList.Role.THROWS_LIST);
374 else {
375 new PsiClassReferenceListStubImpl(JavaStubElementTypes.THROWS_LIST, stub, EMPTY_STRINGS, PsiReferenceList.Role.THROWS_LIST);
378 return new AnnotationCollectingVisitor(stub, modlist);
381 private static int packMethodFlags(final int access) {
382 int commonFlags = packModlistFlags(access);
383 if ((access & Opcodes.ACC_SYNCHRONIZED) != 0) {
384 commonFlags |= ModifierFlags.SYNCHRONIZED_MASK;
387 return commonFlags;
390 private static String parseMethodViaDescription(final String desc, final PsiMethodStubImpl stub, final List<String> args) {
391 final String returnType;
392 returnType = getTypeText(Type.getReturnType(desc));
393 final Type[] argTypes = Type.getArgumentTypes(desc);
394 for (Type argType : argTypes) {
395 args.add(getTypeText(argType));
397 new PsiTypeParameterListStubImpl(stub);
398 return returnType;
401 private static String parseMethodViaGenericSignature(final String signature, final PsiMethodStubImpl stub, final List<String> args)
402 throws ClsFormatException {
403 final String returnType;
404 StringCharacterIterator iterator = new StringCharacterIterator(signature);
405 SignatureParsing.parseTypeParametersDeclaration(iterator, stub);
407 if (iterator.current() != '(') {
408 throw new ClsFormatException();
410 iterator.next();
412 while (iterator.current() != ')' && iterator.current() != CharacterIterator.DONE) {
413 args.add(SignatureParsing.parseTypeString(iterator));
416 if (iterator.current() != ')') {
417 throw new ClsFormatException();
419 iterator.next();
421 returnType = SignatureParsing.parseTypeString(iterator);
422 return returnType;
425 public void visitEnd() {
429 private static class AnnotationTextCollector implements AnnotationVisitor {
430 private final StringBuilder myBuilder = new StringBuilder();
431 private final AnnotationResultCallback myCallback;
432 private boolean hasParams = false;
433 private final String myDesc;
435 public AnnotationTextCollector(@Nullable String desc, AnnotationResultCallback callback) {
436 myCallback = callback;
438 myDesc = desc;
439 if (desc != null) {
440 myBuilder.append('@').append(getTypeText(Type.getType(desc)));
444 public void visit(final String name, final Object value) {
445 valuePairPrefix(name);
446 myBuilder.append(constToString(value));
449 public void visitEnum(final String name, final String desc, final String value) {
450 valuePairPrefix(name);
451 myBuilder.append(getTypeText(Type.getType(desc))).append(".").append(value);
454 private void valuePairPrefix(final String name) {
455 if (!hasParams) {
456 hasParams = true;
457 if (myDesc != null) {
458 myBuilder.append('(');
461 else {
462 myBuilder.append(',');
465 if (name != null && !"value".equals(name)) {
466 myBuilder.append(name).append('=');
470 public AnnotationVisitor visitAnnotation(final String name, final String desc) {
471 valuePairPrefix(name);
472 return new AnnotationTextCollector(desc, new AnnotationResultCallback() {
473 public void callback(final String text) {
474 myBuilder.append(text);
479 public AnnotationVisitor visitArray(final String name) {
480 valuePairPrefix(name);
481 myBuilder.append("{");
482 return new AnnotationTextCollector(null, new AnnotationResultCallback() {
483 public void callback(final String text) {
484 myBuilder.append(text).append('}');
489 public void visitEnd() {
490 if (hasParams && myDesc != null) {
491 myBuilder.append(')');
493 myCallback.callback(myBuilder.toString());
497 private static class AnnotationCollectingVisitor extends EmptyVisitor {
498 private final StubElement myOwner;
499 private final PsiModifierListStub myModList;
501 private AnnotationCollectingVisitor(final StubElement owner, final PsiModifierListStub modList) {
502 myOwner = owner;
503 myModList = modList;
506 public AnnotationVisitor visitAnnotationDefault() {
507 return new AnnotationTextCollector(null, new AnnotationResultCallback() {
508 public void callback(final String text) {
509 ((PsiMethodStubImpl)myOwner).setDefaultValueText(text);
514 public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) {
515 return new AnnotationTextCollector(desc, new AnnotationResultCallback() {
516 public void callback(final String text) {
517 new PsiAnnotationStubImpl(myModList, text);
522 public AnnotationVisitor visitParameterAnnotation(final int parameter, final String desc, final boolean visible) {
523 return new AnnotationTextCollector(desc, new AnnotationResultCallback() {
524 public void callback(final String text) {
525 new PsiAnnotationStubImpl(((PsiMethodStubImpl)myOwner).findParameter(parameter).getModList(), text);
531 @SuppressWarnings({"HardCodedStringLiteral"})
532 @Nullable
533 private static String constToString(final Object value) {
534 if (value == null) return null;
536 if (value instanceof String) return "\"" + StringUtil.escapeStringCharacters((String)value) + "\"";
537 if (value instanceof Integer) return value.toString();
538 if (value instanceof Long) return value.toString() + "L";
540 if (value instanceof Double) {
541 final double d = ((Double)value).doubleValue();
542 if (Double.isInfinite(d)) {
543 return d > 0 ? "1.0 / 0.0" : "-1.0 / 0.0";
545 else if (Double.isNaN(d)) {
546 return "0.0d / 0.0";
548 return Double.toString(d);
551 if (value instanceof Float) {
552 final float v = ((Float)value).floatValue();
554 if (Float.isInfinite(v)) {
555 return v > 0 ? "1.0f / 0.0" : "-1.0f / 0.0";
557 else if (Float.isNaN(v)) {
558 return "0.0f / 0.0";
560 else {
561 return Float.toString(v) + "f";
565 return null;
568 private interface AnnotationResultCallback {
569 void callback(String text);
572 private static String getClassName(final String name) {
573 return getTypeText(Type.getObjectType(name));
576 private static String getTypeText(final Type type) {
577 final String raw = type.getClassName();
578 return raw.replace('$', '.');