LCOV - code coverage report
Current view: top level - source/simulation2/serialization - StdDeserializer.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 223 266 83.8 %
Date: 2023-01-19 00:18:29 Functions: 12 16 75.0 %

          Line data    Source code
       1             : /* Copyright (C) 2022 Wildfire Games.
       2             :  * This file is part of 0 A.D.
       3             :  *
       4             :  * 0 A.D. is free software: you can redistribute it and/or modify
       5             :  * it under the terms of the GNU General Public License as published by
       6             :  * the Free Software Foundation, either version 2 of the License, or
       7             :  * (at your option) any later version.
       8             :  *
       9             :  * 0 A.D. is distributed in the hope that it will be useful,
      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :  * GNU General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU General Public License
      15             :  * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include "precompiled.h"
      19             : 
      20             : #include "StdDeserializer.h"
      21             : 
      22             : #include "lib/byte_order.h"
      23             : #include "lib/utf8.h"
      24             : #include "ps/CLogger.h"
      25             : #include "ps/CStr.h"
      26             : #include "scriptinterface/FunctionWrapper.h"
      27             : #include "scriptinterface/Object.h"
      28             : #include "scriptinterface/ScriptConversions.h"
      29             : #include "scriptinterface/ScriptExtraHeaders.h" // For typed arrays and ArrayBuffer
      30             : #include "scriptinterface/ScriptInterface.h"
      31             : #include "simulation2/serialization/ISerializer.h"
      32             : #include "simulation2/serialization/SerializedScriptTypes.h"
      33             : #include "simulation2/serialization/StdSerializer.h" // for DEBUG_SERIALIZER_ANNOTATE
      34             : 
      35          96 : CStdDeserializer::CStdDeserializer(const ScriptInterface& scriptInterface, std::istream& stream) :
      36          96 :     m_ScriptInterface(scriptInterface), m_Stream(stream)
      37             : {
      38          96 :     JS_AddExtraGCRootsTracer(ScriptRequest(scriptInterface).cx, CStdDeserializer::Trace, this);
      39             :     // Insert a dummy object in front, as valid tags start at 1.
      40          96 :     m_ScriptBackrefs.emplace_back(nullptr);
      41          96 : }
      42             : 
      43         192 : CStdDeserializer::~CStdDeserializer()
      44             : {
      45          96 :     JS_RemoveExtraGCRootsTracer(ScriptRequest(m_ScriptInterface).cx, CStdDeserializer::Trace, this);
      46          96 : }
      47             : 
      48           0 : void CStdDeserializer::Trace(JSTracer *trc, void *data)
      49             : {
      50           0 :     reinterpret_cast<CStdDeserializer*>(data)->TraceMember(trc);
      51           0 : }
      52             : 
      53           0 : void CStdDeserializer::TraceMember(JSTracer *trc)
      54             : {
      55           0 :     for (JS::Heap<JSObject*>& backref : m_ScriptBackrefs)
      56           0 :         JS::TraceEdge(trc, &backref, "StdDeserializer::m_ScriptBackrefs");
      57           0 : }
      58             : 
      59        1446 : void CStdDeserializer::Get(const char* name, u8* data, size_t len)
      60             : {
      61             : #if DEBUG_SERIALIZER_ANNOTATE
      62             :     std::string strName;
      63             :     char c = m_Stream.get();
      64             :     ENSURE(c == '<');
      65             :     while (1)
      66             :     {
      67             :         c = m_Stream.get();
      68             :         if (c == '>')
      69             :             break;
      70             :         else
      71             :             strName += c;
      72             :     }
      73             :     ENSURE(strName == name);
      74             : #else
      75             :     UNUSED2(name);
      76             : #endif
      77        1446 :     m_Stream.read((char*)data, (std::streamsize)len);
      78        1446 :     if (!m_Stream.good())
      79             :     {
      80             :         // hit eof before len, or other errors
      81             :         // NOTE: older libc++ versions incorrectly set eofbit on the last char; test gcount as a workaround
      82             :         // see https://llvm.org/bugs/show_bug.cgi?id=9335
      83           0 :         if (m_Stream.bad() || m_Stream.fail() || (m_Stream.eof() && m_Stream.gcount() != (std::streamsize)len))
      84           0 :             throw PSERROR_Deserialize_ReadFailed();
      85             :     }
      86        1446 : }
      87             : 
      88           0 : std::istream& CStdDeserializer::GetStream()
      89             : {
      90           0 :     return m_Stream;
      91             : }
      92             : 
      93         223 : void CStdDeserializer::RequireBytesInStream(size_t numBytes)
      94             : {
      95             :     // It would be nice to do:
      96             : //  if (numBytes > (size_t)m_Stream.rdbuf()->in_avail())
      97             : //      throw PSERROR_Deserialize_OutOfBounds("RequireBytesInStream");
      98             :     // but that doesn't work (at least on MSVC) since in_avail isn't
      99             :     // guaranteed to return the actual number of bytes available; see e.g.
     100             :     // http://social.msdn.microsoft.com/Forums/en/vclanguage/thread/13009a88-933f-4be7-bf3d-150e425e66a6#70ea562d-8605-4742-8851-1bae431ce6ce
     101             : 
     102             :     // Instead we'll just verify that it's not an extremely large number:
     103         223 :     if (numBytes > 64*MiB)
     104           0 :         throw PSERROR_Deserialize_OutOfBounds("RequireBytesInStream");
     105         223 : }
     106             : 
     107         163 : void CStdDeserializer::AddScriptBackref(JS::HandleObject obj)
     108             : {
     109         163 :     m_ScriptBackrefs.push_back(JS::Heap<JSObject*>(obj));
     110         163 : }
     111             : 
     112          17 : void CStdDeserializer::GetScriptBackref(size_t tag, JS::MutableHandleObject ret)
     113             : {
     114          17 :     ENSURE(m_ScriptBackrefs.size() > tag);
     115          17 :     ret.set(m_ScriptBackrefs[tag]);
     116          17 : }
     117             : 
     118             : ////////////////////////////////////////////////////////////////
     119             : 
     120         324 : JS::Value CStdDeserializer::ReadScriptVal(const char* UNUSED(name), JS::HandleObject preexistingObject)
     121             : {
     122         648 :     ScriptRequest rq(m_ScriptInterface);
     123             : 
     124             :     uint8_t type;
     125         324 :     NumberU8_Unbounded("type", type);
     126         324 :     switch (type)
     127             :     {
     128           2 :     case SCRIPT_TYPE_VOID:
     129           2 :         return JS::UndefinedValue();
     130             : 
     131           2 :     case SCRIPT_TYPE_NULL:
     132           2 :         return JS::NullValue();
     133             : 
     134          75 :     case SCRIPT_TYPE_ARRAY:
     135             :     case SCRIPT_TYPE_OBJECT:
     136             :     case SCRIPT_TYPE_OBJECT_PROTOTYPE:
     137             :     {
     138         150 :         JS::RootedObject obj(rq.cx);
     139          75 :         if (type == SCRIPT_TYPE_ARRAY)
     140             :         {
     141             :             u32 length;
     142          31 :             NumberU32_Unbounded("array length", length);
     143          31 :             obj.set(JS::NewArrayObject(rq.cx, length));
     144             :         }
     145          44 :         else if (type == SCRIPT_TYPE_OBJECT)
     146             :         {
     147          28 :             obj.set(JS_NewPlainObject(rq.cx));
     148             :         }
     149             :         else // SCRIPT_TYPE_OBJECT_PROTOTYPE
     150             :         {
     151          24 :             CStrW prototypeName;
     152          16 :             String("proto", prototypeName, 0, 256);
     153             : 
     154             :             // If an object was passed, no need to construct a new one.
     155          16 :             if (preexistingObject != nullptr)
     156           5 :                 obj.set(preexistingObject);
     157             :             else
     158             :             {
     159          22 :                 JS::RootedValue constructor(rq.cx);
     160          11 :                 if (!ScriptInterface::GetGlobalProperty(rq, prototypeName.ToUTF8(), &constructor))
     161           0 :                     throw PSERROR_Deserialize_ScriptError("Deserializer failed to get constructor object");
     162             : 
     163          22 :                 JS::RootedObject newObj(rq.cx);
     164          11 :                 if (!JS::Construct(rq.cx, constructor, JS::HandleValueArray::empty(), &newObj))
     165           0 :                     throw PSERROR_Deserialize_ScriptError("Deserializer failed to construct object");
     166          11 :                 obj.set(newObj);
     167             :             }
     168             : 
     169          24 :             JS::RootedObject prototype(rq.cx);
     170          16 :             JS_GetPrototype(rq.cx, obj, &prototype);
     171          24 :             SPrototypeSerialization info = GetPrototypeInfo(rq, prototype);
     172             : 
     173          16 :             if (preexistingObject != nullptr && prototypeName != wstring_from_utf8(info.name))
     174           0 :                 throw PSERROR_Deserialize_ScriptError("Deserializer failed: incorrect pre-existing object");
     175             : 
     176             : 
     177          16 :             if (info.hasCustomDeserialize)
     178             :             {
     179           8 :                 AddScriptBackref(obj);
     180             : 
     181             :                 // If Serialize is null, we'll still call Deserialize but with undefined argument
     182          16 :                 JS::RootedValue data(rq.cx);
     183           8 :                 if (!info.hasNullSerialize)
     184           5 :                     ScriptVal("data", &data);
     185             : 
     186          16 :                 JS::RootedValue objVal(rq.cx, JS::ObjectValue(*obj));
     187           8 :                 ScriptFunction::CallVoid(rq, objVal, "Deserialize", data);
     188             : 
     189           8 :                 return JS::ObjectValue(*obj);
     190             :             }
     191           8 :             else if (info.hasNullSerialize)
     192             :             {
     193             :                 // If we serialized null, this means we're pretty much a default-constructed object.
     194             :                 // Nothing to do.
     195           0 :                 AddScriptBackref(obj);
     196           0 :                 return JS::ObjectValue(*obj);
     197             :             }
     198             :         }
     199             : 
     200          67 :         if (!obj)
     201           0 :             throw PSERROR_Deserialize_ScriptError("Deserializer failed to create new object");
     202             : 
     203          67 :         AddScriptBackref(obj);
     204             : 
     205             :         uint32_t numProps;
     206          67 :         NumberU32_Unbounded("num props", numProps);
     207             :         bool isLatin1;
     208         217 :         for (uint32_t i = 0; i < numProps; ++i)
     209             :         {
     210         150 :             Bool("isLatin1", isLatin1);
     211         150 :             if (isLatin1)
     212             :             {
     213         300 :                 std::vector<JS::Latin1Char> propname;
     214         150 :                 ReadStringLatin1("prop name", propname);
     215         300 :                 JS::RootedValue propval(rq.cx, ReadScriptVal("prop value", nullptr));
     216             : 
     217         300 :                 std::u16string prp(propname.begin(), propname.end());;
     218             : // TODO: Should ask upstream about getting a variant of JS_SetProperty with a length param.
     219         150 :                 if (!JS_SetUCProperty(rq.cx, obj, (const char16_t*)prp.data(), prp.length(), propval))
     220           0 :                     throw PSERROR_Deserialize_ScriptError();
     221             :             }
     222             :             else
     223             :             {
     224           0 :                 std::u16string propname;
     225           0 :                 ReadStringUTF16("prop name", propname);
     226           0 :                 JS::RootedValue propval(rq.cx, ReadScriptVal("prop value", nullptr));
     227             : 
     228           0 :                 if (!JS_SetUCProperty(rq.cx, obj, (const char16_t*)propname.data(), propname.length(), propval))
     229           0 :                     throw PSERROR_Deserialize_ScriptError();
     230             :             }
     231             :         }
     232             : 
     233          67 :         return JS::ObjectValue(*obj);
     234             :     }
     235          37 :     case SCRIPT_TYPE_STRING:
     236             :     {
     237          74 :         JS::RootedString str(rq.cx);
     238          37 :         ScriptString("string", &str);
     239          37 :         return JS::StringValue(str);
     240             :     }
     241          83 :     case SCRIPT_TYPE_INT:
     242             :     {
     243             :         int32_t value;
     244          83 :         NumberI32("value", value, JSVAL_INT_MIN, JSVAL_INT_MAX);
     245          83 :         return JS::NumberValue(value);
     246             :     }
     247          14 :     case SCRIPT_TYPE_DOUBLE:
     248             :     {
     249             :         double value;
     250          14 :         NumberDouble_Unbounded("value", value);
     251          28 :         JS::RootedValue rval(rq.cx, JS::NumberValue(value));
     252          14 :         if (rval.isNull())
     253           0 :             throw PSERROR_Deserialize_ScriptError("JS_NewNumberValue failed");
     254          14 :         return rval;
     255             :     }
     256           6 :     case SCRIPT_TYPE_BOOLEAN:
     257             :     {
     258             :         uint8_t value;
     259           6 :         NumberU8("value", value, 0, 1);
     260           6 :         return JS::BooleanValue(value ? true : false);
     261             :     }
     262          17 :     case SCRIPT_TYPE_BACKREF:
     263             :     {
     264             :         i32 tag;
     265          17 :         NumberI32("tag", tag, 0, JSVAL_INT_MAX);
     266          34 :         JS::RootedObject obj(rq.cx);
     267          17 :         GetScriptBackref(tag, &obj);
     268          17 :         if (!obj)
     269           0 :             throw PSERROR_Deserialize_ScriptError("Invalid backref tag");
     270          17 :         return JS::ObjectValue(*obj);
     271             :     }
     272           4 :     case SCRIPT_TYPE_OBJECT_NUMBER:
     273             :     {
     274             :         double value;
     275           4 :         NumberDouble_Unbounded("value", value);
     276           8 :         JS::RootedValue val(rq.cx, JS::NumberValue(value));
     277             : 
     278           8 :         JS::RootedObject ctorobj(rq.cx);
     279           4 :         if (!JS_GetClassObject(rq.cx, JSProto_Number, &ctorobj))
     280           0 :             throw PSERROR_Deserialize_ScriptError("JS_GetClassObject failed");
     281           8 :         JS::RootedValue protoval(rq.cx, JS::ObjectOrNullValue(ctorobj));
     282             : 
     283           8 :         JS::RootedObject obj(rq.cx);
     284           4 :         if (!JS::Construct(rq.cx, protoval, JS::HandleValueArray(val), &obj))
     285           0 :             throw PSERROR_Deserialize_ScriptError("JS_New failed");
     286           4 :         AddScriptBackref(obj);
     287           4 :         return JS::ObjectValue(*obj);
     288             :     }
     289           4 :     case SCRIPT_TYPE_OBJECT_STRING:
     290             :     {
     291           8 :         JS::RootedString str(rq.cx);
     292           4 :         ScriptString("value", &str);
     293           4 :         if (!str)
     294           0 :             throw PSERROR_Deserialize_ScriptError();
     295           8 :         JS::RootedValue val(rq.cx, JS::StringValue(str));
     296             : 
     297           8 :         JS::RootedObject ctorobj(rq.cx);
     298           4 :         if (!JS_GetClassObject(rq.cx, JSProto_String, &ctorobj))
     299           0 :             throw PSERROR_Deserialize_ScriptError("JS_GetClassObject failed");
     300           8 :         JS::RootedValue protoval(rq.cx, JS::ObjectOrNullValue(ctorobj));
     301             : 
     302           8 :         JS::RootedObject obj(rq.cx);
     303           4 :         if (!JS::Construct(rq.cx, protoval, JS::HandleValueArray(val), &obj))
     304           0 :             throw PSERROR_Deserialize_ScriptError("JS_New failed");
     305           4 :         AddScriptBackref(obj);
     306           4 :         return JS::ObjectValue(*obj);
     307             :     }
     308           4 :     case SCRIPT_TYPE_OBJECT_BOOLEAN:
     309             :     {
     310             :         bool value;
     311           4 :         Bool("value", value);
     312           8 :         JS::RootedValue val(rq.cx, JS::BooleanValue(value));
     313             : 
     314           8 :         JS::RootedObject ctorobj(rq.cx);
     315           4 :         if (!JS_GetClassObject(rq.cx, JSProto_Boolean, &ctorobj))
     316           0 :             throw PSERROR_Deserialize_ScriptError("JS_GetClassObject failed");
     317           8 :         JS::RootedValue protoval(rq.cx, JS::ObjectOrNullValue(ctorobj));
     318             : 
     319           8 :         JS::RootedObject obj(rq.cx);
     320           4 :         if (!JS::Construct(rq.cx, protoval, JS::HandleValueArray(val), &obj))
     321           0 :             throw PSERROR_Deserialize_ScriptError("JS_New failed");
     322           4 :         AddScriptBackref(obj);
     323           4 :         return JS::ObjectValue(*obj);
     324             :     }
     325          32 :     case SCRIPT_TYPE_TYPED_ARRAY:
     326             :     {
     327             :         u8 arrayType;
     328             :         u32 byteOffset, length;
     329          32 :         NumberU8_Unbounded("array type", arrayType);
     330          32 :         NumberU32_Unbounded("byte offset", byteOffset);
     331          32 :         NumberU32_Unbounded("length", length);
     332             : 
     333             :         // To match the serializer order, we reserve the typed array's backref tag here
     334          64 :         JS::RootedObject arrayObj(rq.cx);
     335          32 :         AddScriptBackref(arrayObj);
     336             : 
     337             :         // Get buffer object
     338          64 :         JS::RootedValue bufferVal(rq.cx, ReadScriptVal("buffer", nullptr));
     339             : 
     340          32 :         if (!bufferVal.isObject())
     341           0 :             throw PSERROR_Deserialize_ScriptError();
     342             : 
     343          64 :         JS::RootedObject bufferObj(rq.cx, &bufferVal.toObject());
     344          32 :         if (!JS::IsArrayBufferObject(bufferObj))
     345           0 :             throw PSERROR_Deserialize_ScriptError("js_IsArrayBuffer failed");
     346             : 
     347          32 :         switch(arrayType)
     348             :         {
     349           4 :         case SCRIPT_TYPED_ARRAY_INT8:
     350           4 :             arrayObj = JS_NewInt8ArrayWithBuffer(rq.cx, bufferObj, byteOffset, length);
     351           4 :             break;
     352           4 :         case SCRIPT_TYPED_ARRAY_UINT8:
     353           4 :             arrayObj = JS_NewUint8ArrayWithBuffer(rq.cx, bufferObj, byteOffset, length);
     354           4 :             break;
     355           6 :         case SCRIPT_TYPED_ARRAY_INT16:
     356           6 :             arrayObj = JS_NewInt16ArrayWithBuffer(rq.cx, bufferObj, byteOffset, length);
     357           6 :             break;
     358           4 :         case SCRIPT_TYPED_ARRAY_UINT16:
     359           4 :             arrayObj = JS_NewUint16ArrayWithBuffer(rq.cx, bufferObj, byteOffset, length);
     360           4 :             break;
     361           4 :         case SCRIPT_TYPED_ARRAY_INT32:
     362           4 :             arrayObj = JS_NewInt32ArrayWithBuffer(rq.cx, bufferObj, byteOffset, length);
     363           4 :             break;
     364           4 :         case SCRIPT_TYPED_ARRAY_UINT32:
     365           4 :             arrayObj = JS_NewUint32ArrayWithBuffer(rq.cx, bufferObj, byteOffset, length);
     366           4 :             break;
     367           2 :         case SCRIPT_TYPED_ARRAY_FLOAT32:
     368           2 :             arrayObj = JS_NewFloat32ArrayWithBuffer(rq.cx, bufferObj, byteOffset, length);
     369           2 :             break;
     370           2 :         case SCRIPT_TYPED_ARRAY_FLOAT64:
     371           2 :             arrayObj = JS_NewFloat64ArrayWithBuffer(rq.cx, bufferObj, byteOffset, length);
     372           2 :             break;
     373           2 :         case SCRIPT_TYPED_ARRAY_UINT8_CLAMPED:
     374           2 :             arrayObj = JS_NewUint8ClampedArrayWithBuffer(rq.cx, bufferObj, byteOffset, length);
     375           2 :             break;
     376           0 :         default:
     377           0 :             throw PSERROR_Deserialize_ScriptError("Failed to deserialize unrecognized typed array view");
     378             :         }
     379          32 :         if (!arrayObj)
     380           0 :             throw PSERROR_Deserialize_ScriptError("js_CreateTypedArrayWithBuffer failed");
     381             : 
     382          32 :         return JS::ObjectValue(*arrayObj);
     383             :     }
     384          24 :     case SCRIPT_TYPE_ARRAY_BUFFER:
     385             :     {
     386             :         u32 length;
     387          24 :         NumberU32_Unbounded("buffer length", length);
     388             : 
     389             : #if BYTE_ORDER != LITTLE_ENDIAN
     390             : #error TODO: need to convert JS ArrayBuffer data from little-endian
     391             : #endif
     392          24 :         void* contents = malloc(length);
     393          24 :         ENSURE(contents);
     394          24 :         RawBytes("buffer data", (u8*)contents, length);
     395          48 :         JS::RootedObject bufferObj(rq.cx, JS::NewArrayBufferWithContents(rq.cx, length, contents));
     396          24 :         AddScriptBackref(bufferObj);
     397             : 
     398          24 :         return JS::ObjectValue(*bufferObj);
     399             :     }
     400          12 :     case SCRIPT_TYPE_OBJECT_MAP:
     401             :     {
     402          24 :         JS::RootedObject obj(rq.cx, JS::NewMapObject(rq.cx));
     403          12 :         AddScriptBackref(obj);
     404             : 
     405             :         u32 mapSize;
     406          12 :         NumberU32_Unbounded("map size", mapSize);
     407             : 
     408          30 :         for (u32 i=0; i<mapSize; ++i)
     409             :         {
     410          36 :             JS::RootedValue key(rq.cx, ReadScriptVal("map key", nullptr));
     411          36 :             JS::RootedValue value(rq.cx, ReadScriptVal("map value", nullptr));
     412          18 :             JS::MapSet(rq.cx, obj, key, value);
     413             :         }
     414             : 
     415          12 :         return JS::ObjectValue(*obj);
     416             :     }
     417           8 :     case SCRIPT_TYPE_OBJECT_SET:
     418             :     {
     419          16 :         JS::RootedObject obj(rq.cx, JS::NewSetObject(rq.cx));
     420           8 :         AddScriptBackref(obj);
     421             : 
     422             :         u32 setSize;
     423           8 :         NumberU32_Unbounded("set size", setSize);
     424             : 
     425          16 :         for (u32 i=0; i<setSize; ++i)
     426             :         {
     427          16 :             JS::RootedValue value(rq.cx, ReadScriptVal("set value", nullptr));
     428           8 :             JS::SetAdd(rq.cx, obj, value);
     429             :         }
     430             : 
     431           8 :         return JS::ObjectValue(*obj);
     432             :     }
     433           0 :     default:
     434           0 :         throw PSERROR_Deserialize_OutOfBounds();
     435             :     }
     436             : }
     437             : 
     438         185 : void CStdDeserializer::ReadStringLatin1(const char* name, std::vector<JS::Latin1Char>& str)
     439             : {
     440             :     uint32_t len;
     441         185 :     NumberU32_Unbounded("string length", len);
     442         185 :     RequireBytesInStream(len);
     443         185 :     str.resize(len);
     444         185 :     Get(name, (u8*)str.data(), len);
     445         185 : }
     446             : 
     447           6 : void CStdDeserializer::ReadStringUTF16(const char* name, std::u16string& str)
     448             : {
     449             :     uint32_t len;
     450           6 :     NumberU32_Unbounded("string length", len);
     451           6 :     RequireBytesInStream(len*2);
     452           6 :     str.resize(len);
     453           6 :     Get(name, (u8*)str.data(), len*2);
     454           6 : }
     455             : 
     456          41 : void CStdDeserializer::ScriptString(const char* name, JS::MutableHandleString out)
     457             : {
     458             : #if BYTE_ORDER != LITTLE_ENDIAN
     459             : #error TODO: probably need to convert JS strings from little-endian
     460             : #endif
     461             : 
     462          82 :     ScriptRequest rq(m_ScriptInterface);
     463             : 
     464             :     bool isLatin1;
     465          41 :     Bool("isLatin1", isLatin1);
     466          41 :     if (isLatin1)
     467             :     {
     468          70 :         std::vector<JS::Latin1Char> str;
     469          35 :         ReadStringLatin1(name, str);
     470             : 
     471          35 :         out.set(JS_NewStringCopyN(rq.cx, (const char*)str.data(), str.size()));
     472          35 :         if (!out)
     473           0 :             throw PSERROR_Deserialize_ScriptError("JS_NewStringCopyN failed");
     474             :     }
     475             :     else
     476             :     {
     477          12 :         std::u16string str;
     478           6 :         ReadStringUTF16(name, str);
     479             : 
     480           6 :         out.set(JS_NewUCStringCopyN(rq.cx, (const char16_t*)str.data(), str.length()));
     481           6 :         if (!out)
     482           0 :             throw PSERROR_Deserialize_ScriptError("JS_NewUCStringCopyN failed");
     483             :     }
     484          41 : }
     485             : 
     486          93 : void CStdDeserializer::ScriptVal(const char* name, JS::MutableHandleValue out)
     487             : {
     488          93 :     out.set(ReadScriptVal(name, nullptr));
     489          93 : }
     490             : 
     491           5 : void CStdDeserializer::ScriptObjectAssign(const char* name, JS::HandleValue objVal)
     492             : {
     493          10 :     ScriptRequest rq(m_ScriptInterface);
     494             : 
     495           5 :     if (!objVal.isObject())
     496           0 :         throw PSERROR_Deserialize_ScriptError();
     497             : 
     498          10 :     JS::RootedObject obj(rq.cx, &objVal.toObject());
     499           5 :     ReadScriptVal(name, obj);
     500           5 : }

Generated by: LCOV version 1.13