JavascriptSymbol.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  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. PropertyRecordUsageCache * JavascriptSymbol::GetPropertyRecordUsageCache()
  9. {
  10. return &this->propertyRecordUsageCache;
  11. }
  12. Var JavascriptSymbol::NewInstance(RecyclableObject* function, CallInfo callInfo, ...)
  13. {
  14. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  15. ARGUMENTS(args, callInfo);
  16. ScriptContext* scriptContext = function->GetScriptContext();
  17. AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
  18. CHAKRATEL_LANGSTATS_INC_LANGFEATURECOUNT(ES6, Symbol, scriptContext);
  19. // SkipDefaultNewObject function flag should have prevented the default object from
  20. // being created, except when call true a host dispatch.
  21. JavascriptOperators::GetAndAssertIsConstructorSuperCall(args);
  22. if (callInfo.Flags & CallFlags_New)
  23. {
  24. JavascriptError::ThrowTypeError(scriptContext, JSERR_ErrorOnNew, _u("Symbol"));
  25. }
  26. JavascriptString* description;
  27. if (args.Info.Count > 1 && !JavascriptOperators::IsUndefined(args[1]))
  28. {
  29. description = JavascriptConversion::ToString(args[1], scriptContext);
  30. }
  31. else
  32. {
  33. description = scriptContext->GetLibrary()->GetEmptyString();
  34. }
  35. return scriptContext->GetLibrary()->CreateSymbol(description);
  36. }
  37. // Symbol.prototype.valueOf as described in ES 2015
  38. Var JavascriptSymbol::EntryValueOf(RecyclableObject* function, CallInfo callInfo, ...)
  39. {
  40. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  41. ARGUMENTS(args, callInfo);
  42. ScriptContext* scriptContext = function->GetScriptContext();
  43. Assert(!(callInfo.Flags & CallFlags_New));
  44. if (VarIs<JavascriptSymbol>(args[0]))
  45. {
  46. return args[0];
  47. }
  48. else if (VarIs<JavascriptSymbolObject>(args[0]))
  49. {
  50. JavascriptSymbolObject* obj = VarTo<JavascriptSymbolObject>(args[0]);
  51. return CrossSite::MarshalVar(scriptContext, obj->Unwrap(), obj->GetScriptContext());
  52. }
  53. else
  54. {
  55. return TryInvokeRemotelyOrThrow(EntryValueOf, scriptContext, args, JSERR_This_NeedSymbol, _u("Symbol.prototype.valueOf"));
  56. }
  57. }
  58. // Symbol.prototype.toString as described in ES 2015
  59. Var JavascriptSymbol::EntryToString(RecyclableObject* function, CallInfo callInfo, ...)
  60. {
  61. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  62. ARGUMENTS(args, callInfo);
  63. AssertMsg(args.Info.Count, "Should always have implicit 'this'.");
  64. ScriptContext* scriptContext = function->GetScriptContext();
  65. Assert(!(callInfo.Flags & CallFlags_New));
  66. const PropertyRecord* val;
  67. Var aValue = args[0];
  68. if (VarIs<JavascriptSymbol>(aValue))
  69. {
  70. val = VarTo<JavascriptSymbol>(aValue)->GetValue();
  71. }
  72. else if (VarIs<JavascriptSymbolObject>(aValue))
  73. {
  74. val = VarTo<JavascriptSymbolObject>(aValue)->GetValue();
  75. }
  76. else
  77. {
  78. return TryInvokeRemotelyOrThrow(EntryToString, scriptContext, args, JSERR_This_NeedSymbol, _u("Symbol.prototype.toString"));
  79. }
  80. return JavascriptSymbol::ToString(val, scriptContext);
  81. }
  82. // Symbol.for as described in ES 2015
  83. Var JavascriptSymbol::EntryFor(RecyclableObject* function, CallInfo callInfo, ...)
  84. {
  85. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  86. ARGUMENTS(args, callInfo);
  87. AssertMsg(args.Info.Count, "Should always have implicit 'this'.");
  88. ScriptContext* scriptContext = function->GetScriptContext();
  89. JavascriptLibrary* library = scriptContext->GetLibrary();
  90. Assert(!(callInfo.Flags & CallFlags_New));
  91. JavascriptString* key;
  92. if (args.Info.Count > 1)
  93. {
  94. key = JavascriptConversion::ToString(args[1], scriptContext);
  95. }
  96. else
  97. {
  98. key = library->GetUndefinedDisplayString();
  99. }
  100. // Search the global symbol registration map for a symbol with description equal to the string key.
  101. // The map can only have one symbol with that description so if we found a symbol, that is the registered
  102. // symbol for the string key.
  103. const Js::PropertyRecord* propertyRecord = scriptContext->GetThreadContext()->GetSymbolFromRegistrationMap(key->GetString(), key->GetLength());
  104. // If we didn't find a PropertyRecord in the map, we'll create a new symbol with description equal to the key string.
  105. // This is the only place we add new PropertyRecords to the map, so we should never have multiple PropertyRecords in the
  106. // map with the same string key value (since we would return the one we found above instead of creating a new one).
  107. if (propertyRecord == nullptr)
  108. {
  109. propertyRecord = scriptContext->GetThreadContext()->AddSymbolToRegistrationMap(key->GetString(), key->GetLength());
  110. }
  111. Assert(propertyRecord != nullptr);
  112. return scriptContext->GetSymbol(propertyRecord);
  113. }
  114. // Symbol.keyFor as described in ES 2015
  115. Var JavascriptSymbol::EntryKeyFor(RecyclableObject* function, CallInfo callInfo, ...)
  116. {
  117. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  118. ARGUMENTS(args, callInfo);
  119. AssertMsg(args.Info.Count, "Should always have implicit 'this'.");
  120. ScriptContext* scriptContext = function->GetScriptContext();
  121. JavascriptLibrary* library = scriptContext->GetLibrary();
  122. Assert(!(callInfo.Flags & CallFlags_New));
  123. if (args.Info.Count < 2 || !VarIs<JavascriptSymbol>(args[1]))
  124. {
  125. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedSymbol, _u("Symbol.keyFor"));
  126. }
  127. JavascriptSymbol* sym = VarTo<JavascriptSymbol>(args[1]);
  128. const Js::PropertyRecord* symPropertyRecord = sym->GetValue();
  129. const char16* key = symPropertyRecord->GetBuffer();
  130. const charcount_t keyLength = symPropertyRecord->GetLength();
  131. // Search the global symbol registration map for a key equal to the description of the symbol passed into Symbol.keyFor.
  132. // Symbol.for creates a new symbol with description equal to the key and uses that key as a mapping to the new symbol.
  133. // There will only be one symbol in the map with that string key value.
  134. const Js::PropertyRecord* propertyRecord = scriptContext->GetThreadContext()->GetSymbolFromRegistrationMap(key, keyLength);
  135. // If we found a PropertyRecord in the map, make sure it is the same symbol that was passed to Symbol.keyFor.
  136. // If the two are different, it means the symbol passed to keyFor has the same description as a symbol registered via
  137. // Symbol.for _but_ is not the symbol returned from Symbol.for.
  138. if (propertyRecord != nullptr && propertyRecord == sym->GetValue())
  139. {
  140. return JavascriptString::NewCopyBuffer(key, sym->GetValue()->GetLength(), scriptContext);
  141. }
  142. return library->GetUndefined();
  143. }
  144. // Symbol.prototype[@@toPrimitive] as described in ES 2015
  145. Var JavascriptSymbol::EntrySymbolToPrimitive(RecyclableObject* function, CallInfo callInfo, ...)
  146. {
  147. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  148. ARGUMENTS(args, callInfo);
  149. ScriptContext* scriptContext = function->GetScriptContext();
  150. Assert(!(callInfo.Flags & CallFlags_New));
  151. if (VarIs<JavascriptSymbol>(args[0]))
  152. {
  153. return args[0];
  154. }
  155. else if (VarIs<JavascriptSymbolObject>(args[0]))
  156. {
  157. JavascriptSymbolObject* obj = VarTo<JavascriptSymbolObject>(args[0]);
  158. return CrossSite::MarshalVar(scriptContext, obj->Unwrap(), obj->GetScriptContext());
  159. }
  160. else
  161. {
  162. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedSymbol, _u("Symbol[Symbol.toPrimitive]"));
  163. }
  164. }
  165. // Symbol.prototype.description
  166. Var JavascriptSymbol::EntryDescription(RecyclableObject* function, CallInfo callInfo, ...)
  167. {
  168. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  169. ARGUMENTS(args, callInfo);
  170. AssertMsg(args.Info.Count, "Should always have implicit 'this'.");
  171. ScriptContext* scriptContext = function->GetScriptContext();
  172. Assert(!(callInfo.Flags & CallFlags_New));
  173. const PropertyRecord* val;
  174. Var aValue = args[0];
  175. if (VarIs<JavascriptSymbol>(aValue))
  176. {
  177. val = VarTo<JavascriptSymbol>(aValue)->GetValue();
  178. }
  179. else if (VarIs<JavascriptSymbolObject>(aValue))
  180. {
  181. val = VarTo<JavascriptSymbolObject>(aValue)->GetValue();
  182. }
  183. else
  184. {
  185. return TryInvokeRemotelyOrThrow(EntryDescription, scriptContext, args, JSERR_This_NeedSymbol, _u("Symbol.prototype.description"));
  186. }
  187. return scriptContext->GetPropertyString(val->GetPropertyId());
  188. }
  189. RecyclableObject * JavascriptSymbol::CloneToScriptContext(ScriptContext* requestContext)
  190. {
  191. // PropertyRecords are per-ThreadContext so we can just create a new primitive wrapper
  192. // around the PropertyRecord stored in this symbol via the other context library.
  193. return requestContext->GetSymbol(this->GetValue());
  194. }
  195. Var JavascriptSymbol::TryInvokeRemotelyOrThrow(JavascriptMethod entryPoint, ScriptContext * scriptContext, Arguments & args, int32 errorCode, PCWSTR varName)
  196. {
  197. if (JavascriptOperators::GetTypeId(args[0]) == TypeIds_HostDispatch)
  198. {
  199. Var result;
  200. if (VarTo<RecyclableObject>(args[0])->InvokeBuiltInOperationRemotely(entryPoint, args, &result))
  201. {
  202. return result;
  203. }
  204. }
  205. // Don't error if we disabled implicit calls
  206. if (scriptContext->GetThreadContext()->RecordImplicitException())
  207. {
  208. JavascriptError::ThrowTypeError(scriptContext, errorCode, varName);
  209. }
  210. else
  211. {
  212. return scriptContext->GetLibrary()->GetUndefined();
  213. }
  214. }
  215. BOOL JavascriptSymbol::Equals(Var other, BOOL* value, ScriptContext * requestContext)
  216. {
  217. return JavascriptSymbol::Equals(this, other, value, requestContext);
  218. }
  219. BOOL JavascriptSymbol::Equals(JavascriptSymbol* left, Var right, BOOL* value, ScriptContext * requestContext)
  220. {
  221. TypeId typeId = JavascriptOperators::GetTypeId(right);
  222. if (typeId != TypeIds_Symbol && typeId != TypeIds_SymbolObject)
  223. {
  224. right = JavascriptConversion::ToPrimitive<JavascriptHint::None>(right, requestContext);
  225. typeId = JavascriptOperators::GetTypeId(right);
  226. }
  227. switch (typeId)
  228. {
  229. case TypeIds_Symbol:
  230. *value = left == UnsafeVarTo<JavascriptSymbol>(right);
  231. Assert((left->GetValue() == UnsafeVarTo<JavascriptSymbol>(right)->GetValue()) == *value);
  232. break;
  233. case TypeIds_SymbolObject:
  234. *value = left == UnsafeVarTo<JavascriptSymbol>(UnsafeVarTo<JavascriptSymbolObject>(right)->Unwrap());
  235. Assert((left->GetValue() == UnsafeVarTo<JavascriptSymbolObject>(right)->GetValue()) == *value);
  236. break;
  237. default:
  238. *value = FALSE;
  239. break;
  240. }
  241. return TRUE;
  242. }
  243. BOOL JavascriptSymbol::GetDiagValueString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext)
  244. {
  245. if (this->GetValue())
  246. {
  247. stringBuilder->AppendCppLiteral(_u("Symbol("));
  248. stringBuilder->Append(this->GetValue()->GetBuffer(), this->GetValue()->GetLength());
  249. stringBuilder->Append(_u(')'));
  250. }
  251. return TRUE;
  252. }
  253. BOOL JavascriptSymbol::GetDiagTypeString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext)
  254. {
  255. stringBuilder->AppendCppLiteral(_u("Symbol"));
  256. return TRUE;
  257. }
  258. RecyclableObject* JavascriptSymbol::ToObject(ScriptContext * requestContext)
  259. {
  260. return requestContext->GetLibrary()->CreateSymbolObject(this);
  261. }
  262. Var JavascriptSymbol::GetTypeOfString(ScriptContext * requestContext)
  263. {
  264. return requestContext->GetLibrary()->GetSymbolTypeDisplayString();
  265. }
  266. JavascriptString* JavascriptSymbol::ToString(ScriptContext * requestContext)
  267. {
  268. if (requestContext->GetThreadContext()->RecordImplicitException())
  269. {
  270. JavascriptError::ThrowTypeError(requestContext, JSERR_ImplicitStrConv, _u("Symbol"));
  271. }
  272. return requestContext->GetLibrary()->GetEmptyString();
  273. }
  274. JavascriptString* JavascriptSymbol::ToString(const PropertyRecord* propertyRecord, ScriptContext * requestContext)
  275. {
  276. const char16* description = propertyRecord->GetBuffer();
  277. uint len = propertyRecord->GetLength();
  278. CompoundString* str = CompoundString::NewWithCharCapacity(len + _countof(_u("Symbol()")), requestContext->GetLibrary());
  279. str->AppendChars(_u("Symbol("), _countof(_u("Symbol(")) - 1);
  280. str->AppendChars(description, len);
  281. str->AppendChars(_u(')'));
  282. return str;
  283. }
  284. } // namespace Js