amulet.translator.runtimecheck.CExtractor.java Source code

Java tutorial

Introduction

Here is the source code for amulet.translator.runtimecheck.CExtractor.java

Source

/*
 *
 *
 * Copyright 2016 by the Trustees of Dartmouth College and Clemson University, and
 * distributed under the terms of the "Dartmouth College Non-Exclusive Research Use
 * Source Code License Agreement" (for NON-COMMERCIAL research purposes only), as
 * detailed in a file named LICENSE.pdf within this repository.
 */

package amulet.translator.runtimecheck;

import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.*;
import org.stringtemplate.v4.*;

import java.util.*;
import java.io.InputStream;
import java.io.ByteArrayInputStream;

import amulet.qm.QMClass;
import amulet.resourceprofiler.Resource;
import amulet.resourceprofiler.Resource.ComputationType;
import amulet.resourceprofiler.ResourceProfiler;
import amulet.resourceprofiler.Resource.ResourceType;
import amulet.translator.CParser;
import amulet.translator.CLexer;
import amulet.translator.CBaseListener;
import amulet.translator.CParser.DeclarationContext;
import amulet.translator.CParser.ExpressionContext;

public class CExtractor extends CBaseListener {
    TokenStreamRewriter rewriter;
    STGroup templateGroup;
    QMClass qmclass;
    TokenStream tokens;
    CParser parser;
    String appId;
    String appLogId;
    String togglePinCode = "P1OUT |= BIT2; // on\n{volatile uint8_t i=0;\nfor(i;i<200;i++);}\nP1OUT &= (~BIT2); // off";
    boolean togglePins = false;
    Vector<String> arrays;
    Vector<String> global_arrays;
    ResourceProfiler resourceProfiler;

    public CExtractor(QMClass qmclass, TokenStream tokens, CParser parser, boolean toggle,
            Vector<String> global_arrays, ResourceProfiler resourceProfiler) {
        rewriter = new TokenStreamRewriter(tokens);
        this.templateGroup = new STGroupFile("./amulet/translator/runtimecheck/CTemplates.stg");
        this.qmclass = qmclass;
        this.tokens = tokens;
        this.parser = parser;
        this.appId = qmclass.getName().toUpperCase() + "_APP";
        this.appLogId = appId + "_LOG";
        this.togglePins = toggle;
        this.arrays = global_arrays;
        this.global_arrays = global_arrays;
        this.resourceProfiler = resourceProfiler;
    }

    ////Listener methods for translating the AST during the walk of the parse tree.

    public void enterFunctionCall(CParser.FunctionCallContext ctx) {
        if (ctx == null || ctx.Identifier() == null)
            return;

        String func_name = ctx.Identifier().getText();

        // Determine if function call corresponds to custom Amulet API or not.
        boolean isApiCall = func_name.startsWith("Amulet");

        // Track the "type" of resource which is being used in this function call.
        ResourceType observedResourceType = null;

        if (isApiCall) {
            /* 
             * Construct the API Call object which may contain:
             * - apiDef (required)
             * - appLogId (optional; required for Log operations though)
             * - arguments (optional)
             * - appId (required)
             *
             * If there is a need to add a new handler for checking/modifying 
             * function calls during translation, add a method structured as 
             * below (prefixed with "handler_" followed by a descriptive name 
             * for what the handler is checking/handling). The "isOtherCall" 
             * methods should be the last two "catch-all" handlers to handle 
             * function calls in Amulet apps generally. 
             *
             * See: CTemplates.stg for more details relating to existing templates. 
             */
            ST apiCall = null;

            /*
             * Custom handlers. Please put any new handlers among this "group"
             * of handlers. 
             */
            if ((apiCall = handler_isAmuletStateTransitionCall(ctx)) != null) {
                // handled
                observedResourceType = ResourceType.AMULET_API_FUNCTION_CALL;
            } else if ((apiCall = handler_isAmuletLogCall(ctx)) != null) {
                // handled
                observedResourceType = ResourceType.AMULET_API_FUNCTION_CALL;
            } else if ((apiCall = handler_isAmuletSubscribeCall(ctx)) != null) {
                // handled
                observedResourceType = ResourceType.AMULET_API_FUNCTION_CALL;
            } else if ((apiCall = handler_isAmuletTimerCall(ctx)) != null) {
                // handled
                observedResourceType = ResourceType.AMULET_API_FUNCTION_CALL;
            } else if ((apiCall = handler_isAmuletRectCall(ctx)) != null) {
                // handled -- intentionally leave empty so observedResourceType == null
                // and we won't double-add the resource below (it was already added 
                // in the handler).
            }
            /*
             * Catch-all handlers. Please put new handlers ABOVE (i.e., before calling
             * handler_isOtherCallWithArguments() and handler_isOtherCallWithNoArguments())
             */
            else if ((apiCall = handler_isOtherCallWithArguments(ctx)) != null) {
                // handled
                observedResourceType = ResourceType.AMULET_API_FUNCTION_CALL;
            } else {
                apiCall = handler_isOtherCallWithNoArguments(ctx);
                observedResourceType = ResourceType.AMULET_API_FUNCTION_CALL;
            }

            // API CALL: add function caller ID (App ID) to the API call.
            apiCall.add("appId", this.appId);

            // API CALL: PIN handling (before)
            if (togglePins) {
                rewriter.insertBefore(ctx.getStart(), "\n" + togglePinCode + "\n");
            }

            // API CALL: re-write the API call
            rewriter.replace(ctx.getStart(), ctx.getStop(), apiCall.render());

            // API CALL: PIN handling (after)
            if (togglePins) {
                rewriter.insertAfter(ctx.getStop(), ";\n" + togglePinCode);
            }
        } else {
            CParser.ArgumentExpressionListContext argCtx;
            CParser.AssignmentExpressionContext assignCtx;

            // Function call is *not* an API call. Add .values to all array arguments
            observedResourceType = ResourceType.NON_AMULET_API_FUNCTION_CALL;
            argCtx = ctx.argumentExpressionList();
            if (argCtx != null) {
                while (argCtx != null && argCtx.assignmentExpression() != null) {
                    assignCtx = argCtx.assignmentExpression();
                    if (arrays.contains(assignCtx.getText())) {
                        rewriter.insertAfter(assignCtx.getStop(), ".values");
                    }
                    argCtx = argCtx.argumentExpressionList();
                }
            }
        }

        // Add any observed resources (e.g., Non-Amulet API/Amulet API function call, Sensor Subscription).
        //      System.out.println("DEBUG enterFunctionCall()::"+ ctx.getText() + " [func=" + func_name + ", isApiCall=" + isApiCall + ", observerdResType=" + observedResourceType + "]");
        if ((resourceProfiler != null) && (observedResourceType != null)) {
            Resource record = new Resource(func_name, qmclass.getName(), observedResourceType, 0.0);
            resourceProfiler.add(record);
        }
    }

