Bug 792597: implement exception handling (r=wmaddox)
authorTom Rodriguez <throdrig@adobe.com>
Tue, 23 Oct 2012 16:00:33 -0700
changeset 7570 a3f97c1a2bf8
parent 7569 d391629fbd64
child 7571 d9197807ec41
push id4262
push userdschaffe@adobe.com
push dateWed, 30 Jan 2013 19:01:31 +0000
reviewerswmaddox
bugs792597, 1128402
Bug 792597: implement exception handling (r=wmaddox) CL@1128402
halfmoon/generated/InstrFactory_defs_impl.hh
halfmoon/generated/InstrFactory_defs_proto.hh
halfmoon/generated/InstrFactory_preds_impl.hh
halfmoon/generated/InstrFactory_preds_proto.hh
halfmoon/generated/InstrFactory_signatures_impl.hh
halfmoon/generated/KindAdapter_cases.hh
halfmoon/generated/KindAdapter_methods.hh
halfmoon/generated/ShapeAdapter_cases.hh
halfmoon/generated/ShapeAdapter_methods.hh
halfmoon/generated/Stub_callers.hh
halfmoon/generated/Stub_lirtable.hh
halfmoon/generated/Stub_protos.hh
halfmoon/halfmoon.h
halfmoon/hm-abcbuilder.cpp
halfmoon/hm-abcbuilder.h
halfmoon/hm-abcgraph.cpp
halfmoon/hm-abcgraph.h
halfmoon/hm-algorithms.h
halfmoon/hm-bailout-stubs.h
halfmoon/hm-bailouts.cpp
halfmoon/hm-bailouts.h
halfmoon/hm-check.cpp
halfmoon/hm-check.h
halfmoon/hm-cleaner.cpp
halfmoon/hm-cleaner.h
halfmoon/hm-constraints.cpp
halfmoon/hm-constraints.h
halfmoon/hm-dead.cpp
halfmoon/hm-dead.h
halfmoon/hm-debug-inlines.h
halfmoon/hm-debug.cpp
halfmoon/hm-debug.h
halfmoon/hm-dispatch.h
halfmoon/hm-dominatortree.cpp
halfmoon/hm-dominatortree.h
halfmoon/hm-exec.cpp
halfmoon/hm-identityanalyzer.cpp
halfmoon/hm-identityanalyzer.h
halfmoon/hm-inline.cpp
halfmoon/hm-inline.h
halfmoon/hm-instr.h
halfmoon/hm-instrfactory.cpp
halfmoon/hm-instrfactory.h
halfmoon/hm-instrgraph.cpp
halfmoon/hm-instrgraph.h
halfmoon/hm-interpreter.h
halfmoon/hm-jitmanager.cpp
halfmoon/hm-jitmanager.h
halfmoon/hm-jitwriter.cpp
halfmoon/hm-liremitter.cpp
halfmoon/hm-liremitter.h
halfmoon/hm-main.cpp
halfmoon/hm-main.h
halfmoon/hm-models.cpp
halfmoon/hm-models.h
halfmoon/hm-prettyprint.cpp
halfmoon/hm-prettyprint.h
halfmoon/hm-profiler.cpp
halfmoon/hm-profiler.h
halfmoon/hm-schedulers.cpp
halfmoon/hm-schedulers.h
halfmoon/hm-specializer.cpp
halfmoon/hm-specializer.h
halfmoon/hm-stubs.cpp
halfmoon/hm-templatebuilder.cpp
halfmoon/hm-templatebuilder.h
halfmoon/hm-typeanalyzer.cpp
halfmoon/hm-typeanalyzer.h
halfmoon/hm-typeinference.cpp
halfmoon/hm-typeinference.h
halfmoon/hm-types.cpp
halfmoon/hm-types.h
halfmoon/hm-util.h
halfmoon/hm-valnum.cpp
halfmoon/hm-valnum.h
halfmoon/templates/ast.py
halfmoon/templates/hrdefs.py
nanojit/Assembler.cpp
--- a/halfmoon/generated/InstrFactory_defs_impl.hh
+++ b/halfmoon/generated/InstrFactory_defs_impl.hh
@@ -3,48 +3,51 @@
 ///
 
 /// shape_reps[] gives the representations of
 /// the shapes enumerated by InstrShape.
 ///
 extern const ShapeRep shape_reps[SHAPE_MAX] = {
   { 0, 0, kVarIn },     // GOTOINSTR_SHAPE
   { 0, 0, kVarOut },    // ARMINSTR_SHAPE
+  { 0, 0, kVarOut },    // CATCHBLOCKINSTR_SHAPE
   { 0, 0, kVarOut },    // LABELINSTR_SHAPE
   { 0, 1, kVarNone },   // CONSTANTEXPR_SHAPE
   { 0, 1, kVarOut },    // STARTINSTR_SHAPE
   { 1, 0, kVarIn },     // IFINSTR_SHAPE
   { 1, 0, kVarIn },     // SWITCHINSTR_SHAPE
   { 1, 1, kVarNone },   // UNARYEXPR_SHAPE
   { 2, 1, kVarNone },   // BINARYEXPR_SHAPE
   { 2, 1, kVarNone },   // SETLOCALINSTR_SHAPE
   { 1, 0, kVarIn },     // STOPINSTR_SHAPE
   { 1, 1, kVarIn },     // DEOPTSAFEPOINTINSTR_SHAPE
   { 1, 1, kVarNone },   // DEOPTFINISHINSTR_SHAPE
   { 1, 1, kVarNone },   // VOIDSTMT_SHAPE
   { 1, 2, kVarIn },     // NARYSTMT0_SHAPE
+  { 1, 2, kVarIn },     // SAFEPOINTINSTR_SHAPE
+  { 2, 1, kVarNone },   // DEBUGINSTR_SHAPE
   { 2, 1, kVarNone },   // DEOPTFINISHCALLINSTR_SHAPE
   { 2, 2, kVarIn },     // NARYSTMT1_SHAPE
-  { 2, 2, kVarNone },   // SAFEPOINTINSTR_SHAPE
   { 2, 2, kVarNone },   // UNARYSTMT_SHAPE
   { 3, 2, kVarIn },     // CALLSTMT2_SHAPE
   { 3, 2, kVarIn },     // NARYSTMT2_SHAPE
   { 3, 2, kVarNone },   // BINARYSTMT_SHAPE
   { 3, 4, kVarNone },   // HASNEXT2STMT_SHAPE
   { 4, 2, kVarIn },     // CALLSTMT3_SHAPE
   { 4, 2, kVarIn },     // NARYSTMT3_SHAPE
   { 5, 2, kVarIn },     // CALLSTMT4_SHAPE
   { 5, 2, kVarIn },     // NARYSTMT4_SHAPE
 };
 
 /// instr_attrs describes the instructions enumerated in InstrKind.
 ///
 extern const InstrAttrs instr_attrs[HR_MAX] = {
   { "start",                 STARTINSTR_SHAPE, false },
   { "template",              STARTINSTR_SHAPE, false },
+  { "catchblock",            CATCHBLOCKINSTR_SHAPE, false },
   { "return",                STOPINSTR_SHAPE, false },
   { "throw",                 STOPINSTR_SHAPE, false },
   { "goto",                  GOTOINSTR_SHAPE, false },
   { "label",                 LABELINSTR_SHAPE, false },
   { "if",                    IFINSTR_SHAPE, false },
   { "switch",                SWITCHINSTR_SHAPE, false },
   { "arm",                   ARMINSTR_SHAPE, false },
   { "const",                 CONSTANTEXPR_SHAPE, false },
@@ -71,16 +74,17 @@ extern const InstrAttrs instr_attrs[HR_M
   { "loadenv_boolean",       BINARYEXPR_SHAPE, false },
   { "loadenv_number",        BINARYEXPR_SHAPE, false },
   { "loadenv_string",        BINARYEXPR_SHAPE, false },
   { "loadenv_interface",     BINARYEXPR_SHAPE, false },
   { "loadenv",               BINARYEXPR_SHAPE, false },
   { "loadenv_atom",          BINARYEXPR_SHAPE, false },
   { "loadinitenv",           UNARYEXPR_SHAPE, false },
   { "loadsuperinitenv",      UNARYEXPR_SHAPE, false },
+  { "loadenv_env",           BINARYEXPR_SHAPE, false },
   { "newobject",             NARYSTMT0_SHAPE, false },
   { "newarray",              NARYSTMT0_SHAPE, false },
   { "applytype",             NARYSTMT0_SHAPE, false },
   { "newinstance",           UNARYEXPR_SHAPE, false },
   { "abc_convert_s",         UNARYSTMT_SHAPE, false },
   { "abc_esc_xelem",         UNARYSTMT_SHAPE, false },
   { "abc_esc_xattr",         UNARYSTMT_SHAPE, false },
   { "abc_typeof",            UNARYEXPR_SHAPE, false },
@@ -121,23 +125,23 @@ extern const InstrAttrs instr_attrs[HR_M
   { "rshi",                  BINARYEXPR_SHAPE, false },
   { "rshui",                 BINARYEXPR_SHAPE, false },
   { "noti",                  UNARYEXPR_SHAPE, false },
   { "negi",                  UNARYEXPR_SHAPE, false },
   { "negd",                  UNARYEXPR_SHAPE, false },
   { "not",                   UNARYEXPR_SHAPE, false },
   { "newactivation",         UNARYSTMT_SHAPE, false },
   { "abc_finddef",           BINARYSTMT_SHAPE, false },
-  { "abc_findpropstrict",    NARYSTMT2_SHAPE, false },
-  { "abc_findpropstrictx",   NARYSTMT3_SHAPE, false },
-  { "abc_findpropstrictns",  NARYSTMT3_SHAPE, false },
+  { "abc_findpropstrict",    NARYSTMT3_SHAPE, false },
+  { "abc_findpropstrictx",   NARYSTMT4_SHAPE, false },
+  { "abc_findpropstrictns",  NARYSTMT4_SHAPE, false },
   { "abc_findpropstrictnsx", NARYSTMT4_SHAPE, false },
-  { "abc_findproperty",      NARYSTMT2_SHAPE, false },
-  { "abc_findpropertyx",     NARYSTMT3_SHAPE, false },
-  { "abc_findpropertyns",    NARYSTMT3_SHAPE, false },
+  { "abc_findproperty",      NARYSTMT3_SHAPE, false },
+  { "abc_findpropertyx",     NARYSTMT4_SHAPE, false },
+  { "abc_findpropertyns",    NARYSTMT4_SHAPE, false },
   { "abc_findpropertynsx",   NARYSTMT4_SHAPE, false },
   { "newclass",              NARYSTMT2_SHAPE, false },
   { "newfunction",           NARYSTMT1_SHAPE, false },
   { "abc_getsuper",          CALLSTMT2_SHAPE, false },
   { "abc_getsuperx",         CALLSTMT3_SHAPE, false },
   { "abc_getsuperns",        CALLSTMT3_SHAPE, false },
   { "abc_getsupernsx",       CALLSTMT4_SHAPE, false },
   { "abc_getdescendants",    CALLSTMT2_SHAPE, false },
@@ -169,29 +173,32 @@ extern const InstrAttrs instr_attrs[HR_M
   { "slottype",              BINARYEXPR_SHAPE, false },
   { "getouterscope",         BINARYEXPR_SHAPE, false },
   { "safepoint",             SAFEPOINTINSTR_SHAPE, false },
   { "setlocal",              SETLOCALINSTR_SHAPE, false },
   { "newstate",              CONSTANTEXPR_SHAPE, false },
   { "deopt_safepoint",       DEOPTSAFEPOINTINSTR_SHAPE, false },
   { "deopt_finish",          DEOPTFINISHINSTR_SHAPE, false },
   { "deopt_finishcall",      DEOPTFINISHCALLINSTR_SHAPE, false },
+  { "debugline",             DEBUGINSTR_SHAPE, false },
+  { "debugfile",             DEBUGINSTR_SHAPE, false },
   { "string2atom",           UNARYEXPR_SHAPE, false },
   { "double2atom",           UNARYEXPR_SHAPE, false },
   { "int2atom",              UNARYEXPR_SHAPE, false },
   { "uint2atom",             UNARYEXPR_SHAPE, false },
   { "scriptobject2atom",     UNARYEXPR_SHAPE, false },
   { "bool2atom",             UNARYEXPR_SHAPE, false },
   { "ns2atom",               UNARYEXPR_SHAPE, false },
   { "atom2bool",             UNARYEXPR_SHAPE, false },
   { "atom2double",           UNARYEXPR_SHAPE, false },
   { "atom2string",           UNARYEXPR_SHAPE, false },
   { "atom2int",              UNARYEXPR_SHAPE, false },
   { "atom2uint",             UNARYEXPR_SHAPE, false },
   { "atom2scriptobject",     UNARYEXPR_SHAPE, false },
+  { "atom2ns",               UNARYEXPR_SHAPE, false },
   { "i2d",                   UNARYEXPR_SHAPE, false },
   { "u2d",                   UNARYEXPR_SHAPE, false },
   { "d2i",                   UNARYEXPR_SHAPE, false },
   { "d2u",                   UNARYEXPR_SHAPE, false },
   { "toslot",                BINARYEXPR_SHAPE, false },
   { "toprimitive",           UNARYSTMT_SHAPE, false },
   { "eqi",                   BINARYEXPR_SHAPE, false },
   { "lti",                   BINARYEXPR_SHAPE, false },
--- a/halfmoon/generated/InstrFactory_defs_proto.hh
+++ b/halfmoon/generated/InstrFactory_defs_proto.hh
@@ -2,16 +2,17 @@
 /// generated by templates.py -- do not edit
 ///
 
 /// High level intermediate representation (HR) opcodes
 ///
 enum InstrKind {
   HR_start,                 // StartInstr 
   HR_template,              // StartInstr 
+  HR_catchblock,            // CatchBlockInstr 
   HR_return,                // StopInstr 
   HR_throw,                 // StopInstr 
   HR_goto,                  // GotoInstr 
   HR_label,                 // LabelInstr 
   HR_if,                    // IfInstr 
   HR_switch,                // SwitchInstr 
   HR_arm,                   // ArmInstr 
   HR_const,                 // ConstantExpr 
@@ -38,16 +39,17 @@ enum InstrKind {
   HR_loadenv_boolean,       // BinaryExpr 
   HR_loadenv_number,        // BinaryExpr 
   HR_loadenv_string,        // BinaryExpr 
   HR_loadenv_interface,     // BinaryExpr 
   HR_loadenv,               // BinaryExpr 
   HR_loadenv_atom,          // BinaryExpr 
   HR_loadinitenv,           // UnaryExpr 
   HR_loadsuperinitenv,      // UnaryExpr 
+  HR_loadenv_env,           // BinaryExpr 
   HR_newobject,             // NaryStmt0 
   HR_newarray,              // NaryStmt0 
   HR_applytype,             // NaryStmt0 
   HR_newinstance,           // UnaryExpr 
   HR_abc_convert_s,         // UnaryStmt 
   HR_abc_esc_xelem,         // UnaryStmt 
   HR_abc_esc_xattr,         // UnaryStmt 
   HR_abc_typeof,            // UnaryExpr 
@@ -88,23 +90,23 @@ enum InstrKind {
   HR_rshi,                  // BinaryExpr 
   HR_rshui,                 // BinaryExpr 
   HR_noti,                  // UnaryExpr 
   HR_negi,                  // UnaryExpr 
   HR_negd,                  // UnaryExpr 
   HR_not,                   // UnaryExpr 
   HR_newactivation,         // UnaryStmt 
   HR_abc_finddef,           // BinaryStmt 
-  HR_abc_findpropstrict,    // NaryStmt2 
-  HR_abc_findpropstrictx,   // NaryStmt3 
-  HR_abc_findpropstrictns,  // NaryStmt3 
+  HR_abc_findpropstrict,    // NaryStmt3 
+  HR_abc_findpropstrictx,   // NaryStmt4 
+  HR_abc_findpropstrictns,  // NaryStmt4 
   HR_abc_findpropstrictnsx, // NaryStmt4 
-  HR_abc_findproperty,      // NaryStmt2 
-  HR_abc_findpropertyx,     // NaryStmt3 
-  HR_abc_findpropertyns,    // NaryStmt3 
+  HR_abc_findproperty,      // NaryStmt3 
+  HR_abc_findpropertyx,     // NaryStmt4 
+  HR_abc_findpropertyns,    // NaryStmt4 
   HR_abc_findpropertynsx,   // NaryStmt4 
   HR_newclass,              // NaryStmt2 
   HR_newfunction,           // NaryStmt1 
   HR_abc_getsuper,          // CallStmt2 
   HR_abc_getsuperx,         // CallStmt3 
   HR_abc_getsuperns,        // CallStmt3 
   HR_abc_getsupernsx,       // CallStmt4 
   HR_abc_getdescendants,    // CallStmt2 
@@ -136,29 +138,32 @@ enum InstrKind {
   HR_slottype,              // BinaryExpr 
   HR_getouterscope,         // BinaryExpr 
   HR_safepoint,             // SafepointInstr 
   HR_setlocal,              // SetlocalInstr 
   HR_newstate,              // ConstantExpr 
   HR_deopt_safepoint,       // DeoptSafepointInstr 
   HR_deopt_finish,          // DeoptFinishInstr 
   HR_deopt_finishcall,      // DeoptFinishCallInstr 
+  HR_debugline,             // DebugInstr 
+  HR_debugfile,             // DebugInstr 
   HR_string2atom,           // UnaryExpr 
   HR_double2atom,           // UnaryExpr 
   HR_int2atom,              // UnaryExpr 
   HR_uint2atom,             // UnaryExpr 
   HR_scriptobject2atom,     // UnaryExpr 
   HR_bool2atom,             // UnaryExpr 
   HR_ns2atom,               // UnaryExpr 
   HR_atom2bool,             // UnaryExpr 
   HR_atom2double,           // UnaryExpr 
   HR_atom2string,           // UnaryExpr 
   HR_atom2int,              // UnaryExpr 
   HR_atom2uint,             // UnaryExpr 
   HR_atom2scriptobject,     // UnaryExpr 
+  HR_atom2ns,               // UnaryExpr 
   HR_i2d,                   // UnaryExpr 
   HR_u2d,                   // UnaryExpr 
   HR_d2i,                   // UnaryExpr 
   HR_d2u,                   // UnaryExpr 
   HR_toslot,                // BinaryExpr 
   HR_toprimitive,           // UnaryStmt 
   HR_eqi,                   // BinaryExpr 
   HR_lti,                   // BinaryExpr 
@@ -370,41 +375,43 @@ struct ShapeRep {
 
 /// InstrShape is an enumeration of HR instruction shapes.
 /// The representation details of each InstrShape s is described by
 /// shape_reps[s].
 ///
 enum InstrShape {
   GOTOINSTR_SHAPE,          // 0, 0, kVarIn             1 instrs
   ARMINSTR_SHAPE,           // 0, 0, kVarOut            1 instrs
+  CATCHBLOCKINSTR_SHAPE,    // 0, 0, kVarOut            1 instrs
   LABELINSTR_SHAPE,         // 0, 0, kVarOut            1 instrs
   CONSTANTEXPR_SHAPE,       // 0, 1, kVarNone           3 instrs
   STARTINSTR_SHAPE,         // 0, 1, kVarOut            2 instrs
   IFINSTR_SHAPE,            // 1, 0, kVarIn             1 instrs
   SWITCHINSTR_SHAPE,        // 1, 0, kVarIn             1 instrs
-  UNARYEXPR_SHAPE,          // 1, 1, kVarNone           32 instrs
-  BINARYEXPR_SHAPE,         // 2, 1, kVarNone           56 instrs
+  UNARYEXPR_SHAPE,          // 1, 1, kVarNone           33 instrs
+  BINARYEXPR_SHAPE,         // 2, 1, kVarNone           57 instrs
   SETLOCALINSTR_SHAPE,      // 2, 1, kVarNone           1 instrs
   STOPINSTR_SHAPE,          // 1, 0, kVarIn             2 instrs
   DEOPTSAFEPOINTINSTR_SHAPE, // 1, 1, kVarIn             1 instrs
   DEOPTFINISHINSTR_SHAPE,   // 1, 1, kVarNone           1 instrs
   VOIDSTMT_SHAPE,           // 1, 1, kVarNone           0 instrs
   NARYSTMT0_SHAPE,          // 1, 2, kVarIn             3 instrs
+  SAFEPOINTINSTR_SHAPE,     // 1, 2, kVarIn             1 instrs
+  DEBUGINSTR_SHAPE,         // 2, 1, kVarNone           2 instrs
   DEOPTFINISHCALLINSTR_SHAPE, // 2, 1, kVarNone           1 instrs
   NARYSTMT1_SHAPE,          // 2, 2, kVarIn             3 instrs
-  SAFEPOINTINSTR_SHAPE,     // 2, 2, kVarNone           1 instrs
   UNARYSTMT_SHAPE,          // 2, 2, kVarNone           42 instrs
   CALLSTMT2_SHAPE,          // 3, 2, kVarIn             39 instrs
-  NARYSTMT2_SHAPE,          // 3, 2, kVarIn             3 instrs
+  NARYSTMT2_SHAPE,          // 3, 2, kVarIn             1 instrs
   BINARYSTMT_SHAPE,         // 3, 2, kVarNone           73 instrs
   HASNEXT2STMT_SHAPE,       // 3, 4, kVarNone           1 instrs
   CALLSTMT3_SHAPE,          // 4, 2, kVarIn             52 instrs
-  NARYSTMT3_SHAPE,          // 4, 2, kVarIn             4 instrs
+  NARYSTMT3_SHAPE,          // 4, 2, kVarIn             2 instrs
   CALLSTMT4_SHAPE,          // 5, 2, kVarIn             11 instrs
-  NARYSTMT4_SHAPE,          // 5, 2, kVarIn             2 instrs
+  NARYSTMT4_SHAPE,          // 5, 2, kVarIn             6 instrs
   SHAPE_MAX = NARYSTMT4_SHAPE + 1
 };
 
 /// shape_reps[] gives the representations of
 /// the shapes enumerated by InstrShape.
 ///
 extern const ShapeRep shape_reps[SHAPE_MAX];
 
--- a/halfmoon/generated/InstrFactory_preds_impl.hh
+++ b/halfmoon/generated/InstrFactory_preds_impl.hh
@@ -12,16 +12,21 @@ bool InstrFactory::isGotoInstr(InstrKind
   return instr_attrs[k].shape == GOTOINSTR_SHAPE;
 }
 
 /// true if given InstrKind is instance of ArmInstr
 bool InstrFactory::isArmInstr(InstrKind k) {
   return instr_attrs[k].shape == ARMINSTR_SHAPE;
 }
 
+/// true if given InstrKind is instance of CatchBlockInstr
+bool InstrFactory::isCatchBlockInstr(InstrKind k) {
+  return instr_attrs[k].shape == CATCHBLOCKINSTR_SHAPE;
+}
+
 /// true if given InstrKind is instance of LabelInstr
 bool InstrFactory::isLabelInstr(InstrKind k) {
   return instr_attrs[k].shape == LABELINSTR_SHAPE;
 }
 
 /// true if given InstrKind is instance of ConstantExpr
 bool InstrFactory::isConstantExpr(InstrKind k) {
   return instr_attrs[k].shape == CONSTANTEXPR_SHAPE;
@@ -77,31 +82,36 @@ bool InstrFactory::isVoidStmt(InstrKind 
   return instr_attrs[k].shape == VOIDSTMT_SHAPE;
 }
 
 /// true if given InstrKind is instance of NaryStmt0
 bool InstrFactory::isNaryStmt0(InstrKind k) {
   return instr_attrs[k].shape == NARYSTMT0_SHAPE;
 }
 
+/// true if given InstrKind is instance of SafepointInstr
+bool InstrFactory::isSafepointInstr(InstrKind k) {
+  return instr_attrs[k].shape == SAFEPOINTINSTR_SHAPE;
+}
+
+/// true if given InstrKind is instance of DebugInstr
+bool InstrFactory::isDebugInstr(InstrKind k) {
+  return instr_attrs[k].shape == DEBUGINSTR_SHAPE;
+}
+
 /// true if given InstrKind is instance of DeoptFinishCallInstr
 bool InstrFactory::isDeoptFinishCallInstr(InstrKind k) {
   return instr_attrs[k].shape == DEOPTFINISHCALLINSTR_SHAPE;
 }
 
 /// true if given InstrKind is instance of NaryStmt1
 bool InstrFactory::isNaryStmt1(InstrKind k) {
   return instr_attrs[k].shape == NARYSTMT1_SHAPE;
 }
 
-/// true if given InstrKind is instance of SafepointInstr
-bool InstrFactory::isSafepointInstr(InstrKind k) {
-  return instr_attrs[k].shape == SAFEPOINTINSTR_SHAPE;
-}
-
 /// true if given InstrKind is instance of UnaryStmt
 bool InstrFactory::isUnaryStmt(InstrKind k) {
   return instr_attrs[k].shape == UNARYSTMT_SHAPE;
 }
 
 /// true if given InstrKind is instance of CallStmt2
 bool InstrFactory::isCallStmt2(InstrKind k) {
   return instr_attrs[k].shape == CALLSTMT2_SHAPE;
--- a/halfmoon/generated/InstrFactory_preds_proto.hh
+++ b/halfmoon/generated/InstrFactory_preds_proto.hh
@@ -6,16 +6,19 @@
 static bool hasTemplate(InstrKind k);
 
 /// true if given InstrKind is instance of GotoInstr
 static bool isGotoInstr(InstrKind k);
 
 /// true if given InstrKind is instance of ArmInstr
 static bool isArmInstr(InstrKind k);
 
+/// true if given InstrKind is instance of CatchBlockInstr
+static bool isCatchBlockInstr(InstrKind k);
+
 /// true if given InstrKind is instance of LabelInstr
 static bool isLabelInstr(InstrKind k);
 
 /// true if given InstrKind is instance of ConstantExpr
 static bool isConstantExpr(InstrKind k);
 
 /// true if given InstrKind is instance of StartInstr
 static bool isStartInstr(InstrKind k);
@@ -45,25 +48,28 @@ static bool isDeoptSafepointInstr(InstrK
 static bool isDeoptFinishInstr(InstrKind k);
 
 /// true if given InstrKind is instance of VoidStmt
 static bool isVoidStmt(InstrKind k);
 
 /// true if given InstrKind is instance of NaryStmt0
 static bool isNaryStmt0(InstrKind k);
 
+/// true if given InstrKind is instance of SafepointInstr
+static bool isSafepointInstr(InstrKind k);
+
+/// true if given InstrKind is instance of DebugInstr
+static bool isDebugInstr(InstrKind k);
+
 /// true if given InstrKind is instance of DeoptFinishCallInstr
 static bool isDeoptFinishCallInstr(InstrKind k);
 
 /// true if given InstrKind is instance of NaryStmt1
 static bool isNaryStmt1(InstrKind k);
 
-/// true if given InstrKind is instance of SafepointInstr
-static bool isSafepointInstr(InstrKind k);
-
 /// true if given InstrKind is instance of UnaryStmt
 static bool isUnaryStmt(InstrKind k);
 
 /// true if given InstrKind is instance of CallStmt2
 static bool isCallStmt2(InstrKind k);
 
 /// true if given InstrKind is instance of NaryStmt2
 static bool isNaryStmt2(InstrKind k);
--- a/halfmoon/generated/InstrFactory_signatures_impl.hh
+++ b/halfmoon/generated/InstrFactory_signatures_impl.hh
@@ -9,16 +9,20 @@ const Type** InstrFactory::buildInputSig
   case HR_start: {
     /* start: () -> (Effect, Top) */
     return NULL;
   }
   case HR_template: {
     /* template: () -> (Effect, Top) */
     return NULL;
   }
+  case HR_catchblock: {
+    /* catchblock: () -> Top */
+    return NULL;
+  }
   case HR_return: {
     /* return: (Effect, Top) -> () */
     const Type* input_sig[] = { EFFECT, TOP };
     return copySig(2, input_sig);
   }
   case HR_throw: {
     /* throw: (Effect, Atom) -> () */
     const Type* input_sig[] = { EFFECT, lattice_.atom_type[kTypeNullable] };
@@ -179,16 +183,21 @@ const Type** InstrFactory::buildInputSig
   case HR_loadinitenv: {
     /* loadinitenv: ScriptObject~ -> Env */
     return copySig(lattice_.scriptobject_type[kTypeNotNull]);
   }
   case HR_loadsuperinitenv: {
     /* loadsuperinitenv: Env -> Env */
     return copySig(ENV);
   }
+  case HR_loadenv_env: {
+    /* loadenv_env: (Ord, Env) -> Env */
+    const Type* input_sig[] = { ORDINAL, ENV };
+    return copySig(2, input_sig);
+  }
   case HR_newobject: {
     /* newobject: (Effect, Atom) -> (Effect, ScriptObject~) */
     const Type* input_sig[] = { EFFECT, lattice_.atom_type[kTypeNullable] };
     return copySig(2, input_sig);
   }
   case HR_newarray: {
     /* newarray: (Effect, Atom) -> (Effect, Array~) */
     const Type* input_sig[] = { EFFECT, lattice_.atom_type[kTypeNullable] };
@@ -423,53 +432,53 @@ const Type** InstrFactory::buildInputSig
     return copySig(2, input_sig);
   }
   case HR_abc_finddef: {
     /* abc_finddef: (Effect, Name, Env) -> (Effect, ScriptObject~) */
     const Type* input_sig[] = { EFFECT, NAME, ENV };
     return copySig(3, input_sig);
   }
   case HR_abc_findpropstrict: {
-    /* abc_findpropstrict: (Effect, Name, Env, Atom~) -> (Effect, Atom~) */
-    const Type* input_sig[] = { EFFECT, NAME, ENV, lattice_.atom_type[kTypeNotNull] };
-    return copySig(4, input_sig);
+    /* abc_findpropstrict: (Effect, Name, Env, Ord, Atom~) -> (Effect, Atom~) */
+    const Type* input_sig[] = { EFFECT, NAME, ENV, ORDINAL, lattice_.atom_type[kTypeNotNull] };
+    return copySig(5, input_sig);
   }
   case HR_abc_findpropstrictx: {
-    /* abc_findpropstrictx: (Effect, Name, Env, Atom, Atom~) -> (Effect, Atom~) */
-    const Type* input_sig[] = { EFFECT, NAME, ENV, lattice_.atom_type[kTypeNullable], lattice_.atom_type[kTypeNotNull] };
-    return copySig(5, input_sig);
+    /* abc_findpropstrictx: (Effect, Name, Env, Ord, Atom, Atom~) -> (Effect, Atom~) */
+    const Type* input_sig[] = { EFFECT, NAME, ENV, ORDINAL, lattice_.atom_type[kTypeNullable], lattice_.atom_type[kTypeNotNull] };
+    return copySig(6, input_sig);
   }
   case HR_abc_findpropstrictns: {
-    /* abc_findpropstrictns: (Effect, Name, Env, Atom, Atom~) -> (Effect, Atom~) */
-    const Type* input_sig[] = { EFFECT, NAME, ENV, lattice_.atom_type[kTypeNullable], lattice_.atom_type[kTypeNotNull] };
-    return copySig(5, input_sig);
+    /* abc_findpropstrictns: (Effect, Name, Env, Ord, Atom, Atom~) -> (Effect, Atom~) */
+    const Type* input_sig[] = { EFFECT, NAME, ENV, ORDINAL, lattice_.atom_type[kTypeNullable], lattice_.atom_type[kTypeNotNull] };
+    return copySig(6, input_sig);
   }
   case HR_abc_findpropstrictnsx: {
-    /* abc_findpropstrictnsx: (Effect, Name, Env, Atom, Atom, Atom~) -> (Effect, Atom~) */
-    const Type* input_sig[] = { EFFECT, NAME, ENV, lattice_.atom_type[kTypeNullable], lattice_.atom_type[kTypeNullable], lattice_.atom_type[kTypeNotNull] };
+    /* abc_findpropstrictnsx: (Effect, Name, Env, Ord, Atom, Atom~) -> (Effect, Atom~) */
+    const Type* input_sig[] = { EFFECT, NAME, ENV, ORDINAL, lattice_.atom_type[kTypeNullable], lattice_.atom_type[kTypeNotNull] };
     return copySig(6, input_sig);
   }
   case HR_abc_findproperty: {
-    /* abc_findproperty: (Effect, Name, Env, Atom~) -> (Effect, Atom~) */
-    const Type* input_sig[] = { EFFECT, NAME, ENV, lattice_.atom_type[kTypeNotNull] };
-    return copySig(4, input_sig);
+    /* abc_findproperty: (Effect, Name, Env, Ord, Atom~) -> (Effect, Atom~) */
+    const Type* input_sig[] = { EFFECT, NAME, ENV, ORDINAL, lattice_.atom_type[kTypeNotNull] };
+    return copySig(5, input_sig);
   }
   case HR_abc_findpropertyx: {
-    /* abc_findpropertyx: (Effect, Name, Env, Atom, Atom~) -> (Effect, Atom~) */
-    const Type* input_sig[] = { EFFECT, NAME, ENV, lattice_.atom_type[kTypeNullable], lattice_.atom_type[kTypeNotNull] };
-    return copySig(5, input_sig);
+    /* abc_findpropertyx: (Effect, Name, Env, Ord, Atom, Atom~) -> (Effect, Atom~) */
+    const Type* input_sig[] = { EFFECT, NAME, ENV, ORDINAL, lattice_.atom_type[kTypeNullable], lattice_.atom_type[kTypeNotNull] };
+    return copySig(6, input_sig);
   }
   case HR_abc_findpropertyns: {
-    /* abc_findpropertyns: (Effect, Name, Env, Atom, Atom~) -> (Effect, Atom~) */
-    const Type* input_sig[] = { EFFECT, NAME, ENV, lattice_.atom_type[kTypeNullable], lattice_.atom_type[kTypeNotNull] };
-    return copySig(5, input_sig);
+    /* abc_findpropertyns: (Effect, Name, Env, Ord, Atom, Atom~) -> (Effect, Atom~) */
+    const Type* input_sig[] = { EFFECT, NAME, ENV, ORDINAL, lattice_.atom_type[kTypeNullable], lattice_.atom_type[kTypeNotNull] };
+    return copySig(6, input_sig);
   }
   case HR_abc_findpropertynsx: {
-    /* abc_findpropertynsx: (Effect, Name, Env, Atom, Atom, Atom~) -> (Effect, Atom~) */
-    const Type* input_sig[] = { EFFECT, NAME, ENV, lattice_.atom_type[kTypeNullable], lattice_.atom_type[kTypeNullable], lattice_.atom_type[kTypeNotNull] };
+    /* abc_findpropertynsx: (Effect, Name, Env, Ord, Atom, Atom~) -> (Effect, Atom~) */
+    const Type* input_sig[] = { EFFECT, NAME, ENV, ORDINAL, lattice_.atom_type[kTypeNullable], lattice_.atom_type[kTypeNotNull] };
     return copySig(6, input_sig);
   }
   case HR_newclass: {
     /* newclass: (Effect, Traits~, Class, Atom~) -> (Effect, Class~) */
     const Type* input_sig[] = { EFFECT, TRAITS, lattice_.class_type[kTypeNullable], lattice_.atom_type[kTypeNotNull] };
     return copySig(4, input_sig);
   }
   case HR_newfunction: {
@@ -593,32 +602,32 @@ const Type** InstrFactory::buildInputSig
     return copySig(5, input_sig);
   }
   case HR_abc_callsupernsx: {
     /* abc_callsupernsx: (Effect, Name, Atom, Atom, Atom~, Atom) -> (Effect, Atom) */
     const Type* input_sig[] = { EFFECT, NAME, lattice_.atom_type[kTypeNullable], lattice_.atom_type[kTypeNullable], lattice_.atom_type[kTypeNotNull], lattice_.atom_type[kTypeNullable] };
     return copySig(6, input_sig);
   }
   case HR_callstatic: {
-    /* callstatic: (Effect, Ord, TopData, TopData) -> (Effect, TopData) */
-    const Type* input_sig[] = { EFFECT, ORDINAL, TOPDATA, TOPDATA };
+    /* callstatic: (Effect, Env, TopData, TopData) -> (Effect, TopData) */
+    const Type* input_sig[] = { EFFECT, ENV, TOPDATA, TOPDATA };
     return copySig(4, input_sig);
   }
   case HR_callmethod: {
     /* callmethod: (Effect, Env, TopData, TopData) -> (Effect, TopData) */
     const Type* input_sig[] = { EFFECT, ENV, TOPDATA, TOPDATA };
     return copySig(4, input_sig);
   }
   case HR_callinterface: {
     /* callinterface: (Effect, Env, TopData, TopData) -> (Effect, TopData) */
     const Type* input_sig[] = { EFFECT, ENV, TOPDATA, TOPDATA };
     return copySig(4, input_sig);
   }
   case HR_newcatch: {
-    /* newcatch: (Effect, Traits~) -> (Effect, ScriptObject~) */
+    /* newcatch: (Effect, Traits~) -> (Effect, Atom~) */
     const Type* input_sig[] = { EFFECT, TRAITS };
     return copySig(2, input_sig);
   }
   case HR_setslot: {
     /* setslot: (Effect, Ord, ScriptObject~, TopData) -> (Effect, Bot) */
     const Type* input_sig[] = { EFFECT, ORDINAL, lattice_.scriptobject_type[kTypeNotNull], TOPDATA };
     return copySig(4, input_sig);
   }
@@ -638,18 +647,18 @@ const Type** InstrFactory::buildInputSig
     return copySig(2, input_sig);
   }
   case HR_safepoint: {
     /* safepoint: (Effect, State) -> (Effect, State) */
     const Type* input_sig[] = { EFFECT, STATE };
     return copySig(2, input_sig);
   }
   case HR_setlocal: {
-    /* setlocal: (State, TopData) -> State */
-    const Type* input_sig[] = { STATE, TOPDATA };
+    /* setlocal: (State, Atom) -> State */
+    const Type* input_sig[] = { STATE, lattice_.atom_type[kTypeNullable] };
     return copySig(2, input_sig);
   }
   case HR_newstate: {
     /* newstate: () -> State */
     return NULL;
   }
   case HR_deopt_safepoint: {
     /* deopt_safepoint: (Effect, TopData) -> Effect */
@@ -660,16 +669,26 @@ const Type** InstrFactory::buildInputSig
     /* deopt_finish: Effect -> Effect */
     return copySig(EFFECT);
   }
   case HR_deopt_finishcall: {
     /* deopt_finishcall: (Effect, TopData) -> Effect */
     const Type* input_sig[] = { EFFECT, TOPDATA };
     return copySig(2, input_sig);
   }
+  case HR_debugline: {
+    /* debugline: (Effect, Int) -> Effect */
+    const Type* input_sig[] = { EFFECT, lattice_.int_type };
+    return copySig(2, input_sig);
+  }
+  case HR_debugfile: {
+    /* debugfile: (Effect, String) -> Effect */
+    const Type* input_sig[] = { EFFECT, lattice_.string_type[kTypeNullable] };
+    return copySig(2, input_sig);
+  }
   case HR_string2atom: {
     /* string2atom: String -> Atom */
     return copySig(lattice_.string_type[kTypeNullable]);
   }
   case HR_double2atom: {
     /* double2atom: Number -> Atom~ */
     return copySig(lattice_.double_type);
   }
@@ -712,16 +731,20 @@ const Type** InstrFactory::buildInputSig
   case HR_atom2uint: {
     /* atom2uint: Atom -> Uint */
     return copySig(lattice_.atom_type[kTypeNullable]);
   }
   case HR_atom2scriptobject: {
     /* atom2scriptobject: Atom -> ScriptObject */
     return copySig(lattice_.atom_type[kTypeNullable]);
   }
+  case HR_atom2ns: {
+    /* atom2ns: Atom -> Namespace */
+    return copySig(lattice_.atom_type[kTypeNullable]);
+  }
   case HR_i2d: {
     /* i2d: Int -> Number */
     return copySig(lattice_.int_type);
   }
   case HR_u2d: {
     /* u2d: Uint -> Number */
     return copySig(lattice_.uint_type);
   }
@@ -1671,16 +1694,20 @@ const Type** InstrFactory::buildOutputSi
     const Type* output_sig[] = { EFFECT, TOP };
     return copySig(2, output_sig);
   }
   case HR_template: {
     /* template: () -> (Effect, Top) */
     const Type* output_sig[] = { EFFECT, TOP };
     return copySig(2, output_sig);
   }
+  case HR_catchblock: {
+    /* catchblock: () -> Top */
+    return copySig(TOP);
+  }
   case HR_return: {
     /* return: (Effect, Top) -> () */
     return NULL;
   }
   case HR_throw: {
     /* throw: (Effect, Atom) -> () */
     return NULL;
   }
@@ -1830,16 +1857,20 @@ const Type** InstrFactory::buildOutputSi
   case HR_loadinitenv: {
     /* loadinitenv: ScriptObject~ -> Env */
     return copySig(ENV);
   }
   case HR_loadsuperinitenv: {
     /* loadsuperinitenv: Env -> Env */
     return copySig(ENV);
   }
+  case HR_loadenv_env: {
+    /* loadenv_env: (Ord, Env) -> Env */
+    return copySig(ENV);
+  }
   case HR_newobject: {
     /* newobject: (Effect, Atom) -> (Effect, ScriptObject~) */
     const Type* output_sig[] = { EFFECT, lattice_.scriptobject_type[kTypeNotNull] };
     return copySig(2, output_sig);
   }
   case HR_newarray: {
     /* newarray: (Effect, Atom) -> (Effect, Array~) */
     const Type* output_sig[] = { EFFECT, lattice_.array_type[kTypeNotNull] };
@@ -2047,52 +2078,52 @@ const Type** InstrFactory::buildOutputSi
     return copySig(2, output_sig);
   }
   case HR_abc_finddef: {
     /* abc_finddef: (Effect, Name, Env) -> (Effect, ScriptObject~) */
     const Type* output_sig[] = { EFFECT, lattice_.scriptobject_type[kTypeNotNull] };
     return copySig(2, output_sig);
   }
   case HR_abc_findpropstrict: {
-    /* abc_findpropstrict: (Effect, Name, Env, Atom~) -> (Effect, Atom~) */
+    /* abc_findpropstrict: (Effect, Name, Env, Ord, Atom~) -> (Effect, Atom~) */
     const Type* output_sig[] = { EFFECT, lattice_.atom_type[kTypeNotNull] };
     return copySig(2, output_sig);
   }
   case HR_abc_findpropstrictx: {
-    /* abc_findpropstrictx: (Effect, Name, Env, Atom, Atom~) -> (Effect, Atom~) */
+    /* abc_findpropstrictx: (Effect, Name, Env, Ord, Atom, Atom~) -> (Effect, Atom~) */
     const Type* output_sig[] = { EFFECT, lattice_.atom_type[kTypeNotNull] };
     return copySig(2, output_sig);
   }
   case HR_abc_findpropstrictns: {
-    /* abc_findpropstrictns: (Effect, Name, Env, Atom, Atom~) -> (Effect, Atom~) */
+    /* abc_findpropstrictns: (Effect, Name, Env, Ord, Atom, Atom~) -> (Effect, Atom~) */
     const Type* output_sig[] = { EFFECT, lattice_.atom_type[kTypeNotNull] };
     return copySig(2, output_sig);
   }
   case HR_abc_findpropstrictnsx: {
-    /* abc_findpropstrictnsx: (Effect, Name, Env, Atom, Atom, Atom~) -> (Effect, Atom~) */
+    /* abc_findpropstrictnsx: (Effect, Name, Env, Ord, Atom, Atom~) -> (Effect, Atom~) */
     const Type* output_sig[] = { EFFECT, lattice_.atom_type[kTypeNotNull] };
     return copySig(2, output_sig);
   }
   case HR_abc_findproperty: {
-    /* abc_findproperty: (Effect, Name, Env, Atom~) -> (Effect, Atom~) */
+    /* abc_findproperty: (Effect, Name, Env, Ord, Atom~) -> (Effect, Atom~) */
     const Type* output_sig[] = { EFFECT, lattice_.atom_type[kTypeNotNull] };
     return copySig(2, output_sig);
   }
   case HR_abc_findpropertyx: {
-    /* abc_findpropertyx: (Effect, Name, Env, Atom, Atom~) -> (Effect, Atom~) */
+    /* abc_findpropertyx: (Effect, Name, Env, Ord, Atom, Atom~) -> (Effect, Atom~) */
     const Type* output_sig[] = { EFFECT, lattice_.atom_type[kTypeNotNull] };
     return copySig(2, output_sig);
   }
   case HR_abc_findpropertyns: {
-    /* abc_findpropertyns: (Effect, Name, Env, Atom, Atom~) -> (Effect, Atom~) */
+    /* abc_findpropertyns: (Effect, Name, Env, Ord, Atom, Atom~) -> (Effect, Atom~) */
     const Type* output_sig[] = { EFFECT, lattice_.atom_type[kTypeNotNull] };
     return copySig(2, output_sig);
   }
   case HR_abc_findpropertynsx: {
-    /* abc_findpropertynsx: (Effect, Name, Env, Atom, Atom, Atom~) -> (Effect, Atom~) */
+    /* abc_findpropertynsx: (Effect, Name, Env, Ord, Atom, Atom~) -> (Effect, Atom~) */
     const Type* output_sig[] = { EFFECT, lattice_.atom_type[kTypeNotNull] };
     return copySig(2, output_sig);
   }
   case HR_newclass: {
     /* newclass: (Effect, Traits~, Class, Atom~) -> (Effect, Class~) */
     const Type* output_sig[] = { EFFECT, lattice_.class_type[kTypeNotNull] };
     return copySig(2, output_sig);
   }
@@ -2217,33 +2248,33 @@ const Type** InstrFactory::buildOutputSi
     return copySig(2, output_sig);
   }
   case HR_abc_callsupernsx: {
     /* abc_callsupernsx: (Effect, Name, Atom, Atom, Atom~, Atom) -> (Effect, Atom) */
     const Type* output_sig[] = { EFFECT, lattice_.atom_type[kTypeNullable] };
     return copySig(2, output_sig);
   }
   case HR_callstatic: {
-    /* callstatic: (Effect, Ord, TopData, TopData) -> (Effect, TopData) */
+    /* callstatic: (Effect, Env, TopData, TopData) -> (Effect, TopData) */
     const Type* output_sig[] = { EFFECT, TOPDATA };
     return copySig(2, output_sig);
   }
   case HR_callmethod: {
     /* callmethod: (Effect, Env, TopData, TopData) -> (Effect, TopData) */
     const Type* output_sig[] = { EFFECT, TOPDATA };
     return copySig(2, output_sig);
   }
   case HR_callinterface: {
     /* callinterface: (Effect, Env, TopData, TopData) -> (Effect, TopData) */
     const Type* output_sig[] = { EFFECT, TOPDATA };
     return copySig(2, output_sig);
   }
   case HR_newcatch: {
-    /* newcatch: (Effect, Traits~) -> (Effect, ScriptObject~) */
-    const Type* output_sig[] = { EFFECT, lattice_.scriptobject_type[kTypeNotNull] };
+    /* newcatch: (Effect, Traits~) -> (Effect, Atom~) */
+    const Type* output_sig[] = { EFFECT, lattice_.atom_type[kTypeNotNull] };
     return copySig(2, output_sig);
   }
   case HR_setslot: {
     /* setslot: (Effect, Ord, ScriptObject~, TopData) -> (Effect, Bot) */
     const Type* output_sig[] = { EFFECT, BOT };
     return copySig(2, output_sig);
   }
   case HR_getslot: {
@@ -2260,17 +2291,17 @@ const Type** InstrFactory::buildOutputSi
     return copySig(lattice_.atom_type[kTypeNotNull]);
   }
   case HR_safepoint: {
     /* safepoint: (Effect, State) -> (Effect, State) */
     const Type* output_sig[] = { EFFECT, STATE };
     return copySig(2, output_sig);
   }
   case HR_setlocal: {
-    /* setlocal: (State, TopData) -> State */
+    /* setlocal: (State, Atom) -> State */
     return copySig(STATE);
   }
   case HR_newstate: {
     /* newstate: () -> State */
     return copySig(STATE);
   }
   case HR_deopt_safepoint: {
     /* deopt_safepoint: (Effect, TopData) -> Effect */
@@ -2279,16 +2310,24 @@ const Type** InstrFactory::buildOutputSi
   case HR_deopt_finish: {
     /* deopt_finish: Effect -> Effect */
     return copySig(EFFECT);
   }
   case HR_deopt_finishcall: {
     /* deopt_finishcall: (Effect, TopData) -> Effect */
     return copySig(EFFECT);
   }
+  case HR_debugline: {
+    /* debugline: (Effect, Int) -> Effect */
+    return copySig(EFFECT);
+  }
+  case HR_debugfile: {
+    /* debugfile: (Effect, String) -> Effect */
+    return copySig(EFFECT);
+  }
   case HR_string2atom: {
     /* string2atom: String -> Atom */
     return copySig(lattice_.atom_type[kTypeNullable]);
   }
   case HR_double2atom: {
     /* double2atom: Number -> Atom~ */
     return copySig(lattice_.atom_type[kTypeNotNull]);
   }
@@ -2331,16 +2370,20 @@ const Type** InstrFactory::buildOutputSi
   case HR_atom2uint: {
     /* atom2uint: Atom -> Uint */
     return copySig(lattice_.uint_type);
   }
   case HR_atom2scriptobject: {
     /* atom2scriptobject: Atom -> ScriptObject */
     return copySig(lattice_.scriptobject_type[kTypeNullable]);
   }
+  case HR_atom2ns: {
+    /* atom2ns: Atom -> Namespace */
+    return copySig(lattice_.namespace_type[kTypeNullable]);
+  }
   case HR_i2d: {
     /* i2d: Int -> Number */
     return copySig(lattice_.double_type);
   }
   case HR_u2d: {
     /* u2d: Uint -> Number */
     return copySig(lattice_.double_type);
   }
--- a/halfmoon/generated/KindAdapter_cases.hh
+++ b/halfmoon/generated/KindAdapter_cases.hh
@@ -1,16 +1,18 @@
 ///
 /// generated by templates.py -- do not edit
 ///
 
 case HR_start: 
   return a->do_start(cast<StartInstr>(instr));
 case HR_template: 
   return a->do_template(cast<StartInstr>(instr));
+case HR_catchblock: 
+  return a->do_catchblock(cast<CatchBlockInstr>(instr));
 case HR_return: 
   return a->do_return(cast<StopInstr>(instr));
 case HR_throw: 
   return a->do_throw(cast<StopInstr>(instr));
 case HR_goto: 
   return a->do_goto(cast<GotoInstr>(instr));
 case HR_label: 
   return a->do_label(cast<LabelInstr>(instr));
@@ -73,16 +75,18 @@ case HR_loadenv_interface:
 case HR_loadenv: 
   return a->do_loadenv(cast<BinaryExpr>(instr));
 case HR_loadenv_atom: 
   return a->do_loadenv_atom(cast<BinaryExpr>(instr));
 case HR_loadinitenv: 
   return a->do_loadinitenv(cast<UnaryExpr>(instr));
 case HR_loadsuperinitenv: 
   return a->do_loadsuperinitenv(cast<UnaryExpr>(instr));
+case HR_loadenv_env: 
+  return a->do_loadenv_env(cast<BinaryExpr>(instr));
 case HR_newobject: 
   return a->do_newobject(cast<NaryStmt0>(instr));
 case HR_newarray: 
   return a->do_newarray(cast<NaryStmt0>(instr));
 case HR_applytype: 
   return a->do_applytype(cast<NaryStmt0>(instr));
 case HR_newinstance: 
   return a->do_newinstance(cast<UnaryExpr>(instr));
@@ -174,29 +178,29 @@ case HR_negd:
   return a->do_negd(cast<UnaryExpr>(instr));
 case HR_not: 
   return a->do_not(cast<UnaryExpr>(instr));
 case HR_newactivation: 
   return a->do_newactivation(cast<UnaryStmt>(instr));
 case HR_abc_finddef: 
   return a->do_abc_finddef(cast<BinaryStmt>(instr));
 case HR_abc_findpropstrict: 
-  return a->do_abc_findpropstrict(cast<NaryStmt2>(instr));
+  return a->do_abc_findpropstrict(cast<NaryStmt3>(instr));
 case HR_abc_findpropstrictx: 
-  return a->do_abc_findpropstrictx(cast<NaryStmt3>(instr));
+  return a->do_abc_findpropstrictx(cast<NaryStmt4>(instr));
 case HR_abc_findpropstrictns: 
-  return a->do_abc_findpropstrictns(cast<NaryStmt3>(instr));
+  return a->do_abc_findpropstrictns(cast<NaryStmt4>(instr));
 case HR_abc_findpropstrictnsx: 
   return a->do_abc_findpropstrictnsx(cast<NaryStmt4>(instr));
 case HR_abc_findproperty: 
-  return a->do_abc_findproperty(cast<NaryStmt2>(instr));
+  return a->do_abc_findproperty(cast<NaryStmt3>(instr));
 case HR_abc_findpropertyx: 
-  return a->do_abc_findpropertyx(cast<NaryStmt3>(instr));
+  return a->do_abc_findpropertyx(cast<NaryStmt4>(instr));
 case HR_abc_findpropertyns: 
-  return a->do_abc_findpropertyns(cast<NaryStmt3>(instr));
+  return a->do_abc_findpropertyns(cast<NaryStmt4>(instr));
 case HR_abc_findpropertynsx: 
   return a->do_abc_findpropertynsx(cast<NaryStmt4>(instr));
 case HR_newclass: 
   return a->do_newclass(cast<NaryStmt2>(instr));
 case HR_newfunction: 
   return a->do_newfunction(cast<NaryStmt1>(instr));
 case HR_abc_getsuper: 
   return a->do_abc_getsuper(cast<CallStmt2>(instr));
@@ -269,16 +273,20 @@ case HR_setlocal:
 case HR_newstate: 
   return a->do_newstate(cast<ConstantExpr>(instr));
 case HR_deopt_safepoint: 
   return a->do_deopt_safepoint(cast<DeoptSafepointInstr>(instr));
 case HR_deopt_finish: 
   return a->do_deopt_finish(cast<DeoptFinishInstr>(instr));
 case HR_deopt_finishcall: 
   return a->do_deopt_finishcall(cast<DeoptFinishCallInstr>(instr));
+case HR_debugline: 
+  return a->do_debugline(cast<DebugInstr>(instr));
+case HR_debugfile: 
+  return a->do_debugfile(cast<DebugInstr>(instr));
 case HR_string2atom: 
   return a->do_string2atom(cast<UnaryExpr>(instr));
 case HR_double2atom: 
   return a->do_double2atom(cast<UnaryExpr>(instr));
 case HR_int2atom: 
   return a->do_int2atom(cast<UnaryExpr>(instr));
 case HR_uint2atom: 
   return a->do_uint2atom(cast<UnaryExpr>(instr));
@@ -295,16 +303,18 @@ case HR_atom2double:
 case HR_atom2string: 
   return a->do_atom2string(cast<UnaryExpr>(instr));
 case HR_atom2int: 
   return a->do_atom2int(cast<UnaryExpr>(instr));
 case HR_atom2uint: 
   return a->do_atom2uint(cast<UnaryExpr>(instr));
 case HR_atom2scriptobject: 
   return a->do_atom2scriptobject(cast<UnaryExpr>(instr));
+case HR_atom2ns: 
+  return a->do_atom2ns(cast<UnaryExpr>(instr));
 case HR_i2d: 
   return a->do_i2d(cast<UnaryExpr>(instr));
 case HR_u2d: 
   return a->do_u2d(cast<UnaryExpr>(instr));
 case HR_d2i: 
   return a->do_d2i(cast<UnaryExpr>(instr));
 case HR_d2u: 
   return a->do_d2u(cast<UnaryExpr>(instr));
--- a/halfmoon/generated/KindAdapter_methods.hh
+++ b/halfmoon/generated/KindAdapter_methods.hh
@@ -1,14 +1,15 @@
 ///
 /// generated by templates.py -- do not edit
 ///
 
 RETURN_TYPE do_start(StartInstr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_template(StartInstr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
+RETURN_TYPE do_catchblock(CatchBlockInstr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_return(StopInstr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_throw(StopInstr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_goto(GotoInstr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_label(LabelInstr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_if(IfInstr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_switch(SwitchInstr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_arm(ArmInstr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_const(ConstantExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
@@ -35,16 +36,17 @@ RETURN_TYPE do_loadenv_namespace(BinaryE
 RETURN_TYPE do_loadenv_boolean(BinaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_loadenv_number(BinaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_loadenv_string(BinaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_loadenv_interface(BinaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_loadenv(BinaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_loadenv_atom(BinaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_loadinitenv(UnaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_loadsuperinitenv(UnaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
+RETURN_TYPE do_loadenv_env(BinaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_newobject(NaryStmt0* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_newarray(NaryStmt0* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_applytype(NaryStmt0* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_newinstance(UnaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_abc_convert_s(UnaryStmt* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_abc_esc_xelem(UnaryStmt* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_abc_esc_xattr(UnaryStmt* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_abc_typeof(UnaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
@@ -85,23 +87,23 @@ RETURN_TYPE do_lshi(BinaryExpr* i) { ret
 RETURN_TYPE do_rshi(BinaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_rshui(BinaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_noti(UnaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_negi(UnaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_negd(UnaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_not(UnaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_newactivation(UnaryStmt* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_abc_finddef(BinaryStmt* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
-RETURN_TYPE do_abc_findpropstrict(NaryStmt2* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
-RETURN_TYPE do_abc_findpropstrictx(NaryStmt3* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
-RETURN_TYPE do_abc_findpropstrictns(NaryStmt3* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
+RETURN_TYPE do_abc_findpropstrict(NaryStmt3* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
+RETURN_TYPE do_abc_findpropstrictx(NaryStmt4* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
+RETURN_TYPE do_abc_findpropstrictns(NaryStmt4* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_abc_findpropstrictnsx(NaryStmt4* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
-RETURN_TYPE do_abc_findproperty(NaryStmt2* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
-RETURN_TYPE do_abc_findpropertyx(NaryStmt3* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
-RETURN_TYPE do_abc_findpropertyns(NaryStmt3* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
+RETURN_TYPE do_abc_findproperty(NaryStmt3* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
+RETURN_TYPE do_abc_findpropertyx(NaryStmt4* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
+RETURN_TYPE do_abc_findpropertyns(NaryStmt4* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_abc_findpropertynsx(NaryStmt4* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_newclass(NaryStmt2* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_newfunction(NaryStmt1* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_abc_getsuper(CallStmt2* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_abc_getsuperx(CallStmt3* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_abc_getsuperns(CallStmt3* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_abc_getsupernsx(CallStmt4* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_abc_getdescendants(CallStmt2* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
@@ -133,29 +135,32 @@ RETURN_TYPE do_getslot(CallStmt2* i) { r
 RETURN_TYPE do_slottype(BinaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_getouterscope(BinaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_safepoint(SafepointInstr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_setlocal(SetlocalInstr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_newstate(ConstantExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_deopt_safepoint(DeoptSafepointInstr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_deopt_finish(DeoptFinishInstr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_deopt_finishcall(DeoptFinishCallInstr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
+RETURN_TYPE do_debugline(DebugInstr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
+RETURN_TYPE do_debugfile(DebugInstr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_string2atom(UnaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_double2atom(UnaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_int2atom(UnaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_uint2atom(UnaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_scriptobject2atom(UnaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_bool2atom(UnaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_ns2atom(UnaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_atom2bool(UnaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_atom2double(UnaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_atom2string(UnaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_atom2int(UnaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_atom2uint(UnaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_atom2scriptobject(UnaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
+RETURN_TYPE do_atom2ns(UnaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_i2d(UnaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_u2d(UnaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_d2i(UnaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_d2u(UnaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_toslot(BinaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_toprimitive(UnaryStmt* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_eqi(BinaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_lti(BinaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
--- a/halfmoon/generated/ShapeAdapter_cases.hh
+++ b/halfmoon/generated/ShapeAdapter_cases.hh
@@ -1,16 +1,18 @@
 ///
 /// generated by templates.py -- do not edit
 ///
 
 case GOTOINSTR_SHAPE: 
   return a->do_GotoInstr(cast<GotoInstr>(instr));
 case ARMINSTR_SHAPE: 
   return a->do_ArmInstr(cast<ArmInstr>(instr));
+case CATCHBLOCKINSTR_SHAPE: 
+  return a->do_CatchBlockInstr(cast<CatchBlockInstr>(instr));
 case LABELINSTR_SHAPE: 
   return a->do_LabelInstr(cast<LabelInstr>(instr));
 case CONSTANTEXPR_SHAPE: 
   return a->do_ConstantExpr(cast<ConstantExpr>(instr));
 case STARTINSTR_SHAPE: 
   return a->do_StartInstr(cast<StartInstr>(instr));
 case IFINSTR_SHAPE: 
   return a->do_IfInstr(cast<IfInstr>(instr));
@@ -27,22 +29,24 @@ case STOPINSTR_SHAPE:
 case DEOPTSAFEPOINTINSTR_SHAPE: 
   return a->do_DeoptSafepointInstr(cast<DeoptSafepointInstr>(instr));
 case DEOPTFINISHINSTR_SHAPE: 
   return a->do_DeoptFinishInstr(cast<DeoptFinishInstr>(instr));
 case VOIDSTMT_SHAPE: 
   return a->do_VoidStmt(cast<VoidStmt>(instr));
 case NARYSTMT0_SHAPE: 
   return a->do_NaryStmt0(cast<NaryStmt0>(instr));
+case SAFEPOINTINSTR_SHAPE: 
+  return a->do_SafepointInstr(cast<SafepointInstr>(instr));
+case DEBUGINSTR_SHAPE: 
+  return a->do_DebugInstr(cast<DebugInstr>(instr));
 case DEOPTFINISHCALLINSTR_SHAPE: 
   return a->do_DeoptFinishCallInstr(cast<DeoptFinishCallInstr>(instr));
 case NARYSTMT1_SHAPE: 
   return a->do_NaryStmt1(cast<NaryStmt1>(instr));
-case SAFEPOINTINSTR_SHAPE: 
-  return a->do_SafepointInstr(cast<SafepointInstr>(instr));
 case UNARYSTMT_SHAPE: 
   return a->do_UnaryStmt(cast<UnaryStmt>(instr));
 case CALLSTMT2_SHAPE: 
   return a->do_CallStmt2(cast<CallStmt2>(instr));
 case NARYSTMT2_SHAPE: 
   return a->do_NaryStmt2(cast<NaryStmt2>(instr));
 case BINARYSTMT_SHAPE: 
   return a->do_BinaryStmt(cast<BinaryStmt>(instr));
--- a/halfmoon/generated/ShapeAdapter_methods.hh
+++ b/halfmoon/generated/ShapeAdapter_methods.hh
@@ -1,30 +1,32 @@
 ///
 /// generated by templates.py -- do not edit
 ///
 
 RETURN_TYPE do_GotoInstr(GotoInstr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_ArmInstr(ArmInstr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
+RETURN_TYPE do_CatchBlockInstr(CatchBlockInstr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_LabelInstr(LabelInstr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_ConstantExpr(ConstantExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_StartInstr(StartInstr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_IfInstr(IfInstr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_SwitchInstr(SwitchInstr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_UnaryExpr(UnaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_BinaryExpr(BinaryExpr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_SetlocalInstr(SetlocalInstr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_StopInstr(StopInstr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_DeoptSafepointInstr(DeoptSafepointInstr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_DeoptFinishInstr(DeoptFinishInstr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_VoidStmt(VoidStmt* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_NaryStmt0(NaryStmt0* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
+RETURN_TYPE do_SafepointInstr(SafepointInstr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
+RETURN_TYPE do_DebugInstr(DebugInstr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_DeoptFinishCallInstr(DeoptFinishCallInstr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_NaryStmt1(NaryStmt1* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
-RETURN_TYPE do_SafepointInstr(SafepointInstr* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_UnaryStmt(UnaryStmt* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_CallStmt2(CallStmt2* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_NaryStmt2(NaryStmt2* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_BinaryStmt(BinaryStmt* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_Hasnext2Stmt(Hasnext2Stmt* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_CallStmt3(CallStmt3* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_NaryStmt3(NaryStmt3* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
 RETURN_TYPE do_CallStmt4(CallStmt4* i) { return static_cast<SELF_CLASS*>(this)->do_default(i); }
--- a/halfmoon/generated/Stub_callers.hh
+++ b/halfmoon/generated/Stub_callers.hh
@@ -199,16 +199,23 @@ class StubCaller {
   }
 
   // loadsuperinitenv: Env -> Env
   static void do_loadsuperinitenv(Interpreter* interp, UnaryExpr* instr) {
     interp->resultVal(instr->value_out()) = Value(Stubs::do_loadsuperinitenv(&interp->frame_,
         interp->getEnv(instr->use(0))));
   }
 
+  // loadenv_env: (Ord, Env) -> Env
+  static void do_loadenv_env(Interpreter* interp, BinaryExpr* instr) {
+    interp->resultVal(instr->value_out()) = Value(Stubs::do_loadenv_env(&interp->frame_,
+        interp->getOrdinal(instr->use(0)),
+        interp->getEnv(instr->use(1))));
+  }
+
   // newobject: (Effect, Atom) -> (Effect, ScriptObject~)
   static void do_newobject(Interpreter* interp, NaryStmt0* instr) {
     int argc = instr->arg_count();
     Use* arg_uses = instr->args();
     Atom* args = (Atom*)interp->args_out_;
     for (int i = 0; i < argc; ++i)
       args[i] = interp->getAtom(arg_uses[i]);
     interp->resultVal(instr->value_out()) = Value(Stubs::do_newobject(&interp->frame_,
@@ -501,124 +508,130 @@ class StubCaller {
 
   // abc_finddef: (Effect, Name, Env) -> (Effect, ScriptObject~)
   static void do_abc_finddef(Interpreter* interp, BinaryStmt* instr) {
     interp->resultVal(instr->value_out()) = Value(Stubs::do_abc_finddef(&interp->frame_,
         interp->getName(instr->use(1)),
         interp->getEnv(instr->use(2))));
   }
 
-  // abc_findpropstrict: (Effect, Name, Env, Atom~) -> (Effect, Atom~)
-  static void do_abc_findpropstrict(Interpreter* interp, NaryStmt2* instr) {
+  // abc_findpropstrict: (Effect, Name, Env, Ord, Atom~) -> (Effect, Atom~)
+  static void do_abc_findpropstrict(Interpreter* interp, NaryStmt3* instr) {
     int argc = instr->arg_count();
     Use* arg_uses = instr->args();
     Atom* args = (Atom*)interp->args_out_;
     for (int i = 0; i < argc; ++i)
       args[i] = interp->getAtom(arg_uses[i]);
     interp->resultVal(instr->value_out()) = AtomValue(Stubs::do_abc_findpropstrict(&interp->frame_,
         interp->getName(instr->use(1)),
         interp->getEnv(instr->use(2)),
+        interp->getOrdinal(instr->use(3)),
         argc, args));
   }
 
-  // abc_findpropstrictx: (Effect, Name, Env, Atom, Atom~) -> (Effect, Atom~)
-  static void do_abc_findpropstrictx(Interpreter* interp, NaryStmt3* instr) {
+  // abc_findpropstrictx: (Effect, Name, Env, Ord, Atom, Atom~) -> (Effect, Atom~)
+  static void do_abc_findpropstrictx(Interpreter* interp, NaryStmt4* instr) {
     int argc = instr->arg_count();
     Use* arg_uses = instr->args();
     Atom* args = (Atom*)interp->args_out_;
     for (int i = 0; i < argc; ++i)
       args[i] = interp->getAtom(arg_uses[i]);
     interp->resultVal(instr->value_out()) = AtomValue(Stubs::do_abc_findpropstrictx(&interp->frame_,
         interp->getName(instr->use(1)),
         interp->getEnv(instr->use(2)),
-        interp->getAtom(instr->use(3)),
+        interp->getOrdinal(instr->use(3)),
+        interp->getAtom(instr->use(4)),
         argc, args));
   }
 
-  // abc_findpropstrictns: (Effect, Name, Env, Atom, Atom~) -> (Effect, Atom~)
-  static void do_abc_findpropstrictns(Interpreter* interp, NaryStmt3* instr) {
+  // abc_findpropstrictns: (Effect, Name, Env, Ord, Atom, Atom~) -> (Effect, Atom~)
+  static void do_abc_findpropstrictns(Interpreter* interp, NaryStmt4* instr) {
     int argc = instr->arg_count();
     Use* arg_uses = instr->args();
     Atom* args = (Atom*)interp->args_out_;
     for (int i = 0; i < argc; ++i)
       args[i] = interp->getAtom(arg_uses[i]);
     interp->resultVal(instr->value_out()) = AtomValue(Stubs::do_abc_findpropstrictns(&interp->frame_,
         interp->getName(instr->use(1)),
         interp->getEnv(instr->use(2)),
-        interp->getAtom(instr->use(3)),
+        interp->getOrdinal(instr->use(3)),
+        interp->getAtom(instr->use(4)),
         argc, args));
   }
 
-  // abc_findpropstrictnsx: (Effect, Name, Env, Atom, Atom, Atom~) -> (Effect, Atom~)
+  // abc_findpropstrictnsx: (Effect, Name, Env, Ord, Atom, Atom~) -> (Effect, Atom~)
   static void do_abc_findpropstrictnsx(Interpreter* interp, NaryStmt4* instr) {
     int argc = instr->arg_count();
     Use* arg_uses = instr->args();
     Atom* args = (Atom*)interp->args_out_;
     for (int i = 0; i < argc; ++i)
       args[i] = interp->getAtom(arg_uses[i]);
     interp->resultVal(instr->value_out()) = AtomValue(Stubs::do_abc_findpropstrictnsx(&interp->frame_,
         interp->getName(instr->use(1)),
         interp->getEnv(instr->use(2)),
-        interp->getAtom(instr->use(3)),
+        interp->getOrdinal(instr->use(3)),
         interp->getAtom(instr->use(4)),
         argc, args));
   }
 
-  // abc_findproperty: (Effect, Name, Env, Atom~) -> (Effect, Atom~)
-  static void do_abc_findproperty(Interpreter* interp, NaryStmt2* instr) {
+  // abc_findproperty: (Effect, Name, Env, Ord, Atom~) -> (Effect, Atom~)
+  static void do_abc_findproperty(Interpreter* interp, NaryStmt3* instr) {
     int argc = instr->arg_count();
     Use* arg_uses = instr->args();
     Atom* args = (Atom*)interp->args_out_;
     for (int i = 0; i < argc; ++i)
       args[i] = interp->getAtom(arg_uses[i]);
     interp->resultVal(instr->value_out()) = AtomValue(Stubs::do_abc_findproperty(&interp->frame_,
         interp->getName(instr->use(1)),
         interp->getEnv(instr->use(2)),
+        interp->getOrdinal(instr->use(3)),
         argc, args));
   }
 
-  // abc_findpropertyx: (Effect, Name, Env, Atom, Atom~) -> (Effect, Atom~)
-  static void do_abc_findpropertyx(Interpreter* interp, NaryStmt3* instr) {
+  // abc_findpropertyx: (Effect, Name, Env, Ord, Atom, Atom~) -> (Effect, Atom~)
+  static void do_abc_findpropertyx(Interpreter* interp, NaryStmt4* instr) {
     int argc = instr->arg_count();
     Use* arg_uses = instr->args();
     Atom* args = (Atom*)interp->args_out_;
     for (int i = 0; i < argc; ++i)
       args[i] = interp->getAtom(arg_uses[i]);
     interp->resultVal(instr->value_out()) = AtomValue(Stubs::do_abc_findpropertyx(&interp->frame_,
         interp->getName(instr->use(1)),
         interp->getEnv(instr->use(2)),
-        interp->getAtom(instr->use(3)),
+        interp->getOrdinal(instr->use(3)),
+        interp->getAtom(instr->use(4)),
         argc, args));
   }
 
-  // abc_findpropertyns: (Effect, Name, Env, Atom, Atom~) -> (Effect, Atom~)
-  static void do_abc_findpropertyns(Interpreter* interp, NaryStmt3* instr) {
+  // abc_findpropertyns: (Effect, Name, Env, Ord, Atom, Atom~) -> (Effect, Atom~)
+  static void do_abc_findpropertyns(Interpreter* interp, NaryStmt4* instr) {
     int argc = instr->arg_count();
     Use* arg_uses = instr->args();
     Atom* args = (Atom*)interp->args_out_;
     for (int i = 0; i < argc; ++i)
       args[i] = interp->getAtom(arg_uses[i]);
     interp->resultVal(instr->value_out()) = AtomValue(Stubs::do_abc_findpropertyns(&interp->frame_,
         interp->getName(instr->use(1)),
         interp->getEnv(instr->use(2)),
-        interp->getAtom(instr->use(3)),
+        interp->getOrdinal(instr->use(3)),
+        interp->getAtom(instr->use(4)),
         argc, args));
   }
 
-  // abc_findpropertynsx: (Effect, Name, Env, Atom, Atom, Atom~) -> (Effect, Atom~)
+  // abc_findpropertynsx: (Effect, Name, Env, Ord, Atom, Atom~) -> (Effect, Atom~)
   static void do_abc_findpropertynsx(Interpreter* interp, NaryStmt4* instr) {
     int argc = instr->arg_count();
     Use* arg_uses = instr->args();
     Atom* args = (Atom*)interp->args_out_;
     for (int i = 0; i < argc; ++i)
       args[i] = interp->getAtom(arg_uses[i]);
     interp->resultVal(instr->value_out()) = AtomValue(Stubs::do_abc_findpropertynsx(&interp->frame_,
         interp->getName(instr->use(1)),
         interp->getEnv(instr->use(2)),
-        interp->getAtom(instr->use(3)),
+        interp->getOrdinal(instr->use(3)),
         interp->getAtom(instr->use(4)),
         argc, args));
   }
 
   // newclass: (Effect, Traits~, Class, Atom~) -> (Effect, Class~)
   static void do_newclass(Interpreter* interp, NaryStmt2* instr) {
     int argc = instr->arg_count();
     Use* arg_uses = instr->args();
@@ -910,19 +923,19 @@ class StubCaller {
       args[i] = interp->getAtom(arg_uses[i]);
     interp->resultVal(instr->value_out()) = AtomValue(Stubs::do_abc_callsupernsx(&interp->frame_,
         interp->getName(instr->use(1)),
         interp->getAtom(instr->use(2)),
         interp->getAtom(instr->use(3)),
         argc, args));
   }
 
-  // newcatch: (Effect, Traits~) -> (Effect, ScriptObject~)
+  // newcatch: (Effect, Traits~) -> (Effect, Atom~)
   static void do_newcatch(Interpreter* interp, UnaryStmt* instr) {
-    interp->resultVal(instr->value_out()) = Value(Stubs::do_newcatch(&interp->frame_,
+    interp->resultVal(instr->value_out()) = AtomValue(Stubs::do_newcatch(&interp->frame_,
         interp->getTraits(instr->use(1))));
   }
 
   // slottype: (ScriptObject~, Ord) -> Traits
   static void do_slottype(Interpreter* interp, BinaryExpr* instr) {
     interp->resultVal(instr->value_out()) = Value(Stubs::do_slottype(&interp->frame_,
         interp->getObject(instr->use(0)),
         interp->getOrdinal(instr->use(1))));
@@ -937,16 +950,30 @@ class StubCaller {
 
   // deopt_finish: Effect -> Effect
   static void do_deopt_finish(Interpreter* interp, DeoptFinishInstr* instr) {
     Stubs::do_deopt_finish(&interp->frame_);
     (void)interp;
     (void)instr;
   }
 
+  // debugline: (Effect, Int) -> Effect
+  static void do_debugline(Interpreter* interp, DebugInstr* instr) {
+    Stubs::do_debugline(&interp->frame_,
+        interp->getInt(instr->use(1)));
+    (void)interp;
+  }
+
+  // debugfile: (Effect, String) -> Effect
+  static void do_debugfile(Interpreter* interp, DebugInstr* instr) {
+    Stubs::do_debugfile(&interp->frame_,
+        interp->getString(instr->use(1)));
+    (void)interp;
+  }
+
   // string2atom: String -> Atom
   static void do_string2atom(Interpreter* interp, UnaryExpr* instr) {
     interp->resultVal(instr->value_out()) = AtomValue(Stubs::do_string2atom(&interp->frame_,
         interp->getString(instr->use(0))));
   }
 
   // double2atom: Number -> Atom~
   static void do_double2atom(Interpreter* interp, UnaryExpr* instr) {
@@ -1015,16 +1042,22 @@ class StubCaller {
   }
 
   // atom2scriptobject: Atom -> ScriptObject
   static void do_atom2scriptobject(Interpreter* interp, UnaryExpr* instr) {
     interp->resultVal(instr->value_out()) = Value(Stubs::do_atom2scriptobject(&interp->frame_,
         interp->getAtom(instr->use(0))));
   }
 
+  // atom2ns: Atom -> Namespace
+  static void do_atom2ns(Interpreter* interp, UnaryExpr* instr) {
+    interp->resultVal(instr->value_out()) = Value(Stubs::do_atom2ns(&interp->frame_,
+        interp->getAtom(instr->use(0))));
+  }
+
   // i2d: Int -> Number
   static void do_i2d(Interpreter* interp, UnaryExpr* instr) {
     interp->resultVal(instr->value_out()) = Value(Stubs::do_i2d(&interp->frame_,
         interp->getInt(instr->use(0))));
   }
 
   // u2d: Uint -> Number
   static void do_u2d(Interpreter* interp, UnaryExpr* instr) {
@@ -1723,16 +1756,17 @@ class StubCaller {
     (void)interp;
   }
 
 };
 
 const Interpreter::StubCall Interpreter::stub_table[] = {
   0, // start
   0, // template
+  0, // catchblock
   0, // return
   (StubCall)&StubCaller::do_throw,
   0, // goto
   0, // label
   0, // if
   0, // switch
   0, // arm
   0, // const
@@ -1759,16 +1793,17 @@ const Interpreter::StubCall Interpreter:
   (StubCall)&StubCaller::do_loadenv_boolean,
   (StubCall)&StubCaller::do_loadenv_number,
   (StubCall)&StubCaller::do_loadenv_string,
   (StubCall)&StubCaller::do_loadenv_interface,
   (StubCall)&StubCaller::do_loadenv,
   (StubCall)&StubCaller::do_loadenv_atom,
   (StubCall)&StubCaller::do_loadinitenv,
   (StubCall)&StubCaller::do_loadsuperinitenv,
+  (StubCall)&StubCaller::do_loadenv_env,
   (StubCall)&StubCaller::do_newobject,
   (StubCall)&StubCaller::do_newarray,
   (StubCall)&StubCaller::do_applytype,
   (StubCall)&StubCaller::do_newinstance,
   (StubCall)&StubCaller::do_abc_convert_s,
   (StubCall)&StubCaller::do_abc_esc_xelem,
   (StubCall)&StubCaller::do_abc_esc_xattr,
   (StubCall)&StubCaller::do_abc_typeof,
@@ -1857,29 +1892,32 @@ const Interpreter::StubCall Interpreter:
   (StubCall)&StubCaller::do_slottype,
   (StubCall)&StubCaller::do_getouterscope,
   0, // safepoint
   0, // setlocal
   0, // newstate
   0, // deopt_safepoint
   (StubCall)&StubCaller::do_deopt_finish,
   0, // deopt_finishcall
+  (StubCall)&StubCaller::do_debugline,
+  (StubCall)&StubCaller::do_debugfile,
   (StubCall)&StubCaller::do_string2atom,
   (StubCall)&StubCaller::do_double2atom,
   (StubCall)&StubCaller::do_int2atom,
   (StubCall)&StubCaller::do_uint2atom,
   (StubCall)&StubCaller::do_scriptobject2atom,
   (StubCall)&StubCaller::do_bool2atom,
   (StubCall)&StubCaller::do_ns2atom,
   (StubCall)&StubCaller::do_atom2bool,
   (StubCall)&StubCaller::do_atom2double,
   (StubCall)&StubCaller::do_atom2string,
   (StubCall)&StubCaller::do_atom2int,
   (StubCall)&StubCaller::do_atom2uint,
   (StubCall)&StubCaller::do_atom2scriptobject,
+  (StubCall)&StubCaller::do_atom2ns,
   (StubCall)&StubCaller::do_i2d,
   (StubCall)&StubCaller::do_u2d,
   (StubCall)&StubCaller::do_d2i,
   (StubCall)&StubCaller::do_d2u,
   (StubCall)&StubCaller::do_toslot,
   (StubCall)&StubCaller::do_toprimitive,
   (StubCall)&StubCaller::do_eqi,
   (StubCall)&StubCaller::do_lti,
--- a/halfmoon/generated/Stub_lirtable.hh
+++ b/halfmoon/generated/Stub_lirtable.hh
@@ -1,16 +1,17 @@
 ///
 /// generated by templates.py -- do not edit
 ///
 
 namespace halfmoon {
 const nanojit::CallInfo LirEmitter::lir_table[] = {
   { 0, 0, ABI_CDECL, 0, ACCSET_NONE verbose_only(, "start")},
   { 0, 0, ABI_CDECL, 0, ACCSET_NONE verbose_only(, "template")},
+  { 0, 0, ABI_CDECL, 0, ACCSET_NONE verbose_only(, "catchblock")},
   { 0, 0, ABI_CDECL, 0, ACCSET_NONE verbose_only(, "return")},
   { (uintptr_t)&Stubs::do_throw, CallInfo::typeSig2(ARGTYPE_V, ARGTYPE_P, ARGTYPE_P), ABI_CDECL, 0, ACCSET_ALL verbose_only(, "throw")},
   { 0, 0, ABI_CDECL, 0, ACCSET_NONE verbose_only(, "goto")},
   { 0, 0, ABI_CDECL, 0, ACCSET_NONE verbose_only(, "label")},
   { 0, 0, ABI_CDECL, 0, ACCSET_NONE verbose_only(, "if")},
   { 0, 0, ABI_CDECL, 0, ACCSET_NONE verbose_only(, "switch")},
   { 0, 0, ABI_CDECL, 0, ACCSET_NONE verbose_only(, "arm")},
   { 0, 0, ABI_CDECL, 0, ACCSET_NONE verbose_only(, "const")},
@@ -37,16 +38,17 @@ const nanojit::CallInfo LirEmitter::lir_
   { (uintptr_t)&Stubs::do_loadenv_boolean, CallInfo::typeSig3(ARGTYPE_P, ARGTYPE_P, ARGTYPE_I, ARGTYPE_I), ABI_CDECL, 1, ACCSET_NONE verbose_only(, "loadenv_boolean")},
   { (uintptr_t)&Stubs::do_loadenv_number, CallInfo::typeSig3(ARGTYPE_P, ARGTYPE_P, ARGTYPE_I, ARGTYPE_D), ABI_CDECL, 1, ACCSET_NONE verbose_only(, "loadenv_number")},
   { (uintptr_t)&Stubs::do_loadenv_string, CallInfo::typeSig3(ARGTYPE_P, ARGTYPE_P, ARGTYPE_I, ARGTYPE_P), ABI_CDECL, 1, ACCSET_NONE verbose_only(, "loadenv_string")},
   { (uintptr_t)&Stubs::do_loadenv_interface, CallInfo::typeSig3(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P), ABI_CDECL, 1, ACCSET_NONE verbose_only(, "loadenv_interface")},
   { (uintptr_t)&Stubs::do_loadenv, CallInfo::typeSig3(ARGTYPE_P, ARGTYPE_P, ARGTYPE_I, ARGTYPE_P), ABI_CDECL, 1, ACCSET_NONE verbose_only(, "loadenv")},
   { (uintptr_t)&Stubs::do_loadenv_atom, CallInfo::typeSig3(ARGTYPE_P, ARGTYPE_P, ARGTYPE_I, ARGTYPE_P), ABI_CDECL, 1, ACCSET_NONE verbose_only(, "loadenv_atom")},
   { (uintptr_t)&Stubs::do_loadinitenv, CallInfo::typeSig2(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P), ABI_CDECL, 1, ACCSET_NONE verbose_only(, "loadinitenv")},
   { (uintptr_t)&Stubs::do_loadsuperinitenv, CallInfo::typeSig2(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P), ABI_CDECL, 1, ACCSET_NONE verbose_only(, "loadsuperinitenv")},
+  { (uintptr_t)&Stubs::do_loadenv_env, CallInfo::typeSig3(ARGTYPE_P, ARGTYPE_P, ARGTYPE_I, ARGTYPE_P), ABI_CDECL, 1, ACCSET_NONE verbose_only(, "loadenv_env")},
   { (uintptr_t)&Stubs::do_newobject, CallInfo::typeSig3(ARGTYPE_P, ARGTYPE_P, ARGTYPE_I, ARGTYPE_P), ABI_CDECL, 0, ACCSET_ALL verbose_only(, "newobject")},
   { (uintptr_t)&Stubs::do_newarray, CallInfo::typeSig3(ARGTYPE_P, ARGTYPE_P, ARGTYPE_I, ARGTYPE_P), ABI_CDECL, 0, ACCSET_ALL verbose_only(, "newarray")},
   { (uintptr_t)&Stubs::do_applytype, CallInfo::typeSig3(ARGTYPE_P, ARGTYPE_P, ARGTYPE_I, ARGTYPE_P), ABI_CDECL, 0, ACCSET_ALL verbose_only(, "applytype")},
   { (uintptr_t)&Stubs::do_newinstance, CallInfo::typeSig2(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P), ABI_CDECL, 1, ACCSET_NONE verbose_only(, "newinstance")},
   { (uintptr_t)&Stubs::do_abc_convert_s, CallInfo::typeSig2(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P), ABI_CDECL, 0, ACCSET_ALL verbose_only(, "abc_convert_s")},
   { (uintptr_t)&Stubs::do_abc_esc_xelem, CallInfo::typeSig2(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P), ABI_CDECL, 0, ACCSET_ALL verbose_only(, "abc_esc_xelem")},
   { (uintptr_t)&Stubs::do_abc_esc_xattr, CallInfo::typeSig2(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P), ABI_CDECL, 0, ACCSET_ALL verbose_only(, "abc_esc_xattr")},
   { (uintptr_t)&Stubs::do_abc_typeof, CallInfo::typeSig2(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P), ABI_CDECL, 1, ACCSET_NONE verbose_only(, "abc_typeof")},
@@ -87,24 +89,24 @@ const nanojit::CallInfo LirEmitter::lir_
   { (uintptr_t)&Stubs::do_rshi, CallInfo::typeSig3(ARGTYPE_I, ARGTYPE_P, ARGTYPE_I, ARGTYPE_I), ABI_CDECL, 1, ACCSET_NONE verbose_only(, "rshi")},
   { (uintptr_t)&Stubs::do_rshui, CallInfo::typeSig3(ARGTYPE_UI, ARGTYPE_P, ARGTYPE_I, ARGTYPE_I), ABI_CDECL, 1, ACCSET_NONE verbose_only(, "rshui")},
   { (uintptr_t)&Stubs::do_noti, CallInfo::typeSig2(ARGTYPE_I, ARGTYPE_P, ARGTYPE_I), ABI_CDECL, 1, ACCSET_NONE verbose_only(, "noti")},
   { (uintptr_t)&Stubs::do_negi, CallInfo::typeSig2(ARGTYPE_I, ARGTYPE_P, ARGTYPE_I), ABI_CDECL, 1, ACCSET_NONE verbose_only(, "negi")},
   { (uintptr_t)&Stubs::do_negd, CallInfo::typeSig2(ARGTYPE_D, ARGTYPE_P, ARGTYPE_D), ABI_CDECL, 1, ACCSET_NONE verbose_only(, "negd")},
   { (uintptr_t)&Stubs::do_not, CallInfo::typeSig2(ARGTYPE_I, ARGTYPE_P, ARGTYPE_I), ABI_CDECL, 1, ACCSET_NONE verbose_only(, "not")},
   { (uintptr_t)&Stubs::do_newactivation, CallInfo::typeSig2(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P), ABI_CDECL, 0, ACCSET_ALL verbose_only(, "newactivation")},
   { (uintptr_t)&Stubs::do_abc_finddef, CallInfo::typeSig3(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P), ABI_CDECL, 0, ACCSET_ALL verbose_only(, "abc_finddef")},
-  { (uintptr_t)&Stubs::do_abc_findpropstrict, CallInfo::typeSig5(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_I, ARGTYPE_P), ABI_CDECL, 0, ACCSET_ALL verbose_only(, "abc_findpropstrict")},
-  { (uintptr_t)&Stubs::do_abc_findpropstrictx, CallInfo::typeSig6(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_I, ARGTYPE_P), ABI_CDECL, 0, ACCSET_ALL verbose_only(, "abc_findpropstrictx")},
-  { (uintptr_t)&Stubs::do_abc_findpropstrictns, CallInfo::typeSig6(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_I, ARGTYPE_P), ABI_CDECL, 0, ACCSET_ALL verbose_only(, "abc_findpropstrictns")},
-  { (uintptr_t)&Stubs::do_abc_findpropstrictnsx, CallInfo::typeSig7(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_I, ARGTYPE_P), ABI_CDECL, 0, ACCSET_ALL verbose_only(, "abc_findpropstrictnsx")},
-  { (uintptr_t)&Stubs::do_abc_findproperty, CallInfo::typeSig5(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_I, ARGTYPE_P), ABI_CDECL, 0, ACCSET_ALL verbose_only(, "abc_findproperty")},
-  { (uintptr_t)&Stubs::do_abc_findpropertyx, CallInfo::typeSig6(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_I, ARGTYPE_P), ABI_CDECL, 0, ACCSET_ALL verbose_only(, "abc_findpropertyx")},
-  { (uintptr_t)&Stubs::do_abc_findpropertyns, CallInfo::typeSig6(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_I, ARGTYPE_P), ABI_CDECL, 0, ACCSET_ALL verbose_only(, "abc_findpropertyns")},
-  { (uintptr_t)&Stubs::do_abc_findpropertynsx, CallInfo::typeSig7(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_I, ARGTYPE_P), ABI_CDECL, 0, ACCSET_ALL verbose_only(, "abc_findpropertynsx")},
+  { (uintptr_t)&Stubs::do_abc_findpropstrict, CallInfo::typeSig6(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_I, ARGTYPE_I, ARGTYPE_P), ABI_CDECL, 0, ACCSET_ALL verbose_only(, "abc_findpropstrict")},
+  { (uintptr_t)&Stubs::do_abc_findpropstrictx, CallInfo::typeSig7(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_I, ARGTYPE_P, ARGTYPE_I, ARGTYPE_P), ABI_CDECL, 0, ACCSET_ALL verbose_only(, "abc_findpropstrictx")},
+  { (uintptr_t)&Stubs::do_abc_findpropstrictns, CallInfo::typeSig7(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_I, ARGTYPE_P, ARGTYPE_I, ARGTYPE_P), ABI_CDECL, 0, ACCSET_ALL verbose_only(, "abc_findpropstrictns")},
+  { (uintptr_t)&Stubs::do_abc_findpropstrictnsx, CallInfo::typeSig7(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_I, ARGTYPE_P, ARGTYPE_I, ARGTYPE_P), ABI_CDECL, 0, ACCSET_ALL verbose_only(, "abc_findpropstrictnsx")},
+  { (uintptr_t)&Stubs::do_abc_findproperty, CallInfo::typeSig6(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_I, ARGTYPE_I, ARGTYPE_P), ABI_CDECL, 0, ACCSET_ALL verbose_only(, "abc_findproperty")},
+  { (uintptr_t)&Stubs::do_abc_findpropertyx, CallInfo::typeSig7(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_I, ARGTYPE_P, ARGTYPE_I, ARGTYPE_P), ABI_CDECL, 0, ACCSET_ALL verbose_only(, "abc_findpropertyx")},
+  { (uintptr_t)&Stubs::do_abc_findpropertyns, CallInfo::typeSig7(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_I, ARGTYPE_P, ARGTYPE_I, ARGTYPE_P), ABI_CDECL, 0, ACCSET_ALL verbose_only(, "abc_findpropertyns")},
+  { (uintptr_t)&Stubs::do_abc_findpropertynsx, CallInfo::typeSig7(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_I, ARGTYPE_P, ARGTYPE_I, ARGTYPE_P), ABI_CDECL, 0, ACCSET_ALL verbose_only(, "abc_findpropertynsx")},
   { (uintptr_t)&Stubs::do_newclass, CallInfo::typeSig5(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_I, ARGTYPE_P), ABI_CDECL, 0, ACCSET_ALL verbose_only(, "newclass")},
   { (uintptr_t)&Stubs::do_newfunction, CallInfo::typeSig4(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_I, ARGTYPE_P), ABI_CDECL, 0, ACCSET_ALL verbose_only(, "newfunction")},
   { (uintptr_t)&Stubs::do_abc_getsuper, CallInfo::typeSig3(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P), ABI_CDECL, 0, ACCSET_ALL verbose_only(, "abc_getsuper")},
   { (uintptr_t)&Stubs::do_abc_getsuperx, CallInfo::typeSig4(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P), ABI_CDECL, 0, ACCSET_ALL verbose_only(, "abc_getsuperx")},
   { (uintptr_t)&Stubs::do_abc_getsuperns, CallInfo::typeSig4(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P), ABI_CDECL, 0, ACCSET_ALL verbose_only(, "abc_getsuperns")},
   { (uintptr_t)&Stubs::do_abc_getsupernsx, CallInfo::typeSig5(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P), ABI_CDECL, 0, ACCSET_ALL verbose_only(, "abc_getsupernsx")},
   { (uintptr_t)&Stubs::do_abc_getdescendants, CallInfo::typeSig3(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P), ABI_CDECL, 0, ACCSET_ALL verbose_only(, "abc_getdescendants")},
   { (uintptr_t)&Stubs::do_abc_getdescendantsx, CallInfo::typeSig4(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P), ABI_CDECL, 0, ACCSET_ALL verbose_only(, "abc_getdescendantsx")},
@@ -135,29 +137,32 @@ const nanojit::CallInfo LirEmitter::lir_
   { (uintptr_t)&Stubs::do_slottype, CallInfo::typeSig3(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_I), ABI_CDECL, 1, ACCSET_NONE verbose_only(, "slottype")},
   { (uintptr_t)&Stubs::do_getouterscope, CallInfo::typeSig3(ARGTYPE_P, ARGTYPE_P, ARGTYPE_I, ARGTYPE_P), ABI_CDECL, 1, ACCSET_NONE verbose_only(, "getouterscope")},
   { 0, 0, ABI_CDECL, 0, ACCSET_NONE verbose_only(, "safepoint")},
   { 0, 0, ABI_CDECL, 0, ACCSET_NONE verbose_only(, "setlocal")},
   { 0, 0, ABI_CDECL, 0, ACCSET_NONE verbose_only(, "newstate")},
   { 0, 0, ABI_CDECL, 0, ACCSET_NONE verbose_only(, "deopt_safepoint")},
   { (uintptr_t)&Stubs::do_deopt_finish, CallInfo::typeSig1(ARGTYPE_V, ARGTYPE_P), ABI_CDECL, 0, ACCSET_ALL verbose_only(, "deopt_finish")},
   { 0, 0, ABI_CDECL, 0, ACCSET_NONE verbose_only(, "deopt_finishcall")},
+  { (uintptr_t)&Stubs::do_debugline, CallInfo::typeSig2(ARGTYPE_V, ARGTYPE_P, ARGTYPE_I), ABI_CDECL, 0, ACCSET_ALL verbose_only(, "debugline")},
+  { (uintptr_t)&Stubs::do_debugfile, CallInfo::typeSig2(ARGTYPE_V, ARGTYPE_P, ARGTYPE_P), ABI_CDECL, 0, ACCSET_ALL verbose_only(, "debugfile")},
   { (uintptr_t)&Stubs::do_string2atom, CallInfo::typeSig2(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P), ABI_CDECL, 1, ACCSET_NONE verbose_only(, "string2atom")},
   { (uintptr_t)&Stubs::do_double2atom, CallInfo::typeSig2(ARGTYPE_P, ARGTYPE_P, ARGTYPE_D), ABI_CDECL, 1, ACCSET_NONE verbose_only(, "double2atom")},
   { (uintptr_t)&Stubs::do_int2atom, CallInfo::typeSig2(ARGTYPE_P, ARGTYPE_P, ARGTYPE_I), ABI_CDECL, 1, ACCSET_NONE verbose_only(, "int2atom")},
   { (uintptr_t)&Stubs::do_uint2atom, CallInfo::typeSig2(ARGTYPE_P, ARGTYPE_P, ARGTYPE_UI), ABI_CDECL, 1, ACCSET_NONE verbose_only(, "uint2atom")},
   { (uintptr_t)&Stubs::do_scriptobject2atom, CallInfo::typeSig2(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P), ABI_CDECL, 1, ACCSET_NONE verbose_only(, "scriptobject2atom")},
   { (uintptr_t)&Stubs::do_bool2atom, CallInfo::typeSig2(ARGTYPE_P, ARGTYPE_P, ARGTYPE_I), ABI_CDECL, 1, ACCSET_NONE verbose_only(, "bool2atom")},
   { (uintptr_t)&Stubs::do_ns2atom, CallInfo::typeSig2(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P), ABI_CDECL, 1, ACCSET_NONE verbose_only(, "ns2atom")},
   { (uintptr_t)&Stubs::do_atom2bool, CallInfo::typeSig2(ARGTYPE_I, ARGTYPE_P, ARGTYPE_P), ABI_CDECL, 1, ACCSET_NONE verbose_only(, "atom2bool")},
   { (uintptr_t)&Stubs::do_atom2double, CallInfo::typeSig2(ARGTYPE_D, ARGTYPE_P, ARGTYPE_P), ABI_CDECL, 1, ACCSET_NONE verbose_only(, "atom2double")},
   { (uintptr_t)&Stubs::do_atom2string, CallInfo::typeSig2(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P), ABI_CDECL, 1, ACCSET_NONE verbose_only(, "atom2string")},
   { (uintptr_t)&Stubs::do_atom2int, CallInfo::typeSig2(ARGTYPE_I, ARGTYPE_P, ARGTYPE_P), ABI_CDECL, 1, ACCSET_NONE verbose_only(, "atom2int")},
   { (uintptr_t)&Stubs::do_atom2uint, CallInfo::typeSig2(ARGTYPE_UI, ARGTYPE_P, ARGTYPE_P), ABI_CDECL, 1, ACCSET_NONE verbose_only(, "atom2uint")},
   { (uintptr_t)&Stubs::do_atom2scriptobject, CallInfo::typeSig2(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P), ABI_CDECL, 1, ACCSET_NONE verbose_only(, "atom2scriptobject")},
+  { (uintptr_t)&Stubs::do_atom2ns, CallInfo::typeSig2(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P), ABI_CDECL, 1, ACCSET_NONE verbose_only(, "atom2ns")},
   { (uintptr_t)&Stubs::do_i2d, CallInfo::typeSig2(ARGTYPE_D, ARGTYPE_P, ARGTYPE_I), ABI_CDECL, 1, ACCSET_NONE verbose_only(, "i2d")},
   { (uintptr_t)&Stubs::do_u2d, CallInfo::typeSig2(ARGTYPE_D, ARGTYPE_P, ARGTYPE_UI), ABI_CDECL, 1, ACCSET_NONE verbose_only(, "u2d")},
   { (uintptr_t)&Stubs::do_d2i, CallInfo::typeSig2(ARGTYPE_I, ARGTYPE_P, ARGTYPE_D), ABI_CDECL, 1, ACCSET_NONE verbose_only(, "d2i")},
   { (uintptr_t)&Stubs::do_d2u, CallInfo::typeSig2(ARGTYPE_UI, ARGTYPE_P, ARGTYPE_D), ABI_CDECL, 1, ACCSET_NONE verbose_only(, "d2u")},
   { (uintptr_t)&Stubs::do_toslot, CallInfo::typeSig3(ARGTYPE_I, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P), ABI_CDECL, 1, ACCSET_NONE verbose_only(, "toslot")},
   { (uintptr_t)&Stubs::do_toprimitive, CallInfo::typeSig2(ARGTYPE_P, ARGTYPE_P, ARGTYPE_P), ABI_CDECL, 0, ACCSET_ALL verbose_only(, "toprimitive")},
   { (uintptr_t)&Stubs::do_eqi, CallInfo::typeSig3(ARGTYPE_I, ARGTYPE_P, ARGTYPE_I, ARGTYPE_I), ABI_CDECL, 1, ACCSET_NONE verbose_only(, "eqi")},
   { (uintptr_t)&Stubs::do_lti, CallInfo::typeSig3(ARGTYPE_I, ARGTYPE_P, ARGTYPE_I, ARGTYPE_I), ABI_CDECL, 1, ACCSET_NONE verbose_only(, "lti")},
@@ -244,16 +249,17 @@ const nanojit::CallInfo LirEmitter::lir_
   { (uintptr_t)&Stubs::do_abc_initpropx, CallInfo::typeSig5(ARGTYPE_V, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P), ABI_CDECL, 0, ACCSET_ALL verbose_only(, "abc_initpropx")},
   { (uintptr_t)&Stubs::do_abc_initpropns, CallInfo::typeSig5(ARGTYPE_V, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P), ABI_CDECL, 0, ACCSET_ALL verbose_only(, "abc_initpropns")},
   { (uintptr_t)&Stubs::do_abc_initpropnsx, CallInfo::typeSig6(ARGTYPE_V, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P), ABI_CDECL, 0, ACCSET_ALL verbose_only(, "abc_initpropnsx")},
 };
 
 const int LirEmitter::stub_fixc[] = {
   -1, // start
   -1, // template
+  -1, // catchblock
   0, // return
   -1, // throw
   0, // goto
   -1, // label
   1, // if
   1, // switch
   -1, // arm
   -1, // const
@@ -280,16 +286,17 @@ const int LirEmitter::stub_fixc[] = {
   -1, // loadenv_boolean
   -1, // loadenv_number
   -1, // loadenv_string
   -1, // loadenv_interface
   -1, // loadenv
   -1, // loadenv_atom
   -1, // loadinitenv
   -1, // loadsuperinitenv
+  -1, // loadenv_env
   0, // newobject
   0, // newarray
   0, // applytype
   -1, // newinstance
   -1, // abc_convert_s
   -1, // abc_esc_xelem
   -1, // abc_esc_xattr
   -1, // abc_typeof
@@ -330,23 +337,23 @@ const int LirEmitter::stub_fixc[] = {
   -1, // rshi
   -1, // rshui
   -1, // noti
   -1, // negi
   -1, // negd
   -1, // not
   -1, // newactivation
   -1, // abc_finddef
-  2, // abc_findpropstrict
-  3, // abc_findpropstrictx
-  3, // abc_findpropstrictns
+  3, // abc_findpropstrict
+  4, // abc_findpropstrictx
+  4, // abc_findpropstrictns
   4, // abc_findpropstrictnsx
-  2, // abc_findproperty
-  3, // abc_findpropertyx
-  3, // abc_findpropertyns
+  3, // abc_findproperty
+  4, // abc_findpropertyx
+  4, // abc_findpropertyns
   4, // abc_findpropertynsx
   2, // newclass
   1, // newfunction
   -1, // abc_getsuper
   -1, // abc_getsuperx
   -1, // abc_getsuperns
   -1, // abc_getsupernsx
   -1, // abc_getdescendants
@@ -372,35 +379,38 @@ const int LirEmitter::stub_fixc[] = {
   1, // callstatic
   1, // callmethod
   1, // callinterface
   -1, // newcatch
   -1, // setslot
   -1, // getslot
   -1, // slottype
   -1, // getouterscope
-  -1, // safepoint
+  0, // safepoint
   -1, // setlocal
   -1, // newstate
   0, // deopt_safepoint
   -1, // deopt_finish
   -1, // deopt_finishcall
+  -1, // debugline
+  -1, // debugfile
   -1, // string2atom
   -1, // double2atom
   -1, // int2atom
   -1, // uint2atom
   -1, // scriptobject2atom
   -1, // bool2atom
   -1, // ns2atom
   -1, // atom2bool
   -1, // atom2double
   -1, // atom2string
   -1, // atom2int
   -1, // atom2uint
   -1, // atom2scriptobject
+  -1, // atom2ns
   -1, // i2d
   -1, // u2d
   -1, // d2i
   -1, // d2u
   -1, // toslot
   -1, // toprimitive
   -1, // eqi
   -1, // lti
--- a/halfmoon/generated/Stub_protos.hh
+++ b/halfmoon/generated/Stub_protos.hh
@@ -1,16 +1,16 @@
 ///
 /// generated by templates.py -- do not edit
 ///
 
 namespace halfmoon {
 using namespace avmplus;
 struct Stubs {
-  static const int stub_count = 240;
+  static const int stub_count = 245;
 
   // throw: (Effect, Atom) -> ()
   static void do_throw(MethodFrame*, Atom);
 
   // coerce: (Effect, Traits, Atom) -> (Effect, Atom)
   static Atom do_coerce(MethodFrame*, Traits*, Atom);
 
   // cast: (Effect, Traits, Atom) -> (Effect, ScriptObject)
@@ -86,16 +86,19 @@ struct Stubs {
   static MethodEnv* do_loadenv_atom(MethodFrame*, int, Atom);
 
   // loadinitenv: ScriptObject~ -> Env
   static MethodEnv* do_loadinitenv(MethodFrame*, ScriptObject*);
 
   // loadsuperinitenv: Env -> Env
   static MethodEnv* do_loadsuperinitenv(MethodFrame*, MethodEnv*);
 
+  // loadenv_env: (Ord, Env) -> Env
+  static MethodEnv* do_loadenv_env(MethodFrame*, int, MethodEnv*);
+
   // newobject: (Effect, Atom) -> (Effect, ScriptObject~)
   static ScriptObject* do_newobject(MethodFrame*, int, Atom*);
 
   // newarray: (Effect, Atom) -> (Effect, Array~)
   static ArrayObject* do_newarray(MethodFrame*, int, Atom*);
 
   // applytype: (Effect, Atom) -> (Effect, Atom)
   static Atom do_applytype(MethodFrame*, int, Atom*);
@@ -215,39 +218,39 @@ struct Stubs {
   static BoolKind do_not(MethodFrame*, BoolKind);
 
   // newactivation: (Effect, Env) -> (Effect, ScriptObject~)
   static ScriptObject* do_newactivation(MethodFrame*, MethodEnv*);
 
   // abc_finddef: (Effect, Name, Env) -> (Effect, ScriptObject~)
   static ScriptObject* do_abc_finddef(MethodFrame*, const Multiname*, MethodEnv*);
 
-  // abc_findpropstrict: (Effect, Name, Env, Atom~) -> (Effect, Atom~)
-  static Atom do_abc_findpropstrict(MethodFrame*, const Multiname*, MethodEnv*, int, Atom*);
+  // abc_findpropstrict: (Effect, Name, Env, Ord, Atom~) -> (Effect, Atom~)
+  static Atom do_abc_findpropstrict(MethodFrame*, const Multiname*, MethodEnv*, int, int, Atom*);
 
-  // abc_findpropstrictx: (Effect, Name, Env, Atom, Atom~) -> (Effect, Atom~)
-  static Atom do_abc_findpropstrictx(MethodFrame*, const Multiname*, MethodEnv*, Atom, int, Atom*);
+  // abc_findpropstrictx: (Effect, Name, Env, Ord, Atom, Atom~) -> (Effect, Atom~)
+  static Atom do_abc_findpropstrictx(MethodFrame*, const Multiname*, MethodEnv*, int, Atom, int, Atom*);
 
-  // abc_findpropstrictns: (Effect, Name, Env, Atom, Atom~) -> (Effect, Atom~)
-  static Atom do_abc_findpropstrictns(MethodFrame*, const Multiname*, MethodEnv*, Atom, int, Atom*);
+  // abc_findpropstrictns: (Effect, Name, Env, Ord, Atom, Atom~) -> (Effect, Atom~)
+  static Atom do_abc_findpropstrictns(MethodFrame*, const Multiname*, MethodEnv*, int, Atom, int, Atom*);
 
-  // abc_findpropstrictnsx: (Effect, Name, Env, Atom, Atom, Atom~) -> (Effect, Atom~)
-  static Atom do_abc_findpropstrictnsx(MethodFrame*, const Multiname*, MethodEnv*, Atom, Atom, int, Atom*);
+  // abc_findpropstrictnsx: (Effect, Name, Env, Ord, Atom, Atom~) -> (Effect, Atom~)
+  static Atom do_abc_findpropstrictnsx(MethodFrame*, const Multiname*, MethodEnv*, int, Atom, int, Atom*);
 
-  // abc_findproperty: (Effect, Name, Env, Atom~) -> (Effect, Atom~)
-  static Atom do_abc_findproperty(MethodFrame*, const Multiname*, MethodEnv*, int, Atom*);
+  // abc_findproperty: (Effect, Name, Env, Ord, Atom~) -> (Effect, Atom~)
+  static Atom do_abc_findproperty(MethodFrame*, const Multiname*, MethodEnv*, int, int, Atom*);
 
-  // abc_findpropertyx: (Effect, Name, Env, Atom, Atom~) -> (Effect, Atom~)
-  static Atom do_abc_findpropertyx(MethodFrame*, const Multiname*, MethodEnv*, Atom, int, Atom*);
+  // abc_findpropertyx: (Effect, Name, Env, Ord, Atom, Atom~) -> (Effect, Atom~)
+  static Atom do_abc_findpropertyx(MethodFrame*, const Multiname*, MethodEnv*, int, Atom, int, Atom*);
 
-  // abc_findpropertyns: (Effect, Name, Env, Atom, Atom~) -> (Effect, Atom~)
-  static Atom do_abc_findpropertyns(MethodFrame*, const Multiname*, MethodEnv*, Atom, int, Atom*);
+  // abc_findpropertyns: (Effect, Name, Env, Ord, Atom, Atom~) -> (Effect, Atom~)
+  static Atom do_abc_findpropertyns(MethodFrame*, const Multiname*, MethodEnv*, int, Atom, int, Atom*);
 
-  // abc_findpropertynsx: (Effect, Name, Env, Atom, Atom, Atom~) -> (Effect, Atom~)
-  static Atom do_abc_findpropertynsx(MethodFrame*, const Multiname*, MethodEnv*, Atom, Atom, int, Atom*);
+  // abc_findpropertynsx: (Effect, Name, Env, Ord, Atom, Atom~) -> (Effect, Atom~)
+  static Atom do_abc_findpropertynsx(MethodFrame*, const Multiname*, MethodEnv*, int, Atom, int, Atom*);
 
   // newclass: (Effect, Traits~, Class, Atom~) -> (Effect, Class~)
   static ClassClosure* do_newclass(MethodFrame*, Traits*, ClassClosure*, int, Atom*);
 
   // newfunction: (Effect, Method, Atom~) -> (Effect, Function~)
   static ClassClosure* do_newfunction(MethodFrame*, MethodInfo*, int, Atom*);
 
   // abc_getsuper: (Effect, Name, Atom~) -> (Effect, Atom)
@@ -317,28 +320,34 @@ struct Stubs {
   static Atom do_abc_callsuperx(MethodFrame*, const Multiname*, Atom, int, Atom*);
 
   // abc_callsuperns: (Effect, Name, Atom, Atom~, Atom) -> (Effect, Atom)
   static Atom do_abc_callsuperns(MethodFrame*, const Multiname*, Atom, int, Atom*);
 
   // abc_callsupernsx: (Effect, Name, Atom, Atom, Atom~, Atom) -> (Effect, Atom)
   static Atom do_abc_callsupernsx(MethodFrame*, const Multiname*, Atom, Atom, int, Atom*);
 
-  // newcatch: (Effect, Traits~) -> (Effect, ScriptObject~)
-  static ScriptObject* do_newcatch(MethodFrame*, Traits*);
+  // newcatch: (Effect, Traits~) -> (Effect, Atom~)
+  static Atom do_newcatch(MethodFrame*, Traits*);
 
   // slottype: (ScriptObject~, Ord) -> Traits
   static Traits* do_slottype(MethodFrame*, ScriptObject*, int);
 
   // getouterscope: (Ord, Env) -> Atom~
   static Atom do_getouterscope(MethodFrame*, int, MethodEnv*);
 
   // deopt_finish: Effect -> Effect
   static void do_deopt_finish(MethodFrame*);
 
+  // debugline: (Effect, Int) -> Effect
+  static void do_debugline(MethodFrame*, int32_t);
+
+  // debugfile: (Effect, String) -> Effect
+  static void do_debugfile(MethodFrame*, String*);
+
   // string2atom: String -> Atom
   static Atom do_string2atom(MethodFrame*, String*);
 
   // double2atom: Number -> Atom~
   static Atom do_double2atom(MethodFrame*, double);
 
   // int2atom: Int -> Atom~
   static Atom do_int2atom(MethodFrame*, int32_t);
@@ -368,16 +377,19 @@ struct Stubs {
   static int32_t do_atom2int(MethodFrame*, Atom);
 
   // atom2uint: Atom -> Uint
   static uint32_t do_atom2uint(MethodFrame*, Atom);
 
   // atom2scriptobject: Atom -> ScriptObject
   static ScriptObject* do_atom2scriptobject(MethodFrame*, Atom);
 
+  // atom2ns: Atom -> Namespace
+  static Namespace* do_atom2ns(MethodFrame*, Atom);
+
   // i2d: Int -> Number
   static double do_i2d(MethodFrame*, int32_t);
 
   // u2d: Uint -> Number
   static double do_u2d(MethodFrame*, uint32_t);
 
   // d2i: Number -> Int
   static int32_t do_d2i(MethodFrame*, double);
@@ -672,16 +684,17 @@ struct Stubs {
   MethodEnv* Stubs::do_loadenv_boolean(MethodFrame*, int, BoolKind) { assert(false && "loadenv_boolean not implemented"); return 0; }
   MethodEnv* Stubs::do_loadenv_number(MethodFrame*, int, double) { assert(false && "loadenv_number not implemented"); return 0; }
   MethodEnv* Stubs::do_loadenv_string(MethodFrame*, int, String*) { assert(false && "loadenv_string not implemented"); return 0; }
   MethodEnv* Stubs::do_loadenv_interface(MethodFrame*, MethodInfo*, ScriptObject*) { assert(false && "loadenv_interface not implemented"); return 0; }
   MethodEnv* Stubs::do_loadenv(MethodFrame*, int, ScriptObject*) { assert(false && "loadenv not implemented"); return 0; }
   MethodEnv* Stubs::do_loadenv_atom(MethodFrame*, int, Atom) { assert(false && "loadenv_atom not implemented"); return 0; }
   MethodEnv* Stubs::do_loadinitenv(MethodFrame*, ScriptObject*) { assert(false && "loadinitenv not implemented"); return 0; }
   MethodEnv* Stubs::do_loadsuperinitenv(MethodFrame*, MethodEnv*) { assert(false && "loadsuperinitenv not implemented"); return 0; }
+  MethodEnv* Stubs::do_loadenv_env(MethodFrame*, int, MethodEnv*) { assert(false && "loadenv_env not implemented"); return 0; }
   ScriptObject* Stubs::do_newobject(MethodFrame*, int, Atom*) { assert(false && "newobject not implemented"); return 0; }
   ArrayObject* Stubs::do_newarray(MethodFrame*, int, Atom*) { assert(false && "newarray not implemented"); return 0; }
   Atom Stubs::do_applytype(MethodFrame*, int, Atom*) { assert(false && "applytype not implemented"); return 0; }
   ScriptObject* Stubs::do_newinstance(MethodFrame*, ClassClosure*) { assert(false && "newinstance not implemented"); return 0; }
   String* Stubs::do_abc_convert_s(MethodFrame*, Atom) { assert(false && "abc_convert_s not implemented"); return 0; }
   String* Stubs::do_abc_esc_xelem(MethodFrame*, Atom) { assert(false && "abc_esc_xelem not implemented"); return 0; }
   String* Stubs::do_abc_esc_xattr(MethodFrame*, Atom) { assert(false && "abc_esc_xattr not implemented"); return 0; }
   String* Stubs::do_abc_typeof(MethodFrame*, Atom) { assert(false && "abc_typeof not implemented"); return 0; }
@@ -715,24 +728,24 @@ struct Stubs {
   int32_t Stubs::do_rshi(MethodFrame*, int32_t, int32_t) { assert(false && "rshi not implemented"); return 0; }
   uint32_t Stubs::do_rshui(MethodFrame*, int32_t, int32_t) { assert(false && "rshui not implemented"); return 0; }
   int32_t Stubs::do_noti(MethodFrame*, int32_t) { assert(false && "noti not implemented"); return 0; }
   int32_t Stubs::do_negi(MethodFrame*, int32_t) { assert(false && "negi not implemented"); return 0; }
   double Stubs::do_negd(MethodFrame*, double) { assert(false && "negd not implemented"); return 0; }
   BoolKind Stubs::do_not(MethodFrame*, BoolKind) { assert(false && "not not implemented"); return 0; }
   ScriptObject* Stubs::do_newactivation(MethodFrame*, MethodEnv*) { assert(false && "newactivation not implemented"); return 0; }
   ScriptObject* Stubs::do_abc_finddef(MethodFrame*, const Multiname*, MethodEnv*) { assert(false && "abc_finddef not implemented"); return 0; }
-  Atom Stubs::do_abc_findpropstrict(MethodFrame*, const Multiname*, MethodEnv*, int, Atom*) { assert(false && "abc_findpropstrict not implemented"); return 0; }
-  Atom Stubs::do_abc_findpropstrictx(MethodFrame*, const Multiname*, MethodEnv*, Atom, int, Atom*) { assert(false && "abc_findpropstrictx not implemented"); return 0; }
-  Atom Stubs::do_abc_findpropstrictns(MethodFrame*, const Multiname*, MethodEnv*, Atom, int, Atom*) { assert(false && "abc_findpropstrictns not implemented"); return 0; }
-  Atom Stubs::do_abc_findpropstrictnsx(MethodFrame*, const Multiname*, MethodEnv*, Atom, Atom, int, Atom*) { assert(false && "abc_findpropstrictnsx not implemented"); return 0; }
-  Atom Stubs::do_abc_findproperty(MethodFrame*, const Multiname*, MethodEnv*, int, Atom*) { assert(false && "abc_findproperty not implemented"); return 0; }
-  Atom Stubs::do_abc_findpropertyx(MethodFrame*, const Multiname*, MethodEnv*, Atom, int, Atom*) { assert(false && "abc_findpropertyx not implemented"); return 0; }
-  Atom Stubs::do_abc_findpropertyns(MethodFrame*, const Multiname*, MethodEnv*, Atom, int, Atom*) { assert(false && "abc_findpropertyns not implemented"); return 0; }
-  Atom Stubs::do_abc_findpropertynsx(MethodFrame*, const Multiname*, MethodEnv*, Atom, Atom, int, Atom*) { assert(false && "abc_findpropertynsx not implemented"); return 0; }
+  Atom Stubs::do_abc_findpropstrict(MethodFrame*, const Multiname*, MethodEnv*, int, int, Atom*) { assert(false && "abc_findpropstrict not implemented"); return 0; }
+  Atom Stubs::do_abc_findpropstrictx(MethodFrame*, const Multiname*, MethodEnv*, int, Atom, int, Atom*) { assert(false && "abc_findpropstrictx not implemented"); return 0; }
+  Atom Stubs::do_abc_findpropstrictns(MethodFrame*, const Multiname*, MethodEnv*, int, Atom, int, Atom*) { assert(false && "abc_findpropstrictns not implemented"); return 0; }
+  Atom Stubs::do_abc_findpropstrictnsx(MethodFrame*, const Multiname*, MethodEnv*, int, Atom, int, Atom*) { assert(false && "abc_findpropstrictnsx not implemented"); return 0; }
+  Atom Stubs::do_abc_findproperty(MethodFrame*, const Multiname*, MethodEnv*, int, int, Atom*) { assert(false && "abc_findproperty not implemented"); return 0; }
+  Atom Stubs::do_abc_findpropertyx(MethodFrame*, const Multiname*, MethodEnv*, int, Atom, int, Atom*) { assert(false && "abc_findpropertyx not implemented"); return 0; }
+  Atom Stubs::do_abc_findpropertyns(MethodFrame*, const Multiname*, MethodEnv*, int, Atom, int, Atom*) { assert(false && "abc_findpropertyns not implemented"); return 0; }
+  Atom Stubs::do_abc_findpropertynsx(MethodFrame*, const Multiname*, MethodEnv*, int, Atom, int, Atom*) { assert(false && "abc_findpropertynsx not implemented"); return 0; }
   ClassClosure* Stubs::do_newclass(MethodFrame*, Traits*, ClassClosure*, int, Atom*) { assert(false && "newclass not implemented"); return 0; }
   ClassClosure* Stubs::do_newfunction(MethodFrame*, MethodInfo*, int, Atom*) { assert(false && "newfunction not implemented"); return 0; }
   Atom Stubs::do_abc_getsuper(MethodFrame*, const Multiname*, Atom) { assert(false && "abc_getsuper not implemented"); return 0; }
   Atom Stubs::do_abc_getsuperx(MethodFrame*, const Multiname*, Atom, Atom) { assert(false && "abc_getsuperx not implemented"); return 0; }
   Atom Stubs::do_abc_getsuperns(MethodFrame*, const Multiname*, Atom, Atom) { assert(false && "abc_getsuperns not implemented"); return 0; }
   Atom Stubs::do_abc_getsupernsx(MethodFrame*, const Multiname*, Atom, Atom, Atom) { assert(false && "abc_getsupernsx not implemented"); return 0; }
   Atom Stubs::do_abc_getdescendants(MethodFrame*, const Multiname*, Atom) { assert(false && "abc_getdescendants not implemented"); return 0; }
   Atom Stubs::do_abc_getdescendantsx(MethodFrame*, const Multiname*, Atom, Atom) { assert(false && "abc_getdescendantsx not implemented"); return 0; }
@@ -749,33 +762,36 @@ struct Stubs {
   Atom Stubs::do_abc_constructprop(MethodFrame*, const Multiname*, int, Atom*) { assert(false && "abc_constructprop not implemented"); return 0; }
   Atom Stubs::do_abc_constructpropx(MethodFrame*, const Multiname*, Atom, int, Atom*) { assert(false && "abc_constructpropx not implemented"); return 0; }
   Atom Stubs::do_abc_constructpropns(MethodFrame*, const Multiname*, Atom, int, Atom*) { assert(false && "abc_constructpropns not implemented"); return 0; }
   Atom Stubs::do_abc_constructpropnsx(MethodFrame*, const Multiname*, Atom, Atom, int, Atom*) { assert(false && "abc_constructpropnsx not implemented"); return 0; }
   Atom Stubs::do_abc_callsuper(MethodFrame*, const Multiname*, int, Atom*) { assert(false && "abc_callsuper not implemented"); return 0; }
   Atom Stubs::do_abc_callsuperx(MethodFrame*, const Multiname*, Atom, int, Atom*) { assert(false && "abc_callsuperx not implemented"); return 0; }
   Atom Stubs::do_abc_callsuperns(MethodFrame*, const Multiname*, Atom, int, Atom*) { assert(false && "abc_callsuperns not implemented"); return 0; }
   Atom Stubs::do_abc_callsupernsx(MethodFrame*, const Multiname*, Atom, Atom, int, Atom*) { assert(false && "abc_callsupernsx not implemented"); return 0; }
-  ScriptObject* Stubs::do_newcatch(MethodFrame*, Traits*) { assert(false && "newcatch not implemented"); return 0; }
+  Atom Stubs::do_newcatch(MethodFrame*, Traits*) { assert(false && "newcatch not implemented"); return 0; }
   Traits* Stubs::do_slottype(MethodFrame*, ScriptObject*, int) { assert(false && "slottype not implemented"); return 0; }
   Atom Stubs::do_getouterscope(MethodFrame*, int, MethodEnv*) { assert(false && "getouterscope not implemented"); return 0; }
   void Stubs::do_deopt_finish(MethodFrame*) { assert(false && "deopt_finish not implemented"); }
+  void Stubs::do_debugline(MethodFrame*, int32_t) { assert(false && "debugline not implemented"); }
+  void Stubs::do_debugfile(MethodFrame*, String*) { assert(false && "debugfile not implemented"); }
   Atom Stubs::do_string2atom(MethodFrame*, String*) { assert(false && "string2atom not implemented"); return 0; }
   Atom Stubs::do_double2atom(MethodFrame*, double) { assert(false && "double2atom not implemented"); return 0; }
   Atom Stubs::do_int2atom(MethodFrame*, int32_t) { assert(false && "int2atom not implemented"); return 0; }
   Atom Stubs::do_uint2atom(MethodFrame*, uint32_t) { assert(false && "uint2atom not implemented"); return 0; }
   Atom Stubs::do_scriptobject2atom(MethodFrame*, ScriptObject*) { assert(false && "scriptobject2atom not implemented"); return 0; }
   Atom Stubs::do_bool2atom(MethodFrame*, BoolKind) { assert(false && "bool2atom not implemented"); return 0; }
   Atom Stubs::do_ns2atom(MethodFrame*, Namespace*) { assert(false && "ns2atom not implemented"); return 0; }
   BoolKind Stubs::do_atom2bool(MethodFrame*, Atom) { assert(false && "atom2bool not implemented"); return 0; }
   double Stubs::do_atom2double(MethodFrame*, Atom) { assert(false && "atom2double not implemented"); return 0; }
   String* Stubs::do_atom2string(MethodFrame*, Atom) { assert(false && "atom2string not implemented"); return 0; }
   int32_t Stubs::do_atom2int(MethodFrame*, Atom) { assert(false && "atom2int not implemented"); return 0; }
   uint32_t Stubs::do_atom2uint(MethodFrame*, Atom) { assert(false && "atom2uint not implemented"); return 0; }
   ScriptObject* Stubs::do_atom2scriptobject(MethodFrame*, Atom) { assert(false && "atom2scriptobject not implemented"); return 0; }
+  Namespace* Stubs::do_atom2ns(MethodFrame*, Atom) { assert(false && "atom2ns not implemented"); return 0; }
   double Stubs::do_i2d(MethodFrame*, int32_t) { assert(false && "i2d not implemented"); return 0; }
   double Stubs::do_u2d(MethodFrame*, uint32_t) { assert(false && "u2d not implemented"); return 0; }
   int32_t Stubs::do_d2i(MethodFrame*, double) { assert(false && "d2i not implemented"); return 0; }
   uint32_t Stubs::do_d2u(MethodFrame*, double) { assert(false && "d2u not implemented"); return 0; }
   int Stubs::do_toslot(MethodFrame*, ScriptObject*, const Multiname*) { assert(false && "toslot not implemented"); return 0; }
   Atom Stubs::do_toprimitive(MethodFrame*, Atom) { assert(false && "toprimitive not implemented"); return 0; }
   BoolKind Stubs::do_eqi(MethodFrame*, int32_t, int32_t) { assert(false && "eqi not implemented"); return 0; }
   BoolKind Stubs::do_lti(MethodFrame*, int32_t, int32_t) { assert(false && "lti not implemented"); return 0; }
--- a/halfmoon/halfmoon.h
+++ b/halfmoon/halfmoon.h
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 namespace halfmoon {
 using avmplus::AbcEnv;
 using avmplus::CodegenDriver;
 using avmplus::CodeWriter;
@@ -64,17 +64,17 @@ public:
   void writeCheckNull(const FrameState* state, uint32_t index);
   void writeCoerce(const FrameState* state, uint32_t index, Traits *type);
   void writeEpilogue(const FrameState* state);
   void writeBlockStart(const FrameState* state);
 
 private:
   void analyze(AbcOpcode abcop, const uint8_t* pc, const FrameState*);
   void finishBlock(const uint8_t* nextpc);
-  void newBlock(const uint8_t* pc);
+  void newBlock(const uint8_t* pc, const FrameState* state);
   void startBlock(const FrameState*);
 
 public:
   InstrGraph *ir_;  //TODO: matz_inline_experiment review this comment. (stash graph for inlined callees here)
   //maybe put callers context here? Then compile callee could set it when compiling for inliner. matz
 private:
   Allocator alloc_;
   MethodInfo* method_;
@@ -97,11 +97,12 @@ private:
 ///
 /// For the purposes of this analyzer, A loop is formed by an edge from B2->B1
 /// whenever B2 comes after B1 in linear order, ignoring control-flow paths.
 /// An instruction is "in the loop" if its between B1.first_ins and B2.last_ins.
 ///
 /// Returns true on success.  Failures print a diagnostic and assert (debug),
 /// or return false (release).
 ///
-bool checkLir(Fragment*);
+using avmplus::AvmLogControl;
+bool checkLir(Fragment*, AvmLogControl* logc);
 
-} // namespace avmplus
+} // namespace halfmoon
--- a/halfmoon/hm-abcbuilder.cpp
+++ b/halfmoon/hm-abcbuilder.cpp
@@ -18,33 +18,37 @@ using avmplus::TraitsBindings;
 
 using profiler::MethodProfile;
 using profiler::ProfiledState;
 using profiler::RecordedType;
 
 // OP_end means we fell off the end of a label.
 static const AbcOpcode OP_end = AbcOpcode(-1);
 
-AbcBuilder::AbcBuilder(MethodInfo* method, AbcGraph* abc, InstrFactory* factory, Toplevel* toplevel, ProfiledInformation* profiled_information)
+  AbcBuilder::AbcBuilder(MethodInfo* method, AbcGraph* abc, InstrFactory* factory, Toplevel* toplevel, ProfiledInformation* profiled_information,
+                         bool has_reachable_exceptions)
 : alloc_(factory->alloc())
 , alloc0_(factory->alloc())
 , method_(method)
 , sig_(method->getMethodSignature())
 , abc_(abc)
 , pool_(method->pool())
 , console_(pool_->core->console)
 , lattice_(factory->lattice())
 , scope_base_(sig_->local_count())
 , stack_base_(scope_base_ + sig_->max_scope())
 , framesize_(stack_base_ + sig_->max_stack())
-, num_vars_(framesize_ + 2)
+, num_vars_(framesize_ + 2 + (has_reachable_exceptions ? framesize_ : 0))
 , effect_pos_(framesize_)
 , state_pos_(framesize_ + 1)
+, setlocal_pos_(has_reachable_exceptions ? (framesize_ + 2) : 0)
 , frame_(new (alloc0_) Def*[num_vars_])
 , code_pos_(sig_->abc_code_start())
+, pc_(NULL)
+, has_reachable_exceptions_(has_reachable_exceptions)
 , return_label_(0)
 , throw_label_(0)
 , factory_(*factory)
 , ir_(factory->createGraph())
 , builder_(ir_, factory)
 , profiled_info_(profiled_information)
 , toplevel_(toplevel)
 {
@@ -112,54 +116,107 @@ AbcBuilder::AbcBuilder(MethodInfo* metho
 }
 
 AbcBuilder::~AbcBuilder() {
 }
 
 InstrGraph* AbcBuilder::visitBlocks(Seq<AbcBlock*> *list) {
   // Build the instr graph by visiting ABC blocks in reverse postorder.
   buildStart();
-  for (SeqRange<AbcBlock*> b(list); !b.empty(); b.popFront())
-    visitBlock(b.front());
+
+  for (SeqRange<AbcBlock*> b(list); !b.empty(); b.popFront()) {
+    AbcBlock* abc_block = b.front();
+
+    // Exception blocks shouldn't be normally reachable
+    AvmAssert(abc_block->label == NULL || !pcIsHandler(abc_block->start));
+
+    // Lazily fill in the exception handler blocks before parsing them
+    if (abc_block->label == NULL && pcIsHandler(abc_block->start)) {
+      AvmAssert(has_reachable_exceptions_);
+      ensureCatchBlockLabel(abc_block);
+    }
+    visitBlock(abc_block);
+  }
+
+  // In the current world of setjmp/longjmp style exception handling
+  // it's sufficient to link all the exception blocks off the start
+  // block.  Later if we have more precise handling of exception
+  // blocks it might be beneficial to directly track which blocks
+  // might have exception handlers.  So for now this code is disabled
+#if 0
+  for (SeqRange<AbcBlock*> b(list); !b.empty(); b.popFront()) {
+    AbcBlock* abc_block = b.front();
+    if (abc_block->label == NULL)
+      continue;
+    for (int c = 0; c < abc_block->max_catch_blocks; c++) {
+      if (abc_block->catch_blocks[c] != NULL) {
+        BlockEndInstr* end = InstrGraph::blockEnd(abc_block->label);
+        if (end == NULL) continue;
+        CatchBlockInstr* handler = (CatchBlockInstr*)abc_block->catch_blocks[c]->label;
+        linkExceptionEdge(abc_block->label, handler);
+      }
+    }
+  }  
+#endif
+
   finish();
+
+  if (enable_printir)
+    listCfg(console_, ir_);
+
   assert(checkPruned(ir_) && checkSSA(ir_));
   Context cxt(method_);
   propagateTypes(ir_);
   removeDeadCode(&cxt, ir_);
   return ir_;
 }
 
 void AbcBuilder::visitBlock(AbcBlock* abc_block) {
   if (!abc_block->label)
     return;
   startBlock(abc_block);
+
+  bool in_try = false;
+  for (int c = 0; c < abc_block->max_catch_blocks; c++) {
+    if (abc_block->catch_blocks[c] != NULL) {
+      in_try = true;
+      break;
+    }
+  }
+
+  if (!in_try && has_reachable_exceptions_) {
+    safepointStmt(abc_block->start);
+  }
+
   const uint8_t* end = abc_block->end;
   for (const uint8_t* pc = abc_block->start;;) {
     uint32_t imm30 = 0, imm30b = 0;
     int imm8 = 0, imm24 = 0;
     const uint8_t* nextpc = pc;
     AbcOpcode abcop = readInstr(nextpc, end, imm30, imm30b, imm24, imm8);
     if (abcop == OP_end) {
       jumpStmt(); // Add the fall-through edge.
       break;
     }
     // TODO: could we restrict to just this:
     //       (abc_block->in_try && opcodeInfo[abcop].canThrow)
-    if (needAbcState())
+    // if (needAbcState())
+    if (in_try && opcodeInfo[abcop].canThrow)
       safepointStmt(pc);
     // TODO: This is preposterous without a filter for catchable
     // canThrow instructions.  We must also safepoint targets of
     // backward branches.  Avoid safepointing calls.
     //emitThrowSafepoint(pc);
     visitInstr(pc, abcop, imm30, imm30b, imm8);
     assert(checkFrame(frame_, stackp_, scopep_));
     if (isBlockEnd(abcop))
       break;
-    pc = nextpc;
+    pc_ = pc = nextpc;
   }
+  pc_ = NULL;
 }
 
 bool AbcBuilder::shouldSpeculate() {
   bool speculate = enable_profiler > 0;
   profiler::PROFILING_STATE state =
     JitManager::getProfile(method_)->current_profiler_state_;
 
   // We may deopt, so we should stop speculating
@@ -273,16 +330,17 @@ void AbcBuilder::buildStart() {
   builder_.addInstr(newstate);
 
   if (method_->isConstructor())
     emitInitializers(start->data_param(1));
 
   set_state(newstate->value());
   scopep_ = scope_base_ - 1;
   stackp_ = stack_base_ - 1;
+  withbase_ = -1;
 
   int local_id = 0;
   for (int n = start->data_param_count() - 1; local_id < n; ++local_id) {
     Def* local_def = start->data_param(local_id + 1);
     setLocal(local_id, local_def);
   }
 
   for (; local_id < scope_base_; ++local_id) {
@@ -299,41 +357,59 @@ void AbcBuilder::buildStart() {
 void AbcBuilder::finish() {
   if (return_label_)
     ir_->end = cast<StopInstr>(ir_->blockEnd(return_label_));
   if (throw_label_)
     ir_->exit = cast<StopInstr>(ir_->blockEnd(throw_label_));
 }
 
 void AbcBuilder::safepointStmt(const uint8_t* pc) {
-  assert (needAbcState());
+  // assert (needAbcState());
+  Def** vars = new (alloc0_) Def*[num_vars_];
+  int argc = 0;
+
+  for (int i = 0, n = scope_base_; i < n; i++) {
+    vars[argc++] = frame_[setlocal_pos_ + i];
+  }
+
+  // scopep_ starts at scope_base - 1
+  for (int i = scope_base_; i <= scopep_; i++) {
+    vars[argc++] = frame_[setlocal_pos_ + i];
+  }
+
+  // stackp_ starts at stack_base - 1
+  for (int i = stack_base_; i <= stackp_; i++) {
+    vars[argc++] = frame_[setlocal_pos_ + i];
+  }
+  assert(argc < num_vars_);
+
   Def* effect = this->effect();
-  SafepointInstr* instr = factory_.newSafepointInstr(effect, state());
+  SafepointInstr* instr = factory_.newSafepointInstr(effect, argc, vars);
+  instr->vpc = int(pc - code_pos_);
+  instr->sp = stackp_;
+  instr->scopep = scopep_;
+
   set_state(instr->state_out());
   builder_.addInstr(instr);
   set_effect(instr->effect_out());
-
-  instr->vpc = int(pc - code_pos_);
-  instr->sp = stackp_;
-  instr->scopep = scopep_;
 }
 
 /// DEOPT
 /// In the future, we will want to filter the frame slots
 /// with liveness information.  At present, we assume that
 /// all locals and active scopes and operands are live.
 DeoptSafepointInstr* AbcBuilder::emitBailoutSafepoint(const uint8_t* pc) {
   if (!enable_deopt) return NULL;  // DEOPT FIXME
   // TODO: We could re-use a fixed-sized buffer,
   // allocated once per compilation.
   Def** vars = new (alloc0_) Def*[framesize_];
   int argc = 0;
 
   for (int i = 0, n = sig_->local_count(); i < n; i++) {
-      vars[argc++] = getLocal(i);
+    vars[argc++] = getLocal(i);
   }
 
   // scopep_ starts at scope_base - 1
   for (int i = scope_base_; i <= scopep_; i++) {
     vars[argc++] = getLocal(i);
   }
 
   // stackp_ starts at stack_base - 1
@@ -362,17 +438,17 @@ DeoptSafepointInstr* AbcBuilder::emitBai
 DeoptSafepointInstr* AbcBuilder::emitThrowSafepoint(const uint8_t* pc) {
   if (!enable_deopt) return NULL;  // DEOPT FIXME
   // TODO: We could re-use a fixed-sized buffer,
   // allocated once per compilation.
   Def** vars = new (alloc0_) Def*[framesize_];
   int argc = 0;
 
   for (int i = 0, n = sig_->local_count(); i < n; i++) {
-      vars[argc++] = getLocal(i);
+    vars[argc++] = getLocal(i);
   }
 
   // we do not need to preserve scopes or operands
 
   Def* effect = this->effect();
   DeoptSafepointInstr* instr =
     factory_.newDeoptSafepointInstr(effect, argc, vars);
   builder_.addInstr(instr);
@@ -451,17 +527,20 @@ void AbcBuilder::emitFinishCall(DeoptSaf
     DeoptFinishCallInstr* instr = factory_.newDeoptFinishCallInstr(effect, arg);
     builder_.addInstr(instr);
     set_effect(instr->effect_out());
     instr->safepoint = sfp;
   }
 }
 
 void AbcBuilder::set_effect(Def* effect) {
-  frame_[effect_pos_] = peephole(effect);
+  // Def* new_effect = peephole(effect);
+  // assert(effect == new_effect);
+  assert(kind(type(effect)) == kTypeEffect);
+  frame_[effect_pos_] = effect;
 }
 
 void AbcBuilder::set_state(Def* state) {
   frame_[state_pos_] = state;
 }
 
 AbcOpcode peek(const uint8_t *pc, const uint8_t* end) {
   return pc != end ? AbcOpcode(*pc) : OP_end;
@@ -481,18 +560,16 @@ AbcOpcode AbcBuilder::readInstr(const ui
   AbcOpcode abcop = AbcOpcode(*pc);
   AvmCore::readOperands(pc, imm30, imm24, imm30b, imm8);
   if (pc == end || !enable_peephole)
     return abcop;
   switch (abcop) {
     case OP_coerce_a:
     case OP_nop:
     case OP_label:
-    case OP_debugline:
-    case OP_debugfile:
       goto again;
   }
   switch (abcop) {
     case OP_pushundefined:
       abcop = peek(pc, end);
       if (abcop == OP_pop) {
         ++pc;
         goto again;
@@ -732,40 +809,19 @@ bool AbcBuilder::isType(Def* val, Record
 }
 
 /// Stores the current ABC state by emitting setlocals for every value
 /// in the abc stack frame.
 /// scopep offset is the scope depth.
 /// stackp_offset is the stack depth
 /// we cannot rely on the stackp_ / scopep_ vars because defs may already have
 /// been popped / pushed prior to calling savestate.
-void AbcBuilder::saveState(const uint8_t* pc,
-                          int scopep_offset, int stackp_offset) {
-  for (int i = 0; i < this->sig_->local_count(); i++) {
-    createSetLocalInstr(i, getLocal(i));
-  }
-
-  // scopep_ starts at scope_base - 1
-  for (int i = scope_base_; i <= scopep_ + scopep_offset; i++) {
-    createSetLocalInstr(i, getLocal(i));
-  }
-
-  // stackp_ starts at stack_base - 1
-  for (int i = stack_base_; i <= stackp_ + stackp_offset; i++) {
-    createSetLocalInstr(i, getLocal(i));
-  }
-
-  SafepointInstr* instr = factory_.newSafepointInstr(effect(), state());
-  set_state(instr->state_out());
-  builder_.addInstr(instr);
-  set_effect(instr->effect_out());
-
-  instr->vpc = int(pc - code_pos_);
-  instr->sp = stackp_ + stackp_offset;
-  instr->scopep = scopep_ + scopep_offset;
+void AbcBuilder::saveState(const uint8_t*,
+                          int, int) {
+  assert(false);
 }
 
 bool AbcBuilder::hasType(Def* val, RecordedType recorded_type) {
   return isType(val, recorded_type) || !isAny(type(val));
 }
 
 InstrKind AbcBuilder::getSpeculativeKind(RecordedType recorded_type) {
   switch (recorded_type) {
@@ -835,22 +891,38 @@ void AbcBuilder::visitInstr(const uint8_
       assert(false && "Unsupported opcode");
 
     case OP_istype:
       pushDef(binaryStmt(HR_abc_istype, traitsConst(imm30), popDef()));
       break;
 
     case OP_debug:
     case OP_bkpt:
+    case OP_bkptline:
     case OP_label:
     case OP_nop:
     case OP_timestamp:
-    case OP_debugfile:
-    case OP_debugline:
+      break;
+
+    case OP_debugfile: {
+      // Steal code from CodegenLIR and put it into the stubs for this
+      if (enable_trace) {
+        Def* filename = createConst(lattice_.makeStringConst(pool_, imm30));
+        debugInstr(HR_debugfile, filename);
+      }
       break;
+    }
+    case OP_debugline: {
+      // Steal code from CodegenLIR and put it into the stubs for this
+      if (enable_trace) {
+        Def* lineno = createConst(lattice_.makeIntConst(imm30));
+        debugInstr(HR_debugline, lineno);
+      }
+      break;
+    }
 
     case OP_jump:
       jumpStmt();
       break;
 
     case OP_lookupswitch:
       switchStmt(imm30b + 1, popDef());
       break;
@@ -917,35 +989,36 @@ void AbcBuilder::visitInstr(const uint8_
       pushDef(getLocal(imm30));
       break;
 
     case OP_setlocal:
       setLocal(imm30, popDef());
       break;
 
     case OP_dup:
-      temp1 = popDef();
-      pushDef(temp1);
-      pushDef(temp1);
+      pushDef(peekDef());
       break;
 
     case OP_pop:
       popDef();
       break;
 
     case OP_popscope:
       scopep_--;
+      if (withbase_ >= scopep_)
+        withbase_ = -1;
       break;
 
     case OP_pushwith:
-    {
-      // We don't support withbase in halfmoon cryil release.
-      // TODO: fix after release
-      assert(false && "need to track withbase");
-    }
+      temp1 = nullCheck(popDef());
+      setLocal(++scopep_, temp1);
+      if (withbase_ == -1)
+        withbase_ = scopep_;
+      break;
+
     case OP_pushscope:
       temp1 = nullCheck(popDef());
       setLocal(++scopep_, temp1);
       break;
 
     case OP_swap:
       temp1 = popDef();
       temp2 = popDef();
@@ -1027,16 +1100,21 @@ void AbcBuilder::visitInstr(const uint8_
       pushDef(binaryExpr(HR_abc_astype, traitsConst(imm30), popDef()));
       break;
 
     case OP_coerce:
       pushDef(coerceExpr(getNamedTraits(imm30), popDef()));
       break;
 
     case OP_instanceof:
+      temp2 = popDef(); // RHS is null checked by instanceof since it throws a different exception
+      temp1 = popDef();
+      pushDef(binaryStmt(kind_map_[abcop], temp1, temp2));
+      break;
+
     case OP_istypelate:
     case OP_astypelate:
     case OP_in:
       temp2 = nullCheck(popDef()); // RHS object cannot be null.
       temp1 = popDef();
       pushDef(binaryStmt(kind_map_[abcop], temp1, temp2));
       break;
 
@@ -1275,27 +1353,33 @@ void AbcBuilder::visitInstr(const uint8_
       break;
 
     case OP_callsupervoid:
       sfp = emitCallSafepoint(pc, imm30b);
       callStmt(callsuper_kinds, imm30, imm30b);
       emitFinishCall(sfp, NULL);
       break;
 
-    case OP_callstatic:
+    case OP_callstatic: {
       // fixme: coerce args first, but this requires knowing obj type, which
       //        we don't have inside of loops.
       sfp = emitCallSafepoint(pc, imm30b);
       args = popArgs(imm30b);
+      MethodInfo* callee = pool_->getMethodInfo(imm30);
+      assert(callee->method_id() == (int)imm30);
+      temp1 = popDef();
+      temp1 = coerceArgs(temp1, args, imm30b, callee);
+      temp1 = nullCheck(temp1); // obj
       temp2 = ordinalConst(imm30);
-      temp1 = nullCheck(popDef()); // obj
+      temp2 = binaryExpr(HR_loadenv_env, temp2, env_param());
       temp3 = callStmt2(HR_callstatic, temp2, temp1, args, imm30b);
       emitFinishCall(sfp, temp3);
       pushDef(temp3);
       break;
+    }
 
     case OP_call:
       // stack: function thisarg args...
       sfp = emitCallSafepoint(pc, imm30);
       args = popArgs(imm30); // imm30 = extra_argc
       temp2 = popDef(); // thisarg
       temp1 = popDef(); // function
       temp3 = callStmt2(HR_call, temp1, temp2, args, imm30);
@@ -1340,16 +1424,21 @@ void AbcBuilder::visitInstr(const uint8_
     case OP_setglobalslot:
       temp1 = getglobalscope();
       temp2 = popDef();
       setslotStmt(imm30 - 1, temp1, temp2);
       break;
 
     case OP_hasnext2:
       pushDef(hasnext2Stmt(&frame_[imm30], &frame_[imm30b]));
+      if (has_reachable_exceptions_) {
+        // Make sure to update the state of the locals
+        setLocal(imm30, getLocal(imm30));
+        setLocal(imm30b, getLocal(imm30b));
+      }
       break;
 
     case OP_hasnext:
       temp1 = popDef(); // index:int
       temp2 = popDef(); // object
       pushDef(binaryStmt(HR_abc_hasnext, temp2, temp1));
       break;
 
@@ -1363,16 +1452,18 @@ void AbcBuilder::visitInstr(const uint8_
 
     case OP_applytype:
       // FIXME: abc format allows N type parameters as args, but Vector
       // is the only class works, and it only allows 1 arg.  There is a
       // misplaced assert() in VectorClass::applyTypeArgs().
       pushDef(naryStmt(HR_applytype, imm30 + 1));
       break;
   }
+  if (enable_framestate) 
+    printFrameState();
 }
 
 Def* AbcBuilder::getglobalscope() {
   return method_->declaringScope()->size == 0 ? getLocal(scope_base_) :
       getouterscopeStmt(0);
 }
 
 /// Expand getlex into (fatgetprop name (findstrict name [scopes]))
@@ -1401,29 +1492,29 @@ Def* AbcBuilder::findStmt(const InstrKin
           return getouterscopeStmt(abc_instr->index);
         case OP_finddef:
           assert(abc_instr->index == name_index);
           return finddefStmt(name);
         case OP_findpropglobal:
         case OP_findpropglobalstrict:
         case OP_findproperty:
         case OP_findpropstrict:
-          return naryStmt2(kinds[arity], name, env, scopes, scope_count);
+          return naryStmt3(kinds[arity], name, env, ordinalConst(withbase_ == -1 ? -1 : withbase_ - scope_base_), scopes, scope_count);
         case OP_getglobalscope:
           return getglobalscope();
       }
       break;
     }
     case kNameIndex:
     case kNameNs:
-      return naryStmt3(kinds[arity], name, env, popDef(), scopes, scope_count);
+      return naryStmt4(kinds[arity], name, env, ordinalConst(withbase_ == -1 ? -1 : withbase_ - scope_base_), popDef(), scopes, scope_count);
     case kNameNsIndex: {
       Def* index = popDef();
       Def* ns = popDef();
-      return naryStmt4(kinds[arity], name, env, ns, index, scopes, scope_count);
+      return naryStmt4(kinds[arity], name, env, ordinalConst(withbase_ == -1 ? -1 : withbase_ - scope_base_), ns, index, scopes, scope_count);
     }
   }
 }
 
 Def* AbcBuilder::finddefStmt(Def *name) {
   return binaryStmt(HR_abc_finddef, name, env_param());
 }
 
@@ -1449,30 +1540,61 @@ Def* AbcBuilder::toInt(Def* val) {
   assert (false && "Not quite supported");
   if (isInt(type(val)))
     return val;
   // toint is a statement because it can call Object.valueOf()
   return unaryStmt(HR_toint, val);
 }
 
 bool AbcBuilder::needAbcState() {
-  return enable_vmstate || method_->hasExceptions();
+  if (enable_vmstate) return true;
+  if (has_reachable_exceptions_) {
+    return handlerCoversPc(pc_);
+  }
+  return false;
+}
+
+bool AbcBuilder::handlerCoversPc(const uint8_t* pc) {
+  if (has_reachable_exceptions_) {
+    ExceptionHandlerTable* exTable = method_->abc_exceptions();
+    for (int i=0, n=exTable->exception_count; i < n; i++) {
+      ExceptionHandler* handler = &exTable->exceptions[i];
+      if (pc >= code_pos_ + handler->from && pc < code_pos_ + handler->to) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+bool AbcBuilder::pcIsHandler(const uint8_t* pc) {
+  if (has_reachable_exceptions_) {
+    ExceptionHandlerTable* exTable = method_->abc_exceptions();
+    for (int i=0, n=exTable->exception_count; i < n; i++) {
+      ExceptionHandler* handler = &exTable->exceptions[i];
+      if (pc == code_pos_ + handler->target) {
+        return true;
+      }
+    }
+  }
+  return false;
 }
 
 void AbcBuilder::createSetLocalInstr(int i, Def* val) {
   SetlocalInstr* setlocal = factory_.newSetlocalInstr(i, state(), val);
-  set_state(setlocal->state_out());
+  // set_state(setlocal->state_out());
   builder_.addInstr(setlocal);
+  frame_[setlocal_pos_ + i] = setlocal->state_out();
 }
 
 void AbcBuilder::setLocal(int i, Def* val) {
   frame_[i] = val;
   // We have to save all assignments because setlocal and safepoints
   // are used for deopt in addition to exceptions.
-  if (!needAbcState()) {
+  if (!has_reachable_exceptions_) {
     return;
   }
 
   createSetLocalInstr(i, val);
 }
 
 Traits* AbcBuilder::getNamedTraits(uint32_t type_name_index) {
   DomainMgr* domainMgr = pool_->core->domainMgr();
@@ -1541,16 +1663,24 @@ Def* AbcBuilder::naryStmt3(InstrKind kin
 Def* AbcBuilder::naryStmt4(InstrKind kind, Def* name, // multiname
                            Def* env, Def* ns, Def *index, Def* args[],
                            int extra_argc) {
   NaryStmt4* stmt = factory_.newNaryStmt4(kind, effect(), name, env, ns, index,
                                           extra_argc, args);
   return finishStmt(stmt, stmt->effect_out(), stmt->value_out());
 }
 
+Def* AbcBuilder::naryStmt4(InstrKind kind, Def* name, // multiname
+                           Def* env, Def* ns, Def *index, Def* index2, Def* args[],
+                           int extra_argc) {
+  NaryStmt4* stmt = factory_.newNaryStmt4(kind, effect(), name, env, ns, index, index2,
+                                          extra_argc, args);
+  return finishStmt(stmt, stmt->effect_out(), stmt->value_out());
+}
+
 Def* AbcBuilder::callStmt2(InstrKind kind, Def *name, // name, function, or id
                           Def *object, Def* args[], int extra_argc) {
   CallStmt2* call = factory_.newCallStmt2(kind, effect(), name, object,
                                           extra_argc, args);
   return finishStmt(call, call->effect_out(), call->value_out());
 }
 
 Def* AbcBuilder::finishStmt(Instr* instr, Def* effect, Def* value) {
@@ -1744,17 +1874,18 @@ void AbcBuilder::stopStmt(InstrKind kind
   }
   GotoInstr* goto_instr = factory_.newGotoStmt(label);
   goto_instr->args[0] = effect();
   goto_instr->args[1] = value;
   builder_.addInstr(goto_instr);
 }
 
 Def * AbcBuilder::nullCheck(Def* ptr) {
-  if (!isNullable(type(ptr)))
+  const Type* ptr_type = type(ptr);
+  if (!isNullable(ptr_type) && ptr_type->kind != kTypeVoid)
     return ptr;
 
   //ir_->addNullcheck(&effect_, &ptr_out);
   UnaryStmt* stmt = factory_.newUnaryStmt(HR_cknull, effect(), ptr);
   builder_.addInstr(stmt);
   Def* ptr_out = stmt->value_out();
 
   // Refs to ptr now should point to notnull(ptr)
@@ -1764,16 +1895,27 @@ Def * AbcBuilder::nullCheck(Def* ptr) {
   for (FrameRange<Def*> r = frameRange(frame_); !r.empty(); r.popFront())
     if (r.front() == ptr)
       r.front() = ptr_out;
 
   set_effect(stmt->effect_out());
   return ptr_out;
 }
 
+/// add unary statement on passed arg to ir graph in progress.
+/// control input is taken from last statement added.
+/// new statement's data output is returned, unless peephole()
+/// bypasses (but note that stmt is linked into ir regardless)
+///
+void AbcBuilder::debugInstr(InstrKind kind, Def* arg) {
+  DebugInstr* stmt = factory_.newDebugInstr(kind, effect(), arg);
+  builder_.addInstr(stmt);
+  set_effect(stmt->effect_out());
+}
+
 /// Handle an if statement with the given sense and condition
 ///
 void AbcBuilder::ifStmt(bool sense, Def* cond) {
   const Type* cond_type = type(cond);
   if (isConst(cond_type)) {
     // cond is already known to be const, replace if with goto
     int succ = int(boolVal(cond_type) == sense);
     addGoto(abc_block_->succ_blocks[succ]);
@@ -1800,16 +1942,24 @@ void AbcBuilder::setFrameArgs(BlockEndIn
 
   FrameRange<Use> arg_range = frameRange(args);
   FrameRange<Def*> from_def = frameRange(frame_);
   for (; !arg_range.empty(); arg_range.popFront(), from_def.popFront())
     arg_range.front() = from_def.front();
 
   args[effect_pos_] = effect();
   args[state_pos_] = state();
+
+  if (has_reachable_exceptions_) {
+    // bring down the state of the locals from the label too
+    FrameIndexRange fir(stackp_, scopep_, stack_base_);
+    for (; !fir.empty(); fir.popFront()) {
+      args[setlocal_pos_ + fir.front()] = frame_[setlocal_pos_ + fir.front()];
+    }
+  }
 }
 
 /// Map the given successor of the current abc_block_
 /// to a given ArmInstr and current stack/scope chain.
 ///
 void AbcBuilder::addArm(int i, ArmInstr* arm, bool switch_arm) {
   AbcBlock* to_block = abc_block_->succ_blocks[i];
   AbcBlock* current_block = abc_block_;
@@ -1820,17 +1970,17 @@ void AbcBuilder::addArm(int i, ArmInstr*
     int start_pc = int(current_block->end - 4 - abc_->code_pos());
     int target_pc = int(to_block->start - abc_->code_pos());
     MethodProfile* profile = JitManager::getProfile(method_);
     double branch_probability = profile->getBranchProbability(start_pc,
                                                               target_pc);
     profiled_info_->addBranchProbability(arm, branch_probability);
   }
 
-  if (to_block->num_preds > 1) {
+  if (to_block->num_preds > 1 || abc_block_ == to_block) {
     // Target block either has or will need a label, so create an empty
     // block for this arm, ending in a goto.
     builder_.addInstr(arm);
     set_effect(&arm->params[effect_pos_]);
     addGoto(to_block);
   } else {
     // Target block's only predecessor is the CondInstr that owns this arm.
     assert(!to_block->label);
@@ -1878,30 +2028,103 @@ void AbcBuilder::addGoto(AbcBlock* to_bl
   LabelInstr* label = ensureBlockLabel(to_block);
   GotoInstr* go = factory_.newGotoStmt(label, never_def_);
   setFrameArgs(go);
   builder_.addInstr(go);
 }
 
 /// helper - check/initialize an AbcBlock's LabelInstr
 ///
+CatchBlockInstr* AbcBuilder::ensureCatchBlockLabel(AbcBlock* abc_block) {
+  stackp_ = abc_block->start_sp = stack_base_;
+  scopep_ = abc_block->start_scopep = scope_base_ - 1;
+  withbase_ = abc_block->start_withbase;
+  assert(withbase_ == -1 || withbase_ > 0);
+  if (withbase_ != -1) {
+    withbase_ += scope_base_;
+  }
+
+  // set up the label with param capacity for the current frame
+  CatchBlockInstr* catch_block = factory_.newCatchBlockInstr(num_vars_);
+  catch_block->vpc = int(abc_block->start - code_pos_);
+
+  for (int i = 0; i < num_vars_; ++i) {
+    new (&catch_block->params[i]) Def(catch_block, lattice_.void_type);
+  }
+
+  setType(&catch_block->params[effect_pos_], EFFECT);
+  setType(&catch_block->params[state_pos_], STATE);
+
+  abc_block->label = catch_block;
+  ir_->addBlock(catch_block);
+
+  FrameRange<Def> p = frameRange(catch_block->params);
+  FrameRange<const Type*> t = abc_block->startTypesRange(stack_base_);
+  for (; !t.empty(); t.popFront(), p.popFront()) {
+    if (t.front() != NULL) {
+      // The model for the exception edges must be Atom for now to
+      // ensure consistent representations.  It could be relaxed if
+      // all exception edges had the same model.
+      new (&p.front()) Def(catch_block, lattice_.makeAtom(t.front()));
+    }
+  }
+  
+  if (has_reachable_exceptions_) {
+    // these start out simply as state
+    FrameIndexRange fir(stackp_, scopep_, stack_base_);
+    for (; !fir.empty(); fir.popFront()) {
+      setType(&catch_block->params[setlocal_pos_ + fir.front()], STATE);
+    }
+  }
+
+  linkExceptionEdge(ir_->begin, catch_block);
+  return catch_block;
+}
+
+void AbcBuilder::linkExceptionEdge(BlockStartInstr* block, CatchBlockInstr* catch_block) {
+  BlockEndInstr* end = InstrGraph::blockEnd(block);
+  ExceptionEdge* edge = new (alloc_) ExceptionEdge(block, catch_block);
+  if (enable_verbose)
+    console_ << "creating exception edge i" << block->id << " -> i" << catch_block->id << "\n";
+  // Link the edge
+  ExceptionEdge* N = catch_block->catch_preds;
+  if (!N) {
+    catch_block->catch_preds = edge;
+    edge->next_exception = edge->prev_exception = edge;
+  } else {
+    ExceptionEdge* P = N->prev_exception;
+    edge->next_exception = N;
+    edge->prev_exception = P;
+    N->prev_exception = edge;
+    P->next_exception = edge;
+  }
+  if (end->catch_blocks == NULL) {
+    end->catch_blocks = new (alloc_) SeqBuilder<ExceptionEdge*>(alloc_);
+  }
+  end->catch_blocks->add(edge);
+}
+
+/// helper - check/initialize an AbcBlock's LabelInstr
+///
 LabelInstr* AbcBuilder::ensureBlockLabel(AbcBlock* abc_block) {
   BlockStartInstr* block_start = abc_block->label;
   if (!block_start) {
     // set up the label with param capacity for the current frame
     LabelInstr* label = factory_.newLabelInstr(num_vars_);
     abc_block->label = label;
     abc_block->start_sp = stackp_;
     abc_block->start_scopep = scopep_;
     ir_->addBlock(label);
     return label;
   }
   // sanity check abcbuilder state vs. label state
   assert(stackp_ == abc_block->start_sp);
   assert(scopep_ == abc_block->start_scopep);
+  assert(withbase_ == abc_block->start_withbase || 
+         withbase_ == abc_block->start_withbase + scope_base_);
   return cast<LabelInstr>(block_start);
 }
 
 /** Insert a timeout check.  If cktimeout returns true then we branch
  * to a throw.  In reality, cktimeout itself will throw, but this way
  * infinite loops have an exit path, which our IR requires
  */
 void AbcBuilder::cktimeout() {
@@ -1961,42 +2184,63 @@ Def* AbcBuilder::hasnext2Stmt(Def** obj,
 
 /// Start processing this abc_block.
 ///
 void AbcBuilder::startBlock(AbcBlock* abc_block) {
   // set abcbuilder state to this block
   abc_block_ = abc_block;
   stackp_ = abc_block->start_sp;
   scopep_ = abc_block->start_scopep;
-
+  withbase_ = abc_block->start_withbase;
+  assert(withbase_ == -1 || withbase_ > 0);
+  if (withbase_ != -1) {
+    withbase_ += scope_base_;
+  }
   BlockStartInstr* label = abc_block->label;
   assert(label);
 
   if (abc_block->dfs_loop) {
     // Set label params to pessimistic types from CodegenDriver
     // since we have not seen all predecessors yet.
     FrameRange<Def> p = frameRange(label->params);
     FrameRange<const Type*> t = abc_block->startTypesRange(stack_base_);
     for (; !t.empty(); t.popFront(), p.popFront())
       setType(&p.front(), t.front());
     setType(&label->params[effect_pos_], EFFECT);
     setType(&label->params[state_pos_], STATE);
+
+    if (has_reachable_exceptions_) {
+      // these start out simply as state
+      FrameIndexRange fir(stackp_, scopep_, stack_base_);
+      for (; !fir.empty(); fir.popFront()) {
+        setType(&label->params[setlocal_pos_ + fir.front()], STATE);
+      }
+    }
+
   } else {
     // Compute valid types for label params.
     builder_.computeType(label);
   }
-
+  
   set_effect(&label->params[effect_pos_]);
   set_state(&label->params[state_pos_]);
 
   FrameRange<Def> from = frameRange(label->params);
   FrameRange<Def*> to = frameRange(frame_);
   for (; !from.empty(); from.popFront(), to.popFront())
     to.front() = &from.front();
 
+  if (has_reachable_exceptions_) {
+    // bring down the state of the locals from the label too
+    FrameIndexRange fir(stackp_, scopep_, stack_base_);
+    for (; !fir.empty(); fir.popFront()) {
+      frame_[setlocal_pos_ + fir.front()] = &label->params[setlocal_pos_ + fir.front()];
+    }
+  }
+
   builder_.setPos(label);
   if (enable_verbose)
     printStartState(abc_block);
 
   assert(checkFrame(frame_, stackp_, scopep_));
 
   // Ignore the global configuration flag for timeouts because it can lead to
   // IR graphs with no ends, if the graph has an infinite loop.  We require
@@ -2008,11 +2252,49 @@ void AbcBuilder::startBlock(AbcBlock* ab
 void AbcBuilder::printStartState(AbcBlock* abc_block) {
   console_ << (abc_block->dfs_loop ? "LOOP:" : "BLOCK:") << " ["
       << int(abc_block->start - code_pos_) << "-"
       << int(abc_block->end - code_pos_) << "] effect=";
   if (abc_block->label)
     printInstr(console_, abc_block->label);
 }
 
+void AbcBuilder::printFrameState() {
+  // locals
+  console_ << "[ ";
+  for (int i = 0; i < scope_base_; i++) {
+    printDef(console_, frame_[i]);    
+    console_ << ":" << typeName(frame_[i]);
+    if (i+1 < scope_base_)
+      console_ << ' ';
+  }
+  console_ << " ] {";
+  
+  // scope chain
+  for (int i = scope_base_; i < scopep_ + 1; i++) {
+    printDef(console_, frame_[i]);
+    console_ << ":" << typeName(frame_[i]);
+    if (i+1 < stack_base_)
+      console_ << ' ';
+  }
+  console_ << " } ( ";
+  
+  // stack
+  for (int i = stack_base_; i < stackp_ + 1; i++) {
+    printDef(console_, frame_[i]);
+    console_ << ":" << typeName(frame_[i]);
+    if (i+1 < framesize_)
+      console_ << ' ';
+  }
+  console_ << " ) effect ";
+
+  printDef(console_, effect());
+  console_ << ":" << typeName(effect());
+
+  console_ << " withbase_ " << withbase_;
+
+  console_ << "\n";
+  
+}
+
 } // namespace avmplus
 
 #endif // VMCFG_HALFMOON
--- a/halfmoon/hm-abcbuilder.h
+++ b/halfmoon/hm-abcbuilder.h
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef HALFMOON_ABCBUILDER_H_
 #define HALFMOON_ABCBUILDER_H_
 
 namespace halfmoon {
@@ -22,17 +22,17 @@ class AbcBuilder {
     kNameKnown, // name with no args, e.g. o.name
     kNameIndex, // name with index on stack, e.g. o[index]
     kNameNs, // name with namespace on stack, e.g. o.ns::name
     kNameNsIndex
   // name with namespace and index on stack o.ns::[index]
   };
 
 public:
-  AbcBuilder(MethodInfo*, AbcGraph* abc, InstrFactory*, Toplevel*, ProfiledInformation* profiled_info);
+  AbcBuilder(MethodInfo*, AbcGraph* abc, InstrFactory*, Toplevel*, ProfiledInformation* profiled_info, bool has_reachable_exceptions);
   ~AbcBuilder();
   InstrGraph* visitBlocks(Seq<AbcBlock*>*);
 
 private:
   class InitVisitor: public avmplus::InitVisitor {
   public:
     InitVisitor(AbcBuilder* abc, Def* object) : abc(abc), object(object) {}
     virtual ~InitVisitor() {}
@@ -71,16 +71,19 @@ private:
   void pushDef(Def*);
   Def* finishStmt(Instr* instr, Def* effect, Def* value);
   Def* naryStmt1(InstrKind, Def* name, Def** args, int argc);
   Def* naryStmt2(InstrKind, Def* name, Def* obj, Def** args, int extra_argc);
   Def* naryStmt3(InstrKind, Def* name, Def* obj, Def* index, Def** args,
                  int extra_argc);
   Def* naryStmt4(InstrKind, Def* name, Def* obj, Def* ns, Def* index,
                  Def** args, int extra_argc);
+  // Build an NaryStmt4 with an extra fixed arg
+  Def* naryStmt4(InstrKind, Def* name, Def* obj, Def* ns, Def* index, Def* index2,
+                 Def** args, int extra_argc);
   Def* callStmt2(InstrKind, Def* name, Def* obj, Def** args, int extra_argc);
   Def* callStmt3(InstrKind, Def* name, Def* index, Def* obj, Def** args,
                  int extra_argc);
   Def* callStmt4(InstrKind, Def* name, Def* ns, Def* index, Def* obj,
                  Def** args, int extra_argc);
   Def* callStmt(const InstrKind*, uint32_t name_index, int argc);
   Def* initStmt(uint32_t name_index);
   Def* newclassStmt(int scope_count, Def* base, Def* scopes[],
@@ -92,36 +95,40 @@ private:
   Def* binaryStmt(InstrKind, Def* arg0, Def* arg1);
   Def* binaryExpr(InstrKind, Def* arg0, Def* arg1);
   Def* unaryStmt(InstrKind, Def* val_in);
   Def* unaryExpr(InstrKind, Def* value);
   void constructsuperStmt(int argc);
   Def* coerceArgs(Def* obj, Def** args, int argc, MethodInfo* m);
   void stopStmt(InstrKind, Def*, LabelInstr**);
   Def* nullCheck(Def* ptr);
+  void debugInstr(InstrKind, Def* value);
 
   void ifStmt(bool sense, Def* cond);
   void addIf(bool sense, Def* cond);
   void setFrameArgs(BlockEndInstr* end);
   void addArm(int i, ArmInstr* arm, bool switch_arm = false);
   void switchStmt(uint32_t num_cases, Def* index);
   void addSwitch(uint32_t num_cases, Def* index);
   void addGoto(AbcBlock* to);
   LabelInstr* ensureBlockLabel(AbcBlock* abc_block);
+  CatchBlockInstr* ensureCatchBlockLabel(AbcBlock* abc_block);
+  void linkExceptionEdge(BlockStartInstr* block, CatchBlockInstr* catch_block);
 
   void cktimeout();
   void jumpStmt();
   Def* newcatchStmt(uint32_t imm30);
   void setslotStmt(uint32_t slot, Def* obj, Def* val);
   Def* getslotStmt(Def* obj, uint32_t slot);
   Def* getouterscopeStmt(uint32_t scope_index);
   Def* getglobalscope();
   Def* hasnext2Stmt(Def** obj, Def** ctr);
   void startBlock(AbcBlock*);
   void printStartState(AbcBlock*);
+  void printFrameState();
   Def** popArgs(int argc);
   Def* peekDef();
   Def* popDef();
   Def* coerceExpr(Traits*, Def*);
 
   Def* toNumber(Def* val);
   Def* toInt(Def* val);
 
@@ -130,16 +137,19 @@ private:
   InstrKind getSpeculativeKind(RecordedType recorded_type);
   Def* toSpeculativeType(Def* val, RecordedType abc_type);
   bool isType(Def* val, RecordedType abc_type);
 
   void createSetLocalInstr(int i, Def* val);
   void setLocal(int i, Def* val);
   bool needAbcState();
 
+  bool handlerCoversPc(const uint8_t* pc);
+  bool pcIsHandler(const uint8_t* pc);
+
   // Profiler methods
   bool shouldSpeculate();
   Def* getTypedDef(const uint8_t* pc, Def* current_def, int input_index, int input_count, int output_count);
   RecordedType getRecordedType(const uint8_t* pc, int input_index, int input_count, int output_count);
   Def* typeSpecializedBinary(AbcOpcode abcop, const uint8_t* pc, Def* lhs, Def* rhs);
 
 private:
   Def* createConst(const Type* type) {
@@ -195,23 +205,27 @@ private:
   PrintWriter& console_;
   Lattice& lattice_;
   const int scope_base_;
   const int stack_base_;
   const int framesize_;
   const int num_vars_; // framesize + effect + state
   const int effect_pos_; // position of effect within vars[] array
   const int state_pos_; // position of state within vars[] array
+  const int setlocal_pos_;
 
   int scopep_;
   int stackp_;
+  int withbase_;
   Def** const frame_; // most recent data results
   AbcBlock* abc_block_; // current block
 
   const uint8_t * const code_pos_;
+  const uint8_t * pc_;
+  bool has_reachable_exceptions_;
   LabelInstr* return_label_;
   LabelInstr* throw_label_;
   InstrFactory& factory_;
   InstrKind kind_map_[256]; // AbcOpcode -> InstrKind, indexed by AbcOpcode.
   InstrGraph* const ir_;
   InstrGraphBuilder builder_;
   Def* never_def_;
   ProfiledInformation* profiled_info_;
--- a/halfmoon/hm-abcgraph.cpp
+++ b/halfmoon/hm-abcgraph.cpp
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //
 // hm-abcgraph.cpp: implementation code for ABC Control Flow Graphs
 //
 
@@ -25,17 +25,17 @@ AbcGraph::AbcGraph(MethodInfo* method)
 
 /// Create and add a label starting at pc to blockmap_, 
 /// if not already present.
 ///
 AbcBlock* AbcGraph::newAbcBlock(const uint8_t* pc) {
   AbcBlock* b = blockmap_.get(pc);
   if (!b) {
     if (enable_verbose)
-      printf("createBlock %d\n", int(pc - code_pos_));
+      method->pool()->core->console << "createBlock " << int(pc - code_pos_) << "\n";
     blockmap_.put(pc, b = new (alloc0_) AbcBlock(pc));
   }
   return b;
 }
 
 /// Record an edge from one label to another, by adding
 /// 'to' label to 'from' label's succ list. Create 'to'
 /// label from target pc if necessary, and add it to
@@ -44,17 +44,17 @@ AbcBlock* AbcGraph::newAbcBlock(const ui
 void AbcGraph::addAbcEdge(AbcBlock* fm, const uint8_t* target_pc, int edge_index) {
   AbcBlock* to = newAbcBlock(target_pc);
   ++to->num_preds;
   if (target_pc <= fm->start)
     to->abc_loop = true;
   fm->succ_blocks[edge_index] = to;
 
   if (enable_verbose)
-    printf("edge %d->%d\n", int(fm->start - code_pos_), int(target_pc  - code_pos_));
+    method->pool()->core->console << "edge " << int(fm->start - code_pos_) << "->" << int(target_pc  - code_pos_) << "\n";
 }
 
 void AbcGraph::createTryCatchBlocks() {
   handler_count_ = table_->exception_count;
   if (!handler_count_)
     return;
   handlers_ = new (alloc_) AbcBlock*[handler_count_];
   for (int i = 0, n = handler_count_; i < n; ++i) {
--- a/halfmoon/hm-abcgraph.h
+++ b/halfmoon/hm-abcgraph.h
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef HALFMOON_ABCGRAPH_H_
 #define HALFMOON_ABCGRAPH_H_
 
 namespace halfmoon {
@@ -15,17 +15,17 @@ using nanojit::BitSet;
 /**
  * An AbcBlock marks the boundaries of a basic label in ABC.  frame_ is a
  * state vector to track the current value (Def) in each local variable,
  * scope chain slot, and operand stack slot, while building the InstrGraph.
  */
 class AbcBlock {
 public:
   AbcBlock(const uint8_t* pc)
-  : start(pc) {
+  : start(pc), start_withbase(-1) {
     // Everything else is zero'd in newAbcBlock()
   }
 
   template<class E> FrameRange<E> startRange(E* frame, int stack_base) {
     return FrameRange < E > (frame, start_sp, start_scopep, stack_base);
   }
   FrameRange<const Type*> startTypesRange(int stack_base) {
     return startRange(start_types, stack_base);
@@ -39,16 +39,17 @@ public:
   int max_catch_blocks; // size of catch_blocks[] array.
   int post_id;
   int num_preds; // Number of incoming normal and exception edges.
   bool dfs_loop; // True if target of DFS back-edge.
   bool abc_loop; // True if target of ABC back-edge.
   const Type** start_types; // Verifier-derived types at label start.
   BlockStartInstr* label; // BlockStartInstr for this AbcBlock
   int start_sp, start_scopep; // stackp_ and scope at label start
+  int start_withbase;
 };
 
 /**
  * An AbcInstr is a saved copy of arguments passed by the Verifier
  * to JitWriter, for the few cases we can't or don't want to reconstruct
  * from the raw ABC.  Its a cheap way to reuse verifier specializations
  * without re-implementing them in halfmoon.
  */
--- a/halfmoon/hm-algorithms.h
+++ b/halfmoon/hm-algorithms.h
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 namespace halfmoon {
 
 /// Simple max function.
 ///
--- a/halfmoon/hm-bailout-stubs.h
+++ b/halfmoon/hm-bailout-stubs.h
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /***
  * The equivalent of jit-calls.h in CodegenLIR but specific
  * methods ONLY for halfmoon
  */
--- a/halfmoon/hm-bailouts.cpp
+++ b/halfmoon/hm-bailouts.cpp
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "hm-main.h"
 #ifdef VMCFG_HALFMOON
 
 namespace halfmoon {
--- a/halfmoon/hm-bailouts.h
+++ b/halfmoon/hm-bailouts.h
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef HM_BAILOUTS_H
 #define HM_BAILOUTS_H
 
 namespace halfmoon {
--- a/halfmoon/hm-check.cpp
+++ b/halfmoon/hm-check.cpp
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "hm-main.h"
 #ifdef VMCFG_HALFMOON
 
 namespace halfmoon {
@@ -215,18 +215,22 @@ bool TypeChecker::do_default(Instr* inst
   if (hasInputSignature(instr)) {
     // Check types from signature.
     SigRange sig = inputSigRange(instr);
     ArrayRange<Use> u = useRange(instr);
     for (int i = 0; !u.empty(); u.popFront(), sig.popFront(), ++i) {
       const Type* t = type(u.front());
       const Type* constraint = sig.front();
       if (!subtypeof(t, constraint)) {
+        avmplus::AvmCore* core = avmplus::AvmCore::getActiveCore();
+        printCompactInstr(core->console, instr, false);
         report(i, u.front(), constraint, "subtype of");
       } else if (check_model && !submodelof(t, constraint)) {
+        avmplus::AvmCore* core = avmplus::AvmCore::getActiveCore();
+        printCompactInstr(core->console, instr, false);
         report(i, u.front(), constraint, "submodel of");
       }
     }
   } else {
     assert(numUses(instr) == 0 && "missing input signature");
   }
 
   return true;
@@ -275,16 +279,42 @@ bool checkPruned(InstrGraph* ir) {
     marked.set(i.front()->id);
   for (AllInstrRange i(ir); !i.empty(); i.popFront()) {
     Instr* instr = i.front();
     for (AllUsesRange u(instr); !u.empty(); u.popFront())
       assert(marked.get(user(u.front())->id) && "user not linked");
     if (kind(instr) == HR_label)
       for (PredRange p(cast<LabelInstr>(instr)); !p.empty(); p.popFront())
         assert(marked.get(p.front()->id) && "goto not linked");
+    if (kind(instr) == HR_catchblock) {
+      CatchBlockInstr* cblock = cast<CatchBlockInstr>(instr);
+      for (ExceptionEdgeRange p(cblock); !p.empty(); p.popFront()) {
+        assert(p.front()->from->catch_blocks != NULL);
+        bool found = false;
+        for (CatchBlockRange r(p.front()->from); !r.empty(); r.popFront()) {
+          if (r.front() == cblock) found = true;
+        }
+        assert(found);
+      }
+    }
+    if (isBlockEnd(instr)) {
+      BlockEndInstr* end = (BlockEndInstr*)instr;
+      if (end->catch_blocks != NULL) {
+        for (CatchBlockRange r(end); !r.empty(); r.popFront()) {
+          bool found = false;
+          for (ExceptionEdgeRange p(r.front()); !p.empty(); p.popFront()) {
+            if (p.front()->from == end) found = true;
+          }
+          if (!found) {
+            printf("missing exception back edge: i%d -> i%d", end->id, r.front()->id);
+          }
+          assert(found);
+        }
+      }
+    }
   }
   if (ir->end)
     assert(marked.get(ir->end->id) && "end not linked");
   if (ir->exit)
     assert(marked.get(ir->exit->id) && "exit not linked");
   return true;
 }
 
@@ -698,54 +728,78 @@ public:
     checkRetired(def_ins, use_ins);
   }
 
   void def(LIns* def_ins) {
     setStatus(def_ins, kRetired);
   }
 
   // Print the LIR Control Flow Graph.
-  void print() {
+  void print(AvmLogControl* logc) {
     if (!printer)
       return;
     for (LirBlock* b = first_block; b; b = b->next) {
       RefBuf rb1, rb2;
-      printf("B%d [%s - %s]", b->linear_id,
+      logc->printf("B%d [%s - %s]", b->linear_id,
              printer->formatRef(&rb1, b->first_ins, false),
              printer->formatRef(&rb2, b->last_ins, false));
       if (b->idom)
-        printf(" idom=B%d", b->idom->linear_id);
+        logc->printf(" idom=B%d", b->idom->linear_id);
       else if (!isReachable(b))
-        printf(" unreachable");
-      printf("\n");
+        logc->printf(" unreachable");
+
+      logc->printf(" preds [ ");
+      for (SeqRange<LirBlock*> i(b->preds); !i.empty(); i.popFront()) {
+        LirBlock* pred = i.front();
+        logc->printf(" B%d ", pred->linear_id);
+      }
+      logc->printf("]");
+      logc->printf(" succs [ ");
+      for (int s = 0; s < b->num_succs; s++) {
+        LirBlock* succ = b->succs[s];
+        logc->printf(" B%d ", succ->linear_id);
+      }
+      logc->printf("]");
+      logc->printf("\n");
+
+      LIns* last = b->last_ins;
+      LirReader r2(last);
+      ReverseLister rl(&r2, alloc, printer, logc, "Initial LIR");
+
+      // Scan each instruction and populate insmap.
+      LIns* ins;
+      do {
+        ins = rl.read();
+      } while (ins != b->first_ins);
+      rl.finish();
+
     }
   }
 
 public:
   Allocator alloc;
   LInsPrinter* printer;
   LIns* last_ins;
   int num_blocks;
   LirBlock* first_block;  // First block in linear order.
   LirBlock* last_block;   // Last block in linear order.
   insmap_t* insmap;       // Map of LIns -> LirBlock.
   bool error;
 };
 
 // fixme dont use halfmoon-specific enable_verbose
 // todo Test checkLir() on sanities, brightspot, past bugs.
-// todo use Nanojit log instead of printf.
 // todo move checkLir() to nanojit
-bool checkLir(Fragment* fragment) {
+bool checkLir(Fragment* fragment, AvmLogControl* logc) {
   Allocator scratch;
   LirCfg cfg(fragment);
   computeDoms(cfg.first_block);
 
   if (enable_verbose)
-    cfg.print();
+    cfg.print(logc);
 
   // Go through LIR instructions in reverse, checking each one.
   for (LirBlock* b = cfg.last_block; b; b = b->prev) {
     LirReader r(b->last_ins);
     LIns *ins;
     do {
       ins = r.read();
       switch (repKinds[ins->opcode()]) {
--- a/halfmoon/hm-check.h
+++ b/halfmoon/hm-check.h
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 namespace halfmoon {
 
 /// Run checkTypes(Instr) on every instruction reachable by PostorderDefIter.
 ///
--- a/halfmoon/hm-cleaner.cpp
+++ b/halfmoon/hm-cleaner.cpp
@@ -1,10 +1,10 @@
 /* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "hm-main.h"
 #ifdef VMCFG_HALFMOON
 
 namespace halfmoon {
--- a/halfmoon/hm-cleaner.h
+++ b/halfmoon/hm-cleaner.h
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef HM_CLEANER_H_
 #define HM_CLEANER_H_
 
 namespace halfmoon {
--- a/halfmoon/hm-constraints.cpp
+++ b/halfmoon/hm-constraints.cpp
@@ -1,10 +1,10 @@
 /* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "hm-main.h"
 #ifdef VMCFG_HALFMOON
 
 namespace halfmoon {
--- a/halfmoon/hm-constraints.h
+++ b/halfmoon/hm-constraints.h
@@ -1,10 +1,10 @@
 /* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 namespace halfmoon {
 
 /// Range
 ///
--- a/halfmoon/hm-dead.cpp
+++ b/halfmoon/hm-dead.cpp
@@ -1,10 +1,10 @@
 /* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "hm-main.h"
 #ifdef VMCFG_HALFMOON
 
 namespace halfmoon {
@@ -23,16 +23,40 @@ void removeGoto(GotoInstr* go) {
     GotoInstr* P = go->prev_goto;
     if (target->preds == go)
       target->preds = N;
     N->prev_goto = P;
     P->next_goto = N;
   }
   go->next_goto = go->prev_goto = 0;
   go->target = 0;
+
+  if (go->catch_blocks != NULL) {
+    for (SeqRange<ExceptionEdge*> r(*go->catch_blocks); !r.empty(); r.popFront()) {
+      CatchBlockInstr* cblock = r.front()->to;
+
+      for (ExceptionEdgeRange p(cblock); !p.empty(); p.popFront()) {
+        ExceptionEdge* edge = p.front();
+        if (edge->from == go) {
+          // dead edge
+          if (edge->next_exception == edge) {
+            cblock->catch_preds = NULL;
+          } else {
+            ExceptionEdge* N = edge->next_exception;
+            ExceptionEdge* P = edge->prev_exception;
+            if (cblock->catch_preds == edge) {
+              cblock->catch_preds = N;
+            }
+            N->prev_exception = P;
+            P->next_exception = N;
+          }
+        }
+      }
+    }
+  }
 }
 
 /**
  * Dead code elimination.  This class implements the mark phase of an optimistic
  * two-phase algorithm.  The mark phase marks defs and instructions which are
  * directly observable or contribute to observable computations.
  *
  * A branch is marked if some marked instruction is control-dependent on it,
--- a/halfmoon/hm-dead.h
+++ b/halfmoon/hm-dead.h
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef HM_DEAD_H_
 #define HM_DEAD_H_
 
 namespace halfmoon {
--- a/halfmoon/hm-debug-inlines.h
+++ b/halfmoon/hm-debug-inlines.h
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // Debug assistance code.
 
 #ifndef HM_DEBUG_INLINES_H_
 #define HM_DEBUG_INLINES_H_
--- a/halfmoon/hm-debug.cpp
+++ b/halfmoon/hm-debug.cpp
@@ -1,10 +1,10 @@
 /* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "hm-main.h"
 #ifdef VMCFG_HALFMOON
 
 namespace halfmoon {
--- a/halfmoon/hm-debug.h
+++ b/halfmoon/hm-debug.h
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // Debug assistance code.
 
 #ifndef HM_DEBUG_H_
 #define HM_DEBUG_H_
--- a/halfmoon/hm-dispatch.h
+++ b/halfmoon/hm-dispatch.h
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef HALFMOON_DISPATCH_H
 #define HALFMOON_DISPATCH_H
 
 namespace halfmoon {
--- a/halfmoon/hm-dominatortree.cpp
+++ b/halfmoon/hm-dominatortree.cpp
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "hm-main.h"
 #ifdef VMCFG_HALFMOON
 
 namespace halfmoon {
@@ -115,16 +115,20 @@ int CfgInfo::intersect(int idom, int pre
 
 class SuccEdges {
 public:
   class EdgeRange: public ArrayRange<ArmInstr*> {
   public:
     explicit EdgeRange(BlockStartInstr* b)
     : ArrayRange<ArmInstr*>(armRange((CondInstr*)end(b))) {}
   };
+  class CatchEdgeRange: public CatchBlockRange {
+   public:
+    explicit CatchEdgeRange(BlockStartInstr* block): CatchBlockRange(block) {}
+  };
   class RootRange {
   public:
     explicit RootRange(InstrGraph* ir) : begin(ir->begin) {}
     bool empty() const { return begin == 0; }
     BlockStartInstr* front() const { assert(!empty()); return begin; }
     BlockStartInstr* popFront() {
       BlockStartInstr* b = begin;
       begin = 0;
@@ -138,16 +142,19 @@ public:
   }
   static BlockStartInstr* next(BlockStartInstr* b) {
     return cast<GotoInstr>(end(b))->target;
   }
   static bool manyEdges(BlockStartInstr* b) {
     InstrKind k = kind(end(b));
     return k == HR_if || k == HR_switch;
   }
+  static bool hasCatchEdges(BlockStartInstr* b) {
+    return InstrGraph::blockEnd(b)->catch_blocks != NULL;
+  }
 private:
   static BlockEndInstr* end(BlockStartInstr* b) {
     return InstrGraph::blockEnd(b);
   }
 };
 
 class PredEdges {
 public:
@@ -155,16 +162,26 @@ public:
   public:
     explicit EdgeRange(BlockStartInstr* b) : r(cast<LabelInstr>(b)) {}
     bool empty() const { return r.empty(); }
     BlockStartInstr* front() const { return start(r.front()); }
     BlockStartInstr* popFront() { return start(r.popFront()); }
   private:
     PredRange r;
   };
+  class CatchEdgeRange: public ExceptionEdgeRange {
+   public:
+    explicit CatchEdgeRange(BlockStartInstr* block): ExceptionEdgeRange(cast<CatchBlockInstr>(block)) {}
+    BlockStartInstr* front() {
+      return InstrGraph::blockStart(ExceptionEdgeRange::front()->from);
+    }
+    BlockStartInstr* popFront() {
+      return InstrGraph::blockStart(ExceptionEdgeRange::popFront()->from);
+    }
+  };
   class RootRange {
   public:
     explicit RootRange(InstrGraph* ir) : ir(ir) {
       assert((ir->exit || ir->end) && ir->exit != ir->end);
       e = ir->exit ? ir->exit : ir->end;
     }
     bool empty() const { return e == 0; }
     BlockStartInstr* front() const { assert(!empty()); return start(e); }
@@ -181,16 +198,19 @@ public:
     return kind(b) == HR_arm;
   }
   static BlockStartInstr* next(BlockStartInstr* b) {
     return start(cast<ArmInstr>(b)->owner);
   }
   static bool manyEdges(BlockStartInstr* b) {
     return kind(b) == HR_label;
   }
+  static bool hasCatchEdges(BlockStartInstr* b) {
+    return kind(b) == HR_catchblock;
+  }
 private:
   static BlockStartInstr* start(BlockEndInstr* e) {
     return InstrGraph::blockStart(e);
   }
 };
 
 template<class VIEW>
 void CfgInfo::genericDfs(InstrGraph* ir) {
@@ -210,16 +230,20 @@ void CfgInfo::genericDfs(BlockStartInstr
     return;
   post_ids[blockid] = -1; // mark visited
   if (VIEW::oneEdge(block)) {
     genericDfs<VIEW>(VIEW::next(block));
   } else if (VIEW::manyEdges(block)) {
     for (typename VIEW::EdgeRange s(block); !s.empty();)
       genericDfs<VIEW>(s.popFront());
   }
+  if (VIEW::hasCatchEdges(block)) {
+    for (typename VIEW::CatchEdgeRange s(block); !s.empty();)
+      genericDfs<VIEW>(s.popFront());
+  }
   int post_id = ++max_post_id;
   post_ids[blockid] = post_id;
   blocks[post_id].start = block;
 }
 
 template<class VIEW>
 void CfgInfo::computeGenericDoms() {
   blocks[max_post_id].idom = max_post_id;
@@ -235,19 +259,26 @@ void CfgInfo::computeGenericDoms() {
       if (VIEW::oneEdge(block)) {
         idom = post_ids[VIEW::next(block)->blockid];
       } else if (VIEW::manyEdges(block)) {
         for (typename VIEW::EdgeRange e(block); !e.empty();) {
           int pred_post_id = post_ids[e.popFront()->blockid];
           if (pred_post_id)
             idom = intersect(idom, pred_post_id);
         }
-      } else {
+      } else if (!VIEW::hasCatchEdges(block)) {
         idom = max_post_id; // idom is root node
       }
+      if (VIEW::hasCatchEdges(block)) {
+        for (typename VIEW::CatchEdgeRange e(block); !e.empty();) {
+          int pred_post_id = post_ids[e.popFront()->blockid];
+          if (pred_post_id)
+            idom = intersect(idom, pred_post_id);
+        }
+      }
       if (blocks[i].idom != idom) {
         // save the newly computed dominator
         blocks[i].idom = idom;
         changed = true;
       }
     }
   } while (changed);
 }
@@ -273,31 +304,47 @@ void DominatorTree::computeGeneric(Instr
   SparseSet visited(scratch, cfg.max_post_id + 1);
   for (int i = cfg.max_post_id - 1; i > 0; --i) {
     BlockStartInstr* block = cfg.blocks[i].start;
     BlockInfo& info = info_[block->blockid];
     int idom = cfg.blocks[i].idom;
     BlockStartInstr* idom_instr = cfg.blocks[idom].start;
     info.idom = idom_instr;
     info.depth = idom_instr ? info_[idom_instr->blockid].depth + 1 : 1;
-    if (REV::manyEdges(block)) {
+    if (REV::manyEdges(block) || REV::hasCatchEdges(block)) {
       visited.clear();
+    }
+    if (REV::manyEdges(block)) {
       for (typename REV::EdgeRange r(block); !r.empty();) {
         BlockStartInstr* next = r.popFront();
         int next_post_id = cfg.post_ids[next->blockid];
         if (!next_post_id)
           continue; // block was not reachable
         for (int n = next_post_id; n != idom; n = cfg.blocks[n].idom) {
           if (visited.add(n)) {
             Seq<BlockStartInstr*>* &df = info_[cfg.blocks[n].start->blockid].df;
             df = cons(dom_alloc_, block, df);
           }
         }
       }
     }
+    if (REV::hasCatchEdges(block)) {
+      for (typename REV::CatchEdgeRange r(block); !r.empty();) {
+        BlockStartInstr* next = r.popFront();
+        int next_post_id = cfg.post_ids[next->blockid];
+        if (!next_post_id)
+          continue; // block was not reachable
+        for (int n = next_post_id; n != idom; n = cfg.blocks[n].idom) {
+          if (visited.add(n)) {
+            Seq<BlockStartInstr*>* &df = info_[cfg.blocks[n].start->blockid].df;
+            df = cons(dom_alloc_, block, df);
+          }
+        }
+      }
+    }
   }
 }
 
 DominatorTree* forwardDoms(Allocator& alloc, InstrGraph* ir) {
   DominatorTree* d = new (alloc) DominatorTree(alloc, ir);
   d->computeGeneric<SuccEdges, PredEdges>(ir);
   return d;
 }
--- a/halfmoon/hm-dominatortree.h
+++ b/halfmoon/hm-dominatortree.h
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef DOMINATORTREE_H_
 #define DOMINATORTREE_H_
 
 namespace halfmoon {
--- a/halfmoon/hm-exec.cpp
+++ b/halfmoon/hm-exec.cpp
@@ -1,10 +1,10 @@
 /* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //
 // This file contains BaseExecMgr changes that belong in core/exec-jit.cpp
 // or core/exec.cpp, but they are here to encapsulate them until halfmoon
 // is a shipping feature.
@@ -13,25 +13,36 @@
 #ifdef VMCFG_HALFMOON
 #include "profiler/profiler-main.h"
 
 namespace avmplus {
 using halfmoon::JitManager;
 
 bool BaseExecMgr::verifyOptimizeJit(MethodInfo* m, MethodSignaturep ms,
                            Toplevel* toplevel, AbcEnv* abc_env, OSR* /* osr_state */) {
-  if (!halfmoon::canCompile(m))
-    return false;
-  halfmoon::JitWriter jit(m, toplevel, abc_env);
-  verifyCommon(m, ms, toplevel, abc_env, &jit);
-  GprMethodProc code = jit.finish();
-  if (code) {
-    setJit(m, code);
-    return true;
+  if (halfmoon::canCompile(m)) {
+    halfmoon::JitWriter jit(m, toplevel, abc_env);
+    verifyCommon(m, ms, toplevel, abc_env, &jit);
+    GprMethodProc code = jit.finish();
+    if (code) {
+      setJit(m, code);
+      return true;
+    }
   }
+  if (config.jitordie && halfmoon::enable_mode != 0) {
+    Exception* e = new (core->GetGC())
+      Exception(core, core->newStringLatin1("JIT failed")->atom());
+    e->flags |= Exception::EXIT_EXCEPTION;
+#ifdef AVMPLUS_VERBOSE
+    if (m->pool()->isVerbose(VB_execpolicy))
+      core->console << "execpolicy die " << m << " method-jit-failed\n";
+#endif
+    core->throwException(e);
+  }
+
   return false;
 }
 
 void BaseExecMgr::resetMethodInvokers(MethodEnv* env) {
     MethodInfo* method_info = env->method;
     method_info->_implGPR = verifyEnterGPR;
     method_info->_invoker = verifyInvoke;
     env->_implGPR = method_info->_implGPR;
--- a/halfmoon/hm-identityanalyzer.cpp
+++ b/halfmoon/hm-identityanalyzer.cpp
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "hm-main.h"
 #ifdef VMCFG_HALFMOON
 
 namespace halfmoon {
@@ -23,27 +23,31 @@ Def* IdentityAnalyzer::identity(UnaryStm
 /// Return the identity def for the given instruction, which will either
 /// be effect_in, or the given value_in.
 ///
 Def* IdentityAnalyzer::identity(UnaryStmt* instr) {
   return def(def_ == instr->effect_out() ? instr->effect_in() :
              instr->value_in());
 }
 
+Def* IdentityAnalyzer::identity(NaryStmt3* instr, Def* value_in) {
+  return (def_ == instr->effect_out() ? def(instr->effect_in()) : value_in);
+}
+
 Def* IdentityAnalyzer::identity(Instr* instr) {
   assert(definer(def_) == instr && "Illegal def");
   return do_instr(this, instr);
 }
 
 /// Analyze lexical lookup instructions HR_findprop and HR_findpropstrict.
 ///
-Def* IdentityAnalyzer::doFindStmt(NaryStmt2* instr) {
+Def* IdentityAnalyzer::doFindStmt(NaryStmt3* instr) {
   int index;
   if (findScope(lattice_, instr, &index) == kScopeLocal)
-    return def(instr->vararg(index));
+    return identity(instr, def(instr->vararg(index)));
   return def_;
 }
 
 Def* IdentityAnalyzer::do_cast(BinaryStmt* instr) {
   const Type* traits_type = type(instr->lhs_in());
   if (!isConst(traits_type))
     return def_; // Don't know the target traits.
   const Type* to_type = lattice_->makeType(traitsVal(traits_type));
--- a/halfmoon/hm-identityanalyzer.h
+++ b/halfmoon/hm-identityanalyzer.h
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef IDENTITYANALYZER_H_
 #define IDENTITYANALYZER_H_
 
 namespace halfmoon {
@@ -34,35 +34,36 @@ public: // dispatch() adapter methods.
   Def* do_coerce(BinaryStmt*);
   Def* do_castobject(UnaryExpr* i) { return coerceIdentity(i, lattice_->object_type[kTypeNullable]); }
   Def* do_caststring(UnaryStmt* i) { return coerceIdentity(i, lattice_->string_type[kTypeNullable]); }
   Def* do_tonumber(UnaryStmt* i) { return coerceIdentity(i, lattice_->double_type); }
   Def* do_toint(UnaryStmt* i) { return coerceIdentity(i, lattice_->int_type); }
   Def* do_touint(UnaryStmt* i) { return coerceIdentity(i, lattice_->uint_type); }
   Def* do_toboolean(UnaryExpr* i) { return coerceIdentity(i, lattice_->boolean_type); }
   Def* do_doubletoint32(UnaryExpr* i) { return coerceIdentity(i, lattice_->int_type); }
-  Def* do_abc_findproperty(NaryStmt2* i) { return doFindStmt(i); }
-  Def* do_abc_findpropstrict(NaryStmt2* i) { return doFindStmt(i); }
+  Def* do_abc_findproperty(NaryStmt3* i) { return doFindStmt(i); }
+  Def* do_abc_findpropstrict(NaryStmt3* i) { return doFindStmt(i); }
   Def* do_atom2scriptobject(UnaryExpr* i) { return doModelChange(i, HR_scriptobject2atom); }
   Def* do_cast(BinaryStmt*);
   Def* do_cknull(UnaryStmt*);
   Def* do_cknullobject(UnaryStmt* i) { return do_cknull(i); }
   Def* do_u2i(UnaryExpr*);
   Def* do_speculate_number(BinaryExpr*);
 
 private:
   /// Generic analyzer for lexical 'find' instructions.
   ///
-  Def* doFindStmt(NaryStmt2*);
+  Def* doFindStmt(NaryStmt3*);
 
   /// Helper method: return the appropriate identity def based on which
   /// use value_in is, and what def_ is pointing to.
   ///
   Def* identity(BinaryStmt*, Def* value_in);
   Def* identity(UnaryStmt*, Def* value_in);
+  Def* identity(NaryStmt3*, Def* value_in);
 
   /// Helper method: return UnaryStmt.effect_in or value_in based on
   /// what def_ is pointing to.
   ///
   Def* identity(UnaryStmt*);
 
   /// Identity for coerce opcodes
   ///
--- a/halfmoon/hm-inline.cpp
+++ b/halfmoon/hm-inline.cpp
@@ -1,10 +1,10 @@
 /* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "hm-main.h"
 #ifdef VMCFG_HALFMOON
 
 namespace halfmoon {
--- a/halfmoon/hm-inline.h
+++ b/halfmoon/hm-inline.h
@@ -1,10 +1,10 @@
 /* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*
  * hm-inline.h
  *
  *  Created on: Apr 12, 2011
--- a/halfmoon/hm-instr.h
+++ b/halfmoon/hm-instr.h
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef HM_INSTR_H_
 #define HM_INSTR_H_
 
 namespace halfmoon {
@@ -75,36 +75,36 @@ public:
 /// A VarArgInstr has a variable (but nonvolatile)
 /// number of uses (args) above a fixed minimum
 /// number of uses, and a fixed number of defs.
 ///
 /// NOTE: uses are stored 'off the end', requiring
 /// custom allocation (see InstrFactory) and
 /// precluding structural extension by subclasses.
 ///
-template<int USEMIN, int DEFC>
+template<int USEMIN, int DEFC, class BASE>
 class VarArgInstr : public Instr {
   friend class InfoManager;
   friend class InstrFactory;
 
   static InstrInfo createInfo(InstrKind kind, int argc, const Type** insig,
                               const Type** outsig, InstrGraph* ir) {
     return InstrInfo(USEMIN + argc, DEFC,
-                     sizeof(VarArgInstr),
+                     sizeof(BASE),
                      offsetof(VarArgInstr, defs),
                      kind, insig, outsig, ir);
   }
 
 protected:
   VarArgInstr(const InstrInfo* info) :
       Instr(info) {
   }
 
   Use* uses() {
-    return (Use*)(this + 1);
+    return (Use*)((intptr_t)this + info->uses_off);
   }
 
   Def defs[DEFC];
 
 public:
   /// number of variable args
   int vararg_count() const {
     return this->info->num_uses - USEMIN;
@@ -122,27 +122,28 @@ public:
   }
 
   Use& use(int i) {
     assert (i >= 0 && i < info->num_uses);
     return uses()[i];
   }
 };
 
+
 /// VarArgStmt is a VarArgInstr with a fixed minimum number
 /// of arguments, whose first input and output are effects,
 /// and whose second output is a data result. VarArgStmt is
 /// parameterized on its fixed minimum number of value args.
 /// the "arg" range of a VarArgStmt maps exactly to VarArgInstr's
 /// "vararg" range.
 template<int ARGMIN>
-class NaryStmt : public VarArgInstr<ARGMIN + 1, 2> {
+class NaryStmt : public VarArgInstr<ARGMIN + 1, 2, class NaryStmt<ARGMIN> > {
 protected:
   NaryStmt(const InstrInfo* info) :
-      VarArgInstr<ARGMIN + 1, 2>(info) {
+    VarArgInstr<ARGMIN + 1, 2, class NaryStmt<ARGMIN> >(info) {
   }
 
 public:
   Def* effect_out() {
     return &this->defs[0];
   }
 
   Def* value_out() {
@@ -173,20 +174,20 @@ public:
 /// parameterized on its fixed minimum number of value args.
 ///
 /// NOTE: the term 'arg' in CallStmt's API means
 /// the combination of the last fixed arg plus variable args, designed
 /// to be congruent with VM calling conventions.  The only difference
 /// between CallStmt and NaryStmt is the definition of the 'arg' range.
 ///
 template<int ARGMIN>
-class CallStmt : public VarArgInstr<ARGMIN + 1, 2> {
+class CallStmt : public VarArgInstr<ARGMIN + 1, 2, class CallStmt<ARGMIN> > {
 protected:
   CallStmt(const InstrInfo* info) :
-      VarArgInstr<ARGMIN + 1, 2>(info) {
+    VarArgInstr<ARGMIN + 1, 2, class CallStmt<ARGMIN> >(info) {
   }
 
 public:
   Def* effect_out() {
     return &this->defs[0];
   }
 
   Def* value_out() {
@@ -268,17 +269,16 @@ class NaryStmt3 : public NaryStmt<3> {
 
   NaryStmt3(const InstrInfo* info) : NaryStmt<3>(info) {
   }
 
 public:
   // for findproperty opcodes
   Use& name_in() { return uses()[1]; }
   Use& env_in() { return uses()[2]; }
-  Use& ns_in() { return uses()[3]; }    // namespace variable
   Use& index_in() { return uses()[3]; } // index variable
 
 public:
   static const InstrShape shape = NARYSTMT3_SHAPE;
 };
 
 class NaryStmt4 : public NaryStmt<4> {
   friend class InstrFactory;
@@ -286,18 +286,16 @@ class NaryStmt4 : public NaryStmt<4> {
 
   NaryStmt4(const InstrInfo* info) : NaryStmt<4>(info) {
   }
 
 public:
   // for findproperty opcodes
   Use& name_in() { return uses()[1]; }
   Use& env_in() { return uses()[2]; }
-  Use& ns_in() { return uses()[3]; }    // namespace variable
-  Use& index_in() { return uses()[4]; } // index variable
 
 public:
   static const InstrShape shape = NARYSTMT4_SHAPE;
 };
 
 /// CallStmt2 is for instructions that take a name parameter,
 /// an object, and some arguments.
 ///
@@ -569,39 +567,47 @@ public:
 /// 2. Conditional Exception.  This would look just like a conditional branch;
 ///   it would have an explicit control edge to a catch block, and the catch
 ///   block would start with a block and phis for all incoming paths and
 ///   live values.  This supports full dataflow analysis and would be used
 ///   if we want to optimize the try code and catch code at the same time.
 ///
 /// Safepoint MUST output state, because it has an implicit setlocal
 /// It implicitly saves the current abc
-class SafepointInstr : public FixedArgStmt<1, 1> {
+class SafepointInstr : public VarArgInstr<1, 2, class SafepointInstr> {
   friend class InfoManager;
   friend class InstrFactory;
   friend class Copier;
 
   SafepointInstr(const InstrInfo* info) :
-    FixedArgStmt<1, 1>(info) {
+    VarArgInstr<1, 2, class SafepointInstr>(info) {
+    assert(info->uses_off == sizeof(SafepointInstr));
   }
 
+  static const int USEMIN = 1;  // effect in
+  static const int DEFC = 2;    // effect out, state out
+
 public:
   static const InstrShape shape = SAFEPOINTINSTR_SHAPE;
 
   int vpc; // Points into abc bytecode.  fixme: should be uint8_t*.
   int sp; // points to top of operand stack
   int scopep; // points to top of abc scope stack.
 
-  Use& state_in() {
-    return this->uses[1];
+  Def* effect_out() {
+    return &this->defs[0];
   }
 
   Def* state_out() {
     return &this->defs[1];
   }
+
+  Use& effect_in() {
+    return this->uses()[0];
+  }
 };
 
 /// A setlocal instruction updates one element of the abstract "local state"
 /// tuple (called kStateIn and kStateOut).
 /// input:  kStateIn  (v0, ..., vk,     ... vN),  newval, index (k)
 /// output: kStateOut (v0, ..., newval, ... vN)
 ///
 class SetlocalInstr : public FixedArgInstr<2, 1> {
@@ -739,16 +745,35 @@ public:
 
   DeoptSafepointInstr* safepoint;
 
   Use& value_in() {
     return this->uses[1];
   }
 };
 
+
+class DebugInstr : public FixedArgStmt<1, 0> {
+  friend class InfoManager;
+  friend class InstrFactory;
+  friend class Copier;
+
+  DebugInstr(const InstrInfo* info) :
+      FixedArgStmt<1, 0>(info) {
+  }
+
+public:
+  static const InstrShape shape = DEBUGINSTR_SHAPE;
+
+  Use& value_in() {
+    return this->uses[1];
+  }
+};
+
+
 // ------------------------------- IR5 start ---------------------------------
 
 /// BlockStartInstr is the common superclass of all IR5
 /// block start delimiters: StartInstr, LabelInstr,
 /// ArmInstr.
 ///
 /// All block start instructions carry a parameter
 /// list of Defs. Predecessor blocks end with block
@@ -799,31 +824,44 @@ public:
 /// start delimiters carrying congruent parameter
 /// lists.
 ///
 /// Design note: we use an allocated array for args,
 /// rather than inline off-the-end storage, so that
 /// we can factor out a nonvirtual superclass of
 /// block end delimiters.
 ///
+
+class ExceptionEdge {
+ public:
+  ExceptionEdge(BlockStartInstr* f, CatchBlockInstr* t): from(InstrGraph::blockEnd(f)), to(t), next_exception(NULL), prev_exception(NULL) {}
+
+  BlockEndInstr* from;
+  CatchBlockInstr* to;
+  ExceptionEdge *next_exception, *prev_exception;
+};
+
 class BlockEndInstr : public Instr {
 protected:
   static InstrInfo createInfo(InstrKind kind, const Type** insig,
                               const Type** outsig, InstrGraph* ir) {
     return InstrInfo(-1, 0, -1, 0, kind, insig, outsig, ir);
   }
 
   BlockEndInstr(const InstrInfo* info, Use* args) :
-      Instr(info), args(args) {
+    Instr(info), args(args), catch_blocks(NULL) {
   }
 
 public:
   Use* args; // arguments for successor
+
+  SeqBuilder<ExceptionEdge*>* catch_blocks;
 };
 
+
 /// A BlockFooterInstr ends a block that is a unique
 /// predecessor - i.e., none of its successors has any
 /// other predecessor. As such it owns its argument count,
 /// which determines the parameter counts of its successors.
 ///
 class BlockFooterInstr : public BlockEndInstr {
 protected:
   BlockFooterInstr(const InstrInfo* info, int argc, Use* args) :
@@ -931,16 +969,18 @@ public:
 /// A LabelInstr starts a block targeted by multiple predecessors.
 /// Every incoming edge must come from a GotoInstr.
 ///
 class LabelInstr : public BlockHeaderInstr {
   friend class InfoManager;
   friend class InstrFactory;
   friend class Copier;
 
+
+protected:
   LabelInstr(const InstrInfo* info, int paramc, Def* params) :
       BlockHeaderInstr(info, paramc, params), preds(0) {
   }
 
 public:
   static const InstrShape shape = LABELINSTR_SHAPE;
 
   GotoInstr* preds; // list of incoming gotos
@@ -1098,23 +1138,113 @@ public:
     return arms[i];
   }
 
   ArmInstr* default_arm() {
     return arms[num_cases()];
   }
 };
 
+/// A CatchBlockInstr begins the initial block of a catch block. Legal
+/// opcodes are HR_catchblock. The parameters of a CatchBlockInstr are
+/// the incoming state.
+///
+/// HR_catchblock: ([args]
+class CatchBlockInstr : public LabelInstr {
+  friend class InfoManager;
+  friend class InstrFactory;
+  friend class Copier;
+
+  CatchBlockInstr(const InstrInfo* info, int paramc, Def* params) :
+    LabelInstr(info, paramc, params), catch_preds(NULL) {
+  }
+
+public:
+  static const InstrShape shape = CATCHBLOCKINSTR_SHAPE;
+
+  /** Number of parameters, not counting effect */
+  int data_param_count() const {
+    return paramc - 1;
+  }
+
+  /** get data param i */
+  Def* data_param(int i) {
+    assert(i >= 0 && i < data_param_count());
+    return &params[1 + i];
+  }
+
+  Def* effect_out() {
+    return &params[0];
+  }
+
+  int vpc;
+  ExceptionEdge* catch_preds;
+
+  inline void printCatchPreds();
+};
+
+class CatchBlockRange: public SeqRange<ExceptionEdge*> {
+ public:
+  explicit CatchBlockRange(BlockStartInstr* block): SeqRange<ExceptionEdge*>(*InstrGraph::blockEnd(block)->catch_blocks) {}
+  explicit CatchBlockRange(BlockEndInstr* block): SeqRange<ExceptionEdge*>(*block->catch_blocks) {}
+
+  CatchBlockInstr* front() const {
+    return SeqRange<ExceptionEdge*>::front()->to;
+  }
+  CatchBlockInstr* popFront() {
+    CatchBlockInstr* t = front();
+    SeqRange<ExceptionEdge*>::popFront();
+    return t;
+  }
+};
+
+
+class ExceptionEdgeRange {
+public:
+  explicit ExceptionEdgeRange(CatchBlockInstr* catch_block) {
+    ExceptionEdge* p = catch_block->catch_preds;
+    front_ = p;
+    back_ = p ? p->prev_exception : 0;
+  }
+
+  bool empty() const {
+    return !front_;
+  }
+
+  ExceptionEdge* front() const {
+    assert(!empty());
+    return front_;
+  }
+
+  ExceptionEdge* popFront() {
+    ExceptionEdge* t = front();
+    ExceptionEdge* F = front_;
+    front_ = (F == back_) ? (back_ = 0) : F->next_exception;
+    return t;
+  }
+
+private:
+  ExceptionEdge *front_, *back_;
+};
+
+inline void CatchBlockInstr::printCatchPreds() {
+  printf("exception edges ( ");
+  for (ExceptionEdgeRange r(this); !r.empty(); r.popFront())
+    printf("i%d ", r.front()->from->id);
+  printf(") -> i%d\n", id);
+}
+
 // ------------------------------- IR5 end ---------------------------------
 
 /// true if this goto is the only predecessor of its target.
 ///
 inline bool isAlone(GotoInstr* go) {
   assert(go->target);
-  return go->next_goto == go;
+  // Don't allow start block to merge into main block
+  return go->next_goto == go && kind(InstrGraph::blockStart(go)) != HR_start;
 }
 
 /// Range over the param at the given position
 /// for all arms of a CondInstr
 ///
 class ArmParamRange {
 public:
   ArmParamRange(CondInstr* instr, int pos) :
--- a/halfmoon/hm-instrfactory.cpp
+++ b/halfmoon/hm-instrfactory.cpp
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "hm-main.h"
 #ifdef VMCFG_HALFMOON
 #include "hm-templatebuilder.h"
 
@@ -183,19 +183,19 @@ NaryStmt2* InstrFactory::newNaryStmt2(In
 NaryStmt3* InstrFactory::newNaryStmt3(InstrKind kind, Def* effect, Def* param1,
                                       Def* param2, Def* param3, int varargc,
                                       Def* varargs[]) {
   assert(isNaryStmt3(kind) && "invalid opcode");
   InstrInfo* info = infos_.get<NaryStmt3>(kind, varargc, this);
   NaryStmt3* stmt = new (alloc_, info->num_uses, sizeof(Use)) NaryStmt3(info);
   // placement new to initialize Uses
   new (&stmt->effect_in()) Use(stmt, effect);
-  new (&stmt->name_in()) Use(stmt, param1);
-  new (&stmt->env_in()) Use(stmt, param2);
-  new (&stmt->index_in()) Use(stmt, param3);
+  new (&stmt->uses()[1]) Use(stmt, param1);
+  new (&stmt->uses()[2]) Use(stmt, param2);
+  new (&stmt->uses()[3]) Use(stmt, param3);
   for (int i = 0; i < varargc; ++i)
     new (&stmt->vararg(i)) Use(stmt, varargs[i]);
   // placement new to initialize Defs
   new (stmt->effect_out()) Def(stmt);
   new (stmt->value_out()) Def(stmt);
   return stmt;
 }
 
@@ -204,28 +204,52 @@ NaryStmt3* InstrFactory::newNaryStmt3(In
 NaryStmt4* InstrFactory::newNaryStmt4(InstrKind kind, Def* effect, Def* param1,
                                       Def* param2, Def* param3, Def* param4,
                                       int varargc, Def* varargs[]) {
   assert(isNaryStmt4(kind) && "invalid opcode");
   InstrInfo* info = infos_.get<NaryStmt4>(kind, varargc, this);
   NaryStmt4* stmt = new (alloc_, info->num_uses, sizeof(Use)) NaryStmt4(info);
   // placement new to initialize Uses
   new (&stmt->effect_in()) Use(stmt, effect);
-  new (&stmt->name_in()) Use(stmt, param1);
-  new (&stmt->env_in()) Use(stmt, param2);
-  new (&stmt->ns_in()) Use(stmt, param3);
-  new (&stmt->index_in()) Use(stmt, param4);
+  new (&stmt->uses()[1]) Use(stmt, param1);
+  new (&stmt->uses()[2]) Use(stmt, param2);
+  new (&stmt->uses()[3]) Use(stmt, param3);
+  new (&stmt->uses()[4]) Use(stmt, param4);
   for (int i = 0; i < varargc; ++i)
     new (&stmt->vararg(i)) Use(stmt, varargs[i]);
   // placement new to initialize Defs
   new (stmt->effect_out()) Def(stmt);
   new (stmt->value_out()) Def(stmt);
   return stmt;
 }
 
+/// Create an nary5 statement with the given inputs.
+///
+NaryStmt4* InstrFactory::newNaryStmt4(InstrKind kind, Def* effect, Def* param1,
+                                      Def* param2, Def* param3, Def* param4, Def* param5,
+                                      int varargc, Def* varargs[]) {
+  assert(isNaryStmt4(kind) && "invalid opcode");
+  varargc++;
+  InstrInfo* info = infos_.get<NaryStmt4>(kind, varargc, this);
+  NaryStmt4* stmt = new (alloc_, info->num_uses, sizeof(Use)) NaryStmt4(info);
+  // placement new to initialize Uses
+  new (&stmt->effect_in()) Use(stmt, effect);
+  new (&stmt->uses()[1]) Use(stmt, param1);
+  new (&stmt->uses()[2]) Use(stmt, param2);
+  new (&stmt->uses()[3]) Use(stmt, param3);
+  new (&stmt->uses()[4]) Use(stmt, param4);
+  new (&stmt->vararg(0)) Use(stmt, param5);
+  for (int i = 1; i < varargc; ++i)
+    new (&stmt->vararg(i)) Use(stmt, varargs[i - 1]);
+  // placement new to initialize Defs
+  new (stmt->effect_out()) Def(stmt);
+  new (stmt->value_out()) Def(stmt);
+  return stmt;
+}
+
 /// helper - create and initialize a CallStmt2, up to but not including
 /// initializing the varargs
 ///
 CallStmt2* InstrFactory::createCallStmt2(InstrKind kind, Def* effect, Def* param,
                                          Def* obj, int varargc) {
   assert(isCallStmt2(kind) && "invalid opcode for call statement");
   InstrInfo* info = infos_.get<CallStmt2>(kind, varargc, this);
   CallStmt2* call = new (alloc_, info->num_uses, sizeof(Use)) CallStmt2(info);
@@ -504,16 +528,25 @@ StartInstr* InstrFactory::newStartInstr(
     param_types[fixedc] = lattice_.array_type[kTypeNotNull];
 
   for (int i = 0; i < paramc; ++i)
     new (&start->params[i]) Def(start, param_types[i]);
 
   return start;
 }
 
+/// Create CatchBlock instruction with given param types
+///
+CatchBlockInstr* InstrFactory::newCatchBlockInstr(int paramc) {
+  Def* params = new (alloc_) Def[paramc];
+  CatchBlockInstr* start = new (alloc_) CatchBlockInstr(infos_.get<CatchBlockInstr>(HR_catchblock, this),
+                                                        paramc, params);
+  return start;
+}
+
 /// private helper: initialize an ArmInstr within a given owner
 /// NOTE: static, takes an allocator so it can be called by Info::clone()
 /// TODO: refactor so clone just calls a helper in here for the whole CondInstr
 ///
 ArmInstr* InstrFactory::initArm(Allocator& alloc, int arm_pos, CondInstr* owner,
                            const InstrInfo* info) {
   ArmInstr* arm = new (alloc) ArmInstr(info);
   arm->owner = owner;
@@ -690,24 +723,28 @@ StopInstr* InstrFactory::newStopInstr(In
   new (&stop->args[0]) Use(stop, effect);
   for (int i = 0; i < data_argc; ++i)
     new (&stop->args[i + 1]) Use(stop, data_args[i]);
   return stop;
 }
 
 /// create a new safepoint instr with the given inputs
 ///
-SafepointInstr* InstrFactory::newSafepointInstr(Def* effect, Def* state) {
-  SafepointInstr* instr = new (alloc_)
-      SafepointInstr(infos_.get<SafepointInstr>(HR_safepoint, this));
-  new (&instr->effect_in()) Use(instr, effect);
-  new (&instr->state_in()) Use(instr, state);
-  new (instr->effect_out()) Def(instr);
-  new (instr->state_out()) Def(instr);
-  return instr;
+SafepointInstr* InstrFactory::newSafepointInstr(Def* effect, int argc, Def* args[]) {
+  InstrInfo* info = infos_.get<SafepointInstr>(HR_safepoint, argc, this);
+  SafepointInstr* stmt =
+    new (alloc_, info->num_uses, sizeof(Use)) SafepointInstr(info);
+  // placement new to initialize uses.
+  new (&stmt->effect_in()) Use(stmt, effect);
+  for (int i = 0; i < argc; ++i)
+      new (&stmt->vararg(i)) Use(stmt, args[i]); // placement new
+  // placement new to initialize defs.
+  new (stmt->effect_out()) Def(stmt);
+  new (stmt->state_out()) Def(stmt);
+  return stmt;
 }
 
 /// create a new setlocal instr with the given tuple index,
 /// state tuple, and value to set
 ///
 SetlocalInstr* InstrFactory::newSetlocalInstr(int index, Def* state, Def* val) {
   SetlocalInstr* setlocal = new (alloc_)
       SetlocalInstr(infos_.get<SetlocalInstr>(HR_setlocal, this), index);
@@ -754,16 +791,27 @@ DeoptFinishCallInstr* InstrFactory::newD
 DeoptFinishInstr* InstrFactory::newDeoptFinishInstr(Def* effect) {
   InstrInfo* info = infos_.get<DeoptFinishInstr>(HR_deopt_finish, this);
   DeoptFinishInstr* stmt = new (alloc_) DeoptFinishInstr(info);
   new (&stmt->effect_in()) Use(stmt, effect);
   new (stmt->effect_out()) Def(stmt);
   return stmt;
 }
 
+DebugInstr* InstrFactory::newDebugInstr(InstrKind kind, Def* effect, Def* val) {
+  DebugInstr* debuginstr = new (alloc_)
+      DebugInstr(infos_.get<DebugInstr>(kind, this));
+  // placement new to initialize uses
+  new (&debuginstr->effect_in()) Use(debuginstr, effect);
+  new (&debuginstr->value_in()) Use(debuginstr, val);
+  // placement new to initialize defs
+  new (debuginstr->effect_out()) Def(debuginstr);
+  return debuginstr;
+}
+
 /// create a new InstrGraph, using our infos and lattice
 ///
 InstrGraph* InstrFactory::createGraph() {
   return new (alloc_) InstrGraph(this, &infos_);
 }
 
 /// helper: return the HR coercion opcode corresponding
 /// to the given traits.
--- a/halfmoon/hm-instrfactory.h
+++ b/halfmoon/hm-instrfactory.h
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef INSTR_FACTORY_H_
 #define INSTR_FACTORY_H_
 
 namespace halfmoon {
@@ -37,16 +37,18 @@ public:
   NaryStmt2* newNaryStmt2(InstrKind kind, Def* effect, Def* param1,
                           Def* param2, int varargc, Def* varargs[]);
 
   NaryStmt3* newNaryStmt3(InstrKind kind, Def* effect, Def* param1, Def* param2,
                           Def* param3, int varargc, Def* varargs[]);
 
   NaryStmt4* newNaryStmt4(InstrKind kind, Def* effect, Def* param1, Def* param2,
                           Def* param3, Def* param4, int varargc, Def* varargs[]);
+  NaryStmt4* newNaryStmt4(InstrKind kind, Def* effect, Def* param1, Def* param2,
+                          Def* param3, Def* param4, Def* param5, int varargc, Def* varargs[]);
 
   // call stmt with multiname
   CallStmt2* newCallStmt2(InstrKind kind, Def* effect, Def* param,
                         Def* obj, int varargc, Def* varargs[]);
   CallStmt2* newCallStmt2(InstrKind kind, Def* effect, Def* param,
                           Def* obj, Def* arg);
   CallStmt2* newCallStmt2(InstrKind kind, Def* effect, Def* param,
                           Def* obj);
@@ -90,16 +92,19 @@ public:
 
   // label instr
   LabelInstr* newLabelInstr(int param_count);
 
   // start instr
   StartInstr* newStartInstr(MethodInfo* method);
   StartInstr* newStartInstr(InstrKind kind, int num_params, const Type* param_types[]);
 
+  // catchblock instr
+  CatchBlockInstr* newCatchBlockInstr(int num_params);
+
   // if instr
   IfInstr* newIfInstr(Def* cond, int argc, Def* args[]);      // n-arg array
   IfInstr* newIfInstr(Def* cond, Def* arg);                   // single arg
   IfInstr* newIfInstr(Def* cond, Def* arg0, Def* arg1);       // pair of args
   IfInstr* newIfInstr(Def* cond, int argc, Def* default_arg); // n-arg default
   IfInstr* newIfInstr(Def* cond, int argc);                   // n-arg NULL
 
   // switch instr
@@ -108,23 +113,26 @@ public:
   // goto statement
   GotoInstr* newGotoStmt(LabelInstr* target, Def* default_def = 0);
 
   /// stop instruction
   StopInstr* newStopInstr(InstrKind k, Def* effect, Def* data);
   StopInstr* newStopInstr(InstrKind k, Def* effect, int data_argc, Def* data_args[]);
 
   // safepoint instr
-  SafepointInstr* newSafepointInstr(Def* effect, Def* state);
+  SafepointInstr* newSafepointInstr(Def* effect, int argc, Def* args[]);
 
   // DEOPT: new-style safepoint instr
   DeoptSafepointInstr* newDeoptSafepointInstr(Def* effect, int argc, Def* args[]);
   DeoptFinishInstr* newDeoptFinishInstr(Def* effect);
   DeoptFinishCallInstr* newDeoptFinishCallInstr(Def* effect, Def* val);
 
+  // debugfile and debugline
+  DebugInstr* newDebugInstr(InstrKind kind, Def* effect, Def* val);
+
   // setlocal instr
   SetlocalInstr* newSetlocalInstr(int index, Def* state, Def* val);
 
   // create a new InstrGraph over our infos and lattice
   InstrGraph* createGraph();
 
 private:
   // private helper - create a CallStmt2, up to vararg initialization
--- a/halfmoon/hm-instrgraph.cpp
+++ b/halfmoon/hm-instrgraph.cpp
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "hm-main.h"
 #ifdef VMCFG_HALFMOON
 
 namespace halfmoon {
@@ -15,17 +15,17 @@ InstrGraph::InstrGraph(InstrFactory* fac
 , infos_(infos)
 , alloc_(factory->alloc())
 , instr_count_(0)
 , block_count_(0)
 , def_count_(0) {
 }
 
 Instr* finishAdding(InstrGraph* ir, Instr* instr) {
-  if (enable_typecheck && !enable_verbose)
+  if (enable_typecheck)
     printCompactInstr(ir->lattice.console(), instr, false);
   TypeAnalyzer(ir).computeTypes(instr);
   if (enable_verbose)
     printInstr(ir->lattice.console(), instr);
   return instr;
 }
 
 Instr* InstrGraph::addInstrAfter(Instr* pos, Instr* instr) {
@@ -269,16 +269,71 @@ void InstrGraph::joinBlocks(BlockEndInst
   int param_count = numDefs(succ_start);
   // For each succ_start param, redirect all uses to the def specified 
   // by pred_end's corresponding arg, then clear the arg.
   for (int i = 0; i < param_count; ++i) {
     Def* new_def = def(pred_end->args[i]);
     pred_end->args[i] = 0;
     copyUses(&succ_start->params[i], new_def);
   }
+
+  if (pred_end->catch_blocks != NULL) {
+    // Merge these into succ
+    BlockEndInstr* succ_end = blockEnd(succ_start);
+    if (succ_end->catch_blocks == NULL) {
+      // Successors didn't have any, so just transfer them
+      succ_end->catch_blocks = pred_end->catch_blocks;
+      for (SeqRange<ExceptionEdge*> r(*pred_end->catch_blocks); !r.empty(); r.popFront()) {
+        if (enable_verbose) {
+          lattice.console() << "adjusting exception edge from i" << r.front()->from->id << " -> i" << r.front()->to->id << "\n";
+        }
+        r.front()->from = succ_end;
+        if (enable_verbose) {
+          lattice.console() << "adjusting exception edge to i" << r.front()->from->id << " -> i" << r.front()->to->id << "\n";
+        }
+      }
+    } else {
+      // Merge the lists
+      for (CatchBlockRange succ_catch(succ_end); !succ_catch.empty();) {
+        CatchBlockInstr* catch_block = succ_catch.popFront();
+        for (CatchBlockRange pred_catch(pred_end); !pred_catch.empty();) {
+          if (catch_block == pred_catch.popFront()) {
+            catch_block = NULL;
+            break;
+          }
+        }
+        if (catch_block != NULL) {
+          ExceptionEdge* edge = new (alloc_) ExceptionEdge(succ_start, catch_block);
+          pred_end->catch_blocks->add(edge);
+
+          if (enable_verbose)
+            lattice.console() << "adding exception edge i" << succ_start->id << " -> i" << catch_block->id << "\n";
+          
+          ExceptionEdge* N = catch_block->catch_preds;
+          ExceptionEdge* P = N->prev_exception;
+          edge->next_exception = N;
+          edge->prev_exception = P;
+          N->prev_exception = edge;
+          P->next_exception = edge;
+        }
+      }
+      // Disconnect this block
+      for (SeqRange<ExceptionEdge*> r(*pred_end->catch_blocks); !r.empty();) {
+        ExceptionEdge* edge = r.popFront();
+        ExceptionEdge* N = edge->next_exception;
+        ExceptionEdge* P = edge->prev_exception;
+        if (edge->to->catch_preds == edge) {
+          edge->to->catch_preds = N;
+        }
+        N->prev_exception = P;
+        P->next_exception = N;
+      }
+    }
+  }
+
   InstrRange block(succ_start);
   block.unlinkFront(); // remove succ_start from its block
   replaceInstr(pred_end, block);
 }
 
 /// TODO
 ///
 InstrRange InstrGraph::returnBlock() const {
@@ -323,26 +378,34 @@ PrintWriter& InstrGraphBuilder::console(
 void InstrGraphBuilder::setPos(Instr* instr) {
   assert(isBlockStart(instr));
   pos_ = instr;
 }
 
 /// Create a const-generating instr of the given type if one does not already
 /// exist.  Since constants are only created once, link them immediately
 /// after ir->begin to ensure the constant dominates all uses.
+/// In the presence of exception edges this enforced sharing results
+/// problems in the register allocator, so treat them as normal
+/// instructions and let value numbering share them where it's possible locally.
 Def* InstrGraphBuilder::addConst(const Type* t) {
   assert(isConst(t));
+#if 0
   TypeKey k(t);
   Def* c = constants_.get(k);
   if (!c) {
     ConstantExpr* instr = factory_.newConstantExpr(HR_const, t);
     constants_.put(k, c = instr->value());
     ir_->addInstrAfter(ir_->begin, instr);
   }
   return c;
+#endif
+  ConstantExpr* instr = factory_.newConstantExpr(HR_const, t);
+  addInstr(instr);
+  return instr->value();
 }
 
 /// Compute the types of i's defs.
 ///
 void InstrGraphBuilder::computeType(Instr* i) {
   TypeAnalyzer analyzer(ir_);
   analyzer.computeTypes(i);
 }
@@ -405,16 +468,17 @@ void copyAllUses(Instr* old_instr, Instr
 
 /// Return the total number of Defs in instr.
 /// TODO remove special cases once Info knows IR5
 ///
 int numDefs(const Instr* instr) {
   switch (kind(instr)) {
     case HR_start:
     case HR_template:
+    case HR_catchblock:
     case HR_label:
       return ((BlockHeaderInstr*)instr)->paramc;
     case HR_arm:
       return numArgs(((ArmInstr*)instr)->owner);
     default:
       assert(instr->info->num_defs >= 0);
       return instr->info->num_defs;
   }
@@ -422,16 +486,17 @@ int numDefs(const Instr* instr) {
 
 /// Return a pointer to a contiguous array of all instr's Defs.
 /// TODO remove special cases once Info knows IR5
 ///
 Def* getDefs(const Instr* instr) {
   switch (kind(instr)) {
     case HR_start:
     case HR_template:
+    case HR_catchblock:
     case HR_label:
     case HR_arm:
       return ((BlockStartInstr*)instr)->params;
     default:
       assert(instr->info->defs_off >= 0);
       return (Def*) ((char*) instr + instr->info->defs_off);
   }
 }
@@ -529,16 +594,17 @@ ArrayRange<ArmInstr*> armRange(CondInstr
 }
 
 /// true if instruction starts a block
 ///
 bool isBlockStart(const Instr* instr) {
   switch (kind(instr)) {
     case HR_start:
     case HR_template:
+    case HR_catchblock:
     case HR_label:
     case HR_arm:
       return true;
     default:
       return false;
   }
 }
 
@@ -569,17 +635,17 @@ bool isCond(const Instr* instr) {
 }
 
 /// True if an instruction has root Defs, i.e. Defs whose types
 /// are axiomatic. Defs introduced by ConstantExprs and 
 /// predecessor-free block starts have this property.
 ///
 bool hasRootDefs(const Instr* instr) {
   InstrShape s = shape(instr);
-  return s == CONSTANTEXPR_SHAPE || s == STARTINSTR_SHAPE;
+  return s == CONSTANTEXPR_SHAPE || s == STARTINSTR_SHAPE || s == CATCHBLOCKINSTR_SHAPE;
 }
 
 /// true if end/start are a pair of related delimters, 
 /// i.e. if end is predecessor and start is successor.
 /// 
 bool isDelimPair(const Instr* end, const Instr* start) {
   return ((kind(start) == HR_arm && ((ArmInstr*)start)->owner == end) ||
           (kind(end) == HR_goto && ((GotoInstr*)end)->target == start));
@@ -630,29 +696,33 @@ EachBlock::EachBlock(InstrGraph* ir, boo
 /// dictated by second param) of block start delimiters
 /// in our blocks member variable.
 ///
 void EachBlock::dfs(BlockStartInstr* block, bool reverse) {
   if (visited.get(block->blockid))
     return;
   visited.set(block->blockid);
   if (ir->hasBlockEnd(block)) {
-    Instr* end = ir->blockEnd(block);
+    BlockEndInstr* end = ir->blockEnd(block);
     InstrKind k = kind(end);
     // recurse into successors
     if (k == HR_goto) {
       dfs(cast<GotoInstr>(end)->target, reverse);
     } else if (k == HR_if || k == HR_switch) {
       for (ArrayRange<ArmInstr*> r = armRange((CondInstr*)end); !r.empty();)
         dfs(r.popFront(), reverse);
     } else if (k == HR_return || k == HR_throw) {
       // no successors
     } else {
         assert(false && "unsupported block-end opcode");
     }
+    if (end->catch_blocks != NULL) {
+      for (CatchBlockRange r(end); !r.empty();)
+        dfs(r.popFront(), reverse);
+    }
     // add to block list
     if (reverse)
       blocks.insert(block);
     else
       blocks.add(block);
   }
 }
 
@@ -677,16 +747,40 @@ void pruneGraph(InstrGraph* ir) {
         GotoInstr* go = p.popFront();
         if (!mark.get(go->id)) {
           if (enable_verbose)
             printf("prune: goto i%d ->i%d\n", go->id, instr->id);
           removeGoto(go);
         }
       }
     }
+    if (kind(instr) == HR_catchblock) {
+      CatchBlockInstr* cblock = cast<CatchBlockInstr>(instr);
+      ExceptionEdge* head = cblock->catch_preds;
+        
+      for (ExceptionEdge* edge = head; edge != NULL; ) {
+        ExceptionEdge* next = edge == cblock->catch_preds->prev_exception ? NULL : edge->next_exception;
+        if (!mark.get(edge->from->id)) {
+          if (enable_verbose)
+            printf("prune: eliminated exception edge i%d -> i%d\n", edge->from->id, cblock->id);
+          if (edge->next_exception == edge) {
+            cblock->catch_preds = NULL;
+          } else {
+            ExceptionEdge* N = edge->next_exception;
+            ExceptionEdge* P = edge->prev_exception;
+            if (cblock->catch_preds == edge) {
+              head = cblock->catch_preds = N;
+            }
+            N->prev_exception = P;
+            P->next_exception = N;
+          }
+        }
+        edge = next;
+      }
+    }
   }
   if (ir->end && !mark.get(ir->end->id))
     ir->end = 0;
   if (ir->exit && !mark.get(ir->exit->id))
     ir->exit = 0;
   assert(checkPruned(ir));
 }
 
--- a/halfmoon/hm-instrgraph.h
+++ b/halfmoon/hm-instrgraph.h
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef INSTR_GRAPH_H_
 #define INSTR_GRAPH_H_
 
 namespace halfmoon {
--- a/halfmoon/hm-interpreter.h
+++ b/halfmoon/hm-interpreter.h
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef HM_INTERPRETER_H_
 #define HM_INTERPRETER_H_
 
 namespace halfmoon {
--- a/halfmoon/hm-jitmanager.cpp
+++ b/halfmoon/hm-jitmanager.cpp
@@ -1,10 +1,10 @@
 /* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "hm-main.h"
 #ifdef VMCFG_HALFMOON
 
 #include "profiler/profiler-main.h"
--- a/halfmoon/hm-jitmanager.h
+++ b/halfmoon/hm-jitmanager.h
@@ -1,10 +1,10 @@
 /* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 namespace halfmoon {
 using profiler::MethodProfileMgr;
 using profiler::MethodProfile;
 
--- a/halfmoon/hm-jitwriter.cpp
+++ b/halfmoon/hm-jitwriter.cpp
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "hm-main.h"
 #ifdef VMCFG_HALFMOON
 
 namespace halfmoon {
@@ -158,36 +158,37 @@ void JitWriter::analyze(AbcOpcode abcop,
 
   if (current_block_ && pc > current_block_->start && abc_->haveBlock(pc)) {
 #ifdef AVMPLUS_VERBOSE
     if (enable_verbose)
       console_ << "surprise\n";
 #endif
     abc_->analyzeEnd(current_block_, pc);
     finishBlock(pc);
-    newBlock(pc);
+    newBlock(pc, frame);
   }
 }
 
 void JitWriter::finishBlock(const uint8_t* nextpc) {
   current_block_->end = nextpc;
   current_block_ = 0;
 }
 
-void JitWriter::newBlock(const uint8_t* abc_pc) {
+void JitWriter::newBlock(const uint8_t* abc_pc, const FrameState* frame) {
   if (current_block_ && current_block_->start == abc_pc) {
     // we just started this block.  ignore.
     return;
   }
   current_block_ = abc_->newAbcBlock(abc_pc);
+  current_block_->start_withbase = frame->withBase;
   abc_->analyzeExceptions(current_block_);
 }
 
 void JitWriter::startBlock(const FrameState* frame) {
-  newBlock(frame->abc_pc);
+  newBlock(frame->abc_pc, frame);
   MethodSignaturep signature = method_->getMethodSignature();
   const Type** types = new (abc_->alloc0()) const Type*[signature->frame_size()];
   FrameRange<const FrameValue> from = range(&frame->value(0), frame, signature);
   FrameRange<const Type*> t = range(types, frame, signature);
   for (; !from.empty(); from.popFront(), t.popFront())
     t.front() = jit_mgr_->lattice()->makeType(from.front());
   current_block_->start_types = types;
 }
@@ -241,17 +242,17 @@ void JitWriter::writeOp1(const FrameStat
     const uint8_t* nextpc = pc + 4;
     int offset = int32_t(opd1);
     if (abcop == OP_jump) {
       abc_->analyzeEnd(current_block_, nextpc + offset);
       finishBlock(nextpc);
     } else {
       abc_->analyzeBranch(current_block_, abcop, nextpc, offset);
       finishBlock(nextpc);
-      newBlock(nextpc);
+      newBlock(nextpc, frame);
     }
   }
 }
 
 void JitWriter::writeOp2(const FrameState* frame, const uint8_t *pc,
                          AbcOpcode abcop, uint32_t, uint32_t, Traits*) {
   analyze(abcop, pc, frame);
 }
--- a/halfmoon/hm-liremitter.cpp
+++ b/halfmoon/hm-liremitter.cpp
@@ -96,37 +96,137 @@ CodeList* LirHelper2::assemble(const nan
 #ifdef NJ_VERBOSE
   if (pool->isVerbose(LC_Liveness)) {
     Allocator live_alloc;
     LirReader in(frag->lastIns);
     nanojit::live(&in, live_alloc, frag, &codeMgr->log);
   }
 #endif
 
-  AvmAssert(halfmoon::checkLir(frag));
+  AvmAssert(halfmoon::checkLir(frag, &codeMgr->log));
 
   Assembler *assm =
     new (*lir_alloc) Assembler(codeMgr->codeAlloc, codeMgr->allocator,
                                *lir_alloc, &codeMgr->log, config, mdwriter);
-  verbose_only( StringList asmOutput(*lir_alloc);)verbose_only(
-      if (!pool->isVerbose(VB_raw)) assm->_outputCache = &asmOutput;)
+
+#ifdef AVMPLUS_VERBOSE
+  StringList asmOutput(*lir_alloc);
+  if (!pool->isVerbose(VB_raw))
+    assm->_outputCache = &asmOutput;
+#endif
+
+  assm->beginAssembly(frag);
   LirReader bufreader(frag->lastIns);
-  assm->beginAssembly(frag);
   assm->assemble(frag, &bufreader);
   CodeList* code_list = assm->endAssembly(frag);
 
-#ifdef NJ_VERBOSE
+#ifdef AVMPLUS_VERBOSE
   assm->_outputCache = 0;
-  for (Seq<char*>* p = asmOutput.get(); p != NULL; p = p->tail)
+  for (Seq<char*>* p = asmOutput.get(); p != NULL; p = p->tail) {
     assm->outputf("%s", p->head);
+  }
 #endif
 
   return code_list;
 }
 
+#ifdef AVMPLUS_ARM
+#ifdef _MSC_VER
+#define RETURN_METHOD_PTR(_class, _method) \
+return *((int*)&_method);
+#else
+#define RETURN_METHOD_PTR(_class, _method) \
+union { \
+    int (_class::*bar)(); \
+    int foo[2]; \
+}; \
+bar = _method; \
+return foo[0];
+#endif
+
+#elif defined __GNUC__
+#define RETURN_METHOD_PTR(_class, _method)      \
+  union {                                       \
+    int (_class::*bar)();                       \
+    intptr_t foo;                               \
+  };                                            \
+  bar = _method;                                \
+  return foo;
+#else
+#define RETURN_METHOD_PTR(_class, _method)      \
+  return *((intptr_t*)&_method);
+#endif
+
+#ifdef _MSC_VER
+    #if !defined (AVMPLUS_ARM) || defined(UNDER_RT)
+    extern "C"
+    {
+        int __cdecl _setjmp3(jmp_buf jmpbuf, int arg);
+    }
+    #else
+    #include <setjmp.h>
+    #undef setjmp
+    extern "C"
+    {
+        int __cdecl setjmp(jmp_buf jmpbuf);
+    }
+    #endif // AVMPLUS_ARM
+#endif // _MSC_VER
+  
+#if defined _MSC_VER && !defined AVMPLUS_ARM
+#  define SETJMP ((uintptr_t)_setjmp3)
+#elif defined AVMPLUS_MAC_CARBON
+#  define SETJMP setjmpAddress
+#else
+#  define SETJMP ((uintptr_t)VMPI_setjmpNoUnwind)
+#endif // _MSC_VER
+  
+#define FUNCADDR(addr) (uintptr_t)addr
+#define EFADDR(f)   efAddr((int (ExceptionFrame::*)())(&f))
+  
+  intptr_t efAddr( int (ExceptionFrame::*f)() )
+  {
+    RETURN_METHOD_PTR(ExceptionFrame, f);
+  }
+  
+
+  int32_t hmBeginCatch(AvmCore* core,
+                       ExceptionFrame* ef,
+                       MethodInfo* info,
+                       intptr_t pc,
+                       AnyVal* slotPtr)
+  {
+    int32_t ordinal;
+    ef->beginCatch();
+    Exception* exception = core->exceptionAddr;
+    ExceptionHandler* handler = core->findExceptionHandlerNoRethrow(info, pc, exception, &ordinal);
+    if (!handler) {
+      // No matching exception, so rethrow.
+      core->throwException(exception);
+    }
+    ef->beginTry(core);
+    slotPtr->atom = exception->atom;
+
+    return ordinal;
+  }
+
+  void hmBeginTry(ExceptionFrame* ef, AvmCore* core) {
+    ef->beginTry(core);
+  }
+  void hmEndTry(ExceptionFrame* ef) {
+    ef->endTry();
+  }
+
+  FUNCTION(FUNCADDR(hmBeginCatch), SIG5(I,P,P,P,P,P), hmBeginCatch)
+  
+  FUNCTION(SETJMP, SIG2(I,P,I), fsetjmp)
+  METHOD(FUNCADDR(hmBeginTry), SIG2(V,P,P), hmBeginTry)
+  METHOD(FUNCADDR(hmEndTry), SIG1(V,P), hmEndTry)
+  
+
 /// Types understood by LIR.  We don't use LTy here because it conflates
 /// LTy_P with either LTy_I or LTy_Q, but we want to distinguish them.
 enum LirModel {
   kLirPtr,
   kLirInt,
   kLirDouble,
   kLirVoid
 };
@@ -219,25 +319,26 @@ LirEmitter::LirEmitter(Context* cxt, Ins
 , env_param(0)
 , argc_param(0)
 , ap_param(0)
 , loop_live_(*alloc1)
 , npe_label(0)
 , upe_label(0)
 , interrupt_label(0)
 , safepoint_space_(NULL)
-, safepoint_tags_(NULL)
-, vpc_space_(NULL)
 , get_cache_builder(*alloc1, *pool->codeMgr)
 , set_cache_builder(*alloc1, *pool->codeMgr)
 , call_cache_builder(*alloc1, *pool->codeMgr)
 , enable_verbose_lir_(enable_verbose && !enable_trace) 
 , profiled_info_(profiled_info)
 , bailout_branches_(*alloc1)
 , have_safepoints(false)
+, have_catchblocks_(false)
+, emittedBeginCatch(false)
+, catchLabels(NULL)
 {
   allocateTables();
 }
 
 LirEmitter::~LirEmitter() {
 }
 
 /**
@@ -436,16 +537,22 @@ void LirEmitter::analyzeLiveness() {
           for (ArrayRange<ArmInstr*> a = armRange((CondInstr*)branch); !a.empty();)
             analyzeEdgeLiveness(livemap, live, branch, a.popFront(), scratch,
                                 ir->size());
           break;
         }
         default:
           break;
       }
+      if (InstrGraph::blockEnd(block)->catch_blocks != NULL) {
+        for (CatchBlockRange cb(block); !cb.empty();) {
+          analyzeEdgeLiveness(livemap, live, branch, cb.popFront(), scratch,
+                              ir->size());
+        }
+      }
       for (InstrRange i(block); !i.empty(); i.popBack()) {
         Instr* instr = i.back();
         live.clear(instr->id);
         for (ArrayRange<Use> a = useRange(instr); !a.empty(); a.popFront())
           live.set(definerId(a.front()));
       }
       BlockInfo* block_info = livemap.get(block);
       if (!block_info) {
@@ -494,16 +601,26 @@ void LirEmitter::allocateTables() {
   // Sort all the blocks.  Later passes will use this order.
   sortBlocks();
 
   // Fill in the val_offsets and dom_children arrays.
   int num_defs = ir->def_count();
   int max_argc = 0;
   for (ArrayRange<BlockStartInstr*> b(blocks_, num_blocks_); 
         !b.empty(); b.popFront()) {
+
+    if (kind(b.front()) == HR_catchblock) {
+      have_catchblocks_ = true;
+      if (catchLabels == NULL) {
+        catchLabels = new (*lir_alloc) HashMap<intptr_t,CatchBlock*>(*lir_alloc, 4);
+      }
+      CatchBlockInstr* cblock = cast<CatchBlockInstr>(b.front());
+      catchLabels->put(cblock->vpc, new (*lir_alloc) CatchBlock(cblock));
+    }
+
     for (InstrRange r(b.front()); !r.empty(); r.popFront()) {
       max_argc = max(max_argc, numUses(r.front()));
     }
   }
 
   // Table of defs, indexed by val_offsets[instr->id] + pos(def)
   // DEOPT: The def_ins_ array must survive to the end of assembly.
   def_ins_ = new (alloc0) LIns*[num_defs];
@@ -567,17 +684,17 @@ void LirEmitter::emitStackOverflowCheck(
    * if overflow() { call handleOverflow(); } 
    */
   LIns* stack_overflow = lirout->insBranch(LIR_jf, compare_stack, 0);
   callIns(&ci_handleStackOverflowMethodEnv, 1, env_param);
   LIns* begin_label = setName(label(), "begin");
   stack_overflow->setTarget(begin_label);
 }
 
-void LirEmitter::emitBegin() {
+void LirEmitter::emitBegin(bool has_reachable_exceptions) {
 #ifdef NJ_VERBOSE
   bool verbose = enable_verbose_lir_ || pool->isVerbose(VB_jit);
 #else
  bool verbose = enable_verbose_lir_; 
 #endif
 
   LirBuffer* lirbuf = createLirout(verbose, "LIR");
   // add other LirWriters here.
@@ -598,16 +715,32 @@ void LirEmitter::emitBegin() {
   // Create a buffer for epilog code that we can write into in parallel.
   LirBuffer* epilog_buf = new (*lir_alloc) LirBuffer(*lir_alloc);
   epilog_buf->abi = ABI_CDECL;
 #ifdef NJ_VERBOSE
   epilog_buf->printer = lirbuf->printer;
 #endif
   traps_lir = new (*alloc1) LirBufWriter(epilog_buf, core->config.njconfig);
   traps_skip = traps_lir->insSkip(0);
+
+  // then space for the exception frame, be safe if its an init stub
+  if (has_reachable_exceptions) {
+    // [_save_eip][ExceptionFrame]
+    // offsets of local vars, rel to current ESP
+    _save_eip = stackAlloc(sizeof(intptr_t), "_save_eip");
+    _ef       = stackAlloc(sizeof(ExceptionFrame), "_ef");
+    MethodSignaturep method_signature = signature;
+    int alloc_size = method_signature->frame_size();
+    safepoint_space_ = stackAlloc(alloc_size << VARSHIFT(cxt->method),
+                                  "deopt locals");
+  } else {
+    _save_eip = NULL;
+    _ef = NULL;
+    safepoint_space_ = NULL;
+  }
 }
 
 LIns* LirEmitter::emitConst(const Type* t) {
   assert(isConst(t));
   switch (kind(t)) {
     default:
       assert(false && "unsupported kind");
     case kTypeName:
@@ -626,16 +759,18 @@ LIns* LirEmitter::emitConst(const Type* 
     case kTypeBoolean:
     case kTypeNumber:
     case kTypeScriptObject:
       switch (model(t)) {
         default:
           assert(false && "unsupported model");
         case kModelScriptObject:
           return InsConstPtr(objectVal(t));
+        case kModelNamespace:
+          return InsConstPtr(nsVal(t));
         case kModelString:
           return InsConstPtr(stringVal(t));
         case kModelAtom: {
           Atom const_atom = atomVal(t, core);
           // Numbers and strings can be gc allocated
           // So we have to store them in the pool to keep them alive
           if (AvmCore::isDouble(const_atom)) {
             pool->cpool_const_double.add((GCDouble*)atomPtr(const_atom));
@@ -837,50 +972,64 @@ void LirEmitter::emitLiveHints(Instr* ta
 /// Generate a LIR_label instruction for block, and give it a name.
 ///
 LIns* LirEmitter::emitLabel(BlockStartInstr* block) {
   StringBuffer name(core);
   name << halfmoon::label(block) << block->id;
   return setName(label(), name.c_str());
 }
 
+LIns* LirEmitter::emitCatchLabel(CatchBlockInstr* block) {
+  StringBuffer name(core);
+  name << halfmoon::label(block) << block->id;
+  LIns* catchLabel = label();
+  CatchBlock* cb = catchLabels->get(block->vpc);
+  cb->jmp->setTarget(catchLabel);
+  return setName(catchLabel, name.c_str());
+}
+
 /// Patches all points that speculate to jump to bailout point
 void LirEmitter::patchBailouts() {
-  LIns* bailout_label = setName(label(), "Deopt Point");
-  for (SeqRange<LIns*> branches(bailout_branches_); !branches.empty();)
-    branches.popFront()->setTarget(bailout_label);
+  AvmAssert(false && "Unimplemented");
 
-  LIns* abc_pc = ldi(vpc_space_, 0, ACCSET_OTHER, LOAD_NORMAL);
-  
-  bool returns_double = signature->returnTraitsBT() == BUILTIN_number;
-  const CallInfo* deopt_call = returns_double ?
-          FUNCTIONID(fprBailout) : FUNCTIONID(gprBailout);
+  // LIns* bailout_label = setName(label(), "Deopt Point");
+  // for (SeqRange<LIns*> branches(bailout_branches_); !branches.empty();)
+  //   branches.popFront()->setTarget(bailout_label);
 
-  LIns* return_val = callIns(deopt_call, 5,
-                             coreAddr, env_param, abc_pc,
-                             safepoint_space_, safepoint_tags_);
+  // LIns* abc_pc = ldi(vpc_space_, 0, ACCSET_OTHER, LOAD_NORMAL);
+  
+  // bool returns_double = signature->returnTraitsBT() == BUILTIN_number;
+  // const CallInfo* deopt_call = returns_double ?
+  //         FUNCTIONID(fprBailout) : FUNCTIONID(gprBailout);
 
-  popMethodFrame(method_frame_);
-  if (returns_double)
-    retd(return_val);
-  else
-    retp(return_val);
+  // LIns* return_val = callIns(deopt_call, 5,
+  //                            coreAddr, env_param, abc_pc,
+  //                            safepoint_space_, safepoint_tags_);
+
+  // popMethodFrame(method_frame_);
+  // if (returns_double)
+  //   retd(return_val);
+  // else
+  //   retp(return_val);
 }
 
 GprMethodProc LirEmitter::finish(DeoptData** deopt_data) {
   if (!bailout_branches_.isEmpty())
     patchBailouts();
 
   livep(args_);
   livep(coreAddr);
-  if (have_safepoints) {
+  if (safepoint_space_ != NULL) {
     livep(safepoint_space_);
-    livep(vpc_space_);
-    livep(safepoint_tags_);
-  } 
+  }
+  if (catchLabels != NULL) {
+    livep(_ef);
+    livep(_save_eip);
+  }
+
   livep(method_frame_);
   LIns* last_ins = livep(env_param);
 
   if (npe_label || upe_label || interrupt_label) {
     // Link traps lirbuf to main lirbuf.
     traps_skip->initLInsSk(last_ins);
     traps_lir->ins1(LIR_retp, traps_lir->insImmP(0));
     traps_lir->ins1(LIR_livep, coreAddr);
@@ -928,24 +1077,46 @@ GprMethodProc LirEmitter::finish(DeoptDa
 
 GprMethodProc LirEmitter::emit(DeoptData** deopt_data) {
 #ifdef NJ_VERBOSE
   if (enable_verbose_lir_) {
     cxt->out << "Generate LIR " << cxt->method << "\n";
   }
 #endif
 
-  emitBegin();
+  emitBegin(have_catchblocks_);
+
+  // The start block is emitted specially
+  current_block_ = 0;
+  for (InstrRange r(blocks_[0]); !r.empty(); r.popFront()) {
+    if (have_catchblocks_ && kind(r.front()) == HR_goto) {
+      // Initiailize the eip
+      stp(InsConstPtr((void*)-1), _save_eip, 0, ACCSET_OTHER);
 
-   for (int b = 0, n = num_blocks_; b < n; ++b) {
-      current_block_ = b;
-      for (InstrRange r(blocks_[b]); !r.empty(); r.popFront()) {
-        emit(r.front());
-     }
-   }
+      // _ef.beginTry(core);
+      callIns(FUNCTIONID(hmBeginTry), 2, _ef, coreAddr);
+      
+      // Exception* setjmpResult = setjmp(_ef.jmpBuf);
+      // ISSUE this needs to be a cdecl call
+      LIns* jmpbuf = lea(offsetof(ExceptionFrame, jmpbuf), _ef);
+      LIns* setjmpResult = callIns(FUNCTIONID(fsetjmp), 2, jmpbuf, InsConst(0));
+      
+      // If (setjmp() != 0) goto catch dispatcher, which we generate in the epilog.
+      // Note that register contents following setjmp return via longjmp are not predictable.
+      catch_branch = lirout->insBranch(LIR_jf, eqi0(setjmpResult), NULL);
+    }
+    emit(r.front());
+  }
+
+  for (int b = 1, n = num_blocks_; b < n; ++b) {
+    current_block_ = b;
+    for (InstrRange r(blocks_[b]); !r.empty(); r.popFront()) {
+      emit(r.front());
+    }
+  }
 
   return finish(deopt_data);
 }
 
 /// Called by JIT code to trace execution of this IR instruction.
 ///
 static void traceInstr(PrintWriter* out, Instr* instr) {
 #ifdef NJ_VERBOSE
@@ -1303,17 +1474,32 @@ void LirEmitter::do_callinterface(CallSt
          model(value_out_type) == defaultModelKind(interface_sig->returnTraits()));
 
   emitStoreArgs(call->args(), call->arg_count(), interface_sig);
   LIns* result = emitInterfaceAvmCall(value_out_type, callee_env, 
                                       call->arg_count(), args_, iid);
   set_def_ins(call->value_out(), result);
 }
 
+void LirEmitter::do_callstatic(CallStmt2* call) {
+  const Use& method_in = call->param_in();
+  MethodInfo* callee = getMethod(type(method_in));
+  MethodSignaturep callee_sig = callee->getMethodSignature();
+  LIns* callee_method = def_ins(method_in);
+  LIns* result = emitAvmCall(call->args(), call->arg_count(), 
+                             callee_sig, callee_method, call->value_out());
+  set_def_ins(call->value_out(), result);
+}
+
 void LirEmitter::do_return(StopInstr* stop) {
+  if (have_catchblocks_) {
+    // _ef.endTry();
+    callIns(FUNCTIONID(hmEndTry), 1, _ef);
+  }
+
   // brute force; tear down MethodFrame
   popMethodFrame(method_frame_);
   frag->lastIns = emitReturn(stop->value_in());
 }
 
 /// This goto is a fall-through path if it jumps to the next block in linear order.
 ///
 bool LirEmitter::isFallthruGoto(GotoInstr* go) {
@@ -1324,33 +1510,45 @@ bool LirEmitter::isFallthruGoto(GotoInst
 }
 
 /// This label is a fall-through path if the only goto is the previous block
 /// in linear order.
 bool LirEmitter::isFallthruLabel(LabelInstr* label) {
   assert(blocks_[current_block_] == label &&
          "fallthru check on non-current block");
   PredRange p(label);
-  if (p.empty() || (p.popFront(), !p.empty()))
-    return false; // 0 or 2+ predecessors
-  GotoInstr* go = p.front();
+  if (p.empty())
+    return false; // 0 predecessors
+  GotoInstr* go = p.popFront();
+  if (!p.empty())
+    return false; // 2+ predecessors
   return current_block_ > 0 &&
          blocks_[current_block_ - 1] == ir->blockStart(go);
 }
 
 bool LirEmitter::enableSSE() {
 #if defined AVMPLUS_AMD64
   return true;
 #elif defined AVMPLUS_IA32
   return core->config.njconfig.i386_sse2 != 0;
 #else
   return false;
 #endif
 }
 
+
+// Save our current PC location for the catch finder later.
+void LirEmitter::emitSetPc(DeoptSafepointInstr* instr)
+{
+  int vpc = instr->vpc;
+  // update bytecode ip if necessary
+  stp(InsConstPtr((void*)vpc), _save_eip, 0, ACCSET_OTHER);
+}
+
+
 /// BRANCH PATCHING
 ///
 /// label/goto: do_goto() and do_label() take care of patching.  Each goto
 /// (except fall-thrus) generate a LIR_j (jump) and saves it; if do_label() has
 /// come first, the jump is already patched.  Otherwise: do_label() generates
 /// a LIR_label, and patches any previously generated jumps.
 ///
 /// arm/if: do_if() and do_arm() take care of all patching, since Arms
@@ -1429,16 +1627,103 @@ void LirEmitter::do_label(LabelInstr* in
       set_def_ins(
           param,
           emitLoad(param_type, args_, i << VARSHIFT(cxt->method),
                    ACCSET_OTHER, LOAD_NORMAL));
     }
   }
 }
 
+void LirEmitter::emitBeginCatch() {
+  if (emittedBeginCatch)
+    return;
+
+  if (have_catchblocks_) {
+    emittedBeginCatch = true;
+
+    // exception case
+    LIns* catch_label = setName(label(), "catch");
+
+    catch_branch->setTarget(catch_label);
+    
+    // This regfence is necessary for correctness,
+    // as register contents after a longjmp are unpredictable.
+    lirout->ins0(LIR_regfence);
+    
+    MethodInfo* info = cxt->method;
+
+    // _ef.beginCatch()
+    int stackBase = signature->stack_base();
+    LIns* pc = lirout->insLoad(LIR_ldp, _save_eip, 0, ACCSET_OTHER, LOAD_NORMAL);
+    LIns* slotAddr = lea(stackBase << VARSHIFT(info) , safepoint_space_);
+    LIns* handler_ordinal = callIns(FUNCTIONID(hmBeginCatch), 5, coreAddr, _ef, InsConstPtr(info), pc, slotAddr);
+
+    (void)handler_ordinal;
+
+    int handler_count = info->abc_exceptions()->exception_count;
+    // Jump to catch handler
+    // Find last handler, to optimize branches generated below.
+    int i;
+    for (i = handler_count-1; i >= 0; i--) {
+      ExceptionHandler* h = &info->abc_exceptions()->exceptions[i];
+      CatchBlock* cb = catchLabels->get(h->target);
+      if (cb != NULL) break;
+    }
+    int last_ordinal = i;
+    // There should be at least one reachable handler.
+    AvmAssert(last_ordinal >= 0);
+    // Do a compare & branch to each possible target.
+    for (int j = 0; j <= last_ordinal; j++) {
+      ExceptionHandler* h = &info->abc_exceptions()->exceptions[j];
+      CatchBlock* cb = catchLabels->get(h->target);
+      if (cb != NULL) {
+        if (j == last_ordinal) {
+          cb->jmp = lirout->insBranch(LIR_j, NULL, NULL);
+        } else {
+          LIns* cond = binaryIns(LIR_eqi, handler_ordinal, InsConst(j));
+          cb->jmp = lirout->insBranch(LIR_jt, cond, NULL);
+        }
+      }
+    }
+
+    livep(_ef);
+    livep(_save_eip);
+    livep(safepoint_space_);
+  }
+
+
+}
+
+
+void LirEmitter::do_catchblock(CatchBlockInstr* instr) {
+  emitBeginCatch();
+
+  emitCatchLabel(instr);
+
+  lirout->ins0(LIR_regfence);
+
+  int pc = instr->vpc;
+  stp(InsConstPtr((void*)pc), _save_eip, 0, ACCSET_OTHER);
+  lastPcSave = pc;
+
+  // load stuff passed by incoming gotos
+  for (int i = 0, n = instr->paramc; i < n; ++i) {
+    Def* param = &instr->params[i];
+    if (!param->isUsed())
+      continue;
+    const Type* param_type = type(param);
+    if (!isLinear(param_type) && !isState(param_type)) {
+      set_def_ins(
+          param,
+          emitLoad(param_type, safepoint_space_, i << VARSHIFT(cxt->method),
+                   ACCSET_OTHER, LOAD_NORMAL));
+    }
+  }
+}
+
 /// Emit a conditional branch.  We optimize fall-through paths; if the taken
 /// arm is the next block, then swap arms and reverse the sense
 /// of the test.  Then, if the not-taken arm is the next block, omit the jump.
 ///
 /// Patching is done here and in do_arm(), depending on which is seen first for
 /// each arm.  See the "BRANCH PATCHING" comment.
 ///
 void LirEmitter::do_if(IfInstr* instr) {
@@ -1774,17 +2059,32 @@ LIns* LirEmitter::emitNpeHandler() {
 
 /** Generate one handler for all kConvertUndefinedToObjectError errors. */
 LIns* LirEmitter::emitUpeHandler() {
   return emitHandler(&upe_label, &ci_upe);
 }
 
 /** Generate one handler for all timeout interrupts */
 LIns* LirEmitter::emitInterruptHandler() {
-  return emitHandler(&interrupt_label, &ci_handleInterruptMethodEnv);
+  if (interrupt_label == NULL) {
+    LIns* args[] = { env_param };
+    interrupt_label = traps_lir->ins0(LIR_label);
+    
+#ifdef VMCFG_INTERRUPT_SAFEPOINT_POLL
+    traps_lir->ins0(LIR_pushstate);
+#endif 
+    traps_lir->ins0(LIR_regfence);
+    traps_lir->insCall(&ci_handleInterruptMethodEnv, args);
+#ifdef VMCFG_INTERRUPT_SAFEPOINT_POLL
+    traps_lir->ins0(LIR_popstate);
+    traps_lir->ins0(LIR_restorepc);
+#endif
+    
+  }
+  return interrupt_label;
 }
 
 void LirEmitter::do_cknull(UnaryStmt* instr) {
   const Use& value_in = instr->value_in();
   LIns* ptr = def_ins(value_in);
   LIns* undefined_ins = InsConstAtom(undefinedAtom);
   lirout->insBranch(LIR_jt, ltup(ptr, undefined_ins), emitNpeHandler());
   lirout->insBranch(LIR_jt, eqp(ptr, undefined_ins), emitUpeHandler());
@@ -1797,21 +2097,28 @@ void LirEmitter::do_cknullobject(UnarySt
   lirout->insBranch(LIR_jt, eqp0(ptr), emitNpeHandler());
   set_def_ins(instr->value_out(), ptr);
 }
 
 void LirEmitter::do_cktimeout(UnaryStmt* instr) {
   // Omit timeout checks if they are turned off.  We don't do this further
   // upstream, because it can cause the graph to have no endpoints, if there
   // is an infinite loop.
-  if (core->config.interrupts) {
+#ifdef VMCFG_INTERRUPT_SAFEPOINT_POLL
+  bool check_interrupt = true;
+#else
+  bool check_interrupt = core->config.interrupts;
+#endif
+  if (check_interrupt) {
+    lirout->ins0(LIR_savepc);
     LIns* interrupted = ldi(coreAddr, JitFriend::core_interrupted_offset,
                             ACCSET_OTHER, LOAD_VOLATILE);
     LIns* cond = eqi(interrupted, AvmCore::NotInterrupted);
     lirout->insBranch(LIR_jf, cond, emitInterruptHandler());
+    lirout->ins0(LIR_discardpc);
   }
   set_def_ins(instr->value_out(), InsConst(0)); // always return false.
 }
 
 void LirEmitter::do_doubletoint32(UnaryExpr* instr) {
   LIns* arg = def_ins(instr->value_in());
 
   LIns* result;
@@ -1955,46 +2262,33 @@ void LirEmitter::emitHelperCall2(UnaryEx
   LIns* result = callIns(call, 2, coreAddr, def_ins(value_in));
   set_def_ins(instr->value_out(), result);
 }
 
 /// Newstate allocates space on the stack for safepoints
 /// Currently assumes newstate occurs in the entry block prior to any safepoint 
 /// or setlocal instructions.
 void LirEmitter::do_newstate(ConstantExpr* instr) {
-  MethodSignaturep method_signature = signature;
-  int alloc_size = method_signature->frame_size();
-  assert (safepoint_space_ == NULL);
-  assert (safepoint_tags_ == NULL);
-
-  vpc_space_ = stackAlloc(sizeof(int), "safepoint abc pc");
-  safepoint_space_ = stackAlloc(alloc_size << VARSHIFT(cxt->method),
-                                "deopt locals");
-  safepoint_tags_ = stackAlloc(alloc_size * sizeof(SlotStorageType),
-                               "deopt tags");
-  set_def_ins(instr->value(), safepoint_space_);
-  have_safepoints = true;
+  if (safepoint_space_ != NULL) {
+    set_def_ins(instr->value(), safepoint_space_);
+  }
 }
 
 /// Saves the ABC local var, which also represents ABC operand stack values
 /// Stores the local in its native format + type tag. Hopefully
 /// NanoJIT removes the tag stores
 void LirEmitter::do_setlocal(SetlocalInstr* instr) {
   set_def_ins(instr->state_out(), def_ins(instr->state_in()));
   assert (safepoint_space_ != NULL);
   int stackIndex = instr->index;
   emitStore(instr->value_in(), type(instr->value_in()),
             safepoint_space_, stackIndex << VARSHIFT(cxt->method),
             ACCSET_STORE_ANY);
   set_def_ins(instr->state_out(), def_ins(instr->state_in()));
 
-  LIns* sst_model = InsConst(type2sst(type(instr->value_in())));
-  sti(sst_model, safepoint_tags_, 
-      stackIndex * sizeof(int32_t), ACCSET_STORE_ANY);
-
   /// This is really ugly, to directly access J2.
   /// Still contemplating if we should make
   /// deoptGenerator a class or something and make it
   /// walk through the halfmoon IR. Seems overkill for now. Defer
   /// but don't let this stay - Mason
   /// can't put in abcbuilder because type analysis may specialize downstream
   JitManager* jit = JitManager::init(this->pool);
   BailoutData* metaData = jit->ensureMethodData(cxt->method)->bailout_data;
@@ -2002,25 +2296,25 @@ void LirEmitter::do_setlocal(SetlocalIns
   metaData->setNativeType(stackIndex, type2sst(type(instr->value_in())));
 }
 
 /// Generates LIR for a safepoint instruction
 /// Any safepoint instr that gets here cannot be optimized away
 /// Stores the abc pc, setlocals store the actual data prior to this safepoint
 void LirEmitter::do_safepoint(SafepointInstr* instr) {
   assert (safepoint_space_ != NULL);
-  int offset = 0;
-  int abc_pc = instr->vpc;
-  sti(InsConst(abc_pc), vpc_space_, offset, ACCSET_STORE_ANY);
+  int vpc = instr->vpc;
+  // update bytecode ip
+  stp(InsConstPtr((void*)vpc), _save_eip, 0, ACCSET_OTHER);
   
   /// See do_setlocal for why we grab metadata here
   JitManager* jit = JitManager::init(this->pool);
   BailoutData* metaData = jit->ensureMethodData(cxt->method)->bailout_data;
   assert (metaData != NULL);
-  metaData->do_safepoint(abc_pc, instr->scopep, instr->sp);
+  metaData->do_safepoint(vpc, instr->scopep, instr->sp);
 }
 
 // DEOPT
 void LirEmitter::do_deopt_safepoint(DeoptSafepointInstr* instr) {
   lirout->insSafe(LIR_safe, (void*)instr);
 }
 
 // DEOPT
--- a/halfmoon/hm-liremitter.h
+++ b/halfmoon/hm-liremitter.h
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 namespace halfmoon {
 using avmplus::CacheBuilder;
 using avmplus::CallCache;
 using avmplus::GetCache;
@@ -98,22 +98,24 @@ public: // ADAPTER impl
   void do_const(ConstantExpr*);
   // fixme: re-implement-this using finddef_cache void do_abc_finddef(BinaryStmt*);
   void do_loadenv(BinaryExpr*);
   void do_loadinitenv(UnaryExpr*);
   void do_loadsuperinitenv(UnaryExpr*);
   void do_loadenv_interface(BinaryExpr*);
   void do_callmethod(CallStmt2*);
   void do_callinterface(CallStmt2*);
+  void do_callstatic(CallStmt2*);
   void do_newinstance(UnaryExpr*);
   void do_return(StopInstr*);
   void do_arm(ArmInstr*);
   void do_label(LabelInstr*);
   void do_goto(GotoInstr*);
   void do_if(IfInstr*);
+  void do_catchblock(CatchBlockInstr*);
   void do_setslot(CallStmt2*);
   void do_abc_setprop(CallStmt2*);
   void do_abc_callprop(CallStmt2*);
   void do_addd(BinaryExpr* i) { doBinaryInstr(i, LIR_addd); }
   void do_subd(BinaryExpr* i) { doBinaryInstr(i, LIR_subd); }
   void do_muld(BinaryExpr* i) { doBinaryInstr(i, LIR_muld); }
   void do_modulo(BinaryExpr*);
   void do_divd(BinaryExpr* i) { doBinaryInstr(i, LIR_divd); }
@@ -218,36 +220,38 @@ private:
   LIns* emitStore(const Use& value, const Type* constraint, LIns* ptr,
                   int32_t offset, AccSet);
   LIns* emitReturn(const Use& value);
   LIns* emitNpeHandler();
   LIns* emitUpeHandler();
   LIns* emitInterruptHandler();
   LIns* emitHandler(LIns** label, const CallInfo* call);
   LIns* emitLabel(BlockStartInstr*);
+  LIns* emitCatchLabel(CatchBlockInstr*);
   void emitHelperCall2(UnaryExpr*, const CallInfo* call);
   void emitLiveHints(Instr* target);
   void emitLive(Def*);
   void emitStackOverflowCheck();
-  void emitBegin();
+  void emitBegin(bool has_reachable_exceptions);
   void emitInitializers(Def* object);
   void emitStopFence(BlockStartInstr*);
 
 private:
   // other helpers
   void allocateTables();
   void printInstr(Instr*);
   void sortBlocks();
   void analyzeLiveness();
   void patchBailouts();
   bool isFallthruGoto(GotoInstr*);
   bool isFallthruBranch(ArmInstr*);
   bool isFallthruArm(ArmInstr*);
   bool isFallthruLabel(LabelInstr*);
   bool enableSSE();
+  void emitSetPc(DeoptSafepointInstr* instr);
 
 private:
   // helpers to access LIns* associated with each Def*
   LIns* set_def_ins(Def* d, LIns* ins);
   LIns* set_ins(Instr*, LIns* ins);
   LIns* def_ins(const Def*);
   LIns* def_ins(const Use&);
   LIns* ins(Instr*);
@@ -270,26 +274,46 @@ private:
   int max_argc_;
   LoopLiveMap loop_live_; // List of live Instrs for each loop header block.
   LirWriter* traps_lir; // Writer for emitting trap handlers.
   LIns* traps_skip; // LIR_skip that links traps block to main code buffer.
   LIns* npe_label; // label to jump to for null pointer exceptions.
   LIns* upe_label; // label to jump to for undefined pointer exceptions.
   LIns* interrupt_label; // label for interrupt checks.
   LIns* safepoint_space_; // space to store safepoint data.
-  LIns* safepoint_tags_;  // space to store safepoint tags
-  LIns* vpc_space_; // space to store abc pc
   CacheBuilder<GetCache> get_cache_builder;
   CacheBuilder<SetCache> set_cache_builder;
   CacheBuilder<CallCache> call_cache_builder;
   const bool enable_verbose_lir_;
   ProfiledInformation* profiled_info_;
   SeqBuilder<LIns*> bailout_branches_;
   bool have_safepoints;
   bool have_loop_;
+  bool have_catchblocks_;
+  bool emittedBeginCatch;
 
+  LIns *_save_eip, *_ef;
+  const uint8_t* code_pos;
+
+  class CatchBlock {
+   public:
+    CatchBlockInstr* block;
+    LIns* jmp;
+    CatchBlock(CatchBlockInstr* b): block(b), jmp(NULL) {}
+  };
+
+  // This should really be int but gcc-4.2.1 on Mac sometimes screws
+  // up lookups when it's int.
+  HashMap<intptr_t, CatchBlock*> *catchLabels;
+
+  LIns* catch_branch;
+  int lastPcSave;
+    
 private:
   static bool haveStub(InstrKind);
   static const CallInfo lir_table[]; // callinfos for each stub
   static const int stub_fixc[];      // fixed arg count for each stub
+
+  void emitBeginCatch();
+  void emitBeginTry();
 };
 
 }
--- a/halfmoon/hm-main.cpp
+++ b/halfmoon/hm-main.cpp
@@ -1,10 +1,10 @@
 /* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "hm-main.h"
 #ifdef VMCFG_HALFMOON
 #include "profiler/profiler-main.h"
 
@@ -22,17 +22,18 @@ int enable_gml = 0;
 int enable_inline = 0;
 int enable_mode = 0;			// disabled by default, use -Dhalfmoon or env var
 int enable_optional = 1;
 int enable_peephole = 0;
 int enable_printir = 0;
 int enable_profiler = 0;
 int enable_selftest = 0;
 int enable_trace = 0;
-int enable_try = 0;
+int enable_try = 1;
+int enable_framestate = 0;
 int enable_typecheck = 0;
 int enable_verbose = 0;
 int enable_vmstate = 0;
 int enable_welcome = 0;
 ScheduleKind enable_schedule = kScheduleMiddle;
 
 void init() {
   static bool first = true;
@@ -48,16 +49,17 @@ void init() {
   enable_mode = parseEnv("MODE", enable_mode);
   enable_optional = parseEnv("OPTIONAL", enable_optional);
   enable_peephole = parseEnv("PEEPHOLE", enable_peephole);
   enable_printir = parseEnv("PRINTIR", enable_printir);
   enable_profiler = parseEnv("PROFILER", enable_profiler);
   enable_selftest = parseEnv("SELFTEST", enable_selftest);
   enable_trace = parseEnv("TRACE", enable_trace);
   enable_try = parseEnv("TRY", enable_try);
+  enable_framestate = parseEnv("FRAMESTATE", enable_framestate);
   enable_typecheck = parseEnv("TYPECHECK", enable_typecheck);
   enable_verbose = parseEnv("VERBOSE", enable_verbose);
   enable_vmstate = parseEnv("VMSTATE", enable_vmstate);
   enable_welcome = parseEnv("WELCOME", enable_welcome);
   enable_schedule = (ScheduleKind)parseEnv("SCHEDULE", enable_schedule);
 
   debugInit();
 
@@ -139,46 +141,42 @@ void AbcRange::read() {
   pc = next;
   uint32_t imm30, imm30b;
   int32_t imm24, imm8;
   AvmCore::readOperands(next, imm30, imm24, imm30b, imm8);
   if (*pc == OP_lookupswitch)
     next += 3 * (imm30b + 1);
 }
 
-bool hasWith(MethodInfo* m) {
-  AbcRange r(m);
-  AbcRange r2 = find(r, OP_pushwith);
-  return !r2.empty();
-}
-
 bool canCompile(MethodInfo* m) {
   init();
 
   if (enable_mode == kModeNone)
     return false;
 
 #ifdef VMCFG_DEBUGGER
   if (m->pool()->core->debugger())
     return false; // Cannot compile debuggable functions.
 #endif
-  if (m->isNative())
+  if (m->isNative()) {
     return false;
+  }
   if (!enable_builtins && m->pool()->isBuiltin)
     return false; // Ignore builtin code.
   if (!enable_try && m->hasExceptions())
     return false; // Ignore methods with exceptions.
-  if (m->method_id() < 0)
+  if (m->method_id() < 0) {
     return false; // Ignore vm-created initializer methods.
-  if (m->needArguments())
+  }
+  if (m->needArguments()) {
     return false; // Only rest args supported yet.
-  if (!enable_optional && m->hasOptional())
+  }
+  if (!enable_optional && m->hasOptional()) {
     return false;
-  if (hasWith(m))
-    return false; // OP_pushwith not yet supported.
+  }
 
   if (enable_profiler) {
     MethodProfile* profile = JitManager::getProfile(m);
     if (!profile || (!profile->hasBailedOut() && !profile->hasGatheredAllData()))
       return false;
   }
 
   return true;
@@ -267,16 +265,17 @@ void computeIdentities(InstrGraph* ir) {
       for (ArrayRange<Def> d = defRange(instr); !d.empty();) {
         Def* d1 = &d.popFront();
         if (!d1->isUsed())
           continue;
         Def* d2 = peephole(d1, ir, &factory);
         if (k == HR_arm && d1 == d2)
           d2 = unsplit(cast<ArmInstr>(instr), d1, &marks);
         if (d1 != d2) {
+          assert(subtypeof(type(d2), type(d1)));
           copyUses(d1, d2);
           changed = true;
         }
       }
     }
   } while (changed);
   assert(checkPruned(ir) && checkSSA(ir));
 }
@@ -497,35 +496,38 @@ void optimize(Context* cxt, InstrGraph* 
   }
 }
 
 /**
  * Do a depth-first traversal starting from the given block, inserting
  * finished blocks in list, so list ends up in reverse postorder.  Visit either
  * all edges or only normal edges, depending on visit_all param.
  */
-static void dfs(AbcBlock* b, int& post_id, SeqBuilder<AbcBlock*> &list,
-    bool visit_all) {
+static void dfs(AbcBlock* b, int& post_id, bool& has_reachable_exceptions, SeqBuilder<AbcBlock*> &list,
+                bool visit_all) {
   b->post_id = -1;
   // visit ordinary successors.
   for (int i = 0, n = b->num_succ_blocks; i < n; ++i) {
     AbcBlock* succ = b->succ_blocks[i];
     if (!succ->post_id)
-      dfs(succ, post_id, list, visit_all);
+      dfs(succ, post_id, has_reachable_exceptions, list, visit_all);
     else if (succ->post_id == -1)
       succ->dfs_loop = true;
   }
   if (visit_all) {
     // visit catch blocks reachable from this block.
     for (int i = 0, n = b->max_catch_blocks; i < n; ++i) {
       AbcBlock* succ = b->catch_blocks[i];
       if (!succ)
         continue;
+      if (succ->start_types == NULL)  // Unreachable catch block
+        continue;
+      has_reachable_exceptions = true;
       if (!succ->post_id)
-        dfs(succ, post_id, list, visit_all);
+        dfs(succ, post_id, has_reachable_exceptions, list, visit_all);
       else if (succ->post_id == -1)
         succ->dfs_loop = true;
     }
   }
   b->post_id = ++post_id;
   list.insert(b);
 }
 
@@ -535,21 +537,22 @@ InstrGraph* parseAbc(MethodInfo* method,
                      Allocator& ir_alloc, AbcGraph* abc, Toplevel *toplevel,
                      AbcEnv *abc_env, ProfiledInformation* profiled_info,
                      Context &cxt) {
   (void)abc_env; //TODO: change protocol of parseAbc
   // First, sort AbcBlocks in reverse postorder.
   Allocator scratch;
   SeqBuilder<AbcBlock*> list(scratch);
   int post_id = 0;
-  dfs(abc->entry(), post_id, list, false);
+  bool has_reachable_exceptions = false;
+  dfs(abc->entry(), post_id, has_reachable_exceptions, list, true);
 
   // Then visit the AbcBlocks and build an InstrGraph.
   InstrFactory factory(ir_alloc, lattice, infos);
-  AbcBuilder builder(method, abc, &factory, toplevel, profiled_info);
+  AbcBuilder builder(method, abc, &factory, toplevel, profiled_info, has_reachable_exceptions);
   InstrGraph* ir = builder.visitBlocks(list.get());
 
 
   if (enable_gml)
     printAbcCfg(method, list.get(), ir, "0-abc");
 
 #ifdef DEBUG
   if (enable_verbose) {
--- a/halfmoon/hm-main.h
+++ b/halfmoon/hm-main.h
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //
 // This is the main header file for the compiler implementation.
 //
 
@@ -49,27 +49,29 @@ class ConstantExpr;
 class VoidStmt;
 class SafepointInstr;
 class DeoptSafepointInstr;  // DEOPT
 class DeoptFinishInstr;  // DEOPT
 class DeoptFinishCallInstr;  // DEOPT
 class SetlocalInstr;
 class UnaryExpr;
 class UnaryStmt;
+class DebugInstr;
 
 // CFG-related Instrs
 class BlockStartInstr;
 class   ArmInstr;
 class   LabelInstr;
 class BlockEndInstr;
 class   CondInstr;
 class     IfInstr;
 class     SwitchInstr;
 class   GotoInstr;
 class StartInstr;
+class CatchBlockInstr;
 class StopInstr;
 }
 
 namespace profiler {
   class MethodProfile;
   class MethodProfileMgr;
 }
 
@@ -199,16 +201,17 @@ void init();
 // Configuration.
 extern int enable_welcome;    // Print welcome and options.
 extern int enable_verbose;    // Generate verbose output.
 extern int enable_peephole;   // Enable ABC peephole optimizer.
 extern int enable_gml;        // Enable GML graph output.
 extern int enable_vmstate;    // Always generate Safepoint instructions.
 extern int enable_builtins;   // Optimize builtins too.
 extern int enable_try;        // Enable optimizing try/catch functions.
+extern int enable_framestate; // Print the frame state during parsing of abc
 extern int enable_printir;    // Enable printing of final IR
 extern int enable_mode;       // Which execution mode.
 extern int enable_trace;      // Enable execution trace.
 extern int enable_inline;     // Enable inline optimization.
 extern int enable_profiler;   // Enable runtime profiling
 extern int enable_typecheck;  // Enable type-check verbosity.
 extern int enable_optional;   // Enable optional argument support.
 
--- a/halfmoon/hm-models.cpp
+++ b/halfmoon/hm-models.cpp
@@ -84,16 +84,18 @@ InstrKind toModelScriptobject(const Type
 }
 
 InstrKind toModelKind(const Type* val_type, const Type* use_type) {
   switch (model(use_type)) {
     default:
       printf("unknown conversion %s -> %s\n", typeName(val_type),
              typeName(use_type));
       assert(false && "bad model");
+    case kModelNamespace:
+      return HR_atom2ns;
     case kModelAtom:
       return toModelAtom(val_type);
     case kModelScriptObject:
       return toModelScriptobject(val_type);
     case kModelString:
       return HR_atom2string;
     case kModelInt:
       return toModelInt(val_type, use_type);
@@ -149,42 +151,50 @@ void ModelFixer::changeOps() {
   }
 }
 
 /**
  * Insert model conversions after defs, where needed.  If multiple uses
  * need to use the same converted def, reuse the conversion instruction.
  */
 void ModelFixer::fixDefs() {
-  Def* converts[kModelMAX];  // space to memoize one conversion per ModelKind.
+  const int kConvertsMax = 4;
+  Def* converts[kConvertsMax];  // space to memoize one conversion per ModelKind.
+  InstrKind converts_kind[kConvertsMax];  // verify that the conversion is the same
   for (PostorderBlockRange b(ir_); !b.empty();) {
     for (InstrRange i(b.popFront()); !i.empty();) {
       Instr* instr = i.popBack();
       SigRange sr = outputSigRange(instr);
       for (ArrayRange<Def> dr = defRange(instr); !dr.empty(); sr.popFront()) {
         Def* d = &dr.popFront();
-        int have_mask = 0; // mask:  1 << ModelKind
+        int have_count = 0;
         const Type* def_type = type(d);
         const Type* sig_type = sr.front();
         if (isBottom(def_type))
           def_type = sig_type;
         for (UseRange u(*d); !u.empty(); ) {
           Use& use = u.popFront();
           const Type* constraint = getConstraint(use);
           if (!submodelof(def_type, constraint)) {
-            // need a conversion
-            ModelKind need = model(constraint);
-            if (!(have_mask & (1 << need))) {
-              InstrKind convert_kind = toModelKind(def_type, constraint);
+            // need a conversion.  The same model may require different conversions
+            InstrKind convert_kind = toModelKind(def_type, constraint);
+            int h = 0;
+            for (; h < have_count; h++) {
+              if (converts_kind[h] == convert_kind)
+                break;
+            }
+            if (h >= have_count) {
               UnaryExpr* expr = factory_.newUnaryExpr(convert_kind, d);
               ir_->addInstrAfter(instr, expr);
-              converts[need] = expr->value_out();
-              have_mask |= (1 << need);
+              converts[h] = expr->value_out();
+              converts_kind[h] = convert_kind;
+              have_count++;
+              assert(have_count <= kConvertsMax);
             }
-            use = converts[need];
+            use = converts[h];
           }
         }
       }
     }
   }
 }
 
 /// Return the constraint for this use.  Instructions that have Any or
@@ -209,28 +219,26 @@ const Type* ModelFixer::getConstraint(co
         CallStmt2* setslot = cast<CallStmt2>(instr);
         const Type* obj_type = type(setslot->object_in());
         int slot = ordinalVal(type(setslot->param_in()));
         return ir_->lattice.getSlotType(obj_type, slot);
       }
       break;
     case HR_callinterface:
     case HR_callmethod:
+    case HR_callstatic:
       if (use_pos >= 2) {
         CallStmt2* call = cast<CallStmt2>(instr);
         const Type* env_type = type(call->param_in());
         if (isEnv(env_type)) {
           MethodSignaturep sig = getMethod(type(call->param_in()))->getMethodSignature();
           return ir_->lattice.makeParamType(use_pos - 2, sig);
         }
       }
       break;
-    case HR_callstatic:
-      assert(false && "custom signature not implemented");
-      break;
     case HR_return:
       if (use_pos == 1) {
         // return type is based on method signature
         MethodSignaturep sig = cxt_->method->getMethodSignature();
         return ir_->lattice.makeType(sig->returnTraits());
       }
       break;
   }
--- a/halfmoon/hm-models.h
+++ b/halfmoon/hm-models.h
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef HM_MODELS_H_
 #define HM_MODELS_H_
 
 namespace halfmoon {
--- a/halfmoon/hm-prettyprint.cpp
+++ b/halfmoon/hm-prettyprint.cpp
@@ -1,10 +1,10 @@
 /* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "hm-main.h"
 #ifdef VMCFG_HALFMOON
 
 namespace halfmoon {
@@ -535,16 +535,31 @@ void printTerseInstrList(InstrRange list
     i = list.front();
     console << "," << kInstrPrefix << i->id << "-" << name(i);
     if (limit++ > 100)
       console << "printTerseInstrList limit reached";
   }
   console << "]\n";
 }
 
+PrintWriter& printUsers(PrintWriter& console, Instr* instr) {
+  printInstr(console, instr);
+
+  // Print instructions that use any def of this instruction.
+  for (AllUsesRange uses(instr); !uses.empty(); uses.popFront()) {
+    printInstr(console, user(uses.front()));
+  }
+  return console;
+}
+
+void printUsers(Instr* instr) {
+  printUsers(avmplus::AvmCore::getActiveCore()->console, instr);
+}
+
+
 PrintWriter& printInstr(PrintWriter& console, Instr* instr) {
   char namebuf[80];
   sprintf(namebuf, "    %s%-3d %-18.18s  ", kInstrPrefix, instr->id, name(instr));
   console << namebuf;
   print(console, instr);
 
   // Print signature of this instruction: (uses) -> (defs)
   console << "(";
@@ -566,16 +581,36 @@ PrintWriter& printInstr(PrintWriter& con
   if (!uses.empty()) {
     console << " <= " << kInstrPrefix << user(uses.front())->id;
     while (uses.popFront(), !uses.empty())
       console << "," << kInstrPrefix << user(uses.front())->id;
   }
   return console << "\n";
 }
 
+void printInstr(Instr* instr) {
+  printInstr(avmplus::AvmCore::getActiveCore()->console, instr);
+}
+
+PrintWriter& printPhi(PrintWriter& console, LabelInstr* label, int index) {
+  Def* d = &label->params[index];
+  for (LabelArgRange r(label, index); !r.empty(); r.popFront()) {
+    if (def(r.front()) != d) { // ignore self. 
+      printDef(def(r.front())); console << " ";
+      printInstr(user(r.front())); console << "\n";
+    }
+  }
+  printInstr(label);
+  return console;
+}
+
+void printPhi(LabelInstr* label, int index) {
+  printPhi(avmplus::AvmCore::getActiveCore()->console, label, index);
+}
+
 PrintWriter& printCompactInstr(PrintWriter& console, Instr* instr,
                                bool print_defs) {
   char namebuf[80];
   sprintf(namebuf, "    %s%-3d %-18.18s  ", kInstrPrefix, instr->id,
           name(instr));
   console << namebuf;
   print(console, instr);
   if (kind(instr) == HR_const) {
@@ -590,16 +625,22 @@ PrintWriter& printCompactInstr(PrintWrit
     if (print_defs)
       for (ArrayRange<Def> d = defRange(instr); !d.empty(); d.popFront())
         //if (!isEffect(type(d.front())) && !isBottom(type(d.front())))
           console << typeName(d.front()) << delim(d);
   }
   return console << "\n";
 }
 
+void printCompactInstr(Instr* instr,
+                       bool print_defs) {
+  printCompactInstr(avmplus::AvmCore::getActiveCore()->console, instr, print_defs);
+}
+
+
 const char* label(BlockStartInstr* b) {
   return kind(b) == HR_label ? kJoinPrefix : kBlockPrefix;
 }
 
 BlockStartInstr* idom(BlockStartInstr* b, DominatorTree* doms) {
   return doms->hasIDom(b) ? doms->idom(b) : 0;
 }
 
@@ -622,16 +663,20 @@ void listCfg(PrintWriter& console, Instr
       console << label(r.front()) << r.front()->blockid << delim(r);
     console << "} rdom={";
     for (BlockStartInstr* dom = idom(block, rdoms); dom != 0; dom = idom(dom, rdoms))
       console << label(dom) << dom->blockid << (rdoms->hasIDom(dom) ? "," : "");
     console << "} rdf={";
     for (SeqRange<BlockStartInstr*> r(rdoms->df(block)); !r.empty(); r.popFront())
       console << label(r.front()) << r.front()->blockid << delim(r);
     console << "}\n";
+    if (kind(block) == HR_catchblock) {
+      ((CatchBlockInstr*)block)->printCatchPreds();
+    }
+
     for (InstrRange j(block); !j.empty(); j.popFront(), ++instr_count)
       printInstr(console, j.front());
   }
   console << "=== IR end size=" << ir->instr_count() <<
       " actual=" << instr_count << " ===\n\n";
 #else
   (void) console;
   (void) ir;
@@ -672,16 +717,21 @@ void printAbcInstr(MethodInfo* m, PrintW
   console << "\n";
 }
 
 void printDef(PrintWriter& out, const Def* d) {
   out << kDefPrefix << definerId(d);
   printDef(out, *d);
 }
 
+void printDef(const Def* d) {
+  printDef(avmplus::AvmCore::getActiveCore()->console, d);
+  fflush(stdout);
+}
+
 class PrintAdapter: public ShapeAdapter<PrintAdapter, void> {
  public:
   explicit PrintAdapter(PrintWriter& out) : out_(out) {
   }
 
   void do_default(Instr* /* instr */) {
   }
 
@@ -736,17 +786,18 @@ class PrintDefAdapter: public ShapeAdapt
   void do_LabelInstr(LabelInstr*) { doParam(); }
   void do_ArmInstr(ArmInstr*) { doParam(); }
   void do_UnaryStmt(UnaryStmt*) { doStmt(); }
   void do_BinaryStmt(BinaryStmt*) { doStmt(); }
   void do_CallStmt1(CallStmt1*) { doStmt(); }
   void do_CallStmt2(CallStmt2*) { doStmt(); }
   void do_CallStmt3(CallStmt3*) { doStmt(); }
   void do_CallStmt4(CallStmt4*) { doStmt(); }
-  void do_NaryStmt(NaryStmt0*) { doStmt(); }
+  void do_NaryStmt0(NaryStmt0*) { doStmt(); }
+  void do_NaryStmt2(NaryStmt2*) { doStmt(); }
 
   void do_Hasnext2Stmt(Hasnext2Stmt*) {
     static const char* names[] = { "effect", "value", "counter", "object" };
     out << names[pos(d)];
   }
 
  private:
   void doParam() {
@@ -764,16 +815,20 @@ class PrintDefAdapter: public ShapeAdapt
   const Def& d;
 };
 
 void printDef(PrintWriter& out, const Def& d) {
   PrintDefAdapter a(out, d);
   do_shape(&a, definer(d));
 }
 
+void printDef(const Def& d) {
+  printDef(avmplus::AvmCore::getActiveCore()->console, d);
+}
+
 const char* name(Instr* instr) {
   return name(*instr);
 }
 
 const char* name(Instr& instr) {
   return instr.info->name();
 }
 
@@ -801,12 +856,39 @@ void printLirCfg(MethodInfo* method, Fra
   LirReader reader(frag->lastIns);
   CfgLister lister(&reader, alloc, CfgLister::CFG_BB);
   for (LIns* ins = lister.read(); !ins->isop(LIR_start); ins = lister.read()) {
   }
   lister.printGmlCfg(f, frag->lirbuf->printer, &ignore);
   fclose(f);
 }
 
+
+  void printString(Stringp string) {
+    StUTF8String buf(string);
+    fputs(buf.c_str(), stdout);
+    // flush in case debugger output is interleaved with process output
+    fflush(stdout);
+  }
+
+  void printAtom(Atom atom) {
+    StringBuffer buf(avmplus::AvmCore::getActiveCore());
+    buf.writeAtom(atom);
+    fputs(buf.c_str(), stdout);
+    // flush in case debugger output is interleaved with process output
+    fflush(stdout);
+  }
+  
+  void printMethod(MethodInfo* info) {
+    String* name = info->getMethodName();
+    if (!name) return;
+    StUTF8String buf(info->getMethodName());
+    fputs(buf.c_str(), stdout);
+    // flush in case debugger output is interleaved with process output
+    fflush(stdout);
+  }
+
+
+
 #endif
 
 } // namespace avmplus
 #endif // VMCFG_HALFMOON
--- a/halfmoon/hm-prettyprint.h
+++ b/halfmoon/hm-prettyprint.h
@@ -1,26 +1,28 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef HM_PRETTYPRINT_H_
 #define HM_PRETTYPRINT_H_
 
 namespace halfmoon {
 
 /// Print the details of one instruction.
 ///
 PrintWriter& printInstr(PrintWriter&, Instr*);
+void printInstr(Instr*);
 
 /// Print the brief essentials of one instruction.
 ///
 PrintWriter& printCompactInstr(PrintWriter&, Instr*, bool print_defs = true);
+void printCompactInstr(Instr*, bool print_defs = true);
 
 /**
  * Print list of instructions in detail.
  * @param list head of list of instructions.
  * @param ir instruction graph
  * @param title legend printed at head of list.
  * A correctly formed list is circular and the end of the list (and printout) is when the next instruction
  * points back at the list head. To prevent debug code from looping when confronted with incorrect lists there
@@ -84,20 +86,22 @@ extern const char* kBlockPrefix;    // P
 extern const char* kLoopPrefix;     // Prefix for a loop header.
 extern const char* kJoinPrefix;     // Prefix for a non-loop merge block.
 extern const char* kInstrPrefix;    // Prefix for an instruction.
 extern const char* kDefPrefix;      // Prefix for a def.
 
 /// Print the def as kDefPrefix + owner->id + field name.
 ///
 void printDef(PrintWriter& out, const Def* def);
+void printDef(const Def* def);
 
 /// Print just the name of the def.  fixme: field is a historical term, change it.
 ///
 void printDef(PrintWriter& out, const Def& ref);
+void printDef(const Def& ref);
 
 /// Return the pretty-print name of this instruction's opcode.
 ///
 const char* name(Instr*);
 const char* name(Instr&);
 
 /// Return the name of this Def's type.
 ///
--- a/halfmoon/hm-profiler.cpp
+++ b/halfmoon/hm-profiler.cpp
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "hm-main.h"
 #ifdef VMCFG_HALFMOON
 
 namespace halfmoon {
--- a/halfmoon/hm-profiler.h
+++ b/halfmoon/hm-profiler.h
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef OPT_PROFILER_HH
 #define OPT_PROFILER_HH
 
 namespace halfmoon {
--- a/halfmoon/hm-schedulers.cpp
+++ b/halfmoon/hm-schedulers.cpp
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "hm-main.h"
 #ifdef VMCFG_HALFMOON
 
 namespace halfmoon {
--- a/halfmoon/hm-schedulers.h
+++ b/halfmoon/hm-schedulers.h
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef SCHEDULERS_H_
 #define SCHEDULERS_H_
 
 namespace halfmoon {
--- a/halfmoon/hm-specializer.cpp
+++ b/halfmoon/hm-specializer.cpp
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "hm-main.h"
 #ifdef VMCFG_HALFMOON
 
 namespace halfmoon {
@@ -136,29 +136,35 @@ Def* matchUnaryExpr(const Use& v, InstrK
 /// Helper: if definer of v is a UnaryExpr k(x), return x
 ///
 Def* matchUnaryExpr(const Use& v, InstrKind k) {
   assert(InstrFactory::isUnaryExpr(k));
   Instr* i = definer(v);
   return kind(i) == k ? def(cast<UnaryExpr>(i)->value_in()) : 0;
 }
 
-ScopeKind findScope(Lattice* lattice_, NaryStmt2* instr, int* index_) {
+ScopeKind findScope(Lattice* lattice_, NaryStmt3* instr, int* index_) {
   assert(kind(instr) == HR_abc_findproperty ||
          kind(instr) == HR_abc_findpropstrict);
   const Type* name_type = type(instr->name_in());
   assert(isConst(name_type) && isName(name_type));
   int scope_count = instr->vararg_count();
   Def* env = def(instr->env_in());
   const Use* scopes = instr->varargs();
   MethodInfo* caller_method = getMethod(type(env));
   PoolObject* pool = caller_method->pool();
   if (!nameVal(name_type)->isBinding())
     return kScopeNotFound; // not an ordinary known lexical name.
 
+  // Don't short circuit dynamic lookups
+  assert(isOrdinal(type(instr->index_in())));
+  int withbase = ordinalVal(type(instr->index_in()));
+  if (withbase != -1)
+    return kScopeNotFound;
+
   // Search local scopes.
   const ScopeTypeChain* scope = caller_method->declaringScope();
   int base = (scope->size == 0) ? 1 : 0; // Don't try to early bind to global.
   for (int index = scope_count - 1; index >= base; --index) {
     Binding b = lattice_->toBinding(type(scopes[index]), name_type);
     if (isValidBinding(b))
       return (*index_ = index), kScopeLocal;
     if (false /* fixme: if is with scope */)
@@ -518,30 +524,35 @@ void Specializer::do_construct(CallStmt2
                                           def(instr->arg(i)));
   CallStmt2* call = factory_.newCallStmt2(HR_callmethod, builder.effect(),
                                           env, object, arg_count - 1, args + 1);
   builder.addStmt(call);
   connectUsesToDef(*instr->value_out(), object);
   connectUsesToDef(*instr->effect_out(), call->effect_out());
 }
 
-void Specializer::doFindStmt(NaryStmt2* instr) {
+void Specializer::doFindStmt(NaryStmt3* instr) {
+  (void)instr;
+#if 0
+  // Can't in place replace an NaryStmt2 with a BinaryStmt, so just
+  // disable this for now.  It will be deleted anyway.
   int index;
   switch (findScope(lattice_, instr, &index)) {
     case kScopeOuter: {
       SpecialBuilder builder(ir_, &factory_, instr, instr->effect_in());
       instr->name_in() = builder.addConst(lattice_->makeOrdinalConst(index));
       factory_.toBinaryStmt(HR_findprop2getouter, instr);
       assert(def(instr->effect_in()) == builder.effect());
       break;
     }
     case kScopeDomain:
       factory_.toBinaryStmt(HR_findprop2finddef, instr);
       break;
   }
+#endif
 }
 
 void Specializer::do_toint(UnaryStmt* instr) {
   const Type* t = type(instr->value_in());
   if (isUInt(t))
     factory_.toUnaryStmt(HR_uinttoint, instr);
   else if (isNumber(t))
     factory_.toUnaryStmt(HR_numbertoint, instr);
@@ -707,97 +718,10 @@ void Specializer::removeSpeculate(Binary
     Def* state = def(instr->rhs_in());
 	  (void) state;
     assert (isState(type(state)));
     assert (kind(definer(state)) == HR_safepoint);
     connectUsesToDef(*instr->value_out(), lhs);
   }
 }
 
-/***
- * Is there a cleaner way to find out if an instr is a speculative use
- * other than a switch statement? Error prone to add a speculate_instr
- * and forget to modify this method. Otherwise, if we add a speculate_instr
- * and don't add this, the safepoint gets eliminated
- */
-bool Specializer::isSpeculate(Instr* instr) {
-  switch (kind(instr)) {
-  case HR_speculate_int:
-  case HR_speculate_number:
-  case HR_speculate_string:
-  case HR_speculate_object:
-  case HR_speculate_array:
-  case HR_speculate_bool:
-    return true;
-  default:
-    return false;
-  }
-}
-
-bool Specializer::haveSpeculateUses(SafepointInstr* instr) {
-  for (AllUsesRange u(instr); !u.empty(); u.popFront()) {
-    bool is_speculate = isSpeculate(user(u.front()));
-    if (is_speculate) {
-      return true;
-    }
-  }
-
-  return false;
-}
-
-/***
- * DANGER DANGER DANGER:
- * We're assuming setlocal and safepoints are used ONLY BY SPECULATIVE
- * optimizations. We do not support exceptions.
- * Check if we're being used by a speculate opcode
- * if not, remove this safepoint
- * We assume we have no other reasons to see a set local or safepoint
- */
-void Specializer::do_safepoint(SafepointInstr* instr) {
-  if (!haveSpeculateUses(instr)) {
-    // We assume safepoints occur because abcbuilder emitted a setlocal
-    // for every stack slot, then this safepoint. eliminate this safepoint
-    connectUsesToDef(*instr->state_out(), def(instr->state_in()));
-    connectUsesToDef(*instr->effect_out(), def(instr->effect_in()));
-  }
-}
-
-/***
- * DANGER, READ DO_safepoint text
- * Check the previous state. If the setlocal in a previous state sets the local
- * to the same value, as us, delete us.
- * Only make a local decision, we don't check all possible paths at arms 
- * May need a global setlocal optimization if this isn't good enough
- */
-void Specializer::do_setlocal(SetlocalInstr* instr) {
-  Instr* state_in = definer(instr->state_in());
-  int original_index = instr->index;
-  bool have_safepoint = false;
-
-  while(kind(state_in) == HR_setlocal || kind(state_in) == HR_safepoint) {
-    if (kind(state_in) == HR_setlocal) {
-      SetlocalInstr* setlocal = cast<SetlocalInstr>(state_in);
-      state_in = definer(setlocal->state_in());
-
-      if (setlocal->index == original_index) {
-        if (def(setlocal->value_in()) == def(instr->value_in())) {
-          // instr setlocals the same value as a previous setlocal
-          connectUsesToDef(*instr->state_out(), def(instr->state_in()));
-        } else if (!have_safepoint) {
-          // instr setlocal to the same index as a previous setlocal
-          // without a safepoint in between. Delete the previous setlocal
-          // since it has no effect.
-          // DANGER: kind of ugly to delete a previous setlocal...
-          // further downstream rather than deleting it when visiting
-          // the previous setlocal.
-          connectUsesToDef(*setlocal->state_out(), def(setlocal->state_in()));
-        }
-      }
-    } else {
-      SafepointInstr* safepoint = cast<SafepointInstr>(state_in);
-      state_in = definer(safepoint->state_in());
-      have_safepoint = true;
-    }
-  }
-}
-
 } // namespace halfmoon
 #endif // VMCFG_HALFMOON
--- a/halfmoon/hm-specializer.h
+++ b/halfmoon/hm-specializer.h
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef SPECIALIZER_H_
 #define SPECIALIZER_H_
 
 namespace halfmoon {
@@ -16,17 +16,17 @@ enum ScopeKind {
   kScopeDomain // use finddef.
 };
 
 /**
  * Analyzer for lexical binding of findproperty opcodes.  This function will
  * search for a binding, then return a ScopeKind result and parameter depending
  * on the kind.
  */
-ScopeKind findScope(Lattice*, NaryStmt2*, int* index);
+ScopeKind findScope(Lattice*, NaryStmt3*, int* index);
 
 /// Analyze property bindings.
 ///
 struct CallAnalyzer {
   CallAnalyzer(const Type* obj_type, const Type* name_type,
                    Lattice* lattice, int extra_argc);
   Binding binding;
   MethodSignaturep signature;
@@ -43,18 +43,18 @@ struct CallAnalyzer {
 class Specializer: public KindAdapter<Specializer, void> {
 public:
   Specializer(InstrGraph* ir);
 
   void specialize(Instr*);
 
 public: // dispatch() adapter methods.
   void do_default(Instr*) { }
-  void do_abc_findproperty(NaryStmt2* i) { return doFindStmt(i); }
-  void do_abc_findpropstrict(NaryStmt2* i) { return doFindStmt(i); }
+  void do_abc_findproperty(NaryStmt3* i) { return doFindStmt(i); }
+  void do_abc_findpropstrict(NaryStmt3* i) { return doFindStmt(i); }
   void do_toint(UnaryStmt*);
   void do_touint(UnaryStmt*);
   void do_toboolean(UnaryExpr*);
   void do_coerce(BinaryStmt*);
   void do_construct(CallStmt2*);
   void do_abc_callprop(CallStmt2*);
   void do_abc_constructprop(CallStmt2*);
   void do_constructsuper(CallStmt2*);
@@ -74,23 +74,21 @@ public: // dispatch() adapter methods.
   void do_doubletoint32(UnaryExpr*);
   void do_if(IfInstr*);
   void do_speculate_number(BinaryExpr* i) { removeSpeculate(i); }
   void do_speculate_int(BinaryExpr* i) { removeSpeculate(i); }
   void do_speculate_object(BinaryExpr* i) { removeSpeculate(i); }
   void do_speculate_string(BinaryExpr* i) { removeSpeculate(i); }
   void do_speculate_numeric(BinaryExpr* i) { removeSpeculate(i); }
   void do_speculate_bool(BinaryExpr* i) { removeSpeculate(i); }
-  void do_setlocal(SetlocalInstr*);
-  void do_safepoint(SafepointInstr*);
 
 private:
   void doCompare(BinaryExpr*, InstrKind int_kind, InstrKind uint_kind,
                  InstrKind double_kind);
-  void doFindStmt(NaryStmt2*);
+  void doFindStmt(NaryStmt3*);
   bool specializeArithmetic(InstrKind k1, InstrKind k2, UnaryExpr* instr);
   InstrKind getLoadEnvKind(const Type* object, bool is_interface = false);
   bool specializeSlotCallProp(CallStmt2* instr, CallAnalyzer* call_analyzer);
   bool specializeMethodCallProp(CallStmt2* instr, CallAnalyzer* call_analyzer);
   void removeSpeculate(BinaryExpr* instr);
   bool haveSpeculateUses(SafepointInstr*);
   bool isSpeculate(Instr* instr);
 
--- a/halfmoon/hm-stubs.cpp
+++ b/halfmoon/hm-stubs.cpp
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "hm-main.h"
 #ifdef VMCFG_HALFMOON
 
 namespace avmplus {
@@ -98,16 +98,20 @@ MethodEnv* Stubs::do_loadenv_boolean(Met
 MethodEnv* Stubs::do_loadinitenv(MethodFrame*, ScriptObject* object) {
   return object->vtable->init;
 }
 
 MethodEnv* Stubs::do_loadsuperinitenv(MethodFrame*, MethodEnv* env) {
   return JitFriend::superInitEnv(env);
 }
 
+MethodEnv* Stubs::do_loadenv_env(avmplus::MethodFrame*, int method_id, avmplus::MethodEnv* env) {
+  return env->abcEnv()->getMethod(method_id);
+}
+
 ScriptObject* Stubs::do_newinstance(MethodFrame*, ClassClosure* c) {
   return c->vtable->createInstanceProc(c);
 }
 
 Atom Stubs::do_toprimitive(MethodFrame*, Atom x) {
   return AvmCore::primitive(x);
 }
 
@@ -676,16 +680,21 @@ String* Stubs::do_atom2string(MethodFram
   return (String*)atomPtr(x);
 }
 
 BoolKind Stubs::do_atom2bool(MethodFrame*, Atom x) {
   assert(AvmCore::isBoolean(x));
   return BoolKind(x == trueAtom);
 }
 
+Namespace* Stubs::do_atom2ns(MethodFrame*, Atom x) {
+  assert(AvmCore::isNamespace(x) || AvmCore::isNull(x));
+  return (Namespace*)atomPtr(x);
+}
+
 int Stubs::do_atom2int(MethodFrame*, Atom x) {
   return AvmCore::integer_i(x);
 }
 
 uint32_t Stubs::do_atom2uint(MethodFrame*, Atom x) {
   return AvmCore::integer_u(x);
 }
 
@@ -750,93 +759,123 @@ Atom Stubs::do_getouterscope(MethodFrame
 }
 
 /// DEOPT
 /// FIXME: This stub should not be required, though it's not clear how to
 /// convince templates.py of this.  Note that no stubs are generated for
 /// HM_deopt_safepoint and HM_deopt_finishcall.
 void Stubs::do_deopt_finish(MethodFrame*) {}
 
+// debugline: (Effect, Int) -> Effect
+void Stubs::do_debugline(MethodFrame*, int32_t line) {
+  (void)line;
+}
+  
+// debugfile: (Effect, String) -> Effect
+void Stubs::do_debugfile(MethodFrame*, String*) {}
+
 inline Atom abc_find(MethodEnv* env, const Multiname* name, Atom* scopes,
-                     int scope_count, bool strict) {
+                     int scope_count, bool strict, Atom* withbase) {
   // fixme: handle withbase
-  return env->findproperty(env->scope(), scopes, scope_count, name, strict, 0);
+  return env->findproperty(env->scope(), scopes, scope_count, name, strict, withbase);
 }
 
 inline Atom abc_findx(MethodEnv* env, const Multiname* name, Atom index,
-                      Atom* scopes, int scope_count, bool strict) {
+                      Atom* scopes, int scope_count, bool strict, Atom* withbase) {
   // fixme: handle withbase
   Multiname tempname;
   initnamex(env->core(), name, index, &tempname);
   return env->findproperty(env->scope(), scopes, scope_count, &tempname,
-                           strict, 0);
+                           strict, withbase);
 }
 
 inline Atom abc_findns(MethodEnv* env, const Multiname* name, Atom ns,
-                       Atom* scopes, int scope_count, bool strict) {
+                       Atom* scopes, int scope_count, bool strict, Atom* withbase) {
   // fixme: handle withbase
   Multiname tempname;
   initnamens(env, name, ns, &tempname);
   return env->findproperty(env->scope(), scopes, scope_count, &tempname,
-                           strict, 0);
+                           strict, withbase);
 }
 
 inline Atom abc_findnsx(MethodEnv* env, const Multiname* name, Atom ns,
-                       Atom index, Atom* scopes, int scope_count, bool strict) {
+                       Atom index, Atom* scopes, int scope_count, bool strict, Atom* withbase) {
   // fixme: handle withbase
   Multiname tempname;
   initnamensx(env, name, ns, index, &tempname);
   return env->findproperty(env->scope(), scopes, scope_count, &tempname,
-                           strict, 0);
+                           strict, withbase);
 }
 
 Atom Stubs::do_abc_findproperty(MethodFrame*, const Multiname* name,
-                                MethodEnv* env, int scope_count, Atom* scopes) {
-  return abc_find(env, name, scopes, scope_count, false);
+                                MethodEnv* env, int32_t withbase, int scope_count, Atom* scopes) {
+  assert(withbase >= -1 && withbase < scope_count);
+  Atom* withbasep = (withbase == -1) ? NULL : scopes + withbase;
+  return abc_find(env, name, scopes, scope_count, false, withbasep);
 }
 
 Atom Stubs::do_abc_findpropstrict(MethodFrame*, const Multiname* name,
-                                MethodEnv* env, int scope_count, Atom* scopes) {
-  return abc_find(env, name, scopes, scope_count, true);
+                                MethodEnv* env, int32_t withbase, int scope_count, Atom* scopes) {
+  assert(withbase >= -1 && withbase < scope_count);
+  Atom* withbasep = (withbase == -1) ? NULL : scopes + withbase;
+  return abc_find(env, name, scopes, scope_count, true, withbasep);
 }
 
 Atom Stubs::do_abc_findpropertyx(MethodFrame*, const Multiname* name,
-                                 MethodEnv* env, Atom index, int scope_count,
+                                 MethodEnv* env, int32_t withbase, Atom index, int scope_count,
                                  Atom* scopes) {
-  return abc_findx(env, name, index, scopes, scope_count, false);
+  assert(withbase >= -1 && withbase < scope_count);
+  Atom* withbasep = (withbase == -1) ? NULL : scopes + withbase;
+  return abc_findx(env, name, index, scopes, scope_count, false, withbasep);
 }
 
 Atom Stubs::do_abc_findpropstrictx(MethodFrame*, const Multiname* name,
-                                   MethodEnv* env, Atom index, int scope_count,
+                                   MethodEnv* env, int32_t withbase, Atom index, int scope_count,
                                    Atom* scopes) {
-  return abc_findx(env, name, index, scopes, scope_count, true);
+  assert(withbase >= -1 && withbase < scope_count);
+  Atom* withbasep = (withbase == -1) ? NULL : scopes + withbase;
+  return abc_findx(env, name, index, scopes, scope_count, true, withbasep);
 }
 
 Atom Stubs::do_abc_findpropertyns(MethodFrame*, const Multiname* name,
-                                  MethodEnv* env, Atom ns, int scope_count,
+                                  MethodEnv* env, int32_t withbase, Atom ns, int scope_count,
                                   Atom* scopes) {
-  return abc_findns(env, name, ns, scopes, scope_count, false);
+  assert(withbase >= -1 && withbase < scope_count);
+  Atom* withbasep = (withbase == -1) ? NULL : scopes + withbase;
+  return abc_findns(env, name, ns, scopes, scope_count, false, withbasep);
 }
 
 Atom Stubs::do_abc_findpropstrictns(MethodFrame*, const Multiname* name,
-                                    MethodEnv* env, Atom ns, int scope_count,
+                                    MethodEnv* env, int32_t withbase, Atom ns, int scope_count,
                                     Atom* scopes) {
-  return abc_findns(env, name, ns, scopes, scope_count, true);
+  assert(withbase >= -1 && withbase < scope_count);
+  Atom* withbasep = (withbase == -1) ? NULL : scopes + withbase;
+  return abc_findns(env, name, ns, scopes, scope_count, true, withbasep);
 }
 
 Atom Stubs::do_abc_findpropertynsx(MethodFrame*, const Multiname* name,
-                                   MethodEnv* env, Atom ns, Atom index,
+                                   MethodEnv* env, int32_t withbase, Atom ns,
                                    int scope_count, Atom* scopes) {
-  return abc_findnsx(env, name, ns, index, scopes, scope_count, false);
+  assert(withbase >= -1 && withbase < scope_count);
+  Atom* withbasep = (withbase == -1) ? NULL : scopes + withbase;
+  Atom index = *scopes;
+  scopes++;
+  scope_count--;
+  return abc_findnsx(env, name, ns, index, scopes, scope_count, false, withbasep);
 }
 
 Atom Stubs::do_abc_findpropstrictnsx(MethodFrame*, const Multiname* name,
-                                     MethodEnv* env, Atom ns, Atom index,
+                                     MethodEnv* env, int32_t withbase, Atom ns,
                                      int scope_count, Atom* scopes) {
-  return abc_findnsx(env, name, ns, index, scopes, scope_count, true);
+  assert(withbase >= -1 && withbase < scope_count);
+  Atom* withbasep = (withbase == -1) ? NULL : scopes + withbase;
+  Atom index = *scopes;
+  scopes++;
+  scope_count--;
+  return abc_findnsx(env, name, ns, index, scopes, scope_count, true, withbasep);
 }
 
 BoolKind Stubs::do_abc_strictequals(MethodFrame*, Atom x, Atom y) {
   return boolKind(AvmCore::stricteq(x, y) == trueAtom);
 }
 
 ArrayObject* Stubs::do_newarray(MethodFrame* f, int count, Atom* values) {
   return avmplus::newarray(toplevel(f), count, values);
@@ -1277,15 +1316,18 @@ String* Stubs::do_abc_esc_xattr(MethodFr
   return core(f)->EscapeAttributeValue(value);
 }
 
 Atom Stubs::do_ckfilter(MethodFrame* f, Atom value) {
   env(f)->checkfilter(value);
   return value;
 }
 
-ScriptObject* Stubs::do_newcatch(MethodFrame*, Traits*) { assert(false && "newcatch not implemented"); return 0; }
+Atom Stubs::do_newcatch(MethodFrame* f, Traits* traits) {
+  return env(f)->newcatch(traits)->atom();
+}
+
 Traits* Stubs::do_slottype(MethodFrame*, ScriptObject*, int) { assert(false && "slottype not implemented"); return 0; }
 int Stubs::do_toslot(MethodFrame*, ScriptObject*, const Multiname*) { assert(false && "toslot not implemented"); return 0; }
 void Stubs::do_never(MethodFrame*) { assert(false && "never not implemented"); }
 
 } // namespace halfmoon
 #endif // #ifdef VMCFG_HALFMOON
--- a/halfmoon/hm-templatebuilder.cpp
+++ b/halfmoon/hm-templatebuilder.cpp
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "hm-main.h"
 #ifdef VMCFG_HALFMOON
 #include "hm-templatebuilder.h"
 
--- a/halfmoon/hm-templatebuilder.h
+++ b/halfmoon/hm-templatebuilder.h
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef TEMPLATE_BUILDER_H_
 #define TEMPLATE_BUILDER_H_
 
 namespace halfmoon {
--- a/halfmoon/hm-typeanalyzer.cpp
+++ b/halfmoon/hm-typeanalyzer.cpp
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "hm-main.h"
 #ifdef VMCFG_HALFMOON
 
 namespace halfmoon {
@@ -34,16 +34,17 @@ void TypeAnalyzer::coerceOutputModels(In
 
 void TypeAnalyzer::computeTypes(Instr* instr) {
   // TODO consider carefully replacing the switch with the default case.
   // the effect will be the same, but masking off block starts because 
   // their params start with no type points to a deeper issue with where
   // in an instr's lifetime computeTypes() should be called.
   switch (kind(instr)) {
     case HR_goto:
+    case HR_catchblock:
     case HR_label:
     case HR_arm:
       break;
     default: {
       if (numUses(instr) > 0) {
         // For ordinary instructions, any UN input gives UN results.
         for (ArrayRange<Use> r = useRange(instr); !r.empty(); r.popFront())
           if (isBottom(type(r.front()))) {
@@ -146,16 +147,26 @@ void TypeAnalyzer::do_callmethod(CallStm
     if (callee) {
       Traits* return_traits = callee->getMethodSignature()->returnTraits();
       return setStmtType(instr, lattice_->makeType(return_traits));
     }
   }
   return do_default(instr);
 }
 
+/// if we can resolve the called method, set
+/// data result type to the resolved method's
+/// return type.
+///
+void TypeAnalyzer::do_callstatic(CallStmt2* instr) {
+  MethodInfo* method = getMethod(type(instr->param_in()));
+  const Type* return_type = lattice_->makeType(method->getMethodSignature()->returnTraits());
+  return setStmtType(instr, return_type);
+}
+
 /// Always set the output type to the interface method signature
 void TypeAnalyzer::do_callinterface(CallStmt2* instr) {
   MethodInfo* method = getMethod(type(instr->param_in()));
   const Type* return_type = lattice_->makeType(method->getMethodSignature()->returnTraits());
   return setStmtType(instr, return_type);
 }
 
 /// if we have a constant dispatch id and
@@ -200,16 +211,26 @@ void TypeAnalyzer::do_loadsuperinitenv(U
   MethodInfo* info_in = getMethod(env_in);
   if (info_in) {
     MethodInfo* info_out = info_in->declaringTraits()->base->init;
     return setType(instr->value_out(), lattice_->makeEnvType(info_out));
   }
   do_default(instr);
 }
 
+void TypeAnalyzer::do_loadenv_env(BinaryExpr* instr) {
+  const Type* disp_id_type = type(instr->lhs_in());
+  assert(isConst(disp_id_type));
+  int disp_id = ordinalVal(disp_id_type);
+
+  MethodInfo* caller = getMethod(type(instr->rhs_in()));
+  MethodInfo* callee = caller->pool()->getMethodInfo(disp_id);
+  return setType(instr->value_out(), lattice_->makeEnvType(callee));
+}
+
 // adapters to convert C++ bool to BoolKind into and out of stubs
 inline BoolKind b2k(bool b) { return b ? kBoolTrue : kBoolFalse; }
 inline bool k2b(BoolKind k) { return k == kBoolTrue; }
 
 const Type* callstub(Lattice* l, const Type* c, Stub_I_I stub) {
   return l->makeIntConst(stub(0, intVal(c)));
 }
 const Type* callstub(Lattice* l, const Type* c, Stub_I_U stub) {
@@ -433,17 +454,17 @@ void TypeAnalyzer::do_slottype(BinaryExp
   }
   // Unknown slot number.
   do_default(instr);
 }
 
 /// if we find a local definition, set data result type
 /// to its type.
 ///
-void TypeAnalyzer::doFindInstr(NaryStmt2* instr) {
+void TypeAnalyzer::doFindInstr(NaryStmt3* instr) {
   int index;
   if (findScope(lattice_, instr, &index) == kScopeLocal)
     return setStmtType(instr, type(instr->vararg(index)));
   do_default(instr);
 }
 
 void TypeAnalyzer::do_abc_add(BinaryStmt* instr) {
   const Type* string_nn_type = lattice_->string_type[kTypeNotNull];
@@ -538,17 +559,22 @@ void TypeAnalyzer::do_getouterscope(Bina
     const Type* t = lattice_->getOuterScopeType(caller, ordinalVal(ord_type));
     return setType(instr->value_out(), t);
   }
   do_default(instr);
 }
 
 void TypeAnalyzer::do_cknull(UnaryStmt* instr) {
   const Type* value = type(instr->value_in());
-  return setStmtType(instr, lattice_->makeNotNull(value));
+  const Type* not_null = lattice_->makeNotNull(value);
+  if (isBottom(not_null)) {
+      // Don't allow this type to go to bottom, so make up something wide enough to hold it
+      return setStmtType(instr, lattice_->makeNotNull(lattice_->makeUnion(value, lattice_->atom_type[kTypeNullable])));
+  }
+  return setStmtType(instr, not_null);
 }
 
 void TypeAnalyzer::do_newactivation(UnaryStmt* instr) {
   // fixme: opcode table says these can throw, but can they?
   const Type* env = type(instr->value_in());
   MethodInfo* method = getMethod(env);
   if (method)
     return setStmtType(instr, lattice_->makeType(method->activationTraits(), false));
--- a/halfmoon/hm-typeanalyzer.h
+++ b/halfmoon/hm-typeanalyzer.h
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef TYPEANALYZER_H_
 #define TYPEANALYZER_H_
 
 namespace halfmoon {
@@ -52,32 +52,35 @@ public:
   ///
   void computeTypes(Instr*);
 
 public: // dispatch() adapter methods.
   void do_default(Instr*);
   void doTemplateInstr(Instr*);
   void do_start(StartInstr*) { } // has root defs only
   void do_template(StartInstr*) { } // has root defs only
+  void do_catchblock(CatchBlockInstr*) { } // has root defs only
   void do_label(LabelInstr*);
   void do_arm(ArmInstr*);
   void do_const(ConstantExpr*) { } // polymorphic constant
   void do_abc_finddef(BinaryStmt*);
   void do_callmethod(CallStmt2*);
   void do_callinterface(CallStmt2*);
+  void do_callstatic(CallStmt2*);
   void do_newinstance(UnaryExpr*);
   void do_toprimitive(UnaryStmt*);
   void do_string2atom(UnaryExpr* i) { doChangeModel(i, kModelAtom); }
   void do_int2atom(UnaryExpr* i) { doChangeModel(i, kModelAtom); }
   void do_uint2atom(UnaryExpr* i) { doChangeModel(i, kModelAtom); }
   void do_double2atom(UnaryExpr* i) { doChangeModel(i, kModelAtom); }
   void do_scriptobject2atom(UnaryExpr* i) { doChangeModel(i, kModelAtom); }
   void do_bool2atom(UnaryExpr* i) { doChangeModel(i, kModelAtom); }
   void do_ns2atom(UnaryExpr* i) { doChangeModel(i, kModelAtom); }
   void do_atom2scriptobject(UnaryExpr* i) { doChangeModel(i, kModelScriptObject); }
+  void do_atom2ns(UnaryExpr* i) { doChangeModel(i, kModelNamespace); }
   void do_atom2bool(UnaryExpr* i) { doChangeModel(i, kModelInt); }
   void do_atom2string(UnaryExpr* i) { doChangeModel(i, kModelString); }
   void do_i2d(UnaryExpr* i) { doChangeModel(i, kModelDouble); }
   void do_u2d(UnaryExpr* i) { doChangeModel(i, kModelDouble); }
   void do_d2i(UnaryExpr* i) { doChangeModel(i, kModelInt); }
   void do_d2u(UnaryExpr* i) { doChangeModel(i, kModelInt); }
   void do_caststring(UnaryStmt* i) { doCoerceInstr(i, lattice_->string_type[kTypeNullable]); }
   void do_castobject(UnaryExpr* i) { doCoerceInstr(i, lattice_->object_type[kTypeNullable]); }
@@ -89,18 +92,18 @@ public: // dispatch() adapter methods.
   void do_applytype(NaryStmt0*);
   void do_newclass(NaryStmt2*);
   void do_abc_callprop(CallStmt2*);
   void do_abc_constructprop(CallStmt2*);
   void do_toslot(BinaryExpr*);
   void do_slottype(BinaryExpr*);
   void do_coerce(BinaryStmt* i) { doCoerceInstr(i); }
   void do_cast(BinaryStmt* i) { doCoerceInstr(i); }
-  void do_abc_findproperty(NaryStmt2* i) { doFindInstr(i); }
-  void do_abc_findpropstrict(NaryStmt2* i) { doFindInstr(i); }
+  void do_abc_findproperty(NaryStmt3* i) { doFindInstr(i); }
+  void do_abc_findpropstrict(NaryStmt3* i) { doFindInstr(i); }
   void do_abc_add(BinaryStmt*);
   void do_abc_getprop(CallStmt2*);
   void do_abc_getpropx(CallStmt3*);
   void do_getpropertylate_u(BinaryStmt* i) { doGetpropertylate(i); }
   void do_getpropertylate_i(BinaryStmt* i) { doGetpropertylate(i); }
   void do_getpropertylate_d(BinaryStmt* i) { doGetpropertylate(i); }
   void do_getslot(CallStmt2*);
   void do_ckfilter(UnaryExpr*);
@@ -113,16 +116,17 @@ public: // dispatch() adapter methods.
   void do_loadenv_atom(BinaryExpr* i) { do_loadenv(i); }
   void do_loadenv_interface(BinaryExpr*);
   void do_loadenv_string(BinaryExpr* i) { do_loadenv(i); }
   void do_loadenv_number(BinaryExpr* i) { do_loadenv(i); }
   void do_loadenv_boolean(BinaryExpr* i) { do_loadenv(i); }
   void do_loadenv_namespace(BinaryExpr* i) { do_loadenv(i); }
   void do_loadinitenv(UnaryExpr*);
   void do_loadsuperinitenv(UnaryExpr*);
+  void do_loadenv_env(BinaryExpr*);
 
   // constant folding
   void do_addi(BinaryExpr* i)  { fold(i, Stubs::do_addi); }
   void do_subi(BinaryExpr* i)  { fold(i, Stubs::do_subi); }
   void do_muli(BinaryExpr* i)  { fold(i, Stubs::do_muli); }
   void do_ori(BinaryExpr* i)   { fold(i, Stubs::do_ori); }
   void do_andi(BinaryExpr* i)  { fold(i, Stubs::do_andi); }
   void do_xori(BinaryExpr* i)  { fold(i, Stubs::do_xori); }
@@ -157,17 +161,17 @@ public: // dispatch() adapter methods.
   void do_i2u(UnaryExpr* i)    { fold(i, Stubs::do_i2u); }
   void do_d2b(UnaryExpr* i)    { fold(i, Stubs::do_d2b); }
   void do_doubletoint32(UnaryExpr* i) { fold(i, Stubs::do_doubletoint32); }
 
 private:
   void doCoerceInstr(UnaryExpr*, const Type* to_type);
   void doCoerceInstr(UnaryStmt*, const Type* to_type);
   void doCoerceInstr(BinaryStmt*);
-  void doFindInstr(NaryStmt2*);
+  void doFindInstr(NaryStmt3*);
   void doChangeModel(UnaryExpr*, ModelKind);
   void coerceOutputModels(Instr*);
   void doGetpropertylate(BinaryStmt*);
 
   template<typename STUB> void fold(BinaryExpr*, STUB);
   template<typename STUB> void fold(UnaryExpr*, STUB);
 
   template<int ARGMIN>
--- a/halfmoon/hm-typeinference.cpp
+++ b/halfmoon/hm-typeinference.cpp
@@ -1,10 +1,10 @@
 /* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "hm-main.h"
 #ifdef VMCFG_HALFMOON
 #include "profiler/profiler-main.h"
 
@@ -251,16 +251,20 @@ void SCCP::analyzeBranch(BlockEndInstr* 
       if (arm)
         addBlock(arm);
       else
         for (ArrayRange<ArmInstr*> a = armRange(cond); !a.empty();)
           addBlock(a.popFront());
       break;
     }
   }
+  if (end->catch_blocks != NULL) {
+    for (CatchBlockRange r(end); !r.empty();)
+      addBlock(r.popFront());
+  }
 }
 
 void SCCP::analyzeSSA(Instr* instr) {
   if (numDefs(instr) == 0) {
     if (isBlockEnd(instr))
       analyzeBranch((BlockEndInstr*)instr);
     return;
   }
@@ -273,18 +277,22 @@ void SCCP::analyzeSSA(Instr* instr) {
   analyzer.computeTypes(instr);
   // dev: check computation result
   bool changed2 = false;
   i = 0;
   for (ArrayRange<Def> d = defRange(instr); !d.empty(); d.popFront(), ++i) {
     assert(subtypeof(old_types[i], type(d.front())) && "illegal type narrowing");
     changed2 |= *type(d.front()) != *old_types[i];
   }
-  if (changed2)
+  if (changed2) {
+    if (enable_typecheck) {
+      printInstr(instr);
+    }
     addInstrUsers(instr);
+  }
 }
 
 /**
  * propagate types through an InstrGraph, starting with root defs 
  * in start and constant instructions.
  *
  * Note that per SCCP, types in dead arms of constant conditionals 
  * are set to UN.
--- a/halfmoon/hm-typeinference.h
+++ b/halfmoon/hm-typeinference.h
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 namespace halfmoon {
 
 /**
  * Sparse Conditional Constant propagation.  Reset all types of all defs,
--- a/halfmoon/hm-types.cpp
+++ b/halfmoon/hm-types.cpp
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "hm-main.h"
 #ifdef VMCFG_HALFMOON
 
 namespace halfmoon {
@@ -217,23 +217,30 @@ const Type* SimpleDataType<SELF_CLASS, T
   if (t.isSubtypeOf(*this) && submodelof(&t, this))
     return this;
 
   if (t.kind == TYPE_KIND)
     return makeUnion((SELF_CLASS&)t, lat);
 
   if (isDataType(t)) {
     // if we're null, reverse the test so we only need to handle the other-is-null case
-    if (isNull())
+    if (isNull() && halfmoon::kind(this) != kTypeVoid) {
       return t.makeUnion(*this, lat);
+    }
 
     bool union_nullable = is_nullable || t.is_nullable;
     Traits* union_traits = Verifier::findCommonBase(traits, getTraits(&t));
     switch (builtinType(union_traits)) {
     case BUILTIN_any:
+      if (union_traits == NULL) {
+        // null unioned with t just returns a nullable t, except for void
+        if (builtinType(getTraits(&t)) == BUILTIN_null  && builtinType(traits) != BUILTIN_void) {
+          return lat.makeType(SELF_CLASS(model, traits, true, false, (VALUE_REP)0));
+        }
+      }
       return lat.makeType(AnyType(union_traits, union_nullable));
     case BUILTIN_object:
       return lat.makeType(ObjectType(union_traits, union_nullable));
     default:
       if (union_traits == traits) {
         assert(builtinType(getTraits(&t)) == BUILTIN_null);
         return lat.makeType(SELF_CLASS(model, traits, true, false, (VALUE_REP)0));
       } else {
@@ -267,17 +274,17 @@ ObjectType::ObjectType(Traits* traits, b
 }
 
 // --------------------------------------------------------------------
 
 // VoidType
 
 VoidType::VoidType(Traits* traits) :
   SimpleDataType<VoidType, kTypeVoid, kModelAtom, Atom>
-    (traits, kModelAtom, false, true, undefinedAtom) {
+    (traits, kModelAtom, true, true, undefinedAtom) {
   assert(builtinType(traits) == BUILTIN_void);
 }
 
 // --------------------------------------------------------------------
 
 // ScriptObjectType
 
 ScriptObjectType::ScriptObjectType(ModelKind model, Traits* traits,
--- a/halfmoon/hm-types.h
+++ b/halfmoon/hm-types.h
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 namespace halfmoon {
 using avmplus::AvmCore;
 using avmplus::Binding;
 using avmplus::BIND_AMBIGUOUS;
--- a/halfmoon/hm-util.h
+++ b/halfmoon/hm-util.h
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef HM_UTIL_H_
 #define HM_UTIL_H_
 
 //
@@ -70,16 +70,42 @@ public:
 private:
   E* const frame;
   const int stack_base;
   const int stackp;
   const int scopep;
   int i;
 };
 
+/**
+ * Range that accesses each active position in an ABC stack frame.
+ */
+class FrameIndexRange {
+public:
+  FrameIndexRange(int stackp, int scopep, int stack_base) :
+    stack_base(stack_base), stackp(stackp), scopep(scopep), i(0) {
+  }
+
+  bool empty() const {
+    return i > stackp;
+  }
+  int front() const {
+    return i;
+  }
+  void popFront() {
+    i = (i != scopep) ? i + 1 : stack_base;
+  }
+
+private:
+  const int stack_base;
+  const int stackp;
+  const int scopep;
+  int i;
+};
+
 /// Return a FrameRange for frame, given the dimensions from state and signature.
 ///
 template<class E>
 FrameRange<E> range(E* frame, const FrameState* state,
                     MethodSignaturep signature) {
   int stack_base = signature->stack_base();
   int sp = stack_base + state->stackDepth - 1;
   int scopep = signature->scope_base() + state->scopeDepth - 1;
--- a/halfmoon/hm-valnum.cpp
+++ b/halfmoon/hm-valnum.cpp
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "hm-main.h"
 #ifdef VMCFG_HALFMOON
 
 namespace halfmoon {
--- a/halfmoon/hm-valnum.h
+++ b/halfmoon/hm-valnum.h
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
-/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
+/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef HM_VALNUM_H_
 #define HM_VALNUM_H_
 
 namespace halfmoon {
--- a/halfmoon/templates/ast.py
+++ b/halfmoon/templates/ast.py
@@ -317,16 +317,17 @@ callstmt3 = RepInfo("CallStmt3", shape(1
 callstmt4 = RepInfo("CallStmt4", shape(1, 4, 1, 1, DATA_IN))
 unaryexpr = RepInfo("UnaryExpr", shape(0, 1, 0, 1, NONE), gen = True)
 unarystmt = RepInfo("UnaryStmt", shape(1, 1, 1, 1, NONE), gen = True)
 binaryexpr = RepInfo("BinaryExpr", shape(0, 2, 0, 1, NONE), gen = True)
 binarystmt = RepInfo("BinaryStmt", shape(1, 2, 1, 1, NONE), gen = True)
 hasnext2stmt = RepInfo("Hasnext2Stmt", shape(1, 2, 1, 3, NONE))
 voidstmt = RepInfo("VoidStmt", shape(1, 0, 1, 0, NONE))
 constantexpr = RepInfo("ConstantExpr", shape(0, 0, 0, 1, NONE))
+debuginstr = RepInfo("DebugInstr", shape(1, 1, 1, 0, NONE))
 
 # this list is used to populate reps, a map of shapes to reps
 # used to choose representations for instrs. Not all shapes
 # defined above are on this list, because some are used on
 # an override basis only, as specified in instr_rep_overrides
 # and shape_rep_overrides, below.
 #
 replist = [
@@ -336,81 +337,87 @@ replist = [
   callstmt3,
   callstmt4,
   unaryexpr,
   unarystmt,
   binaryexpr,
   binarystmt,
   hasnext2stmt,
   voidstmt,
-  constantexpr
+  constantexpr,
+  debuginstr
 ]
 
 # shape -> rep, used to pick reps for instrs
 # based on instr shape (subject to overrides,
 # see below)
 #
 reps = dict([(rep.shape, rep) for rep in replist])
 
 # NOTE: these reps are only used in IR5 shape overrides
 # (see instr_rep_overrides). They are *not* selectable
 # via reps() by ordinary instructions.
 #
 startinstr = RepInfo("StartInstr", shape(0, 0, 1, 0, DATA_OUT))
+catchblockinstr = RepInfo("CatchBlockInstr", shape(0, 0, 1, 0, DATA_OUT))
 stopinstr = RepInfo("StopInstr", shape(1, 0, 0, 0, DATA_IN))
 
 # some instrs are made to use a non-default rep,
 # irrespective of the instr shape.
 # if an instr name is a key in this map, CG will 
 # use the specified rep, rather than trying to
 # find a match for the instr's shape in reps
 #
 instr_rep_overrides = {
   # these overrides allow a custom C++ class to 
   # store extra information.
   #
-  "safepoint": RepInfo("SafepointInstr", shape(1, 1, 1, 1, NONE)),
+  "safepoint": RepInfo("SafepointInstr", shape(1, 0, 2, 0, DATA_IN)),
   "setlocal": RepInfo("SetlocalInstr", shape(0, 2, 0, 1, NONE)),
   # DEOPT: I think I need this because deopt_safepoint has
   # extra fields for vpc, scopep, sp, etc.
   "deopt_safepoint": RepInfo("DeoptSafepointInstr", shape(1, 0, 1, 0, DATA_IN)),
   "deopt_finish": RepInfo("DeoptFinishInstr", shape(1, 0, 1, 0, NONE)),
   "deopt_finishcall": RepInfo("DeoptFinishCallInstr", shape(1, 1, 1, 0, NONE)),
+
+  "debugline": debuginstr,
+  "debugfile": debuginstr,
   
   # we select IR5 (block delimiter) reps exclusively by
   # override. this lets us keep the IR5 reps out of the
   # standard reps dictionary, which ensures that ordinary 
   # instructions with compatible shapes never inadvertently 
   # select IR5 reps, which have special semantics.
   # 
   "goto": RepInfo("GotoInstr", shape(0, 0, 0, 0, DATA_IN)),
   "label": RepInfo("LabelInstr", shape(0, 0, 0, 0, DATA_OUT)),
   "if": RepInfo("IfInstr", shape(0, 1, 0, 0, DATA_IN)),
   "switch": RepInfo("SwitchInstr", shape(0, 1, 0, 0, DATA_IN)),
   "arm": RepInfo("ArmInstr", shape(0, 0, 0, 0, DATA_OUT)),
   "return": stopinstr,
   "throw": stopinstr,
   "start": startinstr,
   "template": startinstr,
+  "catchblock": RepInfo("CatchBlockInstr", shape(0, 0, 0, 0, DATA_OUT)),
 
   #
   # fixed-arg instructions for which we want to use a
   # specific vararg rep that provides VM call semantics.
   #
   
   "newfunction": narystmt1,
   "newclass": narystmt2,
 
-  "abc_findproperty": narystmt2,
-  "abc_findpropertyx": narystmt3,
-  "abc_findpropertyns": narystmt3,
+  "abc_findproperty": narystmt3,
+  "abc_findpropertyx": narystmt4,
+  "abc_findpropertyns": narystmt4,
   "abc_findpropertynsx": narystmt4,
-  "abc_findpropstrict": narystmt2,
-  "abc_findpropstrictx": narystmt3,
-  "abc_findpropstrictns": narystmt3,
+  "abc_findpropstrict": narystmt3,
+  "abc_findpropstrictx": narystmt4,
+  "abc_findpropstrictns": narystmt4,
   "abc_findpropstrictnsx": narystmt4,
   "findprop2getouter": narystmt1,
   "findprop2finddef": narystmt1,
 
   "abc_getsuper": callstmt2,
   "abc_getsuperx": callstmt3,
   "abc_getsuperns": callstmt3,
   "abc_getsupernsx": callstmt4,
--- a/halfmoon/templates/hrdefs.py
+++ b/halfmoon/templates/hrdefs.py
@@ -36,16 +36,17 @@ hrdefs = '''
 ; use side, it means the required type is determined elsewhere,
 ; for example the constraint on goto inputs comes from the label.
 ; on the def side, it means the actual type is derived from uses
 ; (e.g. arm's def types come from if's uses) or a signature (start),
 ; or a-priori (const).
 
 (start (-> Effect [Top])) ; start's defs include effect, state, env, and data
 (template (-> Effect [Top]))
+(catchblock (-> [Top]))
 
 (return (Effect [Top] -> )) ; some templates may return vm types
 (throw (Effect Atom -> )) ; throw only wants 1 data value even tho StopInstr allows N.
 
 (goto ([Top] -> ))
 (label ( -> [Top]))
 
 (if (Boolean [Top] -> ))
@@ -99,16 +100,17 @@ hrdefs = '''
 (loadenv_boolean (Ord Boolean -> Env)) ; load MethodEnv* from Boolean
 (loadenv_number (Ord Number~ -> Env)) ; load MethodEnv* from Number
 (loadenv_string (Ord String~ -> Env)) ; load MethodEnv* from String
 (loadenv_interface (Method ScriptObject~ -> Env)) ; load MethodEnv* from Method IID
 (loadenv (Ord ScriptObject~ -> Env)) ; load MethodEnv* from ScriptObject->vtable->methods[disp_id]
 (loadenv_atom (Ord Atom~ -> Env)) ; load MethodEnv from toVTable(obj)->methods[disp_id]
 (loadinitenv (ScriptObject~ -> Env)) ; load MethodEnv from ScriptObject->vtable->init
 (loadsuperinitenv (Env -> Env)) ; load MethodEnv from env->vtable->base->init
+(loadenv_env (Ord Env -> Env)) ; load MethodEnv from toVTable(obj)->methods[disp_id]
 
 (newobject (Effect [Atom] -> Effect ScriptObject~)) ;;; TODO: result should be final with Object traits
 (newarray (Effect [Atom] -> Effect Array~))   ;;; TODO: Array~ should be final, dense
 (applytype (Effect [Atom] -> Effect Atom))
 ; fixme: input type is a non-null object whose traits has a nonnull itraits, e.g. a class
 ;       output type is the instance type of that class.
 (newinstance (Class~ -> ScriptObject~))
 
@@ -170,24 +172,24 @@ hrdefs = '''
 (negi (Int -> Int))
 
 (negd (Number -> Number))
 (not (Boolean -> Boolean))
 
 (newactivation (Effect Env -> Effect ScriptObject~))
 
 (abc_finddef (Effect Name Env -> Effect ScriptObject~))
-(abc_findpropstrict (Effect Name Env [Atom~] -> Effect Atom~))
-(abc_findpropstrictx (Effect Name Env Atom [Atom~] -> Effect Atom~))
-(abc_findpropstrictns (Effect Name Env Atom [Atom~] -> Effect Atom~))
-(abc_findpropstrictnsx (Effect Name Env Atom Atom [Atom~] -> Effect Atom~))
-(abc_findproperty (Effect Name Env [Atom~] -> Effect Atom~))
-(abc_findpropertyx (Effect Name Env Atom [Atom~] -> Effect Atom~))
-(abc_findpropertyns (Effect Name Env Atom [Atom~] -> Effect Atom~))
-(abc_findpropertynsx (Effect Name Env Atom Atom [Atom~] -> Effect Atom~))
+(abc_findpropstrict (Effect Name Env Ord [Atom~] -> Effect Atom~))
+(abc_findpropstrictx (Effect Name Env Ord Atom [Atom~] -> Effect Atom~))
+(abc_findpropstrictns (Effect Name Env Ord Atom [Atom~] -> Effect Atom~))
+(abc_findpropstrictnsx (Effect Name Env Ord Atom [Atom~] -> Effect Atom~))
+(abc_findproperty (Effect Name Env Ord [Atom~] -> Effect Atom~))
+(abc_findpropertyx (Effect Name Env Ord Atom [Atom~] -> Effect Atom~))
+(abc_findpropertyns (Effect Name Env Ord Atom [Atom~] -> Effect Atom~))
+(abc_findpropertynsx (Effect Name Env Ord Atom [Atom~] -> Effect Atom~))
 
 (newclass (Effect Traits~ Class [Atom~] -> Effect Class~))  ; TODO vararg is list of Scope, need type
 (newfunction (Effect Method [Atom~] -> Effect Function~))
 
 (abc_getsuper (Effect Name Atom~ -> Effect Atom))              ; same as getprop inputs
 (abc_getsuperx (Effect Name Atom Atom~ -> Effect Atom))        ; same as getpropx inputs
 (abc_getsuperns (Effect Name Atom Atom~ -> Effect Atom))       ; same as getpropns inputs
 (abc_getsupernsx (Effect Name Atom Atom Atom~ -> Effect Atom)) ; same as getpropnsx inputs
@@ -214,56 +216,61 @@ hrdefs = '''
 
 (abc_callsuper    (Effect Name Atom~ [Atom] -> Effect Atom))
 (abc_callsuperx   (Effect Name Atom Atom~ [Atom] -> Effect Atom))
 (abc_callsuperns  (Effect Name Atom Atom~ [Atom] -> Effect Atom))
 (abc_callsupernsx (Effect Name Atom Atom Atom~ [Atom] -> Effect Atom))
 
 ;; these are natively bound calls, they take TopData because
 ;; the real signature comes from the callee.
-(callstatic (Effect Ord TopData [TopData] -> Effect TopData)) 
+(callstatic (Effect Env TopData [TopData] -> Effect TopData)) 
 (callmethod (Effect Env TopData [TopData] -> Effect TopData))
 (callinterface (Effect Env TopData [TopData] -> Effect TopData))
 
-(newcatch (Effect Traits~ -> Effect ScriptObject~))                 ; same as newactivation
+(newcatch (Effect Traits~ -> Effect Atom~))
  
 (setslot (Effect Ord ScriptObject~ TopData -> Effect Bot)) ; actual arg type is slot type
 (getslot (Effect Ord ScriptObject~ -> Effect TopData))    ; actual result type is slot type
 (slottype (ScriptObject~ Ord -> Traits))
 
 (getouterscope (Ord Env -> Atom~)) 
 
 
-(safepoint (Effect State -> Effect State))
+(safepoint (Effect [State] -> Effect State))
 
-(setlocal (State TopData -> State))  ; TopData allows safepoints to handle unboxed values.
+(setlocal (State Atom -> State))  ; setlocal is only used to store state for exception edges, which require Atom
 
 (newstate (-> State))                   ; create abstract VM state value
 
 ; DEOPT: new-style safepoints
 (deopt_safepoint (Effect [TopData] -> Effect))
 (deopt_finish (Effect -> Effect))
 (deopt_finishcall (Effect TopData -> Effect))
 
+; debug
+(debugline (Effect Int -> Effect))
+(debugfile (Effect String -> Effect))
+
 ; conversions to atom
 (string2atom (String -> Atom))
 (double2atom (Number -> Atom~))
 (int2atom (Int -> Atom~))
 (uint2atom (Uint -> Atom~))
 (scriptobject2atom (ScriptObject -> Atom))
 (bool2atom (Boolean -> Atom~))
 (ns2atom (Namespace -> Atom))
 
 ; conversions from atom
 (atom2bool (Atom~ -> Boolean))
 (atom2double (Atom -> Number))
 (atom2string (Atom -> String))
 (atom2int (Atom -> Int))
 (atom2uint (Atom -> Uint))
 (atom2scriptobject (Atom -> ScriptObject))
+(atom2ns (Atom -> Namespace))
 
 ; additional numeric conversions
 (i2d (Int -> Number))
 (u2d (Uint -> Number))
 (d2i (Number -> Int)) ; should only be used when range is int32 already.
 (d2u (Number -> Uint)) ; should only be used when range is uint32 already.
 
 (toslot (ScriptObject Name -> Ord))
--- a/nanojit/Assembler.cpp
+++ b/nanojit/Assembler.cpp
@@ -1637,16 +1637,23 @@ typedef void* (*decode_instructions_ftyp
 
                 case LIR_reti:
                 CASE64(LIR_retq:)
                 case LIR_retd:
                 case LIR_retf:
                 case LIR_retf4:
                     countlir_ret();
                     ins->oprnd1()->setResultLive();
+#ifdef VMCFG_HALFMOON
+                    // The halfmoon emitter may emit it's blocks in a different order which means
+                    // that a ret may be the last thing emitted, so a call to handleLoopCarriedExprs
+                    // is required here.  It's probably not a bad idea to emit it in general but
+                    // but it's ifdef'ed for now.
+                    handleLoopCarriedExprs(pending_lives, 0);
+#endif
                     asm_ret(ins);
                     break;
 
                 // Allocate some stack space.  The value of this instruction
                 // is the address of the stack space.
                 case LIR_allocp:
                     countlir_alloc();
                     if (ins->isExtant()) {