blob: 8b6b5d23cef9c09c5610537f145ce8589c4aa8f8 [file] [log] [blame]
/*
* Copyright (C) 2013 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.ir.ast.JAnnotation;
import com.android.jack.ir.ast.JBlock;
import com.android.jack.ir.ast.JClass;
import com.android.jack.ir.ast.JConstructor;
import com.android.jack.ir.ast.JDefinedClass;
import com.android.jack.ir.ast.JDefinedClassOrInterface;
import com.android.jack.ir.ast.JExpressionStatement;
import com.android.jack.ir.ast.JMethod;
import com.android.jack.ir.ast.JMethodBody;
import com.android.jack.ir.ast.JMethodCall;
import com.android.jack.ir.ast.JMethodId;
import com.android.jack.ir.ast.JModifier;
import com.android.jack.ir.ast.JParameter;
import com.android.jack.ir.ast.JParameterRef;
import com.android.jack.ir.ast.JPrimitiveType.JPrimitiveTypeEnum;
import com.android.jack.ir.ast.JReturnStatement;
import com.android.jack.ir.ast.JSession;
import com.android.jack.ir.ast.JThis;
import com.android.jack.ir.ast.JThisRef;
import com.android.jack.ir.sourceinfo.SourceInfo;
import com.android.jack.library.DumpInLibrary;
import com.android.jack.library.PrebuiltCompatibility;
import com.android.jack.lookup.JMethodLookupException;
import com.android.jack.scheduling.feature.VisibilityBridge;
import com.android.jack.scheduling.filter.SourceTypeFilter;
import com.android.jack.transformations.request.AppendMethod;
import com.android.jack.transformations.request.TransformationRequest;
import com.android.jack.transformations.threeaddresscode.ThreeAddressCodeForm;
import com.android.jack.util.CloneExpressionVisitor;
import com.android.sched.item.Description;
import com.android.sched.item.Synchronized;
import com.android.sched.schedulable.Access;
import com.android.sched.schedulable.ExclusiveAccess;
import com.android.sched.schedulable.Filter;
import com.android.sched.schedulable.RunnableSchedulable;
import com.android.sched.schedulable.Support;
import com.android.sched.schedulable.Transform;
import com.android.sched.util.config.HasKeyId;
import com.android.sched.util.config.id.BooleanPropertyId;
import javax.annotation.Nonnull;
/**
* Build bridge on public method declared in non public super class.
*/
@Description("Build bridge on public method declared in non public super class.")
@Synchronized
@Transform(add = {JMethod.class, JParameter.class, JMethodBody.class,
JMethodCall.class, JThisRef.class, JReturnStatement.class, JExpressionStatement.class,
JParameterRef.class, JBlock.class}, remove = ThreeAddressCodeForm.class)
@Support(VisibilityBridge.class)
@HasKeyId
@Filter(SourceTypeFilter.class)
// This schedulable searches methods in the hierarchy
@ExclusiveAccess(JSession.class)
// Access super classes.
@Access(JSession.class)
public class VisibilityBridgeAdder implements RunnableSchedulable<JDefinedClassOrInterface> {
@Nonnull
public static final BooleanPropertyId VISIBILITY_BRIDGE = BooleanPropertyId.create(
"jack.legacy.runtime.visibilitybridges", "Generate visibility bridges").addDefaultValue(
Boolean.TRUE).addCategory(DumpInLibrary.class).addCategory(PrebuiltCompatibility.class);
@Override
public synchronized void run(@Nonnull JDefinedClassOrInterface declaredType) {
if (!declaredType.isPublic() || !(declaredType instanceof JDefinedClass)) {
return;
}
JDefinedClass superClass = (JDefinedClass) declaredType.getSuperClass();
while (superClass != null && !superClass.isPublic()) {
for (JMethod method : superClass.getMethods()) {
if (method.isPublic() && !(method instanceof JConstructor) && !method.isStatic()
&& !method.isFinal()) {
try {
declaredType.getMethod(method.getMethodId());
} catch (JMethodLookupException e) {
// the method is not declared in class, a bridge is required
synthesizeBridge((JDefinedClass) declaredType, method);
}
}
}
superClass = (JDefinedClass) superClass.getSuperClass();
}
}
private void synthesizeBridge(@Nonnull JDefinedClass jClass, @Nonnull JMethod method) {
SourceInfo sourceInfo = SourceInfo.UNKNOWN;
JMethodId methodId = method.getMethodId();
int bridgeModifier = method.getModifier();
// Remove non inherited flags
bridgeModifier &= ~(JModifier.SYNCHRONIZED | JModifier.ABSTRACT | JModifier.STRICTFP
| JModifier.NATIVE);
// Add bridge specific flags
bridgeModifier |= JModifier.SYNTHETIC | JModifier.BRIDGE;
JMethod bridge = new JMethod(sourceInfo, methodId, jClass, bridgeModifier);
CloneExpressionVisitor cloner = new CloneExpressionVisitor();
for (JParameter param : method.getParams()) {
JParameter bridgeParam = new JParameter(sourceInfo, param.getName(), param.getType(),
param.getModifier() | JModifier.SYNTHETIC, bridge);
for (JAnnotation annotation : param.getAnnotations()) {
bridgeParam.addAnnotation(cloner.cloneExpression(annotation));
}
bridge.addParam(bridgeParam);
}
for (JAnnotation annotation : method.getAnnotations()) {
bridge.addAnnotation(cloner.cloneExpression(annotation));
}
JBlock bodyBlock = new JBlock(sourceInfo);
JMethodBody body = new JMethodBody(sourceInfo, bodyBlock);
JClass superClass = jClass.getSuperClass();
assert superClass != null;
JThis jThis = bridge.getThis();
assert jThis != null;
JMethodCall callToSuper = new JMethodCall(sourceInfo, jThis.makeRef(sourceInfo), superClass,
methodId, false /* isVirtualDispatch */);
for (JParameter param : bridge.getParams()) {
callToSuper.addArg(param.makeRef(sourceInfo));
}
if (method.getType() != JPrimitiveTypeEnum.VOID.getType()) {
bodyBlock.addStmt(new JReturnStatement(sourceInfo, callToSuper));
} else {
bodyBlock.addStmt(new JExpressionStatement(sourceInfo, callToSuper));
bodyBlock.addStmt(new JReturnStatement(sourceInfo, null));
}
bridge.setBody(body);
TransformationRequest tr = new TransformationRequest(jClass);
tr.append(new AppendMethod(jClass, bridge));
tr.commit();
}
}