Bug 1255158 - Update graphite2 library to release 1.3.7. r=jdaggett, a=lizzard
authorJonathan Kew <jkew@mozilla.com>
Tue, 15 Mar 2016 07:53:04 +0000
changeset 351251 a5ddf55a5f6c884b9f44c999bded4d8505f26e00
parent 351250 09df85bf34d9dc48c539b4ef6972f6c3ae22772b
child 351252 303576fae3933ed817545e5beb3d92128fe6caea
push id15502
push userahunt@mozilla.com
push dateThu, 14 Apr 2016 20:27:48 +0000
reviewersjdaggett, lizzard
bugs1255158
milestone47.0a2
Bug 1255158 - Update graphite2 library to release 1.3.7. r=jdaggett, a=lizzard
gfx/graphite2/README.mozilla
gfx/graphite2/include/graphite2/Font.h
gfx/graphite2/moz-gr-update.sh
gfx/graphite2/src/CachedFace.cpp
gfx/graphite2/src/Code.cpp
gfx/graphite2/src/Face.cpp
gfx/graphite2/src/GlyphCache.cpp
gfx/graphite2/src/GlyphFace.cpp
gfx/graphite2/src/NameTable.cpp
gfx/graphite2/src/Pass.cpp
gfx/graphite2/src/Segment.cpp
gfx/graphite2/src/Silf.cpp
gfx/graphite2/src/Slot.cpp
gfx/graphite2/src/TtfUtil.cpp
gfx/graphite2/src/inc/Code.h
gfx/graphite2/src/inc/Face.h
gfx/graphite2/src/inc/GlyphFace.h
gfx/graphite2/src/inc/Rule.h
gfx/graphite2/src/inc/Segment.h
gfx/graphite2/src/inc/Slot.h
gfx/graphite2/src/inc/UtfCodec.h
gfx/graphite2/src/inc/opcode_table.h
gfx/graphite2/src/inc/opcodes.h
--- a/gfx/graphite2/README.mozilla
+++ b/gfx/graphite2/README.mozilla
@@ -1,3 +1,6 @@
-This directory contains the Graphite2 library release 1.3.6 from
-https://github.com/silnrsi/graphite/releases/download/1.3.6/graphite-minimal-1.3.6.tgz
-See ./gfx/graphite2/moz-gr-update.sh for update procedure.
+This directory contains the Graphite2 library release 1.3.7 from
+https://github.com/silnrsi/graphite/releases/download/1.3.7/graphite2-minimal-1.3.7.tgz
+See gfx/graphite2/moz-gr-update.sh for update procedure.
+
+Also cherry-picked the post-1.3.7 commit 7dc29f3e4665b17f6e825957b707db6da36ae7d8
+to keep our reftests happy (affects the PigLatin test font).
\ No newline at end of file
--- a/gfx/graphite2/include/graphite2/Font.h
+++ b/gfx/graphite2/include/graphite2/Font.h
@@ -25,17 +25,17 @@
     either version 2 of the License or (at your option) any later version.
 */
 #pragma once
 
 #include "graphite2/Types.h"
 
 #define GR2_VERSION_MAJOR   1
 #define GR2_VERSION_MINOR   3
