| 1 | // SPDX-License-Identifier: MIT OR MPL-2.0 OR LGPL-2.1-or-later OR GPL-2.0-or-later |
| 2 | // Copyright 2010, SIL International, All rights reserved. |
| 3 | |
| 4 | // This class represents loaded graphite stack machine code. It performs |
| 5 | // basic sanity checks, on the incoming code to prevent more obvious problems |
| 6 | // from crashing graphite. |
| 7 | // Author: Tim Eves |
| 8 | |
| 9 | #pragma once |
| 10 | |
| 11 | #include <cassert> |
| 12 | #include <graphite2/Types.h> |
| 13 | #include "inc/Main.h" |
| 14 | #include "inc/Machine.h" |
| 15 | |
| 16 | namespace graphite2 { |
| 17 | |
| 18 | class Silf; |
| 19 | class Face; |
| 20 | |
| 21 | enum passtype { |
| 22 | PASS_TYPE_UNKNOWN = 0, |
| 23 | PASS_TYPE_LINEBREAK, |
| 24 | PASS_TYPE_SUBSTITUTE, |
| 25 | PASS_TYPE_POSITIONING, |
| 26 | PASS_TYPE_JUSTIFICATION |
| 27 | }; |
| 28 | |
| 29 | namespace vm { |
| 30 | |
| 31 | class Machine::Code |
| 32 | { |
| 33 | public: |
| 34 | enum status_t |
| 35 | { |
| 36 | loaded, |
| 37 | alloc_failed, |
| 38 | invalid_opcode, |
| 39 | unimplemented_opcode_used, |
| 40 | out_of_range_data, |
| 41 | jump_past_end, |
| 42 | arguments_exhausted, |
| 43 | missing_return, |
| 44 | nested_context_item, |
| 45 | underfull_stack |
| 46 | }; |
| 47 | |
| 48 | private: |
| 49 | class decoder; |
| 50 | |
| 51 | instr * _code; |
| 52 | byte * _data; |
| 53 | size_t _data_size, |
| 54 | _instr_count; |
| 55 | byte _max_ref; |
| 56 | mutable status_t _status; |
| 57 | bool _constraint, |
| 58 | _modify, |
| 59 | _delete; |
| 60 | mutable bool _own; |
| 61 | |
| 62 | void release_buffers() throw (); |
| 63 | void failure(const status_t) throw(); |
| 64 | |
| 65 | public: |
| 66 | static size_t estimateCodeDataOut(size_t num_bytecodes, int nRules, int nSlots); |
| 67 | |
| 68 | Code() throw(); |
| 69 | Code(bool is_constraint, const byte * bytecode_begin, const byte * const bytecode_end, |
| 70 | uint8 pre_context, uint16 rule_length, const Silf &, const Face &, |
| 71 | enum passtype pt, byte * * const _out = 0); |
| 72 | Code(const Machine::Code &) throw(); |
| 73 | ~Code() throw(); |
| 74 | |
| 75 | Code & operator=(const Code &rhs) throw(); |
| 76 | operator bool () const throw() { return _code && status() == loaded; } |
| 77 | status_t status() const throw() { return _status; } |
| 78 | bool constraint() const throw() { return _constraint; } |
| 79 | size_t dataSize() const throw() { return _data_size; } |
| 80 | size_t instructionCount() const throw() { return _instr_count; } |
| 81 | bool immutable() const throw() { return !(_delete || _modify); } |
| 82 | bool deletes() const throw() { return _delete; } |
| 83 | size_t maxRef() const throw() { return _max_ref; } |
| 84 | void externalProgramMoved(ptrdiff_t) throw(); |
| 85 | |
| 86 | int32 run(Machine &m, slotref * & map) const; |
| 87 | |
| 88 | CLASS_NEW_DELETE; |
| 89 | }; |
| 90 | |
| 91 | inline |
| 92 | size_t Machine::Code::estimateCodeDataOut(size_t n_bc, int nRules, int nSlots) |
| 93 | { |
| 94 | // max is: all codes are instructions + 1 for each rule + max tempcopies |
| 95 | // allocate space for separate maximal code and data then merge them later |
| 96 | return (n_bc + nRules + nSlots) * sizeof(instr) + n_bc * sizeof(byte); |
| 97 | } |
| 98 | |
| 99 | |
| 100 | inline Machine::Code::Code() throw() |
| 101 | : _code(0), _data(0), _data_size(0), _instr_count(0), _max_ref(0), |
| 102 | _status(loaded), _constraint(false), _modify(false), _delete(false), |
| 103 | _own(false) |
| 104 | { |
| 105 | } |
| 106 | |
| 107 | inline Machine::Code::Code(const Machine::Code &obj) throw () |
| 108 | : _code(obj._code), |
| 109 | _data(obj._data), |
| 110 | _data_size(obj._data_size), |
| 111 | _instr_count(obj._instr_count), |
| 112 | _max_ref(obj._max_ref), |
| 113 | _status(obj._status), |
| 114 | _constraint(obj._constraint), |
| 115 | _modify(obj._modify), |
| 116 | _delete(obj._delete), |
| 117 | _own(obj._own) |
| 118 | { |
| 119 | obj._own = false; |
| 120 | } |
| 121 | |
| 122 | inline Machine::Code & Machine::Code::operator=(const Machine::Code &rhs) throw() { |
| 123 | if (_instr_count > 0) |
| 124 | release_buffers(); |
| 125 | _code = rhs._code; |
| 126 | _data = rhs._data; |
| 127 | _data_size = rhs._data_size; |
| 128 | _instr_count = rhs._instr_count; |
| 129 | _status = rhs._status; |
| 130 | _constraint = rhs._constraint; |
| 131 | _modify = rhs._modify; |
| 132 | _delete = rhs._delete; |
| 133 | _own = rhs._own; |
| 134 | rhs._own = false; |
| 135 | return *this; |
| 136 | } |
| 137 | |
| 138 | inline void Machine::Code::externalProgramMoved(ptrdiff_t dist) throw() |
| 139 | { |
| 140 | if (_code && !_own) |
| 141 | { |
| 142 | _code += dist / signed(sizeof(instr)); |
| 143 | _data += dist; |
| 144 | } |
| 145 | } |
| 146 | |
| 147 | } // namespace vm |
| 148 | } // namespace graphite2 |
| 149 | |