All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.android.tools.lint.client.api.AsmVisitor Maven / Gradle / Ivy

There is a newer version: 25.3.0
Show newest version
/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.tools.lint.client.api;

import com.android.annotations.NonNull;
import com.android.tools.lint.detector.api.ClassContext;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Detector.ClassScanner;
import com.google.common.annotations.Beta;

import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Specialized visitor for running detectors on a class object model.
 * 

* It operates in two phases: *

    *
  1. First, it computes a set of maps where it generates a map from each * significant method name to a list of detectors to consult for that method * name. The set of method names that a detector is interested in is provided * by the detectors themselves. *
  2. Second, it iterates over the DOM a single time. For each method call it finds, * it dispatches to any check that has registered interest in that method name. *
  3. Finally, it runs a full check on those class scanners that do not register * specific method names to be checked. This is intended for those detectors * that do custom work, not related specifically to method calls. *
* It also notifies all the detectors before and after the document is processed * such that they can do pre- and post-processing. *

* NOTE: This is not a public or final API; if you rely on this be prepared * to adjust your code for the next tools release. */ @Beta class AsmVisitor { /** * Number of distinct node types specified in {@link AbstractInsnNode}. Sadly * there isn't a max-constant there, so update this along with ASM library * updates. */ private static final int TYPE_COUNT = AbstractInsnNode.LINE + 1; private final Map> mMethodNameToChecks = new HashMap>(); private final Map> mMethodOwnerToChecks = new HashMap>(); private final List mFullClassChecks = new ArrayList(); private final List mAllDetectors; private List[] mNodeTypeDetectors; // Really want this: // & Detector.ClassScanner> ClassVisitor(T xmlDetectors) { // but it makes client code tricky and ugly. @SuppressWarnings("unchecked") AsmVisitor(@NonNull LintClient client, @NonNull List classDetectors) { mAllDetectors = classDetectors; // TODO: Check appliesTo() for files, and find a quick way to enable/disable // rules when running through a full project! for (Detector detector : classDetectors) { Detector.ClassScanner scanner = (Detector.ClassScanner) detector; boolean checkFullClass = true; Collection names = scanner.getApplicableCallNames(); if (names != null) { checkFullClass = false; for (String element : names) { List list = mMethodNameToChecks.get(element); if (list == null) { list = new ArrayList(); mMethodNameToChecks.put(element, list); } list.add(scanner); } } Collection owners = scanner.getApplicableCallOwners(); if (owners != null) { checkFullClass = false; for (String element : owners) { List list = mMethodOwnerToChecks.get(element); if (list == null) { list = new ArrayList(); mMethodOwnerToChecks.put(element, list); } list.add(scanner); } } int[] types = scanner.getApplicableAsmNodeTypes(); if (types != null) { checkFullClass = false; for (int type : types) { if (type < 0 || type >= TYPE_COUNT) { // Can't support this node type: looks like ASM wasn't updated correctly. client.log(null, "Out of range node type %1$d from detector %2$s", type, scanner); continue; } if (mNodeTypeDetectors == null) { mNodeTypeDetectors = new List[TYPE_COUNT]; } List checks = mNodeTypeDetectors[type]; if (checks == null) { checks = new ArrayList(); mNodeTypeDetectors[type] = checks; } checks.add(scanner); } } if (checkFullClass) { mFullClassChecks.add(detector); } } } @SuppressWarnings("rawtypes") // ASM API uses raw types void runClassDetectors(ClassContext context) { ClassNode classNode = context.getClassNode(); for (Detector detector : mAllDetectors) { detector.beforeCheckFile(context); } for (Detector detector : mFullClassChecks) { Detector.ClassScanner scanner = (Detector.ClassScanner) detector; scanner.checkClass(context, classNode); detector.afterCheckFile(context); } if (!mMethodNameToChecks.isEmpty() || !mMethodOwnerToChecks.isEmpty() || mNodeTypeDetectors != null && mNodeTypeDetectors.length > 0) { List methodList = classNode.methods; for (Object m : methodList) { MethodNode method = (MethodNode) m; InsnList nodes = method.instructions; for (int i = 0, n = nodes.size(); i < n; i++) { AbstractInsnNode instruction = nodes.get(i); int type = instruction.getType(); if (type == AbstractInsnNode.METHOD_INSN) { MethodInsnNode call = (MethodInsnNode) instruction; String owner = call.owner; List scanners = mMethodOwnerToChecks.get(owner); if (scanners != null) { for (ClassScanner scanner : scanners) { scanner.checkCall(context, classNode, method, call); } } String name = call.name; scanners = mMethodNameToChecks.get(name); if (scanners != null) { for (ClassScanner scanner : scanners) { scanner.checkCall(context, classNode, method, call); } } } if (mNodeTypeDetectors != null && type < mNodeTypeDetectors.length) { List scanners = mNodeTypeDetectors[type]; if (scanners != null) { for (ClassScanner scanner : scanners) { scanner.checkInstruction(context, classNode, method, instruction); } } } } } } for (Detector detector : mAllDetectors) { detector.afterCheckFile(context); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy