github.com/nuvolaris/goja@v0.0.0-20230825100449-967811910c6d/string.go (about) 1 package goja 2 3 import ( 4 "io" 5 "strconv" 6 "strings" 7 "unicode/utf8" 8 9 "github.com/nuvolaris/goja/unistring" 10 ) 11 12 const ( 13 __proto__ = "__proto__" 14 ) 15 16 var ( 17 stringTrue String = asciiString("true") 18 stringFalse String = asciiString("false") 19 stringNull String = asciiString("null") 20 stringUndefined String = asciiString("undefined") 21 stringObjectC String = asciiString("object") 22 stringFunction String = asciiString("function") 23 stringBoolean String = asciiString("boolean") 24 stringString String = asciiString("string") 25 stringSymbol String = asciiString("symbol") 26 stringNumber String = asciiString("number") 27 stringNaN String = asciiString("NaN") 28 stringInfinity = asciiString("Infinity") 29 stringNegInfinity = asciiString("-Infinity") 30 stringBound_ String = asciiString("bound ") 31 stringEmpty String = asciiString("") 32 33 stringError String = asciiString("Error") 34 stringAggregateError String = asciiString("AggregateError") 35 stringTypeError String = asciiString("TypeError") 36 stringReferenceError String = asciiString("ReferenceError") 37 stringSyntaxError String = asciiString("SyntaxError") 38 stringRangeError String = asciiString("RangeError") 39 stringEvalError String = asciiString("EvalError") 40 stringURIError String = asciiString("URIError") 41 stringGoError String = asciiString("GoError") 42 43 stringObjectNull String = asciiString("[object Null]") 44 stringObjectUndefined String = asciiString("[object Undefined]") 45 stringInvalidDate String = asciiString("Invalid Date") 46 ) 47 48 type utf16Reader interface { 49 readChar() (c uint16, err error) 50 } 51 52 // String represents an ECMAScript string Value. Its internal representation depends on the contents of the 53 // string, but in any case it is capable of holding any UTF-16 string, either valid or invalid. 54 // Instances of this type, as any other primitive values, are goroutine-safe and can be passed between runtimes. 55 // Strings can be created using Runtime.ToValue(goString) or StringFromUTF16. 56 type String interface { 57 Value 58 CharAt(int) uint16 59 Length() int 60 Concat(String) String 61 Substring(start, end int) String 62 CompareTo(String) int 63 Reader() io.RuneReader 64 utf16Reader() utf16Reader 65 utf16RuneReader() io.RuneReader 66 utf16Runes() []rune 67 index(String, int) int 68 lastIndex(String, int) int 69 toLower() String 70 toUpper() String 71 toTrimmedUTF8() string 72 } 73 74 type stringIterObject struct { 75 baseObject 76 reader io.RuneReader 77 } 78 79 func isUTF16FirstSurrogate(c uint16) bool { 80 return c >= 0xD800 && c <= 0xDBFF 81 } 82 83 func isUTF16SecondSurrogate(c uint16) bool { 84 return c >= 0xDC00 && c <= 0xDFFF 85 } 86 87 func (si *stringIterObject) next() Value { 88 if si.reader == nil { 89 return si.val.runtime.createIterResultObject(_undefined, true) 90 } 91 r, _, err := si.reader.ReadRune() 92 if err == io.EOF { 93 si.reader = nil 94 return si.val.runtime.createIterResultObject(_undefined, true) 95 } 96 return si.val.runtime.createIterResultObject(stringFromRune(r), false) 97 } 98 99 func stringFromRune(r rune) String { 100 if r < utf8.RuneSelf { 101 var sb strings.Builder 102 sb.WriteByte(byte(r)) 103 return asciiString(sb.String()) 104 } 105 var sb unicodeStringBuilder 106 sb.WriteRune(r) 107 return sb.String() 108 } 109 110 func (r *Runtime) createStringIterator(s String) Value { 111 o := &Object{runtime: r} 112 113 si := &stringIterObject{ 114 reader: &lenientUtf16Decoder{utf16Reader: s.utf16Reader()}, 115 } 116 si.class = classObject 117 si.val = o 118 si.extensible = true 119 o.self = si 120 si.prototype = r.getStringIteratorPrototype() 121 si.init() 122 123 return o 124 } 125 126 type stringObject struct { 127 baseObject 128 value String 129 length int 130 lengthProp valueProperty 131 } 132 133 func newStringValue(s string) String { 134 if u := unistring.Scan(s); u != nil { 135 return unicodeString(u) 136 } 137 return asciiString(s) 138 } 139 140 func stringValueFromRaw(raw unistring.String) String { 141 if b := raw.AsUtf16(); b != nil { 142 return unicodeString(b) 143 } 144 return asciiString(raw) 145 } 146 147 func (s *stringObject) init() { 148 s.baseObject.init() 149 s.setLength() 150 } 151 152 func (s *stringObject) setLength() { 153 if s.value != nil { 154 s.length = s.value.Length() 155 } 156 s.lengthProp.value = intToValue(int64(s.length)) 157 s._put("length", &s.lengthProp) 158 } 159 160 func (s *stringObject) getStr(name unistring.String, receiver Value) Value { 161 if i := strToGoIdx(name); i >= 0 && i < s.length { 162 return s._getIdx(i) 163 } 164 return s.baseObject.getStr(name, receiver) 165 } 166 167 func (s *stringObject) getIdx(idx valueInt, receiver Value) Value { 168 i := int(idx) 169 if i >= 0 && i < s.length { 170 return s._getIdx(i) 171 } 172 return s.baseObject.getStr(idx.string(), receiver) 173 } 174 175 func (s *stringObject) getOwnPropStr(name unistring.String) Value { 176 if i := strToGoIdx(name); i >= 0 && i < s.length { 177 val := s._getIdx(i) 178 return &valueProperty{ 179 value: val, 180 enumerable: true, 181 } 182 } 183 184 return s.baseObject.getOwnPropStr(name) 185 } 186 187 func (s *stringObject) getOwnPropIdx(idx valueInt) Value { 188 i := int64(idx) 189 if i >= 0 { 190 if i < int64(s.length) { 191 val := s._getIdx(int(i)) 192 return &valueProperty{ 193 value: val, 194 enumerable: true, 195 } 196 } 197 return nil 198 } 199 200 return s.baseObject.getOwnPropStr(idx.string()) 201 } 202 203 func (s *stringObject) _getIdx(idx int) Value { 204 return s.value.Substring(idx, idx+1) 205 } 206 207 func (s *stringObject) setOwnStr(name unistring.String, val Value, throw bool) bool { 208 if i := strToGoIdx(name); i >= 0 && i < s.length { 209 s.val.runtime.typeErrorResult(throw, "Cannot assign to read only property '%d' of a String", i) 210 return false 211 } 212 213 return s.baseObject.setOwnStr(name, val, throw) 214 } 215 216 func (s *stringObject) setOwnIdx(idx valueInt, val Value, throw bool) bool { 217 i := int64(idx) 218 if i >= 0 && i < int64(s.length) { 219 s.val.runtime.typeErrorResult(throw, "Cannot assign to read only property '%d' of a String", i) 220 return false 221 } 222 223 return s.baseObject.setOwnStr(idx.string(), val, throw) 224 } 225 226 func (s *stringObject) setForeignStr(name unistring.String, val, receiver Value, throw bool) (bool, bool) { 227 return s._setForeignStr(name, s.getOwnPropStr(name), val, receiver, throw) 228 } 229 230 func (s *stringObject) setForeignIdx(idx valueInt, val, receiver Value, throw bool) (bool, bool) { 231 return s._setForeignIdx(idx, s.getOwnPropIdx(idx), val, receiver, throw) 232 } 233 234 func (s *stringObject) defineOwnPropertyStr(name unistring.String, descr PropertyDescriptor, throw bool) bool { 235 if i := strToGoIdx(name); i >= 0 && i < s.length { 236 _, ok := s._defineOwnProperty(name, &valueProperty{enumerable: true}, descr, throw) 237 return ok 238 } 239 240 return s.baseObject.defineOwnPropertyStr(name, descr, throw) 241 } 242 243 func (s *stringObject) defineOwnPropertyIdx(idx valueInt, descr PropertyDescriptor, throw bool) bool { 244 i := int64(idx) 245 if i >= 0 && i < int64(s.length) { 246 s.val.runtime.typeErrorResult(throw, "Cannot redefine property: %d", i) 247 return false 248 } 249 250 return s.baseObject.defineOwnPropertyStr(idx.string(), descr, throw) 251 } 252 253 type stringPropIter struct { 254 str String // separate, because obj can be the singleton 255 obj *stringObject 256 idx, length int 257 } 258 259 func (i *stringPropIter) next() (propIterItem, iterNextFunc) { 260 if i.idx < i.length { 261 name := strconv.Itoa(i.idx) 262 i.idx++ 263 return propIterItem{name: asciiString(name), enumerable: _ENUM_TRUE}, i.next 264 } 265 266 return i.obj.baseObject.iterateStringKeys()() 267 } 268 269 func (s *stringObject) iterateStringKeys() iterNextFunc { 270 return (&stringPropIter{ 271 str: s.value, 272 obj: s, 273 length: s.length, 274 }).next 275 } 276 277 func (s *stringObject) stringKeys(all bool, accum []Value) []Value { 278 for i := 0; i < s.length; i++ { 279 accum = append(accum, asciiString(strconv.Itoa(i))) 280 } 281 282 return s.baseObject.stringKeys(all, accum) 283 } 284 285 func (s *stringObject) deleteStr(name unistring.String, throw bool) bool { 286 if i := strToGoIdx(name); i >= 0 && i < s.length { 287 s.val.runtime.typeErrorResult(throw, "Cannot delete property '%d' of a String", i) 288 return false 289 } 290 291 return s.baseObject.deleteStr(name, throw) 292 } 293 294 func (s *stringObject) deleteIdx(idx valueInt, throw bool) bool { 295 i := int64(idx) 296 if i >= 0 && i < int64(s.length) { 297 s.val.runtime.typeErrorResult(throw, "Cannot delete property '%d' of a String", i) 298 return false 299 } 300 301 return s.baseObject.deleteStr(idx.string(), throw) 302 } 303 304 func (s *stringObject) hasOwnPropertyStr(name unistring.String) bool { 305 if i := strToGoIdx(name); i >= 0 && i < s.length { 306 return true 307 } 308 return s.baseObject.hasOwnPropertyStr(name) 309 } 310 311 func (s *stringObject) hasOwnPropertyIdx(idx valueInt) bool { 312 i := int64(idx) 313 if i >= 0 && i < int64(s.length) { 314 return true 315 } 316 return s.baseObject.hasOwnPropertyStr(idx.string()) 317 } 318 319 func devirtualizeString(s String) (asciiString, unicodeString) { 320 switch s := s.(type) { 321 case asciiString: 322 return s, nil 323 case unicodeString: 324 return "", s 325 case *importedString: 326 s.ensureScanned() 327 if s.u != nil { 328 return "", s.u 329 } 330 return asciiString(s.s), nil 331 default: 332 panic(unknownStringTypeErr(s)) 333 } 334 } 335 336 func unknownStringTypeErr(v Value) interface{} { 337 return newTypeError("Internal bug: unknown string type: %T", v) 338 } 339 340 // StringFromUTF16 creates a string value from an array of UTF-16 code units. The result is a copy, so the initial 341 // slice can be modified after calling this function (but it must not be modified while the function is running). 342 // No validation of any kind is performed. 343 func StringFromUTF16(chars []uint16) String { 344 isAscii := true 345 for _, c := range chars { 346 if c >= utf8.RuneSelf { 347 isAscii = false 348 break 349 } 350 } 351 if isAscii { 352 var sb strings.Builder 353 sb.Grow(len(chars)) 354 for _, c := range chars { 355 sb.WriteByte(byte(c)) 356 } 357 return asciiString(sb.String()) 358 } 359 buf := make([]uint16, len(chars)+1) 360 buf[0] = unistring.BOM 361 copy(buf[1:], chars) 362 return unicodeString(buf) 363 }