builtin_regexp.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. package goja
  2. import (
  3. "fmt"
  4. "github.com/dlclark/regexp2"
  5. "github.com/dop251/goja/parser"
  6. "regexp"
  7. )
  8. func (r *Runtime) newRegexpObject(proto *Object) *regexpObject {
  9. v := &Object{runtime: r}
  10. o := &regexpObject{}
  11. o.class = classRegExp
  12. o.val = v
  13. o.extensible = true
  14. v.self = o
  15. o.prototype = proto
  16. o.init()
  17. return o
  18. }
  19. func (r *Runtime) newRegExpp(pattern regexpPattern, patternStr valueString, global, ignoreCase, multiline bool, proto *Object) *Object {
  20. o := r.newRegexpObject(proto)
  21. o.pattern = pattern
  22. o.source = patternStr
  23. o.global = global
  24. o.ignoreCase = ignoreCase
  25. o.multiline = multiline
  26. return o.val
  27. }
  28. func compileRegexp(patternStr, flags string) (p regexpPattern, global, ignoreCase, multiline bool, err error) {
  29. if flags != "" {
  30. invalidFlags := func() {
  31. err = fmt.Errorf("Invalid flags supplied to RegExp constructor '%s'", flags)
  32. }
  33. for _, chr := range flags {
  34. switch chr {
  35. case 'g':
  36. if global {
  37. invalidFlags()
  38. return
  39. }
  40. global = true
  41. case 'm':
  42. if multiline {
  43. invalidFlags()
  44. return
  45. }
  46. multiline = true
  47. case 'i':
  48. if ignoreCase {
  49. invalidFlags()
  50. return
  51. }
  52. ignoreCase = true
  53. default:
  54. invalidFlags()
  55. return
  56. }
  57. }
  58. }
  59. re2Str, err1 := parser.TransformRegExp(patternStr)
  60. if /*false &&*/ err1 == nil {
  61. re2flags := ""
  62. if multiline {
  63. re2flags += "m"
  64. }
  65. if ignoreCase {
  66. re2flags += "i"
  67. }
  68. if len(re2flags) > 0 {
  69. re2Str = fmt.Sprintf("(?%s:%s)", re2flags, re2Str)
  70. }
  71. pattern, err1 := regexp.Compile(re2Str)
  72. if err1 != nil {
  73. err = fmt.Errorf("Invalid regular expression (re2): %s (%v)", re2Str, err1)
  74. return
  75. }
  76. p = (*regexpWrapper)(pattern)
  77. } else {
  78. var opts regexp2.RegexOptions = regexp2.ECMAScript
  79. if multiline {
  80. opts |= regexp2.Multiline
  81. }
  82. if ignoreCase {
  83. opts |= regexp2.IgnoreCase
  84. }
  85. regexp2Pattern, err1 := regexp2.Compile(patternStr, opts)
  86. if err1 != nil {
  87. err = fmt.Errorf("Invalid regular expression (regexp2): %s (%v)", patternStr, err1)
  88. return
  89. }
  90. p = (*regexp2Wrapper)(regexp2Pattern)
  91. }
  92. return
  93. }
  94. func (r *Runtime) newRegExp(patternStr valueString, flags string, proto *Object) *Object {
  95. pattern, global, ignoreCase, multiline, err := compileRegexp(patternStr.String(), flags)
  96. if err != nil {
  97. panic(r.newSyntaxError(err.Error(), -1))
  98. }
  99. return r.newRegExpp(pattern, patternStr, global, ignoreCase, multiline, proto)
  100. }
  101. func (r *Runtime) builtin_newRegExp(args []Value) *Object {
  102. var pattern valueString
  103. var flags string
  104. if len(args) > 0 {
  105. if obj, ok := args[0].(*Object); ok {
  106. if regexp, ok := obj.self.(*regexpObject); ok {
  107. if len(args) < 2 || args[1] == _undefined {
  108. return regexp.clone()
  109. } else {
  110. return r.newRegExp(regexp.source, args[1].String(), r.global.RegExpPrototype)
  111. }
  112. }
  113. }
  114. if args[0] != _undefined {
  115. pattern = args[0].ToString()
  116. }
  117. }
  118. if len(args) > 1 {
  119. if a := args[1]; a != _undefined {
  120. flags = a.String()
  121. }
  122. }
  123. if pattern == nil {
  124. pattern = stringEmpty
  125. }
  126. return r.newRegExp(pattern, flags, r.global.RegExpPrototype)
  127. }
  128. func (r *Runtime) builtin_RegExp(call FunctionCall) Value {
  129. flags := call.Argument(1)
  130. if flags == _undefined {
  131. if obj, ok := call.Argument(0).(*Object); ok {
  132. if _, ok := obj.self.(*regexpObject); ok {
  133. return call.Arguments[0]
  134. }
  135. }
  136. }
  137. return r.builtin_newRegExp(call.Arguments)
  138. }
  139. func (r *Runtime) regexpproto_exec(call FunctionCall) Value {
  140. if this, ok := r.toObject(call.This).self.(*regexpObject); ok {
  141. return this.exec(call.Argument(0).ToString())
  142. } else {
  143. r.typeErrorResult(true, "Method RegExp.prototype.exec called on incompatible receiver %s", call.This.ToString())
  144. return nil
  145. }
  146. }
  147. func (r *Runtime) regexpproto_test(call FunctionCall) Value {
  148. if this, ok := r.toObject(call.This).self.(*regexpObject); ok {
  149. if this.test(call.Argument(0).ToString()) {
  150. return valueTrue
  151. } else {
  152. return valueFalse
  153. }
  154. } else {
  155. r.typeErrorResult(true, "Method RegExp.prototype.test called on incompatible receiver %s", call.This.ToString())
  156. return nil
  157. }
  158. }
  159. func (r *Runtime) regexpproto_toString(call FunctionCall) Value {
  160. if this, ok := r.toObject(call.This).self.(*regexpObject); ok {
  161. var g, i, m string
  162. if this.global {
  163. g = "g"
  164. }
  165. if this.ignoreCase {
  166. i = "i"
  167. }
  168. if this.multiline {
  169. m = "m"
  170. }
  171. return newStringValue(fmt.Sprintf("/%s/%s%s%s", this.source.String(), g, i, m))
  172. } else {
  173. r.typeErrorResult(true, "Method RegExp.prototype.toString called on incompatible receiver %s", call.This)
  174. return nil
  175. }
  176. }
  177. func (r *Runtime) regexpproto_getSource(call FunctionCall) Value {
  178. if this, ok := r.toObject(call.This).self.(*regexpObject); ok {
  179. return this.source
  180. } else {
  181. r.typeErrorResult(true, "Method RegExp.prototype.source getter called on incompatible receiver %s", call.This.ToString())
  182. return nil
  183. }
  184. }
  185. func (r *Runtime) regexpproto_getGlobal(call FunctionCall) Value {
  186. if this, ok := r.toObject(call.This).self.(*regexpObject); ok {
  187. if this.global {
  188. return valueTrue
  189. } else {
  190. return valueFalse
  191. }
  192. } else {
  193. r.typeErrorResult(true, "Method RegExp.prototype.global getter called on incompatible receiver %s", call.This.ToString())
  194. return nil
  195. }
  196. }
  197. func (r *Runtime) regexpproto_getMultiline(call FunctionCall) Value {
  198. if this, ok := r.toObject(call.This).self.(*regexpObject); ok {
  199. if this.multiline {
  200. return valueTrue
  201. } else {
  202. return valueFalse
  203. }
  204. } else {
  205. r.typeErrorResult(true, "Method RegExp.prototype.multiline getter called on incompatible receiver %s", call.This.ToString())
  206. return nil
  207. }
  208. }
  209. func (r *Runtime) regexpproto_getIgnoreCase(call FunctionCall) Value {
  210. if this, ok := r.toObject(call.This).self.(*regexpObject); ok {
  211. if this.ignoreCase {
  212. return valueTrue
  213. } else {
  214. return valueFalse
  215. }
  216. } else {
  217. r.typeErrorResult(true, "Method RegExp.prototype.ignoreCase getter called on incompatible receiver %s", call.This.ToString())
  218. return nil
  219. }
  220. }
  221. func (r *Runtime) initRegExp() {
  222. r.global.RegExpPrototype = r.NewObject()
  223. o := r.global.RegExpPrototype.self
  224. o._putProp("exec", r.newNativeFunc(r.regexpproto_exec, nil, "exec", nil, 1), true, false, true)
  225. o._putProp("test", r.newNativeFunc(r.regexpproto_test, nil, "test", nil, 1), true, false, true)
  226. o._putProp("toString", r.newNativeFunc(r.regexpproto_toString, nil, "toString", nil, 0), true, false, true)
  227. o.putStr("source", &valueProperty{
  228. configurable: true,
  229. getterFunc: r.newNativeFunc(r.regexpproto_getSource, nil, "get source", nil, 0),
  230. accessor: true,
  231. }, false)
  232. o.putStr("global", &valueProperty{
  233. configurable: true,
  234. getterFunc: r.newNativeFunc(r.regexpproto_getGlobal, nil, "get global", nil, 0),
  235. accessor: true,
  236. }, false)
  237. o.putStr("multiline", &valueProperty{
  238. configurable: true,
  239. getterFunc: r.newNativeFunc(r.regexpproto_getMultiline, nil, "get multiline", nil, 0),
  240. accessor: true,
  241. }, false)
  242. o.putStr("ignoreCase", &valueProperty{
  243. configurable: true,
  244. getterFunc: r.newNativeFunc(r.regexpproto_getIgnoreCase, nil, "get ignoreCase", nil, 0),
  245. accessor: true,
  246. }, false)
  247. r.global.RegExp = r.newNativeFunc(r.builtin_RegExp, r.builtin_newRegExp, "RegExp", r.global.RegExpPrototype, 2)
  248. r.addToGlobal("RegExp", r.global.RegExp)
  249. }