All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.dragome.compiler.units.ClassUnit Maven / Gradle / Ivy

There is a newer version: 0.96-beta4
Show newest version
/*******************************************************************************
 * Copyright (c) 2011-2014 Fernando Petrola
 * 
 * This file is part of Dragome SDK.
 * 
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v3.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/gpl.html
 ******************************************************************************/

// Copyright 2011 The j2js Authors. All Rights Reserved.
//
// This file is part of j2js.
//
// j2js 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 3 of the License, or
// (at your option) any later version.
//
// j2js 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 j2js. If not, see .

package com.dragome.compiler.units;

import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.InvocationHandler;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.commons.io.output.StringBuilderWriter;

import com.dragome.compiler.DragomeJsCompiler;
import com.dragome.compiler.Project;
import com.dragome.compiler.generators.DragomeJavaScriptGenerator;
import com.dragome.compiler.type.Signature;
import com.dragome.compiler.type.TypeCollector;
import com.dragome.compiler.utils.FileObject;
import com.dragome.compiler.utils.Log;

public class ClassUnit extends Unit
{
	public static final String STATIC_MEMBER= "#static-member#";

	static final long serialVersionUID= 1;

	private long lastCompiled;

	public long getLastModified()
	{
		return getClassFile().getLastModified();
	}

	private Map declaredMembers;

	private ClassUnit superUnit;

	private Collection interfaces;

	private Collection subUnits;

	public boolean isInterface= false;

	public boolean isConstructorTainted= false;

	public Map[] annotations;

	private Project project;

	private transient boolean isResolved= false;

	private transient FileObject classFile;

	private List implementors= new ArrayList();

	private transient boolean written= false;

	private String generatedJs;

	public static boolean oneWritten= false;

	public List getImplementors()
	{
		return implementors;
	}

	public void setImplementors(List implementors)
	{
		this.implementors= implementors;
	}

	public static List stringInits= new ArrayList();

	public ClassUnit()
	{
	}

	public ClassUnit(Project theProject, Signature theSignature)
	{
		project= theProject;

		interfaces= new LinkedHashSet();
		declaredMembers= new LinkedHashMap();
		subUnits= new LinkedHashSet();
		lastCompiled= -1;
		setSignature(theSignature);
	}

	public void clear()
	{
		lastCompiled= -1;
		removeInterfaces();
		setSuperUnit(null);
		declaredMembers.clear();
		generatedJs= null;
	}

	public boolean isUpToDate()
	{
		return lastCompiled >= getLastModified();
	}

	public Collection getInterfaces()
	{
		return interfaces;
	}

	public void addInterface(ClassUnit interfaceUnit)
	{
		interfaces.add(interfaceUnit);
		interfaceUnit.addImplementor(this);
	}

	private void addImplementor(ClassUnit classUnit)
	{
		implementors.add(classUnit);
	}

	private void removeInterfaces()
	{
		Iterator iter= interfaces.iterator();
		while (iter.hasNext())
		{
			ClassUnit interfaceUnit= (ClassUnit) iter.next();
			interfaceUnit.removeSubUnit(this);
			iter.remove();
		}
	}

	public void addSubUnit(ClassUnit subUnit)
	{
		if (subUnit == null)
			throw new NullPointerException();
		subUnits.add(subUnit);
	}

	public void removeSubUnit(ClassUnit subUnit)
	{
		if (subUnit == null)
			throw new NullPointerException();
		subUnits.remove(subUnit);
	}

	public Collection getSubUnits()
	{
		return subUnits;
	}

	public MemberUnit getDeclaredMember(String signature)
	{
		if (signature == null)
			throw new NullPointerException();
		return declaredMembers.get(signature);
	}

	public Collection getSupertypes()
	{
		TypeCollector collector= new TypeCollector();
		project.visitSuperTypes(this, collector);
		return collector.collectedTypes;
	}