    ///////////////////////  FUNCTION CALL HANDLERS  ///////////////////////

    /** 
     * API CALL HANDLER: Given a call to use the AmuletStateTransition, 
     * insert QP specific information (e.g., the Active Object) and Amulet audit 
     * information (e.g., the App's ID) automatically. Also, verify that the signal 
     * the app invokes is valid -- if it is, translate it in accordance with 
     * our rules (i.e., prefix the signal with the name of the app).  
     */
    private ST handler_isAmuletStateTransitionCall(CParser.FunctionCallContext ctx) {
        String func_name = ctx.Identifier().getText();

        if (func_name.equals("AmuletStateTransition")) {
            ST apiCall = templateGroup.getInstanceOf("apiCallStateTransition");
            apiCall.add("apiDef", ctx.Identifier().getText());
            apiCall.add("appName", qmclass.getName());

            // Verify that there is exactly one signal specified and that the 
            // caller has the necessary permission/scope to invoke this signal. 
            int nArgs = 0;
            if (ctx.argumentExpressionList() != null)
                nArgs = ctx.argumentExpressionList().getText().split(",").length;

            if (nArgs < 1) {
                apiCall.add("sig", "SIG_ERROR_TOO_FEW_ARGS");
            } else if (nArgs > 1) {
                apiCall.add("sig", "SIG_ERROR_TOO_MANY_ARGS");
            } else { // ctx.argumentExpressionList().size() == 1

                /*
                 * NOTE: AFT assumes that signal call has "_SIG" suffix.
                 */

                // Extract the "raw" signal argument.
                String sig = ctx.argumentExpressionList().getText();

                // Trim off suffix "_SIG" for upcoming comparison.
                String cmpSig = sig.substring(0, sig.lastIndexOf('_'));

                // Check that this signal is within this app's "scope"
                if (qmclass.getSignals().contains(cmpSig)) {
                    apiCall.add("sig", sig);
                } else {
                    apiCall.add("sig", "SIG_ERROR_INVALID_ARG_" + sig);
                }
            }

            // Signify to the caller that this case was handled.
            return apiCall;
        }

        return null;
    }

    /** 
     * API CALL HANDLER: Given a call to use the Amulet Log, insert the app's
     * App ID and Log ID automatically. 
     */
    private ST handler_isAmuletLogCall(CParser.FunctionCallContext ctx) {
        String func_name = ctx.Identifier().getText();

        if ((ctx.argumentExpressionList() != null) && func_name.contains("Log")) {
            // API Log Call w/ Args
            ST apiCall = templateGroup.getInstanceOf("apiLogCallWithArgs");
            apiCall.add("apiDef", ctx.Identifier().getText());
            apiCall.add("appLogId", this.appLogId);
            apiCall.add("arguments", ctx.argumentExpressionList().getText());

            // Signify to the caller that this case was handled.
            return apiCall;
        }

        return null;
    }

