JintObjectExpression.cs 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. using Esprima.Ast;
  2. using Jint.Collections;
  3. using Jint.Native;
  4. using Jint.Native.Function;
  5. using Jint.Native.Object;
  6. using Jint.Runtime.Descriptors;
  7. namespace Jint.Runtime.Interpreter.Expressions
  8. {
  9. /// <summary>
  10. /// http://www.ecma-international.org/ecma-262/#sec-object-initializer
  11. /// </summary>
  12. internal sealed class JintObjectExpression : JintExpression
  13. {
  14. private JintExpression[] _valueExpressions = System.Array.Empty<JintExpression>();
  15. private ObjectProperty[] _properties = System.Array.Empty<ObjectProperty>();
  16. // check if we can do a shortcut when all are object properties
  17. // and don't require duplicate checking
  18. private bool _canBuildFast;
  19. private class ObjectProperty
  20. {
  21. internal readonly string _key;
  22. private JsString _keyJsString;
  23. internal readonly Property _value;
  24. public ObjectProperty(string key, Property property)
  25. {
  26. _key = key;
  27. _value = property;
  28. }
  29. public JsString KeyJsString => _keyJsString ??= _key != null ? JsString.Create(_key) : null;
  30. }
  31. public JintObjectExpression(Engine engine, ObjectExpression expression) : base(engine, expression)
  32. {
  33. _initialized = false;
  34. }
  35. protected override void Initialize()
  36. {
  37. _canBuildFast = true;
  38. var expression = (ObjectExpression) _expression;
  39. if (expression.Properties.Count == 0)
  40. {
  41. // empty object initializer
  42. return;
  43. }
  44. _valueExpressions = new JintExpression[expression.Properties.Count];
  45. _properties = new ObjectProperty[expression.Properties.Count];
  46. for (var i = 0; i < _properties.Length; i++)
  47. {
  48. string propName = null;
  49. var property = expression.Properties[i];
  50. if (property is Property p)
  51. {
  52. if (p.Key is Literal literal)
  53. {
  54. propName = EsprimaExtensions.LiteralKeyToString(literal);
  55. }
  56. if (!p.Computed && p.Key is Identifier identifier)
  57. {
  58. propName = identifier.Name;
  59. }
  60. _properties[i] = new ObjectProperty(propName, p);
  61. if (p.Kind == PropertyKind.Init || p.Kind == PropertyKind.Data)
  62. {
  63. var propertyValue = p.Value;
  64. _valueExpressions[i] = Build(_engine, propertyValue);
  65. _canBuildFast &= !propertyValue.IsFunctionWithName();
  66. }
  67. else
  68. {
  69. _canBuildFast = false;
  70. }
  71. }
  72. else if (property is SpreadElement spreadElement)
  73. {
  74. _canBuildFast = false;
  75. _properties[i] = null;
  76. _valueExpressions[i] = Build(_engine, spreadElement.Argument);
  77. }
  78. else
  79. {
  80. ExceptionHelper.ThrowArgumentOutOfRangeException("property", "cannot handle property " + property);
  81. }
  82. _canBuildFast &= propName != null;
  83. }
  84. }
  85. protected override object EvaluateInternal()
  86. {
  87. return _canBuildFast
  88. ? BuildObjectFast()
  89. : BuildObjectNormal();
  90. }
  91. /// <summary>
  92. /// Version that can safely build plain object with only normal init/data fields fast.
  93. /// </summary>
  94. private object BuildObjectFast()
  95. {
  96. var obj = _engine.Object.Construct(0);
  97. if (_properties.Length == 0)
  98. {
  99. return obj;
  100. }
  101. var properties = new PropertyDictionary(_properties.Length, checkExistingKeys: true);
  102. for (var i = 0; i < _properties.Length; i++)
  103. {
  104. var objectProperty = _properties[i];
  105. var valueExpression = _valueExpressions[i];
  106. var propValue = valueExpression.GetValue().Clone();
  107. properties[objectProperty._key] = new PropertyDescriptor(propValue, PropertyFlag.ConfigurableEnumerableWritable);
  108. }
  109. obj.SetProperties(properties);
  110. return obj;
  111. }
  112. private object BuildObjectNormal()
  113. {
  114. var obj = _engine.Object.Construct(_properties.Length);
  115. bool isStrictModeCode = _engine._isStrict || StrictModeScope.IsStrictModeCode;
  116. for (var i = 0; i < _properties.Length; i++)
  117. {
  118. var objectProperty = _properties[i];
  119. if (objectProperty is null)
  120. {
  121. // spread
  122. if (_valueExpressions[i].GetValue() is ObjectInstance source)
  123. {
  124. source.CopyDataProperties(obj, null);
  125. }
  126. continue;
  127. }
  128. var property = objectProperty._value;
  129. var propName = objectProperty.KeyJsString ?? property.GetKey(_engine);
  130. PropertyDescriptor propDesc;
  131. if (property.Kind == PropertyKind.Init || property.Kind == PropertyKind.Data)
  132. {
  133. var expr = _valueExpressions[i];
  134. var propValue = expr.GetValue().Clone();
  135. if (expr._expression.IsFunctionWithName())
  136. {
  137. var functionInstance = (FunctionInstance) propValue;
  138. functionInstance.SetFunctionName(propName);
  139. }
  140. propDesc = new PropertyDescriptor(propValue, PropertyFlag.ConfigurableEnumerableWritable);
  141. }
  142. else if (property.Kind == PropertyKind.Get || property.Kind == PropertyKind.Set)
  143. {
  144. var function = property.Value as IFunction ?? ExceptionHelper.ThrowSyntaxError<IFunction>(_engine);
  145. var functionInstance = new ScriptFunctionInstance(
  146. _engine,
  147. function,
  148. _engine.ExecutionContext.LexicalEnvironment,
  149. isStrictModeCode
  150. );
  151. functionInstance.SetFunctionName(propName);
  152. functionInstance._prototypeDescriptor = null;
  153. propDesc = new GetSetPropertyDescriptor(
  154. get: property.Kind == PropertyKind.Get ? functionInstance : null,
  155. set: property.Kind == PropertyKind.Set ? functionInstance : null,
  156. PropertyFlag.Enumerable | PropertyFlag.Configurable);
  157. }
  158. else
  159. {
  160. return ExceptionHelper.ThrowArgumentOutOfRangeException<object>();
  161. }
  162. obj.DefineOwnProperty(propName, propDesc);
  163. }
  164. return obj;
  165. }
  166. }
  167. }