blob: 006974baea17abf14d9b1284cb6c2dc4e722cd9a [file] [log] [blame]
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#include "JsrtPch.h"
#include "JsrtSourceHolder.h"
namespace Js
{
template <typename TLoadCallback>
class JsrtSourceHolderPolicy
{
};
#ifdef _WIN32 // JsSerializedScriptLoadSourceCallback is WIN32 only
template <>
class JsrtSourceHolderPolicy<JsSerializedScriptLoadSourceCallback>
{
public:
typedef WCHAR TLoadCharType;
// Helper function for converting a Unicode script to utf8.
// If heapAlloc is true the returned buffer must be freed with HeapDelete.
// Otherwise scriptContext must be provided and GCed object is
// returned.
static void ScriptToUtf8(_When_(heapAlloc, _In_opt_) _When_(!heapAlloc, _In_) Js::ScriptContext *scriptContext,
_In_z_ const WCHAR *script, _Outptr_result_buffer_(*utf8Length) utf8char_t **utf8Script, _Out_ size_t *utf8Length,
_Out_ size_t *scriptLength, _Out_opt_ size_t *utf8AllocLength, _In_ bool heapAlloc)
{
Assert(utf8Script != nullptr);
Assert(utf8Length != nullptr);
Assert(scriptLength != nullptr);
*utf8Script = nullptr;
*utf8Length = 0;
*scriptLength = 0;
if (utf8AllocLength != nullptr)
{
*utf8AllocLength = 0;
}
size_t length = wcslen(script);
if (length > UINT_MAX)
{
Js::JavascriptError::ThrowOutOfMemoryError(nullptr);
}
// `length` should not be bigger than MAXLONG
// UINT_MAX / 3 < MAXLONG
size_t cbUtf8Buffer = ((UINT_MAX / 3) - 1 > length) ? (length + 1) * 3 : UINT_MAX;
if (cbUtf8Buffer >= UINT_MAX)
{
Js::JavascriptError::ThrowOutOfMemoryError(nullptr);
}
if (!heapAlloc)
{
Assert(scriptContext != nullptr);
*utf8Script = RecyclerNewArrayLeaf(scriptContext->GetRecycler(), utf8char_t, cbUtf8Buffer);
}
else
{
*utf8Script = HeapNewArray(utf8char_t, cbUtf8Buffer);
}
*utf8Length = utf8::EncodeIntoAndNullTerminate<utf8::Utf8EncodingKind::TrueUtf8>(*utf8Script, cbUtf8Buffer, script, static_cast<charcount_t>(length));
*scriptLength = length;
if (utf8AllocLength != nullptr)
{
*utf8AllocLength = cbUtf8Buffer;
}
}
static void FreeMappedSource(utf8char_t const * source, size_t allocLength)
{
HeapDeleteArray(allocLength, source);
}
};
#endif // _WIN32
template <typename TLoadCallback, typename TUnloadCallback>
void JsrtSourceHolder<TLoadCallback, TUnloadCallback>::EnsureSource(MapRequestFor requestedFor, const WCHAR* reasonString)
{
if (this->mappedSource != nullptr)
{
return;
}
Assert(scriptLoadCallback != nullptr);
Assert(this->mappedSource == nullptr);
const typename JsrtSourceHolderPolicy<TLoadCallback>::TLoadCharType *source = nullptr;
size_t sourceLength = 0;
utf8char_t *utf8Source = nullptr;
size_t utf8Length = 0;
size_t utf8AllocLength = 0;
if (!scriptLoadCallback(sourceContext, &source))
{
// Assume out of memory
Js::JavascriptError::ThrowOutOfMemoryError(nullptr);
}
JsrtSourceHolderPolicy<TLoadCallback>::ScriptToUtf8(nullptr, source, &utf8Source, &utf8Length, &sourceLength, &utf8AllocLength, true);
this->mappedSource = utf8Source;
this->mappedSourceByteLength = utf8Length;
this->mappedAllocLength = utf8AllocLength;
this->scriptLoadCallback = nullptr;
#if ENABLE_DEBUG_CONFIG_OPTIONS
AssertMsg(reasonString != nullptr, "Reason string for why we are mapping the source was not provided.");
JS_ETW(EventWriteJSCRIPT_SOURCEMAPPING(reasonString, (ushort)requestedFor));
#endif
}
template <typename TLoadCallback, typename TUnloadCallback>
void JsrtSourceHolder<TLoadCallback, TUnloadCallback>::Finalize(bool isShutdown)
{
Unload();
}
template <typename TLoadCallback, typename TUnloadCallback>
void JsrtSourceHolder<TLoadCallback, TUnloadCallback>::Unload()
{
if (scriptUnloadCallback == nullptr)
{
return;
}
scriptUnloadCallback(sourceContext);
if (this->mappedSource != nullptr)
{
JsrtSourceHolderPolicy<TLoadCallback>::FreeMappedSource(
this->mappedSource, this->mappedAllocLength);
this->mappedSource = nullptr;
}
// Don't allow load or unload again after told to unload.
scriptLoadCallback = nullptr;
scriptUnloadCallback = nullptr;
sourceContext = NULL;
}
#ifndef NTBUILD // ChakraCore Only
template <>
class JsrtSourceHolderPolicy<JsSerializedLoadScriptCallback>
{
public:
typedef JsValueRef TLoadCharType;
static void ScriptToUtf8(_When_(heapAlloc, _In_opt_) _When_(!heapAlloc, _In_) Js::ScriptContext *scriptContext,
_In_z_ const byte *script_, bool isUtf8, size_t length, _Outptr_result_buffer_(*utf8Length) utf8char_t **utf8Script, _Out_ size_t *utf8Length,
_Out_ size_t *scriptLength, _Out_opt_ size_t *utf8AllocLength, _In_ bool heapAlloc)
{
if (isUtf8)
{
*utf8Script = (utf8char_t*)script_;
*utf8Length = length;
*scriptLength = length; // xplat-todo: incorrect for utf8
if (utf8AllocLength)
{
*utf8AllocLength = 0;
}
}
else
{
const WCHAR *script = (const WCHAR*) script_;
Assert(utf8Script != nullptr);
Assert(utf8Length != nullptr);
Assert(scriptLength != nullptr);
*utf8Script = nullptr;
*utf8Length = 0;
*scriptLength = 0;
if (utf8AllocLength != nullptr)
{
*utf8AllocLength = 0;
}
size_t script_length = wcslen(script);
if (script_length > UINT_MAX)
{
Js::JavascriptError::ThrowOutOfMemoryError(nullptr);
}
// `script_length` should not be bigger than MAXLONG
// UINT_MAX / 3 < MAXLONG
size_t cbUtf8Buffer = ((UINT_MAX / 3) - 1 > script_length) ? (script_length + 1) * 3 : UINT_MAX;
if (cbUtf8Buffer >= UINT_MAX)
{
Js::JavascriptError::ThrowOutOfMemoryError(nullptr);
}
if (!heapAlloc)
{
Assert(scriptContext != nullptr);
*utf8Script = RecyclerNewArrayLeaf(scriptContext->GetRecycler(), utf8char_t, cbUtf8Buffer);
}
else
{
*utf8Script = HeapNewArray(utf8char_t, cbUtf8Buffer);
}
*utf8Length = utf8::EncodeIntoAndNullTerminate<utf8::Utf8EncodingKind::TrueUtf8>(*utf8Script, cbUtf8Buffer, script, static_cast<charcount_t>(script_length));
*scriptLength = script_length;
if (utf8AllocLength != nullptr)
{
*utf8AllocLength = cbUtf8Buffer;
}
}
}
static void FreeMappedSource(utf8char_t const * source, size_t allocLength)
{
if (allocLength)
{
HeapDeleteArray(allocLength, source);
}
}
};
template <>
void JsrtSourceHolder<JsSerializedLoadScriptCallback,
JsSerializedScriptUnloadCallback>::EnsureSource(MapRequestFor requestedFor,
const WCHAR* reasonString)
{
if (this->mappedSource != nullptr)
{
return;
}
Assert(scriptLoadCallback != nullptr);
Assert(this->mappedSource == nullptr);
JsValueRef scriptVal;
JsParseScriptAttributes attributes = JsParseScriptAttributeNone;
size_t sourceLength = 0;
utf8char_t *utf8Source = nullptr;
size_t utf8Length = 0;
size_t utf8AllocLength = 0;
Js::ScriptContext* scriptContext = JsrtContext::GetCurrent()->GetScriptContext();
BEGIN_LEAVE_SCRIPT(scriptContext)
if (!scriptLoadCallback(sourceContext, &scriptVal, &attributes))
{
// Assume out of memory
Js::JavascriptError::ThrowOutOfMemoryError(nullptr);
}
END_LEAVE_SCRIPT(scriptContext);
bool isExternalArray = Js::ExternalArrayBuffer::Is(scriptVal),
isString = false;
bool isUtf8 = !(attributes & JsParseScriptAttributeArrayBufferIsUtf16Encoded);
if (!isExternalArray)
{
isString = Js::JavascriptString::Is(scriptVal);
if (!isString)
{
Js::JavascriptError::ThrowOutOfMemoryError(nullptr);
return;
}
}
const byte* script = isExternalArray ?
((Js::ExternalArrayBuffer*)(scriptVal))->GetBuffer() :
(const byte*)((Js::JavascriptString*)(scriptVal))->GetSz();
const size_t cb = isExternalArray ?
((Js::ExternalArrayBuffer*)(scriptVal))->GetByteLength() :
((Js::JavascriptString*)(scriptVal))->GetLength();
JsrtSourceHolderPolicy<JsSerializedLoadScriptCallback>::ScriptToUtf8(nullptr,
script, isUtf8, cb, &utf8Source, &utf8Length, &sourceLength,
&utf8AllocLength, true);
this->mappedScriptValue = scriptVal;
this->mappedSource = utf8Source;
this->mappedSourceByteLength = utf8Length;
this->mappedAllocLength = utf8AllocLength;
this->scriptLoadCallback = nullptr;
#if ENABLE_DEBUG_CONFIG_OPTIONS
AssertMsg(reasonString != nullptr, "Reason string for why we are mapping the source was not provided.");
JS_ETW(EventWriteJSCRIPT_SOURCEMAPPING(reasonString, (ushort)requestedFor));
#endif
}
template <>
void JsrtSourceHolder<JsSerializedLoadScriptCallback, JsSerializedScriptUnloadCallback>::Finalize(bool isShutdown)
{
if (this->mappedSource != nullptr)
{
JsrtSourceHolderPolicy<JsSerializedLoadScriptCallback>::FreeMappedSource(
this->mappedSource, this->mappedAllocLength);
this->mappedSource = nullptr;
}
this->mappedScriptValue = nullptr;
// Don't allow load or unload again after told to unload.
scriptLoadCallback = nullptr;
scriptUnloadCallback = nullptr;
sourceContext = NULL;
}
template class JsrtSourceHolder<JsSerializedLoadScriptCallback, JsSerializedScriptUnloadCallback>;
#endif // NTBUILD
#ifdef _WIN32
template class JsrtSourceHolder<JsSerializedScriptLoadSourceCallback, JsSerializedScriptUnloadCallback>;
#endif // _WIN32
};