summaryrefslogtreecommitdiffstats
path: root/js/public/SourceText.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--js/public/SourceText.h353
1 files changed, 353 insertions, 0 deletions
diff --git a/js/public/SourceText.h b/js/public/SourceText.h
new file mode 100644
index 0000000000..88592be875
--- /dev/null
+++ b/js/public/SourceText.h
@@ -0,0 +1,353 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+/*
+ * SourceText encapsulates a count of char16_t (UTF-16) or Utf8Unit (UTF-8)
+ * code units (note: code *units*, not bytes or code points) and those code
+ * units ("source units"). (Latin-1 is not supported: all places where Latin-1
+ * must be compiled first convert to a supported encoding.)
+ *
+ * A SourceText either observes without owning, or takes ownership of, source
+ * units passed to |SourceText::init|. Thus SourceText can be used to
+ * efficiently avoid copying.
+ *
+ * Rules for use:
+ *
+ * 1) The passed-in source units must be allocated with js_malloc(),
+ * js_calloc(), or js_realloc() if |SourceText::init| is instructed to take
+ * ownership of the source units.
+ * 2) If |SourceText::init| merely borrows the source units, the user must
+ * keep them alive until associated JS compilation is complete.
+ * 3) Code that calls |SourceText::take{Chars,Units}()| must keep the source
+ * units alive until JS compilation completes. Normally only the JS engine
+ * should call |SourceText::take{Chars,Units}()|.
+ * 4) Use the appropriate SourceText parameterization depending on the source
+ * units encoding.
+ *
+ * Example use:
+ *
+ * size_t length = 512;
+ * char16_t* chars = js_pod_malloc<char16_t>(length);
+ * if (!chars) {
+ * JS_ReportOutOfMemory(cx);
+ * return false;
+ * }
+ * JS::SourceText<char16_t> srcBuf;
+ * if (!srcBuf.init(cx, chars, length, JS::SourceOwnership::TakeOwnership)) {
+ * return false;
+ * }
+ * JS::Rooted<JSScript*> script(cx);
+ * if (!JS::Compile(cx, options, srcBuf, &script)) {
+ * return false;
+ * }
+ */
+
+#ifndef js_SourceText_h
+#define js_SourceText_h
+
+#include "mozilla/Assertions.h" // MOZ_ASSERT
+#include "mozilla/Attributes.h" // MOZ_COLD, MOZ_IS_CLASS_INIT
+#include "mozilla/Likely.h" // MOZ_UNLIKELY
+
+#include <stddef.h> // size_t
+#include <stdint.h> // UINT32_MAX
+#include <type_traits> // std::conditional_t, std::is_same_v
+
+#include "js/UniquePtr.h" // js::UniquePtr
+#include "js/Utility.h" // JS::FreePolicy
+
+namespace mozilla {
+union Utf8Unit;
+}
+
+namespace js {
+class FrontendContext;
+} // namespace js
+
+namespace JS {
+
+class JS_PUBLIC_API AutoStableStringChars;
+using FrontendContext = js::FrontendContext;
+
+namespace detail {
+
+MOZ_COLD extern JS_PUBLIC_API void ReportSourceTooLong(JSContext* cx);
+MOZ_COLD extern JS_PUBLIC_API void ReportSourceTooLong(JS::FrontendContext* fc);
+
+} // namespace detail
+
+enum class SourceOwnership {
+ Borrowed,
+ TakeOwnership,
+};
+
+template <typename Unit>
+class SourceText final {
+ private:
+ static_assert(std::is_same_v<Unit, mozilla::Utf8Unit> ||
+ std::is_same_v<Unit, char16_t>,
+ "Unit must be either char16_t or Utf8Unit for "
+ "SourceText<Unit>");
+
+ /** |char16_t| or |Utf8Unit| source units of uncertain validity. */
+ const Unit* units_ = nullptr;
+
+ /** The length in code units of |units_|. */
+ uint32_t length_ = 0;
+
+ /**
+ * Whether this owns |units_| or merely observes source units owned by some
+ * other object.
+ */
+ bool ownsUnits_ = false;
+
+ public:
+ // A C++ character type that can represent the source units -- suitable for
+ // passing to C++ string functions.
+ using CharT =
+ std::conditional_t<std::is_same_v<Unit, char16_t>, char16_t, char>;
+
+ public:
+ /**
+ * Construct a SourceText. It must be initialized using |init()| before it
+ * can be used as compilation source text.
+ */
+ SourceText() = default;
+
+ /**
+ * Construct a SourceText from contents extracted from |other|. This
+ * SourceText will then act exactly as |other| would have acted, had it
+ * not been passed to this function. |other| will return to its default-
+ * constructed state and must have |init()| called on it to use it.
+ */
+ SourceText(SourceText&& other)
+ : units_(other.units_),
+ length_(other.length_),
+ ownsUnits_(other.ownsUnits_) {
+ other.units_ = nullptr;
+ other.length_ = 0;
+ other.ownsUnits_ = false;
+ }
+
+ ~SourceText() {
+ if (ownsUnits_) {
+ js_free(const_cast<Unit*>(units_));
+ }
+ }
+
+ private:
+ template <typename ContextT>
+ [[nodiscard]] MOZ_IS_CLASS_INIT bool initImpl(ContextT* context,
+ const Unit* units,
+ size_t unitsLength,
+ SourceOwnership ownership) {
+ MOZ_ASSERT_IF(units == nullptr, unitsLength == 0);
+
+ // Ideally we'd use |Unit| and not cast below, but the risk of a static
+ // initializer is too great.
+ static const CharT emptyString[] = {'\0'};
+
+ // Initialize all fields *before* checking length. This ensures that
+ // if |ownership == SourceOwnership::TakeOwnership|, |units| will be
+ // freed when |this|'s destructor is called.
+ if (units) {
+ units_ = units;
+ length_ = static_cast<uint32_t>(unitsLength);
+ ownsUnits_ = ownership == SourceOwnership::TakeOwnership;
+ } else {
+ units_ = reinterpret_cast<const Unit*>(emptyString);
+ length_ = 0;
+ ownsUnits_ = false;
+ }
+
+ // IMPLEMENTATION DETAIL, DO NOT RELY ON: This limit is used so we can
+ // store offsets in |JSScript|s as |uint32_t|. It could be lifted
+ // fairly easily if desired, as the compiler uses |size_t| internally.
+ if (MOZ_UNLIKELY(unitsLength > UINT32_MAX)) {
+ detail::ReportSourceTooLong(context);
+ return false;
+ }
+
+ return true;
+ }
+
+ public:
+ /**
+ * Initialize this with source unit data: |char16_t| for UTF-16 source
+ * units, or |Utf8Unit| for UTF-8 source units.
+ *
+ * If |ownership == TakeOwnership|, *this function* takes ownership of
+ * |units|, *even if* this function fails, and you MUST NOT free |units|
+ * yourself. This single-owner-friendly approach reduces risk of leaks on
+ * failure.
+ *
+ * |units| may be null if |unitsLength == 0|; if so, this will silently be
+ * initialized using non-null, unowned units.
+ */
+ [[nodiscard]] MOZ_IS_CLASS_INIT bool init(JSContext* cx, const Unit* units,
+ size_t unitsLength,
+ SourceOwnership ownership) {
+ return initImpl(cx, units, unitsLength, ownership);
+ }
+ [[nodiscard]] MOZ_IS_CLASS_INIT bool init(JS::FrontendContext* fc,
+ const Unit* units,
+ size_t unitsLength,
+ SourceOwnership ownership) {
+ return initImpl(fc, units, unitsLength, ownership);
+ }
+
+ /**
+ * Exactly identical to the |init()| overload above that accepts
+ * |const Unit*|, but instead takes character data: |const CharT*|.
+ *
+ * (We can't just write this to accept |const CharT*|, because then in the
+ * UTF-16 case this overload and the one above would be identical. So we
+ * use SFINAE to expose the |CharT| overload only if it's different.)
+ */
+ template <typename Char,
+ typename = std::enable_if_t<std::is_same_v<Char, CharT> &&
+ !std::is_same_v<Char, Unit>>>
+ [[nodiscard]] MOZ_IS_CLASS_INIT bool init(JSContext* cx, const Char* chars,
+ size_t charsLength,
+ SourceOwnership ownership) {
+ return initImpl(cx, reinterpret_cast<const Unit*>(chars), charsLength,
+ ownership);
+ }
+ template <typename Char,
+ typename = std::enable_if_t<std::is_same_v<Char, CharT> &&
+ !std::is_same_v<Char, Unit>>>
+ [[nodiscard]] MOZ_IS_CLASS_INIT bool init(JS::FrontendContext* fc,
+ const Char* chars,
+ size_t charsLength,
+ SourceOwnership ownership) {
+ return initImpl(fc, reinterpret_cast<const Unit*>(chars), charsLength,
+ ownership);
+ }
+
+ /**
+ * Initialize this using source units transferred out of |data|.
+ */
+ [[nodiscard]] bool init(JSContext* cx,
+ js::UniquePtr<Unit[], JS::FreePolicy> data,
+ size_t dataLength) {
+ return initImpl(cx, data.release(), dataLength,
+ SourceOwnership::TakeOwnership);
+ }
+ [[nodiscard]] bool init(JS::FrontendContext* fc,
+ js::UniquePtr<Unit[], JS::FreePolicy> data,
+ size_t dataLength) {
+ return initImpl(fc, data.release(), dataLength,
+ SourceOwnership::TakeOwnership);
+ }
+
+ /**
+ * Exactly identical to the |init()| overload above that accepts
+ * |UniquePtr<Unit[], JS::FreePolicy>|, but instead takes character data:
+ * |UniquePtr<CharT[], JS::FreePolicy>|.
+ *
+ * (We can't just duplicate the signature above with s/Unit/CharT/, because
+ * then in the UTF-16 case this overload and the one above would be identical.
+ * So we use SFINAE to expose the |CharT| overload only if it's different.)
+ */
+ template <typename Char,
+ typename = std::enable_if_t<std::is_same_v<Char, CharT> &&
+ !std::is_same_v<Char, Unit>>>
+ [[nodiscard]] bool init(JSContext* cx,
+ js::UniquePtr<Char[], JS::FreePolicy> data,
+ size_t dataLength) {
+ return init(cx, data.release(), dataLength, SourceOwnership::TakeOwnership);
+ }
+ template <typename Char,
+ typename = std::enable_if_t<std::is_same_v<Char, CharT> &&
+ !std::is_same_v<Char, Unit>>>
+ [[nodiscard]] bool init(JS::FrontendContext* fc,
+ js::UniquePtr<Char[], JS::FreePolicy> data,
+ size_t dataLength) {
+ return init(fc, data.release(), dataLength, SourceOwnership::TakeOwnership);
+ }
+
+ /**
+ * Initialize this using an AutoStableStringChars. Transfers the code units if
+ * they are owned by the AutoStableStringChars, otherwise borrow directly from
+ * the underlying JSString. The AutoStableStringChars must outlive this
+ * SourceText and must be explicitly configured to the same unit type as this
+ * SourceText.
+ */
+ [[nodiscard]] bool initMaybeBorrowed(JSContext* cx,
+ AutoStableStringChars& linearChars);
+ [[nodiscard]] bool initMaybeBorrowed(JS::FrontendContext* fc,
+ AutoStableStringChars& linearChars);
+
+ /**
+ * Access the encapsulated data using a code unit type.
+ *
+ * This function is useful for code that wants to interact with source text
+ * as *code units*, not as string data. This doesn't matter for UTF-16,
+ * but it's a crucial distinction for UTF-8. When UTF-8 source text is
+ * encapsulated, |Unit| being |mozilla::Utf8Unit| unambiguously indicates
+ * that the code units are UTF-8. In contrast |const char*| returned by
+ * |get()| below could hold UTF-8 (or its ASCII subset) or Latin-1 or (in
+ * particularly cursed embeddings) EBCDIC or some other legacy character
+ * set. Prefer this function to |get()| wherever possible.
+ */
+ const Unit* units() const { return units_; }
+
+ /**
+ * Access the encapsulated data using a character type.
+ *
+ * This function is useful for interactions with character-centric actions
+ * like interacting with UniqueChars/UniqueTwoByteChars or printing out
+ * text in a debugger, that only work with |CharT|. But as |CharT| loses
+ * encoding specificity when UTF-8 source text is encapsulated, prefer
+ * |units()| to this function.
+ */
+ const CharT* get() const { return reinterpret_cast<const CharT*>(units_); }
+
+ /**
+ * Returns true if this owns the source units and will free them on
+ * destruction. If true, it is legal to call |take{Chars,Units}()|.
+ */
+ bool ownsUnits() const { return ownsUnits_; }
+
+ /**
+ * Count of the underlying source units -- code units, not bytes or code
+ * points -- in this.
+ */
+ uint32_t length() const { return length_; }
+
+ /**
+ * Retrieve and take ownership of the underlying source units. The caller
+ * is now responsible for calling js_free() on the returned value, *but
+ * only after JS script compilation has completed*.
+ *
+ * After underlying source units have been taken, this will continue to
+ * refer to the same data -- it just won't own the data. get() and
+ * length() will return the same values, but ownsUnits() will be false.
+ * The taken source units must be kept alive until after JS script
+ * compilation completes, as noted above, for this to be safe.
+ *
+ * The caller must check ownsUnits() before calling takeUnits(). Taking
+ * and then free'ing an unowned buffer will have dire consequences.
+ */
+ Unit* takeUnits() {
+ MOZ_ASSERT(ownsUnits_);
+ ownsUnits_ = false;
+ return const_cast<Unit*>(units_);
+ }
+
+ /**
+ * Akin to |takeUnits()| in all respects, but returns characters rather
+ * than units.
+ */
+ CharT* takeChars() { return reinterpret_cast<CharT*>(takeUnits()); }
+
+ private:
+ SourceText(const SourceText&) = delete;
+ void operator=(const SourceText&) = delete;
+};
+
+} // namespace JS
+
+#endif /* js_SourceText_h */