-#define GR2_VERSION_BUGFIX  6
+#define GR2_VERSION_BUGFIX  7
 
 #ifdef __cplusplus
 extern "C"
 {
 #endif
 
 typedef struct gr_face          gr_face;
 typedef struct gr_font          gr_font;
--- a/gfx/graphite2/moz-gr-update.sh
+++ b/gfx/graphite2/moz-gr-update.sh
@@ -14,17 +14,17 @@
 RELEASE=$1
 
 if [ "x$RELEASE" == "x" ]
 then
     echo "Must provide the version number to be used."
     exit 1
 fi
 
-TARBALL="https://github.com/silnrsi/graphite/releases/download/$RELEASE/graphite-minimal-$RELEASE.tgz"
+TARBALL="https://github.com/silnrsi/graphite/releases/download/$RELEASE/graphite2-minimal-$RELEASE.tgz"
 
 foo=`basename $0`
 TMPFILE=`mktemp -t ${foo}` || exit 1
 
 curl -L "$TARBALL" -o "$TMPFILE"
 tar -x -z -C gfx/graphite2/ --strip-components 1 -f "$TMPFILE" || exit 1
 rm "$TMPFILE"
 
--- a/gfx/graphite2/src/CachedFace.cpp
+++ b/gfx/graphite2/src/CachedFace.cpp
@@ -64,20 +64,20 @@ bool CachedFace::runGraphite(Segment *se
         return false;
 
     assert(m_cacheStore);
     // find where the segment can be broken
     Slot * subSegStartSlot = seg->first();
     Slot * subSegEndSlot = subSegStartSlot;
     uint16 cmapGlyphs[eMaxSpliceSize];
     int subSegStart = 0;
-    for (unsigned int i = 0; i < seg->charInfoCount(); ++i)
+    for (unsigned int i = 0; i < seg->charInfoCount() && subSegEndSlot; ++i)
     {
         const unsigned int length = i - subSegStart + 1;
-        if (length < eMaxSpliceSize)
+        if (length < eMaxSpliceSize && subSegEndSlot->gid() < m_cacheStore->maxCmapGid())
             cmapGlyphs[length-1] = subSegEndSlot->gid();
         else return false;
         const bool spaceOnly = m_cacheStore->isSpaceGlyph(subSegEndSlot->gid());
         // at this stage the character to slot mapping is still 1 to 1
         const int   breakWeight = seg->charinfo(i)->breakWeight(),
                     nextBreakWeight = (i + 1 < seg->charInfoCount())?
                             seg->charinfo(i+1)->breakWeight() : 0;
         const uint8 f = seg->charinfo(i)->flags();
--- a/gfx/graphite2/src/Code.cpp
+++ b/gfx/graphite2/src/Code.cpp
@@ -61,93 +61,88 @@ inline bool is_return(const instr i) {
     const instr pop_ret  = *opmap[POP_RET].impl,
                 ret_zero = *opmap[RET_ZERO].impl,
                 ret_true = *opmap[RET_TRUE].impl;
     return i == pop_ret || i == ret_zero || i == ret_true;
 }
 
 struct context
 {
-    context(uint8 ref=0) : codeRef(ref) {flags.changed=false; flags.referenced=false; flags.inserted=false;}
+    context(uint8 ref=0) : codeRef(ref) {flags.changed=false; flags.referenced=false;}
     struct { 
         uint8   changed:1,
-                referenced:1,
-                inserted:1;
+                referenced:1;
     } flags;
     uint8       codeRef;
 };
 
 } // end namespace
 
 
 class Machine::Code::decoder
 {
 public:
     struct limits;
-    struct analysis
-    {
-        static const int NUMCONTEXTS = 256;
-        uint8     slotref;
-        context   contexts[NUMCONTEXTS];
-        byte      max_ref;
-        
-        analysis() : slotref(0), max_ref(0) {};
-        void set_ref(int index, bool incinsert=false) throw();
-        void set_noref(int index) throw();
-        void set_changed(int index) throw();
-
-    };
+    static const int NUMCONTEXTS = 256;
     
     decoder(limits & lims, Code &code, enum passtype pt) throw();
     
     bool        load(const byte * bc_begin, const byte * bc_end);
     void        apply_analysis(instr * const code, instr * code_end);
-    byte        max_ref() { return _analysis.max_ref; }
-    int         pre_context() const { return _pre_context; }
+    byte        max_ref() { return _max_ref; }
+    int         out_index() const { return _out_index; }
     
 private:
+    void        set_ref(int index) throw();
+    void        set_noref(int index) throw();
+    void        set_changed(int index) throw();
     opcode      fetch_opcode(const byte * bc);
     void        analyse_opcode(const opcode, const int8 * const dp) throw();
     bool        emit_opcode(opcode opc, const byte * & bc);
     bool        validate_opcode(const opcode opc, const byte * const bc);
     bool        valid_upto(const uint16 limit, const uint16 x) const throw();
     bool        test_context() const throw();
+    bool        test_ref(int8 index) const throw();
     void        failure(const status_t s) const throw() { _code.failure(s); }
     
     Code              & _code;
-    int                 _pre_context;
-    uint16              _rule_length;
+    int                 _out_index;
+    uint16              _out_length;
     instr             * _instr;
     byte              * _data;
     limits            & _max;
-    analysis            _analysis;
     enum passtype       _passtype;
     int                 _stack_depth;
     bool                _in_ctxt_item;
+    int16               _slotref;
+    context             _contexts[NUMCONTEXTS];
+    byte                _max_ref;
 };
 
 
 struct Machine::Code::decoder::limits
 {
   const byte       * bytecode;
   const uint8        pre_context;
   const uint16       rule_length,
                      classes,
                      glyf_attrs,
                      features;
   const byte         attrid[gr_slatMax];
 };
    
 inline Machine::Code::decoder::decoder(limits & lims, Code &code, enum passtype pt) throw()
 : _code(code),
-  _pre_context(code._constraint ? 0 : lims.pre_context), 
-  _rule_length(code._constraint ? 1 : lims.rule_length), 
+  _out_index(code._constraint ? 0 : lims.pre_context), 
+  _out_length(code._constraint ? 1 : lims.rule_length), 
   _instr(code._code), _data(code._data), _max(lims), _passtype(pt),
   _stack_depth(0),
-  _in_ctxt_item(false)
+  _in_ctxt_item(false),
+  _slotref(0),
+  _max_ref(0)
 { }
     
 
 
 Machine::Code::Code(bool is_constraint, const byte * bytecode_begin, const byte * const bytecode_end,
            uint8 pre_context, uint16 rule_length, const Silf & silf, const Face & face,
            enum passtype pt, byte * * const _out)
  :  _code(0), _data(0), _data_size(0), _instr_count(0), _max_ref(0), _status(loaded),
@@ -163,17 +158,17 @@ Machine::Code::Code(bool is_constraint, 
       return;
     }
     assert(bytecode_end > bytecode_begin);
     const opcode_t *    op_to_fn = Machine::getOpcodeTable();
     
     // Allocate code and data target buffers, these sizes are a worst case
     // estimate.  Once we know their real sizes the we'll shrink them.
     if (_out)   _code = reinterpret_cast<instr *>(*_out);
-    else        _code = static_cast<instr *>(malloc(estimateCodeDataOut(bytecode_end-bytecode_begin)));
+    else        _code = static_cast<instr *>(malloc(estimateCodeDataOut(bytecode_end-bytecode_begin, 1, is_constraint ? 0 : rule_length)));
     _data = reinterpret_cast<byte *>(_code + (bytecode_end - bytecode_begin));
     
     if (!_code || !_data) {
         failure(alloc_failed);
         return;
     }
     
     decoder::limits lims = {
@@ -319,47 +314,57 @@ opcode Machine::Code::decoder::fetch_opc
         case COND :
             _stack_depth -= 2;
             if (_stack_depth <= 0)
                 failure(underfull_stack);
             break;
         case NEXT :
         case NEXT_N :           // runtime checked
         case COPY_NEXT :
-            test_context();
-            ++_pre_context;
+            ++_out_index;
+            if (_out_index < -1 || _out_index > _out_length || _slotref > _max.rule_length)
+                failure(out_of_range_data);
             break;
         case PUT_GLYPH_8BIT_OBS :
             valid_upto(_max.classes, bc[0]);
             test_context();
             break;
         case PUT_SUBS_8BIT_OBS :
-            valid_upto(_rule_length, _pre_context + int8(bc[0]));
+            test_ref(int8(bc[0]));
             valid_upto(_max.classes, bc[1]);
             valid_upto(_max.classes, bc[2]);
             test_context();
             break;
         case PUT_COPY :
-            valid_upto(_rule_length, _pre_context + int8(bc[0]));
+            test_ref(int8(bc[0]));
             test_context();
             break;
         case INSERT :
             if (_passtype >= PASS_TYPE_POSITIONING)
                 failure(invalid_opcode);
-            else
-                --_pre_context;
+            ++_out_length;
+            if (_out_index < 0) ++_out_index;
+            if (_out_index < -1 || _out_index >= _out_length)
+                failure(out_of_range_data);
             break;
         case DELETE :
             if (_passtype >= PASS_TYPE_POSITIONING)
                 failure(invalid_opcode);
-            test_context();
+            if (_out_index < _max.pre_context)
+                failure(out_of_range_data);
+            --_out_index;
+            --_out_length;
+            if (_out_index < -1 || _out_index > _out_length)
+                failure(out_of_range_data);
             break;
         case ASSOC :
+            if (bc[0] == 0)
+                failure(out_of_range_data);
             for (uint8 num = bc[0]; num; --num)
-                valid_upto(_rule_length, _pre_context + int8(bc[num]));
+                test_ref(int8(bc[num]));
             test_context();
             break;
         case CNTXT_ITEM :
             valid_upto(_max.rule_length, _max.pre_context + int8(bc[0]));
             if (bc + 2 + bc[1] >= _max.bytecode)    failure(jump_past_end);
             if (_in_ctxt_item)                      failure(nested_context_item);
             break;
         case ATTR_SET :
@@ -378,52 +383,43 @@ opcode Machine::Code::decoder::fetch_opc
                 failure(underfull_stack);
             if (valid_upto(gr_slatMax, bc[0]))
                 valid_upto(_max.attrid[bc[0]], bc[1]);
             test_context();
             break;
         case PUSH_SLOT_ATTR :
             ++_stack_depth;
             valid_upto(gr_slatMax, bc[0]);
-            valid_upto(_rule_length, _pre_context + int8(bc[1]));
+            test_ref(int8(bc[1]));
             if (attrCode(bc[0]) == gr_slatUserDefn)     // use IATTR for user attributes
                 failure(out_of_range_data);
             break;
         case PUSH_GLYPH_ATTR_OBS :
+        case PUSH_ATT_TO_GATTR_OBS :
             ++_stack_depth;
             valid_upto(_max.glyf_attrs, bc[0]);
-            valid_upto(_rule_length, _pre_context + int8(bc[1]));
+            test_ref(int8(bc[1]));
             break;
+        case PUSH_ATT_TO_GLYPH_METRIC :
         case PUSH_GLYPH_METRIC :
             ++_stack_depth;
             valid_upto(kgmetDescent, bc[0]);
-            valid_upto(_rule_length, _pre_context + int8(bc[1]));
+            test_ref(int8(bc[1]));
             // level: dp[2] no check necessary
             break;
         case PUSH_FEAT :
             ++_stack_depth;
             valid_upto(_max.features, bc[0]);
-            valid_upto(_rule_length, _pre_context + int8(bc[1]));
-            break;
-        case PUSH_ATT_TO_GATTR_OBS :
-            ++_stack_depth;
-            valid_upto(_max.glyf_attrs, bc[0]);
-            valid_upto(_rule_length, _pre_context + int8(bc[1]));
-            break;
-        case PUSH_ATT_TO_GLYPH_METRIC :
-            ++_stack_depth;
-            valid_upto(kgmetDescent, bc[0]);
-            valid_upto(_rule_length, _pre_context + int8(bc[1]));
-            // level: dp[2] no check necessary
+            test_ref(int8(bc[1]));
             break;
         case PUSH_ISLOT_ATTR :
             ++_stack_depth;
             if (valid_upto(gr_slatMax, bc[0]))
             {
-                valid_upto(_rule_length, _pre_context + int8(bc[1]));
+                test_ref(int8(bc[1]));
                 valid_upto(_max.attrid[bc[0]], bc[2]);
             }
             break;
         case PUSH_IGLYPH_ATTR :// not implemented
             ++_stack_depth;
             break;
         case POP_RET :
             if (--_stack_depth < 0)
@@ -442,118 +438,107 @@ opcode Machine::Code::decoder::fetch_opc
                 valid_upto(_max.attrid[bc[0]], bc[1]);
             test_context();
             break;
         case PUSH_PROC_STATE :  // dummy: dp[0] no check necessary
         case PUSH_VERSION :
             ++_stack_depth;
             break;
         case PUT_SUBS :
-            valid_upto(_rule_length, _pre_context + int8(bc[0]));
+            test_ref(int8(bc[0]));
             valid_upto(_max.classes, uint16(bc[1]<< 8) | bc[2]);
             valid_upto(_max.classes, uint16(bc[3]<< 8) | bc[4]);
             test_context();
             break;
         case PUT_SUBS2 :        // not implemented
         case PUT_SUBS3 :        // not implemented
             break;
         case PUT_GLYPH :
             valid_upto(_max.classes, uint16(bc[0]<< 8) | bc[1]);
             test_context();
             break;
         case PUSH_GLYPH_ATTR :
         case PUSH_ATT_TO_GLYPH_ATTR :
             ++_stack_depth;
             valid_upto(_max.glyf_attrs, uint16(bc[0]<< 8) | bc[1]);
-            valid_upto(_rule_length, _pre_context + int8(bc[2]));
+            test_ref(int8(bc[2]));
+            break;
+        case SET_FEAT :
+            valid_upto(_max.features, bc[0]);
+            test_ref(int8(bc[1]));
             break;
         default:
             failure(invalid_opcode);
             break;
     }
 
     return bool(_code) ? opc : MAX_OPCODE;
 }
 
 
 void Machine::Code::decoder::analyse_opcode(const opcode opc, const int8  * arg) throw()
 {
-  if (_code._constraint) return;
-  
   switch (opc)
   {
     case DELETE :
       _code._delete = true;
       break;
+    case ASSOC :
+      set_changed(0);
+//      for (uint8 num = arg[0]; num; --num)
+//        _analysis.set_noref(num);
+      break;
     case PUT_GLYPH_8BIT_OBS :
     case PUT_GLYPH :
       _code._modify = true;
-      _analysis.set_changed(0);
+      set_changed(0);
       break;
     case ATTR_SET :
     case ATTR_ADD :
+    case ATTR_SUB :
     case ATTR_SET_SLOT :
     case IATTR_SET_SLOT :
     case IATTR_SET :
     case IATTR_ADD :
     case IATTR_SUB :
-      _analysis.set_noref(0);
+      set_noref(0);
       break;
     case NEXT :
     case COPY_NEXT :
-      if (!_analysis.contexts[_analysis.slotref].flags.inserted)
-        ++_analysis.slotref;
-      _analysis.contexts[_analysis.slotref] = context(_code._instr_count+1);
+      ++_slotref;
+      _contexts[_slotref] = context(_code._instr_count+1);
       // if (_analysis.slotref > _analysis.max_ref) _analysis.max_ref = _analysis.slotref;
       break;
     case INSERT :
-      _analysis.contexts[_analysis.slotref].flags.inserted = true;
+      if (_slotref >= 0) --_slotref;
       _code._modify = true;
       break;
     case PUT_SUBS_8BIT_OBS :    // slotref on 1st parameter
     case PUT_SUBS : 
       _code._modify = true;
-      _analysis.set_changed(0);
+      set_changed(0);
       GR_FALLTHROUGH;
       // no break
     case PUT_COPY :
-    {
-      if (arg[0] != 0) { _analysis.set_changed(0); _code._modify = true; }
-      if (arg[0] <= 0 && -arg[0] <= _analysis.slotref - _analysis.contexts[_analysis.slotref].flags.inserted)
-        _analysis.set_ref(arg[0], true);
-      else if (arg[0] > 0)
-        _analysis.set_ref(arg[0], true);
+      if (arg[0] != 0) { set_changed(0); _code._modify = true; }
+      set_ref(arg[0]);
       break;
-    }
-    case PUSH_ATT_TO_GATTR_OBS : // slotref on 2nd parameter
-        if (_code._constraint) return;
-        GR_FALLTHROUGH;
-        // no break
     case PUSH_GLYPH_ATTR_OBS :
     case PUSH_SLOT_ATTR :
     case PUSH_GLYPH_METRIC :
+    case PUSH_ATT_TO_GATTR_OBS :
     case PUSH_ATT_TO_GLYPH_METRIC :
     case PUSH_ISLOT_ATTR :
     case PUSH_FEAT :
-      if (arg[1] <= 0 && -arg[1] <= _analysis.slotref - _analysis.contexts[_analysis.slotref].flags.inserted)
-        _analysis.set_ref(arg[1], true);
-      else if (arg[1] > 0)
-        _analysis.set_ref(arg[1], true);
+    case SET_FEAT :
+      set_ref(arg[1]);
       break;
     case PUSH_ATT_TO_GLYPH_ATTR :
-        if (_code._constraint) return;
-        GR_FALLTHROUGH;
-        // no break
     case PUSH_GLYPH_ATTR :
-      if (arg[2] <= 0 && -arg[2] <= _analysis.slotref - _analysis.contexts[_analysis.slotref].flags.inserted)
-        _analysis.set_ref(arg[2], true);
-      else if (arg[2] > 0)
-        _analysis.set_ref(arg[2], true);
-      break;
-    case ASSOC :                // slotrefs in varargs
+      set_ref(arg[2]);
       break;
     default:
         break;
   }
 }
 
 
 bool Machine::Code::decoder::emit_opcode(opcode opc, const byte * & bc)
@@ -579,57 +564,60 @@ bool Machine::Code::decoder::emit_opcode
         _data            += param_sz;
         _code._data_size += param_sz;
     }
     
     // recursively decode a context item so we can split the skip into 
     // instruction and data portions.
     if (opc == CNTXT_ITEM)
     {
-        assert(_pre_context == 0);
+        assert(_out_index == 0);
         _in_ctxt_item = true;
-        _pre_context = _max.pre_context + int8(_data[-2]);
-        _rule_length = _max.rule_length;
+        _out_index = _max.pre_context + int8(_data[-2]);
+        _slotref = int8(_data[-2]);
+        _out_length = _max.rule_length;
 
         const size_t ctxt_start = _code._instr_count;
         byte & instr_skip = _data[-1];
         byte & data_skip  = *_data++;
         ++_code._data_size;
         const byte *curr_end = _max.bytecode;
 
         if (load(bc, bc + instr_skip))
         {
             bc += instr_skip;
             data_skip  = instr_skip - (_code._instr_count - ctxt_start);
             instr_skip = _code._instr_count - ctxt_start;
             _max.bytecode = curr_end;
 
-            _rule_length = 1;
-            _pre_context = 0;
+            _out_length = 1;
+            _out_index = 0;
+            _slotref = 0;
             _in_ctxt_item = false;
         }
         else
         {
-            _pre_context = 0;
+            _out_index = 0;
+            _slotref = 0;
             return false;
         }
     }
     
     return bool(_code);
 }
 
 
 void Machine::Code::decoder::apply_analysis(instr * const code, instr * code_end)
 {
     // insert TEMP_COPY commands for slots that need them (that change and are referenced later)
     int tempcount = 0;
     if (_code._constraint) return;
 
     const instr temp_copy = Machine::getOpcodeTable()[TEMP_COPY].impl[0];
-    for (const context * c = _analysis.contexts, * const ce = c + _analysis.slotref; c != ce; ++c)
+    for (const context * c = _contexts, * const ce = c + _slotref; c < ce; ++c)
     {
         if (!c->flags.referenced || !c->flags.changed) continue;
         
         instr * const tip = code + c->codeRef + tempcount;        
         memmove(tip+1, tip, (code_end - tip) * sizeof(instr));
         *tip = temp_copy;
         ++code_end;
         ++tempcount;
@@ -644,16 +632,21 @@ inline
 bool Machine::Code::decoder::validate_opcode(const opcode opc, const byte * const bc)
 {
     if (opc >= MAX_OPCODE)
     {
         failure(invalid_opcode);
         return false;
     }
     const opcode_t & op = Machine::getOpcodeTable()[opc];
+    if (op.impl[_code._constraint] == 0)
+    {
+        failure(unimplemented_opcode_used);
+        return false;
+    }
     if (op.param_sz == VARARGS && bc >= _max.bytecode)
     {
         failure(arguments_exhausted);
         return false;
     }
     const size_t param_sz = op.param_sz == VARARGS ? bc[0] + 1 : op.param_sz;
     if (bc - 1 + param_sz >= _max.bytecode)
     {
@@ -666,56 +659,59 @@ bool Machine::Code::decoder::validate_op
 
 bool Machine::Code::decoder::valid_upto(const uint16 limit, const uint16 x) const throw()
 {
     const bool t = (limit != 0) && (x < limit);
     if (!t) failure(out_of_range_data);
     return t;
 }
 
+inline
+bool Machine::Code::decoder::test_ref(int8 index) const throw()
+{
+    return valid_upto(_max.rule_length, _slotref + _max.pre_context + index);
+}
+
 bool Machine::Code::decoder::test_context() const throw()
 {
-    if (_pre_context >= _rule_length || _analysis.slotref >= analysis::NUMCONTEXTS - 1)
+    if (_out_index >= _out_length || _out_index < 0 || _slotref >= NUMCONTEXTS - 1)
     {
         failure(out_of_range_data);
         return false;
     }
     return true;
 }
 
 inline 
 void Machine::Code::failure(const status_t s) throw() {
     release_buffers();
     _status = s;
 }
 
 
 inline
-void Machine::Code::decoder::analysis::set_ref(int index, bool incinsert) throw() {
-    if (incinsert && contexts[slotref].flags.inserted) --index;
-    if (index + slotref < 0 || index + slotref >= NUMCONTEXTS) return;
-    contexts[index + slotref].flags.referenced = true;
-    if ((index > 0 || !contexts[index + slotref].flags.inserted) && index + slotref > max_ref) max_ref = index + slotref;
+void Machine::Code::decoder::set_ref(int index) throw() {
+    if (index + _slotref < 0 || index + _slotref >= NUMCONTEXTS) return;
+    _contexts[index + _slotref].flags.referenced = true;
+    if (index + _slotref > _max_ref) _max_ref = index + _slotref;
 }
 
 
 inline
-void Machine::Code::decoder::analysis::set_noref(int index) throw() {
-    if (contexts[slotref].flags.inserted) --index;
-    if (index + slotref < 0 || index + slotref >= NUMCONTEXTS) return;
-    if ((index > 0 || !contexts[index + slotref].flags.inserted) && index + slotref > max_ref) max_ref = index + slotref;
+void Machine::Code::decoder::set_noref(int index) throw() {
+    if (index + _slotref < 0 || index + _slotref >= NUMCONTEXTS) return;
+    if (index + _slotref > _max_ref) _max_ref = index + _slotref;
 }
 
 
 inline
-void Machine::Code::decoder::analysis::set_changed(int index) throw() {
-    if (contexts[slotref].flags.inserted) --index;
-    if (index + slotref < 0 || index + slotref >= NUMCONTEXTS) return;
-    contexts[index + slotref].flags.changed = true;
-    if ((index > 0 || !contexts[index + slotref].flags.inserted) && index + slotref > max_ref) max_ref = index + slotref;
+void Machine::Code::decoder::set_changed(int index) throw() {
+    if (index + _slotref < 0 || index + _slotref >= NUMCONTEXTS) return;
+    _contexts[index + _slotref].flags.changed= true;
+    if (index + _slotref > _max_ref) _max_ref = index + _slotref;
 }
 
 
 void Machine::Code::release_buffers() throw()
 {
     if (_own)
         free(_code);
     _code = 0;
--- a/gfx/graphite2/src/Face.cpp
+++ b/gfx/graphite2/src/Face.cpp
@@ -178,17 +178,18 @@ bool Face::runGraphite(Segment *seg, con
     if ((seg->dir() & 3) == 3 && aSilf->bidiPass() == 0xFF)
         seg->doMirror(aSilf->aMirror());
     bool res = aSilf->runGraphite(seg, 0, aSilf->positionPass(), true);
     if (res)
     {
         seg->associateChars(0, seg->charInfoCount());
         if (aSilf->flags() & 0x20)
             res &= seg->initCollisions();
-        res &= aSilf->runGraphite(seg, aSilf->positionPass(), aSilf->numPasses(), false);
+        if (res)
+            res &= aSilf->runGraphite(seg, aSilf->positionPass(), aSilf->numPasses(), false);
     }
 
 #if !defined GRAPHITE2_NTRACING
     if (dbgout)
 {
         seg->positionSlots(0, 0, 0, aSilf->dir());
         *dbgout             << json::item
                             << json::close // Close up the passes array
@@ -226,17 +227,17 @@ const Silf *Face::chooseSilf(uint32 scri
         return m_silfs;
 }
 
 uint16 Face::findPseudo(uint32 uid) const
 {
     return (m_numSilf) ? m_silfs[0].findPseudo(uid) : 0;
 }
 
-uint16 Face::getGlyphMetric(uint16 gid, uint8 metric) const
+int32 Face::getGlyphMetric(uint16 gid, uint8 metric) const
 {
     switch (metrics(metric))
     {
         case kgmetAscent : return m_ascent;
         case kgmetDescent : return m_descent;
         default: 
             if (gid >= glyphs().numGlyphs()) return 0;
             return glyphs().glyph(gid)->getMetric(metric);
@@ -277,17 +278,17 @@ Face::Table::Table(const Face & face, co
 : _f(&face), _compressed(false)
 {
     size_t sz = 0;
     _p = static_cast<const byte *>((*_f->m_ops.get_table)(_f->m_appFaceHandle, n, &sz));
     _sz = uint32(sz);
 
     if (!TtfUtil::CheckTable(n, _p, _sz))
     {
-        this->~Table();     // Make sure we release the table buffer even if the table filed it's checks
+        releaseBuffers();     // Make sure we release the table buffer even if the table failed it's checks
         return;
     }
 
     if (be::peek<uint32>(_p) >= version)
         decompress();
 }
 
 void Face::Table::releaseBuffers()
@@ -324,17 +325,18 @@ Error Face::Table::decompress()
     switch(compression(hdr >> 27))
     {
     case NONE: return e;
 
     case LZ4:
     {
         uncompressed_size  = hdr & 0x07ffffff;
         uncompressed_table = gralloc<byte>(uncompressed_size);
-        if (!e.test(!uncompressed_table, E_OUTOFMEM))
+        if (!e.test(!uncompressed_table || uncompressed_size < 4, E_OUTOFMEM))
+            memset(uncompressed_table, 0, 4);   // make sure version number is initialised
             // coverity[forward_null : FALSE] - uncompressed_table has been checked so can't be null
             // coverity[checked_return : FALSE] - we test e later
             e.test(lz4::decompress(p, _sz - 2*sizeof(uint32), uncompressed_table, uncompressed_size) != signed(uncompressed_size), E_SHRINKERFAILED);
         break;
     }
 
     default:
         e.error(E_BADSCHEME);
--- a/gfx/graphite2/src/GlyphCache.cpp
+++ b/gfx/graphite2/src/GlyphCache.cpp
@@ -111,18 +111,20 @@ private:
                     _num_glyphs_attributes,
                     _num_attrs;                    // number of glyph attributes per glyph
 };
 
 
 
 GlyphCache::GlyphCache(const Face & face, const uint32 face_options)
 : _glyph_loader(new Loader(face, bool(face_options & gr_face_dumbRendering))),
-  _glyphs(_glyph_loader && *_glyph_loader ? grzeroalloc<const GlyphFace *>(_glyph_loader->num_glyphs()) : 0),
-  _boxes(_glyph_loader && _glyph_loader->has_boxes() ? grzeroalloc<GlyphBox *>(_glyph_loader->num_glyphs()) : 0),
+  _glyphs(_glyph_loader && *_glyph_loader && _glyph_loader->num_glyphs()
+        ? grzeroalloc<const GlyphFace *>(_glyph_loader->num_glyphs()) : 0),
+  _boxes(_glyph_loader && _glyph_loader->has_boxes() && _glyph_loader->num_glyphs()
+        ? grzeroalloc<GlyphBox *>(_glyph_loader->num_glyphs()) : 0),
   _num_glyphs(_glyphs ? _glyph_loader->num_glyphs() : 0),
   _num_attrs(_glyphs ? _glyph_loader->num_attrs() : 0),
   _upem(_glyphs ? _glyph_loader->units_per_em() : 0)
 {
     if ((face_options & gr_face_preloadGlyphs) && _glyph_loader && _glyphs)
     {
         int numsubs = 0;
         GlyphFace * const glyphs = new GlyphFace [_num_glyphs];
@@ -139,17 +141,17 @@ GlyphCache::GlyphCache(const Face & face
         for (uint16 gid = 1; loaded && gid != _num_glyphs; ++gid)
             _glyphs[gid] = loaded = _glyph_loader->read_glyph(gid, glyphs[gid], &numsubs);
 
         if (!loaded)
         {
             _glyphs[0] = 0;
             delete [] glyphs;
         }
-        else if (numsubs > 0)
+        else if (numsubs > 0 && _boxes)
         {
             GlyphBox * boxes = (GlyphBox *)gralloc<char>(_num_glyphs * sizeof(GlyphBox) + numsubs * 8 * sizeof(float));
             GlyphBox * currbox = boxes;
 
             for (uint16 gid = 0; currbox && gid != _num_glyphs; ++gid)
             {
                 _boxes[gid] = currbox;
                 currbox = _glyph_loader->read_box(gid, currbox, *_glyphs[gid]);
@@ -280,26 +282,27 @@ GlyphCache::Loader::Loader(const Face & 
         _long_fmt              = flags & 1;
         int tmpnumgattrs       = (m_pGloc.size()
                                    - (p - m_pGloc)
                                    - sizeof(uint16)*(flags & 0x2 ? _num_attrs : 0))
                                        / (_long_fmt ? sizeof(uint32) : sizeof(uint16)) - 1;
 
         if (version >= 0x00020000 || tmpnumgattrs < 0 || tmpnumgattrs > 65535
             || _num_attrs == 0 || _num_attrs > 0x3000  // is this hard limit appropriate?
-            || _num_glyphs_graphics > tmpnumgattrs)
+            || _num_glyphs_graphics > tmpnumgattrs
+            || m_pGlat.size() < 4)
         {
             _head = Face::Table();
             return;
         }
 
         _num_glyphs_attributes = static_cast<unsigned short>(tmpnumgattrs);
         p = m_pGlat;
         version = be::read<uint32>(p);
-        if (version >= 0x00040000)       // reject Glat tables that are too new
+        if (version >= 0x00040000 || (version >= 0x00030000 && m_pGlat.size() < 8))       // reject Glat tables that are too new
         {
             _head = Face::Table();
             return;
         }
         else if (version >= 0x00030000)
         {
             unsigned int glatflags = be::read<uint32>(p);
             _has_boxes = glatflags & 1;
@@ -381,21 +384,21 @@ const GlyphFace * GlyphCache::Loader::re
         }
         else
         {
             be::skip<uint16>(gloc, glyphid);
             glocs = be::read<uint16>(gloc);
             gloce = be::peek<uint16>(gloc);
         }
 
-        if (glocs >= m_pGlat.size() || gloce > m_pGlat.size())
+        if (glocs + 1 >= m_pGlat.size() || gloce > m_pGlat.size())
             return 0;
 
         const uint32 glat_version = be::peek<uint32>(m_pGlat);
-        if (glat_version == 0x00030000)
+        if (glat_version >= 0x00030000)
         {
             const byte * p = m_pGlat + glocs;
             uint16 bmap = be::read<uint16>(p);
             int num = bit_set_count((uint32)bmap);
             if (numsubs) *numsubs += num;
             glocs += 6 + 8 * num;
             if (glocs > gloce)
                 return 0;
@@ -449,29 +452,31 @@ GlyphBox * GlyphCache::Loader::read_box(
     }
     else
     {
         be::skip<uint16>(gloc, gid);
         glocs = be::read<uint16>(gloc);
         gloce = be::peek<uint16>(gloc);
     }
 
-    if (glocs >= m_pGlat.size() || gloce > m_pGlat.size())
+    if (gloce > m_pGlat.size() || glocs + 6 >= gloce)
         return 0;
 
     const byte * p = m_pGlat + glocs;
     uint16 bmap = be::read<uint16>(p);
     int num = bit_set_count((uint32)bmap);
 
     Rect bbox = glyph.theBBox();
     Rect diamax(Position(bbox.bl.x + bbox.bl.y, bbox.bl.x - bbox.tr.y),
                 Position(bbox.tr.x + bbox.tr.y, bbox.tr.x - bbox.bl.y));
     Rect diabound = readbox(diamax, p[0], p[2], p[1], p[3]);
     ::new (curr) GlyphBox(num, bmap, &diabound);
     be::skip<uint8>(p, 4);
+    if (glocs + 6 + num * 8 >= gloce)
+        return 0;
 
     for (int i = 0; i < num * 2; ++i)
     {
         Rect box = readbox((i & 1) ? diamax : bbox, p[0], p[2], p[1], p[3]);
         curr->addSubBox(i >> 1, i & 1, &box);
         be::skip<uint8>(p, 4);
     } 
     return (GlyphBox *)((char *)(curr) + sizeof(GlyphBox) + 2 * num * sizeof(Rect));
--- a/gfx/graphite2/src/GlyphFace.cpp
+++ b/gfx/graphite2/src/GlyphFace.cpp
@@ -24,25 +24,25 @@ Mozilla Public License (http://mozilla.o
 License, as published by the Free Software Foundation, either version 2
 of the License or (at your option) any later version.
 */
 #include "inc/GlyphFace.h"
 
 
 using namespace graphite2;
 
-uint16 GlyphFace::getMetric(uint8 metric) const
+int32 GlyphFace::getMetric(uint8 metric) const
 {
     switch (metrics(metric))
     {
-        case kgmetLsb       : return static_cast<uint16>(m_bbox.bl.x);
-        case kgmetRsb       : return static_cast<uint16>(m_advance.x - m_bbox.tr.x);
-        case kgmetBbTop     : return static_cast<uint16>(m_bbox.tr.y);
-        case kgmetBbBottom  : return static_cast<uint16>(m_bbox.bl.y);
-        case kgmetBbLeft    : return static_cast<uint16>(m_bbox.bl.x);
-        case kgmetBbRight   : return static_cast<uint16>(m_bbox.tr.x);
-        case kgmetBbHeight  : return static_cast<uint16>(m_bbox.tr.y - m_bbox.bl.y);
-        case kgmetBbWidth   : return static_cast<uint16>(m_bbox.tr.x - m_bbox.bl.x);
-        case kgmetAdvWidth  : return static_cast<uint16>(m_advance.x);
-        case kgmetAdvHeight : return static_cast<uint16>(m_advance.y);
+        case kgmetLsb       : return m_bbox.bl.x;
+        case kgmetRsb       : return m_advance.x - m_bbox.tr.x;
+        case kgmetBbTop     : return m_bbox.tr.y;
+        case kgmetBbBottom  : return m_bbox.bl.y;
+        case kgmetBbLeft    : return m_bbox.bl.x;
+        case kgmetBbRight   : return m_bbox.tr.x;
+        case kgmetBbHeight  : return m_bbox.tr.y - m_bbox.bl.y;
+        case kgmetBbWidth   : return m_bbox.tr.x - m_bbox.bl.x;
+        case kgmetAdvWidth  : return m_advance.x;
+        case kgmetAdvHeight : return m_advance.y;
         default : return 0;
     }
 }
--- a/gfx/graphite2/src/NameTable.cpp
+++ b/gfx/graphite2/src/NameTable.cpp
@@ -42,25 +42,26 @@ NameTable::NameTable(const void* data, s
     memcpy(pdata, data, length);
     m_table = reinterpret_cast<const TtfUtil::Sfnt::FontNames*>(pdata);
 
     if ((length > sizeof(TtfUtil::Sfnt::FontNames)) &&
         (length > sizeof(TtfUtil::Sfnt::FontNames) +
          sizeof(TtfUtil::Sfnt::NameRecord) * ( be::swap<uint16>(m_table->count) - 1)))
     {
         uint16 offset = be::swap<uint16>(m_table->string_offset);
-        m_nameData = reinterpret_cast<const uint8*>(pdata) + offset;
-        setPlatformEncoding(platformId, encodingID);
-        m_nameDataLength = length - offset;
+        if (offset < length)
+        {
+            m_nameData = reinterpret_cast<const uint8*>(pdata) + offset;
+            setPlatformEncoding(platformId, encodingID);
+            m_nameDataLength = length - offset;
+            return;
+        }
     }
-    else
-    {
-        free(const_cast<TtfUtil::Sfnt::FontNames*>(m_table));
-        m_table = NULL;
-    }
+    free(const_cast<TtfUtil::Sfnt::FontNames*>(m_table));
+    m_table = NULL;
 }
 
 uint16 NameTable::setPlatformEncoding(uint16 platformId, uint16 encodingID)
 {
     if (!m_nameData) return 0;
     uint16 i = 0;
     uint16 count = be::swap<uint16>(m_table->count);
     for (; i < count; i++)
@@ -139,28 +140,36 @@ void* NameTable::getName(uint16& languag
     uint16 offset = be::swap<uint16>(nameRecord.offset);
     if(offset + utf16Length > m_nameDataLength)
     {
         languageId = 0;
         length = 0;
         return NULL;
     }
     utf16Length >>= 1; // in utf16 units
-    utf16::codeunit_t * utf16Name = gralloc<utf16::codeunit_t>(utf16Length);
+    utf16::codeunit_t * utf16Name = gralloc<utf16::codeunit_t>(utf16Length + 1);
     if (!utf16Name)
     {
         languageId = 0;
         length = 0;
         return NULL;
     }
     const uint8* pName = m_nameData + offset;
     for (size_t i = 0; i < utf16Length; i++)
     {
         utf16Name[i] = be::read<uint16>(pName);
     }
+    utf16Name[utf16Length] = 0;
+    if (!utf16::validate(utf16Name, utf16Name + utf16Length))
+    {
+        free(utf16Name);
+        languageId = 0;
+        length = 0;
+        return NULL;
+    }
     switch (enc)
     {
     case gr_utf8:
     {
         utf8::codeunit_t* uniBuffer = gralloc<utf8::codeunit_t>(3 * utf16Length + 1);
         if (!uniBuffer)
         {
             free(utf16Name);
--- a/gfx/graphite2/src/Pass.cpp
+++ b/gfx/graphite2/src/Pass.cpp
@@ -96,17 +96,17 @@ bool Pass::readPass(const byte * const p
     const byte * p              = pass_start,
                * const pass_end = p + pass_length;
     size_t numRanges;
 
     if (e.test(pass_length < 40, E_BADPASSLENGTH)) return face.error(e); 
     // Read in basic values
     const byte flags = be::read<byte>(p);
     if (e.test((flags & 0x1f) && 
-            (pt < PASS_TYPE_POSITIONING || !m_silf->aCollision() || !face.glyphs().hasBoxes()),
+            (pt < PASS_TYPE_POSITIONING || !m_silf->aCollision() || !face.glyphs().hasBoxes() || !(m_silf->flags() & 0x20)),
             E_BADCOLLISIONPASS))
         return face.error(e);
     m_numCollRuns = flags & 0x7;
     m_kernColls   = (flags >> 3) & 0x3;
     m_isReverseDir = (flags >> 5) & 0x1;
     m_iMaxLoop = be::read<byte>(p);
     if (m_iMaxLoop < 1) m_iMaxLoop = 1;
     be::skip<byte>(p,2); // skip maxContext & maxBackup
@@ -226,17 +226,21 @@ bool Pass::readRules(const byte * rule_m
     // Load rules.
     const byte * ac_begin = 0, * rc_begin = 0,
                * ac_end = ac_data + be::peek<uint16>(o_action),
                * rc_end = rc_data + be::peek<uint16>(o_constraint);
 
     // Allocate pools
     m_rules = new Rule [m_numRules];
     m_codes = new Code [m_numRules*2];
-    const size_t prog_pool_sz = vm::Machine::Code::estimateCodeDataOut(ac_end - ac_data + rc_end - rc_data);
+    int totalSlots = 0;
+    const uint16 *tsort = sort_key;
+    for (int i = 0; i < m_numRules; ++i)
+        totalSlots += be::peek<uint16>(--tsort);
+    const size_t prog_pool_sz = vm::Machine::Code::estimateCodeDataOut(ac_end - ac_data + rc_end - rc_data, 2 * m_numRules, totalSlots);
     m_progs = gralloc<byte>(prog_pool_sz);
     byte * prog_pool_free = m_progs,
          * prog_pool_end  = m_progs + prog_pool_sz;
     if (e.test(!(m_rules && m_codes && m_progs), E_OUTOFMEM)) return face.error(e);
 
     Rule * r = m_rules + m_numRules - 1;
     for (size_t n = m_numRules; r >= m_rules; --n, --r, ac_end = ac_begin, rc_end = rc_begin)
     {
@@ -249,17 +253,17 @@ bool Pass::readRules(const byte * rule_m
         if (r->sort > 63 || r->preContext >= r->sort || r->preContext > m_maxPreCtxt || r->preContext < m_minPreCtxt)
             return false;
         ac_begin      = ac_data + be::peek<uint16>(--o_action);
         --o_constraint;
         rc_begin      = be::peek<uint16>(o_constraint) ? rc_data + be::peek<uint16>(o_constraint) : rc_end;
 
         if (ac_begin > ac_end || ac_begin > ac_data_end || ac_end > ac_data_end
                 || rc_begin > rc_end || rc_begin > rc_data_end || rc_end > rc_data_end
-                || vm::Machine::Code::estimateCodeDataOut(ac_end - ac_begin + rc_end - rc_begin) > size_t(prog_pool_end - prog_pool_free))
+                || vm::Machine::Code::estimateCodeDataOut(ac_end - ac_begin + rc_end - rc_begin, 2, r->sort) > size_t(prog_pool_end - prog_pool_free))
             return false;
         r->action     = new (m_codes+n*2-2) vm::Machine::Code(false, ac_begin, ac_end, r->preContext, r->sort, *m_silf, face, pt, &prog_pool_free);
         r->constraint = new (m_codes+n*2-1) vm::Machine::Code(true,  rc_begin, rc_end, r->preContext, r->sort, *m_silf, face, pt, &prog_pool_free);
 
         if (e.test(!r->action || !r->constraint, E_OUTOFMEM)
                 || e.test(r->action->status() != Code::loaded, r->action->status() + E_CODEFAILURE)
                 || e.test(r->constraint->status() != Code::loaded, r->constraint->status() + E_CODEFAILURE)
                 || e.test(!r->constraint->immutable(), E_MUTABLECCODE))
@@ -351,17 +355,18 @@ bool Pass::readStates(const byte * start
         if (e.test(begin >= rule_map_end || end > rule_map_end || begin > end, E_BADRULEMAPPING))
         {
             face.error_context((face.error_context() & 0xFFFF00) + EC_ARULEMAP + (n << 24));
             return face.error(e);
         }
         s->rules = begin;
         s->rules_end = (end - begin <= FiniteStateMachine::MAX_RULES)? end :
             begin + FiniteStateMachine::MAX_RULES;
-        qsort(begin, end - begin, sizeof(RuleEntry), &cmpRuleEntry);
+        if (begin)      // keep UBSan happy can't call qsort with null begin
+            qsort(begin, end - begin, sizeof(RuleEntry), &cmpRuleEntry);
     }
 
     return true;
 }
 
 bool Pass::readRanges(const byte * ranges, size_t num_ranges, Error &e)
 {
     m_cols = gralloc<uint16>(m_numGlyphs);
@@ -449,19 +454,19 @@ bool Pass::runFSM(FiniteStateMachine& fs
     if (fsm.slots.context() < m_minPreCtxt)
         return false;
 
     uint16 state = m_startStates[m_maxPreCtxt - fsm.slots.context()];
     uint8  free_slots = SlotMap::MAX_SLOTS;
     do
     {
         fsm.slots.pushSlot(slot);
-        if (--free_slots == 0
-         || slot->gid() >= m_numGlyphs
+        if (slot->gid() >= m_numGlyphs
          || m_cols[slot->gid()] == 0xffffU
+         || --free_slots == 0
          || state >= m_numTransition)
             return free_slots != 0;
 
         const uint16 * transitions = m_transitions + state*m_numColumns;
         state = transitions[m_cols[slot->gid()]];
         if (state >= m_successStart)
             fsm.rules.accumulate_rules(m_states[state]);
 
@@ -627,37 +632,40 @@ bool Pass::testPassConstraint(Machine & 
 }
 
 
 bool Pass::testConstraint(const Rule & r, Machine & m) const
 {
     const uint16 curr_context = m.slotMap().context();
     if (unsigned(r.sort - r.preContext) > m.slotMap().size() - curr_context
         || curr_context - r.preContext < 0) return false;
+
+    vm::slotref * map = m.slotMap().begin() + curr_context - r.preContext;
+    if (map[r.sort - 1] == 0)
+        return false;
+
     if (!*r.constraint) return true;
     assert(r.constraint->constraint());
-
-    vm::slotref * map = m.slotMap().begin() + curr_context - r.preContext;
     for (int n = r.sort; n && map; --n, ++map)
     {
         if (!*map) continue;
         const int32 ret = r.constraint->run(m, map);
         if (!ret || m.status() != Machine::finished)
             return false;
     }
 
     return true;
 }
 
 
 void SlotMap::collectGarbage(Slot * &aSlot)
 {
     for(Slot **s = begin(), *const *const se = end() - 1; s != se; ++s) {
         Slot *& slot = *s;
-        if(slot->isDeleted() || slot->isCopied())
+        if(slot && (slot->isDeleted() || slot->isCopied()))
         {
             if (slot == aSlot)
                 aSlot = slot->prev() ? slot->prev() : slot->next();
             segment.freeSlot(slot);
         }
     }
 }
 
--- a/gfx/graphite2/src/Segment.cpp
+++ b/gfx/graphite2/src/Segment.cpp
@@ -419,16 +419,19 @@ Position Segment::positionSlots(const Fo
         reverseSlots();
         temp = iStart;
         iStart = iEnd;
         iEnd = temp;
     }
     if (!iStart)    iStart = m_first;
     if (!iEnd)      iEnd   = m_last;
 
+    if (!iStart || !iEnd)   // only true for empty segments
+        return currpos;
+
     if (isRtl)
     {
         for (Slot * s = iEnd, * const end = iStart->prev(); s && s != end; s = s->prev())
         {
             if (s->isBase())
                 currpos = s->finalise(this, font, currpos, bbox, 0, clusterMin = currpos.x, isRtl, isFinal);
         }
     }
@@ -526,11 +529,14 @@ void Segment::doMirror(uint16 aMirror)
 }
 
 bool Segment::initCollisions()
 {
     m_collisions = grzeroalloc<SlotCollision>(slotCount());
     if (!m_collisions) return false;
 
     for (Slot *p = m_first; p; p = p->next())
-        ::new (collisionInfo(p)) SlotCollision(this, p);
+        if (p->index() < slotCount())
+            ::new (collisionInfo(p)) SlotCollision(this, p);
+        else
+            return false;
     return true;
 }
--- a/gfx/graphite2/src/Silf.cpp
+++ b/gfx/graphite2/src/Silf.cpp
@@ -350,20 +350,20 @@ uint16 Silf::getClassGlyph(uint16 cid, u
     }
     return 0;
 }
 
 
 bool Silf::runGraphite(Segment *seg, uint8 firstPass, uint8 lastPass, int dobidi) const
 {
     assert(seg != 0);
-    SlotMap            map(*seg, m_dir);
+    unsigned int       maxSize = seg->slotCount() * MAX_SEG_GROWTH_FACTOR;
+    SlotMap            map(*seg, m_dir, maxSize);
     FiniteStateMachine fsm(map, seg->getFace()->logger());
     vm::Machine        m(map);
-    unsigned int       initSize = seg->slotCount();
     uint8              lbidi = m_bPass;
 #if !defined GRAPHITE2_NTRACING
     json * const dbgout = seg->getFace()->logger();
 #endif
 
     if (lastPass == 0)
     {
         if (firstPass == lastPass && lbidi == 0xFF)
@@ -419,13 +419,13 @@ bool Silf::runGraphite(Segment *seg, uin
 
         // test whether to reorder, prepare for positioning
         bool reverse = (lbidi == 0xFF) && (seg->currdir() != ((m_dir & 1) ^ m_passes[i].reverseDir()));
         if ((i >= 32 || (seg->passBits() & (1 << i)) == 0 || m_passes[i].collisionLoops())
                 && !m_passes[i].runGraphite(m, fsm, reverse))
             return false;
         // only subsitution passes can change segment length, cached subsegments are short for their text
         if (m.status() != vm::Machine::finished
-            || (seg->slotCount() && seg->slotCount() * MAX_SEG_GROWTH_FACTOR < initSize))
+            || (seg->slotCount() && seg->slotCount() > maxSize))
             return false;
     }
     return true;
 }
--- a/gfx/graphite2/src/Slot.cpp
+++ b/gfx/graphite2/src/Slot.cpp
@@ -80,20 +80,20 @@ void Slot::set(const Slot & orig, int ch
 
 void Slot::update(int /*numGrSlots*/, int numCharInfo, Position &relpos)
 {
     m_before += numCharInfo;
     m_after += numCharInfo;
     m_position = m_position + relpos;
 }
 
-Position Slot::finalise(const Segment *seg, const Font *font, Position & base, Rect & bbox, uint8 attrLevel, float & clusterMin, bool rtl, bool isFinal)
+Position Slot::finalise(const Segment *seg, const Font *font, Position & base, Rect & bbox, uint8 attrLevel, float & clusterMin, bool rtl, bool isFinal, int depth)
 {
     SlotCollision *coll = NULL;
-    if (attrLevel && m_attLevel > attrLevel) return Position(0, 0);
+    if (depth > 100 || (attrLevel && m_attLevel > attrLevel)) return Position(0, 0);
     float scale = font ? font->scale() : 1.0f;
     Position shift(m_shift.x * (rtl * -2 + 1) + m_just, m_shift.y);
     float tAdvance = m_advance.x + m_just;
     if (isFinal && (coll = seg->collisionInfo(this)))
     {
         const Position &collshift = coll->offset();
         if (!(coll->flags() & SlotCollision::COLL_KERN) || rtl)
             shift = shift + collshift;
@@ -128,23 +128,23 @@ Position Slot::finalise(const Segment *s
     if (glyphFace)
     {
         Rect ourBbox = glyphFace->theBBox() * scale + m_position;
         bbox = bbox.widen(ourBbox);
     }
 
     if (m_child && m_child != this && m_child->attachedTo() == this)
     {
-        Position tRes = m_child->finalise(seg, font, m_position, bbox, attrLevel, clusterMin, rtl, isFinal);
+        Position tRes = m_child->finalise(seg, font, m_position, bbox, attrLevel, clusterMin, rtl, isFinal, depth + 1);
         if ((!m_parent || m_advance.x >= 0.5f) && tRes.x > res.x) res = tRes;
     }
 
     if (m_parent && m_sibling && m_sibling != this && m_sibling->attachedTo() == m_parent)
     {
-        Position tRes = m_sibling->finalise(seg, font, base, bbox, attrLevel, clusterMin, rtl, isFinal);
+        Position tRes = m_sibling->finalise(seg, font, base, bbox, attrLevel, clusterMin, rtl, isFinal, depth + 1);
         if (tRes.x > res.x) res = tRes;
     }
     
     if (!m_parent && clusterMin < base.x)
     {
         Position adj = Position(m_position.x - clusterMin, 0.);
         res += adj;
         m_position += adj;
@@ -160,35 +160,35 @@ int32 Slot::clusterMetric(const Segment 
         return 0;
     Rect bbox = seg->theGlyphBBoxTemporary(glyph());
     float clusterMin = 0.;
     Position res = finalise(seg, NULL, base, bbox, attrLevel, clusterMin, rtl, false);
 
     switch (metrics(metric))
     {
     case kgmetLsb :
-        return static_cast<uint32>(bbox.bl.x);
+        return bbox.bl.x;
     case kgmetRsb :
-        return static_cast<uint32>(res.x - bbox.tr.x);
+        return res.x - bbox.tr.x;
     case kgmetBbTop :
-        return static_cast<uint32>(bbox.tr.y);
+        return bbox.tr.y;
     case kgmetBbBottom :
-        return static_cast<uint32>(bbox.bl.y);
+        return bbox.bl.y;
     case kgmetBbLeft :
-        return static_cast<uint32>(bbox.bl.x);
+        return bbox.bl.x;
     case kgmetBbRight :
-        return static_cast<uint32>(bbox.tr.x);
+        return bbox.tr.x;
     case kgmetBbWidth :
-        return static_cast<uint32>(bbox.tr.x - bbox.bl.x);
+        return bbox.tr.x - bbox.bl.x;
     case kgmetBbHeight :
-        return static_cast<uint32>(bbox.tr.y - bbox.bl.y);
+        return bbox.tr.y - bbox.bl.y;
     case kgmetAdvWidth :
-        return static_cast<uint32>(res.x);
+        return res.x;
     case kgmetAdvHeight :
-        return static_cast<uint32>(res.y);
+        return res.y;
     default :
         return 0;
     }
 }
 
 #define SLOTGETCOLATTR(x) { SlotCollision *c = seg->collisionInfo(this); return c ? int(c-> x) : 0; }
 
 int Slot::getAttr(const Segment *seg, attrCode ind, uint8 subindex) const
@@ -290,19 +290,32 @@ void Slot::setAttr(Segment *seg, attrCod
     case gr_slatAdvX :  m_advance.x = value; break;
     case gr_slatAdvY :  m_advance.y = value; break;
     case gr_slatAttTo :
     {
         const uint16 idx = uint16(value);
         if (idx < map.size() && map[idx])
         {
             Slot *other = map[idx];
-            if (other == this || other == m_parent) break;
-            if (m_parent) m_parent->removeChild(this);
-            if (!other->isChildOf(this) && other->child(this))
+            if (other == this || other == m_parent || other->isCopied()) break;
+            if (m_parent) { m_parent->removeChild(this); attachTo(NULL); }
+            Slot *pOther = other;
+            int count = 0;
+            bool foundOther = false;
+            while (pOther)
+            {
+                ++count;
+                if (pOther == this) foundOther = true;
+                pOther = pOther->attachedTo();
+            }
+            for (pOther = m_child; pOther; pOther = pOther->m_child)
+                ++count;
+            for (pOther = m_sibling; pOther; pOther = pOther->m_sibling)
+                ++count;
+            if (count < 100 && !foundOther && other->child(this))
             {
                 attachTo(other);
                 if ((map.dir() != 0) ^ (idx > subindex))
                     m_with = Position(advance(), 0);
                 else        // normal match to previous root
                     m_attach = Position(other->advance(), 0);
             }
         }
@@ -416,41 +429,34 @@ bool Slot::sibling(Slot *ap)
         m_sibling = ap;
     else
         return m_sibling->sibling(ap);
     return true;
 }
 
 bool Slot::removeChild(Slot *ap)
 {
-    if (this == ap || !m_child) return false;
+    if (this == ap || !m_child || !ap) return false;
     else if (ap == m_child)
     {
         Slot *nSibling = m_child->nextSibling();
-        m_child->removeSibling(nSibling);
+        m_child->nextSibling(NULL);
         m_child = nSibling;
         return true;
     }
-    else
-        return m_child->removeSibling(ap);
-    return true;
-}
-
-bool Slot::removeSibling(Slot *ap)
-{
-    if (this == ap || !m_sibling) return false;
-    else if (ap == m_sibling)
+    for (Slot *p = m_child; p; p = p->m_sibling)
     {
-        m_sibling = m_sibling->nextSibling();
-        if (m_sibling) ap->removeSibling(m_sibling);
-        return true;
+        if (p->m_sibling && p->m_sibling == ap)
+        {
+            p->m_sibling = p->m_sibling->m_sibling;
+            ap->nextSibling(NULL);
+            return true;
+        }
     }
-    else
-        return m_sibling->removeSibling(ap);
-    return true;
+    return false;
 }
 
 void Slot::setGlyph(Segment *seg, uint16 glyphid, const GlyphFace * theGlyph)
 {
     m_glyphid = glyphid;
     m_bidiCls = -1;
     if (!theGlyph)
     {
@@ -475,21 +481,23 @@ void Slot::setGlyph(Segment *seg, uint16
     if (seg->silf()->aPassBits())
     {
         seg->mergePassBits(theGlyph->attrs()[seg->silf()->aPassBits()]);
         if (seg->silf()->numPasses() > 16)
             seg->mergePassBits(theGlyph->attrs()[seg->silf()->aPassBits()+1] << 16);
     }
 }
 
-void Slot::floodShift(Position adj)
+void Slot::floodShift(Position adj, int depth)
 {
+    if (depth > 100)
+        return;
     m_position += adj;
-    if (m_child) m_child->floodShift(adj);
-    if (m_sibling) m_sibling->floodShift(adj);
+    if (m_child) m_child->floodShift(adj, depth + 1);
+    if (m_sibling) m_sibling->floodShift(adj, depth + 1);
 }
 
 void SlotJustify::LoadSlot(const Slot *s, const Segment *seg)
 {
     for (int i = seg->silf()->numJustLevels() - 1; i >= 0; --i)
     {
         Justinfo *justs = seg->silf()->justAttrs() + i;
         int16 *v = values + i * NUMJUSTPARAMS;
@@ -514,15 +522,14 @@ Slot * Slot::nextInCluster(const Slot *s
             return base->nextSibling();
         s = base;
     }
     return NULL;
 }
 
 bool Slot::isChildOf(const Slot *base) const
 {
-    if (m_parent == base)
-        return true;
-    else if (!m_parent)
-        return false;
-    else
-        return m_parent->isChildOf(base);
+    for (Slot *p = m_parent; p; p = p->m_parent)
+        if (p == base)
+            return true;
+    return false;
 }
+
--- a/gfx/graphite2/src/TtfUtil.cpp
+++ b/gfx/graphite2/src/TtfUtil.cpp
@@ -891,25 +891,27 @@ const void * FindCmapSubtable(const void
 ----------------------------------------------------------------------------------------------*/
 bool CheckCmapSubtable4(const void * pCmapSubtable4, const void * pCmapEnd /*, unsigned int maxgid*/)
 {
     size_t table_len = (const byte *)pCmapEnd - (const byte *)pCmapSubtable4;
     if (!pCmapSubtable4) return false;
     const Sfnt::CmapSubTable * pTable = reinterpret_cast<const Sfnt::CmapSubTable *>(pCmapSubtable4);
     // Bob H say some freeware TT fonts have version 1 (eg, CALIGULA.TTF) 
     // so don't check subtable version. 21 Mar 2002 spec changes version to language.
-    if (be::swap(pTable->format) != 4) return false;
+    if (table_len < sizeof(*pTable) || be::swap(pTable->format) != 4) return false;
     const Sfnt::CmapSubTableFormat4 * pTable4 = reinterpret_cast<const Sfnt::CmapSubTableFormat4 *>(pCmapSubtable4);
+    if (table_len < sizeof(*pTable4))
+        return false;
     uint16 length = be::swap(pTable4->length);
     if (length > table_len)
         return false;
     if (length < sizeof(Sfnt::CmapSubTableFormat4))
         return false;
     uint16 nRanges = be::swap(pTable4->seg_count_x2) >> 1;
-    if (length < sizeof(Sfnt::CmapSubTableFormat4) + 4 * nRanges * sizeof(uint16))
+    if (!nRanges || length < sizeof(Sfnt::CmapSubTableFormat4) + 4 * nRanges * sizeof(uint16))
         return false;
     // check last range is properly terminated
     uint16 chEnd = be::peek<uint16>(pTable4->end_code + nRanges - 1);
     if (chEnd != 0xFFFF)
         return false;
 #if 0
     int lastend = -1;
     for (int i = 0; i < nRanges; ++i)
@@ -999,17 +1001,17 @@ gid16 CmapSubtable4Lookup(const void * p
         uint16 idRangeOffset = be::peek<uint16>(pMid += nSeg);
 
         if (idRangeOffset == 0)
             return (uint16)(idDelta + nUnicodeId); // must use modulus 2^16
 
         // Look up value in glyphIdArray
         const ptrdiff_t offset = (nUnicodeId - chStart) + (idRangeOffset >> 1) +
                 (pMid - reinterpret_cast<const uint16 *>(pTable));
-        if (offset * 2 >= be::swap<uint16>(pTable->length))
+        if (offset * 2 + 1 >= be::swap<uint16>(pTable->length))
             return 0;
         gid16 nGlyphId = be::peek<uint16>(reinterpret_cast<const uint16 *>(pTable)+offset);
         // If this value is 0, return 0. Else add the idDelta
         return nGlyphId ? nGlyphId + idDelta : 0;
     }
 
     return 0;
 }
@@ -1081,19 +1083,21 @@ unsigned int CmapSubtable4NextCodepoint(
 /*----------------------------------------------------------------------------------------------
     Check the Microsoft UCS-4 subtable for expected values.
 ----------------------------------------------------------------------------------------------*/
 bool CheckCmapSubtable12(const void *pCmapSubtable12, const void *pCmapEnd /*, unsigned int maxgid*/)
 {
     size_t table_len = (const byte *)pCmapEnd - (const byte *)pCmapSubtable12;
     if (!pCmapSubtable12)  return false;
     const Sfnt::CmapSubTable * pTable = reinterpret_cast<const Sfnt::CmapSubTable *>(pCmapSubtable12);
-    if (be::swap(pTable->format) != 12)
+    if (table_len < sizeof(*pTable) || be::swap(pTable->format) != 12)
         return false;
     const Sfnt::CmapSubTableFormat12 * pTable12 = reinterpret_cast<const Sfnt::CmapSubTableFormat12 *>(pCmapSubtable12);
+    if (table_len < sizeof(*pTable12))
+        return false;
     uint32 length = be::swap(pTable12->length);
     if (length > table_len)
         return false;
     if (length < sizeof(Sfnt::CmapSubTableFormat12))
         return false;
     uint32 num_groups = be::swap(pTable12->num_groups);
     if (num_groups > 0x10000000 || length != (sizeof(Sfnt::CmapSubTableFormat12) + (num_groups - 1) * sizeof(uint32) * 3))
         return false;
--- a/gfx/graphite2/src/inc/Code.h
+++ b/gfx/graphite2/src/inc/Code.h
@@ -81,17 +81,17 @@ private:
                 _modify,
                 _delete;
     mutable bool _own;
 
     void release_buffers() throw ();
     void failure(const status_t) throw();
 
 public:
-    static size_t estimateCodeDataOut(size_t num_bytecodes);
+    static size_t estimateCodeDataOut(size_t num_bytecodes, int nRules, int nSlots);
 
     Code() throw();
     Code(bool is_constraint, const byte * bytecode_begin, const byte * const bytecode_end,
          uint8 pre_context, uint16 rule_length, const Silf &, const Face &,
          enum passtype pt, byte * * const _out = 0);
     Code(const Machine::Code &) throw();
     ~Code() throw();
     
@@ -107,19 +107,21 @@ public:
     void          externalProgramMoved(ptrdiff_t) throw();
 
     int32 run(Machine &m, slotref * & map) const;
     
     CLASS_NEW_DELETE;
 };
 
 inline
-size_t  Machine::Code::estimateCodeDataOut(size_t n_bc)
+size_t  Machine::Code::estimateCodeDataOut(size_t n_bc, int nRules, int nSlots)
 {
-    return (n_bc + 1) * (sizeof(instr)+sizeof(byte));
+    // max is: all codes are instructions + 1 for each rule + max tempcopies
+    // allocate space for separate maximal code and data then merge them later
+    return (n_bc + nRules + nSlots) * sizeof(instr) + n_bc * sizeof(byte);
 }
 
 
 inline Machine::Code::Code() throw()
 : _code(0), _data(0), _data_size(0), _instr_count(0), _max_ref(0),
   _status(loaded), _constraint(false), _modify(false), _delete(false),
   _own(false)
 {
--- a/gfx/graphite2/src/inc/Face.h
+++ b/gfx/graphite2/src/inc/Face.h
@@ -82,17 +82,17 @@ public:
     uint16              languageForLocale(const char * locale) const;
 
     // Features
     uint16              numFeatures() const;
     const FeatureRef  * featureById(uint32 id) const;
     const FeatureRef  * feature(uint16 index) const;
 
     // Glyph related
-    uint16 getGlyphMetric(uint16 gid, uint8 metric) const;
+    int32  getGlyphMetric(uint16 gid, uint8 metric) const;
     uint16 findPseudo(uint32 uid) const;
 
     // Errors
     unsigned int        error() const { return m_error; }
     bool                error(Error e) { m_error = e.error(); return false; }
     unsigned int        error_context() const { return m_error; }
     void                error_context(unsigned int errcntxt) { m_errcntxt = errcntxt; }
 
--- a/gfx/graphite2/src/inc/GlyphFace.h
+++ b/gfx/graphite2/src/inc/GlyphFace.h
@@ -46,17 +46,17 @@ class GlyphFace
 public:
     GlyphFace();
     template<typename I>
     GlyphFace(const Rect & bbox, const Position & adv, I first, const I last);
 
     const Position    & theAdvance() const;
     const Rect        & theBBox() const { return m_bbox; }
     const sparse      & attrs() const { return m_attrs; }
-    uint16              getMetric(uint8 metric) const;
+    int32               getMetric(uint8 metric) const;
 
     CLASS_NEW_DELETE;
 private:
     Rect     m_bbox;        // bounding box metrics in design units
     Position m_advance;     // Advance width and height in design units
     sparse   m_attrs;
 };
 
--- a/gfx/graphite2/src/inc/Rule.h
+++ b/gfx/graphite2/src/inc/Rule.h
@@ -97,17 +97,17 @@ bool State::empty() const
     return rules_end == rules;
 }
 
 
 class SlotMap
 {
 public:
   enum {MAX_SLOTS=64};
-  SlotMap(Segment & seg, uint8 direction);
+  SlotMap(Segment & seg, uint8 direction, int maxSize);
   
   Slot       * * begin();
   Slot       * * end();
   size_t         size() const;
   unsigned short context() const;
   void           reset(Slot &, unsigned short);
   
   Slot * const & operator[](int n) const;
@@ -116,23 +116,25 @@ public:
   void           collectGarbage(Slot *& aSlot);
 
   Slot         * highwater() { return m_highwater; }
   void           highwater(Slot *s) { m_highwater = s; m_highpassed = false; }
   bool           highpassed() const { return m_highpassed; }
   void           highpassed(bool v) { m_highpassed = v; }
 
   uint8          dir() const { return m_dir; }
+  int            decMax() { return --m_maxSize; }
 
   Segment &    segment;
 private:
   Slot         * m_slot_map[MAX_SLOTS+1];
   unsigned short m_size;
   unsigned short m_precontext;
   Slot         * m_highwater;
+  int            m_maxSize;
   uint8          m_dir;
   bool           m_highpassed;
 };
 
 
 class FiniteStateMachine
 {
 public:
@@ -237,18 +239,19 @@ void FiniteStateMachine::Rules::accumula
       return;
     }
   }
   while (rre != rrend && out != lrend) { *out++ = *rre++; }
   m_end = out;
 }
 
 inline
-SlotMap::SlotMap(Segment & seg, uint8 direction)
-: segment(seg), m_size(0), m_precontext(0), m_highwater(0), m_dir(direction), m_highpassed(false)
+SlotMap::SlotMap(Segment & seg, uint8 direction, int maxSize)
+: segment(seg), m_size(0), m_precontext(0), m_highwater(0),
+    m_maxSize(maxSize), m_dir(direction), m_highpassed(false)
 {
     m_slot_map[0] = 0;
 }
 
 inline
 Slot * * SlotMap::begin()
 {
   return &m_slot_map[1]; // allow map to go 1 before slot_map when inserting
--- a/gfx/graphite2/src/inc/Segment.h
+++ b/gfx/graphite2/src/inc/Segment.h
@@ -35,17 +35,17 @@ of the License or (at your option) any l
 #include "inc/FeatureVal.h"
 #include "inc/GlyphCache.h"
 #include "inc/GlyphFace.h"
 #include "inc/Slot.h"
 #include "inc/Position.h"
 #include "inc/List.h"
 #include "inc/Collider.h"
 
-#define MAX_SEG_GROWTH_FACTOR  256
+#define MAX_SEG_GROWTH_FACTOR  64
 
 namespace graphite2 {
 
 typedef Vector<Features>        FeatureList;
 typedef Vector<Slot *>          SlotRope;
 typedef Vector<int16 *>         AttributeRope;
 typedef Vector<SlotJustify *>   JustifyRope;
 
@@ -154,17 +154,17 @@ public:
     int8 getSlotBidiClass(Slot *s) const;
     void doMirror(uint16 aMirror);
     Slot *addLineEnd(Slot *nSlot);
     void delLineEnd(Slot *s);
     bool hasJustification() const { return m_justifies.size() != 0; }
     void reverseSlots();
 
     bool isWhitespace(const int cid) const;
-    bool hasCollisionInfo() const { return (m_flags & SEG_HASCOLLISIONS); }
+    bool hasCollisionInfo() const { return (m_flags & SEG_HASCOLLISIONS) && m_collisions; }
     SlotCollision *collisionInfo(const Slot *s) const { return m_collisions ? m_collisions + s->index() : 0; }
     CLASS_NEW_DELETE
 
 public:       //only used by: GrSegment* makeAndInitialize(const GrFont *font, const GrFace *face, uint32 script, const FeaturesHandle& pFeats/*must not be IsNull*/, encform enc, const void* pStart, size_t nChars, int dir);
     bool read_text(const Face *face, const Features* pFeats/*must not be NULL*/, gr_encform enc, const void*pStart, size_t nChars);
     void finalise(const Font *font, bool reverse=false);
     float justify(Slot *pSlot, const Font *font, float width, enum justFlags flags, Slot *pFirst, Slot *pLast);
     bool initCollisions();
--- a/gfx/graphite2/src/inc/Slot.h
+++ b/gfx/graphite2/src/inc/Slot.h
@@ -92,17 +92,17 @@ public:
     void adjKern(const Position &pos) { m_shift = m_shift + pos; m_advance = m_advance + pos; }
     void origin(const Position &pos) { m_position = pos + m_shift; }
     void originate(int ind) { m_original = ind; }
     int original() const { return m_original; }
     void before(int ind) { m_before = ind; }
     void after(int ind) { m_after = ind; }
     bool isBase() const { return (!m_parent); }
     void update(int numSlots, int numCharInfo, Position &relpos);
-    Position finalise(const Segment* seg, const Font* font, Position & base, Rect & bbox, uint8 attrLevel, float & clusterMin, bool rtl, bool isFinal);
+    Position finalise(const Segment* seg, const Font* font, Position & base, Rect & bbox, uint8 attrLevel, float & clusterMin, bool rtl, bool isFinal, int depth = 0);
     bool isDeleted() const { return (m_flags & DELETED) ? true : false; }
     void markDeleted(bool state) { if (state) m_flags |= DELETED; else m_flags &= ~DELETED; }
     bool isCopied() const { return (m_flags & COPIED) ? true : false; }
     void markCopied(bool state) { if (state) m_flags |= COPIED; else m_flags &= ~COPIED; }
     bool isPositioned() const { return (m_flags & POSITIONED) ? true : false; }
     void markPositioned(bool state) { if (state) m_flags |= POSITIONED; else m_flags &= ~POSITIONED; }
     bool isInsertBefore() const { return !(m_flags & INSERTED); }
     uint8 getBidiLevel() const { return m_bidiLevel; }
@@ -123,20 +123,19 @@ public:
     Position attachOffset() const { return m_attach - m_with; }
     Slot* firstChild() const { return m_child; }
     void firstChild(Slot *ap) { m_child = ap; }
     bool child(Slot *ap);
     Slot* nextSibling() const { return m_sibling; }
     void nextSibling(Slot *ap) { m_sibling = ap; }
     bool sibling(Slot *ap);
     bool removeChild(Slot *ap);
-    bool removeSibling(Slot *ap);
     int32 clusterMetric(const Segment* seg, uint8 metric, uint8 attrLevel, bool rtl);
     void positionShift(Position a) { m_position += a; }
-    void floodShift(Position adj);
+    void floodShift(Position adj, int depth = 0);
     float just() const { return m_just; }
     void just(float j) { m_just = j; }
     Slot *nextInCluster(const Slot *s) const;
     bool isChildOf(const Slot *base) const;
 
     CLASS_NEW_DELETE
 
 private:
--- a/gfx/graphite2/src/inc/UtfCodec.h
+++ b/gfx/graphite2/src/inc/UtfCodec.h
@@ -35,16 +35,17 @@ typedef uint32  uchar_t;
 
 template <int N>
 struct _utf_codec
 {
     typedef uchar_t codeunit_t;
 
     static void     put(codeunit_t * cp, const uchar_t , int8 & len) throw();
     static uchar_t  get(const codeunit_t * cp, int8 & len) throw();
+    static bool     validate(const codeunit_t * s, const codeunit_t * e) throw();
 };
 
 
 template <>
 struct _utf_codec<32>
 {
 private:
     static const uchar_t    limit = 0x110000;
@@ -58,16 +59,22 @@ public:
     }
 
     inline
     static uchar_t get(const codeunit_t * cp, int8 & l) throw()
     {
         if (cp[0] < limit)  { l = 1;  return cp[0]; }
         else                { l = -1; return 0xFFFD; }
     }
+
+    inline
+    static bool validate(codeunit_t * s, codeunit_t * e) throw()
+    {
+        return e > s;
+    }
 };
 
 
 template <>
 struct _utf_codec<16>
 {
 private:
     static const int32  lead_offset      = 0xD800 - (0x10000 >> 10);
@@ -88,22 +95,31 @@ public:
     }
 
     inline
     static uchar_t get(const codeunit_t * cp, int8 & l) throw()
     {
         const uint32    uh = cp[0];
         l = 1;
 
-        if (0xD800 > uh || uh > 0xDFFF) { return uh; }
+        if (uh < 0xD800|| uh > 0xDFFF) { return uh; }
         const uint32 ul = cp[1];
-        if (uh > 0xDBFF || 0xDC00 > ul || ul > 0xDFFF) { l = -1; return 0xFFFD; }
+        if (uh > 0xDBFF || ul < 0xDC00 || ul > 0xDFFF) { l = -1; return 0xFFFD; }
         ++l;
         return (uh<<10) + ul + surrogate_offset;
     }
+
+    inline
+    static bool validate(codeunit_t * s, codeunit_t * e) throw()
+    {
+        const ptrdiff_t n = e-s;
+        if (n <= 0) return n == 0;
+        const uint32 u = *(s+(n-1)); // Get the last codepoint
+        return (u < 0xD800 || u > 0xDBFF);
+    }
 };
 
 
 template <>
 struct _utf_codec<8>
 {
 private:
     static const int8 sz_lut[16];
@@ -143,16 +159,34 @@ public:
 
         if (l != seq_sz || toolong)
         {
             l = -l;
             return 0xFFFD;
         }
         return u;
     }
+
+    inline
+    static bool validate(codeunit_t * s, codeunit_t * e) throw()
+    {
+        const ptrdiff_t n = e-s;
+        if (n <= 0) return n == 0;
+        s += (n-1);
+        if (*s < 0x80) return true;
+        if (*s >= 0xC0) return false;
+        if (n == 1) return true;
+        if (*--s < 0x80) return true;
+        if (*s >= 0xe0) return false;
+        if (n == 2 || *s >= 0xC0) return true;
+        if (*--s < 0x80) return true;
+        if (*s >= 0xF0) return false;
+        return true;
+    }
+
 };
 
 
 template <typename C>
 class _utf_iterator
 {
     typedef _utf_codec<sizeof(C)*8> codec;
 
@@ -195,16 +229,21 @@ public:
 
 template <typename C>
 struct utf
 {
     typedef typename _utf_codec<sizeof(C)*8>::codeunit_t codeunit_t;
 
     typedef _utf_iterator<C>        iterator;
     typedef _utf_iterator<const C>  const_iterator;
+
+    inline
+    static bool validate(codeunit_t * s, codeunit_t * e) throw() {
+        return _utf_codec<sizeof(C)*8>::validate(s,e);
+    }
 };
 
 
 typedef utf<uint32> utf32;
 typedef utf<uint16> utf16;
 typedef utf<uint8>  utf8;
 
 } // namespace graphite2
--- a/gfx/graphite2/src/inc/opcode_table.h
+++ b/gfx/graphite2/src/inc/opcode_table.h
@@ -113,13 +113,13 @@ static const opcode_t opcode_table[] =
     {{NILOP,NILOP},                                 0, "PUT_SUBS3"},
     {{do_(put_glyph), NILOP},                       2, "PUT_GLYPH"},                // output_class output_class
     {{do2(push_glyph_attr)},                        3, "PUSH_GLYPH_ATTR"},          // gattrnum gattrnum slot
     {{do2(push_att_to_glyph_attr)},                 3, "PUSH_ATT_TO_GLYPH_ATTR"},   // gattrnum gattrnum slot
     {{do2(bor)},                                    0, "BITOR"},
     {{do2(band)},                                   0, "BITAND"},
     {{do2(bnot)},                                   0, "BITNOT"},   // 0x40
     {{do2(setbits)},                                4, "BITSET"},
-    {{do2(set_feat)},                               2, "SET_FEAT"},
+    {{do_(set_feat), NILOP},                        2, "SET_FEAT"},                 // featidx slot
     // private opcodes for internal use only, comes after all other on disk opcodes.
     {{do_(temp_copy), NILOP},                       0, "TEMP_COPY"}
 };
 
--- a/gfx/graphite2/src/inc/opcodes.h
+++ b/gfx/graphite2/src/inc/opcodes.h
@@ -237,17 +237,17 @@ STARTOP(put_subs_8bit_obs)
         index = seg.findClassIndex(input_class, slot->gid());
         is->setGlyph(&seg, seg.getClassGlyph(output_class, index));
     }
 ENDOP
 
 STARTOP(put_copy)
     declare_params(1);
     const int  slot_ref = int8(*param);
-    if (is)
+    if (is && !is->isDeleted())
     {
         slotref ref = slotat(slot_ref);
         if (ref && ref != is)
         {
             int16 *tempUserAttrs = is->userAttrs();
             if (is->attachedTo() || is->firstChild()) DIE
             Slot *prev = is->prev();
             Slot *next = is->next();
@@ -262,16 +262,17 @@ STARTOP(put_copy)
                 is->attachedTo()->child(is);
         }
         is->markCopied(false);
         is->markDeleted(false);
     }
 ENDOP
 
 STARTOP(insert)
+    if (smap.decMax() <= 0) DIE;
     Slot *newSlot = seg.newSlot();
     if (!newSlot) DIE;
     Slot *iss = is;
     while (iss && iss->isDeleted()) iss = iss->next();
     if (!iss)
     {
         if (seg.last())
         {
@@ -550,31 +551,31 @@ ENDOP
 
 STARTOP(iattr_add)
     declare_params(2);
     const attrCode      slat = attrCode(uint8(param[0]));
     const size_t        idx  = uint8(param[1]);
     const          int  val  = int(pop());
     if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0)
     {
-        seg.positionSlots(0, *smap.begin(), *(smap.end()-1), dir);
+        seg.positionSlots(0, *smap.begin(), *(smap.end()-1), seg.currdir());
         flags |= POSITIONED;
     }
     int res = is->getAttr(&seg, slat, idx);
     is->setAttr(&seg, slat, idx, val + res, smap);
 ENDOP
 
 STARTOP(iattr_sub)
     declare_params(2);
     const attrCode      slat = attrCode(uint8(param[0]));
     const size_t        idx  = uint8(param[1]);
     const          int  val  = int(pop());
     if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0)
     {
-        seg.positionSlots(0, *smap.begin(), *(smap.end()-1), dir);
+        seg.positionSlots(0, *smap.begin(), *(smap.end()-1), seg.currdir());
         flags |= POSITIONED;
     }
     int res = is->getAttr(&seg, slat, idx);
     is->setAttr(&seg, slat, idx, res - val, smap);
 ENDOP
 
 STARTOP(push_proc_state)
     use_params(1);