com.dragome.compiler.parser.Parser.java Source code

Java tutorial

Introduction

Here is the source code for com.dragome.compiler.parser.Parser.java

Source

/*******************************************************************************
 * 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 <http://www.gnu.org/licenses/>.

package com.dragome.compiler.parser;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

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.generic.ObjectType;
import org.apache.bcel.generic.Type;
import org.apache.commons.io.IOUtils;

import com.dragome.commons.compiler.annotations.CompilerType;
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.FileObject;
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();

        ObjectType type = new ObjectType(jc.getClassName());
        Map<String, String> annotationsValues = getAnnotationsValues(jc.getAttributes());
        TypeDeclaration typeDecl = new TypeDeclaration(type, jc.getAccessFlags(), annotationsValues);
        Project.singleton.addTypeAnnotations(typeDecl);

        fileUnit.isInterface = Modifier.isInterface(typeDecl.getAccess());
        fileUnit.isAbstract = Modifier.isAbstract(typeDecl.getAccess());

        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];

            Attribute[] attributes = method.getAttributes();

            Map<String, String> methodAnnotationsValues = getAnnotationsValues(attributes);

            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;
    }

    private Map<String, String> getAnnotationsValues(Attribute[] attributes) {
        Map<String, String> result = new LinkedHashMap<String, String>();
        for (Attribute attribute : attributes) {
            if (attribute instanceof Annotations) {
                Annotations annotations = (Annotations) attribute;
                AnnotationEntry[] entries = annotations.getAnnotationEntries();
                List<AnnotationEntry> newEntries = new ArrayList<AnnotationEntry>();
                for (AnnotationEntry entry : entries) {
                    if (entry.getElementValuePairs().length == 0)
                        result.put(Type.getType(entry.getAnnotationType()) + "# ", " ");

                    for (int i = 0; i < entry.getElementValuePairs().length; i++) {
                        ElementValuePair elementValuePair = entry.getElementValuePairs()[i];
                        result.put(Type.getType(entry.getAnnotationType()) + "#" + elementValuePair.getNameString(),
                                elementValuePair.getValue().toString());
                    }
                }
            }
        }
        return result;
    }

    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", "<init>", "(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();
    }

    public static void main(String[] args) throws Exception {
        String className = args[0];
        DragomeJsCompiler.compiler = new DragomeJsCompiler(CompilerType.Standard);

        Project.createSingleton(null);
        ClassUnit classUnit = new ClassUnit(Project.singleton, Project.singleton.getSignature(className));
        classUnit.setClassFile(new FileObject(new File(args[1])));
        Parser parser = new Parser(classUnit);
        TypeDeclaration typeDecl = parser.parse();
        DragomeJavaScriptGenerator dragomeJavaScriptGenerator = new DragomeJavaScriptGenerator(Project.singleton);
        dragomeJavaScriptGenerator
                .setOutputStream(new PrintStream(new FileOutputStream(new File("/tmp/webapp.js"))));
        dragomeJavaScriptGenerator.visit(typeDecl);

        FileInputStream fis = new FileInputStream(new File("/tmp/webapp.js"));
        String md5 = org.apache.commons.codec.digest.DigestUtils.md5Hex(fis);
        System.out.println(md5);
    }

}