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