github.com/nuvolaris/goja@v0.0.0-20230825100449-967811910c6d/builtin_global.go (about) 1 package goja 2 3 import ( 4 "errors" 5 "io" 6 "math" 7 "regexp" 8 "strconv" 9 "strings" 10 "unicode/utf8" 11 12 "github.com/nuvolaris/goja/unistring" 13 ) 14 15 const hexUpper = "0123456789ABCDEF" 16 17 var ( 18 parseFloatRegexp = regexp.MustCompile(`^([+-]?(?:Infinity|[0-9]*\.?[0-9]*(?:[eE][+-]?[0-9]+)?))`) 19 ) 20 21 func (r *Runtime) builtin_isNaN(call FunctionCall) Value { 22 if math.IsNaN(call.Argument(0).ToFloat()) { 23 return valueTrue 24 } else { 25 return valueFalse 26 } 27 } 28 29 func (r *Runtime) builtin_parseInt(call FunctionCall) Value { 30 str := call.Argument(0).toString().toTrimmedUTF8() 31 radix := int(toInt32(call.Argument(1))) 32 v, _ := parseInt(str, radix) 33 return v 34 } 35 36 func (r *Runtime) builtin_parseFloat(call FunctionCall) Value { 37 m := parseFloatRegexp.FindStringSubmatch(call.Argument(0).toString().toTrimmedUTF8()) 38 if len(m) == 2 { 39 if s := m[1]; s != "" && s != "+" && s != "-" { 40 switch s { 41 case "+", "-": 42 case "Infinity", "+Infinity": 43 return _positiveInf 44 case "-Infinity": 45 return _negativeInf 46 default: 47 f, err := strconv.ParseFloat(s, 64) 48 if err == nil || isRangeErr(err) { 49 return floatToValue(f) 50 } 51 } 52 } 53 } 54 return _NaN 55 } 56 57 func (r *Runtime) builtin_isFinite(call FunctionCall) Value { 58 f := call.Argument(0).ToFloat() 59 if math.IsNaN(f) || math.IsInf(f, 0) { 60 return valueFalse 61 } 62 return valueTrue 63 } 64 65 func (r *Runtime) _encode(uriString String, unescaped *[256]bool) String { 66 reader := uriString.Reader() 67 utf8Buf := make([]byte, utf8.UTFMax) 68 needed := false 69 l := 0 70 for { 71 rn, _, err := reader.ReadRune() 72 if err != nil { 73 if err != io.EOF { 74 panic(r.newError(r.global.URIError, "Malformed URI")) 75 } 76 break 77 } 78 79 if rn >= utf8.RuneSelf { 80 needed = true 81 l += utf8.EncodeRune(utf8Buf, rn) * 3 82 } else if !unescaped[rn] { 83 needed = true 84 l += 3 85 } else { 86 l++ 87 } 88 } 89 90 if !needed { 91 return uriString 92 } 93 94 buf := make([]byte, l) 95 i := 0 96 reader = uriString.Reader() 97 for { 98 rn, _, err := reader.ReadRune() 99 if err == io.EOF { 100 break 101 } 102 103 if rn >= utf8.RuneSelf { 104 n := utf8.EncodeRune(utf8Buf, rn) 105 for _, b := range utf8Buf[:n] { 106 buf[i] = '%' 107 buf[i+1] = hexUpper[b>>4] 108 buf[i+2] = hexUpper[b&15] 109 i += 3 110 } 111 } else if !unescaped[rn] { 112 buf[i] = '%' 113 buf[i+1] = hexUpper[rn>>4] 114 buf[i+2] = hexUpper[rn&15] 115 i += 3 116 } else { 117 buf[i] = byte(rn) 118 i++ 119 } 120 } 121 return asciiString(buf) 122 } 123 124 func (r *Runtime) _decode(sv String, reservedSet *[256]bool) String { 125 s := sv.String() 126 hexCount := 0 127 for i := 0; i < len(s); { 128 switch s[i] { 129 case '%': 130 if i+2 >= len(s) || !ishex(s[i+1]) || !ishex(s[i+2]) { 131 panic(r.newError(r.global.URIError, "Malformed URI")) 132 } 133 c := unhex(s[i+1])<<4 | unhex(s[i+2]) 134 if !reservedSet[c] { 135 hexCount++ 136 } 137 i += 3 138 default: 139 i++ 140 } 141 } 142 143 if hexCount == 0 { 144 return sv 145 } 146 147 t := make([]byte, len(s)-hexCount*2) 148 j := 0 149 isUnicode := false 150 for i := 0; i < len(s); { 151 ch := s[i] 152 switch ch { 153 case '%': 154 c := unhex(s[i+1])<<4 | unhex(s[i+2]) 155 if reservedSet[c] { 156 t[j] = s[i] 157 t[j+1] = s[i+1] 158 t[j+2] = s[i+2] 159 j += 3 160 } else { 161 t[j] = c 162 if c >= utf8.RuneSelf { 163 isUnicode = true 164 } 165 j++ 166 } 167 i += 3 168 default: 169 if ch >= utf8.RuneSelf { 170 isUnicode = true 171 } 172 t[j] = ch 173 j++ 174 i++ 175 } 176 } 177 178 if !isUnicode { 179 return asciiString(t) 180 } 181 182 us := make([]rune, 0, len(s)) 183 for len(t) > 0 { 184 rn, size := utf8.DecodeRune(t) 185 if rn == utf8.RuneError { 186 if size != 3 || t[0] != 0xef || t[1] != 0xbf || t[2] != 0xbd { 187 panic(r.newError(r.global.URIError, "Malformed URI")) 188 } 189 } 190 us = append(us, rn) 191 t = t[size:] 192 } 193 return unicodeStringFromRunes(us) 194 } 195 196 func ishex(c byte) bool { 197 switch { 198 case '0' <= c && c <= '9': 199 return true 200 case 'a' <= c && c <= 'f': 201 return true 202 case 'A' <= c && c <= 'F': 203 return true 204 } 205 return false 206 } 207 208 func unhex(c byte) byte { 209 switch { 210 case '0' <= c && c <= '9': 211 return c - '0' 212 case 'a' <= c && c <= 'f': 213 return c - 'a' + 10 214 case 'A' <= c && c <= 'F': 215 return c - 'A' + 10 216 } 217 return 0 218 } 219 220 func (r *Runtime) builtin_decodeURI(call FunctionCall) Value { 221 uriString := call.Argument(0).toString() 222 return r._decode(uriString, &uriReservedHash) 223 } 224 225 func (r *Runtime) builtin_decodeURIComponent(call FunctionCall) Value { 226 uriString := call.Argument(0).toString() 227 return r._decode(uriString, &emptyEscapeSet) 228 } 229 230 func (r *Runtime) builtin_encodeURI(call FunctionCall) Value { 231 uriString := call.Argument(0).toString() 232 return r._encode(uriString, &uriReservedUnescapedHash) 233 } 234 235 func (r *Runtime) builtin_encodeURIComponent(call FunctionCall) Value { 236 uriString := call.Argument(0).toString() 237 return r._encode(uriString, &uriUnescaped) 238 } 239 240 func (r *Runtime) builtin_escape(call FunctionCall) Value { 241 s := call.Argument(0).toString() 242 var sb strings.Builder 243 l := s.Length() 244 for i := 0; i < l; i++ { 245 r := s.CharAt(i) 246 if r >= 'A' && r <= 'Z' || r >= 'a' && r <= 'z' || r >= '0' && r <= '9' || 247 r == '@' || r == '*' || r == '_' || r == '+' || r == '-' || r == '.' || r == '/' { 248 sb.WriteByte(byte(r)) 249 } else if r <= 0xff { 250 sb.WriteByte('%') 251 sb.WriteByte(hexUpper[r>>4]) 252 sb.WriteByte(hexUpper[r&0xf]) 253 } else { 254 sb.WriteString("%u") 255 sb.WriteByte(hexUpper[r>>12]) 256 sb.WriteByte(hexUpper[(r>>8)&0xf]) 257 sb.WriteByte(hexUpper[(r>>4)&0xf]) 258 sb.WriteByte(hexUpper[r&0xf]) 259 } 260 } 261 return asciiString(sb.String()) 262 } 263 264 func (r *Runtime) builtin_unescape(call FunctionCall) Value { 265 s := call.Argument(0).toString() 266 l := s.Length() 267 var asciiBuf []byte 268 var unicodeBuf []uint16 269 _, u := devirtualizeString(s) 270 unicode := u != nil 271 if unicode { 272 unicodeBuf = make([]uint16, 1, l+1) 273 unicodeBuf[0] = unistring.BOM 274 } else { 275 asciiBuf = make([]byte, 0, l) 276 } 277 for i := 0; i < l; { 278 r := s.CharAt(i) 279 if r == '%' { 280 if i <= l-6 && s.CharAt(i+1) == 'u' { 281 c0 := s.CharAt(i + 2) 282 c1 := s.CharAt(i + 3) 283 c2 := s.CharAt(i + 4) 284 c3 := s.CharAt(i + 5) 285 if c0 <= 0xff && ishex(byte(c0)) && 286 c1 <= 0xff && ishex(byte(c1)) && 287 c2 <= 0xff && ishex(byte(c2)) && 288 c3 <= 0xff && ishex(byte(c3)) { 289 r = uint16(unhex(byte(c0)))<<12 | 290 uint16(unhex(byte(c1)))<<8 | 291 uint16(unhex(byte(c2)))<<4 | 292 uint16(unhex(byte(c3))) 293 i += 5 294 goto out 295 } 296 } 297 if i <= l-3 { 298 c0 := s.CharAt(i + 1) 299 c1 := s.CharAt(i + 2) 300 if c0 <= 0xff && ishex(byte(c0)) && 301 c1 <= 0xff && ishex(byte(c1)) { 302 r = uint16(unhex(byte(c0))<<4 | unhex(byte(c1))) 303 i += 2 304 } 305 } 306 } 307 out: 308 if r >= utf8.RuneSelf && !unicode { 309 unicodeBuf = make([]uint16, 1, l+1) 310 unicodeBuf[0] = unistring.BOM 311 for _, b := range asciiBuf { 312 unicodeBuf = append(unicodeBuf, uint16(b)) 313 } 314 asciiBuf = nil 315 unicode = true 316 } 317 if unicode { 318 unicodeBuf = append(unicodeBuf, r) 319 } else { 320 asciiBuf = append(asciiBuf, byte(r)) 321 } 322 i++ 323 } 324 if unicode { 325 return unicodeString(unicodeBuf) 326 } 327 328 return asciiString(asciiBuf) 329 } 330 331 func (r *Runtime) initGlobalObject() { 332 o := r.globalObject.self 333 o._putProp("globalThis", r.globalObject, true, false, true) 334 o._putProp("NaN", _NaN, false, false, false) 335 o._putProp("undefined", _undefined, false, false, false) 336 o._putProp("Infinity", _positiveInf, false, false, false) 337 338 o._putProp("isNaN", r.newNativeFunc(r.builtin_isNaN, nil, "isNaN", nil, 1), true, false, true) 339 o._putProp("parseInt", r.newNativeFunc(r.builtin_parseInt, nil, "parseInt", nil, 2), true, false, true) 340 o._putProp("parseFloat", r.newNativeFunc(r.builtin_parseFloat, nil, "parseFloat", nil, 1), true, false, true) 341 o._putProp("isFinite", r.newNativeFunc(r.builtin_isFinite, nil, "isFinite", nil, 1), true, false, true) 342 o._putProp("decodeURI", r.newNativeFunc(r.builtin_decodeURI, nil, "decodeURI", nil, 1), true, false, true) 343 o._putProp("decodeURIComponent", r.newNativeFunc(r.builtin_decodeURIComponent, nil, "decodeURIComponent", nil, 1), true, false, true) 344 o._putProp("encodeURI", r.newNativeFunc(r.builtin_encodeURI, nil, "encodeURI", nil, 1), true, false, true) 345 o._putProp("encodeURIComponent", r.newNativeFunc(r.builtin_encodeURIComponent, nil, "encodeURIComponent", nil, 1), true, false, true) 346 o._putProp("escape", r.newNativeFunc(r.builtin_escape, nil, "escape", nil, 1), true, false, true) 347 o._putProp("unescape", r.newNativeFunc(r.builtin_unescape, nil, "unescape", nil, 1), true, false, true) 348 349 o._putSym(SymToStringTag, valueProp(asciiString(classGlobal), false, false, true)) 350 351 // TODO: Annex B 352 353 } 354 355 func digitVal(d byte) int { 356 var v byte 357 switch { 358 case '0' <= d && d <= '9': 359 v = d - '0' 360 case 'a' <= d && d <= 'z': 361 v = d - 'a' + 10 362 case 'A' <= d && d <= 'Z': 363 v = d - 'A' + 10 364 default: 365 return 36 366 } 367 return int(v) 368 } 369 370 // ECMAScript compatible version of strconv.ParseInt 371 func parseInt(s string, base int) (Value, error) { 372 var n int64 373 var err error 374 var cutoff, maxVal int64 375 var sign bool 376 i := 0 377 378 if len(s) < 1 { 379 err = strconv.ErrSyntax 380 goto Error 381 } 382 383 switch s[0] { 384 case '-': 385 sign = true 386 s = s[1:] 387 case '+': 388 s = s[1:] 389 } 390 391 if len(s) < 1 { 392 err = strconv.ErrSyntax 393 goto Error 394 } 395 396 // Look for hex prefix. 397 if s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X') { 398 if base == 0 || base == 16 { 399 base = 16 400 s = s[2:] 401 } 402 } 403 404 switch { 405 case len(s) < 1: 406 err = strconv.ErrSyntax 407 goto Error 408 409 case 2 <= base && base <= 36: 410 // valid base; nothing to do 411 412 case base == 0: 413 // Look for hex prefix. 414 switch { 415 case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'): 416 if len(s) < 3 { 417 err = strconv.ErrSyntax 418 goto Error 419 } 420 base = 16 421 s = s[2:] 422 default: 423 base = 10 424 } 425 426 default: 427 err = errors.New("invalid base " + strconv.Itoa(base)) 428 goto Error 429 } 430 431 // Cutoff is the smallest number such that cutoff*base > maxInt64. 432 // Use compile-time constants for common cases. 433 switch base { 434 case 10: 435 cutoff = math.MaxInt64/10 + 1 436 case 16: 437 cutoff = math.MaxInt64/16 + 1 438 default: 439 cutoff = math.MaxInt64/int64(base) + 1 440 } 441 442 maxVal = math.MaxInt64 443 for ; i < len(s); i++ { 444 if n >= cutoff { 445 // n*base overflows 446 return parseLargeInt(float64(n), s[i:], base, sign) 447 } 448 v := digitVal(s[i]) 449 if v >= base { 450 break 451 } 452 n *= int64(base) 453 454 n1 := n + int64(v) 455 if n1 < n || n1 > maxVal { 456 // n+v overflows 457 return parseLargeInt(float64(n)+float64(v), s[i+1:], base, sign) 458 } 459 n = n1 460 } 461 462 if i == 0 { 463 err = strconv.ErrSyntax 464 goto Error 465 } 466 467 if sign { 468 n = -n 469 } 470 return intToValue(n), nil 471 472 Error: 473 return _NaN, err 474 } 475 476 func parseLargeInt(n float64, s string, base int, sign bool) (Value, error) { 477 i := 0 478 b := float64(base) 479 for ; i < len(s); i++ { 480 v := digitVal(s[i]) 481 if v >= base { 482 break 483 } 484 n = n*b + float64(v) 485 } 486 if sign { 487 n = -n 488 } 489 // We know it can't be represented as int, so use valueFloat instead of floatToValue 490 return valueFloat(n), nil 491 } 492 493 var ( 494 uriUnescaped [256]bool 495 uriReserved [256]bool 496 uriReservedHash [256]bool 497 uriReservedUnescapedHash [256]bool 498 emptyEscapeSet [256]bool 499 ) 500 501 func init() { 502 for _, c := range "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.!~*'()" { 503 uriUnescaped[c] = true 504 } 505 506 for _, c := range ";/?:@&=+$," { 507 uriReserved[c] = true 508 } 509 510 for i := 0; i < 256; i++ { 511 if uriUnescaped[i] || uriReserved[i] { 512 uriReservedUnescapedHash[i] = true 513 } 514 uriReservedHash[i] = uriReserved[i] 515 } 516 uriReservedUnescapedHash['#'] = true 517 uriReservedHash['#'] = true 518 }