JintUnaryExpression.cs 6.0 KB

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