	public Collection getMembers(String signature)
	{
		if (signature == null)
			throw new NullPointerException();
		ArrayList list= new ArrayList();

		for (ClassUnit clazz : getSupertypes())
		{
			MemberUnit member= clazz.getDeclaredMember(signature);
			if (member != null)
				list.add(member);
		}

		return list;
	}

	public Collection getDeclaredMembers()
	{
		if (!isResolved)
			throw new RuntimeException("Class is not yet resolved: " + getName());
		return declaredMembers.values();
	}

	public void addMemberUnit(MemberUnit unit)
	{
		declaredMembers.put(unit.getSignature().toString(), unit);
	}

	public ClassUnit getSuperUnit()
	{
		return superUnit;
	}

	public void setSuperUnit(ClassUnit theSuperUnit)
	{
		if (superUnit != null)
		{
			superUnit.removeSubUnit(this);
		}
		superUnit= theSuperUnit;
		if (superUnit != null)
		{
			superUnit.addSubUnit(this);
		}
	}

	public void write(int depth, Writer writer2) throws IOException
	{
		if (!isTainted() || !isResolved() || isWritten())
			return;

		Writer writer= new StringBuilderWriter();

		oneWritten= true;
		written= true;

		for (ClassUnit child : getSupertypes())
		{
			child.setTainted();
			child.write(depth, writer2);
		}

		if (getSuperUnit() != null)
		{
			getSuperUnit().setTainted();
			getSuperUnit().write(depth, writer2);
		}

		for (ClassUnit child : getInterfaces())
		{
			child.setTainted();
			child.write(depth, writer2);
		}

		if (generatedJs == null)
			generatedJs= generateJsCode(depth, writer);

		writer2.write(generatedJs);

		taintRelated(depth, writer2);
	}

	private String generateJsCode(int depth, Writer writer) throws IOException
	{
		Log.getLogger().debug(getIndent(depth) + this);

		if (getData() != null)
		{
			writer.write(getData());
		}
		else
		{
			throw new RuntimeException("Cannot compile the class: " + getName());
		}

		Set notImplementedMethods= getNotImplementedMethods();

		if (interfaces.size() > 0)
		{
			String extendOperator= "implement";
			if (isInterface)
				extendOperator= "extend";
			//	    writer.write("_T.interfaces = [");
			writer.write(extendOperator + ": [");
			int i= 0;
			for (ClassUnit interFace : interfaces)
			{
				if (i++ > 0)
					writer.write(", ");
				writer.write(DragomeJavaScriptGenerator.normalizeExpression(interFace.getSignature().toString()));
			}
			writer.write("],\n");
		}

		writer.write("members:\n");
		writer.write("{\n");

		MemberUnit clinitMethod= null;
		List staticMethods= new ArrayList();

		boolean first= true;

		for (MemberUnit member : getDeclaredMembers())
		{
			if (member.getData() != null && member.getData().startsWith(STATIC_MEMBER))
			{
				staticMethods.add(member);
			}
			else if (member.getData() != null && member.getData().contains("_dragomeJs.StringInit"))
			{
				stringInits.add(member);
			}
			else
			{
				if (member.getData() != null && member.getData().trim().length() > 0)
				{
					if (!first)
						writer.write(",\n");
					first= false;

					writeMethodAlternative(depth, writer, member);

					if (member instanceof ProcedureUnit)
					{
						project.currentGeneratedMethods++;
						writer.flush();
					}
				}
			}
			if (isClinit(member))
				clinitMethod= member;
		}

		for (MemberUnit member : notImplementedMethods)
		{
			if (!first)
				writer.write(",\n");

			first= false;

			String methodData= member.getData().replace(STATIC_MEMBER, "");
			methodData= methodData.substring(0, methodData.indexOf("{"));
			writer.write(methodData);
			writer.write("{\n return ");
			writer.write(member.getDeclaringClass().toString().replace(".", "_"));
			writer.write(".$$members.");
			writer.write(methodData.replace(": function ()", ".call (this)").replace(": function (", ".call (this, "));
			writer.write("}\n");

			if (member instanceof ProcedureUnit)
			{
				project.currentGeneratedMethods++;
				writer.flush();
			}
		}

		writer.write("\n},\n");
		writer.write("statics:\n");
		writer.write("{\n");

		writeClinit(depth, writer, clinitMethod, staticMethods);

		boolean hasStaticMembers= staticMethods.size() > 0;
		if (hasStaticMembers)
			writer.write(",");

		writer.write("\n");

		first= true;

		for (MemberUnit member : staticMethods)
		{
			if (member != clinitMethod)
			{
				if (!first)
					writer.write(",\n");

				first= false;

				String memberData= member.getData();
				member.setData(member.getData().replace(STATIC_MEMBER, ""));

				writeMethodAlternative(depth, writer, member);

				member.setData(memberData);
				if (member instanceof ProcedureUnit || member instanceof FieldUnit)
				{
					if (member instanceof ProcedureUnit)
						project.currentGeneratedMethods++;

					writer.flush();
				}
			}
		}

		first= addSuperStaticMethods(writer, !hasStaticMembers ^ first, staticMethods);

		//		addAnnotationsAsStaticMember(writer, first);

		writer.write("\n}\n");

		writer.write("\n}\n\n");
		writer.write(");\n");

		return writer.toString();
	}

