blob: 89f65f424cb42d16289e99f1a16758dd4e9c7736 [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 "SCACorePch.h"
namespace Js
{
template <class Reader>
bool DeserializationCloner<Reader>::TryClonePrimitive(SrcTypeId typeId, Src src, Dst* dst)
{
if (!IsSCAPrimitive(typeId))
{
return false;
}
ScriptContext* scriptContext = this->GetScriptContext();
JavascriptLibrary* lib = scriptContext->GetLibrary();
switch (typeId)
{
case SCA_None:
*dst = NULL;
break;
case SCA_Reference: // Handle reference explictly as a primitive
{
scaposition_t pos;
m_reader->Read(&pos);
if (!this->GetEngine()->TryGetClonedObject(pos, dst))
{
this->ThrowSCADataCorrupt();
}
}
break;
case SCA_NullValue:
*dst = lib->GetNull();
break;
case SCA_UndefinedValue:
*dst = lib->GetUndefined();
break;
case SCA_TrueValue:
*dst = lib->GetTrue();
break;
case SCA_FalseValue:
*dst = lib->GetFalse();
break;
case SCA_Int32Value:
{
int32 n;
m_reader->Read(&n);
*dst = JavascriptNumber::ToVar(n, scriptContext);
}
break;
case SCA_DoubleValue:
{
double dbl;
m_reader->Read(&dbl);
*dst = JavascriptNumber::ToVarWithCheck(dbl, scriptContext);
}
break;
case SCA_Int64Value:
{
__int64 n;
m_reader->Read(&n);
*dst = JavascriptInt64Number::ToVar(n, scriptContext);
}
break;
case SCA_Uint64Value:
{
unsigned __int64 n;
m_reader->Read(&n);
*dst = JavascriptUInt64Number::ToVar(n, scriptContext);
}
break;
default:
return false; // Not a recognized primitive type
}
return true;
}
template <class Reader>
bool DeserializationCloner<Reader>::TryCloneObject(SrcTypeId typeId, Src src, Dst* dst, SCADeepCloneType* deepClone)
{
ScriptContext* scriptContext = this->GetScriptContext();
JavascriptLibrary* lib = scriptContext->GetLibrary();
*deepClone = SCADeepCloneType::None;
bool isObject = true;
if (typeId == SCA_Transferable)
{
scaposition_t pos;
m_reader->Read(&pos);
*dst = this->GetEngine()->ClaimTransferable(pos, lib);
if (*dst == nullptr)
{
this->ThrowSCADataCorrupt();
}
return true;
}
if (IsSCAHostObject(typeId))
{
*dst = m_reader->ReadHostObject();
*deepClone = SCADeepCloneType::HostObject;
return true;
}
switch (typeId)
{
case SCA_StringValue: // Clone string value as object type to resolve multiple references
{
charcount_t len;
const char16* buf = ReadString(&len);
*dst = Js::JavascriptString::NewWithBuffer(buf, len, scriptContext);
isObject = false;
}
break;
case SCA_BooleanTrueObject:
*dst = lib->CreateBooleanObject(TRUE);
break;
case SCA_BooleanFalseObject:
*dst = lib->CreateBooleanObject(FALSE);
break;
case SCA_DateObject:
{
double dbl;
m_reader->Read(&dbl);
*dst = lib->CreateDate(dbl);
}
break;
case SCA_NumberObject:
{
double dbl;
m_reader->Read(&dbl);
*dst = lib->CreateNumberObjectWithCheck(dbl);
}
break;
case SCA_StringObject:
{
charcount_t len;
const char16* buf = ReadString(&len);
*dst = lib->CreateStringObject(buf, len);
}
break;
case SCA_RegExpObject:
{
charcount_t len;
const char16* buf = ReadString(&len);
DWORD flags;
m_reader->Read(&flags);
*dst = JavascriptRegExp::CreateRegEx(buf, len,
static_cast<UnifiedRegex::RegexFlags>(flags), scriptContext);
}
break;
case SCA_Object:
{
*dst = lib->CreateObject();
*deepClone = SCADeepCloneType::Object;
}
break;
case SCA_Map:
{
*dst = JavascriptMap::New(scriptContext);
*deepClone = SCADeepCloneType::Map;
}
break;
case SCA_Set:
{
*dst = JavascriptSet::New(scriptContext);
*deepClone = SCADeepCloneType::Set;
}
break;
case SCA_DenseArray:
case SCA_SparseArray:
{
uint32 length;
Read(&length);
*dst = lib->CreateArray(length);
*deepClone = SCADeepCloneType::Object;
}
break;
case SCA_ArrayBuffer:
{
uint32 len;
m_reader->Read(&len);
ArrayBuffer* arrayBuffer = lib->CreateArrayBuffer(len);
Read(arrayBuffer->GetBuffer(), arrayBuffer->GetByteLength());
*dst = arrayBuffer;
}
break;
case SCA_SharedArrayBuffer:
{
SharedContents * sharedContents;
m_reader->Read((intptr_t*)&sharedContents);
SharedArrayBuffer* arrayBuffer = lib->CreateSharedArrayBuffer(sharedContents);
Assert(arrayBuffer->IsWebAssemblyArrayBuffer() == sharedContents->IsWebAssembly());
*dst = arrayBuffer;
}
break;
//#ifdef ENABLE_WASM
// case SCA_WebAssemblyModule:
// {
// uint32 len;
// m_reader->Read(&len);
// byte* buffer = RecyclerNewArrayLeaf(scriptContext->GetRecycler(), byte, len);
// Read(buffer, len);
// WebAssemblySource wasmSrc(buffer, len, true, scriptContext);
// *dst = WebAssemblyModule::CreateModule(scriptContext, &wasmSrc);
// break;
// }
// case SCA_WebAssemblyMemory:
// {
// uint32 initialLength = 0;
// uint32 maximumLength = 0;
// uint32 isShared = 0;
// m_reader->Read(&initialLength);
// m_reader->Read(&maximumLength);
//
//#ifdef ENABLE_WASM_THREADS
// m_reader->Read(&isShared);
// if (isShared)
// {
// SharedContents * sharedContents;
// m_reader->Read((intptr_t*)&sharedContents);
// *dst = WebAssemblyMemory::CreateFromSharedContents(initialLength, maximumLength, sharedContents, scriptContext);
// }
// else
//#endif
// {
// uint32 len;
// m_reader->Read(&len);
// WebAssemblyMemory* mem = WebAssemblyMemory::CreateForExistingBuffer(initialLength, maximumLength, len, scriptContext);
// Read(mem->GetBuffer()->GetBuffer(), len);
// *dst = mem;
// }
// break;
// }
//#endif
case SCA_Uint8ClampedArray:
// If Khronos Interop is not enabled, we don't have Uint8ClampedArray available.
// This is a scenario where the source buffer was created in a newer document mode
// but needs to be deserialized in an older document mode.
// What we want to do is return the buffer as a CanvasPixelArray instead of
// Uint8ClampedArray since the older document mode knows what CanvasPixelArray is but
// not what Uint8ClampedArray is.
// We don't support pixelarray in edge anymore.
// Intentionally fall through to default (TypedArray) label
default:
if (IsSCATypedArray(typeId) || typeId == SCA_DataView)
{
ReadTypedArray(typeId, dst);
break;
}
return false; // Not a supported object type
}
#ifdef ENABLE_JS_ETW
if (EventEnabledJSCRIPT_RECYCLER_ALLOCATE_OBJECT() && isObject)
{
EventWriteJSCRIPT_RECYCLER_ALLOCATE_OBJECT(*dst);
}
#endif
#if ENABLE_DEBUG_CONFIG_OPTIONS
if (Js::Configuration::Global.flags.IsEnabled(Js::autoProxyFlag))
{
*dst = JavascriptProxy::AutoProxyWrapper(*dst);
}
#endif
return true;
}
template <class Reader>
void DeserializationCloner<Reader>::CloneHostObjectProperties(SrcTypeId typeId, Src src, Dst dst)
{
// We have already created host obect.
}
template <class Reader>
void DeserializationCloner<Reader>::CloneProperties(SrcTypeId typeId, Src src, Dst dst)
{
// ScriptContext* scriptContext = GetScriptContext();
RecyclableObject* obj = VarTo<RecyclableObject>(dst);
if (obj->IsExternal()) // Read host object properties
{
Assert(false);
}
else // Read native object properties
{
// Read array index named properties
if (typeId == SCA_DenseArray)
{
JavascriptArray* arr = JavascriptArray::FromAnyArray(obj); // (might be ES5Array if -ForceES5Array)
uint32 length = arr->GetLength();
for (uint32 i = 0; i < length; i++)
{
Dst value = NULL;
this->GetEngine()->Clone(m_reader->GetPosition(), &value);
if (value)
{
arr->DirectSetItemAt(i, value); //Note: no prototype check
}
}
}
else if (typeId == SCA_SparseArray)
{
JavascriptArray* arr = JavascriptArray::FromAnyArray(obj); // (might be ES5Array if -ForceES5Array)
while (true)
{
uint32 i;
Read(&i);
if (i == SCA_PROPERTY_TERMINATOR)
{
break;
}
Dst value = NULL;
this->GetEngine()->Clone(m_reader->GetPosition(), &value);
if (value == NULL)
{
this->ThrowSCADataCorrupt();
}
arr->DirectSetItemAt(i, value); //Note: no prototype check
}
}
// Read non-index named properties
ReadObjectPropertiesIntoObject(obj);
}
}
template <class Reader>
void DeserializationCloner<Reader>::CloneMap(Src src, Dst dst)
{
JavascriptMap* map = VarTo<JavascriptMap>(dst);
int32 size;
m_reader->Read(&size);
for (int i = 0; i < size; i++)
{
Var key;
Var value;
this->GetEngine()->Clone(m_reader->GetPosition(), &key);
if (!key)
{
this->ThrowSCADataCorrupt();
}
this->GetEngine()->Clone(m_reader->GetPosition(), &value);
if (!value)
{
this->ThrowSCADataCorrupt();
}
map->Set(key, value);
}
}
template <class Reader>
void DeserializationCloner<Reader>::CloneSet(Src src, Dst dst)
{
JavascriptSet* set = VarTo<JavascriptSet>(dst);
int32 size;
m_reader->Read(&size);
for (int i = 0; i < size; i++)
{
Var value;
this->GetEngine()->Clone(m_reader->GetPosition(), &value);
if (!value)
{
this->ThrowSCADataCorrupt();
}
set->Add(value);
}
}
template <class Reader>
void DeserializationCloner<Reader>::CloneObjectReference(Src src, Dst dst)
{
Assert(FALSE); // Should never call this. Object reference handled explictly.
}
//
// Try to read a SCAString layout in the form of: [byteLen] [string content] [padding].
// SCAString is also used for property name in object layout. In case of property terminator,
// SCA_PROPERTY_TERMINATOR will appear at the place of [byteLen]. Return false in this case.
//
// If buffer is not null and the size is appropriate, will try reusing it
//
template <class Reader>
const char16* DeserializationCloner<Reader>::TryReadString(charcount_t* len, bool reuseBuffer) const
{
// m_buffer is allocated on GC heap and stored in a regular field.
// that is ok since 'this' is always a stack instance.
Assert(ThreadContext::IsOnStack(this));
uint32 byteLen;
m_reader->Read(&byteLen);
if (byteLen == SCA_PROPERTY_TERMINATOR)
{
return nullptr;
}
else if (byteLen == 0)
{
*len = 0;
return _u("");
}
else
{
charcount_t newLen = byteLen / sizeof(char16);
char16* buf;
if (reuseBuffer)
{
if (this->m_bufferLength < newLen)
{
Recycler* recycler = this->GetScriptContext()->GetRecycler();
this->m_buffer = RecyclerNewArrayLeaf(recycler, char16, newLen + 1);
this->m_bufferLength = newLen;
}
buf = this->m_buffer;
}
else
{
Recycler* recycler = this->GetScriptContext()->GetRecycler();
buf = RecyclerNewArrayLeaf(recycler, char16, newLen + 1);
}
m_reader->Read(buf, byteLen);
buf[newLen] = NULL;
*len = newLen;
uint32 unalignedLen = byteLen % sizeof(uint32);
if (unalignedLen)
{
uint32 padding;
m_reader->Read(&padding, sizeof(uint32) - unalignedLen);
}
return buf;
}
}
//
// Read a SCAString value from layout: [byteLen] [string content] [padding].
// Throw if seeing SCA_PROPERTY_TERMINATOR.
//
template <class Reader>
const char16* DeserializationCloner<Reader>::ReadString(charcount_t* len) const
{
const char16* str = TryReadString(len, false);
if (str == nullptr)
{
this->ThrowSCADataCorrupt();
}
return str;
}
//
// Read bytes data: [bytes] [padding]
//
template <class Reader>
void DeserializationCloner<Reader>::Read(BYTE* buf, uint32 len) const
{
m_reader->Read(buf, len);
uint32 unalignedLen = len % sizeof(uint32);
if (unalignedLen)
{
uint32 padding;
m_reader->Read(&padding, sizeof(uint32) - unalignedLen);
}
}
//
// Read a TypedArray or DataView.
//
template <class Reader>
void DeserializationCloner<Reader>::ReadTypedArray(SrcTypeId typeId, Dst* dst) const
{
switch (typeId)
{
case SCA_Int8Array:
ReadTypedArray<int8, false>(dst);
break;
case SCA_Uint8Array:
ReadTypedArray<uint8, false>(dst);
break;
case SCA_Uint8ClampedArray:
ReadTypedArray<uint8, true>(dst);
break;
case SCA_Int16Array:
ReadTypedArray<int16, false>(dst);
break;
case SCA_Uint16Array:
ReadTypedArray<uint16, false>(dst);
break;
case SCA_Int32Array:
ReadTypedArray<int32, false>(dst);
break;
case SCA_Uint32Array:
ReadTypedArray<uint32, false>(dst);
break;
case SCA_Float32Array:
ReadTypedArray<float, false>(dst);
break;
case SCA_Float64Array:
ReadTypedArray<double, false>(dst);
break;
case SCA_DataView:
ReadTypedArray<DataView, false>(dst);
break;
default:
Assert(false);
break;
}
}
template class DeserializationCloner<StreamReader>;
Var SCADeserializationEngine::Deserialize(StreamReader* reader, Var* transferableVars, size_t cTransferableVars)
{
ScriptContext* scriptContext = reader->GetScriptContext();
StreamDeserializationCloner cloner(scriptContext, reader);
// Read version
uint32 version;
reader->Read(&version);
if (GetSCAMajor(version) > SCA_FORMAT_MAJOR)
{
cloner.ThrowSCANewVersion();
}
Var value = SCAEngine<scaposition_t, Var, StreamDeserializationCloner>::Clone(reader->GetPosition(), &cloner, transferableVars, cTransferableVars);
if (!value)
{
cloner.ThrowSCADataCorrupt();
}
return value;
}
}