ArgumentsInstance.cs 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. using System.Collections.Generic;
  2. using System.Threading;
  3. using Jint.Native.Function;
  4. using Jint.Native.Object;
  5. using Jint.Native.Symbol;
  6. using Jint.Runtime;
  7. using Jint.Runtime.Descriptors;
  8. using Jint.Runtime.Descriptors.Specialized;
  9. using Jint.Runtime.Environments;
  10. using Jint.Runtime.Interop;
  11. namespace Jint.Native.Argument
  12. {
  13. /// <summary>
  14. /// http://www.ecma-international.org/ecma-262/5.1/#sec-10.6
  15. /// </summary>
  16. public sealed class ArgumentsInstance : ObjectInstance
  17. {
  18. // cache property container for array iteration for less allocations
  19. private static readonly ThreadLocal<HashSet<string>> _mappedNamed = new ThreadLocal<HashSet<string>>(() => new HashSet<string>());
  20. private FunctionInstance _func;
  21. private Key[] _names;
  22. private JsValue[] _args;
  23. private DeclarativeEnvironmentRecord _env;
  24. private bool _canReturnToPool;
  25. private bool _hasRestParameter;
  26. private bool _materialized;
  27. internal ArgumentsInstance(Engine engine)
  28. : base(engine, ObjectClass.Arguments)
  29. {
  30. }
  31. internal void Prepare(
  32. FunctionInstance func,
  33. Key[] names,
  34. JsValue[] args,
  35. DeclarativeEnvironmentRecord env,
  36. bool hasRestParameter)
  37. {
  38. _func = func;
  39. _names = names;
  40. _args = args;
  41. _env = env;
  42. _hasRestParameter = hasRestParameter;
  43. _canReturnToPool = true;
  44. ClearProperties();
  45. }
  46. protected override void Initialize()
  47. {
  48. _canReturnToPool = false;
  49. var args = _args;
  50. DefinePropertyOrThrow(CommonProperties.Length, new PropertyDescriptor(_args.Length, PropertyFlag.NonEnumerable));
  51. if (_func is null)
  52. {
  53. // unmapped
  54. ParameterMap = null;
  55. for (uint i = 0; i < (uint) args.Length; i++)
  56. {
  57. var val = args[i];
  58. CreateDataProperty(JsString.Create(i), val);
  59. }
  60. DefinePropertyOrThrow(CommonProperties.Callee, _engine._callerCalleeArgumentsThrowerNonConfigurable);
  61. }
  62. else
  63. {
  64. ObjectInstance map = null;
  65. if (args.Length > 0)
  66. {
  67. var mappedNamed = _mappedNamed.Value;
  68. mappedNamed.Clear();
  69. map = Engine.Realm.Intrinsics.Object.Construct(Arguments.Empty);
  70. for (uint i = 0; i < (uint) args.Length; i++)
  71. {
  72. SetOwnProperty(JsString.Create(i), new PropertyDescriptor(args[i], PropertyFlag.ConfigurableEnumerableWritable));
  73. if (i < _names.Length)
  74. {
  75. var name = _names[i];
  76. if (mappedNamed.Add(name))
  77. {
  78. map.SetOwnProperty(JsString.Create(i), new ClrAccessDescriptor(_env, Engine, name));
  79. }
  80. }
  81. }
  82. }
  83. ParameterMap = map;
  84. // step 13
  85. DefinePropertyOrThrow(CommonProperties.Callee, new PropertyDescriptor(_func, PropertyFlag.NonEnumerable));
  86. }
  87. var iteratorFunction = new ClrFunctionInstance(Engine, "iterator", _engine.Realm.Intrinsics.Array.PrototypeObject.Values, 0, PropertyFlag.Configurable);
  88. DefinePropertyOrThrow(GlobalSymbolRegistry.Iterator, new PropertyDescriptor(iteratorFunction, PropertyFlag.Writable | PropertyFlag.Configurable));
  89. }
  90. public ObjectInstance ParameterMap { get; set; }
  91. internal override bool IsIntegerIndexedArray => true;
  92. public override PropertyDescriptor GetOwnProperty(JsValue property)
  93. {
  94. EnsureInitialized();
  95. if (!ReferenceEquals(ParameterMap, null))
  96. {
  97. var desc = base.GetOwnProperty(property);
  98. if (desc == PropertyDescriptor.Undefined)
  99. {
  100. return desc;
  101. }
  102. if (ParameterMap.TryGetValue(property, out var jsValue) && !jsValue.IsUndefined())
  103. {
  104. desc.Value = jsValue;
  105. }
  106. return desc;
  107. }
  108. return base.GetOwnProperty(property);
  109. }
  110. /// Implementation from ObjectInstance official specs as the one
  111. /// in ObjectInstance is optimized for the general case and wouldn't work
  112. /// for arrays
  113. public override bool Set(JsValue property, JsValue value, JsValue receiver)
  114. {
  115. EnsureInitialized();
  116. if (!CanPut(property))
  117. {
  118. return false;
  119. }
  120. var ownDesc = GetOwnProperty(property);
  121. if (ownDesc.IsDataDescriptor())
  122. {
  123. var valueDesc = new PropertyDescriptor(value, PropertyFlag.None);
  124. return DefineOwnProperty(property, valueDesc);
  125. }
  126. // property is an accessor or inherited
  127. var desc = GetProperty(property);
  128. if (desc.IsAccessorDescriptor())
  129. {
  130. if (!(desc.Set is ICallable setter))
  131. {
  132. return false;
  133. }
  134. setter.Call(receiver, new[] {value});
  135. }
  136. else
  137. {
  138. var newDesc = new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable);
  139. return DefineOwnProperty(property, newDesc);
  140. }
  141. return true;
  142. }
  143. public override bool DefineOwnProperty(JsValue property, PropertyDescriptor desc)
  144. {
  145. if (_hasRestParameter)
  146. {
  147. // immutable
  148. return true;
  149. }
  150. EnsureInitialized();
  151. if (!(_func is null) && !ReferenceEquals(ParameterMap, null))
  152. {
  153. var map = ParameterMap;
  154. var isMapped = map.GetOwnProperty(property);
  155. var allowed = base.DefineOwnProperty(property, desc);
  156. if (!allowed)
  157. {
  158. return false;
  159. }
  160. if (isMapped != PropertyDescriptor.Undefined)
  161. {
  162. if (desc.IsAccessorDescriptor())
  163. {
  164. map.Delete(property);
  165. }
  166. else
  167. {
  168. var descValue = desc.Value;
  169. if (!ReferenceEquals(descValue, null) && !descValue.IsUndefined())
  170. {
  171. map.Set(property, descValue, false);
  172. }
  173. if (desc.WritableSet && !desc.Writable)
  174. {
  175. map.Delete(property);
  176. }
  177. }
  178. }
  179. return true;
  180. }
  181. return base.DefineOwnProperty(property, desc);
  182. }
  183. public override bool Delete(JsValue property)
  184. {
  185. EnsureInitialized();
  186. if (!(_func is null) && !ReferenceEquals(ParameterMap, null))
  187. {
  188. var map = ParameterMap;
  189. var isMapped = map.GetOwnProperty(property);
  190. var result = base.Delete(property);
  191. if (result && isMapped != PropertyDescriptor.Undefined)
  192. {
  193. map.Delete(property);
  194. }
  195. return result;
  196. }
  197. return base.Delete(property);
  198. }
  199. internal void Materialize()
  200. {
  201. if (_materialized)
  202. {
  203. // already done
  204. return;
  205. }
  206. _materialized = true;
  207. EnsureInitialized();
  208. var args = _args;
  209. var copiedArgs = new JsValue[args.Length];
  210. System.Array.Copy(args, copiedArgs, args.Length);
  211. _args = copiedArgs;
  212. _canReturnToPool = false;
  213. }
  214. internal void FunctionWasCalled()
  215. {
  216. // should no longer expose arguments which is special name
  217. ParameterMap = null;
  218. if (_canReturnToPool)
  219. {
  220. _engine._argumentsInstancePool.Return(this);
  221. // prevent double-return
  222. _canReturnToPool = false;
  223. }
  224. }
  225. }
  226. }