GP-71: Prepping for source release.

This commit is contained in:
Dan 2020-12-10 09:39:41 -05:00
parent ddbfbfe198
commit 8201baef2b
2705 changed files with 305722 additions and 53 deletions

View file

@ -0,0 +1,43 @@
/* ###
* IP: GHIDRA
*
* 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 ghidra.util.database.annotproc;
import java.lang.annotation.Annotation;
import javax.lang.model.element.*;
import javax.tools.Diagnostic.Kind;
public class AbstractDBAnnotationValidator {
protected final ValidationContext ctx;
public AbstractDBAnnotationValidator(ValidationContext ctx) {
this.ctx = ctx;
}
protected void checkEnclosingType(Class<? extends Annotation> annotType, VariableElement field,
TypeElement type) {
if (type.getKind() != ElementKind.CLASS) {
ctx.messager.printMessage(Kind.ERROR, String.format(
"@%s can only be applied to fields in a class", annotType.getSimpleName()), field);
}
else if (!ctx.isSubclass(type, ctx.DB_ANNOTATED_OBJECT_ELEM)) {
ctx.messager.printMessage(Kind.ERROR,
String.format("@%s can only be applied within a subclass of %s",
annotType.getSimpleName(), ctx.DB_ANNOTATED_OBJECT_ELEM),
field);
}
}
}

View file

@ -0,0 +1,62 @@
/* ###
* IP: GHIDRA
*
* 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 ghidra.util.database.annotproc;
import java.util.Set;
import javax.lang.model.element.Modifier;
public enum AccessSpec {
PRIVATE(0), PACKAGE(1), PROTECTED(2), PUBLIC(3);
private final int level;
private AccessSpec(int level) {
this.level = level;
}
/**
* Checks if the second permits the same or more access than the first
*
* @param first the first
* @param second the second
* @return true if the second is the same or more permissive
*/
public static boolean isSameOrMorePermissive(AccessSpec first, AccessSpec second) {
// TODO: I'm not sure protected actually includes package...
// It might be more diamond shaped
return first.level <= second.level;
}
/**
* Get the access specifier derived from the given modifiers
*
* @param modifiers the element's modifiers
* @return the elements access specification
*/
public static AccessSpec get(Set<Modifier> modifiers) {
if (modifiers.contains(Modifier.PRIVATE)) {
return PRIVATE;
}
if (modifiers.contains(Modifier.PROTECTED)) {
return PROTECTED;
}
if (modifiers.contains(Modifier.PUBLIC)) {
return PUBLIC;
}
return PACKAGE;
}
}

View file

