diff options
5 files changed, 306 insertions, 101 deletions
diff --git a/jack/src/com/android/jack/Jack.java b/jack/src/com/android/jack/Jack.java index f6018c4..0bf5664 100644 --- a/jack/src/com/android/jack/Jack.java +++ b/jack/src/com/android/jack/Jack.java @@ -236,6 +236,8 @@ import com.android.jack.transformations.parent.TypeAstChecker; import com.android.jack.transformations.renamepackage.PackageRenamer; import com.android.jack.transformations.rop.cast.RopCastLegalizer; import com.android.jack.transformations.threeaddresscode.ThreeAddressCodeBuilder; +import com.android.jack.transformations.typedef.TypeDefRemover; +import com.android.jack.transformations.typedef.TypeDefRemover.RemoveTypeDef; import com.android.jack.transformations.uselessif.UselessIfChecker; import com.android.jack.transformations.uselessif.UselessIfRemover; import com.android.jack.util.collect.UnmodifiableCollections; @@ -543,6 +545,10 @@ public abstract class Jack { request.addProduction(DependencyInLibraryProduct.class); } + if (config.get(TypeDefRemover.REMOVE_TYPEDEF).booleanValue()) { + request.addFeature(TypeDefRemover.RemoveTypeDef.class); + } + ProductionSet targetProduction = request.getTargetProductions(); FeatureSet features = request.getFeatures(); PlanBuilder<JSession> planBuilder; @@ -892,6 +898,12 @@ public abstract class Jack { planBuilder.append(TypeDuplicateRemoverChecker.class); } + if (features.contains(RemoveTypeDef.class)) { + SubPlanBuilder<JDefinedClassOrInterface> typePlan = + planBuilder.appendSubPlan(JDefinedClassOrInterfaceAdapter.class); + typePlan.append(TypeDefRemover.class); + } + appendStringRefiners(planBuilder); if (features.contains(Jarjar.class)) { diff --git a/jack/src/com/android/jack/jayce/v0002/nodes/NMethod.java b/jack/src/com/android/jack/jayce/v0002/nodes/NMethod.java index e98fb1c..70e18b7 100644 --- a/jack/src/com/android/jack/jayce/v0002/nodes/NMethod.java +++ b/jack/src/com/android/jack/jayce/v0002/nodes/NMethod.java @@ -142,7 +142,7 @@ public class NMethod extends NNode implements HasSourceInfo, MethodNode { public JAbstractMethodBody loadBody(@Nonnull JMethod method) throws JTypeLookupException, JMethodLookupException { if (body != null) { - JSession session = method.getParent(JSession.class); + JSession session = method.getEnclosingType().getSession(); ExportSession exportSession = new ExportSession(session.getPhantomLookup(), session, NodeLevel.FULL); exportSession.setCurrentMethod(method); diff --git a/jack/src/com/android/jack/shrob/shrink/TypeShrinker.java b/jack/src/com/android/jack/shrob/shrink/TypeShrinker.java index 5e455ae..5a95cc4 100644 --- a/jack/src/com/android/jack/shrob/shrink/TypeShrinker.java +++ b/jack/src/com/android/jack/shrob/shrink/TypeShrinker.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2015 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. @@ -16,31 +16,16 @@ package com.android.jack.shrob.shrink; -import com.android.jack.Jack; -import com.android.jack.ir.ast.JClass; -import com.android.jack.ir.ast.JClassOrInterface; -import com.android.jack.ir.ast.JDefinedClass; import com.android.jack.ir.ast.JDefinedClassOrInterface; -import com.android.jack.ir.ast.JDefinedInterface; -import com.android.jack.ir.ast.JInterface; import com.android.jack.ir.ast.JMethod; import com.android.jack.ir.ast.JNode; -import com.android.jack.ir.ast.JSession; -import com.android.jack.transformations.request.Remove; -import com.android.jack.transformations.request.TransformationRequest; +import com.android.jack.transformations.TypeRemover; import com.android.sched.item.Description; import com.android.sched.item.Synchronized; import com.android.sched.schedulable.Constraint; import com.android.sched.schedulable.RunnableSchedulable; -import com.android.sched.util.log.LoggerFactory; import com.android.sched.util.log.TracerFactory; -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.annotation.CheckForNull; import javax.annotation.Nonnull; /** @@ -49,97 +34,24 @@ import javax.annotation.Nonnull; @Description("Removes all types not marked with the KeepMarker") @Synchronized @Constraint(need = KeepMarker.class) -public class TypeShrinker implements RunnableSchedulable<JDefinedClassOrInterface> { - - @Nonnull - private static final Logger logger = LoggerFactory.getLogger(); +public class TypeShrinker extends TypeRemover { @Nonnull private final com.android.sched.util.log.Tracer tracer = TracerFactory.getTracer(); - private static void updateSuperTypeList(@Nonnull JDefinedClassOrInterface type) { - if (type instanceof JDefinedClass) { - JClass superClass = type.getSuperClass(); - while (mustBeRemoved(superClass)) { - assert superClass != null; - for (JInterface i : ((JDefinedClass) superClass).getImplements()) { - addImplements(type, i); - } - superClass = ((JDefinedClass) superClass).getSuperClass(); - } - ((JDefinedClass) type).setSuperClass(superClass); - } - List<JInterface> implementsCopy = new ArrayList<JInterface>(type.getImplements()); - for (JInterface i : implementsCopy) { - if (mustBeRemoved(i)) { - JDefinedInterface jDefinedInterface = (JDefinedInterface) i; - type.remove(jDefinedInterface); - for (JInterface subInterface : jDefinedInterface.getImplements()) { - addImplements(type, subInterface); - } - } - } - } - - private static boolean mustBeRemoved(@CheckForNull JClassOrInterface type) { - return type instanceof JDefinedClassOrInterface && !type.isExternal() - && !((JNode) type).containsMarker(KeepMarker.class); - } - - private static void addImplements(@Nonnull JDefinedClassOrInterface type, @Nonnull JInterface i) { - if (!type.getImplements().contains(i)) { - if (!mustBeRemoved(i)) { - type.addImplements(i); - } else { - for (JInterface subInterface : ((JDefinedInterface) i).getImplements()) { - addImplements(type, subInterface); - } - } - } - } - @Override - public synchronized void run(@Nonnull JDefinedClassOrInterface type) throws Exception { - boolean toRemove = !type.containsMarker(KeepMarker.class); - if (toRemove) { - TransformationRequest request = new TransformationRequest(type); - request.append(new Remove(type)); - logger.log(Level.INFO, "Removed type {0}", Jack.getUserFriendlyFormatter().getName(type)); - JClassOrInterface enclosing = type.getEnclosingType(); - if (enclosing instanceof JDefinedClassOrInterface) { - JDefinedClassOrInterface enclosingType = (JDefinedClassOrInterface) enclosing; - enclosingType.removeMemberType(type); - } - type.getParent(JSession.class).removeTypeToEmit(type); - request.commit(); - } else { - updateSuperTypeList(type); - updateEnclosingType(type); - if (type instanceof JDefinedClass) { - updateEnclosingMethod((JDefinedClass) type); - } - } - tracer.getStatistic(ShrinkStatistic.TYPES_REMOVED).add(toRemove); + protected boolean mustBeRemoved(@Nonnull JDefinedClassOrInterface type) { + return !type.isExternal() && !((JNode) type).containsMarker(KeepMarker.class); } - private void updateEnclosingType(@Nonnull JDefinedClassOrInterface type) { - JClassOrInterface enclosingType = type.getEnclosingType(); - while (enclosingType instanceof JDefinedClassOrInterface) { - if (((JNode) enclosingType).containsMarker(KeepMarker.class) || enclosingType.isExternal()) { - break; - } - enclosingType = ((JDefinedClassOrInterface) enclosingType).getEnclosingType(); - } - type.setEnclosingType(enclosingType); + @Override + public void run(@Nonnull JDefinedClassOrInterface type) throws Exception { + super.run(type); + tracer.getStatistic(ShrinkStatistic.TYPES_REMOVED).add(mustBeRemoved(type)); } - private void updateEnclosingMethod(@Nonnull JDefinedClass type) { - JMethod enclosingMethod = type.getEnclosingMethod(); - if (enclosingMethod != null - && !enclosingMethod.containsMarker(KeepMarker.class)) { - assert !enclosingMethod.isExternal(); - type.setEnclosingMethod(null); - } + @Override + protected boolean isPlannedForRemoval(@Nonnull JMethod method) { + return !method.isExternal() && !((JNode) method).containsMarker(KeepMarker.class); } - } diff --git a/jack/src/com/android/jack/transformations/TypeRemover.java b/jack/src/com/android/jack/transformations/TypeRemover.java new file mode 100644 index 0000000..f81c203 --- /dev/null +++ b/jack/src/com/android/jack/transformations/TypeRemover.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2015 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.jack.transformations; + +import com.android.jack.Jack; +import com.android.jack.ir.ast.JClass; +import com.android.jack.ir.ast.JClassOrInterface; +import com.android.jack.ir.ast.JDefinedClass; +import com.android.jack.ir.ast.JDefinedClassOrInterface; +import com.android.jack.ir.ast.JDefinedInterface; +import com.android.jack.ir.ast.JInterface; +import com.android.jack.ir.ast.JMethod; +import com.android.jack.transformations.request.Remove; +import com.android.jack.transformations.request.TransformationRequest; +import com.android.sched.item.Synchronized; +import com.android.sched.schedulable.RunnableSchedulable; +import com.android.sched.util.log.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; + +/** + * Abstract schedulable removing a selection of types. + */ +@Synchronized +public abstract class TypeRemover implements RunnableSchedulable<JDefinedClassOrInterface> { + + @Nonnull + private static final Logger logger = LoggerFactory.getLogger(); + + private void updateSuperTypeList(@Nonnull JDefinedClassOrInterface type) { + if (type instanceof JDefinedClass) { + JClass superClass = type.getSuperClass(); + while (mustBeRemovedInternal(superClass)) { + assert superClass != null; + for (JInterface i : ((JDefinedClass) superClass).getImplements()) { + addImplements(type, i); + } + superClass = ((JDefinedClass) superClass).getSuperClass(); + } + ((JDefinedClass) type).setSuperClass(superClass); + } + List<JInterface> implementsCopy = new ArrayList<JInterface>(type.getImplements()); + for (JInterface i : implementsCopy) { + if (mustBeRemovedInternal(i)) { + JDefinedInterface jDefinedInterface = (JDefinedInterface) i; + type.remove(jDefinedInterface); + for (JInterface subInterface : jDefinedInterface.getImplements()) { + addImplements(type, subInterface); + } + } + } + } + + private boolean mustBeRemovedInternal(@CheckForNull JClassOrInterface type) { + if (type instanceof JDefinedClassOrInterface) { + return mustBeRemoved((JDefinedClassOrInterface) type); + } + return false; + } + + protected abstract boolean mustBeRemoved(@Nonnull JDefinedClassOrInterface type); + + protected abstract boolean isPlannedForRemoval(@Nonnull JMethod method); + + private void addImplements(@Nonnull JDefinedClassOrInterface type, @Nonnull JInterface i) { + if (!type.getImplements().contains(i)) { + if (!mustBeRemovedInternal(i)) { + type.addImplements(i); + } else { + for (JInterface subInterface : ((JDefinedInterface) i).getImplements()) { + addImplements(type, subInterface); + } + } + } + } + + @Override + public synchronized void run(@Nonnull JDefinedClassOrInterface type) throws Exception { + boolean toRemove = mustBeRemoved(type); + if (toRemove) { + TransformationRequest request = new TransformationRequest(type); + request.append(new Remove(type)); + logger.log(Level.INFO, "Removed type {0}", Jack.getUserFriendlyFormatter().getName(type)); + JClassOrInterface enclosing = type.getEnclosingType(); + if (enclosing instanceof JDefinedClassOrInterface) { + JDefinedClassOrInterface enclosingType = (JDefinedClassOrInterface) enclosing; + enclosingType.removeMemberType(type); + } + type.getSession().removeTypeToEmit(type); + request.commit(); + } else { + updateSuperTypeList(type); + updateEnclosingType(type); + if (type instanceof JDefinedClass) { + updateEnclosingMethod((JDefinedClass) type); + } + } + } + + private void updateEnclosingType(@Nonnull JDefinedClassOrInterface type) { + JClassOrInterface enclosingType = type.getEnclosingType(); + while (enclosingType instanceof JDefinedClassOrInterface) { + if (!mustBeRemovedInternal(enclosingType) || enclosingType.isExternal()) { + break; + } + enclosingType = ((JDefinedClassOrInterface) enclosingType).getEnclosingType(); + } + type.setEnclosingType(enclosingType); + } + + private void updateEnclosingMethod(@Nonnull JDefinedClass type) { + JMethod enclosingMethod = type.getEnclosingMethod(); + if (enclosingMethod != null && isPlannedForRemoval(enclosingMethod)) { + assert !enclosingMethod.isExternal(); + type.setEnclosingMethod(null); + } + } +}
\ No newline at end of file diff --git a/jack/src/com/android/jack/transformations/typedef/TypeDefRemover.java b/jack/src/com/android/jack/transformations/typedef/TypeDefRemover.java new file mode 100644 index 0000000..b63ea93 --- /dev/null +++ b/jack/src/com/android/jack/transformations/typedef/TypeDefRemover.java @@ -0,0 +1,143 @@ +/* + * 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.jack.transformations.typedef; + +import com.android.jack.Jack; +import com.android.jack.ir.ast.JDefinedAnnotationType; +import com.android.jack.ir.ast.JDefinedClassOrInterface; +import com.android.jack.ir.ast.JMethod; +import com.android.jack.ir.ast.JRetentionPolicy; +import com.android.jack.reporting.Reportable; +import com.android.jack.reporting.Reporter.Severity; +import com.android.jack.transformations.TypeRemover; +import com.android.jack.transformations.typedef.TypeDefRemover.RemoveTypeDef; +import com.android.sched.item.Description; +import com.android.sched.item.Feature; +import com.android.sched.item.Synchronized; +import com.android.sched.schedulable.RunnableSchedulable; +import com.android.sched.schedulable.Support; +import com.android.sched.util.config.HasKeyId; +import com.android.sched.util.config.id.BooleanPropertyId; + +import javax.annotation.Nonnull; + +/** + * A {@link RunnableSchedulable} removing @StringDef, @IntDef and all annotations + * annotated with them. + */ +@Description("Removes @StringDef, @IntDef and all annotations annotated with them") +@Synchronized +@Support(RemoveTypeDef.class) +@HasKeyId +public class TypeDefRemover extends TypeRemover { + + @Nonnull + public static final BooleanPropertyId REMOVE_TYPEDEF = BooleanPropertyId.create( + "jack.android.remove-typedef", + "Removes @StringDef, @IntDef and all annotations annotated with them") + .addDefaultValue(false); + + /** + * A {@link Feature} allowing to remove @StringDef, @IntDef and all annotations annotated with + * them. + */ + @Description("Removes @StringDef, @IntDef and all annotations annotated with them") + public static class RemoveTypeDef implements Feature { + } + + private static class InvalidRetentionForTypeDef implements Reportable { + + @Nonnull + private final JDefinedAnnotationType typeDef; + + public InvalidRetentionForTypeDef(@Nonnull JDefinedAnnotationType typeDef) { + this.typeDef = typeDef; + } + + @Override + @Nonnull + public String getMessage() { + return "Annotation @" + Jack.getUserFriendlyFormatter().getName(typeDef) + + "should be annotated with @Retention(RetentionPolicy.SOURCE)"; + } + + @Override + @Nonnull + public ProblemLevel getDefaultProblemLevel() { + return ProblemLevel.WARNING; + } + } + + private static class InvalidTypeDefTarget implements Reportable { + + @Nonnull + private final JDefinedClassOrInterface annotated; + + public InvalidTypeDefTarget(@Nonnull JDefinedClassOrInterface annotated) { + this.annotated = annotated; + } + + @Override + @Nonnull + public String getMessage() { + return "Type " + Jack.getUserFriendlyFormatter().getName(annotated) + + " should not be annotated with @IntDef or @StringDef," + + " only annotations are valid targets"; + } + + @Override + @Nonnull + public ProblemLevel getDefaultProblemLevel() { + return ProblemLevel.WARNING; + } + } + + @Nonnull + private final JDefinedAnnotationType stringDef = + Jack.getSession().getLookup().getAnnotationType("Landroid/annotation/StringDef;"); + + @Nonnull + private final JDefinedAnnotationType intDef = + Jack.getSession().getLookup().getAnnotationType("Landroid/annotation/IntDef;"); + + @Override + protected boolean mustBeRemoved(@Nonnull JDefinedClassOrInterface type) { + if (type == stringDef || type == intDef) { + return true; + } + if (type instanceof JDefinedAnnotationType) { + if ((!type.getAnnotations(stringDef).isEmpty()) || !type.getAnnotations(intDef).isEmpty()) { + JDefinedAnnotationType typeDef = (JDefinedAnnotationType) type; + if (typeDef.getRetentionPolicy() != JRetentionPolicy.SOURCE) { + type.getSession().getReporter().report(Severity.NON_FATAL, + new InvalidRetentionForTypeDef(typeDef)); + } + return true; + } + } else if ((!type.getAnnotations(stringDef).isEmpty()) + || !type.getAnnotations(intDef).isEmpty()) { + Jack.getSession().getReporter().report(Severity.NON_FATAL, + new InvalidTypeDefTarget(type)); + } + return false; + } + + @Override + protected boolean isPlannedForRemoval(@Nonnull JMethod method) { + return mustBeRemoved(method.getEnclosingType()); + } +} |