JavascriptObject.cpp 83 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031
  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. Var JavascriptObject::NewInstance(RecyclableObject* function, CallInfo callInfo, ...)
  9. {
  10. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  11. ARGUMENTS(args, callInfo);
  12. ScriptContext* scriptContext = function->GetScriptContext();
  13. AssertMsg(args.HasArg(), "Should always have implicit 'this'");
  14. // SkipDefaultNewObject function flag should have prevented the default object from
  15. // being created, except when call true a host dispatch.
  16. Var newTarget = args.GetNewTarget();
  17. bool isCtorSuperCall = JavascriptOperators::GetAndAssertIsConstructorSuperCall(args);
  18. if (args.Info.Count > 1)
  19. {
  20. switch (JavascriptOperators::GetTypeId(args[1]))
  21. {
  22. case TypeIds_Undefined:
  23. case TypeIds_Null:
  24. // Break to return a new object
  25. break;
  26. case TypeIds_StringObject:
  27. case TypeIds_Function:
  28. case TypeIds_Array:
  29. case TypeIds_ES5Array:
  30. case TypeIds_RegEx:
  31. case TypeIds_NumberObject:
  32. case TypeIds_SIMDObject:
  33. case TypeIds_Date:
  34. case TypeIds_BooleanObject:
  35. case TypeIds_Error:
  36. case TypeIds_Object:
  37. case TypeIds_Arguments:
  38. case TypeIds_ActivationObject:
  39. case TypeIds_SymbolObject:
  40. return isCtorSuperCall ?
  41. JavascriptOperators::OrdinaryCreateFromConstructor(RecyclableObject::FromVar(newTarget), RecyclableObject::FromVar(args[1]), nullptr, scriptContext) :
  42. args[1];
  43. default:
  44. RecyclableObject* result = nullptr;
  45. if (FALSE == JavascriptConversion::ToObject(args[1], scriptContext, &result))
  46. {
  47. // JavascriptConversion::ToObject should only return FALSE for null and undefined.
  48. Assert(false);
  49. }
  50. return isCtorSuperCall ?
  51. JavascriptOperators::OrdinaryCreateFromConstructor(RecyclableObject::FromVar(newTarget), result, nullptr, scriptContext) :
  52. result;
  53. }
  54. }
  55. if (callInfo.Flags & CallFlags_NotUsed)
  56. {
  57. return args[0];
  58. }
  59. Var newObj = scriptContext->GetLibrary()->CreateObject(true);
  60. return isCtorSuperCall ?
  61. JavascriptOperators::OrdinaryCreateFromConstructor(RecyclableObject::FromVar(newTarget), RecyclableObject::FromVar(newObj), nullptr, scriptContext) :
  62. newObj;
  63. }
  64. Var JavascriptObject::EntryHasOwnProperty(RecyclableObject* function, CallInfo callInfo, ...)
  65. {
  66. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  67. ARGUMENTS(args, callInfo);
  68. ScriptContext* scriptContext = function->GetScriptContext();
  69. Assert(!(callInfo.Flags & CallFlags_New));
  70. AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
  71. RecyclableObject* dynamicObject = nullptr;
  72. if (FALSE == JavascriptConversion::ToObject(args[0], scriptContext, &dynamicObject))
  73. {
  74. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Object.prototype.hasOwnProperty"));
  75. }
  76. // no property specified
  77. if (args.Info.Count == 1)
  78. {
  79. return scriptContext->GetLibrary()->GetFalse();
  80. }
  81. const PropertyRecord* propertyRecord;
  82. PropertyString* propertyString;
  83. JavascriptConversion::ToPropertyKey(args[1], scriptContext, &propertyRecord, &propertyString);
  84. if (JavascriptOperators::HasOwnProperty(dynamicObject, propertyRecord->GetPropertyId(), scriptContext, propertyString))
  85. {
  86. return scriptContext->GetLibrary()->GetTrue();
  87. }
  88. return scriptContext->GetLibrary()->GetFalse();
  89. }
  90. Var JavascriptObject::EntryPropertyIsEnumerable(RecyclableObject* function, CallInfo callInfo, ...)
  91. {
  92. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  93. ARGUMENTS(args, callInfo);
  94. ScriptContext* scriptContext = function->GetScriptContext();
  95. Assert(!(callInfo.Flags & CallFlags_New));
  96. AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
  97. RecyclableObject* dynamicObject = nullptr;
  98. if (FALSE == JavascriptConversion::ToObject(args[0], scriptContext, &dynamicObject))
  99. {
  100. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Object.prototype.propertyIsEnumerable"));
  101. }
  102. if (args.Info.Count >= 2)
  103. {
  104. const PropertyRecord* propertyRecord;
  105. JavascriptConversion::ToPropertyKey(args[1], scriptContext, &propertyRecord, nullptr);
  106. PropertyId propertyId = propertyRecord->GetPropertyId();
  107. PropertyDescriptor currentDescriptor;
  108. BOOL isCurrentDescriptorDefined = JavascriptOperators::GetOwnPropertyDescriptor(dynamicObject, propertyId, scriptContext, &currentDescriptor);
  109. if (isCurrentDescriptorDefined == TRUE)
  110. {
  111. if (currentDescriptor.IsEnumerable())
  112. {
  113. return scriptContext->GetLibrary()->GetTrue();
  114. }
  115. }
  116. }
  117. return scriptContext->GetLibrary()->GetFalse();
  118. }
  119. BOOL JavascriptObject::ChangePrototype(RecyclableObject* object, RecyclableObject* newPrototype, bool shouldThrow, ScriptContext* scriptContext)
  120. {
  121. // 8.3.2 [[SetInheritance]] (V)
  122. // When the [[SetInheritance]] internal method of O is called with argument V the following steps are taken:
  123. // 1. Assert: Either Type(V) is Object or Type(V) is Null.
  124. Assert(JavascriptOperators::IsObject(object));
  125. Assert(JavascriptOperators::IsObjectOrNull(newPrototype));
  126. if (JavascriptProxy::Is(object))
  127. {
  128. JavascriptProxy* proxy = JavascriptProxy::FromVar(object);
  129. CrossSite::ForceCrossSiteThunkOnPrototypeChain(newPrototype);
  130. return proxy->SetPrototypeTrap(newPrototype, shouldThrow, scriptContext);
  131. }
  132. // 2. Let extensible be the value of the [[Extensible]] internal data property of O.
  133. // 3. Let current be the value of the [[Prototype]] internal data property of O.
  134. // 4. If SameValue(V, current), then return true.
  135. if (newPrototype == JavascriptObject::GetPrototypeOf(object, scriptContext))
  136. {
  137. return TRUE;
  138. }
  139. // 5. If extensible is false, then return false.
  140. if (!object->IsExtensible())
  141. {
  142. if (shouldThrow)
  143. {
  144. JavascriptError::ThrowTypeError(scriptContext, JSERR_NonExtensibleObject);
  145. }
  146. return FALSE;
  147. }
  148. if (object->IsProtoImmutable())
  149. {
  150. // ES2016 19.1.3:
  151. // The Object prototype object is the intrinsic object %ObjectPrototype%.
  152. // The Object prototype object is an immutable prototype exotic object.
  153. // ES2016 9.4.7:
  154. // An immutable prototype exotic object is an exotic object that has an immutable [[Prototype]] internal slot.
  155. JavascriptError::ThrowTypeError(scriptContext, JSERR_ImmutablePrototypeSlot);
  156. }
  157. // 6. If V is not null, then
  158. // a. Let p be V.
  159. // b. Repeat, while p is not null
  160. // i. If SameValue(p, O) is true, then return false.
  161. // ii. Let nextp be the result of calling the [[GetInheritance]] internal method of p with no arguments.
  162. // iii. ReturnIfAbrupt(nextp).
  163. // iv. Let p be nextp.
  164. if (IsPrototypeOf(object, newPrototype, scriptContext)) // Reject cycle
  165. {
  166. if (shouldThrow)
  167. {
  168. JavascriptError::ThrowTypeError(scriptContext, JSERR_CyclicProtoValue);
  169. }
  170. return FALSE;
  171. }
  172. // 7. Set the value of the [[Prototype]] internal data property of O to V.
  173. // 8. Return true.
  174. bool isInvalidationOfInlineCacheNeeded = true;
  175. DynamicObject * obj = DynamicObject::FromVar(object);
  176. // If this object was not prototype object, then no need to invalidate inline caches.
  177. // Simply assign it a new type so if this object used protoInlineCache in past, it will
  178. // be invalidated because of type mismatch and subsequently we will update its protoInlineCache
  179. if (!(obj->GetDynamicType()->GetTypeHandler()->GetFlags() & DynamicTypeHandler::IsPrototypeFlag))
  180. {
  181. // If object has locked type, skip changing its type here as it will be changed anyway below
  182. // when object gets newPrototype object.
  183. if (!obj->HasLockedType())
  184. {
  185. obj->ChangeType();
  186. }
  187. Assert(!obj->GetScriptContext()->GetThreadContext()->IsObjectRegisteredInProtoInlineCaches(obj));
  188. Assert(!obj->GetScriptContext()->GetThreadContext()->IsObjectRegisteredInStoreFieldInlineCaches(obj));
  189. isInvalidationOfInlineCacheNeeded = false;
  190. }
  191. if (isInvalidationOfInlineCacheNeeded)
  192. {
  193. // Notify old prototypes that they are being removed from a prototype chain. This triggers invalidating protocache, etc.
  194. JavascriptOperators::MapObjectAndPrototypes<true>(object->GetPrototype(), [=](RecyclableObject* obj)
  195. {
  196. obj->RemoveFromPrototype(scriptContext);
  197. });
  198. // Examine new prototype chain. If it brings in any non-WritableData property, we need to invalidate related caches.
  199. bool objectAndPrototypeChainHasOnlyWritableDataProperties =
  200. JavascriptOperators::CheckIfObjectAndPrototypeChainHasOnlyWritableDataProperties(newPrototype);
  201. if (!objectAndPrototypeChainHasOnlyWritableDataProperties
  202. || object->GetScriptContext() != newPrototype->GetScriptContext())
  203. {
  204. // The HaveOnlyWritableDataProperties cache is cleared when a property is added or changed,
  205. // but only for types in the same script context. Therefore, if the prototype is in another
  206. // context, the object's cache won't be cleared when a property is added or changed on the prototype.
  207. // Moreover, an object is added to the cache only when its whole prototype chain is in the same
  208. // context.
  209. //
  210. // Since we don't have a way to find out which objects have a certain object as their prototype,
  211. // we clear the cache here instead.
  212. // Invalidate fast prototype chain writable data test flag
  213. object->GetLibrary()->NoPrototypeChainsAreEnsuredToHaveOnlyWritableDataProperties();
  214. }
  215. if (!objectAndPrototypeChainHasOnlyWritableDataProperties)
  216. {
  217. // Invalidate StoreField/PropertyGuards for any non-WritableData property in the new chain
  218. JavascriptOperators::MapObjectAndPrototypes<true>(newPrototype, [=](RecyclableObject* obj)
  219. {
  220. if (!obj->HasOnlyWritableDataProperties())
  221. {
  222. obj->AddToPrototype(scriptContext);
  223. }
  224. });
  225. }
  226. }
  227. // Set to new prototype
  228. if (object->IsExternal() || (DynamicType::Is(object->GetTypeId()) && (DynamicObject::UnsafeFromVar(object))->IsCrossSiteObject()))
  229. {
  230. CrossSite::ForceCrossSiteThunkOnPrototypeChain(newPrototype);
  231. }
  232. object->SetPrototype(newPrototype);
  233. return TRUE;
  234. }
  235. Var JavascriptObject::EntryIsPrototypeOf(RecyclableObject* function, CallInfo callInfo, ...)
  236. {
  237. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  238. ARGUMENTS(args, callInfo);
  239. ScriptContext* scriptContext = function->GetScriptContext();
  240. Assert(!(callInfo.Flags & CallFlags_New));
  241. AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
  242. // no property specified
  243. if (args.Info.Count == 1 || !JavascriptOperators::IsObject(args[1]))
  244. {
  245. return scriptContext->GetLibrary()->GetFalse();
  246. }
  247. RecyclableObject* dynamicObject = nullptr;
  248. if (FALSE == JavascriptConversion::ToObject(args[0], scriptContext, &dynamicObject))
  249. {
  250. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Object.prototype.isPrototypeOf"));
  251. }
  252. RecyclableObject* value = RecyclableObject::FromVar(args[1]);
  253. if (dynamicObject->GetTypeId() == TypeIds_GlobalObject)
  254. {
  255. dynamicObject = RecyclableObject::FromVar(static_cast<Js::GlobalObject*>(dynamicObject)->ToThis());
  256. }
  257. while (!JavascriptOperators::IsNull(value))
  258. {
  259. value = JavascriptOperators::GetPrototype(value);
  260. if (dynamicObject == value)
  261. {
  262. return scriptContext->GetLibrary()->GetTrue();
  263. }
  264. }
  265. return scriptContext->GetLibrary()->GetFalse();
  266. }
  267. // 19.1.3.5 - Object.prototype.toLocaleString as of ES6 (6.0)
  268. Var JavascriptObject::EntryToLocaleString(RecyclableObject* function, CallInfo callInfo, ...)
  269. {
  270. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  271. ARGUMENTS(args, callInfo);
  272. ScriptContext* scriptContext = function->GetScriptContext();
  273. Assert(!(callInfo.Flags & CallFlags_New));
  274. AssertMsg(args.Info.Count, "Should always have implicit 'this'");
  275. Var thisValue = args[0];
  276. RecyclableObject* dynamicObject = nullptr;
  277. if (FALSE == JavascriptConversion::ToObject(thisValue, scriptContext, &dynamicObject))
  278. {
  279. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Object.prototype.toLocaleString"));
  280. }
  281. Var toStringVar = nullptr;
  282. if (!JavascriptOperators::GetProperty(thisValue, dynamicObject, Js::PropertyIds::toString, &toStringVar, scriptContext) || !JavascriptConversion::IsCallable(toStringVar))
  283. {
  284. JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedFunction, _u("Object.prototype.toLocaleString"));
  285. }
  286. RecyclableObject* toStringFunc = RecyclableObject::FromVar(toStringVar);
  287. return CALL_FUNCTION(scriptContext->GetThreadContext(), toStringFunc, CallInfo(CallFlags_Value, 1), thisValue);
  288. }
  289. Var JavascriptObject::EntryToString(RecyclableObject* function, CallInfo callInfo, ...)
  290. {
  291. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  292. ARGUMENTS(args, callInfo);
  293. ScriptContext* scriptContext = function->GetScriptContext();
  294. Assert(!(callInfo.Flags & CallFlags_New));
  295. AssertMsg(args.Info.Count, "Should always have implicit 'this'");
  296. return ToStringHelper(args[0], scriptContext);
  297. }
  298. Var JavascriptObject::GetToStringTagValue(RecyclableObject *thisArg, ScriptContext *scriptContext)
  299. {
  300. const PropertyId toStringTagId(PropertyIds::_symbolToStringTag);
  301. PolymorphicInlineCache *cache = scriptContext->GetLibrary()->GetToStringTagCache();
  302. PropertyValueInfo info;
  303. // We don't allow cache resizing, at least for the moment: it's more work, and since there's only one
  304. // cache per script context, we can afford to create each cache with the maximum size.
  305. PropertyValueInfo::SetCacheInfo(&info, cache, false);
  306. Var value;
  307. if (CacheOperators::TryGetProperty<
  308. true, // CheckLocal
  309. true, // CheckProto
  310. true, // CheckAccessor
  311. true, // CheckMissing
  312. true, // CheckPolymorphicInlineCache
  313. true, // CheckTypePropertyCache
  314. !PolymorphicInlineCache::IsPolymorphic, // IsInlineCacheAvailable
  315. PolymorphicInlineCache::IsPolymorphic, // IsPolymorphicInlineCacheAvailable
  316. false> // ReturnOperationInfo
  317. (thisArg, false, thisArg, toStringTagId, &value, scriptContext, nullptr, &info))
  318. {
  319. return value;
  320. }
  321. else
  322. {
  323. #if DBG_DUMP
  324. if (PHASE_VERBOSE_TRACE1(Js::InlineCachePhase))
  325. {
  326. CacheOperators::TraceCache(cache, _u("PatchGetValue"), toStringTagId, scriptContext, thisArg);
  327. }
  328. #endif
  329. return JavascriptOperators::GetProperty(thisArg, thisArg, toStringTagId, scriptContext, &info);
  330. }
  331. }
  332. // ES2017 19.1.3.6 Object.prototype.toString()
  333. JavascriptString* JavascriptObject::ToStringTagHelper(Var thisArg, ScriptContext *scriptContext, TypeId type)
  334. {
  335. JavascriptLibrary *library = scriptContext->GetLibrary();
  336. // 1. If the this value is undefined, return "[object Undefined]".
  337. if (type == TypeIds_Undefined)
  338. {
  339. return library->GetObjectUndefinedDisplayString();
  340. }
  341. // 2. If the this value is null, return "[object Null]".
  342. if (type == TypeIds_Null)
  343. {
  344. return library->GetObjectNullDisplayString();
  345. }
  346. // 3. Let O be ToObject(this value).
  347. RecyclableObject *thisArgAsObject = JavascriptOperators::ToObject(thisArg, scriptContext);
  348. // 15. Let tag be ? Get(O, @@toStringTag).
  349. Var tag = JavascriptObject::GetToStringTagValue(thisArgAsObject, scriptContext);
  350. // 17. Return the String that is the result of concatenating "[object ", tag, and "]".
  351. auto buildToString = [&scriptContext](Var tag) {
  352. JavascriptString *tagStr = JavascriptString::FromVar(tag);
  353. const WCHAR objectStartString[9] = _u("[object ");
  354. const WCHAR objectEndString[1] = { _u(']') };
  355. CompoundString *const cs = CompoundString::NewWithCharCapacity(_countof(objectStartString)
  356. + _countof(objectEndString) + tagStr->GetLength(), scriptContext->GetLibrary());
  357. cs->AppendChars(objectStartString, _countof(objectStartString) - 1 /* ditch \0 */);
  358. cs->AppendChars(tagStr);
  359. cs->AppendChars(objectEndString, _countof(objectEndString));
  360. return cs;
  361. };
  362. if (tag != nullptr && JavascriptString::Is(tag))
  363. {
  364. return buildToString(tag);
  365. }
  366. // 4. Let isArray be ? IsArray(O).
  367. // There is an implicit check for a null proxy handler in IsArray, so use the operator.
  368. BOOL isArray = JavascriptOperators::IsArray(thisArgAsObject);
  369. // If we don't have a tag or it's not a string, use the 'built in tag'.
  370. if (isArray)
  371. {
  372. // 5. If isArray is true, let builtinTag be "Array".
  373. return library->GetObjectArrayDisplayString();
  374. }
  375. JavascriptString* builtInTag = nullptr;
  376. switch (type)
  377. {
  378. // 6. Else if O is an exotic String object, let builtinTag be "String".
  379. case TypeIds_String:
  380. case TypeIds_StringObject:
  381. builtInTag = library->GetObjectStringDisplayString();
  382. break;
  383. // 7. Else if O has an[[ParameterMap]] internal slot, let builtinTag be "Arguments".
  384. case TypeIds_Arguments:
  385. builtInTag = library->GetObjectArgumentsDisplayString();
  386. break;
  387. // 8. Else if O has a [[Call]] internal method, let builtinTag be "Function".
  388. case TypeIds_Function:
  389. builtInTag = library->GetObjectFunctionDisplayString();
  390. break;
  391. // 9. Else if O has an [[ErrorData]] internal slot, let builtinTag be "Error".
  392. case TypeIds_Error:
  393. builtInTag = library->GetObjectErrorDisplayString();
  394. break;
  395. // 10. Else if O has a [[BooleanData]] internal slot, let builtinTag be "Boolean".
  396. case TypeIds_Boolean:
  397. case TypeIds_BooleanObject:
  398. builtInTag = library->GetObjectBooleanDisplayString();
  399. break;
  400. // 11. Else if O has a [[NumberData]] internal slot, let builtinTag be "Number".
  401. case TypeIds_Number:
  402. case TypeIds_Int64Number:
  403. case TypeIds_UInt64Number:
  404. case TypeIds_Integer:
  405. case TypeIds_NumberObject:
  406. builtInTag = library->GetObjectNumberDisplayString();
  407. break;
  408. // 12. Else if O has a [[DateValue]] internal slot, let builtinTag be "Date".
  409. case TypeIds_Date:
  410. case TypeIds_WinRTDate:
  411. builtInTag = library->GetObjectDateDisplayString();
  412. break;
  413. // 13. Else if O has a [[RegExpMatcher]] internal slot, let builtinTag be "RegExp".
  414. case TypeIds_RegEx:
  415. builtInTag = library->GetObjectRegExpDisplayString();
  416. break;
  417. // 14. Else, let builtinTag be "Object".
  418. default:
  419. {
  420. if (thisArgAsObject->IsExternal())
  421. {
  422. builtInTag = buildToString(thisArgAsObject->GetClassName(scriptContext));
  423. }
  424. else
  425. {
  426. builtInTag = library->GetObjectDisplayString(); // [object Object]
  427. }
  428. break;
  429. }
  430. }
  431. Assert(builtInTag != nullptr);
  432. return builtInTag;
  433. }
  434. Var JavascriptObject::ToStringHelper(Var thisArg, ScriptContext* scriptContext)
  435. {
  436. TypeId type = JavascriptOperators::GetTypeId(thisArg);
  437. // We first need to make sure we are in the right context.
  438. if (type == TypeIds_HostDispatch)
  439. {
  440. RecyclableObject* hostDispatchObject = RecyclableObject::FromVar(thisArg);
  441. const DynamicObject* remoteObject = hostDispatchObject->GetRemoteObject();
  442. if (!remoteObject)
  443. {
  444. Var result = nullptr;
  445. Js::Var values[1];
  446. Js::CallInfo info(Js::CallFlags_Value, 1);
  447. Js::Arguments args(info, values);
  448. values[0] = thisArg;
  449. if (hostDispatchObject->InvokeBuiltInOperationRemotely(EntryToString, args, &result))
  450. {
  451. return result;
  452. }
  453. }
  454. }
  455. // Dispatch to @@toStringTag implementation.
  456. if (type >= TypeIds_TypedArrayMin && type <= TypeIds_TypedArrayMax && !scriptContext->GetThreadContext()->IsScriptActive())
  457. {
  458. // Use external call for typedarray in the debugger.
  459. Var toStringValue = nullptr;
  460. BEGIN_JS_RUNTIME_CALL_EX(scriptContext, false);
  461. toStringValue = ToStringTagHelper(thisArg, scriptContext, type);
  462. END_JS_RUNTIME_CALL(scriptContext);
  463. return toStringValue;
  464. }
  465. // By this point, we should be in the correct context, but the thisArg may still need to be marshalled (for to the implicit ToObject conversion call.)
  466. return ToStringTagHelper(CrossSite::MarshalVar(scriptContext, thisArg), scriptContext, type);
  467. }
  468. // -----------------------------------------------------------
  469. // Object.prototype.valueOf
  470. // 1. Let O be the result of calling ToObject passing the this value as the argument.
  471. // 2. If O is the result of calling the Object constructor with a host object (15.2.2.1), then
  472. // a. Return either O or another value such as the host object originally passed to the constructor. The specific result that is returned is implementation-defined.
  473. // 3. Return O.
  474. // -----------------------------------------------------------
  475. Var JavascriptObject::EntryValueOf(RecyclableObject* function, CallInfo callInfo, ...)
  476. {
  477. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  478. ARGUMENTS(args, callInfo);
  479. ScriptContext* scriptContext = function->GetScriptContext();
  480. Assert(!(callInfo.Flags & CallFlags_New));
  481. AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
  482. // throw a TypeError if TypeId is null or undefined, and apply ToObject to the 'this' value otherwise.
  483. if (JavascriptOperators::IsUndefinedOrNull(args[0]))
  484. {
  485. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Object.prototype.valueOf"));
  486. }
  487. else
  488. {
  489. return JavascriptOperators::ToObject(args[0], scriptContext);
  490. }
  491. }
  492. Var JavascriptObject::EntryGetOwnPropertyDescriptor(RecyclableObject* function, CallInfo callInfo, ...)
  493. {
  494. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  495. ARGUMENTS(args, callInfo);
  496. ScriptContext* scriptContext = function->GetScriptContext();
  497. Assert(!(callInfo.Flags & CallFlags_New));
  498. RecyclableObject* obj = nullptr;
  499. if (args.Info.Count < 2)
  500. {
  501. obj = JavascriptOperators::ToObject(scriptContext->GetLibrary()->GetUndefined(), scriptContext);
  502. }
  503. else
  504. {
  505. // Convert the argument to object first
  506. obj = JavascriptOperators::ToObject(args[1], scriptContext);
  507. }
  508. // If the object is HostDispatch try to invoke the operation remotely
  509. if (obj->GetTypeId() == TypeIds_HostDispatch)
  510. {
  511. Var result;
  512. if (obj->InvokeBuiltInOperationRemotely(EntryGetOwnPropertyDescriptor, args, &result))
  513. {
  514. return result;
  515. }
  516. }
  517. Var propertyKey = args.Info.Count > 2 ? args[2] : obj->GetLibrary()->GetUndefined();
  518. return JavascriptObject::GetOwnPropertyDescriptorHelper(obj, propertyKey, scriptContext);
  519. }
  520. Var JavascriptObject::GetOwnPropertyDescriptorHelper(RecyclableObject* obj, Var propertyKey, ScriptContext* scriptContext)
  521. {
  522. const PropertyRecord* propertyRecord;
  523. JavascriptConversion::ToPropertyKey(propertyKey, scriptContext, &propertyRecord, nullptr);
  524. PropertyId propertyId = propertyRecord->GetPropertyId();
  525. PropertyDescriptor propertyDescriptor;
  526. BOOL isPropertyDescriptorDefined;
  527. isPropertyDescriptorDefined = JavascriptObject::GetOwnPropertyDescriptorHelper(obj, propertyId, scriptContext, propertyDescriptor);
  528. if (!isPropertyDescriptorDefined)
  529. {
  530. return scriptContext->GetLibrary()->GetUndefined();
  531. }
  532. return JavascriptOperators::FromPropertyDescriptor(propertyDescriptor, scriptContext);
  533. }
  534. BOOL JavascriptObject::GetOwnPropertyDescriptorHelper(RecyclableObject* obj, PropertyId propertyId, ScriptContext* scriptContext, PropertyDescriptor& propertyDescriptor)
  535. {
  536. BOOL isPropertyDescriptorDefined;
  537. if (obj->IsExternal())
  538. {
  539. isPropertyDescriptorDefined = obj->HasOwnProperty(propertyId) ?
  540. JavascriptOperators::GetOwnPropertyDescriptor(obj, propertyId, scriptContext, &propertyDescriptor) : obj->GetDefaultPropertyDescriptor(propertyDescriptor);
  541. }
  542. else
  543. {
  544. isPropertyDescriptorDefined = JavascriptOperators::GetOwnPropertyDescriptor(obj, propertyId, scriptContext, &propertyDescriptor) ||
  545. obj->GetDefaultPropertyDescriptor(propertyDescriptor);
  546. }
  547. return isPropertyDescriptorDefined;
  548. }
  549. Var JavascriptObject::EntryGetOwnPropertyDescriptors(RecyclableObject* function, CallInfo callInfo, ...)
  550. {
  551. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  552. ARGUMENTS(args, callInfo);
  553. ScriptContext* scriptContext = function->GetScriptContext();
  554. Assert(!(callInfo.Flags & CallFlags_New));
  555. RecyclableObject* obj = nullptr;
  556. if (args.Info.Count < 2)
  557. {
  558. obj = JavascriptOperators::ToObject(scriptContext->GetLibrary()->GetUndefined(), scriptContext);
  559. }
  560. else
  561. {
  562. // Convert the argument to object first
  563. obj = JavascriptOperators::ToObject(args[1], scriptContext);
  564. }
  565. // If the object is HostDispatch try to invoke the operation remotely
  566. if (obj->GetTypeId() == TypeIds_HostDispatch)
  567. {
  568. Var result;
  569. if (obj->InvokeBuiltInOperationRemotely(EntryGetOwnPropertyDescriptors, args, &result))
  570. {
  571. return result;
  572. }
  573. }
  574. JavascriptArray* ownPropertyKeys = JavascriptOperators::GetOwnPropertyKeys(obj, scriptContext);
  575. RecyclableObject* resultObj = scriptContext->GetLibrary()->CreateObject(true, (Js::PropertyIndex) ownPropertyKeys->GetLength());
  576. PropertyDescriptor propDesc;
  577. Var propKey = nullptr;
  578. for (uint i = 0; i < ownPropertyKeys->GetLength(); i++)
  579. {
  580. BOOL getPropResult = ownPropertyKeys->DirectGetItemAt(i, &propKey);
  581. Assert(getPropResult);
  582. if (!getPropResult)
  583. {
  584. continue;
  585. }
  586. PropertyRecord const * propertyRecord;
  587. JavascriptConversion::ToPropertyKey(propKey, scriptContext, &propertyRecord, nullptr);
  588. Var newDescriptor = JavascriptObject::GetOwnPropertyDescriptorHelper(obj, propKey, scriptContext);
  589. if (!JavascriptOperators::IsUndefined(newDescriptor))
  590. {
  591. resultObj->SetProperty(propertyRecord->GetPropertyId(), newDescriptor, PropertyOperation_None, nullptr);
  592. }
  593. }
  594. return resultObj;
  595. }
  596. Var JavascriptObject::EntryGetPrototypeOf(RecyclableObject* function, CallInfo callInfo, ...)
  597. {
  598. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  599. ARGUMENTS(args, callInfo);
  600. ScriptContext* scriptContext = function->GetScriptContext();
  601. Assert(!(callInfo.Flags & CallFlags_New));
  602. CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(Object_Constructor_getPrototypeOf);
  603. // 19.1.2.9
  604. // Object.getPrototypeOf ( O )
  605. // When the getPrototypeOf function is called with argument O, the following steps are taken:
  606. RecyclableObject *object = nullptr;
  607. // 1. Let obj be ToObject(O).
  608. // 2. ReturnIfAbrupt(obj).
  609. if (args.Info.Count < 2 || !JavascriptConversion::ToObject(args[1], scriptContext, &object))
  610. {
  611. JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedObject, _u("Object.getPrototypeOf"));
  612. }
  613. // 3. Return obj.[[GetPrototypeOf]]().
  614. return CrossSite::MarshalVar(scriptContext, GetPrototypeOf(object, scriptContext));
  615. }
  616. Var JavascriptObject::EntrySetPrototypeOf(RecyclableObject* function, CallInfo callInfo, ...)
  617. {
  618. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  619. ARGUMENTS(args, callInfo);
  620. Assert(!(callInfo.Flags & CallFlags_New));
  621. ScriptContext* scriptContext = function->GetScriptContext();
  622. // 19.1.2.18
  623. // Object.setPrototypeOf ( O, proto )
  624. // When the setPrototypeOf function is called with arguments O and proto, the following steps are taken:
  625. // 1. Let O be RequireObjectCoercible(O).
  626. // 2. ReturnIfAbrupt(O).
  627. // 3. If Type(proto) is neither Object or Null, then throw a TypeError exception.
  628. int32 errCode = NOERROR;
  629. if (args.Info.Count < 2 || !JavascriptConversion::CheckObjectCoercible(args[1], scriptContext))
  630. {
  631. errCode = JSERR_FunctionArgument_NeedObject;
  632. }
  633. else if (args.Info.Count < 3 || !JavascriptOperators::IsObjectOrNull(args[2]))
  634. {
  635. errCode = JSERR_FunctionArgument_NotObjectOrNull;
  636. }
  637. if (errCode != NOERROR)
  638. {
  639. JavascriptError::ThrowTypeError(scriptContext, errCode, _u("Object.setPrototypeOf"));
  640. }
  641. // 4. If Type(O) is not Object, return O.
  642. if (!JavascriptOperators::IsObject(args[1]))
  643. {
  644. return args[1];
  645. }
  646. #if ENABLE_COPYONACCESS_ARRAY
  647. JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(args[1]);
  648. #endif
  649. RecyclableObject* object = RecyclableObject::FromVar(args[1]);
  650. RecyclableObject* newPrototype = RecyclableObject::FromVar(args[2]);
  651. // 5. Let status be O.[[SetPrototypeOf]](proto).
  652. // 6. ReturnIfAbrupt(status).
  653. // 7. If status is false, throw a TypeError exception.
  654. ChangePrototype(object, newPrototype, /*shouldThrow*/true, scriptContext);
  655. // 8. Return O.
  656. return object;
  657. }
  658. Var JavascriptObject::EntrySeal(RecyclableObject* function, CallInfo callInfo, ...)
  659. {
  660. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  661. ARGUMENTS(args, callInfo);
  662. ScriptContext* scriptContext = function->GetScriptContext();
  663. Assert(!(callInfo.Flags & CallFlags_New));
  664. CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(Object_Constructor_seal);
  665. // Spec update in Rev29 under section 19.1.2.17
  666. if (args.Info.Count < 2)
  667. {
  668. return scriptContext->GetLibrary()->GetUndefined();
  669. }
  670. else if (!JavascriptOperators::IsObject(args[1]))
  671. {
  672. return args[1];
  673. }
  674. RecyclableObject *object = RecyclableObject::FromVar(args[1]);
  675. GlobalObject* globalObject = object->GetLibrary()->GetGlobalObject();
  676. if (globalObject != object && globalObject && (globalObject->ToThis() == object))
  677. {
  678. globalObject->Seal();
  679. }
  680. object->Seal();
  681. return object;
  682. }
  683. Var JavascriptObject::EntryFreeze(RecyclableObject* function, CallInfo callInfo, ...)
  684. {
  685. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  686. ARGUMENTS(args, callInfo);
  687. ScriptContext* scriptContext = function->GetScriptContext();
  688. Assert(!(callInfo.Flags & CallFlags_New));
  689. CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(Object_Constructor_freeze);
  690. // Spec update in Rev29 under section 19.1.2.5
  691. if (args.Info.Count < 2)
  692. {
  693. return scriptContext->GetLibrary()->GetUndefined();
  694. }
  695. else if (!JavascriptOperators::IsObject(args[1]))
  696. {
  697. return args[1];
  698. }
  699. RecyclableObject *object = RecyclableObject::FromVar(args[1]);
  700. GlobalObject* globalObject = object->GetLibrary()->GetGlobalObject();
  701. if (globalObject != object && globalObject && (globalObject->ToThis() == object))
  702. {
  703. globalObject->Freeze();
  704. }
  705. object->Freeze();
  706. return object;
  707. }
  708. Var JavascriptObject::EntryPreventExtensions(RecyclableObject* function, CallInfo callInfo, ...)
  709. {
  710. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  711. ARGUMENTS(args, callInfo);
  712. ScriptContext* scriptContext = function->GetScriptContext();
  713. Assert(!(callInfo.Flags & CallFlags_New));
  714. CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(Object_Constructor_preventExtensions);
  715. // Spec update in Rev29 under section 19.1.2.15
  716. if (args.Info.Count < 2)
  717. {
  718. return scriptContext->GetLibrary()->GetUndefined();
  719. }
  720. else if (!JavascriptOperators::IsObject(args[1]))
  721. {
  722. return args[1];
  723. }
  724. RecyclableObject *object = RecyclableObject::FromVar(args[1]);
  725. GlobalObject* globalObject = object->GetLibrary()->GetGlobalObject();
  726. if (globalObject != object && globalObject && (globalObject->ToThis() == object))
  727. {
  728. globalObject->PreventExtensions();
  729. }
  730. object->PreventExtensions();
  731. return object;
  732. }
  733. Var JavascriptObject::EntryIsSealed(RecyclableObject* function, CallInfo callInfo, ...)
  734. {
  735. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  736. ARGUMENTS(args, callInfo);
  737. ScriptContext* scriptContext = function->GetScriptContext();
  738. Assert(!(callInfo.Flags & CallFlags_New));
  739. CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(Object_Constructor_isSealed);
  740. if (args.Info.Count < 2 || !JavascriptOperators::IsObject(args[1]))
  741. {
  742. return scriptContext->GetLibrary()->GetTrue();
  743. }
  744. RecyclableObject *object = RecyclableObject::FromVar(args[1]);
  745. BOOL isSealed = object->IsSealed();
  746. GlobalObject* globalObject = object->GetLibrary()->GetGlobalObject();
  747. if (isSealed && globalObject != object && globalObject && (globalObject->ToThis() == object))
  748. {
  749. isSealed = globalObject->IsSealed();
  750. }
  751. return scriptContext->GetLibrary()->GetTrueOrFalse(isSealed);
  752. }
  753. Var JavascriptObject::EntryIsFrozen(RecyclableObject* function, CallInfo callInfo, ...)
  754. {
  755. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  756. ARGUMENTS(args, callInfo);
  757. ScriptContext* scriptContext = function->GetScriptContext();
  758. Assert(!(callInfo.Flags & CallFlags_New));
  759. CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(Object_Constructor_isFrozen);
  760. if (args.Info.Count < 2 || !JavascriptOperators::IsObject(args[1]))
  761. {
  762. return scriptContext->GetLibrary()->GetTrue();
  763. }
  764. RecyclableObject *object = RecyclableObject::FromVar(args[1]);
  765. BOOL isFrozen = object->IsFrozen();
  766. GlobalObject* globalObject = object->GetLibrary()->GetGlobalObject();
  767. if (isFrozen && globalObject != object && globalObject && (globalObject->ToThis() == object))
  768. {
  769. isFrozen = globalObject->IsFrozen();
  770. }
  771. return scriptContext->GetLibrary()->GetTrueOrFalse(isFrozen);
  772. }
  773. Var JavascriptObject::EntryIsExtensible(RecyclableObject* function, CallInfo callInfo, ...)
  774. {
  775. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  776. ARGUMENTS(args, callInfo);
  777. ScriptContext* scriptContext = function->GetScriptContext();
  778. CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(Object_Constructor_isExtensible);
  779. Assert(!(callInfo.Flags & CallFlags_New));
  780. if (args.Info.Count < 2 || !JavascriptOperators::IsObject(args[1]))
  781. {
  782. return scriptContext->GetLibrary()->GetFalse();
  783. }
  784. RecyclableObject *object = RecyclableObject::FromVar(args[1]);
  785. BOOL isExtensible = object->IsExtensible();
  786. GlobalObject* globalObject = object->GetLibrary()->GetGlobalObject();
  787. if (isExtensible && globalObject != object && globalObject && (globalObject->ToThis() == object))
  788. {
  789. isExtensible = globalObject->IsExtensible();
  790. }
  791. return scriptContext->GetLibrary()->GetTrueOrFalse(isExtensible);
  792. }
  793. Var JavascriptObject::EntryGetOwnPropertyNames(RecyclableObject* function, CallInfo callInfo, ...)
  794. {
  795. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  796. ARGUMENTS(args, callInfo);
  797. ScriptContext* scriptContext = function->GetScriptContext();
  798. Assert(!(callInfo.Flags & CallFlags_New));
  799. CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(Object_Constructor_getOwnPropertyNames);
  800. Var tempVar = args.Info.Count < 2 ? scriptContext->GetLibrary()->GetUndefined() : args[1];
  801. RecyclableObject *object = JavascriptOperators::ToObject(tempVar, scriptContext);
  802. if (object->GetTypeId() == TypeIds_HostDispatch)
  803. {
  804. Var result;
  805. if (object->InvokeBuiltInOperationRemotely(EntryGetOwnPropertyNames, args, &result))
  806. {
  807. return result;
  808. }
  809. }
  810. return JavascriptOperators::GetOwnPropertyNames(object, scriptContext);
  811. }
  812. Var JavascriptObject::EntryGetOwnPropertySymbols(RecyclableObject* function, CallInfo callInfo, ...)
  813. {
  814. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  815. ARGUMENTS(args, callInfo);
  816. ScriptContext* scriptContext = function->GetScriptContext();
  817. Assert(!(callInfo.Flags & CallFlags_New));
  818. Var tempVar = args.Info.Count < 2 ? scriptContext->GetLibrary()->GetUndefined() : args[1];
  819. RecyclableObject *object = JavascriptOperators::ToObject(tempVar, scriptContext);
  820. if (object->GetTypeId() == TypeIds_HostDispatch)
  821. {
  822. Var result;
  823. if (object->InvokeBuiltInOperationRemotely(EntryGetOwnPropertySymbols, args, &result))
  824. {
  825. return result;
  826. }
  827. }
  828. return JavascriptOperators::GetOwnPropertySymbols(object, scriptContext);
  829. }
  830. Var JavascriptObject::EntryKeys(RecyclableObject* function, CallInfo callInfo, ...)
  831. {
  832. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  833. ARGUMENTS(args, callInfo);
  834. ScriptContext* scriptContext = function->GetScriptContext();
  835. Assert(!(callInfo.Flags & CallFlags_New));
  836. CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(Object_Constructor_keys);
  837. Var tempVar = args.Info.Count < 2 ? scriptContext->GetLibrary()->GetUndefined() : args[1];
  838. RecyclableObject *object = JavascriptOperators::ToObject(tempVar, scriptContext);
  839. if (object->GetTypeId() == TypeIds_HostDispatch)
  840. {
  841. Var result;
  842. if (object->InvokeBuiltInOperationRemotely(EntryKeys, args, &result))
  843. {
  844. return result;
  845. }
  846. }
  847. return JavascriptOperators::GetOwnEnumerablePropertyNames(object, scriptContext);
  848. }
  849. Var JavascriptObject::GetValuesOrEntries(RecyclableObject* object, bool valuesToReturn, ScriptContext* scriptContext)
  850. {
  851. Assert(object != nullptr);
  852. Assert(scriptContext != nullptr);
  853. JavascriptArray* valuesArray = scriptContext->GetLibrary()->CreateArray(0);
  854. JavascriptArray* ownKeysResult = JavascriptOperators::GetOwnPropertyNames(object, scriptContext);
  855. uint32 length = ownKeysResult->GetLength();
  856. Var nextKey;
  857. const PropertyRecord* propertyRecord = nullptr;
  858. PropertyId propertyId;
  859. for (uint32 i = 0, index = 0; i < length; i++)
  860. {
  861. nextKey = ownKeysResult->DirectGetItem(i);
  862. Assert(JavascriptString::Is(nextKey));
  863. PropertyDescriptor propertyDescriptor;
  864. JavascriptConversion::ToPropertyKey(nextKey, scriptContext, &propertyRecord, nullptr);
  865. propertyId = propertyRecord->GetPropertyId();
  866. Assert(propertyId != Constants::NoProperty);
  867. if (JavascriptOperators::GetOwnPropertyDescriptor(object, propertyId, scriptContext, &propertyDescriptor))
  868. {
  869. if (propertyDescriptor.IsEnumerable())
  870. {
  871. Var value = JavascriptOperators::GetProperty(object, propertyId, scriptContext);
  872. if (!valuesToReturn)
  873. {
  874. // For Object.entries each entry is key, value pair
  875. JavascriptArray* entry = scriptContext->GetLibrary()->CreateArray(2);
  876. entry->DirectSetItemAt(0, CrossSite::MarshalVar(scriptContext, nextKey));
  877. entry->DirectSetItemAt(1, CrossSite::MarshalVar(scriptContext, value));
  878. value = entry;
  879. }
  880. valuesArray->DirectSetItemAt(index++, CrossSite::MarshalVar(scriptContext, value));
  881. }
  882. }
  883. }
  884. return valuesArray;
  885. }
  886. Var JavascriptObject::EntryValues(RecyclableObject* function, CallInfo callInfo, ...)
  887. {
  888. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  889. ARGUMENTS(args, callInfo);
  890. ScriptContext* scriptContext = function->GetScriptContext();
  891. Assert(!(callInfo.Flags & CallFlags_New));
  892. CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(Object_Constructor_values);
  893. Var tempVar = args.Info.Count < 2 ? scriptContext->GetLibrary()->GetUndefined() : args[1];
  894. RecyclableObject *object = JavascriptOperators::ToObject(tempVar, scriptContext);
  895. return GetValuesOrEntries(object, true /*valuesToReturn*/, scriptContext);
  896. }
  897. Var JavascriptObject::EntryEntries(RecyclableObject* function, CallInfo callInfo, ...)
  898. {
  899. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  900. ARGUMENTS(args, callInfo);
  901. ScriptContext* scriptContext = function->GetScriptContext();
  902. Assert(!(callInfo.Flags & CallFlags_New));
  903. CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(Object_Constructor_entries);
  904. Var tempVar = args.Info.Count < 2 ? scriptContext->GetLibrary()->GetUndefined() : args[1];
  905. RecyclableObject *object = JavascriptOperators::ToObject(tempVar, scriptContext);
  906. return GetValuesOrEntries(object, false /*valuesToReturn*/, scriptContext);
  907. }
  908. JavascriptArray* JavascriptObject::CreateOwnSymbolPropertiesHelper(RecyclableObject* object, ScriptContext* scriptContext)
  909. {
  910. return CreateKeysHelper(object, scriptContext, TRUE, true /*includeSymbolsOnly */, false, true /*includeSpecialProperties*/);
  911. }
  912. JavascriptArray* JavascriptObject::CreateOwnStringPropertiesHelper(RecyclableObject* object, ScriptContext* scriptContext)
  913. {
  914. return CreateKeysHelper(object, scriptContext, TRUE, false, true /*includeStringsOnly*/, true /*includeSpecialProperties*/);
  915. }
  916. JavascriptArray* JavascriptObject::CreateOwnStringSymbolPropertiesHelper(RecyclableObject* object, ScriptContext* scriptContext)
  917. {
  918. return CreateKeysHelper(object, scriptContext, TRUE, true/*includeSymbolsOnly*/, true /*includeStringsOnly*/, true /*includeSpecialProperties*/);
  919. }
  920. JavascriptArray* JavascriptObject::CreateOwnEnumerableStringPropertiesHelper(RecyclableObject* object, ScriptContext* scriptContext)
  921. {
  922. return CreateKeysHelper(object, scriptContext, FALSE, false, true/*includeStringsOnly*/, false);
  923. }
  924. JavascriptArray* JavascriptObject::CreateOwnEnumerableStringSymbolPropertiesHelper(RecyclableObject* object, ScriptContext* scriptContext)
  925. {
  926. return CreateKeysHelper(object, scriptContext, FALSE, true/*includeSymbolsOnly*/, true/*includeStringsOnly*/, false);
  927. }
  928. // 9.1.12 [[OwnPropertyKeys]] () in RC#4 dated April 3rd 2015.
  929. JavascriptArray* JavascriptObject::CreateKeysHelper(RecyclableObject* object, ScriptContext* scriptContext, BOOL includeNonEnumerable, bool includeSymbolProperties, bool includeStringProperties, bool includeSpecialProperties)
  930. {
  931. //1. Let keys be a new empty List.
  932. //2. For each own property key P of O that is an integer index, in ascending numeric index order
  933. // a. Add P as the last element of keys.
  934. //3. For each own property key P of O that is a String but is not an integer index, in property creation order
  935. // a. Add P as the last element of keys.
  936. //4. For each own property key P of O that is a Symbol, in property creation order
  937. // a. Add P as the last element of keys.
  938. //5. Return keys.
  939. AssertMsg(includeStringProperties || includeSymbolProperties, "Should either get string or symbol properties.");
  940. JavascriptStaticEnumerator enumerator;
  941. JavascriptArray* newArr = scriptContext->GetLibrary()->CreateArray(0);
  942. JavascriptArray* newArrForSymbols = scriptContext->GetLibrary()->CreateArray(0);
  943. EnumeratorFlags flags = EnumeratorFlags::None;
  944. if (includeNonEnumerable)
  945. {
  946. flags |= EnumeratorFlags::EnumNonEnumerable;
  947. }
  948. if (includeSymbolProperties)
  949. {
  950. flags |= EnumeratorFlags::EnumSymbols;
  951. }
  952. if (!object->GetEnumerator(&enumerator, flags, scriptContext))
  953. {
  954. return newArr; // Return an empty array if we don't have an enumerator
  955. }
  956. JavascriptString * propertyName = nullptr;
  957. PropertyId propertyId;
  958. uint32 propertyIndex = 0;
  959. uint32 symbolIndex = 0;
  960. const PropertyRecord* propertyRecord;
  961. JavascriptSymbol* symbol;
  962. while ((propertyName = enumerator.MoveAndGetNext(propertyId)) != NULL)
  963. {
  964. if (propertyName)
  965. {
  966. if (includeSymbolProperties)
  967. {
  968. propertyRecord = scriptContext->GetPropertyName(propertyId);
  969. if (propertyRecord->IsSymbol())
  970. {
  971. symbol = scriptContext->GetSymbol(propertyRecord);
  972. // no need to marshal symbol because it is created from scriptContext
  973. newArrForSymbols->DirectSetItemAt(symbolIndex++, symbol);
  974. continue;
  975. }
  976. }
  977. if (includeStringProperties)
  978. {
  979. newArr->DirectSetItemAt(propertyIndex++, CrossSite::MarshalVar(scriptContext, propertyName, propertyName->GetScriptContext()));
  980. }
  981. }
  982. }
  983. // Special properties
  984. if (includeSpecialProperties && includeStringProperties)
  985. {
  986. uint32 index = 0;
  987. while (object->GetSpecialPropertyName(index, &propertyName, scriptContext))
  988. {
  989. newArr->DirectSetItemAt(propertyIndex++, propertyName);
  990. index++;
  991. }
  992. }
  993. // Append all the symbols at the end of list
  994. uint32 totalSymbols = newArrForSymbols->GetLength();
  995. for (uint32 symIndex = 0; symIndex < totalSymbols; symIndex++)
  996. {
  997. newArr->DirectSetItemAt(propertyIndex++, newArrForSymbols->DirectGetItem(symIndex));
  998. }
  999. return newArr;
  1000. }
  1001. // args[1] this object to operate on.
  1002. // args[2] property name.
  1003. // args[3] object that attributes for the new descriptor.
  1004. Var JavascriptObject::EntryDefineProperty(RecyclableObject* function, CallInfo callInfo, ...)
  1005. {
  1006. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  1007. ARGUMENTS(args, callInfo);
  1008. ScriptContext* scriptContext = function->GetScriptContext();
  1009. Assert(!(callInfo.Flags & CallFlags_New));
  1010. if (args.Info.Count < 2 || !JavascriptOperators::IsObject(args[1]))
  1011. {
  1012. JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedObject, _u("Object.defineProperty"));
  1013. }
  1014. #if ENABLE_COPYONACCESS_ARRAY
  1015. JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(args[1]);
  1016. #endif
  1017. RecyclableObject* obj = RecyclableObject::FromVar(args[1]);
  1018. // If the object is HostDispatch try to invoke the operation remotely
  1019. if (obj->GetTypeId() == TypeIds_HostDispatch)
  1020. {
  1021. if (obj->InvokeBuiltInOperationRemotely(EntryDefineProperty, args, NULL))
  1022. {
  1023. return obj;
  1024. }
  1025. }
  1026. Var propertyKey = args.Info.Count > 2 ? args[2] : obj->GetLibrary()->GetUndefined();
  1027. PropertyRecord const * propertyRecord;
  1028. JavascriptConversion::ToPropertyKey(propertyKey, scriptContext, &propertyRecord, nullptr);
  1029. Var descVar = args.Info.Count > 3 ? args[3] : obj->GetLibrary()->GetUndefined();
  1030. PropertyDescriptor propertyDescriptor;
  1031. if (!JavascriptOperators::ToPropertyDescriptor(descVar, &propertyDescriptor, scriptContext))
  1032. {
  1033. JavascriptError::ThrowTypeError(scriptContext, JSERR_PropertyDescriptor_Invalid, scriptContext->GetPropertyName(propertyRecord->GetPropertyId())->GetBuffer());
  1034. }
  1035. if (CONFIG_FLAG(UseFullName))
  1036. {
  1037. ModifyGetterSetterFuncName(propertyRecord, propertyDescriptor, scriptContext);
  1038. }
  1039. DefineOwnPropertyHelper(obj, propertyRecord->GetPropertyId(), propertyDescriptor, scriptContext);
  1040. return obj;
  1041. }
  1042. Var JavascriptObject::EntryDefineProperties(RecyclableObject* function, CallInfo callInfo, ...)
  1043. {
  1044. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  1045. ARGUMENTS(args, callInfo);
  1046. ScriptContext* scriptContext = function->GetScriptContext();
  1047. CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(Object_Constructor_defineProperties);
  1048. Assert(!(callInfo.Flags & CallFlags_New));
  1049. if (args.Info.Count < 2 || !JavascriptOperators::IsObject(args[1]))
  1050. {
  1051. JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedObject, _u("Object.defineProperties"));
  1052. }
  1053. #if ENABLE_COPYONACCESS_ARRAY
  1054. JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(args[1]);
  1055. #endif
  1056. RecyclableObject *object = RecyclableObject::FromVar(args[1]);
  1057. // If the object is HostDispatch try to invoke the operation remotely
  1058. if (object->GetTypeId() == TypeIds_HostDispatch)
  1059. {
  1060. if (object->InvokeBuiltInOperationRemotely(EntryDefineProperties, args, NULL))
  1061. {
  1062. return object;
  1063. }
  1064. }
  1065. Var propertiesVar = args.Info.Count > 2 ? args[2] : object->GetLibrary()->GetUndefined();
  1066. RecyclableObject* properties = nullptr;
  1067. if (FALSE == JavascriptConversion::ToObject(propertiesVar, scriptContext, &properties))
  1068. {
  1069. JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NullOrUndefined, _u("Object.defineProperties"));
  1070. }
  1071. return DefinePropertiesHelper(object, properties, scriptContext);
  1072. }
  1073. // args[1] property name.
  1074. // args[2] function object to use as the getter function.
  1075. Var JavascriptObject::EntryDefineGetter(RecyclableObject* function, CallInfo callInfo, ...)
  1076. {
  1077. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  1078. ARGUMENTS(args, callInfo);
  1079. ScriptContext* scriptContext = function->GetScriptContext();
  1080. Assert(!(callInfo.Flags & CallFlags_New));
  1081. // For browser interop, simulate LdThis by calling OP implementation directly.
  1082. // Do not have module id here so use the global id, 0.
  1083. //
  1084. #if ENABLE_COPYONACCESS_ARRAY
  1085. JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(args[0]);
  1086. #endif
  1087. RecyclableObject* obj = nullptr;
  1088. if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj))
  1089. {
  1090. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Object.prototype.__defineGetter__"));
  1091. }
  1092. Var getterFunc = args.Info.Count > 2 ? args[2] : obj->GetLibrary()->GetUndefined();
  1093. if (!JavascriptConversion::IsCallable(getterFunc))
  1094. {
  1095. JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedFunction, _u("Object.prototype.__defineGetter__"));
  1096. }
  1097. Var propertyKey = args.Info.Count > 1 ? args[1] : obj->GetLibrary()->GetUndefined();
  1098. const PropertyRecord* propertyRecord;
  1099. JavascriptConversion::ToPropertyKey(propertyKey, scriptContext, &propertyRecord, nullptr);
  1100. PropertyDescriptor propertyDescriptor;
  1101. propertyDescriptor.SetEnumerable(true);
  1102. propertyDescriptor.SetConfigurable(true);
  1103. propertyDescriptor.SetGetter(getterFunc);
  1104. DefineOwnPropertyHelper(obj, propertyRecord->GetPropertyId(), propertyDescriptor, scriptContext);
  1105. return obj->GetLibrary()->GetUndefined();
  1106. }
  1107. // args[1] property name.
  1108. // args[2] function object to use as the setter function.
  1109. Var JavascriptObject::EntryDefineSetter(RecyclableObject* function, CallInfo callInfo, ...)
  1110. {
  1111. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  1112. ARGUMENTS(args, callInfo);
  1113. ScriptContext* scriptContext = function->GetScriptContext();
  1114. Assert(!(callInfo.Flags & CallFlags_New));
  1115. // For browser interop, simulate LdThis by calling OP implementation directly.
  1116. // Do not have module id here so use the global id, 0.
  1117. //
  1118. RecyclableObject* obj = nullptr;
  1119. if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj))
  1120. {
  1121. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Object.prototype.__defineSetter__"));
  1122. }
  1123. Var setterFunc = args.Info.Count > 2 ? args[2] : obj->GetLibrary()->GetUndefined();
  1124. if (!JavascriptConversion::IsCallable(setterFunc))
  1125. {
  1126. JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedFunction, _u("Object.prototype.__defineSetter__"));
  1127. }
  1128. Var propertyKey = args.Info.Count > 1 ? args[1] : obj->GetLibrary()->GetUndefined();
  1129. const PropertyRecord* propertyRecord;
  1130. JavascriptConversion::ToPropertyKey(propertyKey, scriptContext, &propertyRecord, nullptr);
  1131. PropertyDescriptor propertyDescriptor;
  1132. propertyDescriptor.SetEnumerable(true);
  1133. propertyDescriptor.SetConfigurable(true);
  1134. propertyDescriptor.SetSetter(setterFunc);
  1135. DefineOwnPropertyHelper(obj, propertyRecord->GetPropertyId(), propertyDescriptor, scriptContext);
  1136. return obj->GetLibrary()->GetUndefined();
  1137. }
  1138. // args[1] property name.
  1139. Var JavascriptObject::EntryLookupGetter(RecyclableObject* function, CallInfo callInfo, ...)
  1140. {
  1141. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  1142. ARGUMENTS(args, callInfo);
  1143. ScriptContext* scriptContext = function->GetScriptContext();
  1144. Assert(!(callInfo.Flags & CallFlags_New));
  1145. RecyclableObject* obj = nullptr;
  1146. if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj))
  1147. {
  1148. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Object.prototype.__lookupGetter__"));
  1149. }
  1150. Var propertyKey = args.Info.Count > 1 ? args[1] : obj->GetLibrary()->GetUndefined();
  1151. const PropertyRecord* propertyRecord;
  1152. JavascriptConversion::ToPropertyKey(propertyKey, scriptContext, &propertyRecord, nullptr);
  1153. Var getter = nullptr;
  1154. Var unused = nullptr;
  1155. if (JavascriptOperators::GetAccessors(obj, propertyRecord->GetPropertyId(), scriptContext, &getter, &unused))
  1156. {
  1157. if (getter != nullptr)
  1158. {
  1159. return getter;
  1160. }
  1161. }
  1162. return obj->GetLibrary()->GetUndefined();
  1163. }
  1164. // args[1] property name.
  1165. Var JavascriptObject::EntryLookupSetter(RecyclableObject* function, CallInfo callInfo, ...)
  1166. {
  1167. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  1168. ARGUMENTS(args, callInfo);
  1169. ScriptContext* scriptContext = function->GetScriptContext();
  1170. Assert(!(callInfo.Flags & CallFlags_New));
  1171. RecyclableObject* obj = nullptr;
  1172. if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj))
  1173. {
  1174. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Object.prototype.__lookupSetter__"));
  1175. }
  1176. Var propertyKey = args.Info.Count > 1 ? args[1] : obj->GetLibrary()->GetUndefined();
  1177. const PropertyRecord* propertyRecord;
  1178. JavascriptConversion::ToPropertyKey(propertyKey, scriptContext, &propertyRecord, nullptr);
  1179. Var unused = nullptr;
  1180. Var setter = nullptr;
  1181. if (JavascriptOperators::GetAccessors(obj, propertyRecord->GetPropertyId(), scriptContext, &unused, &setter))
  1182. {
  1183. if (setter != nullptr)
  1184. {
  1185. return setter;
  1186. }
  1187. }
  1188. return obj->GetLibrary()->GetUndefined();
  1189. }
  1190. Var JavascriptObject::EntryIs(RecyclableObject* function, CallInfo callInfo, ...)
  1191. {
  1192. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  1193. ARGUMENTS(args, callInfo);
  1194. ScriptContext* scriptContext = function->GetScriptContext();
  1195. Assert(!(callInfo.Flags & CallFlags_New));
  1196. Var x = args.Info.Count > 1 ? args[1] : scriptContext->GetLibrary()->GetUndefined();
  1197. Var y = args.Info.Count > 2 ? args[2] : scriptContext->GetLibrary()->GetUndefined();
  1198. return JavascriptBoolean::ToVar(JavascriptConversion::SameValue(x, y), scriptContext);
  1199. }
  1200. //ES6 19.1.2.1
  1201. Var JavascriptObject::EntryAssign(RecyclableObject* function, CallInfo callInfo, ...)
  1202. {
  1203. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  1204. ARGUMENTS(args, callInfo);
  1205. ScriptContext* scriptContext = function->GetScriptContext();
  1206. Assert(!(callInfo.Flags & CallFlags_New));
  1207. // 1. Let to be ToObject(target).
  1208. // 2. ReturnIfAbrupt(to).
  1209. // 3 If only one argument was passed, return to.
  1210. RecyclableObject* to = nullptr;
  1211. if (args.Info.Count == 1 || !JavascriptConversion::ToObject(args[1], scriptContext, &to))
  1212. {
  1213. JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedObject, _u("Object.assign"));
  1214. }
  1215. if (args.Info.Count < 3)
  1216. {
  1217. return to;
  1218. }
  1219. // 4. Let sources be the List of argument values starting with the second argument.
  1220. // 5. For each element nextSource of sources, in ascending index order,
  1221. AssignHelper<true>(args[2], to, scriptContext);
  1222. for (unsigned int i = 3; i < args.Info.Count; i++)
  1223. {
  1224. AssignHelper<false>(args[i], to, scriptContext);
  1225. }
  1226. // 6. Return to.
  1227. return to;
  1228. }
  1229. template <bool tryCopy>
  1230. void JavascriptObject::AssignHelper(Var fromArg, RecyclableObject* to, ScriptContext* scriptContext)
  1231. {
  1232. // a. If nextSource is undefined or null, let keys be an empty List.
  1233. // b. Else,
  1234. // i.Let from be ToObject(nextSource).
  1235. // ii.ReturnIfAbrupt(from).
  1236. // iii.Let keys be from.[[OwnPropertyKeys]]().
  1237. // iv.ReturnIfAbrupt(keys).
  1238. RecyclableObject* from = nullptr;
  1239. if (!JavascriptConversion::ToObject(fromArg, scriptContext, &from))
  1240. {
  1241. if (JavascriptOperators::IsUndefinedOrNull(fromArg))
  1242. {
  1243. return;
  1244. }
  1245. JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedObject, _u("Object.assign"));
  1246. }
  1247. #if ENABLE_COPYONACCESS_ARRAY
  1248. JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(from);
  1249. #endif
  1250. // if proxy, take slow path by calling [[OwnPropertyKeys]] on source
  1251. if (JavascriptProxy::Is(from))
  1252. {
  1253. AssignForProxyObjects(from, to, scriptContext);
  1254. }
  1255. // else use enumerator to extract keys from source
  1256. else
  1257. {
  1258. bool copied = false;
  1259. if (tryCopy)
  1260. {
  1261. DynamicObject* fromObj = JavascriptOperators::TryFromVar<DynamicObject>(from);
  1262. DynamicObject* toObj = JavascriptOperators::TryFromVar<DynamicObject>(to);
  1263. if (toObj && fromObj && toObj->GetType() == scriptContext->GetLibrary()->GetObjectType())
  1264. {
  1265. copied = toObj->TryCopy(fromObj);
  1266. }
  1267. }
  1268. if (!copied)
  1269. {
  1270. AssignForGenericObjects(from, to, scriptContext);
  1271. }
  1272. }
  1273. }
  1274. void JavascriptObject::AssignForGenericObjects(RecyclableObject* from, RecyclableObject* to, ScriptContext* scriptContext)
  1275. {
  1276. EnumeratorCache* cache = scriptContext->GetLibrary()->GetObjectAssignCache(from->GetType());
  1277. JavascriptStaticEnumerator enumerator;
  1278. if (!from->GetEnumerator(&enumerator, EnumeratorFlags::SnapShotSemantics | EnumeratorFlags::EnumSymbols | EnumeratorFlags::UseCache, scriptContext, cache))
  1279. {
  1280. // Nothing to enumerate, continue with the nextSource.
  1281. return;
  1282. }
  1283. PropertyId nextKey = Constants::NoProperty;
  1284. Var propValue = nullptr;
  1285. JavascriptString * propertyName = nullptr;
  1286. // Enumerate through each property of properties and fetch the property descriptor
  1287. while ((propertyName = enumerator.MoveAndGetNext(nextKey)) != NULL)
  1288. {
  1289. if (nextKey == Constants::NoProperty)
  1290. {
  1291. PropertyRecord const * propertyRecord = nullptr;
  1292. scriptContext->GetOrAddPropertyRecord(propertyName, &propertyRecord);
  1293. nextKey = propertyRecord->GetPropertyId();
  1294. }
  1295. PropertyString * propertyString = PropertyString::TryFromVar(propertyName);
  1296. // If propertyName is a PropertyString* we can try getting the property from the inline cache to avoid having a full property lookup
  1297. //
  1298. // Whenever possible, our enumerator populates the cache, so we should generally get a cache hit here
  1299. PropertyValueInfo getPropertyInfo;
  1300. if (propertyString == nullptr || !propertyString->TryGetPropertyFromCache<true /* OwnPropertyOnly */>(from, from, &propValue, scriptContext, &getPropertyInfo))
  1301. {
  1302. if (!JavascriptOperators::GetOwnProperty(from, nextKey, &propValue, scriptContext, &getPropertyInfo))
  1303. {
  1304. JavascriptError::ThrowTypeError(scriptContext, JSERR_Operand_Invalid_NeedObject, _u("Object.assign"));
  1305. }
  1306. }
  1307. // Similarly, try to set the property using our cache to avoid having to do the full work of SetProperty
  1308. PropertyValueInfo setPropertyInfo;
  1309. if (propertyString == nullptr || !propertyString->TrySetPropertyFromCache(to, propValue, scriptContext, PropertyOperation_ThrowIfNonWritable, &setPropertyInfo))
  1310. {
  1311. if (!JavascriptOperators::SetProperty(to, to, nextKey, propValue, &setPropertyInfo, scriptContext, PropertyOperation_ThrowIfNonWritable))
  1312. {
  1313. JavascriptError::ThrowTypeError(scriptContext, JSERR_Operand_Invalid_NeedObject, _u("Object.assign"));
  1314. }
  1315. }
  1316. }
  1317. }
  1318. void JavascriptObject::AssignForProxyObjects(RecyclableObject* from, RecyclableObject* to, ScriptContext* scriptContext)
  1319. {
  1320. JavascriptArray *keys = JavascriptOperators::GetOwnEnumerablePropertyNamesSymbols(from, scriptContext);
  1321. // c. Repeat for each element nextKey of keys in List order,
  1322. // i. Let desc be from.[[GetOwnProperty]](nextKey).
  1323. // ii. ReturnIfAbrupt(desc).
  1324. // iii. if desc is not undefined and desc.[[Enumerable]] is true, then
  1325. // 1. Let propValue be Get(from, nextKey).
  1326. // 2. ReturnIfAbrupt(propValue).
  1327. // 3. Let status be Set(to, nextKey, propValue, true);
  1328. // 4. ReturnIfAbrupt(status).
  1329. uint32 length = keys->GetLength();
  1330. Var nextKey;
  1331. const PropertyRecord* propertyRecord = nullptr;
  1332. PropertyId propertyId;
  1333. Var propValue = nullptr;
  1334. for (uint32 j = 0; j < length; j++)
  1335. {
  1336. PropertyDescriptor propertyDescriptor;
  1337. nextKey = keys->DirectGetItem(j);
  1338. AssertMsg(JavascriptSymbol::Is(nextKey) || JavascriptString::Is(nextKey), "Invariant check during ownKeys proxy trap should make sure we only get property key here. (symbol or string primitives)");
  1339. // Spec doesn't strictly call for us to use ToPropertyKey but since we know nextKey is already a symbol or string primitive, ToPropertyKey will be a nop and return us the propertyRecord
  1340. JavascriptConversion::ToPropertyKey(nextKey, scriptContext, &propertyRecord, nullptr);
  1341. propertyId = propertyRecord->GetPropertyId();
  1342. AssertMsg(propertyId != Constants::NoProperty, "AssignForProxyObjects - OwnPropertyKeys returned a propertyId with value NoProperty.");
  1343. if (JavascriptOperators::GetOwnPropertyDescriptor(from, propertyRecord->GetPropertyId(), scriptContext, &propertyDescriptor))
  1344. {
  1345. if (propertyDescriptor.IsEnumerable())
  1346. {
  1347. if (!JavascriptOperators::GetOwnProperty(from, propertyId, &propValue, scriptContext, nullptr))
  1348. {
  1349. JavascriptError::ThrowTypeError(scriptContext, JSERR_Operand_Invalid_NeedObject, _u("Object.assign"));
  1350. }
  1351. if (!JavascriptOperators::SetProperty(to, to, propertyId, propValue, scriptContext))
  1352. {
  1353. JavascriptError::ThrowTypeError(scriptContext, JSERR_Operand_Invalid_NeedObject, _u("Object.assign"));
  1354. }
  1355. }
  1356. }
  1357. }
  1358. }
  1359. //ES5 15.2.3.5
  1360. Var JavascriptObject::EntryCreate(RecyclableObject* function, CallInfo callInfo, ...)
  1361. {
  1362. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  1363. ARGUMENTS(args, callInfo);
  1364. ScriptContext* scriptContext = function->GetScriptContext();
  1365. CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(Object_Constructor_create)
  1366. Assert(!(callInfo.Flags & CallFlags_New));
  1367. if (args.Info.Count < 2)
  1368. {
  1369. JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NotObjectOrNull, _u("Object.create"));
  1370. }
  1371. Var protoVar = args[1];
  1372. if (!JavascriptOperators::IsObjectOrNull(protoVar))
  1373. {
  1374. JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NotObjectOrNull, _u("Object.create"));
  1375. }
  1376. RecyclableObject* protoObj = RecyclableObject::FromVar(protoVar);
  1377. DynamicObject* object = function->GetLibrary()->CreateObject(protoObj);
  1378. JS_ETW(EventWriteJSCRIPT_RECYCLER_ALLOCATE_OBJECT(object));
  1379. #if ENABLE_DEBUG_CONFIG_OPTIONS
  1380. if (Js::Configuration::Global.flags.IsEnabled(Js::autoProxyFlag))
  1381. {
  1382. object = DynamicObject::FromVar(JavascriptProxy::AutoProxyWrapper(object));
  1383. }
  1384. #endif
  1385. if (args.Info.Count > 2 && JavascriptOperators::GetTypeId(args[2]) != TypeIds_Undefined)
  1386. {
  1387. RecyclableObject* properties = nullptr;
  1388. if (FALSE == JavascriptConversion::ToObject(args[2], scriptContext, &properties))
  1389. {
  1390. JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NullOrUndefined, _u("Object.create"));
  1391. }
  1392. return DefinePropertiesHelper(object, properties, scriptContext);
  1393. }
  1394. return object;
  1395. }
  1396. Var JavascriptObject::DefinePropertiesHelper(RecyclableObject *object, RecyclableObject* props, ScriptContext *scriptContext)
  1397. {
  1398. if (JavascriptProxy::Is(props))
  1399. {
  1400. return DefinePropertiesHelperForProxyObjects(object, props, scriptContext);
  1401. }
  1402. else
  1403. {
  1404. return DefinePropertiesHelperForGenericObjects(object, props, scriptContext);
  1405. }
  1406. }
  1407. Var JavascriptObject::DefinePropertiesHelperForGenericObjects(RecyclableObject *object, RecyclableObject* props, ScriptContext *scriptContext)
  1408. {
  1409. size_t descSize = 16;
  1410. size_t descCount = 0;
  1411. struct DescriptorMap
  1412. {
  1413. Field(PropertyRecord const *) propRecord;
  1414. Field(PropertyDescriptor) descriptor;
  1415. Field(Var) originalVar;
  1416. };
  1417. JavascriptStaticEnumerator enumerator;
  1418. if (!props->GetEnumerator(&enumerator, EnumeratorFlags::EnumSymbols, scriptContext))
  1419. {
  1420. return object;
  1421. }
  1422. ENTER_PINNED_SCOPE(DescriptorMap, descriptors);
  1423. descriptors = RecyclerNewArray(scriptContext->GetRecycler(), DescriptorMap, descSize);
  1424. PropertyId propId;
  1425. PropertyRecord const * propertyRecord;
  1426. JavascriptString* propertyName = nullptr;
  1427. //enumerate through each property of properties and fetch the property descriptor
  1428. while ((propertyName = enumerator.MoveAndGetNext(propId)) != NULL)
  1429. {
  1430. if (propId == Constants::NoProperty) //try current property id query first
  1431. {
  1432. scriptContext->GetOrAddPropertyRecord(propertyName, &propertyRecord);
  1433. propId = propertyRecord->GetPropertyId();
  1434. }
  1435. else
  1436. {
  1437. propertyName->GetPropertyRecord(&propertyRecord);
  1438. }
  1439. if (descCount == descSize)
  1440. {
  1441. //reallocate - consider linked list of DescriptorMap if the descSize is too high
  1442. descSize = AllocSizeMath::Mul(descCount, 2);
  1443. __analysis_assume(descSize == descCount * 2);
  1444. DescriptorMap *temp = RecyclerNewArray(scriptContext->GetRecycler(), DescriptorMap, descSize);
  1445. for (size_t i = 0; i < descCount; i++)
  1446. {
  1447. temp[i] = descriptors[i];
  1448. }
  1449. descriptors = temp;
  1450. }
  1451. Var tempVar = JavascriptOperators::GetPropertyNoCache(props, propId, scriptContext);
  1452. AnalysisAssert(descCount < descSize);
  1453. if (!JavascriptOperators::ToPropertyDescriptor(tempVar, &descriptors[descCount].descriptor, scriptContext))
  1454. {
  1455. JavascriptError::ThrowTypeError(scriptContext, JSERR_PropertyDescriptor_Invalid, scriptContext->GetPropertyName(propId)->GetBuffer());
  1456. }
  1457. // In proxy, we need to get back the original ToPropertDescriptor var in [[defineProperty]] trap.
  1458. descriptors[descCount].originalVar = tempVar;
  1459. if (CONFIG_FLAG(UseFullName))
  1460. {
  1461. ModifyGetterSetterFuncName(propertyRecord, descriptors[descCount].descriptor, scriptContext);
  1462. }
  1463. descriptors[descCount].propRecord = propertyRecord;
  1464. descCount++;
  1465. }
  1466. //Once all the property descriptors are in place set each property descriptor to the object
  1467. for (size_t i = 0; i < descCount; i++)
  1468. {
  1469. DefineOwnPropertyHelper(object, descriptors[i].propRecord->GetPropertyId(), descriptors[i].descriptor, scriptContext);
  1470. }
  1471. LEAVE_PINNED_SCOPE();
  1472. return object;
  1473. }
  1474. //ES5 15.2.3.7
  1475. Var JavascriptObject::DefinePropertiesHelperForProxyObjects(RecyclableObject *object, RecyclableObject* props, ScriptContext *scriptContext)
  1476. {
  1477. Assert(JavascriptProxy::Is(props));
  1478. //1. If Type(O) is not Object throw a TypeError exception.
  1479. //2. Let props be ToObject(Properties).
  1480. size_t descCount = 0;
  1481. struct DescriptorMap
  1482. {
  1483. Field(PropertyRecord const *) propRecord;
  1484. Field(PropertyDescriptor) descriptor;
  1485. };
  1486. //3. Let keys be props.[[OwnPropertyKeys]]().
  1487. //4. ReturnIfAbrupt(keys).
  1488. //5. Let descriptors be an empty List.
  1489. JavascriptArray* keys = JavascriptOperators::GetOwnEnumerablePropertyNamesSymbols(props, scriptContext);
  1490. uint32 length = keys->GetLength();
  1491. ENTER_PINNED_SCOPE(DescriptorMap, descriptors);
  1492. descriptors = RecyclerNewArray(scriptContext->GetRecycler(), DescriptorMap, length);
  1493. //6. Repeat for each element nextKey of keys in List order,
  1494. // 1. Let propDesc be props.[[GetOwnProperty]](nextKey).
  1495. // 2. ReturnIfAbrupt(propDesc).
  1496. // 3. If propDesc is not undefined and propDesc.[[Enumerable]] is true, then
  1497. // 1. Let descObj be Get(props, nextKey).
  1498. // 2. ReturnIfAbrupt(descObj).
  1499. // 3. Let desc be ToPropertyDescriptor(descObj).
  1500. // 4. ReturnIfAbrupt(desc).
  1501. // 5. Append the pair(a two element List) consisting of nextKey and desc to the end of descriptors.
  1502. Var nextKey;
  1503. const PropertyRecord* propertyRecord = nullptr;
  1504. PropertyId propertyId;
  1505. Var descObj;
  1506. for (uint32 j = 0; j < length; j++)
  1507. {
  1508. PropertyDescriptor propertyDescriptor;
  1509. nextKey = keys->DirectGetItem(j);
  1510. AssertMsg(JavascriptSymbol::Is(nextKey) || JavascriptString::Is(nextKey), "Invariant check during ownKeys proxy trap should make sure we only get property key here. (symbol or string primitives)");
  1511. JavascriptConversion::ToPropertyKey(nextKey, scriptContext, &propertyRecord, nullptr);
  1512. propertyId = propertyRecord->GetPropertyId();
  1513. AssertMsg(propertyId != Constants::NoProperty, "DefinePropertiesHelper - OwnPropertyKeys returned a propertyId with value NoProperty.");
  1514. if (JavascriptOperators::GetOwnPropertyDescriptor(props, propertyRecord->GetPropertyId(), scriptContext, &propertyDescriptor))
  1515. {
  1516. if (propertyDescriptor.IsEnumerable())
  1517. {
  1518. descObj = JavascriptOperators::GetProperty(props, propertyId, scriptContext);
  1519. if (!JavascriptOperators::ToPropertyDescriptor(descObj, &descriptors[descCount].descriptor, scriptContext))
  1520. {
  1521. JavascriptError::ThrowTypeError(scriptContext, JSERR_PropertyDescriptor_Invalid, scriptContext->GetPropertyName(propertyId)->GetBuffer());
  1522. }
  1523. if (CONFIG_FLAG(UseFullName))
  1524. {
  1525. ModifyGetterSetterFuncName(propertyRecord, descriptors[descCount].descriptor, scriptContext);
  1526. }
  1527. descriptors[descCount].propRecord = propertyRecord;
  1528. descCount++;
  1529. }
  1530. }
  1531. }
  1532. //7. For each pair from descriptors in list order,
  1533. // 1. Let P be the first element of pair.
  1534. // 2. Let desc be the second element of pair.
  1535. // 3. Let status be DefinePropertyOrThrow(O, P, desc).
  1536. // 4. ReturnIfAbrupt(status).
  1537. for (size_t i = 0; i < descCount; i++)
  1538. {
  1539. DefineOwnPropertyHelper(object, descriptors[i].propRecord->GetPropertyId(), descriptors[i].descriptor, scriptContext);
  1540. }
  1541. LEAVE_PINNED_SCOPE();
  1542. //8. Return O.
  1543. return object;
  1544. }
  1545. Var JavascriptObject::GetPrototypeOf(RecyclableObject* obj, ScriptContext* scriptContext)
  1546. {
  1547. return obj->IsExternal() ? obj->GetConfigurablePrototype(scriptContext) : obj->GetPrototype();
  1548. }
  1549. //
  1550. // Check if "proto" is a prototype of "object" (on its prototype chain).
  1551. //
  1552. bool JavascriptObject::IsPrototypeOf(RecyclableObject* proto, RecyclableObject* object, ScriptContext* scriptContext)
  1553. {
  1554. return JavascriptOperators::MapObjectAndPrototypesUntil<false>(object, [=](RecyclableObject* obj)
  1555. {
  1556. return obj == proto;
  1557. });
  1558. }
  1559. static const size_t ConstructNameGetSetLength = 5; // 5 = 1 ( for .) + 3 (get or set) + 1 for null)
  1560. /*static*/
  1561. char16 * JavascriptObject::ConstructName(const PropertyRecord * propertyRecord, const char16 * getOrSetStr, ScriptContext* scriptContext)
  1562. {
  1563. Assert(propertyRecord);
  1564. Assert(scriptContext);
  1565. char16 * finalName = nullptr;
  1566. size_t propertyLength = (size_t)propertyRecord->GetLength();
  1567. if (propertyLength > 0)
  1568. {
  1569. size_t totalChars;
  1570. if (SizeTAdd(propertyLength, ConstructNameGetSetLength, &totalChars) == S_OK)
  1571. {
  1572. finalName = RecyclerNewArrayLeafZ(scriptContext->GetRecycler(), char16, totalChars);
  1573. Assert(finalName != nullptr);
  1574. const char16* propertyName = propertyRecord->GetBuffer();
  1575. Assert(propertyName != nullptr);
  1576. wcscpy_s(finalName, totalChars, propertyName);
  1577. Assert(getOrSetStr != nullptr);
  1578. Assert(wcslen(getOrSetStr) == 4);
  1579. wcscpy_s(finalName + propertyLength, ConstructNameGetSetLength, getOrSetStr);
  1580. }
  1581. }
  1582. return finalName;
  1583. }
  1584. /*static*/
  1585. void JavascriptObject::ModifyGetterSetterFuncName(const PropertyRecord * propertyRecord, const PropertyDescriptor& descriptor, ScriptContext* scriptContext)
  1586. {
  1587. Assert(scriptContext);
  1588. Assert(propertyRecord);
  1589. if (descriptor.GetterSpecified() || descriptor.SetterSpecified())
  1590. {
  1591. charcount_t propertyLength = propertyRecord->GetLength();
  1592. if (descriptor.GetterSpecified()
  1593. && Js::ScriptFunction::Is(descriptor.GetGetter())
  1594. && _wcsicmp(Js::ScriptFunction::FromVar(descriptor.GetGetter())->GetFunctionProxy()->GetDisplayName(), _u("get")) == 0)
  1595. {
  1596. // modify to name.get
  1597. const char16* finalName = ConstructName(propertyRecord, _u(".get"), scriptContext);
  1598. if (finalName != nullptr)
  1599. {
  1600. FunctionProxy::SetDisplayNameFlags flags = (FunctionProxy::SetDisplayNameFlags) (FunctionProxy::SetDisplayNameFlagsDontCopy | FunctionProxy::SetDisplayNameFlagsRecyclerAllocated);
  1601. Js::ScriptFunction::FromVar(descriptor.GetGetter())->GetFunctionProxy()->SetDisplayName(finalName,
  1602. propertyLength + 4 /*".get"*/, propertyLength + 1, flags);
  1603. }
  1604. }
  1605. if (descriptor.SetterSpecified()
  1606. && Js::ScriptFunction::Is(descriptor.GetSetter())
  1607. && _wcsicmp(Js::ScriptFunction::FromVar(descriptor.GetSetter())->GetFunctionProxy()->GetDisplayName(), _u("set")) == 0)
  1608. {
  1609. // modify to name.set
  1610. const char16* finalName = ConstructName(propertyRecord, _u(".set"), scriptContext);
  1611. if (finalName != nullptr)
  1612. {
  1613. FunctionProxy::SetDisplayNameFlags flags = (FunctionProxy::SetDisplayNameFlags) (FunctionProxy::SetDisplayNameFlagsDontCopy | FunctionProxy::SetDisplayNameFlagsRecyclerAllocated);
  1614. Js::ScriptFunction::FromVar(descriptor.GetSetter())->GetFunctionProxy()->SetDisplayName(finalName,
  1615. propertyLength + 4 /*".set"*/, propertyLength + 1, flags);
  1616. }
  1617. }
  1618. }
  1619. }
  1620. BOOL JavascriptObject::DefineOwnPropertyHelper(RecyclableObject* obj, PropertyId propId, const PropertyDescriptor& descriptor, ScriptContext* scriptContext, bool throwOnError /* = true*/)
  1621. {
  1622. BOOL returnValue;
  1623. obj->ThrowIfCannotDefineProperty(propId, descriptor);
  1624. const Type* oldType = obj->GetType();
  1625. obj->ClearWritableDataOnlyDetectionBit();
  1626. // HostDispatch: it doesn't support changing property attributes and default attributes are not per ES5,
  1627. // so there is no benefit in using ES5 DefineOwnPropertyDescriptor for it, use old implementation.
  1628. if (TypeIds_HostDispatch != obj->GetTypeId())
  1629. {
  1630. if (DynamicObject::IsAnyArray(obj))
  1631. {
  1632. returnValue = JavascriptOperators::DefineOwnPropertyForArray(
  1633. JavascriptArray::FromAnyArray(obj), propId, descriptor, throwOnError, scriptContext);
  1634. }
  1635. else
  1636. {
  1637. returnValue = JavascriptOperators::DefineOwnPropertyDescriptor(obj, propId, descriptor, throwOnError, scriptContext);
  1638. if (propId == PropertyIds::__proto__)
  1639. {
  1640. scriptContext->GetLibrary()->GetObjectPrototypeObject()->PostDefineOwnProperty__proto__(obj);
  1641. }
  1642. }
  1643. }
  1644. else
  1645. {
  1646. returnValue = JavascriptOperators::SetPropertyDescriptor(obj, propId, descriptor);
  1647. }
  1648. if (propId == PropertyIds::_symbolSpecies && obj == scriptContext->GetLibrary()->GetArrayConstructor())
  1649. {
  1650. scriptContext->GetLibrary()->SetArrayObjectHasUserDefinedSpecies(true);
  1651. }
  1652. if (obj->IsWritableDataOnlyDetectionBitSet())
  1653. {
  1654. if (obj->GetType() == oldType)
  1655. {
  1656. // Also, if the object's type has not changed, we need to ensure that
  1657. // the cached property string for this property, if any, does not
  1658. // specify this object's type.
  1659. scriptContext->InvalidatePropertyStringAndSymbolCaches(propId, obj->GetType());
  1660. }
  1661. }
  1662. if (descriptor.IsAccessorDescriptor())
  1663. {
  1664. scriptContext->optimizationOverrides.SetSideEffects(Js::SideEffects_Accessor);
  1665. }
  1666. return returnValue;
  1667. }
  1668. }