	private void writeClinit(int depth, Writer writer, MemberUnit clinitMethod, List staticMethods) throws IOException
	{
		String superStaticFields= createSuperStaticFieldsReferences(depth, clinitMethod, staticMethods);

		if (clinitMethod != null)
		{
			String name= DragomeJavaScriptGenerator.normalizeExpression(clinitMethod.getDeclaringClass().getName());
			String replace= clinitMethod.getData().replace("this.", name + ".");
			replace= replace.replace("{", "{ this.$$clinit_=function(){return this};\n" + superStaticFields);
			replace= replace.substring(0, replace.length() - 2) + "\n return this;\n}";

			String memberData= clinitMethod.getData();
			clinitMethod.setData(replace.replace(STATIC_MEMBER, ""));
			clinitMethod.write(depth, writer);
			clinitMethod.setData(memberData);
			String modifyMethodName= DragomeJavaScriptGenerator.normalizeExpression(clinitMethod.getSignature());
			project.getClinits().add("initClass(" + name + ");");
		}
		else
		{
			String replace= "$$clinit_: function(){this.$$clinit_=function(){return this};\n" + superStaticFields + " return this;}";
			writer.write(replace);
		}
	}

	private String createSuperStaticFieldsReferences(int depth, MemberUnit clinitMethod, List staticMethods) throws IOException
	{
		boolean first= true;
		StringBuilder result= new StringBuilder();

		if (superUnit != null)
			for (MemberUnit member : superUnit.getDeclaredMembers())
			{
				if (member.getData() != null && member.getData().startsWith(STATIC_MEMBER))
				{
					//					if (!containsSignature(member.getSignature(), staticMethods))
					if (!(member instanceof MethodUnit))
					{
						if (!member.toString().equals("java.lang.Object#hashCodeCount"))
						{
							if (!containsSignature(member.getSignature(), staticMethods))
							{
								String methodData= member.getData().replace(STATIC_MEMBER, "");
								String substring= methodData.substring(0, methodData.indexOf(":"));
								String fieldName= DragomeJavaScriptGenerator.normalizeExpression(getName()) + ".$$clinit_()." + substring + "=" + DragomeJavaScriptGenerator.normalizeExpression(member.getDeclaringClass().getName()) + ".$$clinit_()." + substring;

								result.append(fieldName);
								result.append(";\n");
								first= false;

								FieldUnit newField= new FieldUnit(member.getSignature(), this);
								newField.setData(member.getData());
								addMemberUnit(newField);
							}
						}
					}
				}
			}

		return result.toString();
	}

