package com.tns.bindings;
import java.util.*;
import org.ow2.asmdex.*;
import org.ow2.asmdex.structureCommon.*;
import com.tns.bindings.desc.ClassDescriptor;
import com.tns.bindings.desc.Descriptor;
import com.tns.bindings.desc.MethodDescriptor;
import com.tns.bindings.desc.reflection.ClassInfo;
import com.tns.bindings.desc.reflection.MethodInfo;
public class Dump
{
public static final char CLASS_NAME_LOCATION_SEPARATOR = '_';
private static final String callJsMethodSignatureCtor = "Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/String;Z[Ljava/lang/Object;";
private static final String callJsMethodSignatureMethod = "Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Class;[Ljava/lang/Object;";
private static final String LCOM_TNS = "Lcom/tns/gen/";
private static final String LCOM_TNS_RUNTIME = "Lcom/tns/Runtime;";
static final String objectClass = "Ljava/lang/Object;";
static final String runtimeClass = LCOM_TNS_RUNTIME;
static final String callJSMethodName = "callJSMethod";
static final String initInstanceMethodName = "initInstance";
static final StringBuffer methodDescriptorBuilder = new StringBuffer();
/**
* Returns the dex descriptor corresponding to the given method.
*
* @param m
* a {@link Method Method} object.
* @return the descriptor of the given method.
*/
public String getDexMethodDescriptor(final MethodDescriptor method)
{
ClassDescriptor[] parameters = method.getParameterTypes();
methodDescriptorBuilder.setLength(0);
getDexDescriptor(methodDescriptorBuilder, method.getReturnType());
for (int i = 0; i < parameters.length; ++i)
{
getDexDescriptor(methodDescriptorBuilder, parameters[i]);
}
return methodDescriptorBuilder.toString();
}
/**
* Returns the dex descriptor corresponding to the given method.
*
* @param m
* a {@link Method Method} object.
* @return the descriptor of the given method.
*/
public String getMethodOverloadDescriptor(final MethodDescriptor method)
{
ClassDescriptor[] parameters = method.getParameterTypes();
methodDescriptorBuilder.setLength(0);
for (int i = 0; i < parameters.length; ++i)
{
getDexDescriptor(methodDescriptorBuilder, parameters[i]);
}
return methodDescriptorBuilder.toString();
}
/**
* Returns the dex descriptor corresponding to the given method.
*
* @param m
* a {@link Method Method} object.
* @return the descriptor of the given method.
*/
public String getDexConstructorDescriptor(final MethodDescriptor constructor)
{
ClassDescriptor[] parameters = constructor.getParameterTypes();
methodDescriptorBuilder.setLength(0);
getDexDescriptor(methodDescriptorBuilder, ClassDescriptor.VOID);
for (int i = 0; i < parameters.length; ++i)
{
getDexDescriptor(methodDescriptorBuilder, parameters[i]);
}
return methodDescriptorBuilder.toString();
}
private void getDexDescriptor(final StringBuffer buf, final ClassDescriptor c)
{
ClassDescriptor d = c;
while (true)
{
if (d.isPrimitive())
{
String sig = d.getSignature();
buf.append(sig);
return;
}
else if (d.isArray())
{
buf.append('[');
d = d.getComponentType();
}
else
{
buf.append('L');
String name = d.getName().replace('.', '/');
buf.append(name);
buf.append(';');
return;
}
}
}
/**
* Returns the descriptor corresponding to the given Java type.
* From ASM sources org.objectweb.asm.Type
* @param c
* an object class, a primitive class or an array class.
* @return the descriptor corresponding to the given class.
*/
public static String getAsmDescriptor(final ClassDescriptor c) {
StringBuffer buf = new StringBuffer();
getAsmDescriptor(buf, c);
return buf.toString();
}
/**
* Appends the descriptor of the given class to the given string buffer.
* From ASM sources org.objectweb.asm.Type
* @param buf
* the string buffer to which the descriptor must be appended.
* @param c
* the class whose descriptor must be computed.
*/
private static void getAsmDescriptor(final StringBuffer buf, final ClassDescriptor c)
{
ClassDescriptor d = c;
while (true)
{
if (d.isPrimitive())
{
String sig = d.getSignature();
buf.append(sig);
return;
}
else if (d.isArray())
{
buf.append('[');
d = d.getComponentType();
}
else
{
buf.append('L');
String name = d.getName().replace('.', '/');
buf.append(name);
buf.append(';');
return;
}
}
}
/**
* Returns the Class signature of the Type normalized if it is a primitive
*/
private static String getClassSignatureOfType(final ClassDescriptor c)
{
String name;
final StringBuffer buf = new StringBuffer();
ClassDescriptor result;
if (c.isPrimitive())
{
name = ClassDescriptor.PrimitiveTypeInfo.getBoxedTypeName(c);
}
else if (c.isArray())
{
name = "java.lang.Object";
}
else
{
name = c.getName();
}
buf.append('L');
buf.append(name.replace('.', '/'));
buf.append(';');
return buf.toString();
}
public void generateProxy(ApplicationWriter aw, String proxyName, ClassDescriptor classTo, HashSet<String> methodOverrides, HashSet<ClassDescriptor> implementedInterfaces, AnnotationDescriptor[] annotations)
{
String classSignature = getAsmDescriptor(classTo);
String tnsClassSignature;
if (proxyName.contains(".")) {
tnsClassSignature = "L" + proxyName.replace('.', '/') + ";";
} else {
tnsClassSignature = LCOM_TNS +
classSignature.substring(1, classSignature.length() - 1).replace("$", "_");
if (!classTo.isInterface()) {
tnsClassSignature += CLASS_NAME_LOCATION_SEPARATOR + proxyName;
}
tnsClassSignature += ";";
}
ClassVisitor cv = generateClass(aw, classTo, classSignature, tnsClassSignature, implementedInterfaces, annotations);
MethodDescriptor[] methods = getSupportedMethods(classTo, methodOverrides, implementedInterfaces);
methods = groupMethodsByNameAndSignature(methods);
generateFields(cv);
MethodDescriptor[] ctors = classTo.getConstructors();
boolean hasOverridenCtor = ((methodOverrides != null) && methodOverrides.contains("init"));
generateCtors(cv, classTo, ctors, classSignature, tnsClassSignature, hasOverridenCtor);
generateMethods(cv, classTo, methods, classSignature, tnsClassSignature);
cv.visitEnd();
}
private MethodDescriptor[] groupMethodsByNameAndSignature(MethodDescriptor[] methods)
{
HashMap<String, MethodDescriptor> result = new HashMap<String, MethodDescriptor>();
for (int i = 0; i < methods.length; i++)
{
MethodDescriptor method = methods[i];
String methodName = method.getName();
String methodOverLoadDescriptor = getMethodOverloadDescriptor(method);
methodName += "_" + methodOverLoadDescriptor;
if (!result.containsKey(methodName))
{
result.put(methodName, method);
}
}
return result.values().toArray(new MethodDescriptor[result.size()]);
}
private String getMethodSignature(MethodDescriptor m)
{
String sig = m.toGenericString();
int nameIdx = sig.indexOf("(");
int endSigIdx = sig.indexOf(")") + 1;
return m.getName() + sig.substring(nameIdx, endSigIdx);
}
private void collectAbstractMethods(final ClassDescriptor clazz, List<MethodDescriptor> result) {
if (!clazz.isAbstract()) {
return;
}
Set<String> alreadyAddedMethods = new HashSet<String>();
for (MethodDescriptor md: result) {
String sig = getMethodSignature(md);
alreadyAddedMethods.add(sig);
}
Set<String> concreteMethods = new HashSet<String>();
// TODO refactor this
boolean isInterfaceClass = clazz.isInterface();
ClassDescriptor startingConcreteClassDesc = isInterfaceClass ? new ClassInfo(Object.class) : clazz;
for (MethodDescriptor objMethod: startingConcreteClassDesc.getDeclaredMethods()) {
if (!objMethod.isStatic()) {
String sig = getMethodSignature(objMethod);
concreteMethods.add(sig);
}
}
Deque<ClassDescriptor> typesToProcess = new ArrayDeque<ClassDescriptor>();
typesToProcess.add(startingConcreteClassDesc);
if (clazz.isInterface()) {
typesToProcess.add(clazz);
}
while (!typesToProcess.isEmpty()) {
ClassDescriptor currentType = typesToProcess.pollFirst();
MethodDescriptor[] methods = currentType.getDeclaredMethods();
for (MethodDescriptor m: methods) {
if (m.isStatic()) {
continue;
}
String sig = getMethodSignature(m);
if (m.isAbstract()) {
if (!concreteMethods.contains(sig) && !alreadyAddedMethods.contains(sig)) {
if(isInterfaceClass) {
m.setAsInterfaceMethod();
}
result.add(m);
alreadyAddedMethods.add(sig);
}
} else if (!concreteMethods.contains(sig)) {
concreteMethods.add(sig);
}
}
if (!currentType.isInterface() && !currentType.getName().equals("java.lang.Object")) {
typesToProcess.addFirst(currentType.getSuperclass());
}
for (ClassDescriptor iface: currentType.getInterfaces()) {
typesToProcess.add(iface);
}
}
}
private MethodDescriptor[] getSupportedMethods(ClassDescriptor clazz, HashSet<String> methodOverrides, HashSet<ClassDescriptor> interfacesToImplement)
{
ArrayList<MethodDescriptor> result = new ArrayList<MethodDescriptor>();
collectAbstractMethods(clazz, result);
for (ClassDescriptor iface : interfacesToImplement) {
collectAbstractMethods(iface, result);
}
boolean isApplicationClass = isApplicationClass(clazz);
if (!clazz.isInterface())
{
HashMap<String, MethodDescriptor> finalMethods = new HashMap<String, MethodDescriptor>();
while (clazz != null)
{
MethodDescriptor[] methods = clazz.getDeclaredMethods();
ArrayList<MethodDescriptor> methodz = new ArrayList<MethodDescriptor>();
for (int i = 0; i < methods.length; i++)
{
MethodDescriptor candidateMethod = methods[i];
if (isApplicationClass && candidateMethod.getName().equals("attachBaseContext")) {
// this is the only Application method called before onCreate (where we initialize the runtime, so we skip this method)
continue;
}
if (methodOverrides != null && !methodOverrides.contains(candidateMethod.getName()))
{
continue;
}
methodz.add(candidateMethod);
}
for (int i = 0; i < methodz.size(); i++)
{
MethodDescriptor method = methodz.get(i);
if (isMethodSupported(method, finalMethods))
{
result.add(method);
}
}
clazz = clazz.getSuperclass();
}
}
return result.toArray(new MethodDescriptor[result.size()]);
//For debugging
//return new Method[] { result.get(0) };
}
private static boolean isMethodSupported(MethodDescriptor method, HashMap<String, MethodDescriptor> finalMethods)
{
if (method.isSynthetic() || method.isStatic() || method.isPrivate())
{
return false;
}
if (method.isFinal())
{
finalMethods.put(method.getName(), method);
return false;
}
boolean isPackagePrivate = !method.isPrivate() && !method.isPublic() && !method.isProtected();
if (isPackagePrivate)
{
return false;
}
if (isMethodMarkedAsFinalInClassHierarchy(method, finalMethods))
{
return false;
}
// if (finalMethods.size() != 0)
// {
// Method finalMethod = finalMethods.get(method.getName());
// if (finalMethod != null)
// {
// boolean isSameFinalMethod = areMethodSignaturesEqual(finalMethod, method);
// if (isSameFinalMethod)
// {
// return false;
// }
// }
// }
return true;
}
private static boolean isMethodMarkedAsFinalInClassHierarchy(MethodDescriptor method, HashMap<String, MethodDescriptor> finalMethods)
{
if (finalMethods.size() != 0)
{
MethodDescriptor finalMethod = finalMethods.get(method.getName());
if (finalMethod != null)
{
boolean isSameFinalMethod = areMethodSignaturesEqual(finalMethod, method);
if (isSameFinalMethod)
{
return true;
}
}
}
return false;
}
private static boolean areMethodSignaturesEqual(MethodDescriptor x, MethodDescriptor y)
{
if (x.equals(y))
return true;
if (!x.getName().equals(y.getName()))
return false;
ClassDescriptor[] xParams = x.getParameterTypes();
ClassDescriptor[] yParams = y.getParameterTypes();
if (xParams.length != yParams.length)
return false;
boolean result = true;
for (int i = 0; i < xParams.length; i++)
{
if (!xParams[i].equals(yParams[i]))
{
result = false;
break;
}
}
return result;
}
private void generateCtors(ClassVisitor cv, ClassDescriptor classTo, MethodDescriptor[] ctors, String classSignature, String tnsClassSignature, boolean hasOverridenCtor)
{
if (classTo.isInterface())
{
try
{
// TODO refactor this
MethodDescriptor defaultCtor = new MethodInfo(Object.class.getConstructor());
generateCtor(cv, classTo, defaultCtor, classSignature, tnsClassSignature, false);
}
catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
else
{
for (MethodDescriptor ctor : ctors)
{
generateCtor(cv, classTo, ctor, classSignature, tnsClassSignature, hasOverridenCtor);
}
}
}
private void generateCtor(ClassVisitor cv, ClassDescriptor classTo, MethodDescriptor ctor, String classSignature, String tnsClassSignature, boolean hasOverridenCtor)
{
//TODO: handle generic and vararg ctors if needed
String ctorSignature = getDexConstructorDescriptor(ctor);
//org.objectweb.asm.Type.getConstructorDescriptor(ctor);
MethodVisitor mv;
int ctorModifiers = getDexModifiers(ctor);
//int locaVarsCount = 2;
//int thisRegister = locaVarsCount + 1;
mv = cv.visitMethod(ctorModifiers + org.ow2.asmdex.Opcodes.ACC_CONSTRUCTOR, "<init>", ctorSignature, null, null);
mv.visitCode();
//mv.visitMaxs(4, 0); //TODO: max stack size should be equal to localVarCount + 1
int thisRegister = generateMaxStackSize(mv, ctor);
int argCount = ctor.getParameterTypes().length;
int[] args = generateArgsArray(thisRegister, argCount, ctor);
if (!classTo.isInterface())
{
mv.visitMethodInsn(org.ow2.asmdex.Opcodes.INSN_INVOKE_DIRECT_RANGE, classSignature, "<init>", ctorSignature, args);
}
else
{
mv.visitMethodInsn(org.ow2.asmdex.Opcodes.INSN_INVOKE_DIRECT_RANGE, objectClass, "<init>", ctorSignature, args);
}
if (!isApplicationClass(classTo)) {
generateInitializedBlock(mv, thisRegister, classSignature, tnsClassSignature);
}
if (hasOverridenCtor) {
generateCtorOverridenBlock(mv, thisRegister, ctor, classSignature, tnsClassSignature);
}
generateReturnVoid(mv);
}
private void generateReturnVoid(MethodVisitor mv)
{
mv.visitInsn(org.ow2.asmdex.Opcodes.INSN_RETURN_VOID);
mv.visitEnd();
}
private void generateCtorOverridenBlock(MethodVisitor mv, int thisRegister, MethodDescriptor ctor, String classSignature, String tnsClassSignature)
{
int argCount = generateArrayForCallJsArguments(mv, ctor.getParameterTypes(), thisRegister, classSignature, tnsClassSignature);
mv.visitStringInsn(org.ow2.asmdex.Opcodes.INSN_CONST_STRING, 1, "init"); //put "init" in register 1
mv.visitVarInsn(org.ow2.asmdex.Opcodes.INSN_CONST_4, 2, 1); //put true to register 2 == isConstructor argument
mv.visitMethodInsn(org.ow2.asmdex.Opcodes.INSN_INVOKE_STATIC, LCOM_TNS_RUNTIME, "callJSMethod", callJsMethodSignatureCtor, new int[]
{ 3, 1, 2, 0 }); //invoke callJSMethod(this, "init", true, params)
}
private void generateInitializedBlock(MethodVisitor mv, int thisRegister, String classSignature, String tnsClassSignature)
{
mv.visitFieldInsn(org.ow2.asmdex.Opcodes.INSN_IGET_BOOLEAN, tnsClassSignature, "__initialized", "Z", thisRegister - 2, thisRegister); //put __initialized in local var 1
Label label = new Label();
mv.visitJumpInsn(org.ow2.asmdex.Opcodes.INSN_IF_NEZ, label, thisRegister - 2, 0); //compare local var 1 with false
mv.visitVarInsn(org.ow2.asmdex.Opcodes.INSN_CONST_4, thisRegister - 1, 1); //put true in local var 1
mv.visitFieldInsn(org.ow2.asmdex.Opcodes.INSN_IPUT_BOOLEAN, tnsClassSignature, "__initialized", "Z", thisRegister - 1 , thisRegister); //set field to the value of 2
mv.visitMethodInsn(org.ow2.asmdex.Opcodes.INSN_INVOKE_STATIC, LCOM_TNS_RUNTIME, "initInstance", "VLjava/lang/Object;", new int[] { thisRegister }); //call init instance passing this as arugment
mv.visitLabel(label);
}
private void generateMethods(ClassVisitor cv, ClassDescriptor classTo, MethodDescriptor[] methods, String classSignature, String tnsClassSignature)
{
//for (Method method : methods)
for (int i = 0; i < methods.length; i++)
{
MethodDescriptor sourceMethod = methods[i];
generateMethod(cv, classTo, sourceMethod, i, classSignature, tnsClassSignature);
}
generateEqualsSuper(cv);
generateHashCodeSuper(cv);
}
private void generateEqualsSuper(ClassVisitor cv)
{
MethodVisitor mv = cv.visitMethod(org.ow2.asmdex.Opcodes.ACC_PUBLIC, "equals__super", "ZLjava/lang/Object;", null, null);
mv.visitCode();
mv.visitMaxs(3, 0);
mv.visitMethodInsn(org.ow2.asmdex.Opcodes.INSN_INVOKE_SUPER, "Ljava/lang/Object;", "equals", "ZLjava/lang/Object;", new int[] { 1, 2 });
mv.visitIntInsn(org.ow2.asmdex.Opcodes.INSN_MOVE_RESULT, 0);
mv.visitIntInsn(org.ow2.asmdex.Opcodes.INSN_RETURN, 0);
mv.visitEnd();
}
private void generateHashCodeSuper(ClassVisitor cv)
{
MethodVisitor mv = cv.visitMethod(org.ow2.asmdex.Opcodes.ACC_PUBLIC, "hashCode__super", "I", null, null);
mv.visitCode();
mv.visitMaxs(2, 0);
mv.visitMethodInsn(org.ow2.asmdex.Opcodes.INSN_INVOKE_SUPER, "Ljava/lang/Object;", "hashCode", "I", new int[] { 1 });
mv.visitIntInsn(org.ow2.asmdex.Opcodes.INSN_MOVE_RESULT, 0);
mv.visitIntInsn(org.ow2.asmdex.Opcodes.INSN_RETURN, 0);
mv.visitEnd();
}
private void generateMethod(ClassVisitor cv, ClassDescriptor classTo, MethodDescriptor method, int methodNumber, String classSignature, String tnsClassSignature)
{
if (ProxyGenerator.IsLogEnabled) {
System.out.println("Generator: generatingMethod " + method.getName());
}
//TODO: handle checked exceptions
String methodDexSignature = getDexMethodDescriptor(method);
String[] exceptions = new String[0];
MethodVisitor mv;
int methodModifiers = getDexModifiers(method);
mv = cv.visitMethod(methodModifiers, method.getName(), methodDexSignature, null, exceptions);
mv.visitCode();
int thisRegister = generateMaxStackSize(mv, method);
if (!classTo.isInterface()) {
if (isApplicationClass(classTo) && method.getName().equals("onCreate")) {
generateRuntimeInitializedBlock(mv, method, thisRegister, classSignature, tnsClassSignature, classTo.getName());
} else {
if(!method.isInterfaceMethod()) { //interface methods do not need an initialized block
generateInitializedBlock(mv, thisRegister, classSignature, tnsClassSignature);
}
}
}
if (!isApplicationClass(classTo) || !method.getName().equals("onCreate")) {
generateCallOverrideBlock(mv, method, thisRegister, classSignature, tnsClassSignature);
}
generateReturnFromObject(mv, method.getReturnType(), thisRegister, 1);
mv.visitEnd();
}
private boolean isApplicationClass(ClassDescriptor clazz) {
boolean isApplicationClass = false;
//TODO: plamen5kov: improve check for application class include MultidexApplication and other common scenarios
String applicationClassName = "android.app.Application";
ClassDescriptor currentClass = clazz;
while ((currentClass != null) && !isApplicationClass) {
isApplicationClass = currentClass.getName().equals(applicationClassName);
if (!isApplicationClass) {
currentClass = currentClass.getSuperclass();
}
}
return isApplicationClass;
}
private void generateRuntimeInitializedBlock(MethodVisitor mv, MethodDescriptor method, int thisRegister, String classSignature, String tnsClassSignature, String superClassname) {
String name = "L" + superClassname.replace('.', '/') + ";";
mv.visitMethodInsn(Opcodes.INSN_INVOKE_SUPER, name, "onCreate", "V", new int[] { thisRegister });
mv.visitMethodInsn(Opcodes.INSN_INVOKE_STATIC, "Lcom/tns/RuntimeHelper;", "initRuntime", "Lcom/tns/Runtime;Landroid/app/Application;", new int[] { thisRegister });
mv.visitIntInsn(org.ow2.asmdex.Opcodes.INSN_MOVE_RESULT_OBJECT, 0);
Label label = new Label();
mv.visitJumpInsn(Opcodes.INSN_IF_EQZ, label, 0, 0);
mv.visitMethodInsn(Opcodes.INSN_INVOKE_VIRTUAL, LCOM_TNS_RUNTIME, "run", "V", new int[] { 0 });
generateCallOverrideBlock(mv, method, thisRegister, classSignature, tnsClassSignature);
mv.visitLabel(label);
}
private int generateMaxStackSize(MethodVisitor mv, MethodDescriptor method)
{
//3 local vars are enough for NativeScript bindings methods. Local vars start from 0 register till register 2.
//Then 'this' is register 3 and then all parameters according to their size
int registersCount = 3/*local vars*/ + 1 /*this*/;
int thisRegister = registersCount - 1;
ClassDescriptor[] paramTypes = method.getParameterTypes();
int paramCount = paramTypes.length;
for (int i = 0; i < paramCount; i++)
{
ClassDescriptor paramType = paramTypes[i];
if (paramType.equals(ClassDescriptor.LONG) || paramType.equals(ClassDescriptor.DOUBLE))
{
registersCount += 2;
}
else
{
registersCount += 1;
}
}
mv.visitMaxs(registersCount, 0);
return thisRegister;
}
private void generateCallOverrideBlock(MethodVisitor mv, MethodDescriptor method, int thisRegister, String classSignature, String tnsClassSignature)
{
//call the override
int argCount = generateArrayForCallJsArguments(mv, method.getParameterTypes() , thisRegister, classSignature, tnsClassSignature);
mv.visitStringInsn(org.ow2.asmdex.Opcodes.INSN_CONST_STRING, 1, method.getName());
ClassDescriptor returnType = method.getReturnType();
if (returnType.isPrimitive())
{
//mv.visitFieldInsn(INSN_SGET_OBJECT, "Ljava/lang/Long;", "TYPE", "Ljava/lang/Class;", 2, 0);
mv.visitFieldInsn(org.ow2.asmdex.Opcodes.INSN_SGET_OBJECT, getClassSignatureOfType(returnType), "TYPE", "Ljava/lang/Class;", 2, 0);
}
else
{
mv.visitTypeInsn(org.ow2.asmdex.Opcodes.INSN_CONST_CLASS, 2, 0, 0, getClassSignatureOfType(returnType));
}
mv.visitMethodInsn(org.ow2.asmdex.Opcodes.INSN_INVOKE_STATIC, runtimeClass, callJSMethodName, callJsMethodSignatureMethod, new int[] { thisRegister, 1, 2, 0 });
//Label returnLabel = new Label();
//mv.visitLabel(returnLabel);
}
private int[] generateArgsArray(int thisRegister, int argCount, MethodDescriptor ctor)
{
ClassDescriptor[] paramTypes = ctor.getParameterTypes();
int argumentsCount = paramTypes.length;
int[] argsForSuper = new int[1 + argumentsCount*2]; //thisRegister + argCount * 2 since it long and double take 2 registers
int argsForSuperIndex = 0;
argsForSuper[argsForSuperIndex] = thisRegister;
argsForSuperIndex++;
int arrayIndex = 0;
while (arrayIndex < argumentsCount)
{
ClassDescriptor paramType = paramTypes[arrayIndex];
if (paramType.equals(ClassDescriptor.LONG) || paramType.equals(ClassDescriptor.DOUBLE))
{
argsForSuper[argsForSuperIndex] = thisRegister + arrayIndex + 1;
argsForSuperIndex++;
argsForSuper[argsForSuperIndex] = thisRegister + arrayIndex + 2;
argsForSuperIndex++;
}
else
{
argsForSuper[argsForSuperIndex] = thisRegister + arrayIndex + 1;
argsForSuperIndex++;
}
arrayIndex++;
}
return Arrays.copyOf(argsForSuper, argsForSuperIndex);
}
/**
* Creates new Object[] or null value (when no arguments) and puts it in register 0
*/
private int generateArrayForCallJsArguments(MethodVisitor mv, ClassDescriptor[] paramTypes, int thisRegister, String classSignature, String tnsClassSignature)
{
int argumentsCount = paramTypes.length;
if (argumentsCount == 0)
{
mv.visitVarInsn(org.ow2.asmdex.Opcodes.INSN_CONST_4, 0, 0); //set null at register 0. our params array is null
return 0;
}
mv.visitVarInsn(org.ow2.asmdex.Opcodes.INSN_CONST_16, 2, argumentsCount); //put array size in register 2
mv.visitTypeInsn(org.ow2.asmdex.Opcodes.INSN_NEW_ARRAY, 0, 0, 2, "[Ljava/lang/Object;"); //create array with size in register 2 and put it in register 0
int arrayIndex = 0;
int argNumber = 4;
int numberOfDoubleRegisterArguments = 0;
while (argNumber < 4 + argumentsCount + numberOfDoubleRegisterArguments)
{
mv.visitVarInsn(org.ow2.asmdex.Opcodes.INSN_CONST_16, 1, arrayIndex); //put the array access index value in register 1
ClassDescriptor paramType = paramTypes[arrayIndex];
if (paramType.isPrimitive())
{
if (paramType.equals(ClassDescriptor.INT))
{
//box the primitive value at register i
mv.visitMethodInsn(org.ow2.asmdex.Opcodes.INSN_INVOKE_STATIC_RANGE, "Ljava/lang/Integer;", "valueOf", "Ljava/lang/Integer;I", new int[] { argNumber });
//move the result object in register 2
mv.visitIntInsn(org.ow2.asmdex.Opcodes.INSN_MOVE_RESULT_OBJECT, 2);
//put the object at register 2 in array at index register 1
mv.visitArrayOperationInsn(org.ow2.asmdex.Opcodes.INSN_APUT_OBJECT, 2, 0, 1);
}
else if (paramType.equals(ClassDescriptor.CHAR))
{
mv.visitMethodInsn(org.ow2.asmdex.Opcodes.INSN_INVOKE_STATIC_RANGE, "Ljava/lang/Character;", "valueOf", "Ljava/lang/Character;C", new int[] { argNumber });
mv.visitIntInsn(org.ow2.asmdex.Opcodes.INSN_MOVE_RESULT_OBJECT, 2);
mv.visitArrayOperationInsn(org.ow2.asmdex.Opcodes.INSN_APUT_OBJECT, 2, 0, 1);
}
else if (paramType.equals(ClassDescriptor.BYTE))
{
mv.visitMethodInsn(org.ow2.asmdex.Opcodes.INSN_INVOKE_STATIC_RANGE, "Ljava/lang/Byte;", "valueOf", "Ljava/lang/Byte;B", new int[] { argNumber });
mv.visitIntInsn(org.ow2.asmdex.Opcodes.INSN_MOVE_RESULT_OBJECT, 2);
mv.visitArrayOperationInsn(org.ow2.asmdex.Opcodes.INSN_APUT_OBJECT, 2, 0, 1);
}
else if (paramType.equals(ClassDescriptor.SHORT))
{
mv.visitMethodInsn(org.ow2.asmdex.Opcodes.INSN_INVOKE_STATIC_RANGE, "Ljava/lang/Short;", "valueOf", "Ljava/lang/Short;S", new int[] { argNumber });
mv.visitIntInsn(org.ow2.asmdex.Opcodes.INSN_MOVE_RESULT_OBJECT, 2);
mv.visitArrayOperationInsn(org.ow2.asmdex.Opcodes.INSN_APUT_OBJECT, 2, 0, 1);
}
else if (paramType.equals(ClassDescriptor.BOOLEAN))
{
mv.visitMethodInsn(org.ow2.asmdex.Opcodes.INSN_INVOKE_STATIC_RANGE, "Ljava/lang/Boolean;", "valueOf", "Ljava/lang/Boolean;Z", new int[] { argNumber });
mv.visitIntInsn(org.ow2.asmdex.Opcodes.INSN_MOVE_RESULT_OBJECT, 2);
mv.visitArrayOperationInsn(org.ow2.asmdex.Opcodes.INSN_APUT_OBJECT, 2, 0, 1);
}
else if (paramType.equals(ClassDescriptor.LONG))
{
mv.visitMethodInsn(org.ow2.asmdex.Opcodes.INSN_INVOKE_STATIC_RANGE, "Ljava/lang/Long;", "valueOf", "Ljava/lang/Long;J", new int[] { argNumber, argNumber + 1 });
mv.visitIntInsn(org.ow2.asmdex.Opcodes.INSN_MOVE_RESULT_OBJECT, 2);
mv.visitArrayOperationInsn(org.ow2.asmdex.Opcodes.INSN_APUT_OBJECT, 2, 0, 1);
argNumber++;
numberOfDoubleRegisterArguments++;
}
else if (paramType.equals(ClassDescriptor.FLOAT))
{
mv.visitMethodInsn(org.ow2.asmdex.Opcodes.INSN_INVOKE_STATIC_RANGE, "Ljava/lang/Float;", "valueOf", "Ljava/lang/Float;F", new int[] { argNumber });
mv.visitIntInsn(org.ow2.asmdex.Opcodes.INSN_MOVE_RESULT_OBJECT, 2);
mv.visitArrayOperationInsn(org.ow2.asmdex.Opcodes.INSN_APUT_OBJECT, 2, 0, 1);
}
else if (paramType.equals(ClassDescriptor.DOUBLE))
{
mv.visitMethodInsn(org.ow2.asmdex.Opcodes.INSN_INVOKE_STATIC_RANGE, "Ljava/lang/Double;", "valueOf", "Ljava/lang/Double;D", new int[] { argNumber, argNumber + 1 });
mv.visitIntInsn(org.ow2.asmdex.Opcodes.INSN_MOVE_RESULT_OBJECT, 2);
mv.visitArrayOperationInsn(org.ow2.asmdex.Opcodes.INSN_APUT_OBJECT, 2, 0, 1);
argNumber++;
numberOfDoubleRegisterArguments++;
}
}
else
{
//register 1 contains the value of the arrayIndex
//i is the register containing the value of the object reference
//0 register contains the reference to the array
mv.visitArrayOperationInsn(org.ow2.asmdex.Opcodes.INSN_APUT_OBJECT, argNumber, 0, 1);
}
// int arrayPutOpCode = getArrayPutInstructionByType(paramTypes[arrayIndex]);
//mv.visitArrayOperationInsn(org.ow2.asmdex.Opcodes.INSN_APUT_OBJECT, i, 0, 1); //put params[i] in the array at index register 1
//mv.visitArrayOperationInsn(org.ow2.asmdex.Opcodes.INSN_APUT_
argNumber++;
arrayIndex++;
}
return argumentsCount;
}
private static final String integerTypeDescriptor = "Ljava/lang/Integer;";
private static final String booleanTypeDescriptor = "Ljava/lang/Boolean;";
private static final String byteTypeDescriptor = "Ljava/lang/Byte;";
private static final String characterTypeDescriptor = "Ljava/lang/Character;";
private static final String shortTypeDescriptor = "Ljava/lang/Short;";
private static final String doubleTypeDescriptor = "Ljava/lang/Double;";
private static final String floatTypeDescriptor = "Ljava/lang/Float;";
private static final String longTypeDescriptor = "Ljava/lang/Long;";
private void generateReturnFromObject(MethodVisitor mv, ClassDescriptor targetReturnType, int thisRegister, int valueRegister)
{
if (targetReturnType.equals(ClassDescriptor.VOID))
{
mv.visitInsn(org.ow2.asmdex.Opcodes.INSN_RETURN_VOID);
return;
}
mv.visitIntInsn(org.ow2.asmdex.Opcodes.INSN_MOVE_RESULT_OBJECT, valueRegister); //get the result of from the very last method call in register 1;
if (targetReturnType.isPrimitive())
{
if (targetReturnType.equals(ClassDescriptor.BOOLEAN))
{
mv.visitTypeInsn(org.ow2.asmdex.Opcodes.INSN_CHECK_CAST, 0, valueRegister, 0, booleanTypeDescriptor); //throw exception if can't cast this reference to the return type
mv.visitMethodInsn(org.ow2.asmdex.Opcodes.INSN_INVOKE_VIRTUAL, booleanTypeDescriptor, "booleanValue", "Z", new int[] { valueRegister });
mv.visitIntInsn(org.ow2.asmdex.Opcodes.INSN_MOVE_RESULT, valueRegister);
mv.visitIntInsn(org.ow2.asmdex.Opcodes.INSN_RETURN, valueRegister);
}
else if (targetReturnType.equals(ClassDescriptor.CHAR))
{
mv.visitTypeInsn(org.ow2.asmdex.Opcodes.INSN_CHECK_CAST, 0, valueRegister, 0, characterTypeDescriptor); //throw exception if can't cast this reference to the return type
mv.visitMethodInsn(org.ow2.asmdex.Opcodes.INSN_INVOKE_VIRTUAL, characterTypeDescriptor, "charValue", "C", new int[] { valueRegister });
mv.visitIntInsn(org.ow2.asmdex.Opcodes.INSN_MOVE_RESULT, valueRegister);
mv.visitIntInsn(org.ow2.asmdex.Opcodes.INSN_RETURN, valueRegister);
}
else if (targetReturnType.equals(ClassDescriptor.BYTE))
{
mv.visitTypeInsn(org.ow2.asmdex.Opcodes.INSN_CHECK_CAST, 0, valueRegister, 0, byteTypeDescriptor); //throw exception if can't cast this reference to the return type
mv.visitMethodInsn(org.ow2.asmdex.Opcodes.INSN_INVOKE_VIRTUAL, byteTypeDescriptor, "byteValue", "B", new int[] { valueRegister });
mv.visitIntInsn(org.ow2.asmdex.Opcodes.INSN_MOVE_RESULT, valueRegister);
mv.visitIntInsn(org.ow2.asmdex.Opcodes.INSN_RETURN, valueRegister);
}
else if (targetReturnType.equals(ClassDescriptor.SHORT))
{
mv.visitTypeInsn(org.ow2.asmdex.Opcodes.INSN_CHECK_CAST, 0, valueRegister, 0, shortTypeDescriptor); //throw exception if can't cast this reference to the return type
mv.visitMethodInsn(org.ow2.asmdex.Opcodes.INSN_INVOKE_VIRTUAL, shortTypeDescriptor, "shortValue", "S", new int[] { valueRegister });
mv.visitIntInsn(org.ow2.asmdex.Opcodes.INSN_MOVE_RESULT, valueRegister);
mv.visitIntInsn(org.ow2.asmdex.Opcodes.INSN_RETURN, valueRegister);
}
else if (targetReturnType.equals(ClassDescriptor.INT))
{
mv.visitTypeInsn(org.ow2.asmdex.Opcodes.INSN_CHECK_CAST, 0, valueRegister, 0, integerTypeDescriptor); //throw exception if can't cast this reference to the return type
mv.visitMethodInsn(org.ow2.asmdex.Opcodes.INSN_INVOKE_VIRTUAL, integerTypeDescriptor, "intValue", "I", new int[] { valueRegister });
mv.visitIntInsn(org.ow2.asmdex.Opcodes.INSN_MOVE_RESULT, valueRegister);
mv.visitIntInsn(org.ow2.asmdex.Opcodes.INSN_RETURN, valueRegister);
}
else if (targetReturnType.equals(ClassDescriptor.LONG))
{
mv.visitTypeInsn(org.ow2.asmdex.Opcodes.INSN_CHECK_CAST, 0, valueRegister, 0, longTypeDescriptor); //throw exception if can't cast this reference to the return type
mv.visitMethodInsn(org.ow2.asmdex.Opcodes.INSN_INVOKE_VIRTUAL, longTypeDescriptor, "longValue", "J", new int[] { valueRegister });
mv.visitIntInsn(org.ow2.asmdex.Opcodes.INSN_MOVE_RESULT_WIDE, valueRegister);
mv.visitIntInsn(org.ow2.asmdex.Opcodes.INSN_RETURN_WIDE, valueRegister);
}
else if (targetReturnType.equals(ClassDescriptor.FLOAT))
{
mv.visitTypeInsn(org.ow2.asmdex.Opcodes.INSN_CHECK_CAST, 0, valueRegister, 0, floatTypeDescriptor); //throw exception if can't cast this reference to the return type
mv.visitMethodInsn(org.ow2.asmdex.Opcodes.INSN_INVOKE_VIRTUAL, floatTypeDescriptor, "floatValue", "F", new int[] { valueRegister });
mv.visitIntInsn(org.ow2.asmdex.Opcodes.INSN_MOVE_RESULT, valueRegister);
mv.visitIntInsn(org.ow2.asmdex.Opcodes.INSN_RETURN, valueRegister);
}
else if (targetReturnType.equals(ClassDescriptor.DOUBLE))
{
mv.visitTypeInsn(org.ow2.asmdex.Opcodes.INSN_CHECK_CAST, 0, valueRegister, 0, doubleTypeDescriptor); //throw exception if can't cast this reference to the return type
mv.visitMethodInsn(org.ow2.asmdex.Opcodes.INSN_INVOKE_VIRTUAL, doubleTypeDescriptor, "doubleValue", "D", new int[] { valueRegister });
mv.visitIntInsn(org.ow2.asmdex.Opcodes.INSN_MOVE_RESULT_WIDE, valueRegister);
mv.visitIntInsn(org.ow2.asmdex.Opcodes.INSN_RETURN_WIDE, valueRegister);
}
}
else //return object
{
String returnTypeDescriptor = getAsmDescriptor(targetReturnType);
mv.visitTypeInsn(org.ow2.asmdex.Opcodes.INSN_CHECK_CAST, 0, valueRegister, 0, returnTypeDescriptor); //throw exception if can't cast this reference to the return type
mv.visitIntInsn(org.ow2.asmdex.Opcodes.INSN_RETURN_OBJECT, valueRegister);
}
}
private void generateFields(ClassVisitor cv) {
FieldVisitor fv = cv.visitField(org.ow2.asmdex.Opcodes.ACC_PRIVATE, "__initialized", "Z", null, null);
fv.visitEnd();
}
static final String[] classImplentedInterfaces = new String[] { "Lcom/tns/NativeScriptHashCodeProvider;" };
static final String[] interfaceImplementedInterfaces = new String[] { "Lcom/tns/NativeScriptHashCodeProvider;", "" };
private ClassVisitor generateClass(ApplicationWriter aw, ClassDescriptor classTo, String classSignature, String tnsClassSignature, HashSet<ClassDescriptor> implementedInterfaces, AnnotationDescriptor[] annotations)
{
ClassVisitor cv;
int classModifiers = getDexModifiers(classTo);
ArrayList<String> interfacesToImplement = new ArrayList(Arrays.asList(classImplentedInterfaces));
if (classTo.isInterface())
{
interfaceImplementedInterfaces[1] = classSignature; //new String[] { "Lcom/tns/NativeScriptHashCodeProvider;", classSignature };
for(String interfaceToImpl : interfaceImplementedInterfaces) {
if(!interfacesToImplement.contains(interfaceToImpl)) {
interfacesToImplement.add(interfaceToImpl);
}
}
classSignature = objectClass;
}
else
{
if(implementedInterfaces != null) {
for(ClassDescriptor interfaceToImpl : implementedInterfaces) {
interfacesToImplement.add(getAsmDescriptor(interfaceToImpl));
}
}
}
String[] interfacesToImplementArr = new String[interfacesToImplement.size()];
interfacesToImplementArr = interfacesToImplement.toArray(interfacesToImplementArr);
cv = aw.visitClass(classModifiers, tnsClassSignature, null, classSignature, interfacesToImplementArr);
cv.visit(0, classModifiers, tnsClassSignature, null, classSignature, interfacesToImplementArr);
if ((annotations != null) && (annotations.length > 0)) {
for (AnnotationDescriptor ad: annotations) {
String annotationClassname = ad.getAnnotationClassname();
boolean isVisible = ad.isRuntimeVisible();
AnnotationVisitor av = cv.visitAnnotation(annotationClassname, isVisible);
setAnnotationFields(av, ad);
av.visitEnd();
}
}
cv.visitSource(classTo.getName() + ".java", null);
return cv;
}
private void setAnnotationFields(AnnotationVisitor av, AnnotationDescriptor ad) {
AnnotationDescriptor.Parameter[] params = ad.getParams();
if (params.length > 0) {
for (AnnotationDescriptor.Parameter p: params) {
av.visit(p.getName(), p.getValue());
}
}
}
private int getDexModifiers(Descriptor descriptor)
{
if (descriptor.isPublic())
{
return org.ow2.asmdex.Opcodes.ACC_PUBLIC;
}
return org.ow2.asmdex.Opcodes.ACC_PROTECTED;
}
}