JSON.cpp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  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. #include "Library/JSONStack.h"
  7. #include "Library/JSONParser.h"
  8. #include "Library/JSON.h"
  9. using namespace Js;
  10. // TODO: make JSON a class and use the normal EntryInfo code
  11. namespace JSON
  12. {
  13. Js::FunctionInfo EntryInfo::Stringify(FORCE_NO_WRITE_BARRIER_TAG(JSON::Stringify), Js::FunctionInfo::ErrorOnNew);
  14. Js::FunctionInfo EntryInfo::Parse(FORCE_NO_WRITE_BARRIER_TAG(JSON::Parse), Js::FunctionInfo::ErrorOnNew);
  15. Js::Var Parse(Js::JavascriptString* input, Js::RecyclableObject* reviver, Js::ScriptContext* scriptContext);
  16. Js::Var Parse(Js::RecyclableObject* function, Js::CallInfo callInfo, ...)
  17. {
  18. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  19. //ES5: parse(text [, reviver])
  20. ARGUMENTS(args, callInfo);
  21. AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
  22. Js::ScriptContext* scriptContext = function->GetScriptContext();
  23. AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("JSON.parse"));
  24. Assert(!(callInfo.Flags & Js::CallFlags_New));
  25. if(args.Info.Count < 2)
  26. {
  27. // if the text argument is missing it is assumed to be undefined.
  28. // ToString(undefined) returns "undefined" which is not a JSON grammar correct construct. Shortcut and throw here
  29. Js::JavascriptError::ThrowSyntaxError(scriptContext, ERRsyntax);
  30. }
  31. Js::Var value = args[1];
  32. Js::JavascriptString * input = JavascriptOperators::TryFromVar<Js::JavascriptString>(value);
  33. if (!input)
  34. {
  35. input = Js::JavascriptConversion::ToString(value, scriptContext);
  36. }
  37. Js::RecyclableObject* reviver = nullptr;
  38. if (args.Info.Count > 2 && Js::JavascriptConversion::IsCallable(args[2]))
  39. {
  40. reviver = Js::UnsafeVarTo<Js::RecyclableObject>(args[2]);
  41. }
  42. return Parse(input, reviver, scriptContext);
  43. }
  44. Js::Var Parse(Js::JavascriptString* input, Js::RecyclableObject* reviver, Js::ScriptContext* scriptContext)
  45. {
  46. // alignment required because of the union in JSONParser::m_token
  47. __declspec (align(8)) JSONParser parser(scriptContext, reviver);
  48. Js::Var result = NULL;
  49. TryFinally([&]()
  50. {
  51. LazyJSONString* lazyString = JavascriptOperators::TryFromVar<LazyJSONString>(input);
  52. if (lazyString)
  53. {
  54. // Try to reconstruct object based on the data collected during stringify
  55. // In case of semantically important gap, this call may fail and we will need to do real parse
  56. result = lazyString->TryParse();
  57. }
  58. if (result == nullptr)
  59. {
  60. result = parser.Parse(input);
  61. }
  62. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  63. if (CONFIG_FLAG(ForceGCAfterJSONParse))
  64. {
  65. Recycler* recycler = scriptContext->GetRecycler();
  66. recycler->CollectNow<CollectNowForceInThread>();
  67. }
  68. #endif
  69. if (reviver)
  70. {
  71. Js::DynamicObject* root = scriptContext->GetLibrary()->CreateObject();
  72. JS_ETW(EventWriteJSCRIPT_RECYCLER_ALLOCATE_OBJECT(root));
  73. Js::PropertyId propertyId = scriptContext->GetEmptyStringPropertyId();
  74. Js::JavascriptOperators::InitProperty(root, propertyId, result);
  75. result = parser.Walk(scriptContext->GetLibrary()->GetEmptyString(), propertyId, root);
  76. }
  77. },
  78. [&](bool/*hasException*/)
  79. {
  80. parser.Finalizer();
  81. });
  82. return result;
  83. }
  84. Js::Var Stringify(Js::RecyclableObject* function, Js::CallInfo callInfo, ...)
  85. {
  86. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  87. //ES5: Stringify(value, [replacer][, space]])
  88. ARGUMENTS(args, callInfo);
  89. Js::JavascriptLibrary* library = function->GetType()->GetLibrary();
  90. Js::ScriptContext* scriptContext = library->GetScriptContext();
  91. AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("JSON.stringify"));
  92. Assert(!(callInfo.Flags & Js::CallFlags_New));
  93. if (args.Info.Count < 2)
  94. {
  95. // if value is missing it is assumed to be 'undefined'.
  96. // shortcut: the stringify algorithm returns undefined in this case.
  97. return library->GetUndefined();
  98. }
  99. Js::Var value = args[1];
  100. Js::Var replacerArg = args.Info.Count > 2 ? args[2] : nullptr;
  101. Js::Var space = args.Info.Count > 3 ? args[3] : library->GetNull();
  102. if (Js::JavascriptOperators::GetTypeId(value) == Js::TypeIds_HostDispatch)
  103. {
  104. // If we a remote object, we need to pull out the underlying JS object to stringify that
  105. Js::DynamicObject* remoteObject = Js::VarTo<Js::RecyclableObject>(value)->GetRemoteObject();
  106. if (remoteObject != nullptr)
  107. {
  108. AssertOrFailFast(Js::VarIsCorrectType(remoteObject));
  109. value = remoteObject;
  110. }
  111. else
  112. {
  113. Js::Var result;
  114. if (Js::VarTo<Js::RecyclableObject>(value)->InvokeBuiltInOperationRemotely(Stringify, args, &result))
  115. {
  116. return result;
  117. }
  118. }
  119. }
  120. LazyJSONString* lazy = JSONStringifier::Stringify(scriptContext, value, replacerArg, space);
  121. if (!lazy)
  122. {
  123. return library->GetUndefined();
  124. }
  125. return lazy;
  126. }
  127. } // namespace JSON