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