LazyJSONString.cpp 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #include "RuntimeLibraryPch.h"
  6. namespace Js
  7. {
  8. /* Generated using the following js program:
  9. function createEscapeMap(count)
  10. {
  11. var escapeMap = new Array(128);
  12. for(var i=0; i < escapeMap.length; i++)
  13. {
  14. escapeMap[i] = count ? 0 : "L\'\\0\'";
  15. }
  16. for(var i=0; i < ' '.charCodeAt(0); i++)
  17. {
  18. escapeMap[i] = count ? 5 : "L\'u\'";
  19. }
  20. escapeMap['\n'.charCodeAt(0)] = count ? 1 : "L\'n\'";
  21. escapeMap['\b'.charCodeAt(0)] = count ? 1 : "L\'b\'";
  22. escapeMap['\t'.charCodeAt(0)] = count ? 1 : "L\'t\'";
  23. escapeMap['\f'.charCodeAt(0)] = count ? 1 : "L\'f\'";
  24. escapeMap['\r'.charCodeAt(0)] = count ? 1 : "L\'r\'";
  25. escapeMap['\\'.charCodeAt(0)] = count ? 1 : "L\'\\\\\'";
  26. escapeMap['"'.charCodeAt(0)] = count ? 1 : "L\'\"\'";
  27. WScript.Echo("{ " + escapeMap.join(", ") + " }");
  28. }
  29. createEscapeMap(false);
  30. createEscapeMap(true);
  31. */
  32. const WCHAR LazyJSONString::escapeMap[] = {
  33. _u('u'), _u('u'), _u('u'), _u('u'), _u('u'), _u('u'), _u('u'), _u('u'), _u('b'), _u('t'), _u('n'), _u('u'), _u('f'),
  34. _u('r'), _u('u'), _u('u'), _u('u'), _u('u'), _u('u'), _u('u'), _u('u'), _u('u'), _u('u'), _u('u'), _u('u'), _u('u'),
  35. _u('u'), _u('u'), _u('u'), _u('u'), _u('u'), _u('u'), _u('\0'), _u('\0'), _u('"'), _u('\0'), _u('\0'), _u('\0'),
  36. _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'),
  37. _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'),
  38. _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'),
  39. _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'),
  40. _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\\'),
  41. _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'),
  42. _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'),
  43. _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'), _u('\0'),
  44. _u('\0'), _u('\0') };
  45. const BYTE LazyJSONString::escapeMapCount[] =
  46. { 5, 5, 5, 5, 5, 5, 5, 5, 1, 1, 1, 5, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 1, 0, 0, 0, 0, 0
  47. , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
  48. , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
  49. , 0, 0, 0, 0, 0, 0, 0, 0 };
  50. LazyJSONString::LazyJSONString(_In_ JSONProperty* jsonContent, charcount_t length, _In_opt_ const char16* gap, charcount_t gapLength, _In_ StaticType* type) :
  51. JavascriptString(type),
  52. jsonContent(jsonContent),
  53. gap(gap),
  54. gapLength(gapLength)
  55. {
  56. // Use SetLength to ensure length is valid
  57. SetLength(length);
  58. }
  59. bool
  60. LazyJSONString::HasComplexGap() const
  61. {
  62. for (charcount_t i = 0; i < this->gapLength; ++i)
  63. {
  64. switch (this->gap[i])
  65. {
  66. // This is not exhaustive, just a useful subset of semantics preserving characters
  67. case _u(' '):
  68. case _u('\t'):
  69. case _u('\n'):
  70. continue;
  71. default:
  72. return true;
  73. }
  74. }
  75. return false;
  76. }
  77. DynamicObject*
  78. LazyJSONString::ReconstructObject(_In_ JSONObject* valueList) const
  79. {
  80. const uint elementCount = valueList->Count();
  81. // This is just a heuristic, so overflow doesn't matter
  82. PropertyIndex requestedInlineSlotCapacity = static_cast<PropertyIndex>(elementCount);
  83. DynamicObject* obj = this->GetLibrary()->CreateObject(
  84. true, // allowObjectHeaderInlining
  85. requestedInlineSlotCapacity);
  86. FOREACH_SLISTCOUNTED_ENTRY(JSONObjectProperty, entry, valueList)
  87. {
  88. Var propertyValue = ReconstructVar(&entry.propertyValue);
  89. JavascriptString* propertyName = entry.propertyName;
  90. PropertyString* propertyString = JavascriptOperators::TryFromVar<PropertyString>(propertyName);
  91. PropertyValueInfo info;
  92. if (!propertyString || !propertyString->TrySetPropertyFromCache(obj, propertyValue, this->GetScriptContext(), PropertyOperation_None, &info))
  93. {
  94. Js::PropertyRecord const * localPropertyRecord;
  95. propertyName->GetPropertyRecord(&localPropertyRecord);
  96. JavascriptOperators::SetProperty(obj, obj, localPropertyRecord->GetPropertyId(),
  97. propertyValue, &info, this->GetScriptContext());
  98. }
  99. }
  100. NEXT_SLISTCOUNTED_ENTRY;
  101. return obj;
  102. }
  103. JavascriptArray*
  104. LazyJSONString::ReconstructArray(_In_ JSONArray* jsonArray) const
  105. {
  106. const uint32 length = jsonArray->length;
  107. JavascriptArray* arr = this->GetLibrary()->CreateArrayLiteral(length);
  108. JSONProperty* prop = jsonArray->arr;
  109. for (uint i = 0; i < length; ++i)
  110. {
  111. Var element = ReconstructVar(&prop[i]);
  112. BOOL result = arr->SetItem(i, element, PropertyOperation_None);
  113. Assert(result); // Setting item in an array we just allocated should always succeed
  114. }
  115. return arr;
  116. }
  117. Var
  118. LazyJSONString::ReconstructVar(_In_ JSONProperty* prop) const
  119. {
  120. switch (prop->type)
  121. {
  122. case JSONContentType::Array:
  123. return ReconstructArray(prop->arr);
  124. case JSONContentType::Null:
  125. return this->GetLibrary()->GetNull();
  126. case JSONContentType::True:
  127. return this->GetLibrary()->GetTrue();
  128. case JSONContentType::False:
  129. return this->GetLibrary()->GetFalse();
  130. case JSONContentType::Number:
  131. return prop->numericValue.value;
  132. case JSONContentType::String:
  133. return prop->stringValue;
  134. case JSONContentType::Object:
  135. return ReconstructObject(prop->obj);
  136. default:
  137. Assume(UNREACHED);
  138. return nullptr;
  139. }
  140. }
  141. Var
  142. LazyJSONString::TryParse() const
  143. {
  144. // If we have thrown away our metadata, we won't be able to Parse
  145. if (this->jsonContent == nullptr)
  146. {
  147. return nullptr;
  148. }
  149. // If the gap is complex, this means that parse will be a non-trivial transformation,
  150. // so fall back to the real parse
  151. if (this->HasComplexGap())
  152. {
  153. return nullptr;
  154. }
  155. Var result = ReconstructVar(this->jsonContent);
  156. return result;
  157. }
  158. const char16*
  159. LazyJSONString::GetSz()
  160. {
  161. if (this->IsFinalized())
  162. {
  163. return this->UnsafeGetBuffer();
  164. }
  165. const charcount_t allocSize = this->SafeSzSize();
  166. Recycler* recycler = GetScriptContext()->GetRecycler();
  167. char16* target = RecyclerNewArrayLeaf(recycler, char16, allocSize);
  168. JSONStringBuilder builder(
  169. this->GetScriptContext(),
  170. this->jsonContent,
  171. target,
  172. allocSize,
  173. this->gap,
  174. this->gapLength);
  175. builder.Build();
  176. this->SetBuffer(target);
  177. // You probably aren't going to parse if you are using the string buffer
  178. // Let's throw away the metadata so we can reclaim the memory
  179. this->jsonContent = nullptr;
  180. return target;
  181. }
  182. template <> bool VarIsImpl<LazyJSONString>(RecyclableObject* obj)
  183. {
  184. return VirtualTableInfo<LazyJSONString>::HasVirtualTable(obj);
  185. }
  186. } // namespace Js