private void addDependencies(DirectClassFile classFile) { for (Constant constant : classFile.getConstantPool().getEntries()) { if (constant instanceof CstType) { checkDescriptor(((CstType) constant).getClassType().getDescriptor()); } else if (constant instanceof CstFieldRef) { checkDescriptor(((CstFieldRef) constant).getType().getDescriptor()); } else if (constant instanceof CstBaseMethodRef) { checkPrototype(((CstBaseMethodRef) constant).getPrototype()); } } FieldList fields = classFile.getFields(); int nbField = fields.size(); for (int i = 0; i < nbField; i++) { checkDescriptor(fields.get(i).getDescriptor().getString()); } MethodList methods = classFile.getMethods(); int nbMethods = methods.size(); for (int i = 0; i < nbMethods; i++) { checkPrototype(Prototype.intern(methods.get(i).getDescriptor().getString())); } } private void checkPrototype(Prototype proto) {
/** * Get whether this is a reference to a signature polymorphic * method. This means it is defined in {@code java.lang.invoke.MethodHandle} and * is either the {@code invoke} or the {@code invokeExact} method. * * @return {@code true} iff this is a reference to a * signature polymorphic method. */ public final boolean isSignaturePolymorphic() { return (getDefiningClass().equals(CstType.METHOD_HANDLE) && getNat().isSignaturePolymorphic()); } }
Prototype meth = cstMeth.getPrototype(); CstType definer = cstMeth.getDefiningClass(); meth = meth.withFirstParameter(definer.getClassType()); return opInvokeVirtual(meth); Prototype meth = cstMeth.getPrototype(); CstType definer = cstMeth.getDefiningClass(); meth = meth.withFirstParameter(definer.getClassType()); return opInvokeSuper(meth); Prototype meth = cstMeth.getPrototype(); CstType definer = cstMeth.getDefiningClass(); meth = meth.withFirstParameter(definer.getClassType()); return opInvokeDirect(meth); Prototype meth = cstMeth.getPrototype(); CstType definer = cstMeth.getDefiningClass(); meth = meth.withFirstParameter(definer.getClassType()); return opInvokeInterface(meth); Prototype proto = cstMeth.getPrototype(); CstType definer = cstMeth.getDefiningClass(); Prototype meth = proto.withFirstParameter(definer.getClassType()); return opInvokePolymorphic(meth);
/** * Gets whether this is a reference to a class initialization * method. This is just a convenient shorthand for * {@code getNat().isClassInit()}. * * @return {@code true} iff this is a reference to an * instance initialization method */ public final boolean isClassInit() { return getNat().isClassInit(); } }
methodDeps.addDependency(methodRef.getDefiningClass().toHuman(), methodRef.getNat().getName().toHuman() + ":" + methodRef.getPrototype().toString());
/** * Gets the prototype of this method as either a * {@code static} or instance method. In the case of a * {@code static} method, this is the same as the raw * prototype. In the case of an instance method, this has an * appropriately-typed {@code this} argument as the first * one. * * @param isStatic whether the method should be considered static * @return {@code non-null;} the method prototype */ public final Prototype getPrototype(boolean isStatic) { if (isStatic) { return prototype; } else { if (instancePrototype == null) { Type thisType = getDefiningClass().getClassType(); instancePrototype = prototype.withFirstParameter(thisType); } return instancePrototype; } }
/** * Gets the size of the outgoing arguments area required by this * method. This is equal to the largest argument word count of any * method referred to by this instance. * * @return {@code >= 0;} the required outgoing arguments size */ public int getOutsSize() { int sz = size(); int result = 0; for (int i = 0; i < sz; i++) { DalvInsn insn = (DalvInsn) get0(i); if (!(insn instanceof CstInsn)) { continue; } Constant cst = ((CstInsn) insn).getConstant(); if (!(cst instanceof CstBaseMethodRef)) { continue; } boolean isStatic = (insn.getOpcode().getFamily() == Opcodes.INVOKE_STATIC); int count = ((CstBaseMethodRef) cst).getParameterWordCount(isStatic); if (count > result) { result = count; } } return result; }
/** * Constructs an instance. * * @param definingClass {@code non-null;} the type of the defining class * @param nat {@code non-null;} the name-and-type */ /*package*/ CstBaseMethodRef(CstType definingClass, CstNat nat) { super(definingClass, nat); String descriptor = getNat().getDescriptor().getString(); if (isSignaturePolymorphic()) { // The prototype for signature polymorphic methods is used to // construct call-site information and select the true invocation // target (invoke() or invokeExact(). The prototype is created // without being interned to avoid polluting the DEX file with // unused data. this.prototype = Prototype.fromDescriptor(descriptor); } else { this.prototype = Prototype.intern(descriptor); } this.instancePrototype = null; }
if (ref.isInstanceInit() || (ref.getDefiningClass() == method.getDefiningClass()) || !method.getAccSuper()) { return RegOps.INVOKE_DIRECT;
case RegOps.PUT_STATIC: return opPutStatic(sources.getType(0)); case RegOps.INVOKE_STATIC: { return opInvokeStatic(((CstBaseMethodRef) cst).getPrototype()); Prototype meth = cstMeth.getPrototype(); CstType definer = cstMeth.getDefiningClass(); meth = meth.withFirstParameter(definer.getClassType()); return opInvokeVirtual(meth); Prototype meth = cstMeth.getPrototype(); CstType definer = cstMeth.getDefiningClass(); meth = meth.withFirstParameter(definer.getClassType()); return opInvokeSuper(meth); Prototype meth = cstMeth.getPrototype(); CstType definer = cstMeth.getDefiningClass(); meth = meth.withFirstParameter(definer.getClassType()); return opInvokeDirect(meth); Prototype meth = cstMeth.getPrototype(); CstType definer = cstMeth.getDefiningClass(); meth = meth.withFirstParameter(definer.getClassType()); return opInvokeInterface(meth);
/** * Gets whether this is a reference to a class initialization * method. This is just a convenient shorthand for * {@code getNat().isClassInit()}. * * @return {@code true} iff this is a reference to an * instance initialization method */ public final boolean isClassInit() { return getNat().isClassInit(); }
/** * Gets the prototype of this method as either a * {@code static} or instance method. In the case of a * {@code static} method, this is the same as the raw * prototype. In the case of an instance method, this has an * appropriately-typed {@code this} argument as the first * one. * * @param isStatic whether the method should be considered static * @return {@code non-null;} the method prototype */ public final Prototype getPrototype(boolean isStatic) { if (isStatic) { return prototype; } else { if (instancePrototype == null) { Type thisType = getDefiningClass().getClassType(); instancePrototype = prototype.withFirstParameter(thisType); } return instancePrototype; } }
/** * Gets the size of the outgoing arguments area required by this * method. This is equal to the largest argument word count of any * method referred to by this instance. * * @return {@code >= 0;} the required outgoing arguments size */ public int getOutsSize() { int sz = size(); int result = 0; for (int i = 0; i < sz; i++) { DalvInsn insn = (DalvInsn) get0(i); if (!(insn instanceof CstInsn)) { continue; } Constant cst = ((CstInsn) insn).getConstant(); if (!(cst instanceof CstBaseMethodRef)) { continue; } boolean isStatic = (insn.getOpcode().getFamily() == Opcodes.INVOKE_STATIC); int count = ((CstBaseMethodRef) cst).getParameterWordCount(isStatic); if (count > result) { result = count; } } return result; }
/** * Constructs an instance. * * @param definingClass {@code non-null;} the type of the defining class * @param nat {@code non-null;} the name-and-type */ /*package*/ CstBaseMethodRef(CstType definingClass, CstNat nat) { super(definingClass, nat); String descriptor = getNat().getDescriptor().getString(); if (isSignaturePolymorphic()) { // The prototype for signature polymorphic methods is used to // construct call-site information and select the true invocation // target (invoke() or invokeExact(). The prototype is created // without being interned to avoid polluting the DEX file with // unused data. this.prototype = Prototype.fromDescriptor(descriptor); } else { this.prototype = Prototype.intern(descriptor); } this.instancePrototype = null; }
/** * Gets the number of words of parameters required by this * method's descriptor. Since instances of this class have no way * to know if they will be used in a {@code static} or * instance context, one has to indicate this explicitly as an * argument. This method is just a convenient shorthand for * {@code getPrototype().getParameterTypes().getWordCount()}, * plus {@code 1} if the method is to be treated as an * instance method. * * @param isStatic whether the method should be considered static * @return {@code >= 0;} the argument word count */ public final int getParameterWordCount(boolean isStatic) { return getPrototype(isStatic).getParameterTypes().getWordCount(); }
Prototype meth = cstMeth.getPrototype(); CstType definer = cstMeth.getDefiningClass(); meth = meth.withFirstParameter(definer.getClassType()); return opInvokeVirtual(meth); Prototype meth = cstMeth.getPrototype(); CstType definer = cstMeth.getDefiningClass(); meth = meth.withFirstParameter(definer.getClassType()); return opInvokeSuper(meth); Prototype meth = cstMeth.getPrototype(); CstType definer = cstMeth.getDefiningClass(); meth = meth.withFirstParameter(definer.getClassType()); return opInvokeDirect(meth); Prototype meth = cstMeth.getPrototype(); CstType definer = cstMeth.getDefiningClass(); meth = meth.withFirstParameter(definer.getClassType()); return opInvokeInterface(meth); Prototype proto = cstMeth.getPrototype(); CstType definer = cstMeth.getDefiningClass(); Prototype meth = proto.withFirstParameter(definer.getClassType()); return opInvokePolymorphic(meth);
/** * Gets whether this is a reference to an instance initialization * method. This is just a convenient shorthand for * {@code getNat().isInstanceInit()}. * * @return {@code true} iff this is a reference to an * instance initialization method */ public final boolean isInstanceInit() { return getNat().isInstanceInit(); }
CstType definingClass = getDefiningClass(); if (definingClass.equals(CstType.METHOD_HANDLE)) { switch (getNat().getName().getString()) { case "invoke": case "invokeExact": switch (getNat().getName().getString()) { case "compareAndExchange": case "compareAndExchangeAcquire":
/** * Gets the prototype of this method as either a * {@code static} or instance method. In the case of a * {@code static} method, this is the same as the raw * prototype. In the case of an instance method, this has an * appropriately-typed {@code this} argument as the first * one. * * @param isStatic whether the method should be considered static * @return {@code non-null;} the method prototype */ public final Prototype getPrototype(boolean isStatic) { if (isStatic) { return prototype; } else { if (instancePrototype == null) { Type thisType = getDefiningClass().getClassType(); instancePrototype = prototype.withFirstParameter(thisType); } return instancePrototype; } }
/** * Gets the size of the outgoing arguments area required by this * method. This is equal to the largest argument word count of any * method referred to by this instance. * * @return {@code >= 0;} the required outgoing arguments size */ public int getOutsSize() { int sz = size(); int result = 0; for (int i = 0; i < sz; i++) { DalvInsn insn = (DalvInsn) get0(i); if (!(insn instanceof CstInsn)) { continue; } Constant cst = ((CstInsn) insn).getConstant(); if (!(cst instanceof CstBaseMethodRef)) { continue; } boolean isStatic = (insn.getOpcode().getFamily() == Opcodes.INVOKE_STATIC); int count = ((CstBaseMethodRef) cst).getParameterWordCount(isStatic); if (count > result) { result = count; } } return result; }