    /**
     * API CALL HANDLER: Record the resources that are requested by applications 
     * in order to identify which resources are needed and which ones are not 
     * when we go to produce the resulting firmware image. Knowing this information 
     * can allow us to only include necessary code in the firmware image and 
     * can greatly reduce the size of the image in the event that certain 
     * resources are not needed for a particular build.
     */
    private ST handler_isAmuletTimerCall(CParser.FunctionCallContext ctx) {
        String func_name = ctx.Identifier().getText();

        if ((ctx.argumentExpressionList() != null) && func_name.contains("AmuletTimer")) {
            ST apiCall = templateGroup.getInstanceOf("apiCallWithArgs");
            apiCall.add("apiDef", ctx.Identifier().getText());
            apiCall.add("arguments", ctx.argumentExpressionList().getText());

            String timerDelayInSeconds = ctx.argumentExpressionList().getText().split(",")[0];
            Resource record = new Resource(func_name, qmclass.getName(), ResourceType.TIMER_SUBSCRIPTION, 0.0);
            record.addExtra(Resource.EXTRA_TIMER_DELAY_IN_SECONDS, timerDelayInSeconds);

            // Add the detected resource request to the list of requested resources.
            if (resourceProfiler != null) {
                resourceProfiler.add(record);
            }

            // Signify to the caller that this case was handled.
            return apiCall;
        }

        return null;
    }

    /**
     * API CALL HANDLER: Record the resources that are requested by applications 
     * in order to identify which resources are needed and which ones are not 
     * when we go to produce the resulting firmware image. Knowing this information 
     * can allow us to only include necessary code in the firmware image and 
     * can greatly reduce the size of the image in the event that certain 
     * resources are not needed for a particular build.
     */
    private ST handler_isAmuletSubscribeCall(CParser.FunctionCallContext ctx) {
        String func_name = ctx.Identifier().getText();

        if ((ctx.argumentExpressionList() != null) && func_name.contains("AmuletSubscribe")) {
            ST apiCall = templateGroup.getInstanceOf("apiCallWithArgs");
            apiCall.add("apiDef", ctx.Identifier().getText());
            apiCall.add("arguments", ctx.argumentExpressionList().getText());

            String resource = ctx.argumentExpressionList().getText().split(",")[0];
            Resource record = new Resource(resource, qmclass.getName(), ResourceType.SENSOR_SUBSCRIPTION, 0.0);

            // Add the detected resource request to the list of requested resources.
            if (resourceProfiler != null) {
                resourceProfiler.add(record);
                //            resourceList.add( record );
            }

            // Signify to the caller that this case was handled.
            return apiCall;
        }

        return null;
    }

    /**
     * API CALL HANDLER: Special case: we need to be able to scale the 
     * cost of rectangle-fillinf related calls because they are more expensive 
     * as they handle larger rectangles. 
     *
     * This could also be applied to drawing lines if we want more precision
     */
    private ST handler_isAmuletRectCall(CParser.FunctionCallContext ctx) { // TODO: JOSIAH: ADD A NEW FUNCTION LIKE THIS FOR OTHER HANDLERS (AS NEEDED)
        String func_name = ctx.Identifier().getText();

        if ((ctx.argumentExpressionList() != null)
                && (func_name.contains("ClearRect") || func_name.contains("FillRect"))) {
            // Setup the ST for the caller -- we won't tweak args or anything here
            // so this is pretty trivial. 
            ST apiCall = templateGroup.getInstanceOf("apiCallWithArgs");
            apiCall.add("apiDef", ctx.Identifier().getText());
            apiCall.add("arguments", ctx.argumentExpressionList().getText());

            // Parse out relevant args passed to this function call. 
            String[] args = ctx.argumentExpressionList().getText().split(",");
            String scaleFactor = args[0];
            String x = args[0];
            String y = args[1];
            String w = args[2];
            String h = args[3];

            String[] t = new String[] { "BOLD_FONT_SIZE", "MEDIUM_FONT_SIZE", "REGULAR_FONT_SIZE",
                    "LCD_HORIZONTAL_MAX", "LCD_VERTICAL_MAX" };
            int[] vals = new int[] { 22, 16, 12, 127, 127 };
            for (int i = 0; i < t.length; i++) {
                w = w.replace(t[i], Integer.toString(vals[i]));
                h = h.replace(t[i], Integer.toString(vals[i]));
            }

            // JAVA is stupid
            try {
                Integer.parseInt(w);
            } catch (Exception e) {
                w = "127";
            }
            try {
                Integer.parseInt(h);
            } catch (Exception e) {
                h = "40";
            }
            double rectsize = Integer.parseInt(w) * Integer.parseInt(h);

            // NOTE: IF you set the cost to be non-zero, the ARP will use *this value* to 
            //       scale the actual energy cost computed from the energy params from the device profile. 
            Resource record = new Resource(func_name, qmclass.getName(), ResourceType.AMULET_API_FUNCTION_CALL,
                    1.0);
            record.addExtra("width", w);
            record.addExtra("height", h);
            // Add the detected resource request to the list of requested resources.
            if (resourceProfiler != null) {
                resourceProfiler.add(record);
            }

            // Signify to the caller that this case was handled.
            return apiCall;
        }
        return null;
    }

