RegExpPrototype.cs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. using System.Text.RegularExpressions;
  2. using Jint.Collections;
  3. using Jint.Native.Array;
  4. using Jint.Native.Object;
  5. using Jint.Runtime;
  6. using Jint.Runtime.Descriptors;
  7. using Jint.Runtime.Interop;
  8. namespace Jint.Native.RegExp
  9. {
  10. public sealed class RegExpPrototype : RegExpInstance
  11. {
  12. private RegExpConstructor _regExpConstructor;
  13. private RegExpPrototype(Engine engine)
  14. : base(engine)
  15. {
  16. }
  17. public static RegExpPrototype CreatePrototypeObject(Engine engine, RegExpConstructor regExpConstructor)
  18. {
  19. var obj = new RegExpPrototype(engine)
  20. {
  21. _prototype = engine.Object.PrototypeObject,
  22. _regExpConstructor = regExpConstructor
  23. };
  24. return obj;
  25. }
  26. protected override void Initialize()
  27. {
  28. _properties = new StringDictionarySlim<PropertyDescriptor>(10)
  29. {
  30. ["constructor"] = new PropertyDescriptor(_regExpConstructor, true, false, true),
  31. ["toString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toString", ToRegExpString), true, false, true),
  32. ["exec"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "exec", Exec, 1), true, false, true),
  33. ["test"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "test", Test, 1), true, false, true),
  34. ["global"] = new PropertyDescriptor(false, false, false, false),
  35. ["ignoreCase"] = new PropertyDescriptor(false, false, false, false),
  36. ["multiline"] = new PropertyDescriptor(false, false, false, false),
  37. ["source"] = new PropertyDescriptor("(?:)", false, false, false),
  38. ["flags"] = new PropertyDescriptor("", false, false, false),
  39. ["lastIndex"] = new PropertyDescriptor(0, true, false, false)
  40. };
  41. }
  42. private static JsValue ToRegExpString(JsValue thisObj, JsValue[] arguments)
  43. {
  44. var regexObj = thisObj.TryCast<ObjectInstance>();
  45. if (regexObj.TryGetValue("source", out var source) == false)
  46. source = Undefined.ToString();
  47. if (regexObj.TryGetValue("flags", out var flags) == false)
  48. flags = Undefined.ToString();
  49. return $"/{source.AsString()}/{flags.AsString()}";
  50. }
  51. private JsValue Test(JsValue thisObj, JsValue[] arguments)
  52. {
  53. var r = TypeConverter.ToObject(Engine, thisObj);
  54. if (r.Class != "RegExp")
  55. {
  56. ExceptionHelper.ThrowTypeError(Engine);
  57. }
  58. var match = Exec(r, arguments);
  59. return !match.IsNull();
  60. }
  61. internal JsValue Exec(JsValue thisObj, JsValue[] arguments)
  62. {
  63. var R = TypeConverter.ToObject(Engine, thisObj) as RegExpInstance;
  64. if (ReferenceEquals(R, null))
  65. {
  66. ExceptionHelper.ThrowTypeError(Engine);
  67. }
  68. var s = TypeConverter.ToString(arguments.At(0));
  69. var length = s.Length;
  70. var lastIndex = TypeConverter.ToNumber(R.Get("lastIndex", R));
  71. var i = TypeConverter.ToInteger(lastIndex);
  72. var global = R.Global;
  73. if (!global)
  74. {
  75. i = 0;
  76. }
  77. if (R.Source == "(?:)") // Reg Exp is really ""
  78. {
  79. // "aaa".match() => [ '', index: 0, input: 'aaa' ]
  80. var aa = InitReturnValueArray((ArrayInstance) Engine.Array.Construct(Arguments.Empty), s, 1, 0);
  81. aa.DefinePropertyOrThrow("0", new PropertyDescriptor("", PropertyFlag.ConfigurableEnumerableWritable));
  82. return aa;
  83. }
  84. Match r = null;
  85. if (i < 0 || i > length)
  86. {
  87. R.Set("lastIndex", (double) 0, true);
  88. return Null;
  89. }
  90. r = R.Match(s, i);
  91. if (!r.Success)
  92. {
  93. R.Set("lastIndex", (double) 0, true);
  94. return Null;
  95. }
  96. var e = r.Index + r.Length;
  97. if (global)
  98. {
  99. R.Set("lastIndex", (double) e, true);
  100. }
  101. var n = r.Groups.Count;
  102. var matchIndex = r.Index;
  103. var a = InitReturnValueArray((ArrayInstance) Engine.Array.Construct(Arguments.Empty), s, n, matchIndex);
  104. for (uint k = 0; k < n; k++)
  105. {
  106. var group = r.Groups[(int) k];
  107. var value = group.Success ? group.Value : Undefined;
  108. a.SetIndexValue(k, value, updateLength: false);
  109. }
  110. a.SetLength((uint) n);
  111. return a;
  112. }
  113. private static ArrayInstance InitReturnValueArray(ArrayInstance array, string inputValue, int lengthValue, int indexValue)
  114. {
  115. array.SetOwnProperty("index", new PropertyDescriptor(indexValue, PropertyFlag.ConfigurableEnumerableWritable));
  116. array.SetOwnProperty("input", new PropertyDescriptor(inputValue, PropertyFlag.ConfigurableEnumerableWritable));
  117. array.SetOwnProperty(KnownKeys.Length, new PropertyDescriptor(lengthValue, PropertyFlag.OnlyWritable));
  118. return array;
  119. }
  120. }
  121. }