Remove class2greylist from frameworks/base.
It's moving to a new home under art/tools, since it is needed by branches which don't include frameworks/base. Bug: 110868826 Test: Build master-art branch Change-Id: Ice8324db52ae1e7e2ac6734e45874a66de935c9a
This commit is contained in:
@@ -1,33 +0,0 @@
|
||||
//
|
||||
// Copyright (C) 2018 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.
|
||||
//
|
||||
|
||||
java_library_host {
|
||||
name: "class2greylistlib",
|
||||
srcs: ["src/**/*.java"],
|
||||
static_libs: [
|
||||
"commons-cli-1.2",
|
||||
"apache-bcel",
|
||||
],
|
||||
}
|
||||
|
||||
java_binary_host {
|
||||
name: "class2greylist",
|
||||
manifest: "src/class2greylist.mf",
|
||||
static_libs: [
|
||||
"class2greylistlib",
|
||||
],
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
Main-Class: com.android.class2greylist.Class2Greylist
|
||||
@@ -1,118 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.class2greylist;
|
||||
|
||||
import org.apache.bcel.Const;
|
||||
import org.apache.bcel.classfile.AnnotationEntry;
|
||||
import org.apache.bcel.classfile.DescendingVisitor;
|
||||
import org.apache.bcel.classfile.ElementValuePair;
|
||||
import org.apache.bcel.classfile.EmptyVisitor;
|
||||
import org.apache.bcel.classfile.Field;
|
||||
import org.apache.bcel.classfile.FieldOrMethod;
|
||||
import org.apache.bcel.classfile.JavaClass;
|
||||
import org.apache.bcel.classfile.Method;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Visits a JavaClass instance and pulls out all members annotated with a
|
||||
* specific annotation. The signatures of such members are passed to {@link
|
||||
* Status#greylistEntry(String)}. Any errors result in a call to {@link
|
||||
* Status#error(String)}.
|
||||
*
|
||||
* If the annotation has a property "expectedSignature" the generated signature
|
||||
* will be verified against the one specified there. If it differs, an error
|
||||
* will be generated.
|
||||
*/
|
||||
public class AnnotationVisitor extends EmptyVisitor {
|
||||
|
||||
private static final String EXPECTED_SIGNATURE = "expectedSignature";
|
||||
|
||||
private final JavaClass mClass;
|
||||
private final String mAnnotationType;
|
||||
private final Status mStatus;
|
||||
private final DescendingVisitor mDescendingVisitor;
|
||||
|
||||
public AnnotationVisitor(JavaClass clazz, String annotation, Status d) {
|
||||
mClass = clazz;
|
||||
mAnnotationType = annotation;
|
||||
mStatus = d;
|
||||
mDescendingVisitor = new DescendingVisitor(clazz, this);
|
||||
}
|
||||
|
||||
public void visit() {
|
||||
mStatus.debug("Visit class %s", mClass.getClassName());
|
||||
mDescendingVisitor.visit();
|
||||
}
|
||||
|
||||
private static String getClassDescriptor(JavaClass clazz) {
|
||||
// JavaClass.getName() returns the Java-style name (with . not /), so we must fetch
|
||||
// the original class name from the constant pool.
|
||||
return clazz.getConstantPool().getConstantString(
|
||||
clazz.getClassNameIndex(), Const.CONSTANT_Class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethod(Method method) {
|
||||
visitMember(method, "L%s;->%s%s");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitField(Field field) {
|
||||
visitMember(field, "L%s;->%s:%s");
|
||||
}
|
||||
|
||||
private void visitMember(FieldOrMethod member, String signatureFormatString) {
|
||||
JavaClass definingClass = (JavaClass) mDescendingVisitor.predecessor();
|
||||
mStatus.debug("Visit member %s : %s", member.getName(), member.getSignature());
|
||||
for (AnnotationEntry a : member.getAnnotationEntries()) {
|
||||
if (mAnnotationType.equals(a.getAnnotationType())) {
|
||||
mStatus.debug("Method has annotation %s", mAnnotationType);
|
||||
String signature = String.format(Locale.US, signatureFormatString,
|
||||
getClassDescriptor(definingClass), member.getName(), member.getSignature());
|
||||
for (ElementValuePair property : a.getElementValuePairs()) {
|
||||
switch (property.getNameString()) {
|
||||
case EXPECTED_SIGNATURE:
|
||||
String expected = property.getValue().stringifyValue();
|
||||
if (!signature.equals(expected)) {
|
||||
error(definingClass, member,
|
||||
"Expected signature does not match generated:\n"
|
||||
+ "Expected: %s\n"
|
||||
+ "Generated: %s", expected, signature);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
mStatus.greylistEntry(signature);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void error(JavaClass clazz, FieldOrMethod member, String message, Object... args) {
|
||||
StringBuilder error = new StringBuilder();
|
||||
error.append(clazz.getSourceFileName())
|
||||
.append(": ")
|
||||
.append(clazz.getClassName())
|
||||
.append(".")
|
||||
.append(member.getName())
|
||||
.append(": ")
|
||||
.append(String.format(Locale.US, message, args));
|
||||
|
||||
mStatus.error(error.toString());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.class2greylist;
|
||||
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.apache.commons.cli.CommandLineParser;
|
||||
import org.apache.commons.cli.GnuParser;
|
||||
import org.apache.commons.cli.HelpFormatter;
|
||||
import org.apache.commons.cli.OptionBuilder;
|
||||
import org.apache.commons.cli.Options;
|
||||
import org.apache.commons.cli.ParseException;
|
||||
import org.apache.commons.cli.PatternOptionBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Build time tool for extracting a list of members from jar files that have the @UsedByApps
|
||||
* annotation, for building the greylist.
|
||||
*/
|
||||
public class Class2Greylist {
|
||||
|
||||
private static final String ANNOTATION_TYPE = "Landroid/annotation/UnsupportedAppUsage ;";
|
||||
|
||||
public static void main(String[] args) {
|
||||
Options options = new Options();
|
||||
options.addOption(OptionBuilder
|
||||
.withLongOpt("debug")
|
||||
.hasArgs(0)
|
||||
.withDescription("Enable debug")
|
||||
.create("d"));
|
||||
options.addOption(OptionBuilder
|
||||
.withLongOpt("help")
|
||||
.hasArgs(0)
|
||||
.withDescription("Show this help")
|
||||
.create("h"));
|
||||
|
||||
CommandLineParser parser = new GnuParser();
|
||||
CommandLine cmd;
|
||||
|
||||
try {
|
||||
cmd = parser.parse(options, args);
|
||||
} catch (ParseException e) {
|
||||
System.err.println(e.getMessage());
|
||||
help(options);
|
||||
return;
|
||||
}
|
||||
if (cmd.hasOption('h')) {
|
||||
help(options);
|
||||
}
|
||||
|
||||
String[] jarFiles = cmd.getArgs();
|
||||
if (jarFiles.length == 0) {
|
||||
System.err.println("Error: no jar files specified.");
|
||||
help(options);
|
||||
}
|
||||
|
||||
Status status = new Status(cmd.hasOption('d'));
|
||||
|
||||
for (String jarFile : jarFiles) {
|
||||
status.debug("Processing jar file %s", jarFile);
|
||||
try {
|
||||
JarReader reader = new JarReader(status, jarFile);
|
||||
reader.stream().forEach(clazz -> new AnnotationVisitor(
|
||||
clazz, ANNOTATION_TYPE, status).visit());
|
||||
reader.close();
|
||||
} catch (IOException e) {
|
||||
status.error(e);
|
||||
}
|
||||
}
|
||||
if (status.ok()) {
|
||||
System.exit(0);
|
||||
} else {
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void help(Options options) {
|
||||
new HelpFormatter().printHelp(
|
||||
"class2greylist path/to/classes.jar [classes2.jar ...]",
|
||||
"Extracts greylist entries from classes jar files given",
|
||||
options, null, true);
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.class2greylist;
|
||||
|
||||
import org.apache.bcel.classfile.ClassParser;
|
||||
import org.apache.bcel.classfile.JavaClass;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
/**
|
||||
* Reads {@link JavaClass} members from a zip/jar file, providing a stream of them for processing.
|
||||
* Any errors are reported via {@link Status#error(Throwable)}.
|
||||
*/
|
||||
public class JarReader {
|
||||
|
||||
private final Status mStatus;
|
||||
private final String mFileName;
|
||||
private final ZipFile mZipFile;
|
||||
|
||||
public JarReader(Status s, String filename) throws IOException {
|
||||
mStatus = s;
|
||||
mFileName = filename;
|
||||
mZipFile = new ZipFile(mFileName);
|
||||
}
|
||||
|
||||
private JavaClass openZipEntry(ZipEntry e) {
|
||||
try {
|
||||
mStatus.debug("Reading %s from %s", e.getName(), mFileName);
|
||||
return new ClassParser(mZipFile.getInputStream(e), e.getName()).parse();
|
||||
} catch (IOException ioe) {
|
||||
mStatus.error(ioe);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Stream<JavaClass> stream() {
|
||||
return mZipFile.stream()
|
||||
.filter(zipEntry -> zipEntry.getName().endsWith(".class"))
|
||||
.map(zipEntry -> openZipEntry(zipEntry))
|
||||
.filter(Objects::nonNull);
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
mZipFile.close();
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.class2greylist;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public class Status {
|
||||
|
||||
// Highlight "Error:" in red.
|
||||
private static final String ERROR = "\u001B[31mError: \u001B[0m";
|
||||
|
||||
private final boolean mDebug;
|
||||
private boolean mHasErrors;
|
||||
|
||||
public Status(boolean debug) {
|
||||
mDebug = debug;
|
||||
}
|
||||
|
||||
public void debug(String msg, Object... args) {
|
||||
if (mDebug) {
|
||||
System.err.println(String.format(Locale.US, msg, args));
|
||||
}
|
||||
}
|
||||
|
||||
public void error(Throwable t) {
|
||||
System.err.print(ERROR);
|
||||
t.printStackTrace(System.err);
|
||||
mHasErrors = true;
|
||||
}
|
||||
|
||||
public void error(String message) {
|
||||
System.err.print(ERROR);
|
||||
System.err.println(message);
|
||||
mHasErrors = true;
|
||||
}
|
||||
|
||||
public void greylistEntry(String signature) {
|
||||
System.out.println(signature);
|
||||
}
|
||||
|
||||
public boolean ok() {
|
||||
return !mHasErrors;
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
# Copyright (C) 2018 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.
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
# Only compile source java files in this apk.
|
||||
LOCAL_SRC_FILES := $(call all-java-files-under, src)
|
||||
|
||||
LOCAL_MODULE := class2greylisttest
|
||||
|
||||
LOCAL_STATIC_JAVA_LIBRARIES := class2greylistlib truth-host-prebuilt mockito-host junit-host
|
||||
|
||||
# tag this module as a cts test artifact
|
||||
LOCAL_COMPATIBILITY_SUITE := general-tests
|
||||
|
||||
include $(BUILD_HOST_JAVA_LIBRARY)
|
||||
|
||||
# Build the test APKs using their own makefiles
|
||||
include $(call all-makefiles-under,$(LOCAL_PATH))
|
||||
@@ -1,21 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2018 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.
|
||||
-->
|
||||
<configuration description="class2greylist tests">
|
||||
<test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
|
||||
<option name="jar" value="class2greylisttest.jar" />
|
||||
<option name="runtime-hint" value="1m" />
|
||||
</test>
|
||||
</configuration>
|
||||
@@ -1,202 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.javac;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.MockitoAnnotations.initMocks;
|
||||
|
||||
import com.android.class2greylist.Status;
|
||||
import com.android.class2greylist.AnnotationVisitor;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class AnnotationVisitorTest {
|
||||
|
||||
private static final String ANNOTATION = "Lannotation/Anno;";
|
||||
|
||||
private Javac mJavac;
|
||||
@Mock
|
||||
private Status mStatus;
|
||||
|
||||
@Before
|
||||
public void setup() throws IOException {
|
||||
initMocks(this);
|
||||
mJavac = new Javac();
|
||||
mJavac.addSource("annotation.Anno", Joiner.on('\n').join(
|
||||
"package annotation;",
|
||||
"import static java.lang.annotation.RetentionPolicy.CLASS;",
|
||||
"import java.lang.annotation.Retention;",
|
||||
"import java.lang.annotation.Target;",
|
||||
"@Retention(CLASS)",
|
||||
"public @interface Anno {",
|
||||
" String expectedSignature() default \"\";",
|
||||
"}"));
|
||||
}
|
||||
|
||||
private void assertNoErrors() {
|
||||
verify(mStatus, never()).error(any(Throwable.class));
|
||||
verify(mStatus, never()).error(any(String.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGreylistMethod() throws IOException {
|
||||
mJavac.addSource("a.b.Class", Joiner.on('\n').join(
|
||||
"package a.b;",
|
||||
"import annotation.Anno;",
|
||||
"public class Class {",
|
||||
" @Anno",
|
||||
" public void method() {}",
|
||||
"}"));
|
||||
assertThat(mJavac.compile()).isTrue();
|
||||
|
||||
new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), ANNOTATION, mStatus)
|
||||
.visit();
|
||||
|
||||
assertNoErrors();
|
||||
ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
|
||||
verify(mStatus, times(1)).greylistEntry(greylist.capture());
|
||||
assertThat(greylist.getValue()).isEqualTo("La/b/Class;->method()V");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGreylistConstructor() throws IOException {
|
||||
mJavac.addSource("a.b.Class", Joiner.on('\n').join(
|
||||
"package a.b;",
|
||||
"import annotation.Anno;",
|
||||
"public class Class {",
|
||||
" @Anno",
|
||||
" public Class() {}",
|
||||
"}"));
|
||||
assertThat(mJavac.compile()).isTrue();
|
||||
|
||||
new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), ANNOTATION, mStatus)
|
||||
.visit();
|
||||
|
||||
assertNoErrors();
|
||||
ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
|
||||
verify(mStatus, times(1)).greylistEntry(greylist.capture());
|
||||
assertThat(greylist.getValue()).isEqualTo("La/b/Class;-><init>()V");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGreylistField() throws IOException {
|
||||
mJavac.addSource("a.b.Class", Joiner.on('\n').join(
|
||||
"package a.b;",
|
||||
"import annotation.Anno;",
|
||||
"public class Class {",
|
||||
" @Anno",
|
||||
" public int i;",
|
||||
"}"));
|
||||
assertThat(mJavac.compile()).isTrue();
|
||||
|
||||
new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), ANNOTATION, mStatus)
|
||||
.visit();
|
||||
|
||||
assertNoErrors();
|
||||
ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
|
||||
verify(mStatus, times(1)).greylistEntry(greylist.capture());
|
||||
assertThat(greylist.getValue()).isEqualTo("La/b/Class;->i:I");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGreylistMethodExpectedSignature() throws IOException {
|
||||
mJavac.addSource("a.b.Class", Joiner.on('\n').join(
|
||||
"package a.b;",
|
||||
"import annotation.Anno;",
|
||||
"public class Class {",
|
||||
" @Anno(expectedSignature=\"La/b/Class;->method()V\")",
|
||||
" public void method() {}",
|
||||
"}"));
|
||||
assertThat(mJavac.compile()).isTrue();
|
||||
|
||||
new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), ANNOTATION, mStatus)
|
||||
.visit();
|
||||
|
||||
assertNoErrors();
|
||||
ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
|
||||
verify(mStatus, times(1)).greylistEntry(greylist.capture());
|
||||
assertThat(greylist.getValue()).isEqualTo("La/b/Class;->method()V");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGreylistMethodExpectedSignatureWrong() throws IOException {
|
||||
mJavac.addSource("a.b.Class", Joiner.on('\n').join(
|
||||
"package a.b;",
|
||||
"import annotation.Anno;",
|
||||
"public class Class {",
|
||||
" @Anno(expectedSignature=\"La/b/Class;->nomethod()V\")",
|
||||
" public void method() {}",
|
||||
"}"));
|
||||
assertThat(mJavac.compile()).isTrue();
|
||||
|
||||
new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), ANNOTATION, mStatus)
|
||||
.visit();
|
||||
|
||||
verify(mStatus, times(1)).error(any(String.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGreylistInnerClassMethod() throws IOException {
|
||||
mJavac.addSource("a.b.Class", Joiner.on('\n').join(
|
||||
"package a.b;",
|
||||
"import annotation.Anno;",
|
||||
"public class Class {",
|
||||
" public class Inner {",
|
||||
" @Anno",
|
||||
" public void method() {}",
|
||||
" }",
|
||||
"}"));
|
||||
assertThat(mJavac.compile()).isTrue();
|
||||
|
||||
new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class$Inner"), ANNOTATION,
|
||||
mStatus).visit();
|
||||
|
||||
assertNoErrors();
|
||||
ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
|
||||
verify(mStatus, times(1)).greylistEntry(greylist.capture());
|
||||
assertThat(greylist.getValue()).isEqualTo("La/b/Class$Inner;->method()V");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMethodNotGreylisted() throws IOException {
|
||||
mJavac.addSource("a.b.Class", Joiner.on('\n').join(
|
||||
"package a.b;",
|
||||
"public class Class {",
|
||||
" public void method() {}",
|
||||
"}"));
|
||||
assertThat(mJavac.compile()).isTrue();
|
||||
|
||||
new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), ANNOTATION, mStatus)
|
||||
.visit();
|
||||
|
||||
assertNoErrors();
|
||||
verify(mStatus, never()).greylistEntry(any(String.class));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.javac;
|
||||
|
||||
import com.google.common.io.Files;
|
||||
|
||||
import org.apache.bcel.classfile.ClassParser;
|
||||
import org.apache.bcel.classfile.JavaClass;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.tools.DiagnosticCollector;
|
||||
import javax.tools.JavaCompiler;
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.SimpleJavaFileObject;
|
||||
import javax.tools.StandardJavaFileManager;
|
||||
import javax.tools.StandardLocation;
|
||||
import javax.tools.ToolProvider;
|
||||
|
||||
/**
|
||||
* Helper class for compiling snippets of Java source and providing access to the resulting class
|
||||
* files.
|
||||
*/
|
||||
public class Javac {
|
||||
|
||||
private final JavaCompiler mJavac;
|
||||
private final StandardJavaFileManager mFileMan;
|
||||
private final List<JavaFileObject> mCompilationUnits;
|
||||
private final File mClassOutDir;
|
||||
|
||||
public Javac() throws IOException {
|
||||
mJavac = ToolProvider.getSystemJavaCompiler();
|
||||
mFileMan = mJavac.getStandardFileManager(null, Locale.US, null);
|
||||
mClassOutDir = Files.createTempDir();
|
||||
mFileMan.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(mClassOutDir));
|
||||
mFileMan.setLocation(StandardLocation.CLASS_PATH, Arrays.asList(mClassOutDir));
|
||||
mCompilationUnits = new ArrayList<>();
|
||||
}
|
||||
|
||||
private String classToFileName(String classname) {
|
||||
return classname.replace('.', '/');
|
||||
}
|
||||
|
||||
public Javac addSource(String classname, String contents) {
|
||||
JavaFileObject java = new SimpleJavaFileObject(URI.create(
|
||||
String.format("string:///%s.java", classToFileName(classname))),
|
||||
JavaFileObject.Kind.SOURCE
|
||||
){
|
||||
@Override
|
||||
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
|
||||
return contents;
|
||||
}
|
||||
};
|
||||
mCompilationUnits.add(java);
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean compile() {
|
||||
JavaCompiler.CompilationTask task = mJavac.getTask(
|
||||
null,
|
||||
mFileMan,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
mCompilationUnits);
|
||||
return task.call();
|
||||
}
|
||||
|
||||
public InputStream getClassFile(String classname) throws IOException {
|
||||
Iterable<? extends JavaFileObject> objs = mFileMan.getJavaFileObjects(
|
||||
new File(mClassOutDir, String.format("%s.class", classToFileName(classname))));
|
||||
if (!objs.iterator().hasNext()) {
|
||||
return null;
|
||||
}
|
||||
return objs.iterator().next().openInputStream();
|
||||
}
|
||||
|
||||
public JavaClass getCompiledClass(String classname) throws IOException {
|
||||
return new ClassParser(getClassFile(classname),
|
||||
String.format("%s.class", classToFileName(classname))).parse();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user