    /**
     * API CALL HANDLER: Generic handler for doing basic translation of app
     * calls to the Amulet API. This handler specifically works for calls to 
     * functions defined in the Amulet API that -have- arguments.
     *
     * Currently, this handler exists to automatically insert the App ID of the 
     * caller as the final parameter to the function call.
     */
    private ST handler_isOtherCallWithArguments(CParser.FunctionCallContext ctx) {
        if (ctx.argumentExpressionList() != null) {
            ST apiCall = templateGroup.getInstanceOf("apiCallWithArgs");
            apiCall.add("apiDef", ctx.Identifier().getText());
            apiCall.add("arguments", ctx.argumentExpressionList().getText());
            return apiCall;
        }

        return null;
    }

    /**
     * API CALL HANDLER: Generic handler for doing basic translation of app
     * calls to the Amulet API. This handler specifically works for calls to 
     * functions defined in the Amulet API that -don't have- arguments.
     *
     * Currently, this handler exists to automatically insert the App ID of the 
     * caller as the final parameter to the function call.
     */
    private ST handler_isOtherCallWithNoArguments(CParser.FunctionCallContext ctx) {
        if (ctx.argumentExpressionList() == null) {
            ST apiCall = templateGroup.getInstanceOf("apiCallWithoutArgs");
            apiCall.add("apiDef", ctx.Identifier().getText());
            return apiCall;
        }

        return null;
    }

    ////////////////////////////////////////////////////////////////////////

    @Override
    public void enterParameterDeclaration(CParser.ParameterDeclarationContext ctx) {
        CParser.AbstractDeclaratorContext abstractCtx = ctx.abstractDeclarator();
        System.out.println("DEBUG enterParameterDeclaration():: " + abstractCtx.getText());
        CParser.DeclaratorContext declCtx = ctx.declarator();
        CParser.DeclarationSpecifiers2Context declSpec2Ctx;
        CParser.DeclarationSpecifiersContext declSpecCtx;
        List<CParser.DeclarationSpecifierContext> declSpecListCtx = null;
        CParser.TypeSpecifierContext typeSpecCtx = null;
        TerminalNode arrayDeclNode = null;
        //Check if array declaration or not
        if (abstractCtx != null && abstractCtx.getText().indexOf("[") != -1
                && abstractCtx.getText().indexOf("]") != -1) {
            declSpec2Ctx = ctx.declarationSpecifiers2();
            arrayDeclNode = abstractCtx.directAbstractDeclarator().arrayDeclarator().Identifier();
            declSpecListCtx = declSpec2Ctx.declarationSpecifier();
            //rewriter.replace(ctx.getStart(),ctx.getStop(),"__"+declSpec2Ctx.getText()+"_array "+arrayDeclNode.getText());
        } else if (declCtx != null && declCtx.getText().indexOf("[") != -1
                && declCtx.getText().indexOf("]") != -1) {
            declSpecCtx = ctx.declarationSpecifiers();
            arrayDeclNode = declCtx.directDeclarator().directDeclarator().Identifier();
            declSpecListCtx = declSpecCtx.declarationSpecifier();
            //rewriter.replace(ctx.getStart(),ctx.getStop(),"__"+declSpecCtx.getText()+"_array "+arrayDeclNode.getText());
        }
        if (declSpecListCtx != null) {
            for (CParser.DeclarationSpecifierContext declSpCtx : declSpecListCtx) {
                if (declSpCtx.typeSpecifier() != null) {
                    typeSpecCtx = declSpCtx.typeSpecifier();
                    break;
                }
            }
        }
        if (typeSpecCtx != null && arrayDeclNode != null) {
            rewriter.replace(ctx.getStart(), ctx.getStop(),
                    "__" + typeSpecCtx.getText() + "_array " + arrayDeclNode.getText());
        }
    }

