JintUnaryExpression.cs 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. using Esprima.Ast;
  2. using Jint.Native;
  3. using Jint.Runtime.Environments;
  4. using Jint.Runtime.References;
  5. namespace Jint.Runtime.Interpreter.Expressions
  6. {
  7. internal sealed class JintUnaryExpression : JintExpression
  8. {
  9. private readonly JintExpression _argument;
  10. private readonly UnaryOperator _operator;
  11. private JintUnaryExpression(Engine engine, UnaryExpression expression) : base(engine, expression)
  12. {
  13. _argument = Build(engine, expression.Argument);
  14. _operator = expression.Operator;
  15. }
  16. internal static JintExpression Build(Engine engine, UnaryExpression expression)
  17. {
  18. if (expression.Operator == UnaryOperator.Minus
  19. && expression.Argument is Literal literal)
  20. {
  21. var value = JintLiteralExpression.ConvertToJsValue(literal);
  22. if (!(value is null))
  23. {
  24. // valid for caching
  25. return new JintConstantExpression(engine, expression, EvaluateMinus(value));
  26. }
  27. }
  28. return new JintUnaryExpression(engine, expression);
  29. }
  30. public override JsValue GetValue()
  31. {
  32. // need to notify correct node when taking shortcut
  33. _engine._lastSyntaxNode = _expression;
  34. return (JsValue) EvaluateInternal();
  35. }
  36. protected override object EvaluateInternal()
  37. {
  38. switch (_operator)
  39. {
  40. case UnaryOperator.Plus:
  41. var plusValue = _argument.GetValue();
  42. return plusValue.IsInteger() && plusValue.AsInteger() != 0
  43. ? plusValue
  44. : JsNumber.Create(TypeConverter.ToNumber(plusValue));
  45. case UnaryOperator.Minus:
  46. return EvaluateMinus(_argument.GetValue());
  47. case UnaryOperator.BitwiseNot:
  48. return JsNumber.Create(~TypeConverter.ToInt32(_argument.GetValue()));
  49. case UnaryOperator.LogicalNot:
  50. return !TypeConverter.ToBoolean(_argument.GetValue()) ? JsBoolean.True : JsBoolean.False;
  51. case UnaryOperator.Delete:
  52. var r = _argument.Evaluate() as Reference;
  53. if (r == null)
  54. {
  55. return JsBoolean.True;
  56. }
  57. if (r.IsUnresolvableReference())
  58. {
  59. if (r.IsStrictReference())
  60. {
  61. ExceptionHelper.ThrowSyntaxError(_engine);
  62. }
  63. _engine._referencePool.Return(r);
  64. return JsBoolean.True;
  65. }
  66. if (r.IsPropertyReference())
  67. {
  68. var o = TypeConverter.ToObject(_engine, r.GetBase());
  69. var deleteStatus = o.Delete(r.GetReferencedName());
  70. if (!deleteStatus && r.IsStrictReference())
  71. {
  72. ExceptionHelper.ThrowTypeError(_engine);
  73. }
  74. _engine._referencePool.Return(r);
  75. return deleteStatus ? JsBoolean.True : JsBoolean.False;
  76. }
  77. if (r.IsStrictReference())
  78. {
  79. ExceptionHelper.ThrowSyntaxError(_engine);
  80. }
  81. var bindings = r.GetBase().TryCast<EnvironmentRecord>();
  82. var property = r.GetReferencedName();
  83. _engine._referencePool.Return(r);
  84. return bindings.DeleteBinding(property.ToString()) ? JsBoolean.True : JsBoolean.False;
  85. case UnaryOperator.Void:
  86. _argument.GetValue();
  87. return Undefined.Instance;
  88. case UnaryOperator.TypeOf:
  89. var value = _argument.Evaluate();
  90. r = value as Reference;
  91. if (r != null)
  92. {
  93. if (r.IsUnresolvableReference())
  94. {
  95. _engine._referencePool.Return(r);
  96. return JsString.UndefinedString;
  97. }
  98. }
  99. var v = _argument.GetValue();
  100. if (v.IsUndefined())
  101. {
  102. return JsString.UndefinedString;
  103. }
  104. if (v.IsNull())
  105. {
  106. return JsString.ObjectString;
  107. }
  108. switch (v.Type)
  109. {
  110. case Types.Boolean: return JsString.BooleanString;
  111. case Types.Number: return JsString.NumberString;
  112. case Types.String: return JsString.StringString;
  113. case Types.Symbol: return JsString.SymbolString;
  114. }
  115. if (v.IsCallable)
  116. {
  117. return JsString.FunctionString;
  118. }
  119. return JsString.ObjectString;
  120. default:
  121. return ExceptionHelper.ThrowArgumentException<object>();
  122. }
  123. }
  124. private static JsNumber EvaluateMinus(JsValue value)
  125. {
  126. var minusValue = value;
  127. if (minusValue.IsInteger())
  128. {
  129. var asInteger = minusValue.AsInteger();
  130. if (asInteger != 0)
  131. {
  132. return JsNumber.Create(asInteger * -1);
  133. }
  134. }
  135. var n = TypeConverter.ToNumber(minusValue);
  136. return JsNumber.Create(double.IsNaN(n) ? double.NaN : n * -1);
  137. }
  138. }
  139. }