blob: 3b339358bae2360609cadf6098789efe721a63ac [file] [log] [blame]
/*
* 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.ast;
import com.android.jack.Jack;
import com.android.jack.Options;
import com.android.jack.ir.ast.JAsgOperation;
import com.android.jack.ir.ast.JBlock;
import com.android.jack.ir.ast.JCatchBlock;
import com.android.jack.ir.ast.JClass;
import com.android.jack.ir.ast.JClassLiteral;
import com.android.jack.ir.ast.JDefinedClass;
import com.android.jack.ir.ast.JExpression;
import com.android.jack.ir.ast.JExpressionStatement;
import com.android.jack.ir.ast.JLocal;
import com.android.jack.ir.ast.JLocalRef;
import com.android.jack.ir.ast.JLock;
import com.android.jack.ir.ast.JMethod;
import com.android.jack.ir.ast.JMethodBody;
import com.android.jack.ir.ast.JSession;
import com.android.jack.ir.ast.JStatement;
import com.android.jack.ir.ast.JSynchronizedBlock;
import com.android.jack.ir.ast.JThisRef;
import com.android.jack.ir.ast.JTryStatement;
import com.android.jack.ir.ast.JType;
import com.android.jack.ir.ast.JUnlock;
import com.android.jack.ir.ast.JVariable;
import com.android.jack.ir.ast.JVisitor;
import com.android.jack.ir.sourceinfo.SourceInfo;
import com.android.jack.library.DumpInLibrary;
import com.android.jack.library.PrebuiltCompatibility;
import com.android.jack.lookup.CommonTypes;
import com.android.jack.scheduling.filter.TypeWithoutPrebuiltFilter;
import com.android.jack.transformations.LocalVarCreator;
import com.android.jack.transformations.request.AppendBefore;
import com.android.jack.transformations.request.Replace;
import com.android.jack.transformations.request.TransformationRequest;
import com.android.jack.transformations.threeaddresscode.ThreeAddressCodeForm;
import com.android.sched.item.Description;
import com.android.sched.item.Name;
import com.android.sched.schedulable.Constraint;
import com.android.sched.schedulable.Filter;
import com.android.sched.schedulable.RunnableSchedulable;
import com.android.sched.schedulable.Transform;
import com.android.sched.schedulable.Use;
import com.android.sched.util.config.HasKeyId;
import com.android.sched.util.config.ThreadConfig;
import com.android.sched.util.config.id.BooleanPropertyId;
import java.util.Collections;
import javax.annotation.Nonnull;
/**
* Transform {@link JSynchronizedBlock} and synchronized flag of method into try/finally with
* {@link JLock} and {@link JUnlock} statement.
*/
@Description("Transform synchronization into try/finally with jlock/junlock statement.")
@Name("SynchronizeTransformer")
@Constraint(need = {NoImplicitBlock.class})
@Transform(remove = {JSynchronizedBlock.class, ThreeAddressCodeForm.class}, add = {JBlock.class,
JTryStatement.class,
JTryStatement.FinallyBlock.class,
JLock.class,
JUnlock.class,
JLocalRef.class,
JAsgOperation.NonReusedAsg.class,
JClassLiteral.class,
JThisRef.class,
JExpressionStatement.class})
@Use(LocalVarCreator.class)
@HasKeyId
@Filter(TypeWithoutPrebuiltFilter.class)
public class SynchronizeTransformer implements RunnableSchedulable<JMethod> {
@Nonnull
private final com.android.jack.util.filter.Filter<JMethod> filter =
ThreadConfig.get(Options.METHOD_FILTER);
@Nonnull
public static final BooleanPropertyId REUSE_SYNC_VARIABLE = BooleanPropertyId.create(
"jack.transformation.reusesyncvariable",
"Reduce the 'get class' usage in static synchronized methods by reusing a local variable")
.addDefaultValue(Boolean.TRUE)
.addCategory(DumpInLibrary.class)
.addCategory(PrebuiltCompatibility.class);
private final boolean reuseSyncVariable = ThreadConfig.get(REUSE_SYNC_VARIABLE).booleanValue();
@Nonnull
private final JSession session = Jack.getSession();
private class Visitor extends JVisitor {
@Nonnull
private final TransformationRequest tr;
@Nonnull
private final LocalVarCreator lvCreator;
public Visitor(@Nonnull TransformationRequest tr, @Nonnull LocalVarCreator lvCreator) {
this.tr = tr;
this.lvCreator = lvCreator;
}
@Override
public boolean visit(@Nonnull JMethodBody methodBody) {
JMethod enclosingMethod = methodBody.getMethod();
if (enclosingMethod.isSynchronized()) {
JBlock bodyBlock = methodBody.getBlock();
JTryStatement tryStmt = getTryFinally(SourceInfo.UNKNOWN, bodyBlock);
JType enclosingType = enclosingMethod.getEnclosingType();
JExpression lockExpr = null;
JExpression unlockExpr = null;
JBlock newBodyBlock = new JBlock(methodBody.getSourceInfo());
if (reuseSyncVariable && enclosingMethod.isStatic()) {
JLocal syncVar = lvCreator.createTempLocal(enclosingType, SourceInfo.UNKNOWN, tr);
JExpression syncVarValue = new JClassLiteral(SourceInfo.UNKNOWN, enclosingType);
JAsgOperation asg = new JAsgOperation(SourceInfo.UNKNOWN,
syncVar.makeRef(SourceInfo.UNKNOWN), syncVarValue);
newBodyBlock.addStmt(asg.makeStatement());
lockExpr = syncVar.makeRef(SourceInfo.UNKNOWN);
unlockExpr = syncVar.makeRef(SourceInfo.UNKNOWN);
} else {
if (enclosingMethod.isStatic()) {
lockExpr = new JClassLiteral(SourceInfo.UNKNOWN, enclosingType);
unlockExpr = new JClassLiteral(SourceInfo.UNKNOWN, enclosingType);
} else {
assert enclosingType instanceof JDefinedClass;
JVariable thisVar = enclosingMethod.getThis();
assert thisVar != null;
lockExpr = thisVar.makeRef(SourceInfo.UNKNOWN);
unlockExpr = thisVar.makeRef(SourceInfo.UNKNOWN);
}
}
newBodyBlock.addStmt(new JLock(SourceInfo.UNKNOWN, lockExpr));
newBodyBlock.addStmt(tryStmt);
JBlock finallyBlock = tryStmt.getFinallyBlock();
assert finallyBlock != null;
finallyBlock.addStmt(new JUnlock(SourceInfo.UNKNOWN, unlockExpr));
tr.append(new Replace(bodyBlock, newBodyBlock));
}
return super.visit(methodBody);
}
@Override
public boolean visit(@Nonnull JSynchronizedBlock syncBlock) {
SourceInfo srcInfo = syncBlock.getSourceInfo();
JBlock bodyBlock = syncBlock.getSynchronizedBlock();
JTryStatement tryStmt = getTryFinally(srcInfo, bodyBlock);
JExpression lockExpr = syncBlock.getLockExpr();
JType lockExprType = lockExpr.getType();
// $sync0 = lockExpr
JLocal syncVar = lvCreator.createTempLocal(lockExprType, srcInfo, tr);
JLocalRef asgLhs = syncVar.makeRef(srcInfo);
JAsgOperation asg = new JAsgOperation(srcInfo, asgLhs, lockExpr);
JBlock finallyBlock = tryStmt.getFinallyBlock();
assert finallyBlock != null;
finallyBlock.addStmt(new JUnlock(SourceInfo.UNKNOWN, syncVar.makeRef(srcInfo)));
tr.append(new AppendBefore(syncBlock, asg.makeStatement()));
tr.append(new AppendBefore(syncBlock, new JLock(srcInfo, syncVar.makeRef(srcInfo))));
tr.append(new Replace(syncBlock, tryStmt));
return super.visit(syncBlock);
}
@Nonnull
private JTryStatement getTryFinally(@Nonnull SourceInfo mthSrcInfo, @Nonnull JBlock bodyBlock) {
JBlock finallyBlock = new JBlock(mthSrcInfo);
JTryStatement tryStmt = new JTryStatement(mthSrcInfo,
Collections.<JStatement>emptyList(),
bodyBlock,
Collections.<JCatchBlock>emptyList(),
finallyBlock);
return tryStmt;
}
@Nonnull
private JClass getJLClass() {
return session.getPhantomLookup().getClass(CommonTypes.JAVA_LANG_CLASS);
}
}
@Override
public void run(@Nonnull JMethod method) {
if (method.isNative() || method.isAbstract() || !filter.accept(this.getClass(), method)) {
return;
}
TransformationRequest tr = new TransformationRequest(method);
LocalVarCreator lvCreator = new LocalVarCreator(method, "sync");
Visitor visitor = new Visitor(tr, lvCreator);
visitor.accept(method);
tr.commit();
}
}