com.dragome.compiler.parser.Parser Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dragome-bytecode-js-compiler Show documentation
Show all versions of dragome-bytecode-js-compiler Show documentation
Dragome SDK module: bytecode to javascript compiler
/*******************************************************************************
* 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.parser;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.bcel.classfile.AnnotationEntry;
import org.apache.bcel.classfile.Annotations;
import org.apache.bcel.classfile.Attribute;
import org.apache.bcel.classfile.AttributeReader;
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.ConstantClass;
import org.apache.bcel.classfile.ConstantPool;
import org.apache.bcel.classfile.DescendingVisitor;
import org.apache.bcel.classfile.ElementValuePair;
import org.apache.bcel.classfile.EmptyVisitor;
import org.apache.bcel.classfile.Field;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.classfile.ParameterAnnotationEntry;
import org.apache.bcel.generic.ObjectType;
import org.apache.bcel.generic.Type;
import org.apache.commons.io.IOUtils;
import com.dragome.compiler.DragomeJsCompiler;
import com.dragome.compiler.Project;
import com.dragome.compiler.annotations.AnnotationReader;
import com.dragome.compiler.ast.ASTNode;
import com.dragome.compiler.ast.Block;
import com.dragome.compiler.ast.MethodBinding;
import com.dragome.compiler.ast.MethodDeclaration;
import com.dragome.compiler.ast.ReturnStatement;
import com.dragome.compiler.ast.ThrowStatement;
import com.dragome.compiler.ast.TypeDeclaration;
import com.dragome.compiler.ast.VariableDeclaration;
import com.dragome.compiler.exceptions.UnhandledCompilerProblemException;
import com.dragome.compiler.generators.DragomeJavaScriptGenerator;
import com.dragome.compiler.invokedynamic.InvokeDynamicBackporter;
import com.dragome.compiler.type.Signature;
import com.dragome.compiler.units.ClassUnit;
import com.dragome.compiler.utils.Log;
import com.dragome.compiler.utils.Utils;
public class Parser
{
public static String getResourcePath(String name)
{
name= name.replace('.', '/') + ".class";
java.net.URL url= Parser.class.getClassLoader().getResource(name);
if (url == null)
throw new RuntimeException("Resource not found: " + name);
return url.getPath();
}
private JavaClass jc;
private ClassUnit fileUnit;
InvokeDynamicBackporter lambdaUsageBackporter= new InvokeDynamicBackporter();
public Parser(ClassUnit theFileUnit)
{
fileUnit= theFileUnit;
fileUnit.annotations= null;
AttributeReader r= new AnnotationReader(fileUnit);
Attribute.addAttributeReader("RuntimeVisibleAnnotations", r);
try
{
InputStream openInputStream= fileUnit.getClassFile().openInputStream();
String filename= fileUnit.getName();
byte[] originalByteArray= IOUtils.toByteArray(openInputStream);
byte[] transformedArray= originalByteArray;
transformedArray= lambdaUsageBackporter.transform(filename, transformedArray);
if (DragomeJsCompiler.compiler.bytecodeTransformer != null)
if (DragomeJsCompiler.compiler.bytecodeTransformer.requiresTransformation(filename))
transformedArray= DragomeJsCompiler.compiler.bytecodeTransformer.transform(filename, transformedArray);
fileUnit.setBytecodeArrayI(transformedArray);
ClassParser cp= new ClassParser(new ByteArrayInputStream(transformedArray), filename);
jc= cp.parse();
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
public TypeDeclaration parse()
{
DescendingVisitor classWalker= new DescendingVisitor(jc, new EmptyVisitor()
{
public void visitConstantClass(ConstantClass obj)
{
ConstantPool cp= jc.getConstantPool();
String bytes= obj.getBytes(cp);
fileUnit.addDependency(bytes.replace("/", "."));
}
});
classWalker.visit();
org.apache.bcel.classfile.Method[] bcelMethods= jc.getMethods();
org.apache.bcel.classfile.Field[] bcelFields= jc.getFields();
ObjectType type= new ObjectType(jc.getClassName());
Map annotationsValues= getAnnotationsValues(jc.getAttributes(), "::::");
for (Field field : bcelFields)
{
Attribute[] attributes= field.getAttributes();
String name= ":" + field.getName() + ":::";
Map methodAnnotationsValues= getAnnotationsValues(attributes, name);
annotationsValues.putAll(methodAnnotationsValues);
}
for (Method method : bcelMethods)
{
Attribute[] attributes= method.getAttributes();
String name= "::" + method.getName() + ":";
Map methodAnnotationsValues= getAnnotationsValues(attributes, name + ":");
ParameterAnnotationEntry[] parameterAnnotationEntries= method.getParameterAnnotationEntries();
for (int i= 0; i < parameterAnnotationEntries.length; i++)
{
AnnotationEntry[] annotationEntries= parameterAnnotationEntries[i].getAnnotationEntries();
putEntries(name + "arg" + i + ":", annotationsValues, annotationEntries);
}
annotationsValues.putAll(methodAnnotationsValues);
}
TypeDeclaration typeDecl= new TypeDeclaration(type, jc.getAccessFlags(), annotationsValues);
fileUnit.isInterface= Modifier.isInterface(typeDecl.getAccess());
fileUnit.isAbstract= Modifier.isAbstract(typeDecl.getAccess());
Project.singleton.addTypeAnnotations(typeDecl);
fileUnit.setAnnotations(annotationsValues);
if (!type.getClassName().equals("java.lang.Object"))
{
ObjectType superType= new ObjectType(jc.getSuperclassName());
typeDecl.setSuperType(superType);
ClassUnit superUnit= Project.getSingleton().getOrCreateClassUnit(superType.getClassName());
fileUnit.setSuperUnit(superUnit);
String[] interfaceNames= jc.getInterfaceNames();
for (int i= 0; i < interfaceNames.length; i++)
{
ObjectType interfaceType= new ObjectType(interfaceNames[i]);
ClassUnit interfaceUnit= Project.getSingleton().getOrCreateClassUnit(interfaceType.getClassName());
fileUnit.addInterface(interfaceUnit);
}
}
Field[] fields= jc.getFields();
for (int i= 0; i < fields.length; i++)
{
Field field= fields[i];
VariableDeclaration variableDecl= new VariableDeclaration(VariableDeclaration.NON_LOCAL);
variableDecl.setName(field.getName());
variableDecl.setModifiers(field.getModifiers());
variableDecl.setType(field.getType());
typeDecl.addField(variableDecl);
}
for (int i= 0; i < bcelMethods.length; i++)
{
Method method= bcelMethods[i];
Map methodAnnotationsValues= null;
try
{
methodAnnotationsValues= checkSuperAnnotations(method, jc, "MethodAlias", 0, 4);
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
}
MethodBinding binding= MethodBinding.lookup(jc.getClassName(), method.getName(), method.getSignature());
String genericSignature= method.getGenericSignature();
if (genericSignature != null && !genericSignature.equals(method.getSignature()))
{
Signature signature= Project.getSingleton().getSignature(binding.toString()).relative();
String normalizedSignature= DragomeJavaScriptGenerator.normalizeExpression(signature);
String normalizedClassname= DragomeJavaScriptGenerator.normalizeExpression(type.getClassName());
Project.getSingleton().addGenericSignature(normalizedClassname + "|" + normalizedSignature + "|" + genericSignature);
// System.out.println(genericSignature);
}
if (DragomeJsCompiler.compiler.getSingleEntryPoint() != null)
{
Signature signature= Project.getSingleton().getSignature(binding.toString());
String singleSignature= DragomeJsCompiler.compiler.getSingleEntryPoint();
if (!signature.toString().equals(singleSignature))
continue;
}
MethodDeclaration methodDecl= new MethodDeclaration(binding, method.getAccessFlags(), method.getCode(), methodAnnotationsValues);
typeDecl.addMethod(methodDecl);
parseMethod(typeDecl, methodDecl, method);
}
return typeDecl;
}
// Recursive algorithm to check for annotations. If the super method has the annotation it will use it. if the child has it, it will skip the super method.
private Map checkSuperAnnotations(final Method method, JavaClass curClass, final String annotationName, int nDepth, final int maxRecursive) throws ClassNotFoundException
{
String methodName= method.getName();
nDepth++;
Map curAnnotationsValues= null;
Method curMethod= null;
Method[] methods= curClass.getMethods();
for (int j= 0; j < methods.length; j++)
{ // find the method if there is one.
if (methods[j].getName().equals(methodName) && methods[j].getArgumentTypes().length == method.getArgumentTypes().length)
{
curMethod= methods[j];
break;
}
}
if (curMethod != null)
{
Attribute[] attributes= curMethod.getAttributes();
curAnnotationsValues= getAnnotationsValues(attributes, "");
Set> entrySet= curAnnotationsValues.entrySet();
Iterator> iterator= entrySet.iterator();
while (iterator.hasNext())
{
Entry next= iterator.next();
String key= next.getKey();
if (key.contains(annotationName))
return curAnnotationsValues; // if contains the annotation already skip checking the super methods.
}
}
else
curAnnotationsValues= new LinkedHashMap();
if (nDepth >= maxRecursive)
return curAnnotationsValues;
JavaClass[] interfaces= curClass.getInterfaces();
for (int i= 0; i < interfaces.length; i++)
{ // check interfaces
JavaClass javaClass= interfaces[i];
Map returnedAnnotation= checkSuperAnnotations(method, javaClass, annotationName, nDepth, maxRecursive);
mergeAnno(curAnnotationsValues, returnedAnnotation, annotationName);
}
JavaClass superClass= curClass.getSuperClass(); // check super class
if (superClass != null && superClass.getClassName().contains("java.lang.Object") == false)
{ // stop checking super when It detects root java object.
Map returnedAnnotation= checkSuperAnnotations(method, superClass, annotationName, nDepth, maxRecursive);
mergeAnno(curAnnotationsValues, returnedAnnotation, annotationName);
}
return curAnnotationsValues;
}
private void mergeAnno(Map curAnnotationsValues, Map returnedAnnotation, String annoationName)
{
Set> entrySet= returnedAnnotation.entrySet();
Iterator> iterator= entrySet.iterator();
while (iterator.hasNext())
{
Entry next= iterator.next();
String key= next.getKey();
String value= next.getValue();
if (key.contains(annoationName))
{
boolean containsKey= curAnnotationsValues.containsKey(key);
if (containsKey == false)
curAnnotationsValues.put(key, value);
}
}
}
private Map getAnnotationsValues(Attribute[] attributes, String prefix)
{
Map result= new LinkedHashMap();
for (Attribute attribute : attributes)
{
if (attribute instanceof Annotations)
{
Annotations annotations= (Annotations) attribute;
AnnotationEntry[] entries= annotations.getAnnotationEntries();
List newEntries= new ArrayList();
putEntries(prefix, result, entries);
}
}
return result;
}
private void putEntries(String prefix, Map result, AnnotationEntry[] entries)
{
for (AnnotationEntry entry : entries)
{
String key= Type.getType(entry.getAnnotationType()) + "#" + prefix;
if (entry.getElementValuePairs().length == 0)
result.put(key, " ");
for (int i= 0; i < entry.getElementValuePairs().length; i++)
{
ElementValuePair elementValuePair= entry.getElementValuePairs()[i];
result.put(key + elementValuePair.getNameString(), elementValuePair.getValue().toString());
}
}
}
public void parseMethod(TypeDeclaration typeDecl, MethodDeclaration methodDecl, Method method)
{
Type[] types= method.getArgumentTypes();
int offset;
if (Modifier.isStatic(methodDecl.getAccess()))
{
offset= 0;
}
else
{
offset= 1;
}
for (int i= 0; i < types.length; i++)
{
VariableDeclaration variableDecl= new VariableDeclaration(VariableDeclaration.LOCAL_PARAMETER);
variableDecl.setName(VariableDeclaration.getLocalVariableName(method, offset, 0));
variableDecl.setType(types[i]);
methodDecl.addParameter(variableDecl);
offset+= types[i].getSize();
}
if (methodDecl.getCode() == null)
return;
Log.getLogger().debug("Parsing " + methodDecl.toString());
Pass1 pass1= new Pass1(jc);
try
{
pass1.parse(method, methodDecl);
}
catch (Throwable ex)
{
if (ex instanceof UnhandledCompilerProblemException)
{
Pass1.setClassNotReversible(methodDecl);
}
else
{
ASTNode node= null;
if (ex instanceof ParseException)
{
node= ((ParseException) ex).getAstNode();
}
else
{
node= Pass1.getCurrentNode();
}
if (DragomeJsCompiler.compiler.isFailOnError())
{
throw Utils.generateException(ex, methodDecl, node);
}
else
{
fileUnit.addNotReversibleMethod(Pass1.extractMethodNameSignature(methodDecl.getMethodBinding()));
//String msg= Utils.generateExceptionMessage(methodDecl, node);
//DragomeJsCompiler.errorCount++;
// Log.getLogger().error(msg + "\n" + Utils.stackTraceToString(ex));
}
}
Block body= new Block();
ThrowStatement throwStmt= new ThrowStatement();
/*
MethodBinding binding= MethodBinding.lookup("java.lang.RuntimeException", "", "(java/lang/String)V;");
ClassInstanceCreation cic= new ClassInstanceCreation(methodDecl, binding);
cic.addArgument(new StringLiteral("Unresolved decompilation problem"));
throwStmt.setExpression(cic);
body.appendChild(throwStmt);*/
methodDecl.setBody(body);
}
if (DragomeJsCompiler.compiler.optimize && methodDecl.getBody().getLastChild() instanceof ReturnStatement)
{
ReturnStatement ret= (ReturnStatement) methodDecl.getBody().getLastChild();
if (ret.getExpression() == null)
{
methodDecl.getBody().removeChild(ret);
}
}
// Pass1.dump(methodDecl.getBody(), "Body of " + methodDecl.toString());
return;
}
public ConstantPool getConstantPool()
{
return jc.getConstantPool();
}
public String toString()
{
return jc.getClassName();
}
}