RegExpConstructor.cs 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. using System.Text.RegularExpressions;
  2. using Jint.Collections;
  3. using Jint.Native.Function;
  4. using Jint.Native.Object;
  5. using Jint.Native.Symbol;
  6. using Jint.Runtime;
  7. using Jint.Runtime.Descriptors;
  8. using Jint.Runtime.Interop;
  9. namespace Jint.Native.RegExp;
  10. public sealed class RegExpConstructor : Constructor
  11. {
  12. private static readonly JsString _functionName = new JsString("RegExp");
  13. internal RegExpConstructor(
  14. Engine engine,
  15. Realm realm,
  16. FunctionPrototype functionPrototype,
  17. ObjectPrototype objectPrototype)
  18. : base(engine, realm, _functionName)
  19. {
  20. _prototype = functionPrototype;
  21. PrototypeObject = new RegExpPrototype(engine, realm, this, objectPrototype);
  22. _length = new PropertyDescriptor(2, PropertyFlag.Configurable);
  23. _prototypeDescriptor = new PropertyDescriptor(PrototypeObject, PropertyFlag.AllForbidden);
  24. }
  25. internal RegExpPrototype PrototypeObject { get; }
  26. protected override void Initialize()
  27. {
  28. var symbols = new SymbolDictionary(1)
  29. {
  30. [GlobalSymbolRegistry.Species] = new GetSetPropertyDescriptor(get: new ClrFunction(_engine, "get [Symbol.species]", (thisObj, _) => thisObj, 0, PropertyFlag.Configurable), set: Undefined, PropertyFlag.Configurable)
  31. };
  32. SetSymbols(symbols);
  33. }
  34. protected internal override JsValue Call(JsValue thisObject, JsValue[] arguments)
  35. {
  36. return Construct(arguments, thisObject);
  37. }
  38. public ObjectInstance Construct(JsValue[] arguments)
  39. {
  40. return Construct(arguments, this);
  41. }
  42. /// <summary>
  43. /// https://tc39.es/ecma262/#sec-regexp-pattern-flags
  44. /// </summary>
  45. public override ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
  46. {
  47. var pattern = arguments.At(0);
  48. var flags = arguments.At(1);
  49. var patternIsRegExp = pattern.IsRegExp();
  50. if (newTarget.IsUndefined())
  51. {
  52. newTarget = this;
  53. if (patternIsRegExp && flags.IsUndefined())
  54. {
  55. var patternConstructor = pattern.Get(CommonProperties.Constructor);
  56. if (ReferenceEquals(newTarget, patternConstructor))
  57. {
  58. return (ObjectInstance) pattern;
  59. }
  60. }
  61. }
  62. JsValue p;
  63. JsValue f;
  64. if (pattern is JsRegExp regExpInstance)
  65. {
  66. p = regExpInstance.Source;
  67. f = flags.IsUndefined() ? regExpInstance.Flags : flags;
  68. }
  69. else if (patternIsRegExp)
  70. {
  71. p = pattern.Get(RegExpPrototype.PropertySource);
  72. f = flags.IsUndefined() ? pattern.Get(RegExpPrototype.PropertyFlags) : flags;
  73. }
  74. else
  75. {
  76. p = pattern;
  77. f = flags;
  78. }
  79. var r = RegExpAlloc(newTarget);
  80. return RegExpInitialize(r, p, f);
  81. }
  82. private JsRegExp RegExpInitialize(JsRegExp r, JsValue pattern, JsValue flags)
  83. {
  84. var p = pattern.IsUndefined() ? "" : TypeConverter.ToString(pattern);
  85. if (string.IsNullOrEmpty(p))
  86. {
  87. p = "(?:)";
  88. }
  89. var f = flags.IsUndefined() ? "" : TypeConverter.ToString(flags);
  90. var parserOptions = _engine.GetActiveParserOptions();
  91. try
  92. {
  93. var regExpParseResult = Tokenizer.AdaptRegExp(p, f, compiled: false, parserOptions.RegexTimeout,
  94. ecmaVersion: parserOptions.EcmaVersion,
  95. experimentalESFeatures: parserOptions.ExperimentalESFeatures);
  96. if (!regExpParseResult.Success)
  97. {
  98. ExceptionHelper.ThrowSyntaxError(_realm, $"Unsupported regular expression. {regExpParseResult.ConversionError!.Description}");
  99. }
  100. r.Value = regExpParseResult.Regex!;
  101. r.ParseResult = regExpParseResult;
  102. }
  103. catch (Exception ex)
  104. {
  105. ExceptionHelper.ThrowSyntaxError(_realm, ex.Message);
  106. }
  107. r.Flags = f;
  108. r.Source = p;
  109. RegExpInitialize(r);
  110. return r;
  111. }
  112. private JsRegExp RegExpAlloc(JsValue newTarget)
  113. {
  114. var r = OrdinaryCreateFromConstructor(
  115. newTarget,
  116. static intrinsics => intrinsics.RegExp.PrototypeObject,
  117. static (Engine engine, Realm _, object? _) => new JsRegExp(engine));
  118. return r;
  119. }
  120. public JsRegExp Construct(Regex regExp, string source, string flags, RegExpParseResult regExpParseResult = default)
  121. {
  122. var r = RegExpAlloc(this);
  123. r.Value = regExp;
  124. r.Source = source;
  125. r.Flags = flags;
  126. r.ParseResult = regExpParseResult;
  127. RegExpInitialize(r);
  128. return r;
  129. }
  130. private static void RegExpInitialize(JsRegExp r)
  131. {
  132. r.SetOwnProperty(JsRegExp.PropertyLastIndex, new PropertyDescriptor(0, PropertyFlag.OnlyWritable));
  133. }
  134. }