blob: 69cefa235eabaf04a89624874535e1a464b6cae8 [file] [log] [blame]
/*
* Copyright (C) 2016 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.backend.dex.invokecustom;
import com.android.jack.Jack;
import com.android.jack.backend.dex.DexAnnotations;
import com.android.jack.backend.dex.rop.RopHelper;
import com.android.jack.dx.rop.cst.CstCallSiteRef;
import com.android.jack.dx.rop.cst.CstFieldRef;
import com.android.jack.dx.rop.cst.CstMethodHandleRef;
import com.android.jack.dx.rop.cst.CstMethodHandleRef.MethodHandleKind;
import com.android.jack.dx.rop.cst.CstMethodRef;
import com.android.jack.dx.rop.cst.CstNat;
import com.android.jack.dx.rop.cst.CstPrototypeRef;
import com.android.jack.dx.rop.cst.CstString;
import com.android.jack.dx.rop.type.Prototype;
import com.android.jack.ir.ast.JAbstractMethodCall;
import com.android.jack.ir.ast.JAnnotation;
import com.android.jack.ir.ast.JAnnotationMethod;
import com.android.jack.ir.ast.JArrayLiteral;
import com.android.jack.ir.ast.JClassLiteral;
import com.android.jack.ir.ast.JDefinedAnnotationType;
import com.android.jack.ir.ast.JEnumLiteral;
import com.android.jack.ir.ast.JLiteral;
import com.android.jack.ir.ast.JMethod;
import com.android.jack.ir.ast.JNameValuePair;
import com.android.jack.ir.ast.JStringLiteral;
import com.android.jack.ir.formatter.BinarySignatureFormatter;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
/**
* Helper to generate invoke custom based on annotations.
*/
public class InvokeCustomHelper {
public static boolean isInvokeCustom(@Nonnull JAbstractMethodCall call) {
Collection<JMethod> methods = call.getMethodId().getMethods();
Iterator<JMethod> methodsIt = methods.iterator();
boolean isInvokeCustom = false;
while (methodsIt.hasNext()) {
JMethod method = methodsIt.next();
if (getInvokeCustomCallsite(method) != null) {
isInvokeCustom = true;
break;
}
}
assert !isInvokeCustom || methods.size() == 1;
return isInvokeCustom;
}
@Nonnull
public static CstCallSiteRef readInvokeCustomCallSite(
@Nonnull JAnnotation invokeCustomCallSite) {
CstMethodHandleRef methodHandle = null;
JStringLiteral callSiteMethodName = null;
JClassLiteral callSiteReturnType = null;
JArrayLiteral callSiteArgumentTypes = null;
for (JNameValuePair nameValuePair : invokeCustomCallSite.getNameValuePairs()) {
if (nameValuePair.getName().equals("invokeMethodHandle")) {
JArrayLiteral arrayLiteral = (JArrayLiteral) nameValuePair.getValue();
assert arrayLiteral.getValues().size() == 1;
methodHandle = readLinkerMethodHandle((JAnnotation) arrayLiteral.getValues().get(0));
} else if (nameValuePair.getName().equals("fieldMethodHandle")) {
JArrayLiteral arrayLiteral = (JArrayLiteral) nameValuePair.getValue();
assert arrayLiteral.getValues().size() == 1;
methodHandle = readLinkerFieldHandle((JAnnotation) arrayLiteral.getValues().get(0));
} else if (nameValuePair.getName().equals("name")) {
callSiteMethodName = (JStringLiteral) (nameValuePair.getValue());
} else if (nameValuePair.getName().equals("returnType")) {
callSiteReturnType = (JClassLiteral) (nameValuePair.getValue());
} else if (nameValuePair.getName().equals("argumentTypes")) {
callSiteArgumentTypes = (JArrayLiteral) (nameValuePair.getValue());
}
}
assert methodHandle != null;
assert callSiteMethodName != null;
assert callSiteArgumentTypes != null;
assert callSiteReturnType != null;
Prototype callSitePrototype =
Prototype.intern(buildSignature(callSiteArgumentTypes, callSiteReturnType));
return new CstCallSiteRef(methodHandle, callSiteMethodName.getValue(),
new CstPrototypeRef(callSitePrototype));
}
@CheckForNull
public static JAnnotation getInvokeCustomCallsite(@Nonnull JMethod method) {
for (JAnnotation annotation : method.getAnnotations()) {
if (annotation.getType().getName().equals("CalledByInvokeCustom")) {
return annotation;
}
}
return null;
}
@Nonnull
private static CstMethodHandleRef readLinkerFieldHandle(@Nonnull JAnnotation linkerFieldHandle) {
JNameValuePair kindValuePair = linkerFieldHandle.getNameValuePair("kind");
assert kindValuePair != null;
JEnumLiteral enumKind = (JEnumLiteral) kindValuePair.getValue();
MethodHandleKind kind = (MethodHandleKind.valueOf(enumKind.getFieldId().getName()));
assert kind == MethodHandleKind.GET_INSTANCE || kind == MethodHandleKind.GET_STATIC
|| kind == MethodHandleKind.PUT_INSTANCE || kind == MethodHandleKind.PUT_STATIC;
JNameValuePair ownerValuePair = linkerFieldHandle.getNameValuePair("enclosingType");
assert ownerValuePair != null;
JClassLiteral owner = (JClassLiteral) ownerValuePair.getValue();
JNameValuePair nameValuePair = linkerFieldHandle.getNameValuePair("name");
assert nameValuePair != null;
JStringLiteral name = (JStringLiteral) nameValuePair.getValue();
JNameValuePair typeValuePair = linkerFieldHandle.getNameValuePair("type");
JClassLiteral type;
if (typeValuePair == null) {
type = (JClassLiteral) getDefaultValue(linkerFieldHandle, "type");
} else {
type = (JClassLiteral) typeValuePair.getValue();
}
assert type != null;
BinarySignatureFormatter bsf = BinarySignatureFormatter.getFormatter();
CstNat nat =
new CstNat(new CstString(name.getValue()), new CstString(bsf.getName(type.getRefType())));
CstFieldRef fieldRef = new CstFieldRef(RopHelper.getCstType(owner.getRefType()), nat);
return new CstMethodHandleRef(kind, fieldRef);
}
@Nonnull
private static CstMethodHandleRef readLinkerMethodHandle(
@Nonnull JAnnotation linkerMethodHandle) {
JNameValuePair kindValuePair = linkerMethodHandle.getNameValuePair("kind");
assert kindValuePair != null;
JEnumLiteral enumKind = (JEnumLiteral) kindValuePair.getValue();
MethodHandleKind kind = (MethodHandleKind.valueOf(enumKind.getFieldId().getName()));
assert kind == MethodHandleKind.INVOKE_CONSTRUCTOR || kind == MethodHandleKind.INVOKE_INSTANCE
|| kind == MethodHandleKind.INVOKE_STATIC;
JNameValuePair ownerValuePair = linkerMethodHandle.getNameValuePair("enclosingType");
assert ownerValuePair != null;
JClassLiteral owner = (JClassLiteral) ownerValuePair.getValue();
JNameValuePair nameValuePair = linkerMethodHandle.getNameValuePair("name");
assert nameValuePair != null;
JStringLiteral name = (JStringLiteral) nameValuePair.getValue();
JNameValuePair returnTypeValuePair = linkerMethodHandle.getNameValuePair("returnType");
JClassLiteral returnType;
if (returnTypeValuePair == null) {
returnType = (JClassLiteral) getDefaultValue(linkerMethodHandle, "returnType");
} else {
returnType = (JClassLiteral) returnTypeValuePair.getValue();
}
assert returnType != null;
JNameValuePair argumentTypesValuePair = linkerMethodHandle.getNameValuePair("argumentTypes");
JArrayLiteral argumentsTypes;
if (argumentTypesValuePair == null) {
argumentsTypes = (JArrayLiteral) getDefaultValue(linkerMethodHandle, "argumentTypes");
} else {
argumentsTypes = (JArrayLiteral) argumentTypesValuePair.getValue();
}
assert argumentsTypes != null;
CstNat nat = new CstNat(new CstString(name.getValue()),
new CstString(buildSignature(argumentsTypes, returnType)));
CstMethodRef methodRef = new CstMethodRef(RopHelper.getCstType(owner.getRefType()), nat);
return new CstMethodHandleRef(kind, methodRef);
}
@Nonnull
private static JLiteral getDefaultValue(@Nonnull JAnnotation annotation, @Nonnull String name) {
JLiteral returnValue = null;
JDefinedAnnotationType annotationType = (JDefinedAnnotationType) annotation.getType();
if (annotationType.isToEmit()) {
List<JAnnotation> annotations = annotationType.getAnnotations(Jack.getSession()
.getPhantomLookup().getAnnotationType(DexAnnotations.ANNOTATION_ANNOTATION_DEFAULT));
assert annotations.size() == 1;
JNameValuePair defaultAnnotationPair = annotations.get(0).getNameValuePair("value");
assert defaultAnnotationPair != null;
JNameValuePair nameValuePair =
((JAnnotation) defaultAnnotationPair.getValue()).getNameValuePair(name);
assert nameValuePair != null;
returnValue = nameValuePair.getValue();
} else {
for (JMethod annotationMethod : annotationType.getMethods()) {
if (annotationMethod.getName().equals(name)) {
returnValue = ((JAnnotationMethod) annotationMethod).getDefaultValue();
break;
}
}
}
assert returnValue != null;
return returnValue;
}
@Nonnull
private static String buildSignature(@Nonnull JArrayLiteral signature,
@Nonnull JClassLiteral returnType) {
BinarySignatureFormatter bsf = BinarySignatureFormatter.getFormatter();
StringBuilder signatureStr = new StringBuilder();
signatureStr.append('(');
for (JLiteral lit : signature.getValues()) {
assert lit instanceof JClassLiteral;
JClassLiteral classLit = (JClassLiteral) lit;
signatureStr.append(bsf.getName(classLit.getRefType()));
}
assert returnType != null;
signatureStr.append(')');
signatureStr.append(bsf.getName(returnType.getRefType()));
return signatureStr.toString();
}
}