JintObjectExpression.cs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Threading;
  4. using Esprima.Ast;
  5. using Jint.Collections;
  6. using Jint.Native.Function;
  7. using Jint.Runtime.Descriptors;
  8. using Jint.Runtime.Descriptors.Specialized;
  9. namespace Jint.Runtime.Interpreter.Expressions
  10. {
  11. /// <summary>
  12. /// http://www.ecma-international.org/ecma-262/5.1/#sec-11.1.5
  13. /// </summary>
  14. internal sealed class JintObjectExpression : JintExpression
  15. {
  16. // cache key container for array iteration for less allocations
  17. private static readonly ThreadLocal<HashSet<string>> _nameDuplicateChecks = new ThreadLocal<HashSet<string>>(() => new HashSet<string>());
  18. private JintExpression[] _valueExpressions;
  19. private ObjectProperty[] _properties;
  20. // check if we can do a shortcut when all are object properties
  21. // and don't require duplicate checking
  22. private bool _canBuildFast;
  23. private class ObjectProperty
  24. {
  25. private readonly Key _name;
  26. internal readonly Property _value;
  27. public ObjectProperty(in Key propName, Property property)
  28. {
  29. _name = propName;
  30. _value = property;
  31. }
  32. public ref readonly Key Name => ref _name;
  33. }
  34. public JintObjectExpression(Engine engine, ObjectExpression expression) : base(engine, expression)
  35. {
  36. _initialized = false;
  37. }
  38. protected override void Initialize()
  39. {
  40. var expression = (ObjectExpression) _expression;
  41. _valueExpressions = new JintExpression[expression.Properties.Count];
  42. _properties = new ObjectProperty[expression.Properties.Count];
  43. var propertyNames = _nameDuplicateChecks.Value;
  44. propertyNames.Clear();
  45. _canBuildFast = true;
  46. for (var i = 0; i < _properties.Length; i++)
  47. {
  48. var property = expression.Properties[i];
  49. string propName = null;
  50. if (property.Key is Literal literal)
  51. {
  52. propName = literal.Value as string ?? Convert.ToString(literal.Value, provider: null);
  53. }
  54. if (property.Key is Esprima.Ast.Identifier identifier)
  55. {
  56. propName = identifier.Name;
  57. }
  58. _properties[i] = new ObjectProperty(propName ?? "", property);
  59. if (property.Kind == PropertyKind.Init || property.Kind == PropertyKind.Data)
  60. {
  61. var propertyValue = (Expression) property.Value;
  62. _valueExpressions[i] = Build(_engine, propertyValue);
  63. _canBuildFast &= !propertyValue.IsFunctionWithName();
  64. }
  65. else
  66. {
  67. _canBuildFast = false;
  68. }
  69. _canBuildFast &= propertyNames.Add(propName);
  70. _canBuildFast &= propName != null;
  71. }
  72. }
  73. protected override object EvaluateInternal()
  74. {
  75. return _canBuildFast
  76. ? BuildObjectFast()
  77. : BuildObjectNormal();
  78. }
  79. /// <summary>
  80. /// Version that can safely build plain object with only normal init/data fields fast.
  81. /// </summary>
  82. private object BuildObjectFast()
  83. {
  84. var obj = _engine.Object.Construct(0);
  85. var properties = new StringDictionarySlim<PropertyDescriptor>(_properties.Length);
  86. for (var i = 0; i < _properties.Length; i++)
  87. {
  88. var objectProperty = _properties[i];
  89. var valueExpression = _valueExpressions[i];
  90. var propValue = valueExpression.GetValue();
  91. properties[objectProperty.Name] = new PropertyDescriptor(propValue, PropertyFlag.ConfigurableEnumerableWritable);
  92. }
  93. obj._properties = properties;
  94. return obj;
  95. }
  96. private object BuildObjectNormal()
  97. {
  98. var obj = _engine.Object.Construct(System.Math.Max(2, _properties.Length));
  99. bool isStrictModeCode = StrictModeScope.IsStrictModeCode;
  100. for (var i = 0; i < _properties.Length; i++)
  101. {
  102. var objectProperty = _properties[i];
  103. var property = objectProperty._value;
  104. var propName = !string.IsNullOrEmpty(objectProperty.Name)
  105. ? objectProperty.Name
  106. : (Key) objectProperty._value.Key.GetKey(_engine);
  107. PropertyDescriptor propDesc;
  108. if (property.Kind == PropertyKind.Init || property.Kind == PropertyKind.Data)
  109. {
  110. var expr = _valueExpressions[i];
  111. var propValue = expr.GetValue();
  112. if (expr._expression.IsFunctionWithName())
  113. {
  114. var functionInstance = (FunctionInstance) propValue;
  115. functionInstance.SetFunctionName(objectProperty.Name);
  116. }
  117. propDesc = new PropertyDescriptor(propValue, PropertyFlag.ConfigurableEnumerableWritable);
  118. }
  119. else if (property.Kind == PropertyKind.Get || property.Kind == PropertyKind.Set)
  120. {
  121. var function = property.Value as IFunction ?? ExceptionHelper.ThrowSyntaxError<IFunction>(_engine);
  122. var functionInstance = new ScriptFunctionInstance(
  123. _engine,
  124. function,
  125. _engine.ExecutionContext.LexicalEnvironment,
  126. isStrictModeCode
  127. );
  128. functionInstance.SetFunctionName(objectProperty.Name);
  129. functionInstance._prototype = null;
  130. propDesc = new GetSetPropertyDescriptor(
  131. get: property.Kind == PropertyKind.Get ? functionInstance : null,
  132. set: property.Kind == PropertyKind.Set ? functionInstance : null,
  133. PropertyFlag.Enumerable | PropertyFlag.Configurable);
  134. }
  135. else
  136. {
  137. return ExceptionHelper.ThrowArgumentOutOfRangeException<object>();
  138. }
  139. obj.DefineOwnProperty(propName, propDesc, false);
  140. }
  141. return obj;
  142. }
  143. }
  144. }