DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Implementation

Mercurial (409f3966645a)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * vim: set ts=8 sts=4 et sw=4 tw=99:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef frontend_BinTokenReaderBase_h
#define frontend_BinTokenReaderBase_h

#include "frontend/BinToken.h"
#include "frontend/TokenStream.h"

#include "js/Result.h"
#include "js/TypeDecls.h"

namespace js {
namespace frontend {

// A constant used by tokenizers to represent a null float.
extern const uint64_t NULL_FLOAT_REPRESENTATION;

class MOZ_STACK_CLASS BinTokenReaderBase
{
  public:
    template<typename T> using ErrorResult = mozilla::GenericErrorResult<T>;

    // The information needed to skip a subtree.
    class SkippableSubTree {
      public:
        SkippableSubTree(const uint8_t* start, const size_t length)
          : start_(start)
          , length_(length)
        { }

        // The position in the source buffer at which the subtree starts.
        //
        // `SkippableSubTree` does *not* attempt to keep anything alive.
        const uint8_t* start() const {
            return start_;
        }

        // The length of the subtree.
        size_t length() const {
            return length_;
        }
      private:
        const uint8_t* start_;
        const size_t length_;
    };

    /**
     * Return the position of the latest token.
     */
    TokenPos pos();
    TokenPos pos(size_t startOffset);
    size_t offset() const;

     /**
      * Poison this tokenizer.
      */
    void poison();

    /**
     * Raise an error.
     *
     * Once `raiseError` has been called, the tokenizer is poisoned.
     */
    MOZ_MUST_USE ErrorResult<JS::Error&> raiseError(const char* description);
    MOZ_MUST_USE ErrorResult<JS::Error&> raiseOOM();
    MOZ_MUST_USE ErrorResult<JS::Error&> raiseInvalidNumberOfFields(
        const BinKind kind, const uint32_t expected, const uint32_t got);
    MOZ_MUST_USE ErrorResult<JS::Error&> raiseInvalidField(const char* kind,
        const BinField field);

  protected:
    BinTokenReaderBase(JSContext* cx, const uint8_t* start, const size_t length)
        : cx_(cx)
        , poisoned_(false)
        , start_(start)
        , current_(start)
        , stop_(start + length)
        , latestKnownGoodPos_(0)
    { }

    /**
     * Read a single byte.
     */
    MOZ_MUST_USE JS::Result<uint8_t> readByte();

    /**
     * Read several bytes.
     *
     * If there is not enough data, or if the tokenizer has previously been
     * poisoned, return an error.
     */
    MOZ_MUST_USE JS::Result<Ok> readBuf(uint8_t* bytes, uint32_t len);

    /**
     * Read a sequence of chars, ensuring that they match an expected
     * sequence of chars.
     *
     * @param value The sequence of chars to expect, NUL-terminated.
     */
     template <size_t N>
     MOZ_MUST_USE JS::Result<Ok> readConst(const char (&value)[N])
     {
        updateLatestKnownGood();
        if (!matchConst(value, false))
            return raiseError("Could not find expected literal");
        return Ok();
     }

     /**
     * Read a sequence of chars, consuming the bytes only if they match an expected
     * sequence of chars.
     *
     * @param value The sequence of chars to expect, NUL-terminated.
     * @param expectNul If true, expect NUL in the stream, otherwise don't.
     * @return true if `value` represents the next few chars in the
     * internal buffer, false otherwise. If `true`, the chars are consumed,
     * otherwise there is no side-effect.
     */
    template <size_t N>
    MOZ_MUST_USE bool matchConst(const char (&value)[N], bool expectNul) {
        MOZ_ASSERT(N > 0);
        MOZ_ASSERT(value[N - 1] == 0);
        MOZ_ASSERT(!hasRaisedError());

        if (current_ + N - 1 > stop_)
            return false;

        // Perform lookup, without side-effects.
        if (!std::equal(current_, current_ + N + (expectNul ? 0 : -1)/*implicit NUL*/, value))
            return false;

        // Looks like we have a match. Now perform side-effects
        current_ += N + (expectNul ? 0 : -1);
        updateLatestKnownGood();
        return true;
    }

    void updateLatestKnownGood();

#ifdef DEBUG
    bool hasRaisedError() const;
#endif

    JSContext* cx_;

    // `true` if we have encountered an error. Errors are non recoverable.
    // Attempting to read from a poisoned tokenizer will cause assertion errors.
    bool poisoned_;

    // The first byte of the buffer. Not owned.
    const uint8_t* start_;

    // The current position.
    const uint8_t* current_;

    // The last+1 byte of the buffer.
    const uint8_t* stop_;

    // Latest known good position. Used for error reporting.
    size_t latestKnownGoodPos_;

  private:
    BinTokenReaderBase(const BinTokenReaderBase&) = delete;
    BinTokenReaderBase(BinTokenReaderBase&&) = delete;
    BinTokenReaderBase& operator=(BinTokenReaderBase&) = delete;
};

} // namespace frontend
} // namespace js

#endif // frontend_BinTokenReaderBase_h