go.ketch.com/lib/goja@v0.0.1/builtin_global.go (about) 1 package goja 2 3 import ( 4 "errors" 5 "go.ketch.com/lib/goja/unistring" 6 "io" 7 "math" 8 "regexp" 9 "strconv" 10 "strings" 11 "unicode/utf8" 12 ) 13 14 const hexUpper = "0123456789ABCDEF" 15 16 var ( 17 parseFloatRegexp = regexp.MustCompile(`^([+-]?(?:Infinity|[0-9]*\.?[0-9]*(?:[eE][+-]?[0-9]+)?))`) 18 ) 19 20 func (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 28 func (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 35 func (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 56 func (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 64 func (r *Runtime) _encode(uriString valueString, unescaped *[256]bool) valueString { 65 reader := uriString.reader() 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() 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 123 func (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 195 func 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 207 func 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 219 func (r *Runtime) builtin_decodeURI(call FunctionCall) Value { 220 uriString := call.Argument(0).toString() 221 return r._decode(uriString, &uriReservedHash) 222 } 223 224 func (r *Runtime) builtin_decodeURIComponent(call FunctionCall) Value { 225 uriString := call.Argument(0).toString() 226 return r._decode(uriString, &emptyEscapeSet) 227 } 228 229 func (r *Runtime) builtin_encodeURI(call FunctionCall) Value { 230 uriString := call.Argument(0).toString() 231 return r._encode(uriString, &uriReservedUnescapedHash) 232 } 233 234 func (r *Runtime) builtin_encodeURIComponent(call FunctionCall) Value { 235 uriString := call.Argument(0).toString() 236 return r._encode(uriString, &uriUnescaped) 237 } 238 239 func (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 263 func (r *Runtime) builtin_unescape(call FunctionCall) Value { 264 s := call.Argument(0).toString() 265 l := s.length() 266 var asciiBuf []byte 267 var unicodeBuf []uint16 268 _, u := devirtualizeString(s) 269 unicode := u != nil 270 if unicode { 271 unicodeBuf = make([]uint16, 1, l+1) 272 unicodeBuf[0] = unistring.BOM 273 } else { 274 asciiBuf = make([]byte, 0, l) 275 } 276 for i := 0; i < l; { 277 r := s.charAt(i) 278 if r == '%' { 279 if i <= l-6 && s.charAt(i+1) == 'u' { 280 c0 := s.charAt(i + 2) 281 c1 := s.charAt(i + 3) 282 c2 := s.charAt(i + 4) 283 c3 := s.charAt(i + 5) 284 if c0 <= 0xff && ishex(byte(c0)) && 285 c1 <= 0xff && ishex(byte(c1)) && 286 c2 <= 0xff && ishex(byte(c2)) && 287 c3 <= 0xff && ishex(byte(c3)) { 288 r = rune(unhex(byte(c0)))<<12 | 289 rune(unhex(byte(c1)))<<8 | 290 rune(unhex(byte(c2)))<<4 | 291 rune(unhex(byte(c3))) 292 i += 5 293 goto out 294 } 295 } 296 if i <= l-3 { 297 c0 := s.charAt(i + 1) 298 c1 := s.charAt(i + 2) 299 if c0 <= 0xff && ishex(byte(c0)) && 300 c1 <= 0xff && ishex(byte(c1)) { 301 r = rune(unhex(byte(c0))<<4 | unhex(byte(c1))) 302 i += 2 303 } 304 } 305 } 306 out: 307 if r >= utf8.RuneSelf && !unicode { 308 unicodeBuf = make([]uint16, 1, l+1) 309 unicodeBuf[0] = unistring.BOM 310 for _, b := range asciiBuf { 311 unicodeBuf = append(unicodeBuf, uint16(b)) 312 } 313 asciiBuf = nil 314 unicode = true 315 } 316 if unicode { 317 unicodeBuf = append(unicodeBuf, uint16(r)) 318 } else { 319 asciiBuf = append(asciiBuf, byte(r)) 320 } 321 i++ 322 } 323 if unicode { 324 return unicodeString(unicodeBuf) 325 } 326 327 return asciiString(asciiBuf) 328 } 329 330 func (r *Runtime) initGlobalObject() { 331 o := r.globalObject.self 332 o._putProp("globalThis", r.globalObject, true, false, true) 333 o._putProp("NaN", _NaN, false, false, false) 334 o._putProp("undefined", _undefined, false, false, false) 335 o._putProp("Infinity", _positiveInf, false, false, false) 336 337 o._putProp("isNaN", r.newNativeFunc(r.builtin_isNaN, nil, "isNaN", nil, 1), true, false, true) 338 o._putProp("parseInt", r.newNativeFunc(r.builtin_parseInt, nil, "parseInt", nil, 2), true, false, true) 339 o._putProp("parseFloat", r.newNativeFunc(r.builtin_parseFloat, nil, "parseFloat", nil, 1), true, false, true) 340 o._putProp("isFinite", r.newNativeFunc(r.builtin_isFinite, nil, "isFinite", nil, 1), true, false, true) 341 o._putProp("decodeURI", r.newNativeFunc(r.builtin_decodeURI, nil, "decodeURI", nil, 1), true, false, true) 342 o._putProp("decodeURIComponent", r.newNativeFunc(r.builtin_decodeURIComponent, nil, "decodeURIComponent", nil, 1), true, false, true) 343 o._putProp("encodeURI", r.newNativeFunc(r.builtin_encodeURI, nil, "encodeURI", nil, 1), true, false, true) 344 o._putProp("encodeURIComponent", r.newNativeFunc(r.builtin_encodeURIComponent, nil, "encodeURIComponent", nil, 1), true, false, true) 345 o._putProp("escape", r.newNativeFunc(r.builtin_escape, nil, "escape", nil, 1), true, false, true) 346 o._putProp("unescape", r.newNativeFunc(r.builtin_unescape, nil, "unescape", nil, 1), true, false, true) 347 348 o._putSym(SymToStringTag, valueProp(asciiString(classGlobal), false, false, true)) 349 350 // TODO: Annex B 351 352 } 353 354 func digitVal(d byte) int { 355 var v byte 356 switch { 357 case '0' <= d && d <= '9': 358 v = d - '0' 359 case 'a' <= d && d <= 'z': 360 v = d - 'a' + 10 361 case 'A' <= d && d <= 'Z': 362 v = d - 'A' + 10 363 default: 364 return 36 365 } 366 return int(v) 367 } 368 369 // ECMAScript compatible version of strconv.ParseInt 370 func parseInt(s string, base int) (Value, error) { 371 var n int64 372 var err error 373 var cutoff, maxVal int64 374 var sign bool 375 i := 0 376 377 if len(s) < 1 { 378 err = strconv.ErrSyntax 379 goto Error 380 } 381 382 switch s[0] { 383 case '-': 384 sign = true 385 s = s[1:] 386 case '+': 387 s = s[1:] 388 } 389 390 if len(s) < 1 { 391 err = strconv.ErrSyntax 392 goto Error 393 } 394 395 // Look for hex prefix. 396 if s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X') { 397 if base == 0 || base == 16 { 398 base = 16 399 s = s[2:] 400 } 401 } 402 403 switch { 404 case len(s) < 1: 405 err = strconv.ErrSyntax 406 goto Error 407 408 case 2 <= base && base <= 36: 409 // valid base; nothing to do 410 411 case base == 0: 412 // Look for hex prefix. 413 switch { 414 case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'): 415 if len(s) < 3 { 416 err = strconv.ErrSyntax 417 goto Error 418 } 419 base = 16 420 s = s[2:] 421 default: 422 base = 10 423 } 424 425 default: 426 err = errors.New("invalid base " + strconv.Itoa(base)) 427 goto Error 428 } 429 430 // Cutoff is the smallest number such that cutoff*base > maxInt64. 431 // Use compile-time constants for common cases. 432 switch base { 433 case 10: 434 cutoff = math.MaxInt64/10 + 1 435 case 16: 436 cutoff = math.MaxInt64/16 + 1 437 default: 438 cutoff = math.MaxInt64/int64(base) + 1 439 } 440 441 maxVal = math.MaxInt64 442 for ; i < len(s); i++ { 443 if n >= cutoff { 444 // n*base overflows 445 return parseLargeInt(float64(n), s[i:], base, sign) 446 } 447 v := digitVal(s[i]) 448 if v >= base { 449 break 450 } 451 n *= int64(base) 452 453 n1 := n + int64(v) 454 if n1 < n || n1 > maxVal { 455 // n+v overflows 456 return parseLargeInt(float64(n)+float64(v), s[i+1:], base, sign) 457 } 458 n = n1 459 } 460 461 if i == 0 { 462 err = strconv.ErrSyntax 463 goto Error 464 } 465 466 if sign { 467 n = -n 468 } 469 return intToValue(n), nil 470 471 Error: 472 return _NaN, err 473 } 474 475 func parseLargeInt(n float64, s string, base int, sign bool) (Value, error) { 476 i := 0 477 b := float64(base) 478 for ; i < len(s); i++ { 479 v := digitVal(s[i]) 480 if v >= base { 481 break 482 } 483 n = n*b + float64(v) 484 } 485 if sign { 486 n = -n 487 } 488 // We know it can't be represented as int, so use valueFloat instead of floatToValue 489 return valueFloat(n), nil 490 } 491 492 var ( 493 uriUnescaped [256]bool 494 uriReserved [256]bool 495 uriReservedHash [256]bool 496 uriReservedUnescapedHash [256]bool 497 emptyEscapeSet [256]bool 498 ) 499 500 func init() { 501 for _, c := range "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.!~*'()" { 502 uriUnescaped[c] = true 503 } 504 505 for _, c := range ";/?:@&=+$," { 506 uriReserved[c] = true 507 } 508 509 for i := 0; i < 256; i++ { 510 if uriUnescaped[i] || uriReserved[i] { 511 uriReservedUnescapedHash[i] = true 512 } 513 uriReservedHash[i] = uriReserved[i] 514 } 515 uriReservedUnescapedHash['#'] = true 516 uriReservedHash['#'] = true 517 }