string_imported.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  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() valueString {
  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.global.StringPrototype)
  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) rune {
  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 valueString) valueString {
  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) valueString {
  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 valueString) 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 rune
  178. }
  179. func (s *stringUtf16Reader) ReadRune() (r rune, size int, err error) {
  180. if s.second >= 0 {
  181. r = s.second
  182. s.second = -1
  183. size = 1
  184. return
  185. }
  186. if s.pos < len(s.s) {
  187. r1, size1 := utf8.DecodeRuneInString(s.s[s.pos:])
  188. s.pos += size1
  189. size = 1
  190. if r1 <= 0xFFFF {
  191. r = r1
  192. } else {
  193. r, s.second = utf16.EncodeRune(r1)
  194. }
  195. } else {
  196. err = io.EOF
  197. }
  198. return
  199. }
  200. func (i *importedString) utf16Reader() io.RuneReader {
  201. if i.scanned {
  202. if i.u != nil {
  203. return i.u.utf16Reader()
  204. }
  205. return asciiString(i.s).utf16Reader()
  206. }
  207. return &stringUtf16Reader{
  208. s: i.s,
  209. second: -1,
  210. }
  211. }
  212. func (i *importedString) utf16Runes() []rune {
  213. i.ensureScanned()
  214. if i.u != nil {
  215. return i.u.utf16Runes()
  216. }
  217. return asciiString(i.s).utf16Runes()
  218. }
  219. func (i *importedString) index(v valueString, start int) int {
  220. i.ensureScanned()
  221. if i.u != nil {
  222. return i.u.index(v, start)
  223. }
  224. return asciiString(i.s).index(v, start)
  225. }
  226. func (i *importedString) lastIndex(v valueString, pos int) int {
  227. i.ensureScanned()
  228. if i.u != nil {
  229. return i.u.lastIndex(v, pos)
  230. }
  231. return asciiString(i.s).lastIndex(v, pos)
  232. }
  233. func (i *importedString) toLower() valueString {
  234. i.ensureScanned()
  235. if i.u != nil {
  236. return toLower(i.s)
  237. }
  238. return asciiString(i.s).toLower()
  239. }
  240. func (i *importedString) toUpper() valueString {
  241. i.ensureScanned()
  242. if i.u != nil {
  243. caser := cases.Upper(language.Und)
  244. return newStringValue(caser.String(i.s))
  245. }
  246. return asciiString(i.s).toUpper()
  247. }
  248. func (i *importedString) toTrimmedUTF8() string {
  249. return strings.Trim(i.s, parser.WhitespaceChars)
  250. }