/* * Copyright (C) 2014 RoboVM AB * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/gpl-2.0.html>. */ package org.robovm.compiler.util.generic; import soot.SootClass; import soot.SootMethod; import soot.SootResolver; import soot.tagkit.EnclosingMethodTag; import soot.tagkit.InnerClassTag; import soot.tagkit.SignatureTag; import soot.tagkit.Tag; /** * {@link Type} implementation which wraps a {@link SootClass} and contains a * subset of the methods implemented by {@link Class}. */ public class SootClassType extends SootBaseType implements GenericDeclaration { private final SootClass sootClass; public SootClassType(String name) { this(SootResolver.v().makeClassRef(name)); } public SootClassType(SootClass sootClass) { this.sootClass = sootClass; } public SootClass getSootClass() { return sootClass; } private boolean isAssignableFrom(SootClass sub) { if (this.sootClass.equals(sub)) { return true; } if (sootClass.isInterface()) { SootClass c = sub; while (c != null) { for (SootClass ifs : c.getInterfaces()) { if (isAssignableFrom(ifs)) { return true; } } c = c.hasSuperclass() ? c.getSuperclass() : null; } } else if (sub.hasSuperclass()) { return isAssignableFrom(sub.getSuperclass()); } return false; } public boolean isAssignableFrom(SootClassType c) { return isAssignableFrom(c.sootClass); } @SuppressWarnings("unchecked") public TypeVariable<?>[] getTypeParameters() { GenericSignatureParser parser = new GenericSignatureParser(); parser.parseForClass(this, (SignatureTag) sootClass.getTag("SignatureTag")); return parser.formalTypeParameters.clone(); } public Type[] getGenericInterfaces() { GenericSignatureParser parser = new GenericSignatureParser(); parser.parseForClass(this, (SignatureTag) sootClass.getTag("SignatureTag")); return parser.interfaceTypes.getResolvedTypes(); } public Type getGenericSuperclass() { GenericSignatureParser parser = new GenericSignatureParser(); parser.parseForClass(this, (SignatureTag) sootClass.getTag("SignatureTag")); return Types.getType(parser.superclassType); } private SootMethodType getEnclosingMethod(boolean constructor) { EnclosingMethodTag emTag = (EnclosingMethodTag) sootClass.getTag("EnclosingMethodTag"); if (emTag != null) { String clsName = emTag.getEnclosingClass(); String name = emTag.getEnclosingMethod(); String desc = emTag.getEnclosingMethodSig(); if (clsName != null && name != null && desc != null) { if ((constructor && name.equals("<init>")) || (!constructor && !name.equals("<init>") && !name.equals("<clinit>"))) { SootClass cls = SootResolver.v().makeClassRef(clsName); if (!cls.isPhantom()) { for (SootMethod m : cls.getMethods()) { String mDesc = org.robovm.compiler.Types.getDescriptor(m); if (m.getName().equals(name) && desc.equals(mDesc)) { return new SootMethodType(m); } } } } } } return null; } public SootMethodType getEnclosingMethod() { return getEnclosingMethod(false); } public SootMethodType getEnclosingConstructor() { return getEnclosingMethod(true); } public SootClassType getEnclosingClass() { EnclosingMethodTag emTag = (EnclosingMethodTag) sootClass.getTag("EnclosingMethodTag"); if (emTag == null) { return getDeclaringClass(); } return new SootClassType(emTag.getEnclosingClass().replace('/', '.')); } public SootClassType getSuperclass() { if (sootClass.isPhantom()) { return new SootClassType("java.lang.Object"); } return sootClass.hasSuperclass() ? new SootClassType(sootClass.getSuperclass()) : null; } public SootClassType[] getInterfaces() { if (sootClass.isPhantom()) { return new SootClassType[0]; } return wrapClasses(sootClass.getInterfaces()); } public SootClassType getDeclaringClass() { for (Tag tag : sootClass.getTags()) { if (tag instanceof InnerClassTag) { InnerClassTag icTag = (InnerClassTag) tag; if (icTag.getInnerClass() != null && icTag.getOuterClass() != null) { String innerName = icTag.getInnerClass().replace('/', '.'); if (sootClass.getName().equals(innerName)) { return new SootClassType(icTag.getOuterClass().replace('/', '.')); } } } } return null; } @Override public String toString() { return sootClass.getName(); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((sootClass == null) ? 0 : sootClass.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } SootClassType other = (SootClassType) obj; if (sootClass == null) { if (other.sootClass != null) { return false; } } else if (!sootClass.equals(other.sootClass)) { return false; } return true; } private String toSimpleClassTypeSignature() { StringBuilder sb = new StringBuilder(); SootClassType declaringClass = getDeclaringClass(); if (declaringClass != null && declaringClass.sootClass.hasTag("SignatureTag")) { sb.append(declaringClass.toSimpleClassTypeSignature()); sb.append('.'); String innerName = sootClass.getName().substring(declaringClass.sootClass.getName().length() + 1); sb.append(innerName); } else { sb.append(sootClass.getName().replace('.', '/')); } TypeVariable<?>[] typeArgs = getTypeParameters(); if (typeArgs.length > 0) { sb.append("<"); for (TypeVariable<?> ta : typeArgs) { sb.append(ta.toGenericSignature()); } sb.append(">"); } return sb.toString(); } @Override public String toGenericSignature() { return "L" + toSimpleClassTypeSignature() + ";"; } }