@ -0,0 +1,56 @@
/* ###
* IP: GHIDRA
*
* 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 ghidra.util.database.annotproc;
import java.util.Set;
import javax.lang.model.element.*;
import javax.tools.Diagnostic.Kind;
import ghidra.util.database.annot.DBAnnotatedColumn;
public class DBAnnotatedColumnValidator extends AbstractDBAnnotationValidator {
final VariableElement column;
public DBAnnotatedColumnValidator(ValidationContext ctx, VariableElement column) {
super(ctx);
this.column = column;
}
public void validate() {
if (!ctx.hasType(column, ctx.DB_OBJECT_COLUMN_ELEM)) {
ctx.messager.printMessage(Kind.ERROR,
String.format("@%s can only be applied to fields of type %s",
DBAnnotatedColumn.class.getSimpleName(), ctx.DB_OBJECT_COLUMN_ELEM),
column);
}
Set<Modifier> mods = column.getModifiers();
if (mods.contains(Modifier.FINAL)) {
ctx.messager.printMessage(Kind.ERROR,
String.format("@%s cannot be applied to a final field",
DBAnnotatedColumn.class.getSimpleName()),
column);
}
if (!mods.contains(Modifier.STATIC)) {
ctx.messager.printMessage(Kind.ERROR,
String.format("@%s must be applied to a static field",
DBAnnotatedColumn.class.getSimpleName()),
column);
}
TypeElement type = (TypeElement) column.getEnclosingElement();
checkEnclosingType(DBAnnotatedColumn.class, column, type);
}
}

View file

@ -0,0 +1,214 @@
/* ###
* IP: GHIDRA
*
* 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 ghidra.util.database.annotproc;
import java.util.*;
import javax.lang.model.element.*;
import javax.lang.model.type.*;
import javax.tools.Diagnostic.Kind;
import db.DBHandle;
import ghidra.util.database.DBCachedDomainObjectAdapter;
import ghidra.util.database.DBOpenMode;
import ghidra.util.database.annot.DBAnnotatedField;
import ghidra.util.task.TaskMonitor;
public class DBAnnotatedFieldValidator extends AbstractDBAnnotationValidator {
final VariableElement field;
final Map<TypeMirror, TypeElement> javaToDBTypeMap;
final static String FACTORY_NAME = "ghidra.util.database.DBCachedObjectStoreFactory";
final static String BOOLEAN_CODEC_NAME = FACTORY_NAME + ".BooleanDBFieldCodec";
final static String BYTE_CODEC_NAME = FACTORY_NAME + ".ByteDBFieldCodec";
final static String SHORT_CODEC_NAME = FACTORY_NAME + ".ShortDBFieldCodec";
final static String INT_CODEC_NAME = FACTORY_NAME + ".IntDBFieldCodec";
final static String LONG_CODEC_NAME = FACTORY_NAME + ".LongDBFieldCodec";
final static String STRING_CODEC_NAME = FACTORY_NAME + ".StringDBFieldCodec";
final static String BYTE_ARRAY_CODEC_NAME = FACTORY_NAME + ".ByteArrayDBFieldCodec";
final static String LONG_ARRAY_CODEC_NAME = FACTORY_NAME + ".LongArrayDBFieldCodec";
final static String ENUM_CODEC_NAME = FACTORY_NAME + ".EnumDBByteFieldCodec";
final TypeElement ENUM_CODEC_ELEM;
public DBAnnotatedFieldValidator(ValidationContext ctx, VariableElement field) {
super(ctx);
this.field = field;
Map<TypeMirror, TypeElement> typeMap = new LinkedHashMap<>();
putPrimitiveTypeCodec(typeMap, TypeKind.BOOLEAN, BOOLEAN_CODEC_NAME);
putPrimitiveTypeCodec(typeMap, TypeKind.BYTE, BYTE_CODEC_NAME);
putPrimitiveTypeCodec(typeMap, TypeKind.SHORT, SHORT_CODEC_NAME);
putPrimitiveTypeCodec(typeMap, TypeKind.INT, INT_CODEC_NAME);
putPrimitiveTypeCodec(typeMap, TypeKind.LONG, LONG_CODEC_NAME);
putTypeCodec(typeMap, String.class, STRING_CODEC_NAME);
putPrimitiveArrayTypeCodec(typeMap, TypeKind.BYTE, BYTE_ARRAY_CODEC_NAME);
putPrimitiveArrayTypeCodec(typeMap, TypeKind.LONG, LONG_ARRAY_CODEC_NAME);
// NOTE: Enum requires subtype check
javaToDBTypeMap = Map.copyOf(typeMap);
ENUM_CODEC_ELEM = ctx.elementUtils.getTypeElement(ENUM_CODEC_NAME);
}
protected void putPrimitiveTypeCodec(Map<TypeMirror, TypeElement> map, TypeKind kind,
String codecName) {
PrimitiveType primitive = ctx.typeUtils.getPrimitiveType(kind);
TypeMirror boxed = ctx.typeUtils.boxedClass(primitive).asType();
TypeElement codec = ctx.elementUtils.getTypeElement(codecName);
map.put(primitive, codec);
map.put(boxed, codec);
}
protected void putTypeCodec(Map<TypeMirror, TypeElement> map, Class<?> cls, String codecName) {
TypeMirror type = ctx.elementUtils.getTypeElement(cls.getCanonicalName()).asType();
TypeElement codec = ctx.elementUtils.getTypeElement(codecName);
map.put(type, codec);
}
protected void putPrimitiveArrayTypeCodec(Map<TypeMirror, TypeElement> map, TypeKind kind,
String codecName) {
PrimitiveType primitive = ctx.typeUtils.getPrimitiveType(kind);
ArrayType array = ctx.typeUtils.getArrayType(primitive);
TypeElement codec = ctx.elementUtils.getTypeElement(codecName);
map.put(array, codec);
}
public void validate() {
Set<Modifier> mods = field.getModifiers();
if (mods.contains(Modifier.FINAL)) {
ctx.messager.printMessage(Kind.ERROR,
String.format("@%s cannot be applied to a final field",
DBAnnotatedField.class.getSimpleName()),
field);
}
if (mods.contains(Modifier.STATIC)) {
ctx.messager.printMessage(Kind.ERROR,
String.format("@%s cannot be applied to a static field",
DBAnnotatedField.class.getSimpleName()),
field);
}
TypeElement type = (TypeElement) field.getEnclosingElement();
checkEnclosingType(DBAnnotatedField.class, field, type);
checkCodecTypes(type);
}
protected TypeElement getDefaultCodecType(TypeMirror javaType) {
if (ctx.isEnumType(javaType)) {
return ENUM_CODEC_ELEM;
}
return javaToDBTypeMap.get(javaType);
}
protected TypeElement getCodecTypeElement() {
DBAnnotatedField annotation = field.getAnnotation(DBAnnotatedField.class);
TypeElement codecElem;
try {
codecElem = ctx.elementUtils.getTypeElement(annotation.codec().getCanonicalName());
}
catch (MirroredTypeException e) {
codecElem = (TypeElement) ((DeclaredType) e.getTypeMirror()).asElement();
}
if (codecElem == ctx.DEFAULT_CODEC_ELEM) {
return getDefaultCodecType(field.asType());
}
return codecElem;
}
class A extends DBCachedDomainObjectAdapter {
protected A(DBHandle dbh, DBOpenMode openMode, TaskMonitor monitor, String name,
int timeInterval, int bufSize, Object consumer) {
super(dbh, openMode, monitor, name, timeInterval, bufSize, consumer);
// TODO Auto-generated constructor stub
}
@Override
public boolean isChangeable() {
// TODO Auto-generated method stub
return false;
}
@Override
public String getDescription() {
// TODO Auto-generated method stub
return null;
}
}
protected void checkCodecTypes(TypeElement objectType) {
//experiment(new Blargh(null, null));
TypeElement codecType = getCodecTypeElement();
if (codecType == null) {
ctx.messager.printMessage(Kind.ERROR,
String.format("Could not select default codec for %s. @%s.codec must be specified.",
field.asType(), DBAnnotatedField.class.getSimpleName()),
field);
return;
}
// REQUIREMENTS:
// 1) ValueType matches the field's type exactly
// Cannot be super or extends because it's read/write
// 2) ObjectType is super of the containing object
// Need to ensure extra interfaces (intersection) are considered
// 3) FieldType is non-abstract
// 4) The codec has an appropriate constructor
for (Element enc : codecType.getEnclosedElements()) {
if (enc.getKind() == ElementKind.CONSTRUCTOR) {
ExecutableElement exe = (ExecutableElement) enc;
ExecutableType exeType = (ExecutableType) exe.asType();
//throw new RuntimeException();
}
}
Map<String, TypeMirror> args = ctx.getArguments(codecType, ctx.DB_FIELD_CODEC_ELEM);
// 1)
TypeMirror argVT = args.get("VT");
if (!ctx.hasType(field, argVT)) {
ctx.messager.printMessage(Kind.ERROR,
String.format("Codec %s can only be used with fields of type %s", codecType, argVT),
field);
}
// 2) (INCOMPLETE)
TypeMirror argOT = args.get("OT");
if (!ctx.isCapturable(objectType.asType(), argOT)) {
ctx.messager.printMessage(Kind.ERROR,
String.format("Codec %s requires the containing object to conform to %s", codecType,
ctx.format(argOT)),
field);
}
// 3)
TypeMirror argFT = args.get("FT");
if (argFT.getKind() != TypeKind.DECLARED) {
ctx.messager.printMessage(Kind.ERROR,
String.format("Codec %s must have a non-abstract class for its field type, not %s",
codecType, argFT),
codecType);
}
else if (((DeclaredType) argFT).asElement().getModifiers().contains(Modifier.ABSTRACT)) {
ctx.messager.printMessage(Kind.ERROR,
String.format("Codec %s must have a non-abstract class for its field type, not %s",
codecType, argFT),
codecType);
}
}
}

View file

@ -0,0 +1,96 @@
/* ###
* IP: GHIDRA
*
* 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 ghidra.util.database.annotproc;
import java.lang.annotation.Annotation;
import java.util.*;
import java.util.stream.Collectors;
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.*;
import ghidra.util.database.DBAnnotatedObject;
import ghidra.util.database.annot.*;
/**
* A compile-time annotation processor for {@link DBAnnotatedObject}-related annotations.
*
* Currently just performs compile-time checks. It does not generate any code, but perhaps one day,
* it will.
*/
//@AutoService(Processor.class) // TODO: Evaluate Google's auto-service as a dependency
public class DBAnnotatedObjectProcessor extends AbstractProcessor {
static final Set<Class<? extends Annotation>> SUPPORTED_ANNOTATIONS =
Set.of(DBAnnotatedColumn.class, DBAnnotatedField.class, DBAnnotatedObjectInfo.class);
private ValidationContext ctx;
@Override
public synchronized void init(ProcessingEnvironment env) {
//System.err.println("HERE4");
super.init(env);
ctx = new ValidationContext(env);
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
Map<TypeElement, DBAnnotatedObjectValidator> types = new LinkedHashMap<>();
for (Element element : roundEnv.getElementsAnnotatedWith(DBAnnotatedObjectInfo.class)) {
TypeElement type = (TypeElement) element; // Required by annotation Target
types.put(type, new DBAnnotatedObjectValidator(ctx, type));
}
for (Element field : roundEnv.getElementsAnnotatedWith(DBAnnotatedField.class)) {
VariableElement varField = (VariableElement) field; // Required by annotation Target
// Fields can only be members of types, right?
TypeElement type = (TypeElement) field.getEnclosingElement();
DBAnnotatedObjectValidator validator =
types.computeIfAbsent(type, t -> new DBAnnotatedObjectValidator(ctx, type));
validator.addAnnotatedField(varField);
}
for (Element column : roundEnv.getElementsAnnotatedWith(DBAnnotatedColumn.class)) {
VariableElement varColumn = (VariableElement) column; // Required by annotation Target
// Fields can only be members of types, right?
TypeElement type = (TypeElement) column.getEnclosingElement();
DBAnnotatedObjectValidator validator =
types.computeIfAbsent(type, t -> new DBAnnotatedObjectValidator(ctx, type));
validator.addAnnotatedColumn(varColumn);
}
for (DBAnnotatedObjectValidator ov : types.values()) {
ov.validate();
}
return true;
}
@Override
public Iterable<? extends Completion> getCompletions(Element element,
AnnotationMirror annotation, ExecutableElement member, String userText) {
// TODO Auto-generated method stub
return super.getCompletions(element, annotation, member, userText);
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
@Override
public Set<String> getSupportedAnnotationTypes() {
return SUPPORTED_ANNOTATIONS.stream().map(Class::getCanonicalName).collect(
Collectors.toSet());
}
}

View file

@ -0,0 +1,137 @@
/* ###
* IP: GHIDRA
*
* 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 ghidra.util.database.annotproc;
import java.util.*;
import javax.lang.model.element.*;
import javax.tools.Diagnostic.Kind;
import ghidra.util.database.annot.*;
public class DBAnnotatedObjectValidator {
private final ValidationContext ctx;
private final TypeElement type;
private final Map<String, DBAnnotatedFieldValidator> fieldsByName = new LinkedHashMap<>();
private final Map<String, DBAnnotatedColumnValidator> columnsByName = new LinkedHashMap<>();
public DBAnnotatedObjectValidator(ValidationContext ctx, TypeElement type) {
this.ctx = ctx;
this.type = type;
}
public void addAnnotatedField(VariableElement field) {
DBAnnotatedField annotation = field.getAnnotation(DBAnnotatedField.class);
assert annotation != null;
fieldsByName.put(annotation.column(), new DBAnnotatedFieldValidator(ctx, field));
}
public void addAnnotatedColumn(VariableElement column) {
DBAnnotatedColumn annotation = column.getAnnotation(DBAnnotatedColumn.class);
assert annotation != null;
columnsByName.put(annotation.value(), new DBAnnotatedColumnValidator(ctx, column));
}
public void validate() {
DBAnnotatedObjectInfo annotation = type.getAnnotation(DBAnnotatedObjectInfo.class);
if (annotation != null && type.getKind() != ElementKind.CLASS) {
ctx.messager.printMessage(Kind.ERROR,
String.format("@%s cannot be applied to an interface",
DBAnnotatedObjectInfo.class.getSimpleName()),
type);
}
else if (annotation != null && type.getModifiers().contains(Modifier.ABSTRACT)) {
ctx.messager.printMessage(Kind.ERROR,
String.format("@%s cannot be applied to an abstract class",
DBAnnotatedObjectInfo.class.getSimpleName()),
type);
}
if (annotation != null && !ctx.isSubclass(type, ctx.DB_ANNOTATED_OBJECT_ELEM)) {
ctx.messager.printMessage(Kind.ERROR,
String.format("@%s can only be applied to subclasses of %s", "DBAnnotatedObject",
DBAnnotatedObjectInfo.class.getSimpleName()));
}
if (annotation == null && !type.getModifiers().contains(Modifier.ABSTRACT)) {
ctx.messager.printMessage(Kind.ERROR,
String.format("Non-abstract subclasses of %s must have @%s annotation",
"DBAnnotatedObject", DBAnnotatedObjectInfo.class.getSimpleName()),
type);
}
if (annotation != null && annotation.version() < 0) {
ctx.messager.printMessage(Kind.ERROR, String.format("@%s.version cannot be negative",
DBAnnotatedObjectInfo.class.getSimpleName()), type);
}
validateFields();
validateColumns();
checkMissing();
}
protected void validateFields() {
for (DBAnnotatedFieldValidator fv : fieldsByName.values()) {
fv.validate();
}
}
protected void validateColumns() {
for (DBAnnotatedColumnValidator cv : columnsByName.values()) {
cv.validate();
}
}
protected void checkMissing() {
Set<String> names = new LinkedHashSet<>();
names.addAll(fieldsByName.keySet());
names.addAll(columnsByName.keySet());
for (String n : names) {
DBAnnotatedFieldValidator fv = fieldsByName.get(n);
DBAnnotatedColumnValidator cv = columnsByName.get(n);
if (fv == null && cv != null && !type.getModifiers().contains(Modifier.ABSTRACT)) {
ctx.messager.printMessage(Kind.ERROR,
String.format("@%s is missing corresponding @%s of the same column name: %s",
DBAnnotatedColumn.class.getSimpleName(),
DBAnnotatedField.class.getSimpleName(), n),
cv.column);
}
if (fv != null && cv == null && !type.getModifiers().contains(Modifier.ABSTRACT)) {
ctx.messager.printMessage(Kind.WARNING,
String.format("@%s is missing corresponding @%s of the same column name: %s",
DBAnnotatedField.class.getSimpleName(),
DBAnnotatedColumn.class.getSimpleName(), n),
fv.field);
}
if (fv != null && cv != null) {
checkAccess(fv.field, cv.column, n);
}
}
}
protected void checkAccess(VariableElement field, VariableElement column, String name) {
AccessSpec fieldSpec = AccessSpec.get(field.getModifiers());
AccessSpec columnSpec = AccessSpec.get(column.getModifiers());
if (!AccessSpec.isSameOrMorePermissive(fieldSpec, columnSpec)) {
ctx.messager.printMessage(Kind.WARNING,
String.format(
"field with @%s should have same or greater access than field with" +
" corresponding @%s for column name: %s",
DBAnnotatedColumn.class.getSimpleName(), DBAnnotatedField.class.getSimpleName(),
name),
column);
}
}
}

View file

@ -0,0 +1,326 @@
/* ###
* IP: GHIDRA
*
* 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 ghidra.util.database.annotproc;
import java.util.*;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.*;
import javax.lang.model.type.*;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import ghidra.util.database.annot.DBAnnotatedField;
public class ValidationContext {
final Types typeUtils;
final Elements elementUtils;
final Messager messager;
final TypeElement LIST_ELEM;
final TypeElement DB_ANNOTATED_OBJECT_ELEM;
final TypeElement DB_OBJECT_COLUMN_ELEM;
final TypeElement DB_FIELD_CODEC_ELEM;
final TypeElement DEFAULT_CODEC_ELEM;
final TypeElement ENUM_ELEM;
public ValidationContext(ProcessingEnvironment env) {
typeUtils = env.getTypeUtils();
elementUtils = env.getElementUtils();
messager = env.getMessager();
LIST_ELEM = elementUtils.getTypeElement(List.class.getCanonicalName());
DB_ANNOTATED_OBJECT_ELEM =
elementUtils.getTypeElement("ghidra.util.database.DBAnnotatedObject");
DB_OBJECT_COLUMN_ELEM = elementUtils.getTypeElement("ghidra.util.database.DBObjectColumn");
DB_FIELD_CODEC_ELEM = elementUtils.getTypeElement(
"ghidra.util.database.DBCachedObjectStoreFactory.DBFieldCodec");
DEFAULT_CODEC_ELEM = elementUtils.getTypeElement(
DBAnnotatedField.class.getCanonicalName() + ".DefaultCodec");
ENUM_ELEM = elementUtils.getTypeElement(Enum.class.getCanonicalName());
}
public boolean isSubclass(TypeElement t1, TypeElement t2) {
return typeUtils.isSubtype(typeUtils.erasure(t1.asType()), typeUtils.erasure(t2.asType()));
}
public boolean hasType(VariableElement field, TypeElement type) {
return hasType(field, type.asType());
}
public boolean hasType(VariableElement field, TypeMirror type) {
TypeMirror fieldType = field.asType();
try {
PrimitiveType unboxed = typeUtils.unboxedType(type);
if (typeUtils.isSameType(fieldType, unboxed)) {
return true;
}
}
catch (IllegalArgumentException e) {
// Eh, I guess it's not unboxable
}
if (fieldType.getKind() == TypeKind.DECLARED) {
DeclaredType declType = (DeclaredType) fieldType;
if (isSubclass((TypeElement) declType.asElement(), ENUM_ELEM)) {
Map<String, TypeMirror> enumArgs = getArguments(declType, ENUM_ELEM);
TypeMirror argE = enumArgs.get("E");
if (typeUtils.isSameType(declType, argE)) {
return true;
}
}
}
return typeUtils.isAssignable(fieldType, type);
// return typeUtils.isSameType(fieldType, type);
}
public boolean isCapturable(TypeMirror t1, TypeMirror t2) {
// TODO: This only works for typevar at top level...
// TODO: Need to figure out how to check for capture and check
if (t2.getKind() == TypeKind.TYPEVAR) {
TypeVariable v2 = (TypeVariable) t2;
if (!typeUtils.isSubtype(t1, v2.getUpperBound())) {
return false;
}
if (!typeUtils.isSubtype(v2.getLowerBound(), t1)) {
return false;
}
return true;
}
return typeUtils.isSubtype(t1, t2);
}
public boolean isEnumType(TypeMirror t) {
if (t.getKind() != TypeKind.DECLARED) {
return false;
}
DeclaredType enumType = typeUtils.getDeclaredType(ENUM_ELEM, t);
return typeUtils.isSubtype(t, enumType);
}
protected DeclaredType findSupertype(Set<DeclaredType> types, TypeElement superType) {
Set<DeclaredType> next;
while (!types.isEmpty()) {
next = new HashSet<>();
for (DeclaredType t : types) {
List<? extends TypeMirror> supers = typeUtils.directSupertypes(t);
for (TypeMirror s : supers) {
DeclaredType ds = (DeclaredType) s;
if (superType == ds.asElement()) {
return ds;
}
next.add(ds);
}
}
types = next;
}
return null;
}
public DeclaredType findSupertype(DeclaredType type, TypeElement superElem) {
return findSupertype(Set.of(type), superElem);
}
public DeclaredType findSupertype(TypeElement elem, TypeElement superElem) {
return findSupertype((DeclaredType) elem.asType(), superElem);
}
protected Map<String, TypeMirror> toArgsMap(TypeElement superElem, DeclaredType superType) {
List<? extends TypeParameterElement> typeParameters = superElem.getTypeParameters();
List<? extends TypeMirror> typeArguments = superType.getTypeArguments();
assert typeParameters.size() == typeArguments.size();
Map<String, TypeMirror> result = new HashMap<>();
for (int i = 0; i < typeParameters.size(); i++) {
result.put(typeParameters.get(i).getSimpleName().toString(), typeArguments.get(i));
}
return result;
}
public Map<String, TypeMirror> getArguments(DeclaredType type, TypeElement superElem) {
return toArgsMap(superElem, findSupertype(type, superElem));
}
public Map<String, TypeMirror> getArguments(TypeElement elem, TypeElement superElem) {
return toArgsMap(superElem, findSupertype(elem, superElem));
}
public String format(TypeMirror type) {
FormatVisitor vis = new FormatVisitor();
type.accept(vis, null);
return vis.buf.toString();
}
}
class FormatVisitor implements TypeVisitor<Void, Void> {
StringBuffer buf = new StringBuffer();
@Override
public Void visit(TypeMirror t, Void p) {
switch (t.getKind()) {
case ARRAY:
return visitArray((ArrayType) t, p);
case BOOLEAN:
case BYTE:
case CHAR:
case DOUBLE:
case FLOAT:
case INT:
case LONG:
case SHORT:
case VOID:
return visitPrimitive((PrimitiveType) t, p);
case DECLARED:
return visitDeclared((DeclaredType) t, p);
case ERROR:
return visitError((ErrorType) t, p);
case EXECUTABLE:
return visitExecutable((ExecutableType) t, p);
case INTERSECTION:
return visitIntersection((IntersectionType) t, p);
case NONE:
return visitNoType((NoType) t, p);
case NULL:
return visitNull((NullType) t, p);
case TYPEVAR:
return visitTypeVariable((TypeVariable) t, p);
case UNION:
return visitUnion((UnionType) t, p);
case WILDCARD:
return visitWildcard((WildcardType) t, p);
default:
return visitUnknown(t, p);
}
}
@Override
public Void visitPrimitive(PrimitiveType t, Void p) {
buf.append(t.toString());
return null;
}
@Override
public Void visitNull(NullType t, Void p) {
buf.append(t.toString());
return null;
}
@Override
public Void visitArray(ArrayType t, Void p) {
visit(t.getComponentType());
buf.append("[]");
return null;
}
@Override
public Void visitDeclared(DeclaredType t, Void p) {
buf.append(t.asElement().toString());
Iterator<? extends TypeMirror> it = t.getTypeArguments().iterator();
if (it.hasNext()) {
buf.append("<");
visit(it.next());
while (it.hasNext()) {
buf.append(", ");
visit(it.next());
}
buf.append(">");
}
return null;
}
@Override
public Void visitError(ErrorType t, Void p) {
buf.append(t.toString());
return null;
}
@Override
public Void visitTypeVariable(TypeVariable t, Void p) {
buf.append(t.toString());
TypeMirror lower = t.getLowerBound();
if (lower.getKind() != TypeKind.NULL) {
buf.append(" super ");
visit(lower);
}
TypeMirror upper = t.getUpperBound();
if (!upper.toString().equals("java.lang.Object")) {
buf.append(" extends ");
visit(upper);
}
return null;
}
@Override
public Void visitWildcard(WildcardType t, Void p) {
buf.append("?");
TypeMirror sup = t.getSuperBound();
if (sup != null) {
buf.append(" super ");
visit(sup);
}
TypeMirror ext = t.getExtendsBound();
if (ext != null) {
buf.append(" extends ");
visit(ext);
}
return null;
}
@Override
public Void visitExecutable(ExecutableType t, Void p) {
buf.append(t.toString());
return null;
}
@Override
public Void visitNoType(NoType t, Void p) {
buf.append(t.toString());
return null;
}
@Override
public Void visitUnknown(TypeMirror t, Void p) {
buf.append(t.toString());
return null;
}
@Override
public Void visitUnion(UnionType t, Void p) {
Iterator<? extends TypeMirror> it = t.getAlternatives().iterator();
if (it.hasNext()) {
visit(it.next());
while (it.hasNext()) {
buf.append(" | ");
visit(it.next());
}
}
return null;
}
@Override
public Void visitIntersection(IntersectionType t, Void p) {
Iterator<? extends TypeMirror> it = t.getBounds().iterator();
if (it.hasNext()) {
visit(it.next());
while (it.hasNext()) {
buf.append(" & ");
visit(it.next());
}
}
return null;
}
}

View file

@ -0,0 +1 @@
ghidra.util.database.annotproc.DBAnnotatedObjectProcessor