flex-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From cd...@apache.org
Subject [39/51] [partial] - Migrated the directory structure to a more Maven style structure. - Started migrating the Parser from Antlr2+3 and JFlex to Antlr4.
Date Tue, 22 Jul 2014 13:36:05 GMT
http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/59f6373b/compiler/src/main/java/org/apache/flex/abc/print/ABCDumpVisitor.java
----------------------------------------------------------------------
diff --git a/compiler/src/main/java/org/apache/flex/abc/print/ABCDumpVisitor.java b/compiler/src/main/java/org/apache/flex/abc/print/ABCDumpVisitor.java
new file mode 100644
index 0000000..cecf4fc
--- /dev/null
+++ b/compiler/src/main/java/org/apache/flex/abc/print/ABCDumpVisitor.java
@@ -0,0 +1,1095 @@
+/*
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+package org.apache.flex.abc.print;
+
+import org.apache.flex.abc.ABCConstants;
+import org.apache.flex.abc.ABCParser;
+import org.apache.flex.abc.PoolingABCVisitor;
+import org.apache.flex.abc.graph.IBasicBlock;
+import org.apache.flex.abc.graph.IFlowgraph;
+import org.apache.flex.abc.semantics.ClassInfo;
+import org.apache.flex.abc.semantics.ExceptionInfo;
+import org.apache.flex.abc.semantics.InstanceInfo;
+import org.apache.flex.abc.semantics.Instruction;
+import org.apache.flex.abc.semantics.Label;
+import org.apache.flex.abc.semantics.Metadata;
+import org.apache.flex.abc.semantics.MethodBodyInfo;
+import org.apache.flex.abc.semantics.MethodInfo;
+import org.apache.flex.abc.semantics.Name;
+import org.apache.flex.abc.semantics.Namespace;
+import org.apache.flex.abc.semantics.Nsset;
+import org.apache.flex.abc.semantics.ScriptInfo;
+import org.apache.flex.abc.semantics.Trait;
+import org.apache.flex.abc.semantics.Traits;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Vector;
+
+import static org.apache.flex.abc.ABCConstants.CONSTANT_Multiname;
+import static org.apache.flex.abc.ABCConstants.CONSTANT_MultinameA;
+import static org.apache.flex.abc.ABCConstants.CONSTANT_MultinameL;
+import static org.apache.flex.abc.ABCConstants.CONSTANT_Namespace;
+import static org.apache.flex.abc.ABCConstants.CONSTANT_PackageInternalNs;
+import static org.apache.flex.abc.ABCConstants.CONSTANT_PackageNs;
+import static org.apache.flex.abc.ABCConstants.CONSTANT_PrivateNs;
+import static org.apache.flex.abc.ABCConstants.CONSTANT_ProtectedNs;
+import static org.apache.flex.abc.ABCConstants.CONSTANT_Qname;
+import static org.apache.flex.abc.ABCConstants.CONSTANT_QnameA;
+import static org.apache.flex.abc.ABCConstants.CONSTANT_RTQname;
+import static org.apache.flex.abc.ABCConstants.CONSTANT_RTQnameA;
+import static org.apache.flex.abc.ABCConstants.CONSTANT_StaticProtectedNs;
+import static org.apache.flex.abc.ABCConstants.CONSTANT_TypeName;
+import static org.apache.flex.abc.ABCConstants.OP_debugfile;
+import static org.apache.flex.abc.ABCConstants.OP_lookupswitch;
+import static org.apache.flex.abc.ABCConstants.OP_pushstring;
+import static org.apache.flex.abc.ABCConstants.TRAIT_Class;
+import static org.apache.flex.abc.ABCConstants.TRAIT_Const;
+import static org.apache.flex.abc.ABCConstants.TRAIT_Function;
+import static org.apache.flex.abc.ABCConstants.TRAIT_Getter;
+import static org.apache.flex.abc.ABCConstants.TRAIT_Method;
+import static org.apache.flex.abc.ABCConstants.TRAIT_Setter;
+import static org.apache.flex.abc.ABCConstants.TRAIT_Var;
+
+/**
+ * ABC Visitor implementation that can take an ABC and
+ * dump out a textual representation of it.  Similar to
+ * abcdump/abcdis in the tamarin project.
+ * <p/>
+ * To use, construct one of these and pass it ABCParser,
+ * or whatever is driving the ABC events.
+ */
+public class ABCDumpVisitor extends PoolingABCVisitor
+{
+    /**
+     * Constructor
+     *
+     * @param p The PrintWriter to write the textual represention of the ABC to
+     */
+    public ABCDumpVisitor (PrintWriter p)
+    {
+        super();
+        printer = new IndentingPrinter(p, 0, 2);
+        dumpedMethods = new HashSet<MethodInfo>();
+    }
+
+    private IndentingPrinter printer;
+    private Set<MethodInfo> dumpedMethods;
+
+    /**
+     * Turn a namespace, name pair into a user readable String
+     */
+    private String qnameToString (Namespace ns, String n)
+    {
+        if (ns == null)
+            return n;
+
+        if ((ns.getKind() == CONSTANT_PackageNs) && (ns.getName().length() > 0))
+            return ns.getName() + "::" + n;
+
+        String qual = nsQualifierForNamespace(ns);
+        if (qual.length() > 0)
+            return n;
+
+        if (ns.getName().length() == 0)
+            return n;
+
+        return ns.getName() + "::" + n;
+    }
+
+    /**
+     * Turn a namespace set into a user readable String
+     */
+    private String nssetToString (Nsset nsSet)
+    {
+        String s = "";
+        for (Namespace ns : nsSet)
+        {
+            if (ns.getKind() != CONSTANT_PrivateNs)
+                s += (ns.getName() + ", ");
+            else
+                s += "private, ";
+        }
+        return "{" + s + "}";
+    }
+
+    /**
+     * Turn a Name into a user readable String
+     */
+    private String nameToString (Name n)
+    {
+        if (n == null || n.couldBeAnyType())
+            return "*";
+
+        Nsset nsset;
+
+        switch (n.getKind())
+        {
+            case CONSTANT_Qname:
+            case CONSTANT_QnameA:
+                return qnameToString(n.getSingleQualifier(), n.getBaseName());
+            case CONSTANT_Multiname:
+            case CONSTANT_MultinameA:
+                nsset = n.getQualifiers();
+                if (nsset.length() == 1)
+                    return qnameToString(nsset.iterator().next(), n.getBaseName());
+                else
+                    return (nssetToString(nsset) + "::") + n.getBaseName();
+            case CONSTANT_RTQname:
+            case CONSTANT_RTQnameA:
+                return "<error> " + n.toString();
+            case CONSTANT_MultinameL:
+                return "<error> " + n.toString();
+            case CONSTANT_TypeName:
+                Name typeName = n.getTypeNameParameter();
+                return nameToString(n.getTypeNameBase()) + ".<" + nameToString(typeName) + ">";
+        }
+        return "<error> " + n.toString();
+    }
+
+    /**
+     * Get a String that can be used as the  qualifier for a given Name
+     */
+    private String nsQualifierForName (Name n)
+    {
+        Nsset nsset;
+        switch (n.getKind())
+        {
+            case CONSTANT_Qname:
+            case CONSTANT_QnameA:
+                return nsQualifierForNamespace(n.getSingleQualifier());
+            case CONSTANT_Multiname:
+            case CONSTANT_MultinameA:
+                nsset = n.getQualifiers();
+                if (nsset.length() == 1)
+                    return nsQualifierForNamespace(nsset.iterator().next());
+                break;
+            case CONSTANT_RTQname:
+            case CONSTANT_RTQnameA:
+                break;
+            case CONSTANT_MultinameL:
+                break;
+            case CONSTANT_TypeName:
+                break;
+        }
+        return "";
+    }
+
+    /**
+     * Get a String representing the access modifier(public, private, etc) based on a namespace value
+     */
+    private String nsQualifierForNamespace (Namespace ns)
+    {
+        switch (ns.getKind())
+        {
+            case CONSTANT_PackageNs:
+                return "public ";
+            case CONSTANT_ProtectedNs:
+            case CONSTANT_StaticProtectedNs:
+                //case CONSTANT_StaticProtectedNs2:
+                return "protected ";
+            case CONSTANT_PackageInternalNs:
+                return "internal ";
+            case CONSTANT_PrivateNs:
+                return "private ";
+        }
+
+        if (ns.getKind() == CONSTANT_Namespace)
+        {
+            if (ns.getName().equals("http://adobe.com/AS3/2006/builtin"))
+                return "AS3 ";
+        }
+        return ns.getName() + " ";
+    }
+
+    /**
+     * Escape a string for displaying better
+     */
+    private String stringToEscapedString (String s)
+    {
+        String charsToEscape = "\b\t\n\f\r\"\'\\";
+        String escapeChars = "btnfr\"\'\\";
+        int escapeIndex;
+        char currChar;
+        String result = "";
+        for (int i = 0; i < s.length(); ++i)
+        {
+            currChar = s.charAt(i);
+            escapeIndex = charsToEscape.indexOf(currChar);
+            if (escapeIndex != -1)
+                result += "\\" + escapeChars.charAt(escapeIndex);
+            else
+                result += currChar;
+        }
+        return result;
+    }
+
+    /**
+     * Print the ABC
+     */
+    public void write ()
+    {
+        traverse();
+
+        writeIterable(getMethodInfos(), new IWriteFunction()
+        {
+            public void write (Object v, int index)
+            {
+                writeAnonMethodInfo((MethodInfo) v, index);
+            }
+        }
+        );
+    }
+
+    /**
+     * Walk over the elements of the ABC
+     * Starts with the Scripts and walks down from there
+     */
+    public void traverse ()
+    {
+        int nScripts = getScriptInfos().size();
+        ScriptInfo si;
+        for (int i = 0; i < nScripts; ++i)
+        {
+            si = getScriptInfos().get(i);
+            traverseScript(i, si);
+        }
+    }
+
+    /**
+     * Traverse a Script, and its traits
+     */
+    protected void traverseScript (int id, ScriptInfo scriptInfo)
+    {
+        printer.println("// script " + id);
+        traverseScriptTraits(scriptInfo.getTraits(), scriptInfo);
+        MethodInfo initMethodInfo = scriptInfo.getInit();
+        traverseScriptInit(initMethodInfo, scriptInfo, id);
+        printer.println("");
+    }
+
+    /**
+     * Traverse the traits of a script
+     */
+    protected void traverseScriptTraits (Traits traits, ScriptInfo si)
+    {
+        for (Trait t : traits)
+        {
+            switch (t.getKind())
+            {
+                case TRAIT_Const:
+                    traverseScriptConstTrait(t, si);
+                    break;
+                case TRAIT_Var:
+                    traverseScriptSlotTrait(t, si);
+                    break;
+                case TRAIT_Method:
+                    traverseScriptMethodTrait(t, si);
+                    break;
+                case TRAIT_Getter:
+                    traverseScriptGetterTrait(t, si);
+                    break;
+                case TRAIT_Setter:
+                    traverseScriptSetterTrait(t, si);
+                    break;
+                case TRAIT_Function:
+                    traverseScriptFunctionTrait(t, si);
+                    break;
+                case TRAIT_Class:
+                    traverseScriptClassTrait(t, si);
+                    break;
+            }
+        }
+    }
+
+    /**
+     * traverse the traits of an Instance Info
+     */
+    protected void traverseInstanceTraits (Traits traits)
+    {
+        for (Trait t : traits)
+        {
+            switch (t.getKind())
+            {
+                case TRAIT_Const:
+                    traverseInstanceConstTrait(t);
+                    break;
+                case TRAIT_Var:
+                    traverseInstanceSlotTrait(t);
+                    break;
+                case TRAIT_Method:
+                    traverseInstanceMethodTrait(t);
+                    break;
+                case TRAIT_Getter:
+                    traverseInstanceGetterTrait(t);
+                    break;
+                case TRAIT_Setter:
+                    traverseInstanceSetterTrait(t);
+                    break;
+                case TRAIT_Function:
+                    traverseInstanceFunctionTrait(t);
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Traverse the traits of a Class Info
+     */
+    protected void traverseClassTraits (Traits traits)
+    {
+        for (Trait t : traits)
+        {
+            switch (t.getKind())
+            {
+                case TRAIT_Const:
+                    traverseClassConstTrait(t);
+                    break;
+                case TRAIT_Var:
+                    traverseClassSlotTrait(t);
+                    break;
+                case TRAIT_Method:
+                    traverseClassMethodTrait(t);
+                    break;
+                case TRAIT_Getter:
+                    traverseClassGetterTrait(t);
+                    break;
+                case TRAIT_Setter:
+                    traverseClassSetterTrait(t);
+                    break;
+                case TRAIT_Function:
+                    traverseClassFunctionTrait(t);
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Traverse a slot trait of a script
+     */
+    protected void traverseScriptSlotTrait (Trait trait, ScriptInfo scriptInfo)
+    {
+        writeSlotTrait("var", trait, false);
+    }
+
+    /**
+     * Traverse a const trait of a script
+     */
+    protected void traverseScriptConstTrait (Trait trait, ScriptInfo scriptInfo)
+    {
+        writeSlotTrait("const", trait, false);
+    }
+
+    /**
+     * Traverse a method trait of a script
+     */
+    protected void traverseScriptMethodTrait (Trait trait, ScriptInfo scriptInfo)
+    {
+        writeMethodTrait("function", trait, false);
+    }
+
+    /**
+     * Traverse a getter trait of a script
+     */
+    protected void traverseScriptGetterTrait (Trait trait, ScriptInfo scriptInfo)
+    {
+        writeMethodTrait("function get", trait, false);
+    }
+
+    /**
+     * Traverse a setter trait of a script
+     */
+    protected void traverseScriptSetterTrait (Trait trait, ScriptInfo scriptInfo)
+    {
+        writeMethodTrait("function set", trait, false);
+    }
+
+    /**
+     * Traverse a function trait of a script
+     */
+    protected void traverseScriptFunctionTrait (Trait trait, ScriptInfo scriptInfo)
+    {
+        writeMethodTrait("function", trait, false);
+    }
+
+    /**
+     * Traverse a class trait of a script
+     */
+    protected void traverseScriptClassTrait (Trait trait, ScriptInfo scriptInfo)
+    {
+        ClassInfo ci = (ClassInfo) trait.getAttr(Trait.TRAIT_CLASS);
+        int classIndex = getClassId(ci);
+        ClassVisitor cv = getDefinedClasses().get(classIndex);
+        InstanceInfo iinfo = cv.getInstanceInfo();
+
+        traverseScriptClassTrait(classIndex, iinfo, ci, trait, scriptInfo);
+    }
+
+    /**
+     * Traverse a class trait of a script
+     */
+    protected void traverseScriptClassTrait (int classId, InstanceInfo instanceInfo, ClassInfo classInfo, Trait trait, ScriptInfo scriptInfo)
+    {
+        printer.println("");
+
+        int slotId = 0;
+        if( trait.hasAttr(Trait.TRAIT_SLOT ))
+            slotId = trait.getIntAttr(Trait.TRAIT_SLOT);
+
+        printer.println("// class_id=" + classId + " slot_id=" + String.valueOf(slotId));
+        String def;
+        if (instanceInfo.isInterface())
+        {
+            def = "interface";
+        }
+        else
+        {
+            def = "class";
+            if (!instanceInfo.isSealed())
+                def = "dynamic " + def;
+            if (instanceInfo.isFinal())
+                def = "final " + def;
+        }
+        writeMetaData(trait);
+        printer.println(nsQualifierForName(trait.getName()) + def + " " + nameToString(trait.getName()) + " extends " + nameToString(instanceInfo.superName));
+        if (instanceInfo.interfaceNames.length > 0)
+        {
+            printer.indent();
+            List<String> interfaceNames = new ArrayList<String>();
+            for (Name interfaceName : instanceInfo.interfaceNames)
+            {
+                interfaceNames.add(nameToString(interfaceName));
+            }
+            printer.println(joinOn(",",interfaceNames));
+            printer.unindent();
+        }
+
+        printer.println("{");
+        printer.indent();
+
+        traverseInstanceInit(instanceInfo.iInit, instanceInfo, trait, scriptInfo);
+        traverseInstanceTraits(instanceInfo.traits);
+        traverseClassInit(classInfo.cInit, classInfo, trait, scriptInfo);
+        traverseClassTraits(classInfo.classTraits);
+
+        printer.unindent();
+        printer.println("}");
+
+    }
+
+    /**
+     * Traverse the Script init method
+     */
+    protected void traverseScriptInit (MethodInfo init, ScriptInfo scriptInfo, int scriptId)
+    {
+        printer.println("");
+        writeMethodInfo("", "script" + scriptId + "$init", "function", init, false, false, false);
+    }
+
+    /**
+     * Traverse an instance init method
+     */
+    protected void traverseInstanceInit (MethodInfo init, InstanceInfo instanceInfo, Trait classTrait, ScriptInfo scriptInfo)
+    {
+        printer.println("");
+        printer.println("// method_id=" + getMethodInfos().getId(instanceInfo.iInit));
+        writeMethodInfo("public ", nameToString(classTrait.getName()), "function", init, false, false, false);
+    }
+
+    /**
+     * Traverse a slot trait of an instance info
+     */
+    protected void traverseInstanceSlotTrait (Trait trait)
+    {
+        writeSlotTrait("var", trait, false);
+    }
+
+    /**
+     * Traverse a const trait of an instance info
+     */
+    protected void traverseInstanceConstTrait (Trait trait)
+    {
+        writeSlotTrait("const", trait, false);
+    }
+
+    /**
+     * Traverse a method trait of an instance info
+     */
+    protected void traverseInstanceMethodTrait (Trait trait)
+    {
+        writeMethodTrait("function", trait, false);
+    }
+
+    /**
+     * Traverse a getter trait of an instance info
+     */
+    protected void traverseInstanceGetterTrait (Trait trait)
+    {
+        writeMethodTrait("function get", trait, false);
+    }
+
+    /**
+     * Traverse a setter trait of an instance info
+     */
+    protected void traverseInstanceSetterTrait (Trait trait)
+    {
+        writeMethodTrait("function set", trait, false);
+    }
+
+    /**
+     * Traverse a function trait of an instance info
+     */
+    protected void traverseInstanceFunctionTrait (Trait trait)
+    {
+        writeMethodTrait("function", trait, false);
+    }
+
+    /**
+     * Traverse a class init method
+     */
+    protected void traverseClassInit (MethodInfo init, ClassInfo classInfo, Trait classTrait, ScriptInfo scriptInfo)
+    {
+        printer.println("");
+        //printer.println("// method_id=" + classInfo.init_index)
+        writeMethodInfo("public ", nameToString(classTrait.getName()) + "$", "function", init, true, false, false);
+    }
+
+    /**
+     * Traverse a slot trait of a class info
+     */
+    protected void traverseClassSlotTrait (Trait trait)
+    {
+        writeSlotTrait("var", trait, true);
+    }
+
+    /**
+     * Traverse a const trait of a class info
+     */
+    protected void traverseClassConstTrait (Trait trait)
+    {
+        writeSlotTrait("const", trait, true);
+    }
+
+    /**
+     * Traverse a method trait of a class info
+     */
+    protected void traverseClassMethodTrait (Trait trait)
+    {
+        writeMethodTrait("function", trait, true);
+    }
+
+    /**
+     * Traverse a getter trait of a class info
+     */
+    protected void traverseClassGetterTrait (Trait trait)
+    {
+        writeMethodTrait("function get", trait, true);
+    }
+
+    /**
+     * Traverse a setter trait of a class info
+     */
+    protected void traverseClassSetterTrait (Trait trait)
+    {
+        writeMethodTrait("function set", trait, true);
+    }
+
+    /**
+     * Traverse a function trait of a class info
+     */
+    protected void traverseClassFunctionTrait (Trait trait)
+    {
+        writeMethodTrait("function", trait, true);
+    }
+
+    /**
+     * Write out the metadata for a given Trait
+     */
+    private void writeMetaData (Trait t)
+    {
+        if (!t.hasMetadata())
+            return;
+        for (Metadata mid : t.getMetadata())
+        {
+            List<String> entries = new Vector<String>();
+            String[] keys = mid.getKeys();
+            for (int i = 0; i < keys.length; ++i)
+            {
+                String key = keys[i];
+                String value = mid.getValues()[i];
+                if (key == null || key.length() == 0)
+                    entries.add("\"" + value + "\"");
+                else
+                    entries.add(key + "=\"" + value + "\"");
+            }
+            StringBuilder sb = new StringBuilder();
+            for (int i = 0; i < entries.size(); ++i)
+            {
+                sb.append(entries.get(i));
+                if (i < entries.size() - 1)
+                    sb.append(", ");
+            }
+            printer.println("[" + mid.getName() + "(" + sb.toString() + ")]" + " // metadata_id=" + mid);
+        }
+    }
+
+    /**
+     * Write out the method info for an anonymous method
+     */
+    private void writeAnonMethodInfo (MethodInfo mi, int id)
+    {
+        if (dumpedMethods.contains(mi))
+            return;
+        printer.println("");
+        printer.println("// " + id + " " + mi.getMethodName());
+        writeMethodInfo("", "", "function ", mi, false, false, false);
+    }
+
+    /**
+     * Write out a slot trait
+     *
+     * @param kindStr  the kind of trait (var or const)
+     * @param t        the trait
+     * @param isStatic whether the trait is static or not
+     */
+    private void writeSlotTrait (String kindStr, Trait t, boolean isStatic)
+    {
+        printer.println("");
+        String qual = nsQualifierForName(t.getName());
+        String nameStr = nameToString(t.getName());
+
+        Object value = null;
+        if( t.hasAttr(Trait.SLOT_VALUE) )
+            value = t.getAttr(Trait.SLOT_VALUE);
+
+        String valueStr = "";
+        if (value instanceof String)
+            valueStr = " = \"" + value + "\"";
+        else if (value instanceof Namespace)
+            valueStr = " = " + ((Namespace) value).getName();
+        else if (value == ABCConstants.NULL_VALUE)
+            valueStr = " = null";
+        else if (value == ABCConstants.UNDEFINED_VALUE)
+            valueStr = "";
+        else if (value != null)
+            valueStr = " = " + value.toString();
+
+        String staticStr = isStatic ? "static " : "";
+        writeMetaData(t);
+        //printer.println("// name_id=" + t.name_index + " slot_id=" + t.slot_id)
+        printer.println(qual + staticStr + kindStr + " " + nameStr + ":" + nameToString((Name) t.getAttr(Trait.TRAIT_TYPE)) + valueStr);
+    }
+
+    /**
+     * Generate a string for displaying a lookupswitch instruction
+     *
+     * @param inst       the lookupswitch instruction
+     * @param mb         the method body
+     * @param blockNames a map of IBasicBlock to display name of block
+     * @param cfg        the control flow graph for the current method
+     * @return a string to use to display the lookupswitch istruction
+     */
+    private String stringForLookupSwitch (Instruction inst, MethodBodyInfo mb, Map<IBasicBlock, String> blockNames, IFlowgraph cfg)
+    {
+        int case_size = inst.getOperandCount() - 1;
+
+        // Last label is the default
+        String defaultStr = "default: " + blockNames.get(cfg.getBlock((Label) inst.getOperand(case_size)));
+
+        String maxCaseStr = "maxcase: " + case_size;
+        List<String> result = new Vector<String>();
+        result.add(defaultStr);
+        result.add(maxCaseStr);
+        for (int i = 0; i < case_size; ++i)
+        {
+            result.add(blockNames.get(cfg.getBlock((Label) inst.getOperand(i))));
+        }
+        return joinOn(" ", result);
+    }
+
+    /**
+     * Write out a method info, and it's corresponding body
+     */
+    private void writeMethodInfo (String qualStr, String nameStr, String kindStr, MethodInfo methodInfo, boolean isStatic, boolean isOverride, boolean isFinal)
+    {
+        dumpedMethods.add(methodInfo);
+        List<String> paramTypeStrings = new Vector<String>();
+        for (Name paramTypeName : methodInfo.getParamTypes())
+            paramTypeStrings.add(nameToString(paramTypeName));
+
+        String staticStr = isStatic ? "static " : "";
+        String overrideStr = isOverride ? "override " : "";
+        String nativeStr = methodInfo.isNative() ? "native " : "";
+        String finalStr = isFinal ? "final " : "";
+        printer.println(qualStr + staticStr + nativeStr + finalStr + overrideStr + kindStr + " " + nameStr + "(" + joinOn(",", paramTypeStrings) + "):" + nameToString(methodInfo.getReturnType()));
+        MethodBodyInfo mb = getMethodBodyForMethodInfo(methodInfo);
+        if (mb != null)
+        {
+            printer.println("{");
+            printer.indent();
+            TablePrinter tablePrinter = new TablePrinter(3, 2);
+            tablePrinter.addRow(new String[]{"//", "derivedName", methodInfo.getMethodName()});
+            tablePrinter.addRow(new String[]{"//", "method_info", String.valueOf(getMethodInfos().getId(mb.getMethodInfo()))});
+            tablePrinter.addRow(new String[]{"//", "max_stack", String.valueOf(mb.max_stack)});
+            tablePrinter.addRow(new String[]{"//", "max_regs", String.valueOf(mb.max_local)});
+            tablePrinter.addRow(new String[]{"//", "scope_depth", String.valueOf(mb.initial_scope)});
+            tablePrinter.addRow(new String[]{"//", "max_scope", String.valueOf(mb.max_scope)});
+            tablePrinter.addRow(new String[]{"//", "code_length", String.valueOf(mb.code_len)});
+            //tablePrinter.addRow(["//", "code_offset", mb.code_offset]);
+            tablePrinter.print(printer);
+            if (mb.getTraits() != null && mb.getTraits().getTraitCount() > 0)
+            {
+                printer.println("activation_traits {");
+                printer.indent();
+
+                for (Trait trait : mb.getTraits())
+                {
+                    //var kindStr : String;
+                    switch (trait.getKind())
+                    {
+                        case TRAIT_Var:
+                            kindStr = "var";
+                            break;
+                        case TRAIT_Const:
+                            kindStr = "const";
+                            break;
+                        default:
+                            throw new Error("Illegal activation trait in " + methodInfo.getMethodName());
+                    }
+                    writeSlotTrait(kindStr, trait, false);
+                }
+
+                printer.unindent();
+                printer.println("}");
+            }
+            IFlowgraph cfg = mb.getCfg();
+            Map<IBasicBlock, String> blockNames = new HashMap<IBasicBlock, String>();
+            int i = 0;
+            for (IBasicBlock block : cfg.getBlocksInEntryOrder())
+            {
+                blockNames.put(block, "bb" + i++);
+            }
+            int offset = 0;
+            for (IBasicBlock block : cfg.getBlocksInEntryOrder())
+            {
+                printer.println(blockNames.get(block));
+                printer.indent();
+                // TODO: preds
+                //printer.println("preds=[" + block.getPredeccessor()mb.blocks[i].getPredIds(mb.blocks).join(", ") + "]");
+                Collection<? extends IBasicBlock> succs = block.getSuccessors();
+                List<String> succNames = new ArrayList<String>();
+                for (IBasicBlock s : succs)
+                    succNames.add(blockNames.get(s));
+
+                printer.println("succs=[" + joinOn(",", succNames) + "]");
+                /*
+                // TODO: implement this with FrameModelEncoder
+                if(mb.blocks[i].state != null) {
+                    printer.println("verification = " + (mb.blocks[i].state.verifyError == null ? "ok" : "failed: " + mb.blocks[i].state.verifyError));
+                }
+                */
+                tablePrinter = new TablePrinter(4, 2);
+                for (int j = 0; j < block.size(); j++)
+                {
+                    Instruction inst = block.get(j);
+                    String constantStr = "";
+
+                    if (inst.hasOperands() && inst.getOperand(0) instanceof Name)
+                    {
+                        constantStr = nameToString((Name) inst.getOperand(0));
+                    }
+                    else if (inst.isBranch() && inst.getOpcode() != OP_lookupswitch)
+                    {
+                        constantStr = blockNames.get(cfg.getBlock((Label) inst.getOperand(0)));
+                    }
+                    else
+                    {
+                        switch (inst.getOpcode())
+                        {
+                            case OP_debugfile:
+                            case OP_pushstring:
+                                constantStr = "\"" + stringToEscapedString((String) inst.getOperand(0)) + "\"";
+                                break;
+                            case OP_lookupswitch:
+                                constantStr = stringForLookupSwitch(inst, mb, blockNames, cfg);
+                                break;
+                        }
+                    }
+                    tablePrinter.addRow(new String[]{offset + "    ",
+                            Instruction.decodeOp(inst.getOpcode()),
+                            constantStr,
+                            inst.isImmediate() ? String.valueOf(inst.getImmediate()) : ""
+                            // TODO : Use FrameModelEncoder to keep track
+                            // TODO: of stack/local values
+                            //(inst.getStackDepth() == null ? "" : "// stack: " + inst.getStackDepth()),
+                            //(inst.getState() == null ? "" : "// stack["+inst.getState().stackDepth+"]: " + inst.getState().stackTypeString()),
+                            //(inst.getState() == null ? "" : "// locals: " + inst.getState().localsTypeString()),
+                            //inst.getVerifyError()
+                    });
+                    offset++;
+                }
+                tablePrinter.print(printer);
+                printer.unindent();
+            }
+
+            printer.unindent();
+            printer.println("}");
+            if (mb.getExceptions().size() > 0)
+            {
+                tablePrinter = new TablePrinter(7, 2);
+                tablePrinter.addRow(new String[]{"//", "exception", "start", "end", "target", "type string", "name string"});
+                for (i = 0; i < mb.getExceptions().size(); i++)
+                {
+                    ExceptionInfo exception = mb.getExceptions().get(i);
+                    tablePrinter.addRow(new String[]{"//", String.valueOf(i), String.valueOf(exception.getFrom().getPosition()),
+                            String.valueOf(exception.getTo().getPosition()),
+                            String.valueOf(exception.getTarget().getPosition()),
+                            nameToString(exception.getExceptionType()),
+                            nameToString(exception.getCatchVar())});
+                }
+                tablePrinter.print(printer);
+                printer.println("");
+            }
+        }
+    }
+
+    /**
+     * Write out a method trait
+     */
+    private void writeMethodTrait (String kindStr, Trait t, boolean isStatic)
+    {
+        String qual = nsQualifierForName(t.getName());
+        String nameStr = nameToString(t.getName());
+        MethodInfo methodInfo = (MethodInfo) t.getAttr(Trait.TRAIT_METHOD);
+        printer.println("");
+        writeMetaData(t);
+        //printer.println("// name_id=" + t.name_index + " method_id=" + t.method_info + " disp_id=" + t.disp_id)
+        writeMethodInfo(qual, nameStr, kindStr, methodInfo, isStatic, t.getBooleanAttr(Trait.TRAIT_OVERRIDE), t.getBooleanAttr(Trait.TRAIT_FINAL));
+    }
+
+    /**
+     * Helper interface to pass around writers
+     */
+    interface IWriteFunction
+    {
+        void write (Object v, int index);
+    }
+
+    /**
+     * Write an array using the given IWriteFunction
+     */
+    private <T> void writeIterable (Iterable<T> p, IWriteFunction writefunc)
+    {
+        int i = 0;
+        for (Object v : p)
+        {
+            writefunc.write(v, i++);
+        }
+    }
+
+    /**
+     * Helper class to manage indenting
+     */
+    private static class IndentingPrinter
+    {
+        private PrintWriter delegate;
+        private String currentIndent;
+        private int indentIncrement;
+
+        public IndentingPrinter (PrintWriter delegate, int initialIndent, int indentIncrement)
+        {
+            this.delegate = delegate;
+            this.currentIndent = makeIndentStr(initialIndent);
+            this.indentIncrement = indentIncrement;
+        }
+
+        private static String makeIndentStr (int indent)
+        {
+            String result;
+            StringBuilder sb = new StringBuilder();
+            for (int i = 0; i < indent; ++i)
+                sb.append(" ");
+            result = sb.toString();
+            return result;
+        }
+
+        public void println (String s)
+        {
+            if (s.length() > 0)
+                s = currentIndent + s;
+            delegate.println(s);
+        }
+
+        public void indent ()
+        {
+            int newIndent = currentIndent.length() + indentIncrement;
+            currentIndent = makeIndentStr(newIndent);
+        }
+
+        public void unindent ()
+        {
+            int newIndent = currentIndent.length() - indentIncrement;
+            currentIndent = makeIndentStr(newIndent);
+        }
+
+        public void flush ()
+        {
+            delegate.flush();
+        }
+    }
+
+    /**
+     * Helper class to display nicely formatted tables of data
+     */
+    public static class TablePrinter
+    {
+        public TablePrinter (int nCols, int minPadding)
+        {
+            cols = nCols;
+            this.minPadding = minPadding;
+            m_rows = new Vector<Row>();
+        }
+
+        public void addRow (String[] r)
+        {
+            if (r.length != cols)
+                throw new Error("Invalid row");
+            m_rows.add(new Row(r));
+        }
+
+        public void print (IndentingPrinter p)
+        {
+            int[] colWidths = new int[cols];
+            int i;
+            for (i = 0; i < cols; ++i)
+                colWidths[i] = 0;
+
+            for (Row r : m_rows)
+                r.measure(colWidths, minPadding);
+
+            for (Row r : m_rows)
+                r.print(p, colWidths);
+        }
+
+        private int cols;
+        private int minPadding;
+        private Vector<Row> m_rows;
+
+        private class Row
+        {
+            private String[] cells;
+
+            public Row (String[] cells)
+            {
+                this.cells = cells;
+            }
+
+            public void measure (int[] colWidths, int minPadding)
+            {
+                for (int i = 0; i < cells.length; ++i)
+                    colWidths[i] = Math.max(colWidths[i], getRowItemStr(i).length() + minPadding);
+            }
+
+            public void print (IndentingPrinter p, int[] colWidths)
+            {
+                String rowStr = "";
+                for (int i = 0; i < cells.length; ++i)
+                    rowStr += padString(getRowItemStr(i), colWidths[i]);
+                p.println(rowStr);
+            }
+
+            private String getRowItemStr (int i)
+            {
+                if (cells[i] == null)
+                    return "null";
+
+                if (i < cells.length)
+                    return cells[i];
+
+                return "error - out of range " + i;
+            }
+
+            private String padString (String s, int minLength)
+            {
+                while (s.length() < minLength)
+                    s += " ";
+                return s;
+            }
+        }
+    }
+
+    /**
+     * Entry point for testing
+     * <p/>
+     * Spits out the dump to System.out
+     */
+    public static void main (String[] args) throws Exception
+    {
+        for (String arg : args)
+        {
+            File f = new File(arg);
+            if (f.exists())
+            {
+                ABCParser parser = new ABCParser(new BufferedInputStream(new FileInputStream(f)));
+                PoolingABCVisitor printer = new ABCDumpVisitor(new PrintWriter(System.out));
+                parser.parseABC(printer);
+            }
+        }
+    }
+
+    /**
+     * This implementation will dump out a text representation of the ABC to
+     * the PrintWriter that was passed in on construction.
+     */
+    public void visitEnd ()
+    {
+        write();
+        printer.flush();
+    }
+
+    /**
+     * Stringify a collection of items.
+     * @param separator the separator string.
+     * @param items the items to stringify.
+     * @return the items, listed out with a separator in between.
+     */
+    private static String joinOn(String separator, Collection<? extends Object> items)
+    {
+        StringBuilder result = new StringBuilder();
+
+        for ( Object item: items )
+        {
+            if ( result.length() > 0 )
+                result.append(separator);
+            result.append(item);
+        }
+
+        return result.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/59f6373b/compiler/src/main/java/org/apache/flex/abc/print/package.html
----------------------------------------------------------------------
diff --git a/compiler/src/main/java/org/apache/flex/abc/print/package.html b/compiler/src/main/java/org/apache/flex/abc/print/package.html
new file mode 100644
index 0000000..45d5bed
--- /dev/null
+++ b/compiler/src/main/java/org/apache/flex/abc/print/package.html
@@ -0,0 +1,27 @@
+<!--
+
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+-->
+
+<html>
+<body>
+
+This package contains specialized visitors and supporting classes that transform their input into a human readable form.
+This is useful for debugging generated code, as it gives users an easy way to see what the generated code does.
+
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/59f6373b/compiler/src/main/java/org/apache/flex/abc/semantics/ArbitraryOperandsInstruction.java
----------------------------------------------------------------------
diff --git a/compiler/src/main/java/org/apache/flex/abc/semantics/ArbitraryOperandsInstruction.java b/compiler/src/main/java/org/apache/flex/abc/semantics/ArbitraryOperandsInstruction.java
new file mode 100644
index 0000000..394e093
--- /dev/null
+++ b/compiler/src/main/java/org/apache/flex/abc/semantics/ArbitraryOperandsInstruction.java
@@ -0,0 +1,118 @@
+/*
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+package org.apache.flex.abc.semantics;
+
+import org.apache.flex.abc.ABCConstants;
+
+/**
+ * An ArbitraryOperandsInstruction holds an array of operands as specified by
+ * the caller, or in some special cases as set by a fixup pass.
+ */
+public class ArbitraryOperandsInstruction extends Instruction
+{
+    /**
+     * Construct an ArbitraryOperandsInstruction.
+     * 
+     * @param opcode - the opcode. Passed to superclass constructor.
+     * @param operands - the operands. May be null.
+     */
+    public ArbitraryOperandsInstruction(int opcode, Object[] operands)
+    {
+        super(opcode);
+        this.operands = operands;
+    }
+
+    /**
+     * "Copy" construct an ArbitraryOperandsInstruction with a new opcode.
+     * 
+     * @param opcode - the opcode.
+     * @param original - the original ArbitraryOperandsInstruction whose
+     * operands are to be incorporated into the new instruction.
+     */
+    public ArbitraryOperandsInstruction(int opcode, ArbitraryOperandsInstruction original)
+    {
+        this(opcode, original.operands);
+    }
+
+    /**
+     * The instruction's operands.
+     */
+    private Object[] operands;
+
+    @Override
+    public String toString()
+    {
+        StringBuffer result = new StringBuffer(super.toString());
+
+        result.append("[");
+
+        for (Object x : operands)
+        {
+            result.append(x);
+            result.append(",");
+        }
+        result.append("]");
+
+        return result.toString();
+    }
+
+    /**
+     * Fetch an operand.
+     * 
+     * @param index - the index of the operand of interest.
+     * @return the operand at the specified index.
+     */
+    @Override
+    public Object getOperand(int index)
+    {
+        return this.operands[index];
+    }
+
+    /**
+     * @return the instruction's operand count; if the operands are not yet
+     * present, then return 0.
+     */
+    @Override
+    public int getOperandCount()
+    {
+        return this.operands != null ? this.operands.length : 0;
+    }
+
+    /**
+     * Set this instruction's operands in a fixup pass.
+     * 
+     * @param operands - the instruction's operands.
+     */
+    @Override
+    public void setOperands(Object[] operands)
+    {
+        this.operands = operands;
+    }
+
+    /**
+     * Set the temporary register operands of a hasnext2 instruction.
+     */
+    @Override
+    public void setTempRegisters(Object[] tempregs)
+    {
+        assert opcode == ABCConstants.OP_hasnext2 : "Cannot set temp registers of " + this;
+        this.operands = tempregs;
+    }
+}

http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/59f6373b/compiler/src/main/java/org/apache/flex/abc/semantics/Block.java
----------------------------------------------------------------------
diff --git a/compiler/src/main/java/org/apache/flex/abc/semantics/Block.java b/compiler/src/main/java/org/apache/flex/abc/semantics/Block.java
new file mode 100644
index 0000000..d6281a0
--- /dev/null
+++ b/compiler/src/main/java/org/apache/flex/abc/semantics/Block.java
@@ -0,0 +1,140 @@
+/*
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+package org.apache.flex.abc.semantics;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.flex.abc.ABCConstants;
+import org.apache.flex.abc.graph.IBasicBlock;
+
+/**
+ * Block implements {@link IBasicBlock} and is
+ * the normal representation of a vertex in a
+ * method's flowgraph.
+ */
+public class Block implements IBasicBlock
+{
+    /**
+     * Instructions in this block. A Block created by a ControlFlowGraph has one
+     * targetable instruction as its last instruction. A Block that has been
+     * modified to have a targetable instruction in its interior will have
+     * unpredictable results until it's serialized into a new InstructionList
+     * and the ControlFlowGraph rebuilt.
+     */
+    private ArrayList<Instruction> instructions = new ArrayList<Instruction>();
+
+    /**
+     * Successors to this block.
+     */
+    private Set<IBasicBlock> successors = Collections.emptySet();
+
+    /**
+     * @return successors of this block.
+     */
+    public Collection<IBasicBlock> getSuccessors()
+    {
+        return this.successors;
+    }
+
+    /**
+     * Add a successor to this block.
+     * 
+     * @param succ - the successor block.
+     */
+    void addSuccessor(IBasicBlock succ)
+    {
+        if (this.successors.size() == 0)
+            this.successors = new HashSet<IBasicBlock>();
+
+        this.successors.add(succ);
+    }
+
+    void add(Instruction insn)
+    {
+        this.instructions.add(insn);
+    }
+
+    /**
+     * @return this Block's instructions' size.
+     */
+    public int size()
+    {
+        return this.instructions.size();
+    }
+
+    /**
+     * @return the Instruction at the specified index.
+     */
+    public Instruction get(int idx)
+    {
+        return this.instructions.get(idx);
+    }
+
+    /**
+     * @return this Block's instructions as a mutable list.
+     */
+    public List<Instruction> getInstructions()
+    {
+        return this.instructions;
+    }
+
+    /**
+     * Determines if control can fall through the end of the Block.
+     * This will look back through the Block's instructions,
+     * skipping any non-exectuable instructions such as debug instructions.
+     * Returns, throws, and unconditional branches will not fall through, but
+     * conditional branches and all other instructions will fall through.
+     * 
+     * @return true if control can fall through this Block.
+     */
+    public boolean canFallThrough()
+    {
+        //  Look for the last executable instruction;
+        //  skip non-executable instructions, e.g., debug
+        for ( int i = this.instructions.size() -1; i >= 0; i-- )
+        {
+            Instruction insn = this.instructions.get(i);
+
+            if (insn.isExecutable())
+            {
+                int prev_op = insn.getOpcode();
+                if (
+                    prev_op == ABCConstants.OP_jump ||
+                    prev_op == ABCConstants.OP_throw ||
+                    insn.isReturn())
+                {
+                    return false;
+                }
+                else
+                {
+                    return true;
+                }
+            }
+        }
+
+        //  No executable instructions in this block.
+        return true;
+    }
+}

http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/59f6373b/compiler/src/main/java/org/apache/flex/abc/semantics/ClassInfo.java
----------------------------------------------------------------------
diff --git a/compiler/src/main/java/org/apache/flex/abc/semantics/ClassInfo.java b/compiler/src/main/java/org/apache/flex/abc/semantics/ClassInfo.java
new file mode 100644
index 0000000..989a802
--- /dev/null
+++ b/compiler/src/main/java/org/apache/flex/abc/semantics/ClassInfo.java
@@ -0,0 +1,39 @@
+/*
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+package org.apache.flex.abc.semantics;
+
+import org.apache.flex.abc.semantics.MethodInfo;
+import org.apache.flex.abc.semantics.Traits;
+
+/**
+ * ClassInfo represents a class' <a href="http://learn.adobe.com/wiki/display/AVM2/4.9+Class">class info</a> structure.
+ */
+public class ClassInfo
+{
+    /**
+     * The class' static initialization method.
+     */
+    public MethodInfo cInit = null;
+    
+    /**
+     * The class' static traits.
+     */
+    public Traits classTraits = null;
+}

http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/59f6373b/compiler/src/main/java/org/apache/flex/abc/semantics/ControlFlowGraph.java
----------------------------------------------------------------------
diff --git a/compiler/src/main/java/org/apache/flex/abc/semantics/ControlFlowGraph.java b/compiler/src/main/java/org/apache/flex/abc/semantics/ControlFlowGraph.java
new file mode 100644
index 0000000..579f2f2
--- /dev/null
+++ b/compiler/src/main/java/org/apache/flex/abc/semantics/ControlFlowGraph.java
@@ -0,0 +1,580 @@
+/*
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+package org.apache.flex.abc.semantics;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.flex.abc.ABCConstants;
+import org.apache.flex.abc.graph.IBasicBlock;
+import org.apache.flex.abc.graph.IFlowgraph;
+import org.apache.flex.abc.graph.algorithms.DepthFirstPreorderIterator;
+import org.apache.flex.abc.graph.algorithms.DominatorTree;
+import org.apache.flex.abc.visitors.IFlowGraphVisitor;
+
+/**
+ * A ControlFlowGraph represents the flow of control through a sequence of
+ * instructions. The instructions are organized into a set of Blocks --
+ * sequences of instructions where normal control flow proceeds linearly from
+ * one instruction to the next -- and a set of edges, representing the
+ * discontinuous transfer of control points.
+
+ */
+public class ControlFlowGraph implements IFlowgraph
+{
+    /**
+     * Search direction in the initial block of a block-by-block search
+     * (typically for debug information).
+     */
+    private enum SearchDirection { Forward, Backward };
+
+    /**
+     * Create a ControlFlowGraph from the instructions in the MethodBodyInfo.
+     * @param mbi  - the MethodBodyInfo of the method whose control flow graph is to be computed.
+     */
+    ControlFlowGraph(MethodBodyInfo mbi)
+    {
+        this.mbi = mbi;
+        this.startBlock = newBlock();
+        buildCfg();
+    }
+
+    /**
+     *  This ControlFlowGraph's MethodBodyInfo.
+     */
+    private final MethodBodyInfo mbi;
+
+    /**
+     *  Blocks of the flow graph in entry order.
+     */
+    private ArrayList<IBasicBlock> blocks = new ArrayList<IBasicBlock>();
+
+    /**
+     * The entry point of this routine; known in
+     * graph theory as the start block.
+     */
+    private Block startBlock;
+
+    /**
+     *  Blocks keyed by their Labels.  Multiple Labels may map to a Block.
+     */
+    private Map<Label, IBasicBlock> blocksByLabel = new HashMap<Label, IBasicBlock>();
+
+    /**
+     *  Catch targets defined in this method.
+     */
+    private ArrayList<IBasicBlock> catchTargets = new ArrayList<IBasicBlock>();
+
+    /**
+     *  The graph's dominator tree, built on demand.
+     */
+    private DominatorTree dominatorTree = null;
+
+    /**
+     * Build the CFG.
+     */
+    private void buildCfg()
+    {
+        Block current_block = startBlock;
+
+        boolean last_block_transferred_control = false;
+
+        //  Sort the labels by position.
+        Collections.sort(mbi.getInstructionList().getActiveLabels());
+        Iterator<Label> labels = mbi.getInstructionList().getActiveLabels().iterator();
+        Label next_label = getNextLabel(labels);
+
+        //  Labels of a Block's successors, keyed by the "from" block.
+        //  These are turned into edges from the "from" block once all
+        //  blocks have been seen.
+        Map<Block, Collection<Label>> successor_labels = new HashMap<Block, Collection<Label>>();
+
+        //  Iterate through the method's instructions by position; the active Labels
+        //  have embedded position information that corresponds to this zero-based 
+        //  enumeration of the instructions.
+        List<Instruction> instructions = mbi.getInstructionList().getInstructions();
+
+        for (int i = 0; i < instructions.size(); i++)
+        {
+            if (i == next_label.getPosition() || last_block_transferred_control)
+            {
+                if ( current_block.size() > 0 )
+                {
+                    Block prev_block = current_block;
+                    current_block = newBlock();
+
+                    if (prev_block.canFallThrough())
+                    {
+                        // Add an edge from the prev block to the current block, since
+                        // control can fall through from that block to the current one.
+                        prev_block.addSuccessor(current_block);
+                    }
+                }
+                else
+                {
+                    // If the first instruction is a label target,
+                    // then the start block will still be empty;
+                    // this is actually OK for the flowgraph, but
+                    // would create a special case empty block
+                    // and complicate clients' block processing.
+                    assert current_block == startBlock;
+                }
+
+                //  Add catch targets to the CFG's mapping.
+                if (i == next_label.getPosition() && this.mbi.isCatchTarget(next_label))
+                    this.catchTargets.add(current_block);
+
+                //  Map all labels targeting this block.
+                while (next_label.getPosition() == i) 
+                {
+                    blocksByLabel.put(next_label, current_block);
+                    next_label = getNextLabel(labels);
+                }
+            }
+            
+            Instruction insn = instructions.get(i);
+            current_block.add(insn);
+
+            //  Begin a new block after any transfer of control.
+            last_block_transferred_control = insn.isTransferOfControl();
+
+            if (insn.isBranch())
+            {
+                Collection<Label> successors = successor_labels.get(current_block);
+                if (successors == null)
+                {
+                    successors = new ArrayList<Label>();
+                    successor_labels.put(current_block, successors);
+                }
+                
+                // The target may be a forward jump to a block we haven't seen,
+                // just store the label for now, and we'll get its block later after
+                // we've seen all the instructions.
+                if (insn.getOpcode() == ABCConstants.OP_lookupswitch)
+                {
+                    for (int j = 0; j < insn.getOperandCount(); j++)
+                    {
+                        if (insn.getOperand(j) instanceof Label)
+                            successors.add((Label)insn.getOperand(j));
+                    }
+                }
+                else
+                {
+                    successors.add(insn.getTarget());
+                }
+            }
+        }
+
+        // We've seen all the instructions now, so we can compute the blocks that
+        // each label corresponds to, and fill in the rest of the graph
+        for (Map.Entry<Block, Collection<Label>> entry : successor_labels.entrySet())
+            for (Label target_label : entry.getValue())
+            {
+                IBasicBlock target_block = getBlock(target_label);
+                if ( target_block != null )
+                    entry.getKey().addSuccessor(target_block);
+            }
+    }
+    
+    /**
+     *  Get a Label's target Block.
+     * 
+     *  @param l - the Label of interest.
+     *  @return the corresponding Block, or null if not found.
+     */
+    public IBasicBlock getBlock(Label l)
+    {
+        return blocksByLabel.get(l);
+    }
+
+    /**
+     *  Get the start block.
+     *  @return the start block.
+     */
+    public IBasicBlock getStartBlock()
+    {
+        return this.startBlock;
+    }
+
+    /**
+     *  Is the given Block a catch target?
+     *  @param b - the Block of interest.
+     *  @return true if the Block is a catch target.
+     *  @see MethodBodyInfo#isCatchTarget(Label)
+     *    which differs from this routine in that it uses 
+     *    positional information, which is not valid if the
+     *    ControlFlowGraph has been edited.
+     */
+    public boolean isCatchTarget(IBasicBlock b)
+    {
+        return this.catchTargets.contains(b);
+    }
+
+    /**
+     * Get an iterator that will iterate over the blocks in the graph.
+     * in depth-first preorder. This will traverse each edge in the graph
+     * once, but may return the same block multiple times if multiple edges lead
+     * to it.
+     */
+    public Iterable<IBasicBlock> blocksInControlFlowOrder()
+    {
+        return new Iterable<IBasicBlock>()
+        {
+            @Override
+            public Iterator<IBasicBlock> iterator()
+            {
+                return new DepthFirstPreorderIterator(ControlFlowGraph.this.getRoots());
+            }
+        };
+    }
+
+    /**
+     *  Start a new block.
+     * 
+     *  @return the new Block.
+     */
+    private Block newBlock()
+    {
+        Block result = new Block();
+        blocks.add(result);
+        return result;
+    }
+    
+    /**
+     *  @param labels - an Iterator of Labels.
+     *  @return the next Label in the sequence, or a marker
+     *  Label if the sequence is exhausted.
+     */
+    private Label getNextLabel(Iterator<Label> labels)
+    {
+        if (labels.hasNext())
+            return labels.next();
+        else
+            return END_OF_LABEL_SEQUENCE;
+    }
+
+    private static final Label END_OF_LABEL_SEQUENCE = new Label();
+
+    /**
+     *  Get the graph's blocks in their original order.
+     * 
+     *  @return an immutable List that presents the blocks in entry order.
+     */
+    public List<IBasicBlock> getBlocksInEntryOrder()
+    {
+        return Collections.unmodifiableList(this.blocks);
+    }
+
+    /**
+     *  Get the graph's blocks as a mutable list.
+     *  @return the blocks in a list.
+     */
+    List<IBasicBlock> getBlocks()
+    {
+        return this.blocks;
+    }
+
+    /**
+     * Walk a IFlowGraphVisitor over this CFG.
+     * 
+     *  @param visitor - the visitor.
+     */
+    public void traverseGraph(IFlowGraphVisitor visitor)
+    {
+        for (IBasicBlock b : this.blocksInControlFlowOrder())
+        {
+            if (visitor.visitBlock(b))
+            {
+                for (Instruction i : b.getInstructions())
+                    visitor.visitInstruction(i);
+
+                visitor.visitEnd(b);
+            }
+        }
+    }
+
+    /**
+     *  Touch the graph's dominator tree and fetch it.
+     *  @return the graph's dominator tree.
+     */
+    public DominatorTree getDominatorTree()
+    {
+        if ( this.dominatorTree == null )
+            this.dominatorTree = new DominatorTree(getRoots());
+
+        return this.dominatorTree;
+    }
+
+    /**
+     * Synthesize this flowgraph's "roots."  The flowgraph actually
+     * has only one root, the start block, but the exception handlers
+     * aren't reachable by normal control flow so for the moment they're
+     * considered alternate roots.
+     * @return the Collection of the start block and any exception handlers.
+     */
+    private Collection<IBasicBlock> getRoots()
+    {
+        Collection<IBasicBlock> roots = new ArrayList<IBasicBlock>();
+        roots.add(this.startBlock);
+        roots.addAll(this.catchTargets);
+
+        return roots;
+    }
+
+    /**
+     *  Remove an unreachable block from the CFG.
+     *  @param b - the Block to remove.  
+     *  @pre b must be unreachable.
+     */
+    public void removeUnreachableBlock(IBasicBlock b)
+    {
+        assert(!isReachable(b));
+
+        //  The CFG is naive about reachability 
+        //  of exception-handling targets, so
+        //  this procedure may remove blocks
+        //  that were formerly "reachable."
+        boolean removedFormerlyReachable = false;
+
+        //  Edit any exception-handlers that reference b.
+        for ( ExceptionInfo ex: this.mbi.getExceptions() )
+        {
+            if ( b.equals(getBlock(ex.getFrom())) )
+            {
+                if ( b.equals(getBlock(ex.getTo())) )
+                {
+                    //  This exception-handler is now dead.
+                    ex.setLive(false);
+                    this.catchTargets.remove(getBlock(ex.getTarget()));
+                    removedFormerlyReachable = true;
+                }
+                else
+                {
+                    //  Move the mapping of the From label
+                    //  to the next block in the covered region.
+                    int bIdx = this.blocks.indexOf(b);
+                    assert bIdx >= 0 && bIdx < this.blocks.size();
+
+                    this.blocksByLabel.put(ex.getFrom(), this.blocks.get(bIdx+1));
+                }
+            }
+            else if ( b.equals(getBlock(ex.getTo())) )
+            {
+                if ( b.equals(getBlock(ex.getFrom())) )
+                {
+                    //  This exception-handler is now dead.
+                    ex.setLive(false);
+                    this.catchTargets.remove(getBlock(ex.getTarget()));
+                    removedFormerlyReachable = true;
+                }
+                else
+                {
+                    //  Move the mapping of the To label
+                    //  to the previous block in the covered region.
+                    int bIdx = this.blocks.indexOf(b);
+                    assert bIdx >= 1 && bIdx < this.blocks.size();
+
+                    this.blocksByLabel.put(ex.getTo(), this.blocks.get(bIdx-1));
+                }
+            }
+        }
+
+        if ( removedFormerlyReachable )
+        {
+            //  The dominator tree needs to be recomputed.
+            this.dominatorTree = null;
+        }
+
+        //  Finally, remove the block itself.
+        this.blocks.remove(b);
+    }
+
+    /**
+     *  Is the given Block reachable?
+     *  @param b - the block of interest.
+     *  @return true if a path exists from
+     *    any "entry" block to b.
+     */
+    public boolean isReachable(IBasicBlock b)
+    {
+        return getDominatorTree().topologicalTraversal().contains(b);
+    }
+
+    /**
+     * Find the closest matching line number to the start of a block.
+     * @param b the block of interest.
+     * @return any initial debugline within the block, or the nearest
+     * debugline in the preceeding (entry-order) blocks.
+     */
+    public int findLineNumber(IBasicBlock b)
+    {
+        return findLineNumber(b, 0, SearchDirection.Forward);
+    }
+
+    /**
+     * Find the nearest debugline instruction preceeding the given
+     * (Block,offset) position and fetch its line number.
+     * @param b - the Block of interest.
+     * @param initialOffset - the start offset in the block.
+     * @return the closest debugline instruction's line number,
+     * or -1 if not found.
+     */
+    public int findLineNumber(IBasicBlock b, int initialOffset)
+    {
+        return findLineNumber(b, b.size()-1, SearchDirection.Backward);
+    }
+
+    /**
+     * Find the nearest debugline instruction preceeding the given
+     * (Block,offset) position and fetch its line number.
+     * @param b - the Block of interest.
+     * @param initialOffset - the start offset in the block.
+     * @param initialDirection Search the first block Forward or Backward.
+     * @return the closest debugline instruction's line number,
+     * or -1 if not found.
+     */
+    private int findLineNumber(IBasicBlock b, int initialOffset, SearchDirection initialDirection)
+    {
+        assert(this.blocks.contains(b));
+        // Start searching in the block given; the offset may
+        // be a pseudo-offset that specifies an initial forward
+        // search through non-executable instructions.
+        Instruction result = searchBlock(b, ABCConstants.OP_debugline, initialOffset, initialDirection);
+
+        if ( result != null )
+        {
+            //  Found a good line number match; return it as a zero-based offset.
+            return result.getImmediate() - 1;
+        }
+        else
+        {
+            //  Search backwards through the instruction stream; bump any line
+            //  number found during this search since the dead block is probably
+            //  on the next line.
+            for ( int i = this.blocks.indexOf(b) - 1; i >= 0 && result == null; i-- )
+            {
+                IBasicBlock candidate = this.blocks.get(i);
+                result = searchBlock( candidate, ABCConstants.OP_debugline, candidate.size() - 1, SearchDirection.Backward);
+            }
+        }
+
+        return result != null? result.getImmediate(): -1;
+    }
+
+    /**
+     * Find the nearest debugfile instruction to the start of
+     * the given block and fetch its source path.
+     * @param b - the Block of interest.
+     * @return the closest debugfile instruction's source path,
+     * or null if not found.
+     */
+    public String findSourcePath(IBasicBlock b)
+    {
+        return findSourcePath(b, 0, SearchDirection.Forward);
+    }
+    /**
+     * Find the nearest instruction preceeding the given
+     * (Block,offset) position and fetch its source path.
+     * @param b - the Block of interest.
+     * @param initialOffset - the start offset in the block.
+     * @return the closest debugfile instruction's source path,
+     * or null if not found.
+     */
+    public String findSourcePath(IBasicBlock b, int initialOffset)
+    {
+        return findSourcePath(b, initialOffset, SearchDirection.Backward);
+    }
+
+    /**
+     * Find the nearest debugline instruction to the given (Block, offset)
+     * position and fetch its source path.
+     * @param b the block of interest.
+     * @param initialOffset the start offset in the block.
+     * @param initialDirection Search the first block Forward or Backward.
+     * @return the closest debugfile instruction's source path,
+     * or null if not found.
+     */
+    private String findSourcePath(IBasicBlock b, int initialOffset, SearchDirection initialDirection)
+    {
+        assert(this.blocks.contains(b));
+
+        Instruction result = searchBlock(b, ABCConstants.OP_debugfile, initialOffset, initialDirection);
+
+        //  Search backwards through the instruction stream if necessary.
+        if ( result == null )
+        {
+            for ( int i = this.blocks.indexOf(b) - 1; i >= 0 && result == null; i-- )
+            {
+                IBasicBlock candidate = this.blocks.get(i);
+                result = searchBlock(candidate, ABCConstants.OP_debugfile, candidate.size() - 1, SearchDirection.Backward);
+            }
+        }
+
+        if (result == null)
+            return null;
+
+        // The debug filename can sometimes be in the format: sourcepath;package;filename so
+        // need to translate that back to an absolute path by replacing the ;'s with system
+        // path separators, making sure to normailize path separators.
+        String debugFilename = result.getOperand(0).toString();
+        char otherSeparator = File.separatorChar == '/' ? '\\' : '/';
+        debugFilename = debugFilename.replace(otherSeparator, File.separatorChar);
+        return debugFilename.replace(';', File.separatorChar);
+    }
+
+    /**
+     * Find an instruction in the given block, working 
+     * forwards or backwards from the specified offset.
+     * @param b the block of interest.
+     * @param opcode the opcode of the desired Instruction.
+     * @param initialOffset the offset at which to begin searching.
+     * @param direction Forward or Backward.
+     * @return the first instruction encountered with the specified
+     * opcode, or null if none found or the offset is out of range.
+     */
+    private Instruction searchBlock(IBasicBlock b, int opcode, int initialOffset, SearchDirection direction)
+    {
+        Instruction result = null;
+        int blockSize = b.size();
+
+        assert initialOffset >= 0 && initialOffset < blockSize: String.format("invalid initialOffset %d", initialOffset);
+
+        int offset = initialOffset;
+        while ( result == null && offset >= 0 && offset < blockSize )
+        {
+            Instruction candidate = b.get(offset);
+
+            if ( candidate.getOpcode() == opcode )
+                result = candidate;
+            else if ( direction == SearchDirection.Forward )
+                offset++;
+            else
+                offset--;
+        }
+
+        return result;
+    }
+}


Mime
View raw message