	private void addAnnotationsAsStaticMember(Writer writer, boolean first) throws IOException
	{
		if (!first)
			writer.write(",\n");

		writer.write("{\n");

		for (Entry entry : annotationsValues.entrySet())
		{
			writer.write(entry.getKey() + "\n");
			writer.write(entry.getValue() + "\n");
		}

		writer.write("}\n");
	}

	private Set getNotImplementedMethods()
	{
		Set interfacesMembers= new HashSet();
		getDeclaredMembersInInterfaces(this, interfacesMembers);

		Set implementedMembers= new HashSet();
		getImplementedMembersInHierarchy(this, implementedMembers);

		interfacesMembers.removeAll(implementedMembers);
		if (isImplementing(InvocationHandler.class) || isAbstract || isInterface || interfacesMembers.size() <= 0 || interfacesMembers.isEmpty())
			interfacesMembers.clear();

		return interfacesMembers;
	}

	private boolean isImplementing(Class class1)
	{
		if (getSuperUnit() != null && getSuperUnit().isImplementing(class1))
			return true;

		for (ClassUnit interfaz : getInterfaces())
		{
			if (interfaz.toString().equals(class1.getName()) || interfaz.isImplementing(class1))
				return true;
		}
		return false;
	}

	private void getImplementedMembersInHierarchy(ClassUnit classUnit, Collection implementedMembers)
	{
		if (classUnit != null)
		{
			if (isInterface)
				return;

			implementedMembers.addAll(filterMethods(classUnit.getDeclaredMembers()));
			getImplementedMembersInHierarchy(classUnit.getSuperUnit(), implementedMembers);
		}
	}

	private Collection filterMethods(Collection declaredMembers2)
	{
		Collection result= new ArrayList();
		for (MemberUnit memberUnit : declaredMembers2)
		{
			if (memberUnit instanceof MethodUnit)
				result.add((MethodUnit) memberUnit);
		}
		return result;
	}

	private void getDeclaredMembersInInterfaces(ClassUnit classUnit, Collection interfacesMembers)
	{
		if (classUnit != null)
		{
			if (isInterface)
				interfacesMembers.addAll(filterMethods(classUnit.getDeclaredMembers()));

			for (ClassUnit interfaceUnit : classUnit.getInterfaces())
			{
				interfacesMembers.addAll(filterMethods(interfaceUnit.getDeclaredMembers()));
				interfaceUnit.getDeclaredMembersInInterfaces(interfaceUnit, interfacesMembers);
			}

			getDeclaredMembersInInterfaces(classUnit.getSuperUnit(), interfacesMembers);
		}
	}

	private boolean isClinit(MemberUnit member)
	{
		return member.getSignature().toString().equals("()void");
	}

	private boolean addSuperStaticMethods(Writer writer, boolean first, List staticMethods) throws IOException
	{
		if (superUnit != null)
			for (MemberUnit member : superUnit.getDeclaredMembers())
			{
				if (member.getData() != null && member.getData().startsWith(STATIC_MEMBER))
				{
					if (member instanceof MethodUnit)
					{
						if (!isClinit(member))
							if (!containsSignature(member.getSignature(), staticMethods))
							{
								addMemberUnit(member);
								String methodData= member.getData().replace(STATIC_MEMBER, "");
								String substring= methodData.substring(0, methodData.indexOf("{"));
								String methodName= substring.replace(": function", "").replace("\n", "");
								substring+= "{ \n\t return this.superclass." + methodName + ";\n}";

								if (!first)
									writer.write(",\n");

								writer.write(substring);
								first= false;
							}
					}
				}
			}

		return first;
	}
	private boolean containsSignature(Signature signature, List staticMethods)
	{
		for (MemberUnit memberUnit : staticMethods)
		{
			if (memberUnit.getSignature().equals(signature))
				return true;
		}

		return false;
	}