    @Override
    public void enterExpressionStatement(CParser.ExpressionStatementContext exprStctx) {
        /*
         * Update "basic block" count in For-loop Context.
         *  + Rule: any expression without an amulet API function call.
         *  
         * NOTE: Right now we are trying to guard against double counting Amulet API function 
         * calls so we do some primitive checking of the line of code for indicators that 
         * this expression includes a call to such a function. 
         * 
         * TODO: we should make a better way of checking if there really is a call to an 
         * amulet API function...
         */
        //      System.out.println("DEBUG>>>>>>>>>>enterExpressionStatement: " + exprStctx.getText() + " (" + resourceProfiler.loopContext.isForLoopContext() + ")");
        if (resourceProfiler != null && resourceProfiler.loopContext.isForLoopContext()) {
            if (!exprStctx.getText().contains("Amulet")
                    || (!exprStctx.getText().contains("(") && !exprStctx.getText().contains(")"))) {
                resourceProfiler.incNumberStatementsInLoop();
                //            System.out.println("     LOOP INCLUDE!");
            }
        }
        /*
         * Update "basic block" count in QM Context (e.g., state, transition).
         *  + Rule: any expression without an amulet API function call.
         */
        else if (resourceProfiler != null && !resourceProfiler.loopContext.isForLoopContext()) {
            if (!exprStctx.getText().contains("Amulet")
                    || (!exprStctx.getText().contains("(") && !exprStctx.getText().contains(")"))) {
                resourceProfiler.incNumLinesOfCode();
                //            System.out.println("     QM INCLUDE!");
            }
        }

        if (exprStctx.expression().assignmentExpression().leftExpression() != null
                || exprStctx.expression().assignmentExpression().rightExpression() != null) {
            //Enter only if the expression statement is assignment expression with left and right hand sides
            CParser.AssignmentExpressionContext ctx = exprStctx.expression().assignmentExpression();
            CParser.LeftExpressionContext ctxLeft = ctx.leftExpression();
            CParser.RightExpressionContext ctxRight = ctx.rightExpression();
            CParser.AssignmentOperatorContext ctxAssignOp = ctx.assignmentOperator();
            CParser.CastExpressionContext ctxLeftCast = ctxLeft.conditionalExpression().logicalOrExpression()
                    .logicalAndExpression().inclusiveOrExpression().exclusiveOrExpression().andExpression()
                    .equalityExpression().relationalExpression().shiftExpression().additiveExpression()
                    .multiplicativeExpression().castExpression();
            //         CParser.CastExpressionContext ctxRightCast = ctxRight.conditionalExpression().logicalOrExpression().logicalAndExpression().inclusiveOrExpression().exclusiveOrExpression().andExpression().equalityExpression().relationalExpression().shiftExpression().additiveExpression().multiplicativeExpression().castExpression();

            //////////////////////////////////////////////////////////////////////////
            //         System.out.println("            + left::"+ctxLeft.getText());
            //         System.out.println("            + op::"+ctxAssignOp.getText());
            //         System.out.println("            + right::"+ctxRight.getText());
            //////////////////////////////////////////////////////////////////////////

            if (ctxLeft.getText().indexOf("[") != -1 && ctxLeft.getText().indexOf("]") != -1) {
                //Array assignment statement (i.e., assign INTO array index)...
                ST arrayAssignment = templateGroup.getInstanceOf("arrayAssignment");
                arrayAssignment.add("arrayName",
                        ctxLeftCast.unaryExpression().postfixExpression().postfixExpression().getText());
                arrayAssignment.add("arrayIndex",
                        ctxLeftCast.unaryExpression().postfixExpression().expression().getText());
                arrayAssignment.add("assignmentOp", ctxAssignOp.getText());
                if (ctxRight.getText().indexOf("[") != -1 && ctxRight.getText().indexOf("]") != -1) {
                    //... array assignment statement with AND array access

                    /*ST arrayAssignmentWithArrayAccess = templateGroup.getInstanceOf("arrayAssignmentWithArrayAccess");
                    arrayAssignmentWithArrayAccess.add("arrayName",ctxLeftCast.unaryExpression().postfixExpression().postfixExpression().getText());
                    arrayAssignmentWithArrayAccess.add("arrayIndex",ctxLeftCast.unaryExpression().postfixExpression().expression().getText());
                    arrayAssignmentWithArrayAccess.add("assignmentOp",ctxAssignOp.getText());
                    arrayAssignmentWithArrayAccess.add("arrayToBeRead",ctxRightCast.unaryExpression().postfixExpression().postfixExpression().getText());
                    arrayAssignmentWithArrayAccess.add("arrayReadIndex",ctxRightCast.unaryExpression().postfixExpression().expression().getText());
                    rewriter.insertAfter(exprStctx.getStop(),arrayAssignmentWithArrayAccess.render());*/

                    InputStream is = new ByteArrayInputStream(ctxRight.getText().getBytes());
                    ANTLRInputStream input;
                    try {
                        input = new ANTLRInputStream(is);
                    } catch (Exception e) {
                        return;
                    }
                    CLexer lexer = new CLexer(input);
                    CommonTokenStream tokens = new CommonTokenStream(lexer);
                    CParser parser = new CParser(tokens);
                    ParseTree tree = parser.expression(); // parse

                    ParseTreeWalker walker = new ParseTreeWalker(); // create standard walker
                    CExtractor extractor = new CExtractor(qmclass, tokens, parser, togglePins, global_arrays,
                            resourceProfiler);
                    walker.walk(extractor, tree); // initiate walk of tree with listener

                    arrayAssignment.add("value", extractor.rewriter.getText());
                } else {
                    //... only array assignment statement (and NO array access)

                    /*ST arrayAssignment = templateGroup.getInstanceOf("arrayAssignment");
                    arrayAssignment.add("arrayName",ctxLeftCast.unaryExpression().postfixExpression().postfixExpression().getText());
                    arrayAssignment.add("arrayIndex",ctxLeftCast.unaryExpression().postfixExpression().expression().getText());*/
                    arrayAssignment.add("value", ctxRight.getText());
                    /*arrayAssignment.add("assignmentOp",ctxAssignOp.getText());
                    rewriter.insertAfter(exprStctx.getStop(),arrayAssignment.render());*/
                }
                rewriter.insertAfter(exprStctx.getStop(), arrayAssignment.render());
            } else if (ctxRight.getText().indexOf("[") != -1 && ctxRight.getText().indexOf("]") != -1) {
                //Assignment statement from array accessed index.

                /*ST arrayAccess = templateGroup.getInstanceOf("arrayAccess");
                arrayAccess.add("arrayName",ctxRightCast.unaryExpression().postfixExpression().postfixExpression().getText());
                arrayAccess.add("arrayIndex",ctxRightCast.unaryExpression().postfixExpression().expression().getText());
                rewriter.insertAfter(exprStctx.getStop(),ctxLeft.getText()+ctxAssignOp.getText()+arrayAccess.render()+";");*/
            } else {
                // Other Assignment (i.e., assign to non-array variable AND NO array access).

                // Add (*ONLY*) assignment statements that assign int values to a variable to the ResourceProfiler's intVarResolver.
                try {
                    Integer.parseInt(ctxRight.getText());
                    resourceProfiler.setResolverIntVar(ctxLeft.getText(), ctxRight.getText());
                } catch (NumberFormatException nfe) {
                    //pass
                    //               System.out.println("WARNING: right val not an int value::" + ctxRight.getText());
                }
            }
        }
    }

