blob: f648c4318d5063eb6ac427ab9319ec786940740f [file] [log] [blame]
/* -*- 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/. */
#include "vm/Xdr.h"
#include <string.h>
#include "jsapi.h"
#include "jsscript.h"
#include "vm/Debugger.h"
#include "vm/ScopeObject.h"
using namespace js;
void
XDRBuffer::freeBuffer()
{
js_free(base);
#ifdef DEBUG
memset(this, 0xe2, sizeof *this);
#endif
}
bool
XDRBuffer::grow(size_t n)
{
MOZ_ASSERT(n > size_t(limit - cursor));
const size_t MIN_CAPACITY = 8192;
const size_t MAX_CAPACITY = size_t(INT32_MAX) + 1;
size_t offset = cursor - base;
MOZ_ASSERT(offset <= MAX_CAPACITY);
if (n > MAX_CAPACITY - offset) {
js::gc::AutoSuppressGC suppressGC(cx());
JS_ReportErrorNumber(cx(), GetErrorMessage, nullptr, JSMSG_TOO_BIG_TO_ENCODE);
return false;
}
size_t newCapacity = mozilla::RoundUpPow2(offset + n);
if (newCapacity < MIN_CAPACITY)
newCapacity = MIN_CAPACITY;
MOZ_ASSERT(newCapacity <= MAX_CAPACITY);
void* data = js_realloc(base, newCapacity);
if (!data) {
ReportOutOfMemory(cx());
return false;
}
base = static_cast<uint8_t*>(data);
cursor = base + offset;
limit = base + newCapacity;
return true;
}
template<XDRMode mode>
bool
XDRState<mode>::codeChars(const Latin1Char* chars, size_t nchars)
{
static_assert(sizeof(Latin1Char) == sizeof(uint8_t), "Latin1Char must fit in 1 byte");
MOZ_ASSERT(mode == XDR_ENCODE);
uint8_t* ptr = buf.write(nchars);
if (!ptr)
return false;
mozilla::PodCopy(ptr, chars, nchars);
return true;
}
template<XDRMode mode>
bool
XDRState<mode>::codeChars(char16_t* chars, size_t nchars)
{
size_t nbytes = nchars * sizeof(char16_t);
if (mode == XDR_ENCODE) {
uint8_t* ptr = buf.write(nbytes);
if (!ptr)
return false;
mozilla::NativeEndian::copyAndSwapToLittleEndian(ptr, chars, nchars);
} else {
const uint8_t* ptr = buf.read(nbytes);
mozilla::NativeEndian::copyAndSwapFromLittleEndian(chars, ptr, nchars);
}
return true;
}
template<XDRMode mode>
static bool
VersionCheck(XDRState<mode>* xdr)
{
uint32_t bytecodeVer;
if (mode == XDR_ENCODE)
bytecodeVer = XDR_BYTECODE_VERSION;
if (!xdr->codeUint32(&bytecodeVer))
return false;
if (mode == XDR_DECODE && bytecodeVer != XDR_BYTECODE_VERSION) {
/* We do not provide binary compatibility with older scripts. */
JS_ReportErrorNumber(xdr->cx(), GetErrorMessage, nullptr, JSMSG_BAD_SCRIPT_MAGIC);
return false;
}
return true;
}
template<XDRMode mode>
bool
XDRState<mode>::codeFunction(MutableHandleFunction objp)
{
if (mode == XDR_DECODE)
objp.set(nullptr);
if (!VersionCheck(this))
return false;
RootedObject staticLexical(cx(), &cx()->global()->lexicalScope().staticBlock());
return XDRInterpretedFunction(this, staticLexical, nullptr, objp);
}
template<XDRMode mode>
bool
XDRState<mode>::codeScript(MutableHandleScript scriptp)
{
if (mode == XDR_DECODE)
scriptp.set(nullptr);
if (!VersionCheck(this))
return false;
RootedObject staticLexical(cx(), &cx()->global()->lexicalScope().staticBlock());
if (!XDRScript(this, staticLexical, nullptr, nullptr, scriptp))
return false;
return true;
}
template<XDRMode mode>
bool
XDRState<mode>::codeConstValue(MutableHandleValue vp)
{
return XDRScriptConst(this, vp);
}
XDRDecoder::XDRDecoder(JSContext* cx, const void* data, uint32_t length)
: XDRState<XDR_DECODE>(cx)
{
buf.setData(data, length);
}
template class js::XDRState<XDR_ENCODE>;
template class js::XDRState<XDR_DECODE>;