JintUpdateExpression.cs 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. using Jint.Native;
  2. using Jint.Runtime.Environments;
  3. namespace Jint.Runtime.Interpreter.Expressions
  4. {
  5. internal sealed class JintUpdateExpression : JintExpression
  6. {
  7. private JintExpression _argument = null!;
  8. private int _change;
  9. private bool _prefix;
  10. private JintIdentifierExpression? _leftIdentifier;
  11. private bool _evalOrArguments;
  12. private bool _initialized;
  13. public JintUpdateExpression(UpdateExpression expression) : base(expression)
  14. {
  15. }
  16. private void Initialize()
  17. {
  18. var expression = (UpdateExpression) _expression;
  19. _prefix = expression.Prefix;
  20. _argument = Build(expression.Argument);
  21. if (expression.Operator == Operator.Increment)
  22. {
  23. _change = 1;
  24. }
  25. else if (expression.Operator == Operator.Decrement)
  26. {
  27. _change = -1;
  28. }
  29. else
  30. {
  31. ExceptionHelper.ThrowArgumentException();
  32. }
  33. _leftIdentifier = _argument as JintIdentifierExpression;
  34. _evalOrArguments = _leftIdentifier?.HasEvalOrArguments == true;
  35. }
  36. protected override object EvaluateInternal(EvaluationContext context)
  37. {
  38. if (!_initialized)
  39. {
  40. Initialize();
  41. _initialized = true;
  42. }
  43. var fastResult = _leftIdentifier != null
  44. ? UpdateIdentifier(context)
  45. : null;
  46. return fastResult ?? UpdateNonIdentifier(context);
  47. }
  48. private JsValue UpdateNonIdentifier(EvaluationContext context)
  49. {
  50. var engine = context.Engine;
  51. var reference = _argument.Evaluate(context) as Reference;
  52. if (reference is null)
  53. {
  54. ExceptionHelper.ThrowTypeError(engine.Realm, "Invalid left-hand side expression");
  55. }
  56. reference.AssertValid(engine.Realm);
  57. var value = engine.GetValue(reference, false);
  58. var isInteger = value._type == InternalTypes.Integer;
  59. JsValue? newValue = null;
  60. var operatorOverloaded = false;
  61. if (context.OperatorOverloadingAllowed)
  62. {
  63. if (JintUnaryExpression.TryOperatorOverloading(context, _argument.GetValue(context), _change > 0 ? "op_Increment" : "op_Decrement", out var result))
  64. {
  65. operatorOverloaded = true;
  66. newValue = result;
  67. }
  68. }
  69. if (!operatorOverloaded)
  70. {
  71. if (isInteger)
  72. {
  73. newValue = JsNumber.Create(value.AsInteger() + _change);
  74. }
  75. else if (!value.IsBigInt())
  76. {
  77. newValue = JsNumber.Create(TypeConverter.ToNumber(value) + _change);
  78. }
  79. else
  80. {
  81. newValue = JsBigInt.Create(TypeConverter.ToBigInt(value) + _change);
  82. }
  83. }
  84. engine.PutValue(reference, newValue!);
  85. engine._referencePool.Return(reference);
  86. if (_prefix)
  87. {
  88. return newValue!;
  89. }
  90. else
  91. {
  92. if (isInteger || operatorOverloaded)
  93. {
  94. return value;
  95. }
  96. if (!value.IsBigInt())
  97. {
  98. return JsNumber.Create(TypeConverter.ToNumber(value));
  99. }
  100. return JsBigInt.Create(value);
  101. }
  102. }
  103. private JsValue? UpdateIdentifier(EvaluationContext context)
  104. {
  105. var name = _leftIdentifier!.Identifier;
  106. if (JintEnvironment.TryGetIdentifierEnvironmentWithBindingValue(
  107. context.Engine.ExecutionContext.LexicalEnvironment,
  108. name,
  109. out var environmentRecord,
  110. out var value))
  111. {
  112. if (_evalOrArguments && StrictModeScope.IsStrictModeCode)
  113. {
  114. ExceptionHelper.ThrowSyntaxError(context.Engine.Realm);
  115. }
  116. var isInteger = value._type == InternalTypes.Integer;
  117. JsValue? newValue = null;
  118. var operatorOverloaded = false;
  119. if (context.OperatorOverloadingAllowed)
  120. {
  121. if (JintUnaryExpression.TryOperatorOverloading(context, _argument.GetValue(context), _change > 0 ? "op_Increment" : "op_Decrement", out var result))
  122. {
  123. operatorOverloaded = true;
  124. newValue = result;
  125. }
  126. }
  127. if (!operatorOverloaded)
  128. {
  129. if (isInteger)
  130. {
  131. newValue = JsNumber.Create(value.AsInteger() + _change);
  132. }
  133. else if (value._type != InternalTypes.BigInt)
  134. {
  135. newValue = JsNumber.Create(TypeConverter.ToNumber(value) + _change);
  136. }
  137. else
  138. {
  139. newValue = JsBigInt.Create(TypeConverter.ToBigInt(value) + _change);
  140. }
  141. }
  142. environmentRecord.SetMutableBinding(name.Key, newValue!, StrictModeScope.IsStrictModeCode);
  143. if (_prefix)
  144. {
  145. return newValue;
  146. }
  147. if (!value.IsBigInt() && !value.IsNumber() && !operatorOverloaded)
  148. {
  149. return JsNumber.Create(TypeConverter.ToNumber(value));
  150. }
  151. return value;
  152. }
  153. return null;
  154. }
  155. }
  156. }