    @Override
    public void exitExpressionStatement(CParser.ExpressionStatementContext exprStctx) {
        if (exprStctx.expression().assignmentExpression().leftExpression() != null
                || exprStctx.expression().assignmentExpression().rightExpression() != null) {
            //Enter only if he expression statement is assignment expression with left and right hand sides
            CParser.AssignmentExpressionContext ctx = exprStctx.expression().assignmentExpression();
            CParser.LeftExpressionContext ctxLeft = ctx.leftExpression();
            CParser.RightExpressionContext ctxRight = ctx.rightExpression();
            //         CParser.AssignmentOperatorContext ctxAssignOp = ctx.assignmentOperator();
            if (ctxLeft.getText().indexOf("[") != -1 && ctxLeft.getText().indexOf("]") != -1) {
                //Array assignment is being done
                if (ctxRight.getText().indexOf("[") != -1 && ctxRight.getText().indexOf("]") != -1) {
                    //Array assignment and array access
                    rewriter.delete(exprStctx.getStart(), exprStctx.getStop());
                } else {
                    //Just array assignment
                    rewriter.delete(exprStctx.getStart(), exprStctx.getStop());
                }
            } else if (ctxRight.getText().indexOf("[") != -1 && ctxRight.getText().indexOf("]") != -1) {
                //Just array access
                //rewriter.delete(exprStctx.getStart(), exprStctx.getStop());
            }
        }
    }

