JintUnaryExpression.cs 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. using Esprima.Ast;
  2. using Jint.Extensions;
  3. using Jint.Native;
  4. using Jint.Runtime.Environments;
  5. using Jint.Runtime.Interop;
  6. using Jint.Runtime.References;
  7. using System.Collections.Concurrent;
  8. using System.Linq;
  9. namespace Jint.Runtime.Interpreter.Expressions
  10. {
  11. internal sealed class JintUnaryExpression : JintExpression
  12. {
  13. #if NETSTANDARD
  14. private static readonly ConcurrentDictionary<(string OperatorName, System.Type Operand), MethodDescriptor> _knownOperators =
  15. new ConcurrentDictionary<(string OperatorName, System.Type Operand), MethodDescriptor>();
  16. #else
  17. private static readonly ConcurrentDictionary<string, MethodDescriptor> _knownOperators = new ConcurrentDictionary<string, MethodDescriptor>();
  18. #endif
  19. private readonly JintExpression _argument;
  20. private readonly UnaryOperator _operator;
  21. private JintUnaryExpression(Engine engine, UnaryExpression expression) : base(engine, expression)
  22. {
  23. _argument = Build(engine, expression.Argument);
  24. _operator = expression.Operator;
  25. }
  26. internal static JintExpression Build(Engine engine, UnaryExpression expression)
  27. {
  28. if (expression.Operator == UnaryOperator.Minus
  29. && expression.Argument is Literal literal)
  30. {
  31. var value = JintLiteralExpression.ConvertToJsValue(literal);
  32. if (!(value is null))
  33. {
  34. // valid for caching
  35. return new JintConstantExpression(engine, expression, EvaluateMinus(value));
  36. }
  37. }
  38. return new JintUnaryExpression(engine, expression);
  39. }
  40. public override JsValue GetValue()
  41. {
  42. // need to notify correct node when taking shortcut
  43. _engine._lastSyntaxNode = _expression;
  44. return (JsValue) EvaluateInternal();
  45. }
  46. protected override object EvaluateInternal()
  47. {
  48. switch (_operator)
  49. {
  50. case UnaryOperator.Plus:
  51. {
  52. var v = _argument.GetValue();
  53. if (_engine.Options.Interop.OperatorOverloadingAllowed &&
  54. TryOperatorOverloading(_engine, v, "op_UnaryPlus", out var result))
  55. {
  56. return result;
  57. }
  58. return v.IsInteger() && v.AsInteger() != 0
  59. ? v
  60. : JsNumber.Create(TypeConverter.ToNumber(v));
  61. }
  62. case UnaryOperator.Minus:
  63. {
  64. var v = _argument.GetValue();
  65. if (_engine.Options.Interop.OperatorOverloadingAllowed &&
  66. TryOperatorOverloading(_engine, v, "op_UnaryNegation", out var result))
  67. {
  68. return result;
  69. }
  70. return EvaluateMinus(v);
  71. }
  72. case UnaryOperator.BitwiseNot:
  73. {
  74. var v = _argument.GetValue();
  75. if (_engine.Options.Interop.OperatorOverloadingAllowed &&
  76. TryOperatorOverloading(_engine, v, "op_OnesComplement", out var result))
  77. {
  78. return result;
  79. }
  80. return JsNumber.Create(~TypeConverter.ToInt32(v));
  81. }
  82. case UnaryOperator.LogicalNot:
  83. {
  84. var v = _argument.GetValue();
  85. if (_engine.Options.Interop.OperatorOverloadingAllowed &&
  86. TryOperatorOverloading(_engine, v, "op_LogicalNot", out var result))
  87. {
  88. return result;
  89. }
  90. return !TypeConverter.ToBoolean(v) ? JsBoolean.True : JsBoolean.False;
  91. }
  92. case UnaryOperator.Delete:
  93. var r = _argument.Evaluate() as Reference;
  94. if (r == null)
  95. {
  96. return JsBoolean.True;
  97. }
  98. if (r.IsUnresolvableReference())
  99. {
  100. if (r.IsStrictReference())
  101. {
  102. ExceptionHelper.ThrowSyntaxError(_engine.Realm);
  103. }
  104. _engine._referencePool.Return(r);
  105. return JsBoolean.True;
  106. }
  107. if (r.IsPropertyReference())
  108. {
  109. if (r.IsSuperReference())
  110. {
  111. ExceptionHelper.ThrowReferenceError(_engine.Realm, r);
  112. }
  113. var o = TypeConverter.ToObject(_engine.Realm, r.GetBase());
  114. var deleteStatus = o.Delete(r.GetReferencedName());
  115. if (!deleteStatus && r.IsStrictReference())
  116. {
  117. ExceptionHelper.ThrowTypeError(_engine.Realm);
  118. }
  119. _engine._referencePool.Return(r);
  120. return deleteStatus ? JsBoolean.True : JsBoolean.False;
  121. }
  122. if (r.IsStrictReference())
  123. {
  124. ExceptionHelper.ThrowSyntaxError(_engine.Realm);
  125. }
  126. var bindings = r.GetBase().TryCast<EnvironmentRecord>();
  127. var property = r.GetReferencedName();
  128. _engine._referencePool.Return(r);
  129. return bindings.DeleteBinding(property.ToString()) ? JsBoolean.True : JsBoolean.False;
  130. case UnaryOperator.Void:
  131. _argument.GetValue();
  132. return Undefined.Instance;
  133. case UnaryOperator.TypeOf:
  134. {
  135. var value = _argument.Evaluate();
  136. r = value as Reference;
  137. if (r != null)
  138. {
  139. if (r.IsUnresolvableReference())
  140. {
  141. _engine._referencePool.Return(r);
  142. return JsString.UndefinedString;
  143. }
  144. }
  145. // TODO: double evaluation problem
  146. var v = _argument.GetValue();
  147. if (v.IsUndefined())
  148. {
  149. return JsString.UndefinedString;
  150. }
  151. if (v.IsNull())
  152. {
  153. return JsString.ObjectString;
  154. }
  155. switch (v.Type)
  156. {
  157. case Types.Boolean: return JsString.BooleanString;
  158. case Types.Number: return JsString.NumberString;
  159. case Types.String: return JsString.StringString;
  160. case Types.Symbol: return JsString.SymbolString;
  161. }
  162. if (v.IsCallable)
  163. {
  164. return JsString.FunctionString;
  165. }
  166. return JsString.ObjectString;
  167. }
  168. default:
  169. ExceptionHelper.ThrowArgumentException();
  170. return null;
  171. }
  172. }
  173. private static JsNumber EvaluateMinus(JsValue value)
  174. {
  175. var minusValue = value;
  176. if (minusValue.IsInteger())
  177. {
  178. var asInteger = minusValue.AsInteger();
  179. if (asInteger != 0)
  180. {
  181. return JsNumber.Create(asInteger * -1);
  182. }
  183. }
  184. var n = TypeConverter.ToNumber(minusValue);
  185. return JsNumber.Create(double.IsNaN(n) ? double.NaN : n * -1);
  186. }
  187. internal static bool TryOperatorOverloading(Engine _engine, JsValue value, string clrName, out JsValue result)
  188. {
  189. var operand = value.ToObject();
  190. if (operand != null)
  191. {
  192. var operandType = operand.GetType();
  193. var arguments = new[] { value };
  194. #if NETSTANDARD
  195. var key = (clrName, operandType);
  196. #else
  197. var key = $"{clrName}->{operandType}";
  198. #endif
  199. var method = _knownOperators.GetOrAdd(key, _ =>
  200. {
  201. var foundMethod = operandType.GetOperatorOverloadMethods()
  202. .FirstOrDefault(x => x.Name == clrName && x.GetParameters().Length == 1);
  203. if (foundMethod != null)
  204. {
  205. return new MethodDescriptor(foundMethod);
  206. }
  207. return null;
  208. });
  209. if (method != null)
  210. {
  211. result = method.Call(_engine, null, arguments);
  212. return true;
  213. }
  214. }
  215. result = null;
  216. return false;
  217. }
  218. }
  219. }