private void checkCompoundIds(Class<?> javaClass) throws IOException { String javaClassName = javaClass.getCanonicalName(); PsiClass psiClass = myJavaPsiFacade.findClass( javaClassName, GlobalSearchScope.moduleWithLibrariesScope(myModule)); assertNotNull(psiClass); for (java.lang.reflect.Method javaMethod : javaClass.getDeclaredMethods()) { Method method = new Method( Type.getType(javaClass).getInternalName(), javaMethod.getName(), Type.getMethodDescriptor(javaMethod)); boolean noKey = javaMethod.getAnnotation(ExpectNoPsiKey.class) != null; PsiMethod psiMethod = psiClass.findMethodsByName(javaMethod.getName(), false)[0]; checkCompoundId(method, psiMethod, noKey); } for (Constructor<?> constructor : javaClass.getDeclaredConstructors()) { Method method = new Method( Type.getType(javaClass).getInternalName(), "<init>", Type.getConstructorDescriptor(constructor)); boolean noKey = constructor.getAnnotation(ExpectNoPsiKey.class) != null; PsiMethod[] constructors = psiClass.getConstructors(); PsiMethod psiMethod = constructors[0]; checkCompoundId(method, psiMethod, noKey); } }
public AnnotationTextCollector(@Nullable String desc, AnnotationResultCallback callback) { super(ASM_API); myCallback = callback; myDesc = desc; if (desc != null) { myBuilder.append('@').append(getTypeText(Type.getType(desc))); } }
@Override public BasicValue naryOperation(AbstractInsnNode insn, List<? extends BasicValue> values) throws AnalyzerException { int opcode = insn.getOpcode(); if (opcode == MULTIANEWARRAY) { return newValue(Type.getType(((MultiANewArrayInsnNode) insn).desc)); } else if (opcode == INVOKEDYNAMIC) { return newValue(Type.getReturnType(((InvokeDynamicInsnNode) insn).desc)); } else { return newValue(Type.getReturnType(((MethodInsnNode) insn).desc)); } }
@NotNull private static TypeInfo fieldTypeViaDescription(@NotNull String desc) { Type type = Type.getType(desc); final int dim = type.getSort() == Type.ARRAY ? type.getDimensions() : 0; if (dim > 0) { type = type.getElementType(); } return new TypeInfo( getTypeText(type), (byte) dim, false, PsiAnnotationStub.EMPTY_ARRAY); // todo read annos from .class file }
// add assert startup code private void initAssertions(MethodVisitor mv) { mv.visitLdcInsn(Type.getType("L" + myClassName + ";")); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "desiredAssertionStatus", "()Z", false); Label l0 = new Label(); mv.visitJumpInsn(IFNE, l0); mv.visitInsn(ICONST_1); Label l1 = new Label(); mv.visitJumpInsn(GOTO, l1); mv.visitLabel(l0); mv.visitInsn(ICONST_0); mv.visitLabel(l1); mv.visitFieldInsn(PUTSTATIC, myClassName, ASSERTIONS_DISABLED_NAME, "Z"); }
private static void checkLeakingParameters(Class<?> jClass) throws IOException { final HashMap<Method, boolean[]> map = new HashMap<Method, boolean[]>(); // collecting leakedParameters final ClassReader classReader = new ClassReader( new FileInputStream( jClass.getResource("/" + jClass.getName().replace('.', '/') + ".class").getFile())); classReader.accept( new ClassVisitor(Opcodes.ASM5) { @Override public MethodVisitor visitMethod( int access, String name, String desc, String signature, String[] exceptions) { final MethodNode node = new MethodNode(Opcodes.ASM5, access, name, desc, signature, exceptions); final Method method = new Method(classReader.getClassName(), name, desc); return new MethodVisitor(Opcodes.ASM5, node) { @Override public void visitEnd() { super.visitEnd(); try { map.put( method, LeakingParameters.build(classReader.getClassName(), node, false).parameters); } catch (AnalyzerException ignore) { } } }; } }, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); for (java.lang.reflect.Method jMethod : jClass.getDeclaredMethods()) { Method method = new Method( Type.getType(jClass).getInternalName(), jMethod.getName(), Type.getMethodDescriptor(jMethod)); Annotation[][] annotations = jMethod.getParameterAnnotations(); for (int i = 0; i < annotations.length; i++) { boolean isLeaking = false; Annotation[] parameterAnnotations = annotations[i]; for (Annotation parameterAnnotation : parameterAnnotations) { if (parameterAnnotation.annotationType() == ExpectLeaking.class) { isLeaking = true; } } assertEquals(method.toString() + " #" + i, isLeaking, map.get(method)[i]); } } }
private List<CapturedParamInfo> extractParametersMappingAndPatchConstructor( @NotNull MethodNode constructor, @NotNull ParametersBuilder capturedParamBuilder, @NotNull ParametersBuilder constructorParamBuilder, @NotNull final AnonymousObjectGeneration anonymousObjectGen, @NotNull FieldRemapper parentFieldRemapper) { CapturedParamOwner owner = new CapturedParamOwner() { @Override public Type getType() { return Type.getObjectType(anonymousObjectGen.getOwnerInternalName()); } }; Set<LambdaInfo> capturedLambdas = new LinkedHashSet<LambdaInfo>(); // captured var of inlined parameter List<CapturedParamInfo> constructorAdditionalFakeParams = new ArrayList<CapturedParamInfo>(); Map<Integer, LambdaInfo> indexToLambda = anonymousObjectGen.getLambdasToInline(); Set<Integer> capturedParams = new HashSet<Integer>(); // load captured parameters and patch instruction list (NB: there is also could be object // fields) AbstractInsnNode cur = constructor.instructions.getFirst(); while (cur != null) { if (cur instanceof FieldInsnNode) { FieldInsnNode fieldNode = (FieldInsnNode) cur; String fieldName = fieldNode.name; if (fieldNode.getOpcode() == Opcodes.PUTFIELD && InlineCodegenUtil.isCapturedFieldName(fieldName)) { boolean isPrevVarNode = fieldNode.getPrevious() instanceof VarInsnNode; boolean isPrevPrevVarNode = isPrevVarNode && fieldNode.getPrevious().getPrevious() instanceof VarInsnNode; if (isPrevPrevVarNode) { VarInsnNode node = (VarInsnNode) fieldNode.getPrevious().getPrevious(); if (node.var == 0) { VarInsnNode previous = (VarInsnNode) fieldNode.getPrevious(); int varIndex = previous.var; LambdaInfo lambdaInfo = indexToLambda.get(varIndex); String newFieldName = isThis0(fieldName) && shouldRenameThis0(parentFieldRemapper, indexToLambda.values()) ? getNewFieldName(fieldName, true) : fieldName; CapturedParamInfo info = capturedParamBuilder.addCapturedParam( owner, fieldName, newFieldName, Type.getType(fieldNode.desc), lambdaInfo != null, null); if (lambdaInfo != null) { info.setLambda(lambdaInfo); capturedLambdas.add(lambdaInfo); } constructorAdditionalFakeParams.add(info); capturedParams.add(varIndex); constructor.instructions.remove(previous.getPrevious()); constructor.instructions.remove(previous); AbstractInsnNode temp = cur; cur = cur.getNext(); constructor.instructions.remove(temp); continue; } } } } cur = cur.getNext(); } constructorParamBuilder.addThis(oldObjectType, false); String constructorDesc = anonymousObjectGen.getConstructorDesc(); if (constructorDesc == null) { // in case of anonymous object with empty closure constructorDesc = Type.getMethodDescriptor(Type.VOID_TYPE); } Type[] types = Type.getArgumentTypes(constructorDesc); for (Type type : types) { LambdaInfo info = indexToLambda.get(constructorParamBuilder.getNextValueParameterIndex()); ParameterInfo parameterInfo = constructorParamBuilder.addNextParameter(type, info != null, null); parameterInfo.setLambda(info); if (capturedParams.contains(parameterInfo.getIndex())) { parameterInfo.setCaptured(true); } else { // otherwise it's super constructor parameter } } // For all inlined lambdas add their captured parameters // TODO: some of such parameters could be skipped - we should perform additional analysis Map<String, LambdaInfo> capturedLambdasToInline = new HashMap<String, LambdaInfo>(); // captured var of inlined parameter List<CapturedParamDesc> allRecapturedParameters = new ArrayList<CapturedParamDesc>(); boolean addCapturedNotAddOuter = parentFieldRemapper.isRoot() || (parentFieldRemapper instanceof InlinedLambdaRemapper && parentFieldRemapper.getParent().isRoot()); Map<String, CapturedParamInfo> alreadyAdded = new HashMap<String, CapturedParamInfo>(); for (LambdaInfo info : capturedLambdas) { if (addCapturedNotAddOuter) { for (CapturedParamDesc desc : info.getCapturedVars()) { String key = desc.getFieldName() + "$$$" + desc.getType().getClassName(); CapturedParamInfo alreadyAddedParam = alreadyAdded.get(key); CapturedParamInfo recapturedParamInfo = capturedParamBuilder.addCapturedParam( desc, alreadyAddedParam != null ? alreadyAddedParam.getNewFieldName() : getNewFieldName(desc.getFieldName(), false)); StackValue composed = StackValue.field( desc.getType(), oldObjectType, /*TODO owner type*/ recapturedParamInfo.getNewFieldName(), false, StackValue.LOCAL_0); recapturedParamInfo.setRemapValue(composed); allRecapturedParameters.add(desc); constructorParamBuilder .addCapturedParam(recapturedParamInfo, recapturedParamInfo.getNewFieldName()) .setRemapValue(composed); if (alreadyAddedParam != null) { recapturedParamInfo.setSkipInConstructor(true); } if (isThis0(desc.getFieldName())) { alreadyAdded.put(key, recapturedParamInfo); } } } capturedLambdasToInline.put(info.getLambdaClassType().getInternalName(), info); } if (parentFieldRemapper instanceof InlinedLambdaRemapper && !capturedLambdas.isEmpty() && !addCapturedNotAddOuter) { // lambda with non InlinedLambdaRemapper already have outer FieldRemapper parent = parentFieldRemapper.getParent(); assert parent instanceof RegeneratedLambdaFieldRemapper; final Type ownerType = Type.getObjectType(parent.getLambdaInternalName()); CapturedParamDesc desc = new CapturedParamDesc( new CapturedParamOwner() { @Override public Type getType() { return ownerType; } }, InlineCodegenUtil.THIS, ownerType); CapturedParamInfo recapturedParamInfo = capturedParamBuilder.addCapturedParam( desc, InlineCodegenUtil.THIS$0 /*outer lambda/object*/); StackValue composed = StackValue.LOCAL_0; recapturedParamInfo.setRemapValue(composed); allRecapturedParameters.add(desc); constructorParamBuilder .addCapturedParam(recapturedParamInfo, recapturedParamInfo.getNewFieldName()) .setRemapValue(composed); } anonymousObjectGen.setAllRecapturedParameters(allRecapturedParameters); anonymousObjectGen.setCapturedLambdasToInline(capturedLambdasToInline); return constructorAdditionalFakeParams; }
public void generatePushValue(final GeneratorAdapter generator, final Object value) { final Type enumType = Type.getType(value.getClass()); generator.getStatic(enumType, value.toString(), enumType); }
@Override public void visitEnum(final String name, final String desc, final String value) { valuePairPrefix(name); myBuilder.append(getTypeText(Type.getType(desc))).append(".").append(value); }
@Override public BasicValue newOperation(@NotNull AbstractInsnNode insn) throws AnalyzerException { if (insn.getOpcode() == Opcodes.ACONST_NULL) { return newValue(Type.getObjectType("java/lang/Object")); } switch (insn.getOpcode()) { case ACONST_NULL: return newValue(Type.getObjectType("null")); case ICONST_M1: case ICONST_0: case ICONST_1: case ICONST_2: case ICONST_3: case ICONST_4: case ICONST_5: return StrictBasicValue.INT_VALUE; case LCONST_0: case LCONST_1: return StrictBasicValue.LONG_VALUE; case FCONST_0: case FCONST_1: case FCONST_2: return StrictBasicValue.FLOAT_VALUE; case DCONST_0: case DCONST_1: return StrictBasicValue.DOUBLE_VALUE; case BIPUSH: case SIPUSH: return StrictBasicValue.INT_VALUE; case LDC: Object cst = ((LdcInsnNode) insn).cst; if (cst instanceof Integer) { return StrictBasicValue.INT_VALUE; } else if (cst instanceof Float) { return StrictBasicValue.FLOAT_VALUE; } else if (cst instanceof Long) { return StrictBasicValue.LONG_VALUE; } else if (cst instanceof Double) { return StrictBasicValue.DOUBLE_VALUE; } else if (cst instanceof String) { return newValue(Type.getObjectType("java/lang/String")); } else if (cst instanceof Type) { int sort = ((Type) cst).getSort(); if (sort == Type.OBJECT || sort == Type.ARRAY) { return newValue(Type.getObjectType("java/lang/Class")); } else if (sort == Type.METHOD) { return newValue(Type.getObjectType("java/lang/invoke/MethodType")); } else { throw new IllegalArgumentException("Illegal LDC constant " + cst); } } else if (cst instanceof Handle) { return newValue(Type.getObjectType("java/lang/invoke/MethodHandle")); } else { throw new IllegalArgumentException("Illegal LDC constant " + cst); } case GETSTATIC: return newValue(Type.getType(((FieldInsnNode) insn).desc)); case NEW: return newValue(Type.getObjectType(((TypeInsnNode) insn).desc)); default: throw new Error("Internal error."); } }
@Override public BasicValue unaryOperation(AbstractInsnNode insn, BasicValue value) throws AnalyzerException { switch (insn.getOpcode()) { case INEG: case IINC: case L2I: case F2I: case D2I: case I2B: case I2C: case I2S: return StrictBasicValue.INT_VALUE; case FNEG: case I2F: case L2F: case D2F: return StrictBasicValue.FLOAT_VALUE; case LNEG: case I2L: case F2L: case D2L: return StrictBasicValue.LONG_VALUE; case DNEG: case I2D: case L2D: case F2D: return StrictBasicValue.DOUBLE_VALUE; case IFEQ: case IFNE: case IFLT: case IFGE: case IFGT: case IFLE: case TABLESWITCH: case LOOKUPSWITCH: case IRETURN: case LRETURN: case FRETURN: case DRETURN: case ARETURN: case PUTSTATIC: return null; case GETFIELD: return newValue(Type.getType(((FieldInsnNode) insn).desc)); case NEWARRAY: switch (((IntInsnNode) insn).operand) { case T_BOOLEAN: return newValue(Type.getType("[Z")); case T_CHAR: return newValue(Type.getType("[C")); case T_BYTE: return newValue(Type.getType("[B")); case T_SHORT: return newValue(Type.getType("[S")); case T_INT: return newValue(Type.getType("[I")); case T_FLOAT: return newValue(Type.getType("[F")); case T_DOUBLE: return newValue(Type.getType("[D")); case T_LONG: return newValue(Type.getType("[J")); default: throw new AnalyzerException(insn, "Invalid array type"); } case ANEWARRAY: String desc = ((TypeInsnNode) insn).desc; return newValue(Type.getType("[" + Type.getObjectType(desc))); case ARRAYLENGTH: return StrictBasicValue.INT_VALUE; case ATHROW: return null; case CHECKCAST: desc = ((TypeInsnNode) insn).desc; return newValue(Type.getObjectType(desc)); case INSTANCEOF: return StrictBasicValue.INT_VALUE; case MONITORENTER: case MONITOREXIT: case IFNULL: case IFNONNULL: return null; default: throw new Error("Internal error."); } }
/** * @author yole * @noinspection HardCodedStringLiteral */ public class FontPropertyCodeGenerator extends PropertyCodeGenerator { private static final Type ourFontType = Type.getType(Font.class); private static final Type ourUIManagerType = Type.getType("Ljavax/swing/UIManager;"); private static final Type ourObjectType = Type.getType(Object.class); private static final Type ourStringType = Type.getType(String.class); private static final Method ourInitMethod = Method.getMethod("void <init>(java.lang.String,int,int)"); private static final Method ourUIManagerGetFontMethod = new Method("getFont", ourFontType, new Type[] {ourObjectType}); private static final Method ourGetNameMethod = new Method("getName", ourStringType, new Type[0]); private static final Method ourGetSizeMethod = new Method("getSize", Type.INT_TYPE, new Type[0]); private static final Method ourGetStyleMethod = new Method("getStyle", Type.INT_TYPE, new Type[0]); public boolean generateCustomSetValue( final LwComponent lwComponent, final InstrumentationClassFinder.PseudoClass componentClass, final LwIntrospectedProperty property, final GeneratorAdapter generator, final int componentLocal, final String formClassName) { FontDescriptor descriptor = (FontDescriptor) property.getPropertyValue(lwComponent); if (descriptor.isFixedFont() && !descriptor.isFullyDefinedFont()) { generator.loadLocal(componentLocal); generatePushFont( generator, componentLocal, lwComponent, descriptor, property.getReadMethodName()); Method setFontMethod = new Method(property.getWriteMethodName(), Type.VOID_TYPE, new Type[] {ourFontType}); Type componentType = AsmCodeGenerator.typeFromClassName(lwComponent.getComponentClassName()); generator.invokeVirtual(componentType, setFontMethod); return true; } return false; } public static void generatePushFont( final GeneratorAdapter generator, final int componentLocal, final LwComponent lwComponent, final FontDescriptor descriptor, final String readMethodName) { final int fontLocal = generator.newLocal(ourFontType); generator.loadLocal(componentLocal); Type componentType = AsmCodeGenerator.typeFromClassName(lwComponent.getComponentClassName()); Method getFontMethod = new Method(readMethodName, ourFontType, new Type[0]); generator.invokeVirtual(componentType, getFontMethod); generator.storeLocal(fontLocal); generator.newInstance(ourFontType); generator.dup(); if (descriptor.getFontName() != null) { generator.push(descriptor.getFontName()); } else { generator.loadLocal(fontLocal); generator.invokeVirtual(ourFontType, ourGetNameMethod); } if (descriptor.getFontStyle() >= 0) { generator.push(descriptor.getFontStyle()); } else { generator.loadLocal(fontLocal); generator.invokeVirtual(ourFontType, ourGetStyleMethod); } if (descriptor.getFontSize() >= 0) { generator.push(descriptor.getFontSize()); } else { generator.loadLocal(fontLocal); generator.invokeVirtual(ourFontType, ourGetSizeMethod); } generator.invokeConstructor(ourFontType, ourInitMethod); } public void generatePushValue(final GeneratorAdapter generator, final Object value) { FontDescriptor descriptor = (FontDescriptor) value; if (descriptor.isFixedFont()) { if (!descriptor.isFullyDefinedFont()) throw new IllegalStateException("Unexpected font state"); generator.newInstance(ourFontType); generator.dup(); generator.push(descriptor.getFontName()); generator.push(descriptor.getFontStyle()); generator.push(descriptor.getFontSize()); generator.invokeConstructor(ourFontType, ourInitMethod); } else if (descriptor.getSwingFont() != null) { generator.push(descriptor.getSwingFont()); generator.invokeStatic(ourUIManagerType, ourUIManagerGetFontMethod); } else { throw new IllegalStateException("Unknown font type"); } } }