	private void writeMethodAlternative(int depth, Writer writer, MemberUnit member) throws IOException
	{
		if (member instanceof MethodUnit)
		{
			MethodUnit methodUnit= (MethodUnit) member;
			String normalizedSignature= DragomeJavaScriptGenerator.normalizeExpression(methodUnit.getSignature());
			String normalizedClassname= DragomeJavaScriptGenerator.normalizeExpression(methodUnit.getDeclaringClass().getName());
			Project.getSingleton().getWrittenSignatures().add(normalizedClassname + "|" + normalizedSignature);
		}

		if (member instanceof MethodUnit && notReversibleMethods.contains(((MethodUnit) member).getNameAndSignature()))
		{
			MethodUnit methodUnit= (MethodUnit) member;
			writer.write(extractMethodDefinition(alternativeCompilation, methodUnit.getNameAndSignature()));
		}
		else
			member.write(depth + 1, writer);
	}

	private String extractMethodDefinition(String compiledCode, String nameAndSignature)
	{
		int startIndex= compiledCode.indexOf("start of " + nameAndSignature);
		int endIndex= compiledCode.indexOf("end of " + nameAndSignature);

		String part= compiledCode.substring(startIndex, endIndex);
		String result= part.substring(part.indexOf("*/"), part.lastIndexOf("}") + 1);
		result= result.substring(result.indexOf("$"));
		return result;
	}

	private void taintRelated(int depth, Writer writer) throws IOException
	{
		if (!"java.lang.Object".equals(toString()))
			for (String dependency : dependencies)
			{
				ClassUnit dependencyClassUnit= project.getOrCreateClassUnit(dependency);
				dependencyClassUnit.setTainted();
				//		dependencyClassUnit.write(depth, writer);
			}

		for (ClassUnit child : getSubUnits())
		{
			child.setTainted();
			child.write(depth, writer);
		}

		for (ClassUnit child : getImplementors())
		{
			child.setTainted();
			//	    child.write(depth, writer);
		}
	}

	private boolean isWritten()
	{
		return written;
	}

	void setSignature(Signature theSignature)
	{
		super.setSignature(theSignature);
	}

	public FileObject getClassFile()
	{
		if (classFile == null)
		{
			classFile= DragomeJsCompiler.compiler.fileManager.getFileForInput(getSignature().toString().replaceAll("\\.", "/") + ".class");
		}
		return classFile;
	}

	public void setClassFile(FileObject classFile)
	{
		this.classFile= classFile;
	}

	public void setLastCompiled(long theLastCompiled)
	{
		lastCompiled= theLastCompiled;
	}

	public boolean isResolved()
	{
		return isResolved;
	}

	public void setSuperTainted()
	{
		ClassUnit clazz= this;
		do
		{
			clazz.setTainted();
			clazz= clazz.getSuperUnit();
		}
		while (clazz != null);

		for (ClassUnit i : interfaces)
		{
			i.setSuperTainted();
		}
	}

	public void setResolved(boolean theIsResolved)
	{
		isResolved= theIsResolved;
	}

	public String getName()
	{
		return getSignature().className();
	}

	public Project getProject()
	{
		return project;
	}

	private List dependencies= new ArrayList();

	private byte[] bytecode;

	public byte[] getBytecode()
	{
		return bytecode;
	}

	private List notReversibleMethods= new ArrayList();

	private String alternativeCompilation;

	public boolean isAbstract= false;

	private Map annotationsValues;

	public List getNotReversibleMethods()
	{
		return notReversibleMethods;
	}

	public void addDependency(String dependency)
	{
		dependencies.add(dependency);
	}

	public void setBytecodeArrayI(byte[] bytecode)
	{
		this.bytecode= bytecode;
	}

	public void addNotReversibleMethod(String methodNameSignature)
	{
		notReversibleMethods.add(methodNameSignature);
	}

	public void setAlternativeCompilation(String alternativeCompilation)
	{
		this.alternativeCompilation= alternativeCompilation;
	}

	public void setAnnotations(Map annotationsValues)
	{
		this.annotationsValues= annotationsValues;
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy