/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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/. */

/**
 * Container class for passing in script source buffers to the JS engine.  This
 * not only groups the buffer and length values, it also provides a way to
 * optionally pass ownership of the buffer to the JS engine without copying.
 * Rules for use:
 *
 *  1) The data array must be allocated with js_malloc() or js_realloc() if
 *     ownership is being granted to the SourceBufferHolder.
 *  2) If ownership is not given to the SourceBufferHolder, then the memory
 *     must be kept alive until the JS compilation is complete.
 *  3) Any code calling SourceBufferHolder::take() must guarantee to keep the
 *     memory alive until JS compilation completes.  Normally only the JS
 *     engine should be calling take().
 *
 * Example use:
 *
 *    size_t length = 512;
 *    char16_t* chars = static_cast<char16_t*>(js_malloc(sizeof(char16_t) * length));
 *    JS::SourceBufferHolder srcBuf(chars, length, JS::SourceBufferHolder::GiveOwnership);
 *    JS::Compile(cx, options, srcBuf);
 */

#ifndef js_SourceBufferHolder_h
#define js_SourceBufferHolder_h

#include "mozilla/Assertions.h" // MOZ_ASSERT

#include <stddef.h> // size_t

#include "js/Utility.h" // JS::UniqueTwoByteChars

namespace JS {

class MOZ_STACK_CLASS SourceBufferHolder final
{
  public:
    enum Ownership {
      NoOwnership,
      GiveOwnership
    };

    SourceBufferHolder(const char16_t* data, size_t dataLength, Ownership ownership)
      : data_(data),
        length_(dataLength),
        ownsChars_(ownership == GiveOwnership)
    {
        // Ensure that null buffers properly return an unowned, empty,
        // null-terminated string.
        static const char16_t NullChar_ = 0;
        if (!get()) {
            data_ = &NullChar_;
            length_ = 0;
            ownsChars_ = false;
        }
    }

    SourceBufferHolder(SourceBufferHolder&& other)
      : data_(other.data_),
        length_(other.length_),
        ownsChars_(other.ownsChars_)
    {
        other.data_ = nullptr;
        other.length_ = 0;
        other.ownsChars_ = false;
    }

    ~SourceBufferHolder() {
        if (ownsChars_)
            js_free(const_cast<char16_t*>(data_));
    }

    // Access the underlying source buffer without affecting ownership.
    const char16_t* get() const { return data_; }

    // Length of the source buffer in char16_t code units (not bytes)
    size_t length() const { return length_; }

    // Returns true if the SourceBufferHolder owns the buffer and will free
    // it upon destruction.  If true, it is legal to call take().
    bool ownsChars() const { return ownsChars_; }

    // Retrieve and take ownership of the underlying data buffer.  The caller
    // is now responsible for calling js_free() on the returned value, *but only
    // after JS script compilation has completed*.
    //
    // After the buffer has been taken the SourceBufferHolder functions as if
    // it had been constructed on an unowned buffer;  get() and length() still
    // work.  In order for this to be safe the taken buffer must be kept alive
    // until after JS script compilation completes as noted above.
    //
    // Note, it's the caller's responsibility to check ownsChars() before taking
    // the buffer.  Taking and then free'ing an unowned buffer will have dire
    // consequences.
    char16_t* take() {
        MOZ_ASSERT(ownsChars_);
        ownsChars_ = false;
        return const_cast<char16_t*>(data_);
    }

  private:
    SourceBufferHolder(SourceBufferHolder&) = delete;
    SourceBufferHolder& operator=(SourceBufferHolder&) = delete;

    const char16_t* data_;
    size_t length_;
    bool ownsChars_;
};

} // namespace JS

#endif /* js_SourceBufferHolder_h */
