private String getLambdaAccessMethodDesc(Handle implMethod) { if (implMethod.getTag() == H_INVOKESTATIC) { // static method call -> keep as-is return implMethod.getDesc(); } else if (implMethod.getTag() == H_NEWINVOKESPECIAL) { // constructor call -> change to a a factory method return Types.changeReturnType( Type.getObjectType(implMethod.getOwner()), implMethod.getDesc()); } else { // instance method call -> change to a static method return Types.prependArgumentType(Type.getObjectType(className), implMethod.getDesc()); } }
/** * Full constructor * * @param serializedLambda the fully {@link SerializedLambda} carrying all the required info. */ public SerializedLambdaInfo(final SerializedLambda serializedLambda) { this( Type.getObjectType(serializedLambda.getImplClass()).getClassName(), serializedLambda.getImplMethodName(), serializedLambda.getImplMethodSignature(), getCapturedArguments(serializedLambda)); }
/* (non-Javadoc) * @see edu.umd.cs.findbugs.ba.INullnessAnnotationDatabase#addDefaultAnnotation(java.lang.String, java.lang.String, edu.umd.cs.findbugs.ba.NullnessAnnotation) */ public void addDefaultAnnotation(Target target, String c, NullnessAnnotation n) { if (DEBUG) { System.out.println("addDefaultAnnotation: target=" + target + ", c=" + c + ", n=" + n); } ClassDescriptor classDesc = DescriptorFactory.instance().getClassDescriptorForDottedClassName(c); ClassInfo xclass; // Get the XClass (really a ClassInfo object) try { xclass = (ClassInfo) Global.getAnalysisCache().getClassAnalysis(XClass.class, classDesc); } catch (MissingClassException e) { // // AnalysisContext.currentAnalysisContext().getLookupFailureCallback().reportMissingClass(e.getClassDescriptor()); return; } catch (CheckedAnalysisException e) { // AnalysisContext.logError("Error adding built-in nullness annotation", e); return; } if (n == NullnessAnnotation.NONNULL && target == AnnotationDatabase.Target.PARAMETER) { xclass.addAnnotation(new AnnotationValue(PARAMETERS_ARE_NONNULL_BY_DEFAULT)); return; } else if (n == NullnessAnnotation.NONNULL && target == AnnotationDatabase.Target.METHOD) { xclass.addAnnotation(new AnnotationValue(RETURN_VALUES_ARE_NONNULL_BY_DEFAULT)); return; } // Get the default annotation type ClassDescriptor defaultAnnotationType; if (target == AnnotationDatabase.Target.ANY) { defaultAnnotationType = FindBugsDefaultAnnotations.DEFAULT_ANNOTATION; } else if (target == AnnotationDatabase.Target.FIELD) { defaultAnnotationType = FindBugsDefaultAnnotations.DEFAULT_ANNOTATION_FOR_FIELDS; } else if (target == AnnotationDatabase.Target.METHOD) { defaultAnnotationType = FindBugsDefaultAnnotations.DEFAULT_ANNOTATION_FOR_METHODS; } else if (target == AnnotationDatabase.Target.PARAMETER) { defaultAnnotationType = FindBugsDefaultAnnotations.DEFAULT_ANNOTATION_FOR_PARAMETERS; } else { throw new IllegalArgumentException("Unknown target for default annotation: " + target); } // Get the JSR-305 nullness annotation type ClassDescriptor nullnessAnnotationType = getNullnessAnnotationClassDescriptor(n); // Construct an AnnotationValue containing the default annotation AnnotationValue annotationValue = new AnnotationValue(defaultAnnotationType); AnnotationVisitor v = annotationValue.getAnnotationVisitor(); v.visit("value", Type.getObjectType(nullnessAnnotationType.getClassName())); v.visitEnd(); if (DEBUG) { System.out.println("Adding AnnotationValue " + annotationValue + " to class " + xclass); } // Destructively add the annotation to the ClassInfo object xclass.addAnnotation(annotationValue); }
private InsnList generateInvokeDynamicConstructor(String name, String owner, String desc) { InsnList insnList = new InsnList(); Handle methodHandle = new Handle( Opcodes.H_INVOKESTATIC, BOOTSTRAP_CLASS, "dynvokeConstructor", BOOTSTRAP_SIGNATURE); String descReceiver = Type.getMethodDescriptor(Type.getObjectType(owner), Type.getArgumentTypes(desc)); insnList.add(new InvokeDynamicInsnNode(owner + "." + name, descReceiver, methodHandle, "")); return insnList; }
@Override public void visit( int version, int access, String name, String signature, String superName, String[] interfaces) { this.className = Type.getObjectType(name).getClassName(); this.access = access; }
@Override public BasicValue newOperation(AbstractInsnNode insn) throws AnalyzerException { switch (insn.getOpcode()) { case ACONST_NULL: return new TaggedValue(Type.getObjectType("null"), Tag.NULL); case NEW: analyzer.incrNewOperationCounters(topLevelMethod); // make new objects a tagged value to have possibility to tag an // input container later return new TaggedValue(Type.getObjectType(((TypeInsnNode) insn).desc)); // tag "int"-like constants case BIPUSH: case SIPUSH: final IntInsnNode intInsn = (IntInsnNode) insn; return new TaggedValue(intInsn.operand); case LDC: final Object cst = ((LdcInsnNode) insn).cst; if (cst instanceof Integer) { return new TaggedValue((Integer) cst); } return super.newOperation(insn); case ICONST_M1: return new TaggedValue(-1); case ICONST_0: return new TaggedValue(0); case ICONST_1: return new TaggedValue(1); case ICONST_2: return new TaggedValue(2); case ICONST_3: return new TaggedValue(3); case ICONST_4: return new TaggedValue(4); case ICONST_5: return new TaggedValue(5); default: return super.newOperation(insn); } }
public static ClassMetadata scan(ClassReader source) { ClassMetadata info = new ClassMetadata(); source.accept(new MetaScanner(info), 0); info.type = Type.getObjectType(source.getClassName()); for (FieldDescriptor fd : info.fields) { if ((fd.access & ACC_PUBLIC) == ACC_PUBLIC) { info.directFieldAccess = true; } } return info; }
private InsnList generateInvokeDynamicVirtualInterfaceSpecial( String name, String owner, String desc, String bootstrapMethod) { InsnList insnList = new InsnList(); Handle methodHandle = new Handle(Opcodes.H_INVOKESTATIC, BOOTSTRAP_CLASS, bootstrapMethod, BOOTSTRAP_SIGNATURE); List<Type> argsList = new ArrayList<Type>(Arrays.asList(new Type[] {Type.getObjectType(owner)})); argsList.addAll(Arrays.asList(Type.getArgumentTypes(desc))); String descReceiver = Type.getMethodDescriptor( Type.getReturnType(desc), argsList.toArray(new Type[argsList.size()])); insnList.add(new InvokeDynamicInsnNode(owner + "." + name, descReceiver, methodHandle, "")); return insnList; }
@Override public void visitTypeInsn(int opcode, String type) { addType(Type.getObjectType(type)); }
private void addInternalName(String name) { addType(Type.getObjectType(name)); }
/** * A {@link MethodVisitor} that renumbers local variables in their order of appearance. This adapter * allows one to easily addClass new local variables to a method. It may be used by inheriting from * this class, but the preferred way of using it is via delegation: the next visitor in the chain * can indeed addClass new locals when needed by calling {@link #newLocal} on this adapter (this * requires a reference back to this {@link LocalVariablesSorter}). * * @author Chris Nokleberg * @author Eugene Kuleshov * @author Eric Bruneton */ public class LocalVariablesSorter extends MethodVisitor { private static final Type OBJECT_TYPE = Type.getObjectType("java/lang/Object"); /** * Mapping from old to new local variable indexes. A local variable at index i of size 1 is * remapped to 'mapping[2*i]', while a local variable at index i of size 2 is remapped to * 'mapping[2*i+1]'. */ private int[] mapping = new int[40]; /** Array used to store stack map local variable types after remapping. */ private Object[] newLocals = new Object[20]; /** Index of the first local variable, after formal parameters. */ protected final int firstLocal; /** Index of the next local variable to be created by {@link #newLocal}. */ protected int nextLocal; /** Indicates if at least one local variable has moved due to remapping. */ private boolean changed; /** * Creates a new {@link LocalVariablesSorter}. <i>Subclasses must not use this constructor</i>. * Instead, they must use the {@link #LocalVariablesSorter(int, int, String, MethodVisitor)} * version. * * @param access access flags of the adapted method. * @param desc the method's descriptor (see {@link Type Type}). * @param mv the method visitor to which this adapter delegates calls. */ public LocalVariablesSorter(final int access, final String desc, final MethodVisitor mv) { this(Opcodes.ASM4, access, desc, mv); } /** * Creates a new {@link LocalVariablesSorter}. * * @param api the ASM API version implemented by this visitor. Must be one of {@link * Opcodes#ASM4}. * @param access access flags of the adapted method. * @param desc the method's descriptor (see {@link Type Type}). * @param mv the method visitor to which this adapter delegates calls. */ protected LocalVariablesSorter( final int api, final int access, final String desc, final MethodVisitor mv) { super(api, mv); Type[] args = Type.getArgumentTypes(desc); nextLocal = (Opcodes.ACC_STATIC & access) == 0 ? 1 : 0; for (int i = 0; i < args.length; i++) { nextLocal += args[i].getSize(); } firstLocal = nextLocal; } @Override public void visitVarInsn(final int opcode, final int var) { Type type; switch (opcode) { case Opcodes.LLOAD: case Opcodes.LSTORE: type = Type.LONG_TYPE; break; case Opcodes.DLOAD: case Opcodes.DSTORE: type = Type.DOUBLE_TYPE; break; case Opcodes.FLOAD: case Opcodes.FSTORE: type = Type.FLOAT_TYPE; break; case Opcodes.ILOAD: case Opcodes.ISTORE: type = Type.INT_TYPE; break; default: // case Opcodes.ALOAD: // case Opcodes.ASTORE: // case RET: type = OBJECT_TYPE; break; } mv.visitVarInsn(opcode, remap(var, type)); } @Override public void visitIincInsn(final int var, final int increment) { mv.visitIincInsn(remap(var, Type.INT_TYPE), increment); } @Override public void visitMaxs(final int maxStack, final int maxLocals) { mv.visitMaxs(maxStack, nextLocal); } @Override public void visitLocalVariable( final String name, final String desc, final String signature, final Label start, final Label end, final int index) { int newIndex = remap(index, Type.getType(desc)); mv.visitLocalVariable(name, desc, signature, start, end, newIndex); } @Override public void visitFrame( final int type, final int nLocal, final Object[] local, final int nStack, final Object[] stack) { if (type != Opcodes.F_NEW) { // uncompressed frame throw new IllegalStateException( "ClassReader.accept() should be called with EXPAND_FRAMES flag"); } if (!changed) { // optimization for the case where mapping = identity mv.visitFrame(type, nLocal, local, nStack, stack); return; } // creates a copy of newLocals Object[] oldLocals = new Object[newLocals.length]; System.arraycopy(newLocals, 0, oldLocals, 0, oldLocals.length); updateNewLocals(newLocals); // copies types from 'local' to 'newLocals' // 'newLocals' already contains the variables added with 'newLocal' int index = 0; // old local variable index int number = 0; // old local variable number for (; number < nLocal; ++number) { Object t = local[number]; int size = t == Opcodes.LONG || t == Opcodes.DOUBLE ? 2 : 1; if (t != Opcodes.TOP) { Type typ = OBJECT_TYPE; if (t == Opcodes.INTEGER) { typ = Type.INT_TYPE; } else if (t == Opcodes.FLOAT) { typ = Type.FLOAT_TYPE; } else if (t == Opcodes.LONG) { typ = Type.LONG_TYPE; } else if (t == Opcodes.DOUBLE) { typ = Type.DOUBLE_TYPE; } else if (t instanceof String) { typ = Type.getObjectType((String) t); } setFrameLocal(remap(index, typ), t); } index += size; } // removes TOP after long and double types as well as trailing TOPs index = 0; number = 0; for (int i = 0; index < newLocals.length; ++i) { Object t = newLocals[index++]; if (t != null && t != Opcodes.TOP) { newLocals[i] = t; number = i + 1; if (t == Opcodes.LONG || t == Opcodes.DOUBLE) { index += 1; } } else { newLocals[i] = Opcodes.TOP; } } // visits remapped frame mv.visitFrame(type, number, newLocals, nStack, stack); // restores original value of 'newLocals' newLocals = oldLocals; } // ------------- /** * Creates a new local variable of the given type. * * @param type the type of the local variable to be created. * @return the identifier of the newly created local variable. */ public int newLocal(final Type type) { Object t; switch (type.getSort()) { case Type.BOOLEAN: case Type.CHAR: case Type.BYTE: case Type.SHORT: case Type.INT: t = Opcodes.INTEGER; break; case Type.FLOAT: t = Opcodes.FLOAT; break; case Type.LONG: t = Opcodes.LONG; break; case Type.DOUBLE: t = Opcodes.DOUBLE; break; case Type.ARRAY: t = type.getDescriptor(); break; // case Type.OBJECT: default: t = type.getInternalName(); break; } int local = newLocalMapping(type); setLocalType(local, type); setFrameLocal(local, t); return local; } /** * Notifies subclasses that a new stack map frame is being visited. The array argument contains * the stack map frame types corresponding to the local variables added with {@link #newLocal}. * This method can update these types in place for the stack map frame being visited. The default * implementation of this method does nothing, i.e. a local variable added with {@link #newLocal} * will have the same type in all stack map frames. But this behavior is not always the desired * one, for instance if a local variable is added in the middle of a try/catch block: the frame * for the exception handler should have a TOP type for this new local. * * @param newLocals the stack map frame types corresponding to the local variables added with * {@link #newLocal} (and null for the others). The format of this array is the same as in * {@link MethodVisitor#visitFrame}, except that long and double types use two slots. The * types for the current stack map frame must be updated in place in this array. */ protected void updateNewLocals(Object[] newLocals) {} /** * Notifies subclasses that a local variable has been added or remapped. The default * implementation of this method does nothing. * * @param local a local variable identifier, as returned by {@link #newLocal newLocal()}. * @param type the type of the value being stored in the local variable. */ protected void setLocalType(final int local, final Type type) {} private void setFrameLocal(final int local, final Object type) { int l = newLocals.length; if (local >= l) { Object[] a = new Object[Math.max(2 * l, local + 1)]; System.arraycopy(newLocals, 0, a, 0, l); newLocals = a; } newLocals[local] = type; } private int remap(final int var, final Type type) { if (var + type.getSize() <= firstLocal) { return var; } int key = 2 * var + type.getSize() - 1; int size = mapping.length; if (key >= size) { int[] newMapping = new int[Math.max(2 * size, key + 1)]; System.arraycopy(mapping, 0, newMapping, 0, size); mapping = newMapping; } int value = mapping[key]; if (value == 0) { value = newLocalMapping(type); setLocalType(value, type); mapping[key] = value + 1; } else { value--; } if (value != var) { changed = true; } return value; } protected int newLocalMapping(final Type type) { int local = nextLocal; nextLocal += type.getSize(); return local; } }
@Override public void visitFrame( final int type, final int nLocal, final Object[] local, final int nStack, final Object[] stack) { if (type != Opcodes.F_NEW) { // uncompressed frame throw new IllegalStateException( "ClassReader.accept() should be called with EXPAND_FRAMES flag"); } if (!changed) { // optimization for the case where mapping = identity mv.visitFrame(type, nLocal, local, nStack, stack); return; } // creates a copy of newLocals Object[] oldLocals = new Object[newLocals.length]; System.arraycopy(newLocals, 0, oldLocals, 0, oldLocals.length); updateNewLocals(newLocals); // copies types from 'local' to 'newLocals' // 'newLocals' already contains the variables added with 'newLocal' int index = 0; // old local variable index int number = 0; // old local variable number for (; number < nLocal; ++number) { Object t = local[number]; int size = t == Opcodes.LONG || t == Opcodes.DOUBLE ? 2 : 1; if (t != Opcodes.TOP) { Type typ = OBJECT_TYPE; if (t == Opcodes.INTEGER) { typ = Type.INT_TYPE; } else if (t == Opcodes.FLOAT) { typ = Type.FLOAT_TYPE; } else if (t == Opcodes.LONG) { typ = Type.LONG_TYPE; } else if (t == Opcodes.DOUBLE) { typ = Type.DOUBLE_TYPE; } else if (t instanceof String) { typ = Type.getObjectType((String) t); } setFrameLocal(remap(index, typ), t); } index += size; } // removes TOP after long and double types as well as trailing TOPs index = 0; number = 0; for (int i = 0; index < newLocals.length; ++i) { Object t = newLocals[index++]; if (t != null && t != Opcodes.TOP) { newLocals[i] = t; number = i + 1; if (t == Opcodes.LONG || t == Opcodes.DOUBLE) { index += 1; } } else { newLocals[i] = Opcodes.TOP; } } // visits remapped frame mv.visitFrame(type, number, newLocals, nStack, stack); // restores original value of 'newLocals' newLocals = oldLocals; }
void addInternalName(final String name) { addType(Type.getObjectType(name)); }
@SuppressWarnings({"rawtypes", "unchecked"}) @Override public BasicValue naryOperation(AbstractInsnNode insn, List rawValues) throws AnalyzerException { final List<BasicValue> values = (List<BasicValue>) rawValues; boolean isStatic = false; switch (insn.getOpcode()) { case INVOKESTATIC: isStatic = true; case INVOKESPECIAL: case INVOKEVIRTUAL: case INVOKEINTERFACE: final MethodInsnNode method = (MethodInsnNode) insn; String methodOwner = method.owner; // special case: in case that class is extending tuple we need to find the class // that contains the actual implementation to determine the tuple size if (method.name.equals("getField") || method.name.equals("setField")) { try { final String newMethodOwner = (String) findMethodNode(methodOwner, method.name, method.desc)[1]; if (newMethodOwner.startsWith("org/apache/flink/api/java/tuple/Tuple")) { methodOwner = newMethodOwner; } } catch (IllegalStateException e) { // proceed with the known method owner } } // special case: collect method of Collector if (method.name.equals("collect") && methodOwner.equals("org/apache/flink/util/Collector") && isTagged(values.get(0)) && tagged(values.get(0)).isCollector()) { // check for invalid return value if (isTagged(values.get(1)) && tagged(values.get(1)).isNull()) { analyzer.handleNullReturn(); } // valid return value with input dependencies else if (hasImportantDependencies(values.get(1))) { // add a copy and a reference // to capture the current state and future changes in alternative paths analyzer.getCollectorValues().add(tagged(values.get(1))); analyzer.getCollectorValues().add(tagged(values.get(1)).copy()); } // valid return value without input dependencies else { analyzer.getCollectorValues().add(null); } } // special case: iterator method of Iterable else if (method.name.equals("iterator") && methodOwner.equals("java/lang/Iterable") && isTagged(values.get(0)) && tagged(values.get(0)).isInputIterable()) { return new TaggedValue( Type.getObjectType("java/util/Iterator"), (tagged(values.get(0)).isInput1Iterable()) ? Tag.INPUT_1_ITERATOR : Tag.INPUT_2_ITERATOR); } // special case: hasNext method of Iterator else if (method.name.equals("hasNext") && methodOwner.equals("java/util/Iterator") && isTagged(values.get(0)) && tagged(values.get(0)).isInputIterator() && !analyzer.isUdfBinary() && !analyzer.isIteratorTrueAssumptionApplied()) { return new TaggedValue(Type.BOOLEAN_TYPE, Tag.ITERATOR_TRUE_ASSUMPTION); } // special case: next method of Iterator else if (method.name.equals("next") && methodOwner.equals("java/util/Iterator") && isTagged(values.get(0)) && tagged(values.get(0)).isInputIterator()) { // after this call it is not possible to assume "TRUE" of "hasNext()" again analyzer.applyIteratorTrueAssumption(); if (tagged(values.get(0)).isInput1Iterator()) { return analyzer.getInput1AsTaggedValue(); } else { return analyzer.getInput2AsTaggedValue(); } } // if the UDF class contains instance variables that contain input, // we need to analyze also methods without input-dependent arguments // special case: do not follow the getRuntimeContext method of RichFunctions else if (!isStatic && isTagged(values.get(0)) && tagged(values.get(0)).isThis() && hasImportantDependencies(values.get(0)) && !isGetRuntimeContext(method)) { TaggedValue tv = invokeNestedMethod(values, method); if (tv != null) { return tv; } } // the arguments have input dependencies ("THIS" does not count to the arguments) // we can assume that method has at least one argument else if ((!isStatic && isTagged(values.get(0)) && tagged(values.get(0)).isThis() && hasImportantDependencies(values, true)) || (!isStatic && (!isTagged(values.get(0)) || !tagged(values.get(0)).isThis()) && hasImportantDependencies(values, false)) || (isStatic && hasImportantDependencies(values, false))) { // special case: Java unboxing/boxing methods on input Type newType; if (isTagged(values.get(0)) && tagged(values.get(0)).isInput() && (!isStatic && (newType = checkForUnboxing(method.name, methodOwner)) != null || (isStatic && (newType = checkForBoxing(method.name, method.desc, methodOwner)) != null))) { return tagged(values.get(0)).copy(newType); } // special case: setField method of TupleXX else if (method.name.equals("setField") && methodOwner.startsWith("org/apache/flink/api/java/tuple/Tuple") && isTagged(values.get(0))) { final TaggedValue tuple = tagged(values.get(0)); tuple.setTag(Tag.CONTAINER); // check if fieldPos is constant // if not, we can not determine a state for the tuple if (!isTagged(values.get(2)) || !tagged(values.get(2)).isIntConstant()) { tuple.makeRegular(); } else { final int constant = tagged(values.get(2)).getIntConstant(); if (constant < 0 || Integer.valueOf(methodOwner.split("Tuple")[1]) <= constant) { analyzer.handleInvalidTupleAccess(); } // if it is at least tagged, add it anyways if (isTagged(values.get(1))) { tuple.addContainerMapping("f" + constant, tagged(values.get(1)), currentFrame); } // mark the field as it has an undefined state else { tuple.addContainerMapping("f" + constant, null, currentFrame); } } } // special case: getField method of TupleXX else if (method.name.equals("getField") && methodOwner.startsWith("org/apache/flink/api/java/tuple/Tuple")) { final TaggedValue tuple = tagged(values.get(0)); // we can assume that 0 is an input dependent tuple // constant field index if (isTagged(values.get(1)) // constant && tagged(values.get(1)).isIntConstant()) { final int constant = tagged(values.get(1)).getIntConstant(); if (constant < 0 || Integer.valueOf(methodOwner.split("Tuple")[1]) <= constant) { analyzer.handleInvalidTupleAccess(); } if (tuple.containerContains("f" + constant)) { final TaggedValue tupleField = tuple.getContainerMapping().get("f" + constant); if (tupleField != null) { return tupleField; } } } // unknown field index else { // we need to make the tuple regular as we cannot track modifications of fields tuple.makeRegular(); return new TaggedValue(Type.getObjectType("java/lang/Object")); } } // nested method invocation else { TaggedValue tv = invokeNestedMethod(values, method); if (tv != null) { return tv; } } } return super.naryOperation(insn, values); default: // TODO support for INVOKEDYNAMIC instructions return super.naryOperation(insn, values); } }
private String getInternalName(String internalName) { return getDescriptor(Type.getObjectType(internalName)); }