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