@Test public void testClassAnnotations2() throws Exception { TypeReference typeUnderTest = TypeReference.findOrCreate( ClassLoaderReference.Application, "Lannotations/AnnotatedClass2"); Collection<Annotation> expectedRuntimeInvisibleAnnotations = HashSetFactory.make(); expectedRuntimeInvisibleAnnotations.add( Annotation.make( TypeReference.findOrCreate( ClassLoaderReference.Application, "Lannotations/RuntimeInvisableAnnotation"))); expectedRuntimeInvisibleAnnotations.add( Annotation.make( TypeReference.findOrCreate( ClassLoaderReference.Application, "Lannotations/RuntimeInvisableAnnotation2"))); Collection<Annotation> expectedRuntimeVisibleAnnotations = HashSetFactory.make(); expectedRuntimeVisibleAnnotations.add( Annotation.make( TypeReference.findOrCreate( ClassLoaderReference.Application, "Lannotations/RuntimeVisableAnnotation"))); expectedRuntimeVisibleAnnotations.add( Annotation.make( TypeReference.findOrCreate( ClassLoaderReference.Application, "Lannotations/RuntimeVisableAnnotation2"))); testClassAnnotations( typeUnderTest, expectedRuntimeInvisibleAnnotations, expectedRuntimeVisibleAnnotations); }
@Test public void testClassAnnotations3() throws Exception { TypeReference typeRef = TypeReference.findOrCreate( ClassLoaderReference.Application, "Lannotations/AnnotatedClass3"); IClass klass = cha.lookupClass(typeRef); Assert.assertNotNull(klass); ShrikeClass shrikeClass = (ShrikeClass) klass; Collection<Annotation> classAnnotations = shrikeClass.getAnnotations(true); Assert.assertEquals( "[Annotation type <Application,Lannotations/AnnotationWithParams> {strParam=classStrParam}]", classAnnotations.toString()); MethodReference methodRefUnderTest = MethodReference.findOrCreate(typeRef, Selector.make("foo()V")); IMethod methodUnderTest = cha.resolveMethod(methodRefUnderTest); Assert.assertNotNull(methodRefUnderTest.toString() + " not found", methodUnderTest); Assert.assertTrue(methodUnderTest instanceof ShrikeCTMethod); ShrikeCTMethod shrikeCTMethodUnderTest = (ShrikeCTMethod) methodUnderTest; Collection<Annotation> runtimeInvisibleAnnotations = shrikeCTMethodUnderTest.getAnnotations(true); Assert.assertEquals( "[Annotation type <Application,Lannotations/AnnotationWithParams> {enumParam=EnumElementValue [type=Lannotations/AnnotationEnum;, val=VAL1], strArrParam=ArrayElementValue [vals=[biz, boz]], annotParam=AnnotationElementValue [type=Lannotations/AnnotationWithSingleParam;, elementValues={value=sdfevs}], strParam=sdfsevs, intParam=25, klassParam=Ljava/lang/Integer;}]", runtimeInvisibleAnnotations.toString()); }
@Override public void visitJavaScriptInvoke(JavaScriptInvoke invk) { // check whether this instruction corresponds to a function expression/declaration if (isFunctionConstructorInvoke(invk)) { int defn = invk.getDef(); // the name of the function String fnName = symtab.getStringValue(invk.getUse(1)); IClass fnClass = cha.lookupClass(TypeReference.findOrCreate(JavaScriptTypes.jsLoader, fnName)); if (fnClass == null) { System.err.println( "cannot find " + fnName + " at " + ((AstMethod) method).getSourcePosition()); return; } IMethod fn = fnClass.getMethod(AstMethodReference.fnSelector); FuncVertex callee = factory.makeFuncVertex(fnClass); // look at all uses for (Iterator<SSAInstruction> uses = du.getUses(defn); uses.hasNext(); ) { SSAInstruction use = uses.next(); // check whether this is a local call if (use instanceof JavaScriptInvoke && ((JavaScriptInvoke) use).getFunction() == defn) { JavaScriptInvoke use_invk = (JavaScriptInvoke) use; // yes, so add edges from arguments to parameters... for (int i = 2; i < use_invk.getNumberOfParameters(); ++i) flowgraph.addEdge( factory.makeVarVertex(caller, use_invk.getUse(i)), factory.makeParamVertex(callee, i - 1)); // ...and from return to result flowgraph.addEdge( factory.makeRetVertex(callee), factory.makeVarVertex(caller, use.getDef())); // note: local calls are never qualified, so there is no flow into the receiver vertex } else { // no, it's a more complicated use, so add flows from/to unknown for (int i = 1; i < fn.getNumberOfParameters(); ++i) flowgraph.addEdge(factory.makeUnknownVertex(), factory.makeParamVertex(callee, i)); flowgraph.addEdge(factory.makeRetVertex(callee), factory.makeUnknownVertex()); } } } else { // this is a genuine function call; find out where the function came from SSAInstruction def = du.getDef(invk.getFunction()); // if it's not a local call, add flows from/to unknown if (!(def instanceof JavaScriptInvoke) || !isFunctionConstructorInvoke((JavaScriptInvoke) def)) { for (int i = 1; i < invk.getNumberOfParameters(); ++i) flowgraph.addEdge( factory.makeVarVertex(caller, invk.getUse(i)), factory.makeUnknownVertex()); flowgraph.addEdge( factory.makeUnknownVertex(), factory.makeVarVertex(caller, invk.getDef())); } } }
@Override public IMethod getCalleeTarget(CGNode caller, CallSiteReference site, IClass receiver) { IMethod target = base.getCalleeTarget(caller, site, receiver); if (target != null && target.getReference().equals(loadFileFunRef)) { Set<String> names = new HashSet<String>(); SSAInstruction call = caller.getIR() .getInstructions()[ caller.getIR().getCallInstructionIndices(site).intIterator().next()]; if (call.getNumberOfUses() > 1) { LocalPointerKey fileNameV = new LocalPointerKey(caller, call.getUse(1)); OrdinalSet<InstanceKey> ptrs = builder.getPointerAnalysis().getPointsToSet(fileNameV); for (InstanceKey k : ptrs) { if (k instanceof ConstantKey) { Object v = ((ConstantKey) k).getValue(); if (v instanceof String) { names.add((String) v); } } } if (names.size() == 1) { String str = names.iterator().next(); try { JavaScriptLoader cl = (JavaScriptLoader) builder.getClassHierarchy().getLoader(JavaScriptTypes.jsLoader); URL url = new URL(builder.getBaseURL(), str); if (!loadedFiles.contains(url)) { // try to open the input stream for the URL. if it fails, we'll get an IOException // and fall through to default case InputStream inputStream = url.openConnection().getInputStream(); inputStream.close(); JSCallGraphUtil.loadAdditionalFile(builder.getClassHierarchy(), cl, url); loadedFiles.add(url); IClass script = builder .getClassHierarchy() .lookupClass( TypeReference.findOrCreate(cl.getReference(), "L" + url.getFile())); return script.getMethod(JavaScriptMethods.fnSelector); } } catch (MalformedURLException e1) { // do nothing, fall through and return 'target' } catch (IOException e) { // do nothing, fall through and return 'target' } catch (RuntimeException e) { // do nothing, fall through and return 'target' } } } } return target; }
protected void doNewObject( WalkContext context, CAstNode newNode, int result, Object type, int[] arguments) { assert arguments == null; TypeReference typeRef = TypeReference.findOrCreate(JavaScriptTypes.jsLoader, TypeName.string2TypeName("L" + type)); context .cfg() .addInstruction( insts.NewInstruction( result, NewSiteReference.make(context.cfg().getCurrentInstruction(), typeRef))); }
protected void addFieldToList( List<FieldImpl> L, Atom name, ImmutableByteArray fieldType, int accessFlags, Collection<Annotation> annotations) { TypeName T = null; if (fieldType.get(fieldType.length() - 1) == ';') { T = TypeName.findOrCreate(fieldType, 0, fieldType.length() - 1); } else { T = TypeName.findOrCreate(fieldType); } TypeReference type = TypeReference.findOrCreate(getClassLoader().getReference(), T); FieldReference fr = FieldReference.findOrCreate(getReference(), name, type); FieldImpl f = new FieldImpl(this, fr, accessFlags, annotations); L.add(f); }
private IMethod makeFunctionConstructor( IR callerIR, SSAAbstractInvokeInstruction callStmt, IClass cls, int nargs) { SymbolTable ST = callerIR.getSymbolTable(); if (nargs == 0) { return makeFunctionConstructor(cls, cls); } else if (nargs == 1) { if (ST.isStringConstant(callStmt.getUse(1))) { TypeReference ref = TypeReference.findOrCreate( JavaScriptTypes.jsLoader, TypeName.string2TypeName((String) ST.getStringValue(callStmt.getUse(1)))); if (DEBUG) { System.err.println( ("ctor type name is " + (String) ST.getStringValue(callStmt.getUse(1)))); } IClass cls2 = cha.lookupClass(ref); if (cls2 != null) { return makeFunctionConstructor(cls, cls2); } } return makeFunctionConstructor(cls, cls); } else { assert nargs > 1; JavaScriptLoader cl = (JavaScriptLoader) cha.getLoader(JavaScriptTypes.jsLoader); for (int i = 1; i < callStmt.getNumberOfUses(); i++) if (!ST.isStringConstant(callStmt.getUse(i))) return makeFunctionConstructor(cls, cls); StringBuffer fun = new StringBuffer("function _fromctor ("); for (int j = 1; j < callStmt.getNumberOfUses() - 1; j++) { if (j != 1) fun.append(","); fun.append(ST.getStringValue(callStmt.getUse(j))); } fun.append(") {"); fun.append(ST.getStringValue(callStmt.getUse(callStmt.getNumberOfUses() - 1))); fun.append("}"); try { String fileName = "ctor$" + ++ctorCount; File f = new File(System.getProperty("java.io.tmpdir") + File.separator + fileName); FileWriter FO = new FileWriter(f); FO.write(fun.toString()); FO.close(); Set<String> fnNames = JSCallGraphUtil.loadAdditionalFile(cha, cl, fileName, f.toURI().toURL()); IClass fcls = null; for (String nm : fnNames) { if (nm.endsWith("_fromctor")) { fcls = cl.lookupClass(nm, cha); } } assert fcls != null : "cannot find class for " + fileName + " in " + f; f.delete(); if (DEBUG) System.err.println(("looking for ctor " + ctorCount + " and got " + fcls)); if (fcls != null) return makeFunctionConstructor(cls, fcls); } catch (IOException e) { } return makeFunctionConstructor(cls, cls); } }
/** * Flexible class to create {@link InstanceKey}s depending on various policies ranging from * class-based (i.e. 0-CFA) to allocation-site-based (0-1-CFA variants). */ public class ZeroXInstanceKeys implements InstanceKeyFactory { private static final TypeName JavaLangStringBufferName = TypeName.string2TypeName("Ljava/lang/StringBuffer"); public static final TypeReference JavaLangStringBuffer = TypeReference.findOrCreate(ClassLoaderReference.Primordial, JavaLangStringBufferName); private static final TypeName JavaLangStringBuilderName = TypeName.string2TypeName("Ljava/lang/StringBuilder"); public static final TypeReference JavaLangStringBuilder = TypeReference.findOrCreate(ClassLoaderReference.Primordial, JavaLangStringBuilderName); private static final TypeName JavaLangAbstractStringBuilderName = TypeName.string2TypeName("Ljava/lang/AbstractStringBuilder"); public static final TypeReference JavaLangAbstractStringBuilder = TypeReference.findOrCreate( ClassLoaderReference.Primordial, JavaLangAbstractStringBuilderName); /** The NONE policy is not allocation-site based */ public static final int NONE = 0; /** * An ALLOCATIONS - based policy distinguishes instances by allocation site. Otherwise, the policy * distinguishes instances by type. */ public static final int ALLOCATIONS = 1; /** * A policy variant where String and StringBuffers are NOT disambiguated according to allocation * site. */ public static final int SMUSH_STRINGS = 2; /** * A policy variant where {@link Throwable} instances are NOT disambiguated according to * allocation site. */ public static final int SMUSH_THROWABLES = 4; /** * A policy variant where if a type T has only primitive instance fields, then instances of type T * are NOT disambiguated by allocation site. */ public static final int SMUSH_PRIMITIVE_HOLDERS = 8; /** * This variant counts the N, number of allocation sites of a particular type T in each method. If * N > SMUSH_LIMIT, then these N allocation sites are NOT distinguished ... instead there is a * single abstract allocation site for <N,T> * * <p>Probably the best choice in many cases. */ public static final int SMUSH_MANY = 16; /** Should we use constant-specific keys? */ public static final int CONSTANT_SPECIFIC = 32; /** When using smushing, how many sites in a node will be kept distinct before smushing? */ private final int SMUSH_LIMIT = 25; /** The policy choice for instance disambiguation */ private final int policy; /** A delegate object to create class-based abstract instances */ private final ClassBasedInstanceKeys classBased; /** A delegate object to create allocation site-based abstract instances */ private final AllocationSiteInNodeFactory siteBased; /** A delegate object to create "abstract allocation site" - based abstract instances */ private final SmushedAllocationSiteInstanceKeys smushed; /** The governing class hierarchy */ private final IClassHierarchy cha; /** An object which interprets nodes in context. */ private final RTAContextInterpreter contextInterpreter; /** a Map from CGNode->Set<IClass> that should be smushed. */ protected final Map<CGNode, Set<IClass>> smushMap = HashMapFactory.make(); public ZeroXInstanceKeys( AnalysisOptions options, IClassHierarchy cha, RTAContextInterpreter contextInterpreter, int policy) { if (options == null) { throw new IllegalArgumentException("null options"); } this.policy = policy; if (disambiguateConstants()) { // this is an ugly hack. TODO: clean it all up. options.setUseConstantSpecificKeys(true); } classBased = new ClassBasedInstanceKeys(options, cha); siteBased = new AllocationSiteInNodeFactory(options, cha); smushed = new SmushedAllocationSiteInstanceKeys(options, cha); this.cha = cha; this.contextInterpreter = contextInterpreter; } /** @return true iff the policy smushes some allocation sites */ private boolean smushMany() { return (policy & SMUSH_MANY) > 0; } private boolean allocationPolicy() { return (policy & ALLOCATIONS) > 0; } private boolean smushStrings() { return (policy & SMUSH_STRINGS) > 0; } public boolean smushThrowables() { return (policy & SMUSH_THROWABLES) > 0; } private boolean smushPrimHolders() { return (policy & SMUSH_PRIMITIVE_HOLDERS) > 0; } public boolean disambiguateConstants() { return (policy & CONSTANT_SPECIFIC) > 0; } public InstanceKey getInstanceKeyForAllocation(CGNode node, NewSiteReference allocation) { if (allocation == null) { throw new IllegalArgumentException("allocation is null"); } TypeReference t = allocation.getDeclaredType(); IClass C = cha.lookupClass(t); if (C != null && isInteresting(C)) { if (smushMany()) { if (exceedsSmushLimit(C, node)) { return smushed.getInstanceKeyForAllocation(node, allocation); } else { return siteBased.getInstanceKeyForAllocation(node, allocation); } } else { return siteBased.getInstanceKeyForAllocation(node, allocation); } } else { return classBased.getInstanceKeyForAllocation(node, allocation); } } /** * side effect: populates the smush map. * * @return true iff the node contains too many allocation sites of type c */ private boolean exceedsSmushLimit(IClass c, CGNode node) { Set<IClass> s = smushMap.get(node); if (s == null) { Map<IClass, Integer> count = countAllocsByType(node); HashSet<IClass> smushees = HashSetFactory.make(5); for (Iterator<Map.Entry<IClass, Integer>> it = count.entrySet().iterator(); it.hasNext(); ) { Map.Entry<IClass, Integer> e = it.next(); Integer i = e.getValue(); if (i.intValue() > SMUSH_LIMIT) { smushees.add(e.getKey()); } } s = smushees.isEmpty() ? Collections.<IClass>emptySet() : smushees; smushMap.put(node, s); } return s.contains(c); } /** @return Map: IClass -> Integer, the number of allocation sites for each type. */ private Map<IClass, Integer> countAllocsByType(CGNode node) { Map<IClass, Integer> count = HashMapFactory.make(); for (Iterator it = contextInterpreter.iterateNewSites(node); it.hasNext(); ) { NewSiteReference n = (NewSiteReference) it.next(); IClass alloc = cha.lookupClass(n.getDeclaredType()); if (alloc != null) { Integer old = count.get(alloc); if (old == null) { count.put(alloc, new Integer(1)); } else { count.put(alloc, new Integer(old.intValue() + 1)); } } } return count; } public InstanceKey getInstanceKeyForMultiNewArray( CGNode node, NewSiteReference allocation, int dim) { if (allocationPolicy()) { return siteBased.getInstanceKeyForMultiNewArray(node, allocation, dim); } else { return classBased.getInstanceKeyForMultiNewArray(node, allocation, dim); } } public <T> InstanceKey getInstanceKeyForConstant(TypeReference type, T S) { if (type == null) { throw new IllegalArgumentException("null type"); } if (disambiguateConstants() || isReflectiveType(type)) { return new ConstantKey<T>(S, getClassHierarchy().lookupClass(type)); } else { return classBased.getInstanceKeyForConstant(type, S); } } private boolean isReflectiveType(TypeReference type) { return type.equals(TypeReference.JavaLangReflectConstructor) || type.equals(TypeReference.JavaLangReflectMethod); } /* * @see com.ibm.wala.ipa.callgraph.propagation.InstanceKeyFactory#getInstanceKeyForPEI(com.ibm.wala.ipa.callgraph.CGNode, * com.ibm.wala.classLoader.ProgramCounter, com.ibm.wala.types.TypeReference) */ public InstanceKey getInstanceKeyForPEI(CGNode node, ProgramCounter pei, TypeReference type) { return classBased.getInstanceKeyForPEI(node, pei, type); } public InstanceKey getInstanceKeyForClassObject(TypeReference type) { return classBased.getInstanceKeyForClassObject(type); } /** A class is "interesting" iff we distinguish instances of the class */ public boolean isInteresting(IClass C) { if (!allocationPolicy()) { return false; } else { if (smushStrings() && isStringish(C)) { return false; } else if (smushThrowables() && (isThrowable(C) || isStackTraceElement(C))) { return false; } else if (smushPrimHolders() && allFieldsArePrimitive(C)) { return false; } return true; } } public static boolean isStringish(IClass C) { if (C == null) { throw new IllegalArgumentException("C is null"); } return C.getReference().equals(TypeReference.JavaLangString) || C.getReference().equals(JavaLangStringBuffer) || C.getReference().equals(JavaLangStringBuilder) || C.getReference().equals(JavaLangAbstractStringBuilder); } public static boolean isThrowable(IClass c) { if (c == null) { throw new IllegalArgumentException("null c"); } return c.getClassHierarchy() .isSubclassOf(c, c.getClassHierarchy().lookupClass(TypeReference.JavaLangThrowable)); } public boolean isStackTraceElement(IClass c) { if (c == null) { throw new IllegalArgumentException("C is null"); } return c.getReference().equals(TypeReference.JavaLangStackTraceElement); } private boolean allFieldsArePrimitive(IClass c) { if (c.isArrayClass()) { TypeReference t = c.getReference().getArrayElementType(); return t.isPrimitiveType(); } else { if (c.getReference().equals(TypeReference.JavaLangObject)) { return true; } else { for (Iterator<IField> it = c.getDeclaredInstanceFields().iterator(); it.hasNext(); ) { IField f = it.next(); if (f.getReference().getFieldType().isReferenceType()) { return false; } } return allFieldsArePrimitive(c.getSuperclass()); } } } protected IClassHierarchy getClassHierarchy() { return cha; } public ClassBasedInstanceKeys getClassBasedInstanceKeys() { return classBased; } }