string_imported.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. package goja
  2. import (
  3. "hash/maphash"
  4. "io"
  5. "math"
  6. "reflect"
  7. "strings"
  8. "unicode/utf16"
  9. "unicode/utf8"
  10. "github.com/dop251/goja/parser"
  11. "github.com/dop251/goja/unistring"
  12. "golang.org/x/text/cases"
  13. "golang.org/x/text/language"
  14. )
  15. // Represents a string imported from Go. The idea is to delay the scanning for unicode characters and converting
  16. // to unicodeString until necessary. This way strings that are merely passed through never get scanned which
  17. // saves CPU and memory.
  18. // Currently, importedString is created in 2 cases: Runtime.ToValue() for strings longer than 16 bytes and as a result
  19. // of JSON.stringify() if it may contain unicode characters. More cases could be added in the future.
  20. type importedString struct {
  21. s string
  22. u unicodeString
  23. scanned bool
  24. }
  25. func (i *importedString) scan() {
  26. i.u = unistring.Scan(i.s)
  27. i.scanned = true
  28. }
  29. func (i *importedString) ensureScanned() {
  30. if !i.scanned {
  31. i.scan()
  32. }
  33. }
  34. func (i *importedString) ToInteger() int64 {
  35. i.ensureScanned()
  36. if i.u != nil {
  37. return 0
  38. }
  39. return asciiString(i.s).ToInteger()
  40. }
  41. func (i *importedString) toString() String {
  42. return i
  43. }
  44. func (i *importedString) string() unistring.String {
  45. i.ensureScanned()
  46. if i.u != nil {
  47. return unistring.FromUtf16(i.u)
  48. }
  49. return unistring.String(i.s)
  50. }
  51. func (i *importedString) ToString() Value {
  52. return i
  53. }
  54. func (i *importedString) String() string {
  55. return i.s
  56. }
  57. func (i *importedString) ToFloat() float64 {
  58. i.ensureScanned()
  59. if i.u != nil {
  60. return math.NaN()
  61. }
  62. return asciiString(i.s).ToFloat()
  63. }
  64. func (i *importedString) ToNumber() Value {
  65. i.ensureScanned()
  66. if i.u != nil {
  67. return i.u.ToNumber()
  68. }
  69. return asciiString(i.s).ToNumber()
  70. }
  71. func (i *importedString) ToBoolean() bool {
  72. return len(i.s) != 0
  73. }
  74. func (i *importedString) ToObject(r *Runtime) *Object {
  75. return r._newString(i, r.getStringPrototype())
  76. }
  77. func (i *importedString) SameAs(other Value) bool {
  78. return i.StrictEquals(other)
  79. }
  80. func (i *importedString) Equals(other Value) bool {
  81. if i.StrictEquals(other) {
  82. return true
  83. }
  84. i.ensureScanned()
  85. if i.u != nil {
  86. return i.u.Equals(other)
  87. }
  88. return asciiString(i.s).Equals(other)
  89. }
  90. func (i *importedString) StrictEquals(other Value) bool {
  91. switch otherStr := other.(type) {
  92. case asciiString:
  93. if i.u != nil {
  94. return false
  95. }
  96. return i.s == string(otherStr)
  97. case unicodeString:
  98. i.ensureScanned()
  99. if i.u != nil && i.u.equals(otherStr) {
  100. return true
  101. }
  102. case *importedString:
  103. return i.s == otherStr.s
  104. }
  105. return false
  106. }
  107. func (i *importedString) Export() interface{} {
  108. return i.s
  109. }
  110. func (i *importedString) ExportType() reflect.Type {
  111. return reflectTypeString
  112. }
  113. func (i *importedString) baseObject(r *Runtime) *Object {
  114. i.ensureScanned()
  115. if i.u != nil {
  116. return i.u.baseObject(r)
  117. }
  118. return asciiString(i.s).baseObject(r)
  119. }
  120. func (i *importedString) hash(hasher *maphash.Hash) uint64 {
  121. i.ensureScanned()
  122. if i.u != nil {
  123. return i.u.hash(hasher)
  124. }
  125. return asciiString(i.s).hash(hasher)
  126. }
  127. func (i *importedString) CharAt(idx int) uint16 {
  128. i.ensureScanned()
  129. if i.u != nil {
  130. return i.u.CharAt(idx)
  131. }
  132. return asciiString(i.s).CharAt(idx)
  133. }
  134. func (i *importedString) Length() int {
  135. i.ensureScanned()
  136. if i.u != nil {
  137. return i.u.Length()
  138. }
  139. return asciiString(i.s).Length()
  140. }
  141. func (i *importedString) Concat(v String) String {
  142. if !i.scanned {
  143. if v, ok := v.(*importedString); ok {
  144. if !v.scanned {
  145. return &importedString{s: i.s + v.s}
  146. }
  147. }
  148. i.ensureScanned()
  149. }
  150. if i.u != nil {
  151. return i.u.Concat(v)
  152. }
  153. return asciiString(i.s).Concat(v)
  154. }
  155. func (i *importedString) Substring(start, end int) String {
  156. i.ensureScanned()
  157. if i.u != nil {
  158. return i.u.Substring(start, end)
  159. }
  160. return asciiString(i.s).Substring(start, end)
  161. }
  162. func (i *importedString) CompareTo(v String) int {
  163. return strings.Compare(i.s, v.String())
  164. }
  165. func (i *importedString) Reader() io.RuneReader {
  166. if i.scanned {
  167. if i.u != nil {
  168. return i.u.Reader()
  169. }
  170. return asciiString(i.s).Reader()
  171. }
  172. return strings.NewReader(i.s)
  173. }
  174. type stringUtf16Reader struct {
  175. s string
  176. pos int
  177. second uint16
  178. }
  179. func (s *stringUtf16Reader) readChar() (c uint16, err error) {
  180. if s.second != 0 {
  181. c, s.second = s.second, 0
  182. return
  183. }
  184. if s.pos < len(s.s) {
  185. r1, size1 := utf8.DecodeRuneInString(s.s[s.pos:])
  186. s.pos += size1
  187. if r1 <= 0xFFFF {
  188. c = uint16(r1)
  189. } else {
  190. first, second := utf16.EncodeRune(r1)
  191. c, s.second = uint16(first), uint16(second)
  192. }
  193. } else {
  194. err = io.EOF
  195. }
  196. return
  197. }
  198. func (s *stringUtf16Reader) ReadRune() (r rune, size int, err error) {
  199. c, err := s.readChar()
  200. if err != nil {
  201. return
  202. }
  203. r = rune(c)
  204. size = 1
  205. return
  206. }
  207. func (i *importedString) utf16Reader() utf16Reader {
  208. if i.scanned {
  209. if i.u != nil {
  210. return i.u.utf16Reader()
  211. }
  212. return asciiString(i.s).utf16Reader()
  213. }
  214. return &stringUtf16Reader{
  215. s: i.s,
  216. }
  217. }
  218. func (i *importedString) utf16RuneReader() io.RuneReader {
  219. if i.scanned {
  220. if i.u != nil {
  221. return i.u.utf16RuneReader()
  222. }
  223. return asciiString(i.s).utf16RuneReader()
  224. }
  225. return &stringUtf16Reader{
  226. s: i.s,
  227. }
  228. }
  229. func (i *importedString) utf16Runes() []rune {
  230. i.ensureScanned()
  231. if i.u != nil {
  232. return i.u.utf16Runes()
  233. }
  234. return asciiString(i.s).utf16Runes()
  235. }
  236. func (i *importedString) index(v String, start int) int {
  237. i.ensureScanned()
  238. if i.u != nil {
  239. return i.u.index(v, start)
  240. }
  241. return asciiString(i.s).index(v, start)
  242. }
  243. func (i *importedString) lastIndex(v String, pos int) int {
  244. i.ensureScanned()
  245. if i.u != nil {
  246. return i.u.lastIndex(v, pos)
  247. }
  248. return asciiString(i.s).lastIndex(v, pos)
  249. }
  250. func (i *importedString) toLower() String {
  251. i.ensureScanned()
  252. if i.u != nil {
  253. return toLower(i.s)
  254. }
  255. return asciiString(i.s).toLower()
  256. }
  257. func (i *importedString) toUpper() String {
  258. i.ensureScanned()
  259. if i.u != nil {
  260. caser := cases.Upper(language.Und)
  261. return newStringValue(caser.String(i.s))
  262. }
  263. return asciiString(i.s).toUpper()
  264. }
  265. func (i *importedString) toTrimmedUTF8() string {
  266. return strings.Trim(i.s, parser.WhitespaceChars)
  267. }