    @Override
    public void enterDeclaration(CParser.DeclarationContext ctx) {
        /*
         * Update "basic block" count in For-loop Context.
         *  + Rule: any declaration without an amulet API function call.
         *  
         * NOTE: Right now we are trying to guard against double counting Amulet API function 
         * calls so we do some primitive checking of the line of code for indicators that 
         * this expression includes a call to such a function. 
         * 
         * TODO: we should make a better way of checking if there really is a call to an 
         * amulet API function...
         */
        //      System.out.println("DEBUG>>>>>>>>>>enterDeclaration: " + ctx.getText() + " (" + resourceProfiler.loopContext.isForLoopContext() + ")");
        if (resourceProfiler != null && resourceProfiler.loopContext.isForLoopContext()) {
            if (!ctx.getText().contains("Amulet")
                    || (!ctx.getText().contains("(") && !ctx.getText().contains(")"))) {
                if (resourceProfiler.loopContext.isForLoopContext()) {
                    resourceProfiler.incNumberStatementsInLoop();
                    //               System.out.println("     LOOP INCLUDE!");
                }
            }
        }
        /*
         * Update "basic block" count in QM Context (e.g., state, transition).
         *  + Rule: any declaration without a function call.
         */
        else if (resourceProfiler != null && !resourceProfiler.loopContext.isForLoopContext()) {
            if (!ctx.getText().contains("Amulet")
                    || (!ctx.getText().contains("(") && !ctx.getText().contains(")"))) {
                // Rule: any expression with an assignment statement.
                resourceProfiler.incNumLinesOfCode();
                //            System.out.println("     QM INCLUDE!");
            }
        }

        if (ctx.declarationSpecifiers().getText().indexOf("free") != -1 && ctx.initDeclaratorList() != null) {
            CParser.DeclaratorContext declaratorCtx = ctx.initDeclaratorList().initDeclarator().declarator();
            if (declaratorCtx != null && declaratorCtx.getText().indexOf("[") != -1
                    && declaratorCtx.getText().indexOf("]") != -1) {
                //Enter only if it has array declaration
                declaratorCtx = declaratorCtx.directDeclarator().declarator();
                rewriter.replace(ctx.getStart(), ctx.getStop(),
                        "free(" + declaratorCtx.directDeclarator().directDeclarator().getText() + ".values["
                                + declaratorCtx.directDeclarator().assignmentExpression().getText() + "]);");
            }

        } else if (ctx.initDeclaratorList() != null) {
            CParser.DeclaratorContext declaratorCtx = ctx.initDeclaratorList().initDeclarator().declarator();
            //Enter only if it has array declaration
            if (declaratorCtx != null && declaratorCtx.getText().indexOf("[") != -1
                    && declaratorCtx.getText().indexOf("]") != -1) {
                Vector<CParser.InitDeclaratorContext> arrayContexts = new Vector<CParser.InitDeclaratorContext>();
                String variables = "";
                CParser.InitDeclaratorContext initDeclCtx;
                CParser.InitDeclaratorListContext initDeclLstctx = ctx.initDeclaratorList();
                //Loop through all initDeclaratorList and add arrays into vector and variables into string
                while (initDeclLstctx.initDeclaratorList() != null) {
                    if (initDeclLstctx.initDeclarator().declarator().getText().indexOf("[") != -1
                            && initDeclLstctx.initDeclarator().declarator().getText().indexOf("]") != -1) {
                        initDeclCtx = initDeclLstctx.initDeclarator();
                        arrayContexts.add(initDeclCtx);
                    } else {
                        variables += initDeclLstctx.initDeclarator().getText() + ',';
                    }
                    initDeclLstctx = initDeclLstctx.initDeclaratorList();
                }
                if (initDeclLstctx.initDeclarator().getText().indexOf("[") != -1
                        && initDeclLstctx.initDeclarator().getText().indexOf("]") != -1) {
                    initDeclCtx = initDeclLstctx.initDeclarator();
                    arrayContexts.add(initDeclCtx);
                } else {
                    variables += initDeclLstctx.initDeclarator().getText() + ',';
                }
                String arrayDecl = "";
                //            String arrayInitializer = "";
                CParser.DirectDeclaratorContext directDeclCtx;
                ST arrayDeclaration = templateGroup.getInstanceOf("arrayDeclaration");
                for (CParser.InitDeclaratorContext declCtx : arrayContexts) {
                    arrayDeclaration = templateGroup.getInstanceOf("arrayDeclaration");
                    directDeclCtx = declCtx.declarator().directDeclarator();
                    String arrayName = directDeclCtx.directDeclarator().getText();
                    String arraySize = directDeclCtx.assignmentExpression().getText();
                    String arrayType = ctx.declarationSpecifiers().getText();
                    //TODO: What if initializer has an array read. The previous version was failing to do that too.
                    if (declCtx.initializer() != null) {
                        arrayDeclaration = templateGroup.getInstanceOf("arrayDeclarationWithInit");
                        arrayDeclaration.add("arrayInit", declCtx.initializer().getText());
                    }

                    arrays.add(arrayName);

                    // Add memory resource to profiler (array declaration).
                    resourceProfiler.addMemoryResource(qmclass.getName(), arrayType, arrayName, arraySize,
                            ResourceType.MEMORY);

                    arrayDeclaration.add("arrayType", ctx.declarationSpecifiers().getText());
                    arrayDeclaration.add("arrayName", arrayName);
                    arrayDeclaration.add("arraySize", arraySize);
                    arrayDecl += arrayDeclaration.render() + "\n";
                    arrayDeclaration.remove("arrayType");
                    arrayDeclaration.remove("arrayName");
                    arrayDeclaration.remove("arraySize");
                }

                //Insert variables in the beginning
                if (variables.length() > 0) {
                    rewriter.replace(ctx.getStart(), ctx.getStop(), ctx.declarationSpecifiers().getText() + " "
                            + variables.substring(0, variables.length() - 1) + ";");
                    if (arrayContexts.size() > 0) {
                        rewriter.insertAfter(ctx.getStop(), arrayDecl);
                    }
                } else {
                    if (arrayContexts.size() > 0) {
                        rewriter.replace(ctx.getStart(), ctx.getStop(), arrayDecl);
                    }
                }
            } else {
                // Add memory resource to profiler (not an array but still a declaration so we need to count for resource profiler).
                resourceProfiler.addMemoryResource(qmclass.getName(), ctx.declarationSpecifiers().getText(),
                        declaratorCtx.getText(), "1", ResourceType.MEMORY);
            }
        }
    }

