org.xmlvm.proc.XmlvmResource Maven / Gradle / Ivy
Show all versions of dragome-bytecode-js-compiler Show documentation
/* Copyright (c) 2002-2011 by XMLVM.org
*
* Project Info: http://www.xmlvm.org
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*/
package org.xmlvm.proc;
import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.Namespace;
import org.jdom.input.SAXBuilder;
import org.jdom.output.XMLOutputter;
import org.xmlvm.proc.out.OutputFile;
import org.xmlvm.util.comparators.XmlvmMethodComparator;
/**
* This class describes a XMLVM resource, that is e.g. produces by
* InputProcesses.
*
* TODO(Sascha): Some aspects in this class don't properly reflect the CLI input
* type. E.g., a CLI resource can have multiple types and therefore multiple
* super types.
*/
public class XmlvmResource
{
/**
*
*/
private static final String ATTRIBUTE_INTERFACE_TABLE_SIZE= "interfaceTableSize";
/**
*
*/
private static final String TAG_CLASS= "class";
/**
* Possible types for an XmlvmResource.
*/
public static enum Type
{
JVM, CLI, CLI_DFA, DEX, CONST_POOL
}
/**
* Possible tags for an XmlvmResource.
*/
public static enum Tag
{
SKELETON_ONLY;
}
/**
* Wrapper for a <dex:invoke-*>
element.
*/
public class XmlvmInvokeInstruction
{
public Element invokeElement;
/**
* Wrapper for a <dex:invoke-*>
element.
*
* @param invokeElement
* DOM element representing a
* <dex:invoke-*>
.
*/
public XmlvmInvokeInstruction(Element invokeElement)
{
this.invokeElement= invokeElement;
}
public String toString()
{
return (new XMLOutputter()).outputString(invokeElement);
}
/**
* @return
*/
public String getClassType()
{
return invokeElement.getAttributeValue("class-type");
}
/**
* @return
*/
public String getMethodName()
{
return invokeElement.getAttributeValue("method");
}
/**
* @param vtableIndex
*/
public void setVtableIndex(int vtableIndex)
{
invokeElement.setAttribute("vtable-index", "" + vtableIndex);
}
/**
* @param type
*/
public void setClassType(String type)
{
invokeElement.setAttribute("class-type", type);
}
/**
* Returns a list of all parameter types (e.g. java.lang.String, ...)
*
* @return list of all parameter types
*/
public List getParameterTypes()
{
Element signature= invokeElement.getChild("parameters", nsDEX);
@SuppressWarnings("unchecked")
List parameterList= signature.getChildren("parameter", nsDEX);
List parameterTypes= new ArrayList();
for (Element parameter : parameterList)
{
parameterTypes.add(parameter.getAttributeValue("type"));
}
return parameterTypes;
}
}
/**
* Wrapper for those XMLVM elements that have an associated string constant.
* In XMLVM these can only be either <dex:const-string>
* or a <vm:field>
element with a constant string
* initializer.
*/
public class XmlvmConstantStringElement
{
private Element element;
/**
* Wrapper for a <dex:const-string*>
or a
* <vm:field>
element.
*
* @param element
* DOM element representing a
* <dex:const-string>
or a
* <vm:field>
.
*/
public XmlvmConstantStringElement(Element element)
{
this.element= element;
}
/**
* Returns the escaped version of the string that uses the \ooo octal
* notation for representing special characters.
*/
public String getEscapedStringConstant()
{
return element.getAttributeValue("value");
}
/**
* Returns the encoded version of the string. The encoded version
* represents the string as a comma-separated list of short values.
*/
public String getEncodedStringConstant()
{
return element.getAttributeValue("encoded-value");
}
/**
* Returns the length of the string.
*/
public int getLength()
{
return Integer.parseInt(element.getAttributeValue("length"));
}
/**
* Set the constant pool ID for this string.
*
* @param id
* Constant pool ID.
*/
public void setContantPoolID(int id)
{
element.setAttribute("id", "" + id);
}
}
public class XmlvmMemberReadWrite
{
public Element memberReadWriteElement;
/**
* Wrapper for a <dex:iget-*>
or a
* <dex:iput-*>
element.
*
* @param invokeElement
* DOM element representing a <dex:iget-*>
* or a <dex:iput-*>
.
*/
public XmlvmMemberReadWrite(Element memberReadWriteElement)
{
this.memberReadWriteElement= memberReadWriteElement;
}
public String toString()
{
return (new XMLOutputter()).outputString(memberReadWriteElement);
}
/**
* @return
*/
public String getMemberName()
{
return this.memberReadWriteElement.getAttributeValue("member-name");
}
/**
* @param type
*/
public void setClassType(String type)
{
this.memberReadWriteElement.setAttribute("class-type", type);
}
/**
* @return
*/
public String getClassType()
{
return this.memberReadWriteElement.getAttributeValue("class-type");
}
}
/**
* Wrapper for a <vm:method>
element.
*/
public class XmlvmMethod
{
public Element methodElement;
/**
* Wrapper for a <vm:method>
element.
*
* @param invokeElement
* DOM element representing a <vm:method>
.
*/
public XmlvmMethod(Element methodElement)
{
this.methodElement= methodElement;
}
public int hashCode()
{
return toString().hashCode();
}
public String toString()
{
return (new XMLOutputter()).outputString(methodElement);
}
public String getName()
{
return methodElement.getAttributeValue("name");
}
/**
* Retrieve all invoke instructions that are handled via a vtable (i.e.,
* <dex:invoke-virtual>
and
* <dex:invoke-interface>
instructions).
*
* @return All <dex:invoke-virtual>
and
* <dex:invoke-interface>
instructions of
* this method.
*/
public List getVtableInvokeInstructions()
{
List invokeInstructions= new ArrayList();
searchForVtableInvokeInstructions(invokeInstructions, methodElement);
return invokeInstructions;
}
@SuppressWarnings("unchecked")
private void searchForVtableInvokeInstructions(List invokeInstructions, Element element)
{
List children= element.getChildren("invoke-virtual", nsDEX);
for (Element instruction : children)
{
XmlvmInvokeInstruction invoke= new XmlvmInvokeInstruction(instruction);
invokeInstructions.add(invoke);
}
children= element.getChildren("invoke-virtual-range", nsDEX);
for (Element instruction : children)
{
XmlvmInvokeInstruction invoke= new XmlvmInvokeInstruction(instruction);
invokeInstructions.add(invoke);
}
children= element.getChildren();
for (Element node : children)
{
searchForVtableInvokeInstructions(invokeInstructions, node);
}
}
/**
* Determines if two Java methods can override each other. Two methods
* override each other, if their names as well as all their input
* parameter types are identical. Note that the return types need not be
* identical since Java allows covariant returns.
* doesOverrideMethod()
does not check for subtype
* relationship of the return type. The return type is essentially
* ignored and therefore doesOverrideMethod()
is
* commutative.
*
* @param method
* {@link #XmlvmResource} to be checked.
* @return true, if method
overrides this
.
*/
@SuppressWarnings("unchecked")
public boolean doesOverrideMethod(XmlvmMethod method)
{
return doesOverrideMethod(method.getName(), method.methodElement.getChild("signature", nsXMLVM).getChildren("parameter", nsXMLVM));
}
/**
* Determines if two Java methods can override each other. Two methods
* override each other, if their names as well as all their input
* parameter types are identical. Note that the return types need not be
* identical since Java allows covariant returns.
* doesOverrideMethod()
does not check for subtype
* relationship of the return type. The return type is essentially
* ignored and therefore doesOverrideMethod()
is
* commutative.
*
* @param method
* {@link #XmlvmInvokeVirtual} to be checked.
* @return true, if method
overrides this
.
*/
@SuppressWarnings("unchecked")
public boolean doesOverrideMethod(XmlvmInvokeInstruction instruction)
{
return doesOverrideMethod(instruction.getMethodName(), instruction.invokeElement.getChild("parameters", nsDEX).getChildren("parameter", nsDEX));
}
@SuppressWarnings("unchecked")
private boolean doesOverrideMethod(String methodName, List parameters)
{
if (!this.getName().equals(methodName))
{
return false;
}
Element mySignature= methodElement.getChild("signature", nsXMLVM);
List myParameters= mySignature.getChildren("parameter", nsXMLVM);
if (myParameters.size() != parameters.size())
{
return false;
}
for (int i= 0; i < myParameters.size(); i++)
{
String myParameterType= myParameters.get(i).getAttributeValue("type");
String otherParameterType= parameters.get(i).getAttributeValue("type");
if (!myParameterType.equals(otherParameterType))
{
return false;
}
}
return true;
}
/**
* Returns true if this method is static.
*/
public boolean isStatic()
{
String flag= methodElement.getAttributeValue("isStatic");
return flag != null && flag.equals("true");
}
/**
* Returns true if this method is private.
*/
public boolean isPrivate()
{
String flag= methodElement.getAttributeValue("isPrivate");
return flag != null && flag.equals("true");
}
/**
* Returns true if this method is protected.
*/
public boolean isProtected()
{
String flag= methodElement.getAttributeValue("isProtected");
return flag != null && flag.equals("true");
}
/**
* Returns true if this method is public.
*/
public boolean isPublic()
{
String flag= methodElement.getAttributeValue("isPublic");
return flag != null && flag.equals("true");
}
/**
* Returns true if this method is abstract.
*/
public boolean isAbstract()
{
String flag= methodElement.getAttributeValue("isAbstract");
return flag != null && flag.equals("true");
}
/**
* Returns true if this method is native.
*/
public boolean isNative()
{
String flag= methodElement.getAttributeValue("isNative");
return flag != null && flag.equals("true");
}
/**
* Returns true if this method is a constructor.
*/
public boolean isConstructor()
{
return methodElement.getAttributeValue("name").equals("");
}
/**
* Returns true if this method is synthetic.
*/
public boolean isSynthetic()
{
String flag= methodElement.getAttributeValue("isSynthetic");
return flag != null && flag.equals("true");
}
/**
* Sets the @isSynthetic flag of the method.
*/
public void setSynthetic(boolean flag)
{
if (flag)
{
methodElement.setAttribute("isSynthetic", "true");
}
else
{
methodElement.removeAttribute("isSynthetic");
}
}
/**
* Returns whether this method is overriding another method the
* containing class inherited.
*
* This is used by the C# backend which needs to emit the special
* override keyword in the method signature in this case (see
* http://msdn.microsoft.com/en-us/library/ebca9ah3.aspx).
*
* It is not filled by other backends!
*/
public boolean isOverriding()
{
String flag= methodElement.getAttributeValue("isOverriding");
return flag != null && flag.equals("true");
}
/**
* Set if this method is overriding another method the containing class
* inherited.
*
* This is used by the C# backend which needs to emit the special
* override keyword in the method signature in this case (see
* http://msdn.microsoft.com/en-us/library/ebca9ah3.aspx)
*
* It is not filled by other backends!
*/
public void setOverriding(boolean overriding)
{
methodElement.setAttribute("isOverriding", Boolean.toString(overriding));
}
/**
* Set a vtable index for this method (XML attribute
* vtableIndex
).
*/
public void setVtableIndex(int idx)
{
methodElement.setAttribute("vtableIndex", "" + idx);
}
/**
* Returns a list of all parameter types (e.g. java.lang.String, ...)
*
* @return list of all parameter types
*/
public List getParameterTypes()
{
Element signature= methodElement.getChild("signature", nsXMLVM);
@SuppressWarnings("unchecked")
List parameterList= signature.getChildren("parameter", nsXMLVM);
List parameterTypes= new ArrayList();
for (Element parameter : parameterList)
{
parameterTypes.add(parameter.getAttributeValue("type"));
}
return parameterTypes;
}
/**
* @return the index in the interface table for this method, or null for
* none
*/
public Integer getInterfaceTableIndex()
{
Integer interfaceTableIndex= null;
String strVal= methodElement.getAttributeValue("itableIndex");
if (strVal != null)
{
try
{
interfaceTableIndex= Integer.parseInt(strVal);
}
catch (NumberFormatException e)
{
// do nothing
}
}
return interfaceTableIndex;
}
/**
* @param interfaceTableIndex
* the index of in the interface table for this method, or
* null for none
*/
public void setInterfaceTableIndex(Integer interfaceTableIndex)
{
methodElement.setAttribute("itableIndex", interfaceTableIndex == null ? "" : interfaceTableIndex.toString());
}
}
/**
* Add a copy of the given method to the XmlvmResource
*
* @param method
* method to add
* @return Copied method
*/
public XmlvmMethod addMethod(XmlvmMethod method)
{
Element clazz= xmlvmDocument.getRootElement().getChild(TAG_CLASS, nsXMLVM);
Element clone= (Element) method.methodElement.clone();
clazz.addContent(clone);
return new XmlvmMethod(clone);
}
/**
* Wrapper for a <vm:field>
element.
*/
public class XmlvmField
{
public Element fieldElement;
/**
* Wrapper for a <vm:field>
element.
*
* @param fieldElement
* DOM element representing a <vm:field>
.
*/
public XmlvmField(Element fieldElement)
{
this.fieldElement= fieldElement;
}
/**
* Returns the name of the field.
*/
public String getName()
{
return fieldElement.getAttributeValue("name");
}
/**
* Returns the type of the field.
*/
public String getType()
{
return fieldElement.getAttributeValue("type");
}
/**
* Returns true if this field is private.
*/
public boolean isPrivate()
{
String flag= fieldElement.getAttributeValue("isPrivate");
return flag != null && flag.equals("true");
}
/**
* Returns true if this field is protected.
*/
public boolean isProtected()
{
String flag= fieldElement.getAttributeValue("isProtected");
return flag != null && flag.equals("true");
}
/**
* Returns true if this field is public.
*/
public boolean isPublic()
{
String flag= fieldElement.getAttributeValue("isPublic");
return flag != null && flag.equals("true");
}
/**
* Returns true if the field the instruction
is accessing
* has the same name as this field.
*/
public boolean matchesName(XmlvmMemberReadWrite instruction)
{
return getName().equals(instruction.getMemberName());
}
/**
* Returns true if the given field
has the same name and
* type as this field.
*/
public boolean matchesDeclaration(XmlvmField field)
{
return getName().equals(field.getName()) && getType().equals(field.getType());
}
}
/**
* @param search
*/
@SuppressWarnings("unchecked")
public void removeMethod(XmlvmMethod search)
{
List classes= xmlvmDocument.getRootElement().getChildren(TAG_CLASS, nsXMLVM);
for (Element clazz : classes)
{
if (clazz.removeContent(search.methodElement))
{
return;
}
}
}
/**
* vm:itable contains mappings between interface indices of implemented
* interfaces and the class vtable or directly methods of the class
*/
public class XmlvmItable
{
private Element itableElement;
public XmlvmItable(Element itableElement)
{
this.itableElement= itableElement;
}
/**
* Create a new mapping between a global interface method index and the
* vtable of a class
*
* @param itableIndex
* global interface method index
* @param vtableIndexClass
* Corresponding vtable index of an implementing method in a
* class.
*/
public void addVtableMapping(String ifaceName, XmlvmMethod ifaceMethod, int vtableIndex)
{
Element map= new Element("vtable-map", nsXMLVM);
map.setAttribute("ifaceName", ifaceName);
map.setAttribute("ifaceMethodName", ifaceMethod.getName());
Element signature= (Element) ifaceMethod.methodElement.getChild("signature", nsXMLVM).clone();
map.addContent(signature);
map.setAttribute("vtableIndex", "" + vtableIndex);
itableElement.addContent(map);
}
/**
* Create a new mapping between a global interface method index and a
* direct invoke
*
* @param itableIndex
* global interface method index
* @param classType
* class containing the method to be mapped
* @param methodName
* name of the method to be mapped
*/
public void addDirectMapping(String ifaceName, XmlvmMethod ifaceMethod, String classType)
{
Element map= new Element("direct-map", nsXMLVM);
map.setAttribute("ifaceName", ifaceName);
map.setAttribute("ifaceMethodName", ifaceMethod.getName());
Element signature= (Element) ifaceMethod.methodElement.getChild("signature", nsXMLVM).clone();
map.addContent(signature);
map.setAttribute("className", classType);
itableElement.addContent(map);
}
}
public static Namespace nsXMLVM= Namespace.getNamespace("vm", "http://xmlvm.org");
public static Namespace nsDEX= Namespace.getNamespace("dex", "http://xmlvm.org/dex");
public static Namespace nsJVM= Namespace.getNamespace("jvm", "http://xmlvm.org/jvm");
private final Type type;
private final Document xmlvmDocument;
private final Set referencedTypes;
private final String name;
private final String superTypeName;
private final Map tags= new HashMap();
public XmlvmResource(Type type, Document xmlvmDocument)
{
this.type= type;
this.xmlvmDocument= xmlvmDocument;
if (type != Type.CONST_POOL)
{
this.referencedTypes= extractReferencedTypes(xmlvmDocument);
Element classElement= xmlvmDocument.getRootElement().getChild(TAG_CLASS, nsXMLVM);
this.name= classElement.getAttributeValue("name");
this.superTypeName= classElement.getAttributeValue("extends");
}
else
{
this.referencedTypes= null;
this.name= "ConstantPool";
this.superTypeName= null;
}
}
private static Set extractReferencedTypes(Document xmlvmDocument)
{
Set result= new HashSet();
Element referencesElement= xmlvmDocument.getRootElement().getChild("references", nsXMLVM);
if (referencesElement == null)
{
return result;
}
@SuppressWarnings("unchecked")
List references= referencesElement.getChildren("reference", nsXMLVM);
for (Element reference : references)
{
result.add(reference.getAttributeValue("name"));
}
return result;
}
public String toString()
{
return getFullName();
}
/**
* Returns the XMLVM document of this resource.
*/
public Document getXmlvmDocument()
{
return xmlvmDocument;
}
/**
* Returns the type of this XMLVM resource.
*/
public Type getType()
{
return type;
}
/**
* Returns the name of this XMLVM resource.
*/
public String getName()
{
return name;
}
/**
* Returns the full name, which is the normal name prefixed by the package.
*/
public String getFullName()
{
String fullResourceName= getPackageName();
if (!fullResourceName.isEmpty())
{
fullResourceName+= ".";
}
fullResourceName+= getName();
return fullResourceName;
}
/**
* Checks if this class is abstract
*
* @return true if it's an abstract class otherwise false
*/
public boolean isAbstract()
{
Element clazz= xmlvmDocument.getRootElement().getChild(TAG_CLASS, nsXMLVM);
String flag= clazz.getAttributeValue("isAbstract");
return flag != null && flag.equals("true");
}
/**
* Returns the names of all types that are referenced in this resource.
*/
public Set getReferencedTypes()
{
return referencedTypes;
}
/**
* Returns the name of the super class type.
*/
public String getSuperTypeName()
{
return superTypeName;
}
/**
* Returns the name of the package, this resource is in.
*
* E.g. "java.lang"
*/
public String getPackageName()
{
if (type == Type.CONST_POOL)
{
return "org.xmlvm";
}
Element clazz= xmlvmDocument.getRootElement().getChild(TAG_CLASS, nsXMLVM);
return clazz.getAttributeValue("package");
}
/**
* Returns a comma-separated list of interfaces this resources implements.
*/
public String getInterfaces()
{
Element clazz= xmlvmDocument.getRootElement().getChild(TAG_CLASS, nsXMLVM);
return clazz.getAttributeValue("interfaces");
}
/**
* Returns all methods defined in this resource.
*/
public List getMethods()
{
List result= new ArrayList();
List methods= getMethodElements();
for (Element method : methods)
{
result.add(new XmlvmMethod(method));
}
return result;
}
/**
* Returns a sorted list of all methods defined in this resource.
*/
public List getMethodsSorted()
{
List result= getMethods();
Collections.sort(result, new XmlvmMethodComparator());
return result;
}
/**
* Returns all fields defined in this resource.
*/
@SuppressWarnings("unchecked")
public List getFields()
{
List result= new ArrayList();
List classes= xmlvmDocument.getRootElement().getChildren(TAG_CLASS, nsXMLVM);
for (Element clazz : classes)
{
List fields= clazz.getChildren("field", nsXMLVM);
for (Element field : fields)
{
result.add(new XmlvmField(field));
}
}
return result;
}
/**
* Returns whether this resource represents an interface.
*/
public boolean isInterface()
{
return Boolean.parseBoolean(xmlvmDocument.getRootElement().getChild(TAG_CLASS, nsXMLVM).getAttributeValue("isInterface"));
}
@SuppressWarnings("unchecked")
// JDOM's non-generic API.
private List getMethodElements()
{
List result= new ArrayList();
List classes= xmlvmDocument.getRootElement().getChildren(TAG_CLASS, nsXMLVM);
for (Element clazz : classes)
{
result.addAll(clazz.getChildren("method", nsXMLVM));
}
return result;
}
/**
* Set the XML attribute vtableSize
that designates the number
* of vtable entries for this class.
*
* @param vtableSize
* Size of the vtable.
*/
@SuppressWarnings("unchecked")
// JDOM's non-generic API.
public void setVtableSize(int vtableSize)
{
List classes= xmlvmDocument.getRootElement().getChildren(TAG_CLASS, nsXMLVM);
if (classes.size() != 1)
{
System.err.println("XmlvmResource.setVtableSize(): cannot deal with multiple classes");
System.exit(-1);
}
classes.get(0).setAttribute("vtableSize", "" + vtableSize);
}
/**
* Creates an itable for this class.The created itable is essentially a
* mapping of methods defined in an interface to methods defined in the
* class. The XML markup of the vtable is as follows:
*
*
* <itable>
* <vtable-map itableIndex="..." vtableIndex="..."/>
* <vtable-map itableIndex="..." vtableIndex="..."/>
* ...
* <direct-map itableIndex="..." class-type="..." name="..."/>
* <direct-map itableIndex="..." class-type="..." name="..."/>
* ...
* </itable>
*
*
* @return A newly created itable.
*/
public XmlvmItable createItable()
{
Element itableElement= new Element("itable", nsXMLVM);
xmlvmDocument.getRootElement().getChild(TAG_CLASS, nsXMLVM).addContent(itableElement);
return new XmlvmItable(itableElement);
}
/**
* Collects a list of all instructions whose type may need to be adjusted
* for a specific set of classes that comprise the application being
* cross-compiled (see
* {@link org.xmlvm.plugins.c.COutputProcess#adjustTypes()}). Instructions
* that need to be considered are invoke-static, invoke-super, iput, iget,
* sput, and sget.
*
* @param invokeInstructions
* Will be filled with all invoke-static and invoke-super
* instructions upon return.
* @param readWriteInstructions
* Will be filled with all iput, iget, sput, and sget
* instructions upon return.
*/
@SuppressWarnings("unchecked")
public void collectInstructions(List invokeInstructions, List readWriteInstructions)
{
Element root= xmlvmDocument.getRootElement().getChild(TAG_CLASS, nsXMLVM);
Iterator