go.ketch.com/lib/goja@v0.0.1/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 "go.ketch.com/lib/goja/parser" 13 "go.ketch.com/lib/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() valueString { 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) rune { 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 valueString) valueString { 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) valueString { 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 valueString) 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 rune 208 } 209 210 func (s *stringUtf16Reader) ReadRune() (r rune, size int, err error) { 211 if s.second >= 0 { 212 r = s.second 213 s.second = -1 214 size = 1 215 return 216 } 217 if s.pos < len(s.s) { 218 r1, size1 := utf8.DecodeRuneInString(s.s[s.pos:]) 219 s.pos += size1 220 size = 1 221 if r1 <= 0xFFFF { 222 r = r1 223 } else { 224 r, s.second = utf16.EncodeRune(r1) 225 } 226 } else { 227 err = io.EOF 228 } 229 return 230 } 231 232 func (i *importedString) utf16Reader() io.RuneReader { 233 if i.scanned { 234 if i.u != nil { 235 return i.u.utf16Reader() 236 } 237 return asciiString(i.s).utf16Reader() 238 } 239 return &stringUtf16Reader{ 240 s: i.s, 241 second: -1, 242 } 243 } 244 245 func (i *importedString) utf16Runes() []rune { 246 i.ensureScanned() 247 if i.u != nil { 248 return i.u.utf16Runes() 249 } 250 return asciiString(i.s).utf16Runes() 251 } 252 253 func (i *importedString) index(v valueString, start int) int { 254 i.ensureScanned() 255 if i.u != nil { 256 return i.u.index(v, start) 257 } 258 return asciiString(i.s).index(v, start) 259 } 260 261 func (i *importedString) lastIndex(v valueString, pos int) int { 262 i.ensureScanned() 263 if i.u != nil { 264 return i.u.lastIndex(v, pos) 265 } 266 return asciiString(i.s).lastIndex(v, pos) 267 } 268 269 func (i *importedString) toLower() valueString { 270 i.ensureScanned() 271 if i.u != nil { 272 return toLower(i.s) 273 } 274 return asciiString(i.s).toLower() 275 } 276 277 func (i *importedString) toUpper() valueString { 278 i.ensureScanned() 279 if i.u != nil { 280 caser := cases.Upper(language.Und) 281 return newStringValue(caser.String(i.s)) 282 } 283 return asciiString(i.s).toUpper() 284 } 285 286 func (i *importedString) toTrimmedUTF8() string { 287 return strings.Trim(i.s, parser.WhitespaceChars) 288 }