    @Override
    public void enterUnaryExpression(CParser.UnaryExpressionContext ctx) {
        if (ctx.getText().indexOf("[") != -1 && ctx.getText().indexOf("]") != -1
                && ctx.postfixExpression() != null) {
            if (ctx.postfixExpression().postfixExpression() != null) {
                CParser.PostfixExpressionContext postfixCtx = ctx.postfixExpression();
                ST arrayAccess = templateGroup.getInstanceOf("arrayAccess");
                arrayAccess.add("arrayName", postfixCtx.postfixExpression().getText());
                arrayAccess.add("arrayIndex", postfixCtx.expression().getText());
                rewriter.replace(ctx.getStart(), ctx.getStop(), arrayAccess.render());
            }
        } else if (ctx.getText().indexOf("sizeof") != -1 && ctx.unaryExpression() != null) {
            if (ctx.unaryExpression().postfixExpression() != null) {
                if (ctx.unaryExpression().postfixExpression().primaryExpression() != null) {
                    CParser.ExpressionContext exprCtx = ctx.unaryExpression().postfixExpression()
                            .primaryExpression().expression();
                    if (exprCtx != null) {
                        if (arrays.contains(exprCtx.getText())) {
                            rewriter.insertAfter(exprCtx.getStop(), ".values");
                        }
                    }
                }
            }
        }
    }

    @Override
    public void enterIterationStatement(CParser.IterationStatementContext ctx) {
        /*
         * This only supports very basic for-loops right now! (e.g., well-defined for(...;...;...) loops; 
         * start at MIN and go up to, but not including, MAX; increment by 1).
         * 
         * TODO: Support while loops
         * TODO: Support nested loop structures. 
         * TODO: 'update expression' parsing/handling: parse "++" as "1".
         * TODO: 'update expression' parsing/handling: parse "+= N" as "N".
         * TODO: 'update expression' parsing/handling: parse "--" as "-1".
         * TODO: 'update expression' parsing/handling: parse "-= N" as "-N".
         */
        String loopType = ctx.start.getText();

        if (loopType.equals("for")) {
            // Java objects for for-loop definition.
            DeclarationContext declarationCtx = ctx.declaration();
            ExpressionContext conditionCtx = ctx.expression(0);
            //         ExpressionContext updateCtx       = ctx.expression(1);

            /*
             * Parse/resolve strings for the loop bounds to count iterations. Then, 
             * enter the "for-loop context". All records added to the Resource Profiler will 
             * be annotated as being within the for-loop for more accurate accounting. 
             */

            // Update for-loop bounds based on (a VERY simple) ResourceProfiler int variable resolver...
            String start = declarationCtx.initDeclaratorList().stop.getText();
            if (resourceProfiler.getResolverIntVar(start) != null) {
                start = resourceProfiler.getResolverIntVar(start);
            }

            String end = conditionCtx.assignmentExpression().conditionalExpression().logicalOrExpression()
                    .logicalAndExpression().inclusiveOrExpression().exclusiveOrExpression().andExpression()
                    .equalityExpression().relationalExpression().shiftExpression().additiveExpression()
                    .multiplicativeExpression().castExpression().getText();
            if (resourceProfiler.getResolverIntVar(end) != null) {
                end = resourceProfiler.getResolverIntVar(end);
            }

            String update = "1";
            //         if (resourceProfiler.getResolverIntVar(update) != null) {
            //            update = resourceProfiler.getResolverIntVar(update);
            //         }

            // Inform the ResourceProfiler that we've entered a for-loop.
            resourceProfiler.enterForLoopContext(start, end, update);
        } else {
            System.err.println("\nCExtractor.java:: ***LOOP-TYPE '" + loopType
                    + "' NOT SUPPORTED UNDER RESOURCE PARSING***\n");
        }
    }

    @Override
    public void exitIterationStatement(CParser.IterationStatementContext ctx) {
        // Add a general for-loop (COMPUTATION) resource for recording "basic blocks".
        if (resourceProfiler != null) {
            Resource record = new Resource(ComputationType.FOR_LOOP.text(), qmclass.getName(),
                    ResourceType.COMPUTATION, 0.0);
            resourceProfiler.add(record);
        }

        // Inform the ResourceProfiler that we've exited the for-loop.
        resourceProfiler.exitForLoopContext();
    }
}