gfx/graphite2/src/Code.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /*  GRAPHITE2 LICENSING
     3     Copyright 2010, SIL International
     4     All rights reserved.
     6     This library is free software; you can redistribute it and/or modify
     7     it under the terms of the GNU Lesser General Public License as published
     8     by the Free Software Foundation; either version 2.1 of License, or
     9     (at your option) any later version.
    11     This program is distributed in the hope that it will be useful,
    12     but WITHOUT ANY WARRANTY; without even the implied warranty of
    13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    14     Lesser General Public License for more details.
    16     You should also have received a copy of the GNU Lesser General Public
    17     License along with this library in the file named "LICENSE".
    18     If not, write to the Free Software Foundation, 51 Franklin Street, 
    19     Suite 500, Boston, MA 02110-1335, USA or visit their web page on the 
    20     internet at http://www.fsf.org/licenses/lgpl.html.
    22 Alternatively, the contents of this file may be used under the terms of the
    23 Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
    24 License, as published by the Free Software Foundation, either version 2
    25 of the License or (at your option) any later version.
    26 */
    27 // This class represents loaded graphite stack machine code.  It performs 
    28 // basic sanity checks, on the incoming code to prevent more obvious problems
    29 // from crashing graphite.
    30 // Author: Tim Eves
    32 #include <cassert>
    33 #include <cstddef>
    34 #include <cstdlib>
    35 #include <cstring>
    36 #include "graphite2/Segment.h"
    37 #include "inc/Code.h"
    38 #include "inc/Face.h"
    39 #include "inc/GlyphFace.h"
    40 #include "inc/GlyphCache.h"
    41 #include "inc/Machine.h"
    42 #include "inc/Rule.h"
    43 #include "inc/Silf.h"
    45 #include <stdio.h>
    47 #ifdef NDEBUG
    48 #ifdef __GNUC__
    49 #pragma GCC diagnostic ignored "-Wunused-parameter"
    50 #endif
    51 #endif
    54 using namespace graphite2;
    55 using namespace vm;
    57 namespace {
    59 inline bool is_return(const instr i) {
    60     const opcode_t * opmap = Machine::getOpcodeTable();
    61     const instr pop_ret  = *opmap[POP_RET].impl,
    62                 ret_zero = *opmap[RET_ZERO].impl,
    63                 ret_true = *opmap[RET_TRUE].impl;
    64     return i == pop_ret || i == ret_zero || i == ret_true;
    65 }
    67 struct context
    68 {
    69     context(uint8 ref=0) : codeRef(ref) {flags.changed=false; flags.referenced=false; flags.inserted=false;}
    70     struct { 
    71         uint8   changed:1,
    72                 referenced:1,
    73                 inserted:1;
    74     } flags;
    75     uint8       codeRef;
    76 };
    78 } // end namespace
    81 class Machine::Code::decoder
    82 {
    83 public:
    84     struct limits;
    85     struct analysis
    86     {
    87         uint8     slotref;
    88         context   contexts[256];
    89         byte      max_ref;
    91         analysis() : slotref(0), max_ref(0) {};
    92         void set_ref(int index) throw();
    93         void set_changed(int index) throw();
    95     };
    97     decoder(const limits & lims, Code &code) throw();
    99     bool        load(const byte * bc_begin, const byte * bc_end);
   100     void        apply_analysis(instr * const code, instr * code_end);
   101     byte        max_ref() { return _analysis.max_ref; }
   102     int         pre_context() const { return _pre_context; }
   104 private:
   105     opcode      fetch_opcode(const byte * bc);
   106     void        analyse_opcode(const opcode, const int8 * const dp) throw();
   107     bool        emit_opcode(opcode opc, const byte * & bc);
   108     bool        validate_opcode(const opcode opc, const byte * const bc);
   109     bool        valid_upto(const uint16 limit, const uint16 x) const throw();
   110     void        failure(const status_t s) const throw() { _code.failure(s); }
   112     Code              & _code;
   113     int                 _pre_context;
   114     uint16              _rule_length;
   115     instr             * _instr;
   116     byte              * _data;
   117     const limits      & _max;
   118     analysis            _analysis;
   119 };
   122 struct Machine::Code::decoder::limits
   123 {
   124   const byte * const bytecode;
   125   const uint8        pre_context;
   126   const uint16       rule_length,
   127                      classes,
   128                      glyf_attrs,
   129                      features;
   130   const byte         attrid[gr_slatMax];
   131 };
   133 inline Machine::Code::decoder::decoder(const limits & lims, Code &code) throw()
   134 : _code(code),
   135   _pre_context(code._constraint ? 0 : lims.pre_context), 
   136   _rule_length(code._constraint ? 1 : lims.rule_length), 
   137   _instr(code._code), _data(code._data), _max(lims)
   138 { }
   142 Machine::Code::Code(bool is_constraint, const byte * bytecode_begin, const byte * const bytecode_end,
   143            uint8 pre_context, uint16 rule_length, const Silf & silf, const Face & face)
   144  :  _code(0), _data(0), _data_size(0), _instr_count(0), _max_ref(0), _status(loaded),
   145     _constraint(is_constraint), _modify(false), _delete(false), _own(true)
   146 {
   147 #ifdef GRAPHITE2_TELEMETRY
   148     telemetry::category _code_cat(face.tele.code);
   149 #endif
   150     assert(bytecode_begin != 0);
   151     if (bytecode_begin == bytecode_end)
   152     {
   153       ::new (this) Code();
   154       return;
   155     }
   156     assert(bytecode_end > bytecode_begin);
   157     const opcode_t *    op_to_fn = Machine::getOpcodeTable();
   159     // Allocate code and dat target buffers, these sizes are a worst case 
   160     // estimate.  Once we know their real sizes the we'll shrink them.
   161     _code = static_cast<instr *>(malloc((bytecode_end - bytecode_begin)
   162                                              * sizeof(instr)));
   163     _data = static_cast<byte *>(malloc((bytecode_end - bytecode_begin)
   164                                              * sizeof(byte)));
   166     if (!_code || !_data) {
   167         failure(alloc_failed);
   168         return;
   169     }
   171     const decoder::limits lims = {
   172         bytecode_end,
   173         pre_context,
   174         rule_length,
   175         silf.numClasses(),
   176         face.glyphs().numAttrs(),
   177         face.numFeatures(), 
   178         {1,1,1,1,1,1,1,1, 
   179          1,1,1,1,1,1,1,255,
   180          1,1,1,1,1,1,1,1, 
   181          1,1,1,1,1,1,0,0, 
   182          0,0,0,0,0,0,0,0, 
   183          0,0,0,0,0,0,0,0, 
   184          0,0,0,0,0,0,0, silf.numUser()}
   185     };
   187     decoder dec(lims, *this);
   188     if(!dec.load(bytecode_begin, bytecode_end))
   189        return;
   191     // Is this an empty program?
   192     if (_instr_count == 0)
   193     {
   194       release_buffers();
   195       ::new (this) Code();
   196       return;
   197     }
   199     // When we reach the end check we've terminated it correctly
   200     if (!is_return(_code[_instr_count-1])) {
   201         failure(missing_return);
   202         return;
   203     }
   205     assert((_constraint && immutable()) || !_constraint);
   206     dec.apply_analysis(_code, _code + _instr_count);
   207     _max_ref = dec.max_ref();
   209     // Now we know exactly how much code and data the program really needs
   210     // realloc the buffers to exactly the right size so we don't waste any 
   211     // memory.
   212     assert((bytecode_end - bytecode_begin) >= std::ptrdiff_t(_instr_count));
   213     assert((bytecode_end - bytecode_begin) >= std::ptrdiff_t(_data_size));
   214     _code = static_cast<instr *>(realloc(_code, (_instr_count+1)*sizeof(instr)));
   215     _data = static_cast<byte *>(realloc(_data, _data_size*sizeof(byte)));
   217     if (!_code)
   218     {
   219         failure(alloc_failed);
   220         return;
   221     }
   223     // Make this RET_ZERO, we should never reach this but just in case ...
   224     _code[_instr_count] = op_to_fn[RET_ZERO].impl[_constraint];
   226 #ifdef GRAPHITE2_TELEMETRY
   227     telemetry::count_bytes(_data_size + (_instr_count+1)*sizeof(instr));
   228 #endif
   229 }
   231 Machine::Code::~Code() throw ()
   232 {
   233     if (_own)
   234         release_buffers();
   235 }
   238 bool Machine::Code::decoder::load(const byte * bc, const byte * bc_end)
   239 {
   240     while (bc < bc_end)
   241     {
   242         const opcode opc = fetch_opcode(bc++);
   243         if (opc == vm::MAX_OPCODE)
   244             return false;
   246         analyse_opcode(opc, reinterpret_cast<const int8 *>(bc));
   248         if (!emit_opcode(opc, bc))
   249             return false;
   250     }
   252     return bool(_code);
   253 }
   255 // Validation check and fixups.
   256 //
   258 opcode Machine::Code::decoder::fetch_opcode(const byte * bc)
   259 {
   260     const opcode opc = opcode(*bc++);
   262     // Do some basic sanity checks based on what we know about the opcode
   263     if (!validate_opcode(opc, bc))  return MAX_OPCODE;
   265     // And check it's arguments as far as possible
   266     switch (opc)
   267     {
   268         case NOP :
   269         case PUSH_BYTE :
   270         case PUSH_BYTEU :
   271         case PUSH_SHORT :
   272         case PUSH_SHORTU :
   273         case PUSH_LONG :
   274         case ADD :
   275         case SUB :
   276         case MUL :
   277         case DIV :
   278         case MIN_ :
   279         case MAX_ :
   280         case NEG :
   281         case TRUNC8 :
   282         case TRUNC16 :
   283         case COND :
   284         case AND :
   285         case OR :
   286         case NOT :
   287         case EQUAL :
   288         case NOT_EQ :
   289         case LESS :
   290         case GTR :
   291         case LESS_EQ :
   292         case GTR_EQ :
   293             break;
   294         case NEXT :
   295         case NEXT_N :           // runtime checked
   296         case COPY_NEXT :
   297             ++_pre_context;
   298             break;
   299         case PUT_GLYPH_8BIT_OBS :
   300             valid_upto(_max.classes, bc[0]);
   301             break;
   302         case PUT_SUBS_8BIT_OBS :
   303             valid_upto(_rule_length, _pre_context + int8(bc[0]));
   304             valid_upto(_max.classes, bc[1]);
   305             valid_upto(_max.classes, bc[2]);
   306             break;
   307         case PUT_COPY :
   308             valid_upto(_rule_length, _pre_context + int8(bc[0]));
   309             break;
   310         case INSERT :
   311             --_pre_context;
   312             break;
   313         case DELETE :
   314             break;
   315         case ASSOC :
   316             for (uint8 num = bc[0]; num; --num)
   317                 valid_upto(_rule_length, _pre_context + int8(bc[num]));
   318             break;
   319         case CNTXT_ITEM :
   320             valid_upto(_max.rule_length, _max.pre_context + int8(bc[0]));
   321             if (bc + 2 + bc[1] >= _max.bytecode)  failure(jump_past_end);
   322             if (_pre_context != 0)                failure(nested_context_item);
   323             break;
   324         case ATTR_SET :
   325         case ATTR_ADD :
   326         case ATTR_SUB :
   327         case ATTR_SET_SLOT :
   328             valid_upto(gr_slatMax, bc[0]);
   329             break;
   330         case IATTR_SET_SLOT :
   331             if (valid_upto(gr_slatMax, bc[0]))
   332                 valid_upto(_max.attrid[bc[0]], bc[1]);
   333             break;
   334         case PUSH_SLOT_ATTR :
   335             valid_upto(gr_slatMax, bc[0]);
   336             valid_upto(_rule_length, _pre_context + int8(bc[1]));
   337             break;
   338         case PUSH_GLYPH_ATTR_OBS :
   339             valid_upto(_max.glyf_attrs, bc[0]);
   340             valid_upto(_rule_length, _pre_context + int8(bc[1]));
   341             break;
   342         case PUSH_GLYPH_METRIC :
   343             valid_upto(kgmetDescent, bc[0]);
   344             valid_upto(_rule_length, _pre_context + int8(bc[1]));
   345             // level: dp[2] no check necessary
   346             break;
   347         case PUSH_FEAT :
   348             valid_upto(_max.features, bc[0]);
   349             valid_upto(_rule_length, _pre_context + int8(bc[1]));
   350             break;
   351         case PUSH_ATT_TO_GATTR_OBS :
   352             valid_upto(_max.glyf_attrs, bc[0]);
   353             valid_upto(_rule_length, _pre_context + int8(bc[1]));
   354             break;
   355         case PUSH_ATT_TO_GLYPH_METRIC :
   356             valid_upto(kgmetDescent, bc[0]);
   357             valid_upto(_rule_length, _pre_context + int8(bc[1]));
   358             // level: dp[2] no check necessary
   359             break;
   360         case PUSH_ISLOT_ATTR :
   361             if (valid_upto(gr_slatMax, bc[0]))
   362             {
   363                 valid_upto(_rule_length, _pre_context + int8(bc[1]));
   364                 valid_upto(_max.attrid[bc[0]], bc[2]);
   365             }
   366             break;
   367         case PUSH_IGLYPH_ATTR :// not implemented
   368         case POP_RET :
   369         case RET_ZERO :
   370         case RET_TRUE :
   371             break;
   372         case IATTR_SET :
   373         case IATTR_ADD :
   374         case IATTR_SUB :
   375             if (valid_upto(gr_slatMax, bc[0]))
   376                 valid_upto(_max.attrid[bc[0]], bc[1]);
   377             break;
   378         case PUSH_PROC_STATE :  // dummy: dp[0] no check necessary
   379         case PUSH_VERSION :
   380             break;
   381         case PUT_SUBS :
   382             valid_upto(_rule_length, _pre_context + int8(bc[0]));
   383             valid_upto(_max.classes, uint16(bc[1]<< 8) | bc[2]);
   384             valid_upto(_max.classes, uint16(bc[3]<< 8) | bc[4]);
   385             break;
   386         case PUT_SUBS2 :        // not implemented
   387         case PUT_SUBS3 :        // not implemented
   388             break;
   389         case PUT_GLYPH :
   390             valid_upto(_max.classes, uint16(bc[0]<< 8) | bc[1]);
   391             break;
   392         case PUSH_GLYPH_ATTR :
   393         case PUSH_ATT_TO_GLYPH_ATTR :
   394             valid_upto(_max.glyf_attrs, uint16(bc[0]<< 8) | bc[1]);
   395             valid_upto(_rule_length, _pre_context + int8(bc[2]));
   396             break;
   397         default:
   398             failure(invalid_opcode);
   399             break;
   400     }
   402     return bool(_code) ? opc : MAX_OPCODE;
   403 }
   406 void Machine::Code::decoder::analyse_opcode(const opcode opc, const int8  * arg) throw()
   407 {
   408   if (_code._constraint) return;
   410   switch (opc)
   411   {
   412     case DELETE :
   413       _code._delete = true;
   414       break;
   415     case PUT_GLYPH_8BIT_OBS :
   416     case PUT_GLYPH :
   417       _code._modify = true;
   418       _analysis.set_changed(_analysis.slotref);
   419       break;
   420     case NEXT :
   421     case COPY_NEXT :
   422       if (!_analysis.contexts[_analysis.slotref].flags.inserted)
   423         ++_analysis.slotref;
   424       _analysis.contexts[_analysis.slotref] = context(_code._instr_count+1);
   425       if (_analysis.slotref > _analysis.max_ref) _analysis.max_ref = _analysis.slotref;
   426       break;
   427     case INSERT :
   428       _analysis.contexts[_analysis.slotref].flags.inserted = true;
   429       _code._modify = true;
   430       break;
   431     case PUT_SUBS_8BIT_OBS :    // slotref on 1st parameter
   432     case PUT_SUBS : 
   433       _code._modify = true;
   434       _analysis.set_changed(_analysis.slotref);
   435       // no break
   436     case PUT_COPY :
   437     {
   438       if (arg[0] != 0) { _analysis.set_changed(_analysis.slotref); _code._modify = true; }
   439       if (arg[0] <= 0 && -arg[0] <= _analysis.slotref - _analysis.contexts[_analysis.slotref].flags.inserted)
   440         _analysis.set_ref(_analysis.slotref + arg[0] - _analysis.contexts[_analysis.slotref].flags.inserted);
   441       else if (_analysis.slotref + arg[0] > _analysis.max_ref) _analysis.max_ref = _analysis.slotref + arg[0];
   442       break;
   443     }
   444     case PUSH_ATT_TO_GATTR_OBS : // slotref on 2nd parameter
   445         if (_code._constraint) return;
   446         // no break
   447     case PUSH_GLYPH_ATTR_OBS :
   448     case PUSH_SLOT_ATTR :
   449     case PUSH_GLYPH_METRIC :
   450     case PUSH_ATT_TO_GLYPH_METRIC :
   451     case PUSH_ISLOT_ATTR :
   452     case PUSH_FEAT :
   453       if (arg[1] <= 0 && -arg[1] <= _analysis.slotref - _analysis.contexts[_analysis.slotref].flags.inserted)
   454         _analysis.set_ref(_analysis.slotref + arg[1] - _analysis.contexts[_analysis.slotref].flags.inserted);
   455       else if (_analysis.slotref + arg[1] > _analysis.max_ref) _analysis.max_ref = _analysis.slotref + arg[1];
   456       break;
   457     case PUSH_ATT_TO_GLYPH_ATTR :
   458         if (_code._constraint) return;
   459         // no break
   460     case PUSH_GLYPH_ATTR :
   461       if (arg[2] <= 0 && -arg[2] <= _analysis.slotref - _analysis.contexts[_analysis.slotref].flags.inserted)
   462         _analysis.set_ref(_analysis.slotref + arg[2] - _analysis.contexts[_analysis.slotref].flags.inserted);
   463       else if (_analysis.slotref + arg[2] > _analysis.max_ref) _analysis.max_ref = _analysis.slotref + arg[2];
   464       break;
   465     case ASSOC :                // slotrefs in varargs
   466       break;
   467     default:
   468         break;
   469   }
   470 }
   473 bool Machine::Code::decoder::emit_opcode(opcode opc, const byte * & bc)
   474 {
   475     const opcode_t * op_to_fn = Machine::getOpcodeTable();
   476     const opcode_t & op       = op_to_fn[opc];
   477     if (op.impl[_code._constraint] == 0)
   478     {
   479         failure(unimplemented_opcode_used);
   480         return false;
   481     }
   483     const size_t     param_sz = op.param_sz == VARARGS ? bc[0] + 1 : op.param_sz;
   485     // Add this instruction
   486     *_instr++ = op.impl[_code._constraint]; 
   487     ++_code._instr_count;
   489     // Grab the parameters
   490     if (param_sz) {
   491         memcpy(_data, bc, param_sz * sizeof(byte));
   492         bc               += param_sz;
   493         _data            += param_sz;
   494         _code._data_size += param_sz;
   495     }
   497     // recursively decode a context item so we can split the skip into 
   498     // instruction and data portions.
   499     if (opc == CNTXT_ITEM)
   500     {
   501         assert(_pre_context == 0);
   502         _pre_context = _max.pre_context + int8(_data[-2]);
   503         _rule_length = _max.rule_length;
   505         const size_t ctxt_start = _code._instr_count;
   506         byte & instr_skip = _data[-1];
   507         byte & data_skip  = *_data++;
   508         ++_code._data_size;
   510         if (load(bc, bc + instr_skip))
   511         {
   512             bc += instr_skip;
   513             data_skip  = instr_skip - (_code._instr_count - ctxt_start);
   514             instr_skip = _code._instr_count - ctxt_start;
   516             _rule_length = 1;
   517             _pre_context = 0;
   518         }
   519     }
   521     return bool(_code);
   522 }
   525 void Machine::Code::decoder::apply_analysis(instr * const code, instr * code_end)
   526 {
   527     // insert TEMP_COPY commands for slots that need them (that change and are referenced later)
   528     int tempcount = 0;
   529     if (_code._constraint) return;
   531     const instr temp_copy = Machine::getOpcodeTable()[TEMP_COPY].impl[0];
   532     for (const context * c = _analysis.contexts, * const ce = c + _analysis.slotref; c != ce; ++c)
   533     {
   534         if (!c->flags.referenced || !c->flags.changed) continue;
   536         instr * const tip = code + c->codeRef + tempcount;        
   537         memmove(tip+1, tip, (code_end - tip) * sizeof(instr));
   538         *tip = temp_copy;
   539         ++code_end;
   540         ++tempcount;
   541     }
   543     _code._instr_count = code_end - code;
   544 }
   547 inline
   548 bool Machine::Code::decoder::validate_opcode(const opcode opc, const byte * const bc)
   549 {
   550     if (opc >= MAX_OPCODE)
   551     {
   552         failure(invalid_opcode);
   553         return false;
   554     }
   555     const opcode_t & op = Machine::getOpcodeTable()[opc];
   556     const size_t param_sz = op.param_sz == VARARGS ? bc[0] + 1 : op.param_sz;
   557     if (bc + param_sz > _max.bytecode)
   558     {
   559         failure(arguments_exhausted);
   560         return false;
   561     }
   562     return true;
   563 }
   566 bool Machine::Code::decoder::valid_upto(const uint16 limit, const uint16 x) const throw()
   567 {
   568     const bool t = x < limit;
   569     if (!t) failure(out_of_range_data);
   570     return t;
   571 }
   574 inline 
   575 void Machine::Code::failure(const status_t s) throw() {
   576     release_buffers();
   577     _status = s;
   578 }
   581 inline
   582 void Machine::Code::decoder::analysis::set_ref(const int index) throw() {
   583     contexts[index].flags.referenced = true;
   584     if (index > max_ref) max_ref = index;
   585 }
   588 inline
   589 void Machine::Code::decoder::analysis::set_changed(const int index) throw() {
   590     contexts[index].flags.changed = true;
   591     if (index > max_ref) max_ref = index;
   592 }
   595 void Machine::Code::release_buffers() throw()
   596 {
   597     free(_code);
   598     free(_data);
   599     _code = 0;
   600     _data = 0;
   601     _own  = false;
   602 }
   605 int32 Machine::Code::run(Machine & m, slotref * & map) const
   606 {
   607     assert(_own);
   608     assert(*this);          // Check we are actually runnable
   610     if (m.slotMap().size() <= size_t(_max_ref + m.slotMap().context()))
   611     {
   612         m._status = Machine::slot_offset_out_bounds;
   613 //        return (m.slotMap().end() - map);
   614         return 1;
   615     }
   617     return  m.run(_code, _data, map);
   618 }

mercurial