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() {
40 public static PsiFileStub
build(final VirtualFile vFile
, byte[] bytes
) throws ClsFormatException
{
41 final PsiJavaFileStubImpl file
= new PsiJavaFileStubImpl("dont.know.yet", true);
43 final PsiClassStub result
= buildClass(vFile
, bytes
, file
, 0);
44 if (result
== null) return null;
46 file
.setPackageName(getPackageName(result
));
49 throw new ClsFormatException();
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
)) {
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
) {
89 public PsiClassStub
getResult() {
93 public void visit(final int version
,
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) {
120 SignatureParsing
.parseTypeParametersDeclaration(signatureIterator
, myResult
);
122 catch (ClsFormatException e
) {
123 signatureIterator
= null;
127 new PsiTypeParameterListStubImpl(myResult
);
130 String convertedSuper
;
131 List
<String
> convertedInterfaces
= new ArrayList
<String
>();
132 if (signatureIterator
== null) {
133 convertedSuper
= parseClassDescription(superName
, interfaces
, convertedInterfaces
);
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()]);
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
);
151 if (convertedSuper
!= null && !"java.lang.Object".equals(convertedSuper
)) {
152 new PsiClassReferenceListStubImpl(JavaStubElementTypes
.EXTENDS_LIST
, myResult
, new String
[] {convertedSuper
}, PsiReferenceList
.Role
.EXTENDS_LIST
);
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
);
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
;
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
) {
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
;
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
;
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();
266 final VirtualFile innerFile
= dir
.findChild(basename
+ "$" + innerName
+ ".class");
267 if (innerFile
!= null) {
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) {
288 return TypeInfo
.fromString(SignatureParsing
.parseTypeString(new StringCharacterIterator(signature
, 0)));
290 catch (ClsFormatException e
) {
291 return fieldTypeViaDescription(desc
);
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;
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;
314 public MethodVisitor
visitMethod(final int access
,
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
));
335 List
<String
> args
= new ArrayList
<String
>();
336 if (signature
== null) {
337 returnType
= parseMethodViaDescription(desc
, stub
, args
);
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
);
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
;
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
);
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();
412 while (iterator
.current() != ')' && iterator
.current() != CharacterIterator
.DONE
) {
413 args
.add(SignatureParsing
.parseTypeString(iterator
));
416 if (iterator
.current() != ')') {
417 throw new ClsFormatException();
421 returnType
= SignatureParsing
.parseTypeString(iterator
);
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
;
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
) {
457 if (myDesc
!= null) {
458 myBuilder
.append('(');
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
) {
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"})
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
)) {
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
)) {
561 return Float
.toString(v
) + "f";
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('$', '.');