BoundFunction.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  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. FunctionInfo BoundFunction::functionInfo(FORCE_NO_WRITE_BARRIER_TAG(BoundFunction::NewInstance), FunctionInfo::DoNotProfile);
  9. BoundFunction::BoundFunction(DynamicType * type)
  10. : JavascriptFunction(type, &functionInfo),
  11. targetFunction(nullptr),
  12. boundThis(nullptr),
  13. count(0),
  14. boundArgs(nullptr)
  15. {
  16. // Constructor used during copy on write.
  17. DebugOnly(VerifyEntryPoint());
  18. }
  19. BoundFunction::BoundFunction(Arguments args, DynamicType * type)
  20. : JavascriptFunction(type, &functionInfo),
  21. count(0),
  22. boundArgs(nullptr)
  23. {
  24. DebugOnly(VerifyEntryPoint());
  25. AssertMsg(args.Info.Count > 0, "wrong number of args in BoundFunction");
  26. ScriptContext *scriptContext = this->GetScriptContext();
  27. targetFunction = RecyclableObject::FromVar(args[0]);
  28. Assert(!CrossSite::NeedMarshalVar(targetFunction, scriptContext));
  29. // Let proto be targetFunction.[[GetPrototypeOf]]().
  30. RecyclableObject* proto = JavascriptOperators::GetPrototype(targetFunction);
  31. if (proto != type->GetPrototype())
  32. {
  33. if (type->GetIsShared())
  34. {
  35. this->ChangeType();
  36. type = this->GetDynamicType();
  37. }
  38. type->SetPrototype(proto);
  39. }
  40. // If targetFunction is proxy, need to make sure that traps are called in right order as per 19.2.3.2 in RC#4 dated April 3rd 2015.
  41. // Here although we won't use value of length, this is just to make sure that we call traps involved with HasOwnProperty(Target, "length") and Get(Target, "length")
  42. if (JavascriptProxy::Is(targetFunction))
  43. {
  44. if (JavascriptOperators::HasOwnProperty(targetFunction, PropertyIds::length, scriptContext, nullptr) == TRUE)
  45. {
  46. int len = 0;
  47. Var varLength;
  48. if (targetFunction->GetProperty(targetFunction, PropertyIds::length, &varLength, nullptr, scriptContext))
  49. {
  50. len = JavascriptConversion::ToInt32(varLength, scriptContext);
  51. }
  52. }
  53. GetTypeHandler()->EnsureObjectReady(this);
  54. }
  55. if (args.Info.Count > 1)
  56. {
  57. boundThis = args[1];
  58. // function object and "this" arg
  59. const uint countAccountedFor = 2;
  60. count = args.Info.Count - countAccountedFor;
  61. // Store the args excluding function obj and "this" arg
  62. if (args.Info.Count > 2)
  63. {
  64. boundArgs = RecyclerNewArray(scriptContext->GetRecycler(), Field(Var), count);
  65. for (uint i=0; i<count; i++)
  66. {
  67. boundArgs[i] = args[i+countAccountedFor];
  68. }
  69. }
  70. }
  71. else
  72. {
  73. // If no "this" is passed, "undefined" is used
  74. boundThis = scriptContext->GetLibrary()->GetUndefined();
  75. }
  76. }
  77. BoundFunction::BoundFunction(RecyclableObject* targetFunction, Var boundThis, Var* args, uint argsCount, DynamicType * type)
  78. : JavascriptFunction(type, &functionInfo),
  79. count(argsCount),
  80. boundArgs(nullptr)
  81. {
  82. DebugOnly(VerifyEntryPoint());
  83. this->targetFunction = targetFunction;
  84. this->boundThis = boundThis;
  85. if (argsCount != 0)
  86. {
  87. this->boundArgs = RecyclerNewArray(this->GetScriptContext()->GetRecycler(), Field(Var), argsCount);
  88. for (uint i = 0; i < argsCount; i++)
  89. {
  90. this->boundArgs[i] = args[i];
  91. }
  92. }
  93. }
  94. BoundFunction* BoundFunction::New(ScriptContext* scriptContext, ArgumentReader args)
  95. {
  96. Recycler* recycler = scriptContext->GetRecycler();
  97. BoundFunction* boundFunc = RecyclerNew(recycler, BoundFunction, args,
  98. scriptContext->GetLibrary()->GetBoundFunctionType());
  99. return boundFunc;
  100. }
  101. Var BoundFunction::NewInstance(RecyclableObject* function, CallInfo callInfo, ...)
  102. {
  103. RUNTIME_ARGUMENTS(args, callInfo);
  104. ScriptContext* scriptContext = function->GetScriptContext();
  105. if (args.Info.Count == 0)
  106. {
  107. JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedFunction /* TODO-ERROR: get arg name - args[0] */);
  108. }
  109. BoundFunction *boundFunction = (BoundFunction *) function;
  110. Var targetFunction = boundFunction->targetFunction;
  111. //
  112. // var o = new boundFunction()
  113. // a new object should be created using the actual function object
  114. //
  115. Var newVarInstance = nullptr;
  116. if (callInfo.Flags & CallFlags_New)
  117. {
  118. if (JavascriptProxy::Is(targetFunction))
  119. {
  120. JavascriptProxy* proxy = JavascriptProxy::FromVar(targetFunction);
  121. Arguments proxyArgs(CallInfo(CallFlags_New, 1), &targetFunction);
  122. args.Values[0] = newVarInstance = proxy->ConstructorTrap(proxyArgs, scriptContext, 0);
  123. }
  124. else
  125. {
  126. args.Values[0] = newVarInstance = JavascriptOperators::NewScObjectNoCtor(targetFunction, scriptContext);
  127. }
  128. }
  129. Js::Arguments actualArgs = args;
  130. if (boundFunction->count > 0)
  131. {
  132. // OACR thinks that this can change between here and the check in the for loop below
  133. const unsigned int argCount = args.Info.Count;
  134. if ((boundFunction->count + argCount) > CallInfo::kMaxCountArgs)
  135. {
  136. JavascriptError::ThrowRangeError(scriptContext, JSERR_ArgListTooLarge);
  137. }
  138. Field(Var) *newValues = RecyclerNewArray(scriptContext->GetRecycler(), Field(Var), boundFunction->count + argCount);
  139. uint index = 0;
  140. //
  141. // For [[Construct]] use the newly created var instance
  142. // For [[Call]] use the "this" to which bind bound it.
  143. //
  144. if (callInfo.Flags & CallFlags_New)
  145. {
  146. newValues[index++] = args[0];
  147. }
  148. else
  149. {
  150. newValues[index++] = boundFunction->boundThis;
  151. }
  152. for (uint i = 0; i < boundFunction->count; i++)
  153. {
  154. newValues[index++] = boundFunction->boundArgs[i];
  155. }
  156. // Copy the extra args
  157. for (uint i=1; i<argCount; i++)
  158. {
  159. newValues[index++] = args[i];
  160. }
  161. actualArgs = Arguments(args.Info, unsafe_write_barrier_cast<Var*>(newValues));
  162. actualArgs.Info.Count = boundFunction->count + argCount;
  163. }
  164. else
  165. {
  166. if (!(callInfo.Flags & CallFlags_New))
  167. {
  168. actualArgs.Values[0] = boundFunction->boundThis;
  169. }
  170. }
  171. RecyclableObject* actualFunction = RecyclableObject::FromVar(targetFunction);
  172. // Number of arguments are allowed to be more than Constants::MaxAllowedArgs in runtime. Need to use the larger argcount logic for this call.
  173. Var aReturnValue = JavascriptFunction::CallFunction<true>(actualFunction, actualFunction->GetEntryPoint(), actualArgs, /* useLargeArgCount */ true);
  174. //
  175. // [[Construct]] and call returned a non-object
  176. // return the newly created var instance
  177. //
  178. if ((callInfo.Flags & CallFlags_New) && !JavascriptOperators::IsObject(aReturnValue))
  179. {
  180. aReturnValue = newVarInstance;
  181. }
  182. return aReturnValue;
  183. }
  184. JavascriptFunction * BoundFunction::GetTargetFunction() const
  185. {
  186. if (targetFunction != nullptr)
  187. {
  188. RecyclableObject* _targetFunction = targetFunction;
  189. while (JavascriptProxy::Is(_targetFunction))
  190. {
  191. _targetFunction = JavascriptProxy::FromVar(_targetFunction)->GetTarget();
  192. }
  193. if (JavascriptFunction::Is(_targetFunction))
  194. {
  195. return JavascriptFunction::FromVar(_targetFunction);
  196. }
  197. // targetFunction should always be a JavascriptFunction.
  198. Assert(FALSE);
  199. }
  200. return nullptr;
  201. }
  202. JavascriptString* BoundFunction::GetDisplayNameImpl() const
  203. {
  204. JavascriptString* displayName = GetLibrary()->GetEmptyString();
  205. if (targetFunction != nullptr)
  206. {
  207. Var value = JavascriptOperators::GetPropertyNoCache(targetFunction, PropertyIds::name, targetFunction->GetScriptContext());
  208. if (JavascriptString::Is(value))
  209. {
  210. displayName = JavascriptString::FromVar(value);
  211. }
  212. }
  213. return LiteralString::Concat(LiteralString::NewCopySz(_u("bound "), this->GetScriptContext()), displayName);
  214. }
  215. RecyclableObject* BoundFunction::GetBoundThis()
  216. {
  217. if (boundThis != nullptr && RecyclableObject::Is(boundThis))
  218. {
  219. return RecyclableObject::FromVar(boundThis);
  220. }
  221. return NULL;
  222. }
  223. inline BOOL BoundFunction::IsConstructor() const
  224. {
  225. if (this->targetFunction != nullptr)
  226. {
  227. return JavascriptOperators::IsConstructor(this->GetTargetFunction());
  228. }
  229. return false;
  230. }
  231. PropertyQueryFlags BoundFunction::HasPropertyQuery(PropertyId propertyId)
  232. {
  233. if (propertyId == PropertyIds::length)
  234. {
  235. return PropertyQueryFlags::Property_Found;
  236. }
  237. return JavascriptFunction::HasPropertyQuery(propertyId);
  238. }
  239. PropertyQueryFlags BoundFunction::GetPropertyQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
  240. {
  241. BOOL result;
  242. if (GetPropertyBuiltIns(originalInstance, propertyId, value, info, requestContext, &result))
  243. {
  244. return JavascriptConversion::BooleanToPropertyQueryFlags(result);
  245. }
  246. return JavascriptFunction::GetPropertyQuery(originalInstance, propertyId, value, info, requestContext);
  247. }
  248. PropertyQueryFlags BoundFunction::GetPropertyQuery(Var originalInstance, JavascriptString* propertyNameString, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
  249. {
  250. BOOL result;
  251. PropertyRecord const* propertyRecord;
  252. this->GetScriptContext()->FindPropertyRecord(propertyNameString, &propertyRecord);
  253. if (propertyRecord != nullptr && GetPropertyBuiltIns(originalInstance, propertyRecord->GetPropertyId(), value, info, requestContext, &result))
  254. {
  255. return JavascriptConversion::BooleanToPropertyQueryFlags(result);
  256. }
  257. return JavascriptFunction::GetPropertyQuery(originalInstance, propertyNameString, value, info, requestContext);
  258. }
  259. bool BoundFunction::GetPropertyBuiltIns(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext, BOOL* result)
  260. {
  261. if (propertyId == PropertyIds::length)
  262. {
  263. // Get the "length" property of the underlying target function
  264. int len = 0;
  265. Var varLength;
  266. if (targetFunction->GetProperty(targetFunction, PropertyIds::length, &varLength, nullptr, requestContext))
  267. {
  268. len = JavascriptConversion::ToInt32(varLength, requestContext);
  269. }
  270. // Reduce by number of bound args
  271. len = len - this->count;
  272. len = max(len, 0);
  273. *value = JavascriptNumber::ToVar(len, requestContext);
  274. *result = true;
  275. return true;
  276. }
  277. return false;
  278. }
  279. PropertyQueryFlags BoundFunction::GetPropertyReferenceQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
  280. {
  281. return BoundFunction::GetPropertyQuery(originalInstance, propertyId, value, info, requestContext);
  282. }
  283. BOOL BoundFunction::SetProperty(PropertyId propertyId, Var value, PropertyOperationFlags flags, PropertyValueInfo* info)
  284. {
  285. BOOL result;
  286. if (SetPropertyBuiltIns(propertyId, value, flags, info, &result))
  287. {
  288. return result;
  289. }
  290. return JavascriptFunction::SetProperty(propertyId, value, flags, info);
  291. }
  292. BOOL BoundFunction::SetProperty(JavascriptString* propertyNameString, Var value, PropertyOperationFlags flags, PropertyValueInfo* info)
  293. {
  294. BOOL result;
  295. PropertyRecord const* propertyRecord;
  296. this->GetScriptContext()->FindPropertyRecord(propertyNameString, &propertyRecord);
  297. if (propertyRecord != nullptr && SetPropertyBuiltIns(propertyRecord->GetPropertyId(), value, flags, info, &result))
  298. {
  299. return result;
  300. }
  301. return JavascriptFunction::SetProperty(propertyNameString, value, flags, info);
  302. }
  303. bool BoundFunction::SetPropertyBuiltIns(PropertyId propertyId, Var value, PropertyOperationFlags flags, PropertyValueInfo* info, BOOL* result)
  304. {
  305. if (propertyId == PropertyIds::length)
  306. {
  307. JavascriptError::ThrowCantAssignIfStrictMode(flags, this->GetScriptContext());
  308. *result = false;
  309. return true;
  310. }
  311. return false;
  312. }
  313. BOOL BoundFunction::GetAccessors(PropertyId propertyId, Var *getter, Var *setter, ScriptContext * requestContext)
  314. {
  315. return DynamicObject::GetAccessors(propertyId, getter, setter, requestContext);
  316. }
  317. DescriptorFlags BoundFunction::GetSetter(PropertyId propertyId, Var *setterValue, PropertyValueInfo* info, ScriptContext* requestContext)
  318. {
  319. return DynamicObject::GetSetter(propertyId, setterValue, info, requestContext);
  320. }
  321. DescriptorFlags BoundFunction::GetSetter(JavascriptString* propertyNameString, Var *setterValue, PropertyValueInfo* info, ScriptContext* requestContext)
  322. {
  323. return DynamicObject::GetSetter(propertyNameString, setterValue, info, requestContext);
  324. }
  325. BOOL BoundFunction::InitProperty(PropertyId propertyId, Var value, PropertyOperationFlags flags, PropertyValueInfo* info)
  326. {
  327. return SetProperty(propertyId, value, PropertyOperation_None, info);
  328. }
  329. BOOL BoundFunction::DeleteProperty(PropertyId propertyId, PropertyOperationFlags flags)
  330. {
  331. if (propertyId == PropertyIds::length)
  332. {
  333. return false;
  334. }
  335. return JavascriptFunction::DeleteProperty(propertyId, flags);
  336. }
  337. BOOL BoundFunction::DeleteProperty(JavascriptString *propertyNameString, PropertyOperationFlags flags)
  338. {
  339. JsUtil::CharacterBuffer<WCHAR> propertyName(propertyNameString->GetString(), propertyNameString->GetLength());
  340. if (BuiltInPropertyRecords::length.Equals(propertyName))
  341. {
  342. return false;
  343. }
  344. return JavascriptFunction::DeleteProperty(propertyNameString, flags);
  345. }
  346. BOOL BoundFunction::IsWritable(PropertyId propertyId)
  347. {
  348. if (propertyId == PropertyIds::length)
  349. {
  350. return false;
  351. }
  352. return JavascriptFunction::IsWritable(propertyId);
  353. }
  354. BOOL BoundFunction::IsConfigurable(PropertyId propertyId)
  355. {
  356. if (propertyId == PropertyIds::length)
  357. {
  358. return false;
  359. }
  360. return JavascriptFunction::IsConfigurable(propertyId);
  361. }
  362. BOOL BoundFunction::IsEnumerable(PropertyId propertyId)
  363. {
  364. if (propertyId == PropertyIds::length)
  365. {
  366. return false;
  367. }
  368. return JavascriptFunction::IsEnumerable(propertyId);
  369. }
  370. BOOL BoundFunction::HasInstance(Var instance, ScriptContext* scriptContext, IsInstInlineCache* inlineCache)
  371. {
  372. return this->targetFunction->HasInstance(instance, scriptContext, inlineCache);
  373. }
  374. #if ENABLE_TTD
  375. void BoundFunction::MarkVisitKindSpecificPtrs(TTD::SnapshotExtractor* extractor)
  376. {
  377. extractor->MarkVisitVar(this->targetFunction);
  378. if(this->boundThis != nullptr)
  379. {
  380. extractor->MarkVisitVar(this->boundThis);
  381. }
  382. for(uint32 i = 0; i < this->count; ++i)
  383. {
  384. extractor->MarkVisitVar(this->boundArgs[i]);
  385. }
  386. }
  387. void BoundFunction::ProcessCorePaths()
  388. {
  389. this->GetScriptContext()->TTDWellKnownInfo->EnqueueNewPathVarAsNeeded(this, this->targetFunction, _u("!targetFunction"));
  390. this->GetScriptContext()->TTDWellKnownInfo->EnqueueNewPathVarAsNeeded(this, this->boundThis, _u("!boundThis"));
  391. TTDAssert(this->count == 0, "Should only have empty args in core image");
  392. }
  393. TTD::NSSnapObjects::SnapObjectType BoundFunction::GetSnapTag_TTD() const
  394. {
  395. return TTD::NSSnapObjects::SnapObjectType::SnapBoundFunctionObject;
  396. }
  397. void BoundFunction::ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc)
  398. {
  399. TTD::NSSnapObjects::SnapBoundFunctionInfo* bfi = alloc.SlabAllocateStruct<TTD::NSSnapObjects::SnapBoundFunctionInfo>();
  400. bfi->TargetFunction = TTD_CONVERT_VAR_TO_PTR_ID(static_cast<RecyclableObject*>(this->targetFunction));
  401. bfi->BoundThis = (this->boundThis != nullptr) ?
  402. TTD_CONVERT_VAR_TO_PTR_ID(static_cast<Var>(this->boundThis)) : TTD_INVALID_PTR_ID;
  403. bfi->ArgCount = this->count;
  404. bfi->ArgArray = nullptr;
  405. if(bfi->ArgCount > 0)
  406. {
  407. bfi->ArgArray = alloc.SlabAllocateArray<TTD::TTDVar>(bfi->ArgCount);
  408. }
  409. TTD_PTR_ID* depArray = alloc.SlabReserveArraySpace<TTD_PTR_ID>(bfi->ArgCount + 2 /*this and bound function*/);
  410. depArray[0] = bfi->TargetFunction;
  411. uint32 depCount = 1;
  412. if(this->boundThis != nullptr && TTD::JsSupport::IsVarComplexKind(this->boundThis))
  413. {
  414. depArray[depCount] = bfi->BoundThis;
  415. depCount++;
  416. }
  417. if(bfi->ArgCount > 0)
  418. {
  419. for(uint32 i = 0; i < bfi->ArgCount; ++i)
  420. {
  421. bfi->ArgArray[i] = this->boundArgs[i];
  422. //Primitive kinds always inflated first so we only need to deal with complex kinds as depends on
  423. if(TTD::JsSupport::IsVarComplexKind(this->boundArgs[i]))
  424. {
  425. depArray[depCount] = TTD_CONVERT_VAR_TO_PTR_ID(this->boundArgs[i]);
  426. depCount++;
  427. }
  428. }
  429. }
  430. alloc.SlabCommitArraySpace<TTD_PTR_ID>(depCount, depCount + bfi->ArgCount);
  431. TTD::NSSnapObjects::StdExtractSetKindSpecificInfo<TTD::NSSnapObjects::SnapBoundFunctionInfo*, TTD::NSSnapObjects::SnapObjectType::SnapBoundFunctionObject>(objData, bfi, alloc, depCount, depArray);
  432. }
  433. BoundFunction* BoundFunction::InflateBoundFunction(
  434. ScriptContext* ctx, RecyclableObject* function, Var bThis, uint32 ct, Field(Var)* args)
  435. {
  436. BoundFunction* res = RecyclerNew(ctx->GetRecycler(), BoundFunction, ctx->GetLibrary()->GetBoundFunctionType());
  437. res->boundThis = bThis;
  438. res->count = ct;
  439. res->boundArgs = args;
  440. res->targetFunction = function;
  441. return res;
  442. }
  443. #endif
  444. } // namespace Js