123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576 |
- package goja
- import (
- "errors"
- "io"
- "math"
- "regexp"
- "strconv"
- "strings"
- "sync"
- "unicode/utf8"
- "github.com/dop251/goja/unistring"
- )
- const hexUpper = "0123456789ABCDEF"
- var (
- parseFloatRegexp = regexp.MustCompile(`^([+-]?(?:Infinity|[0-9]*\.?[0-9]*(?:[eE][+-]?[0-9]+)?))`)
- )
- func (r *Runtime) builtin_isNaN(call FunctionCall) Value {
- if math.IsNaN(call.Argument(0).ToFloat()) {
- return valueTrue
- } else {
- return valueFalse
- }
- }
- func (r *Runtime) builtin_parseInt(call FunctionCall) Value {
- str := call.Argument(0).toString().toTrimmedUTF8()
- radix := int(toInt32(call.Argument(1)))
- v, _ := parseInt(str, radix)
- return v
- }
- func (r *Runtime) builtin_parseFloat(call FunctionCall) Value {
- m := parseFloatRegexp.FindStringSubmatch(call.Argument(0).toString().toTrimmedUTF8())
- if len(m) == 2 {
- if s := m[1]; s != "" && s != "+" && s != "-" {
- switch s {
- case "+", "-":
- case "Infinity", "+Infinity":
- return _positiveInf
- case "-Infinity":
- return _negativeInf
- default:
- f, err := strconv.ParseFloat(s, 64)
- if err == nil || isRangeErr(err) {
- return floatToValue(f)
- }
- }
- }
- }
- return _NaN
- }
- func (r *Runtime) builtin_isFinite(call FunctionCall) Value {
- f := call.Argument(0).ToFloat()
- if math.IsNaN(f) || math.IsInf(f, 0) {
- return valueFalse
- }
- return valueTrue
- }
- func (r *Runtime) _encode(uriString String, unescaped *[256]bool) String {
- reader := uriString.Reader()
- utf8Buf := make([]byte, utf8.UTFMax)
- needed := false
- l := 0
- for {
- rn, _, err := reader.ReadRune()
- if err != nil {
- if err != io.EOF {
- panic(r.newError(r.getURIError(), "Malformed URI"))
- }
- break
- }
- if rn >= utf8.RuneSelf {
- needed = true
- l += utf8.EncodeRune(utf8Buf, rn) * 3
- } else if !unescaped[rn] {
- needed = true
- l += 3
- } else {
- l++
- }
- }
- if !needed {
- return uriString
- }
- buf := make([]byte, l)
- i := 0
- reader = uriString.Reader()
- for {
- rn, _, err := reader.ReadRune()
- if err == io.EOF {
- break
- }
- if rn >= utf8.RuneSelf {
- n := utf8.EncodeRune(utf8Buf, rn)
- for _, b := range utf8Buf[:n] {
- buf[i] = '%'
- buf[i+1] = hexUpper[b>>4]
- buf[i+2] = hexUpper[b&15]
- i += 3
- }
- } else if !unescaped[rn] {
- buf[i] = '%'
- buf[i+1] = hexUpper[rn>>4]
- buf[i+2] = hexUpper[rn&15]
- i += 3
- } else {
- buf[i] = byte(rn)
- i++
- }
- }
- return asciiString(buf)
- }
- func (r *Runtime) _decode(sv String, reservedSet *[256]bool) String {
- s := sv.String()
- hexCount := 0
- for i := 0; i < len(s); {
- switch s[i] {
- case '%':
- if i+2 >= len(s) || !ishex(s[i+1]) || !ishex(s[i+2]) {
- panic(r.newError(r.getURIError(), "Malformed URI"))
- }
- c := unhex(s[i+1])<<4 | unhex(s[i+2])
- if !reservedSet[c] {
- hexCount++
- }
- i += 3
- default:
- i++
- }
- }
- if hexCount == 0 {
- return sv
- }
- t := make([]byte, len(s)-hexCount*2)
- j := 0
- isUnicode := false
- for i := 0; i < len(s); {
- ch := s[i]
- switch ch {
- case '%':
- c := unhex(s[i+1])<<4 | unhex(s[i+2])
- if reservedSet[c] {
- t[j] = s[i]
- t[j+1] = s[i+1]
- t[j+2] = s[i+2]
- j += 3
- } else {
- t[j] = c
- if c >= utf8.RuneSelf {
- isUnicode = true
- }
- j++
- }
- i += 3
- default:
- if ch >= utf8.RuneSelf {
- isUnicode = true
- }
- t[j] = ch
- j++
- i++
- }
- }
- if !isUnicode {
- return asciiString(t)
- }
- us := make([]rune, 0, len(s))
- for len(t) > 0 {
- rn, size := utf8.DecodeRune(t)
- if rn == utf8.RuneError {
- if size != 3 || t[0] != 0xef || t[1] != 0xbf || t[2] != 0xbd {
- panic(r.newError(r.getURIError(), "Malformed URI"))
- }
- }
- us = append(us, rn)
- t = t[size:]
- }
- return unicodeStringFromRunes(us)
- }
- func ishex(c byte) bool {
- switch {
- case '0' <= c && c <= '9':
- return true
- case 'a' <= c && c <= 'f':
- return true
- case 'A' <= c && c <= 'F':
- return true
- }
- return false
- }
- func unhex(c byte) byte {
- switch {
- case '0' <= c && c <= '9':
- return c - '0'
- case 'a' <= c && c <= 'f':
- return c - 'a' + 10
- case 'A' <= c && c <= 'F':
- return c - 'A' + 10
- }
- return 0
- }
- func (r *Runtime) builtin_decodeURI(call FunctionCall) Value {
- uriString := call.Argument(0).toString()
- return r._decode(uriString, &uriReservedHash)
- }
- func (r *Runtime) builtin_decodeURIComponent(call FunctionCall) Value {
- uriString := call.Argument(0).toString()
- return r._decode(uriString, &emptyEscapeSet)
- }
- func (r *Runtime) builtin_encodeURI(call FunctionCall) Value {
- uriString := call.Argument(0).toString()
- return r._encode(uriString, &uriReservedUnescapedHash)
- }
- func (r *Runtime) builtin_encodeURIComponent(call FunctionCall) Value {
- uriString := call.Argument(0).toString()
- return r._encode(uriString, &uriUnescaped)
- }
- func (r *Runtime) builtin_escape(call FunctionCall) Value {
- s := call.Argument(0).toString()
- var sb strings.Builder
- l := s.Length()
- for i := 0; i < l; i++ {
- r := s.CharAt(i)
- if r >= 'A' && r <= 'Z' || r >= 'a' && r <= 'z' || r >= '0' && r <= '9' ||
- r == '@' || r == '*' || r == '_' || r == '+' || r == '-' || r == '.' || r == '/' {
- sb.WriteByte(byte(r))
- } else if r <= 0xff {
- sb.WriteByte('%')
- sb.WriteByte(hexUpper[r>>4])
- sb.WriteByte(hexUpper[r&0xf])
- } else {
- sb.WriteString("%u")
- sb.WriteByte(hexUpper[r>>12])
- sb.WriteByte(hexUpper[(r>>8)&0xf])
- sb.WriteByte(hexUpper[(r>>4)&0xf])
- sb.WriteByte(hexUpper[r&0xf])
- }
- }
- return asciiString(sb.String())
- }
- func (r *Runtime) builtin_unescape(call FunctionCall) Value {
- s := call.Argument(0).toString()
- l := s.Length()
- var asciiBuf []byte
- var unicodeBuf []uint16
- _, u := devirtualizeString(s)
- unicode := u != nil
- if unicode {
- unicodeBuf = make([]uint16, 1, l+1)
- unicodeBuf[0] = unistring.BOM
- } else {
- asciiBuf = make([]byte, 0, l)
- }
- for i := 0; i < l; {
- r := s.CharAt(i)
- if r == '%' {
- if i <= l-6 && s.CharAt(i+1) == 'u' {
- c0 := s.CharAt(i + 2)
- c1 := s.CharAt(i + 3)
- c2 := s.CharAt(i + 4)
- c3 := s.CharAt(i + 5)
- if c0 <= 0xff && ishex(byte(c0)) &&
- c1 <= 0xff && ishex(byte(c1)) &&
- c2 <= 0xff && ishex(byte(c2)) &&
- c3 <= 0xff && ishex(byte(c3)) {
- r = uint16(unhex(byte(c0)))<<12 |
- uint16(unhex(byte(c1)))<<8 |
- uint16(unhex(byte(c2)))<<4 |
- uint16(unhex(byte(c3)))
- i += 5
- goto out
- }
- }
- if i <= l-3 {
- c0 := s.CharAt(i + 1)
- c1 := s.CharAt(i + 2)
- if c0 <= 0xff && ishex(byte(c0)) &&
- c1 <= 0xff && ishex(byte(c1)) {
- r = uint16(unhex(byte(c0))<<4 | unhex(byte(c1)))
- i += 2
- }
- }
- }
- out:
- if r >= utf8.RuneSelf && !unicode {
- unicodeBuf = make([]uint16, 1, l+1)
- unicodeBuf[0] = unistring.BOM
- for _, b := range asciiBuf {
- unicodeBuf = append(unicodeBuf, uint16(b))
- }
- asciiBuf = nil
- unicode = true
- }
- if unicode {
- unicodeBuf = append(unicodeBuf, r)
- } else {
- asciiBuf = append(asciiBuf, byte(r))
- }
- i++
- }
- if unicode {
- return unicodeString(unicodeBuf)
- }
- return asciiString(asciiBuf)
- }
- func createGlobalObjectTemplate() *objectTemplate {
- t := newObjectTemplate()
- t.protoFactory = func(r *Runtime) *Object {
- return r.global.ObjectPrototype
- }
- t.putStr("Object", func(r *Runtime) Value { return valueProp(r.getObject(), true, false, true) })
- t.putStr("Function", func(r *Runtime) Value { return valueProp(r.getFunction(), true, false, true) })
- t.putStr("Array", func(r *Runtime) Value { return valueProp(r.getArray(), true, false, true) })
- t.putStr("String", func(r *Runtime) Value { return valueProp(r.getString(), true, false, true) })
- t.putStr("Number", func(r *Runtime) Value { return valueProp(r.getNumber(), true, false, true) })
- t.putStr("BigInt", func(r *Runtime) Value { return valueProp(r.getBigInt(), true, false, true) })
- t.putStr("RegExp", func(r *Runtime) Value { return valueProp(r.getRegExp(), true, false, true) })
- t.putStr("Date", func(r *Runtime) Value { return valueProp(r.getDate(), true, false, true) })
- t.putStr("Boolean", func(r *Runtime) Value { return valueProp(r.getBoolean(), true, false, true) })
- t.putStr("Proxy", func(r *Runtime) Value { return valueProp(r.getProxy(), true, false, true) })
- t.putStr("Reflect", func(r *Runtime) Value { return valueProp(r.getReflect(), true, false, true) })
- t.putStr("Error", func(r *Runtime) Value { return valueProp(r.getError(), true, false, true) })
- t.putStr("AggregateError", func(r *Runtime) Value { return valueProp(r.getAggregateError(), true, false, true) })
- t.putStr("TypeError", func(r *Runtime) Value { return valueProp(r.getTypeError(), true, false, true) })
- t.putStr("ReferenceError", func(r *Runtime) Value { return valueProp(r.getReferenceError(), true, false, true) })
- t.putStr("SyntaxError", func(r *Runtime) Value { return valueProp(r.getSyntaxError(), true, false, true) })
- t.putStr("RangeError", func(r *Runtime) Value { return valueProp(r.getRangeError(), true, false, true) })
- t.putStr("EvalError", func(r *Runtime) Value { return valueProp(r.getEvalError(), true, false, true) })
- t.putStr("URIError", func(r *Runtime) Value { return valueProp(r.getURIError(), true, false, true) })
- t.putStr("GoError", func(r *Runtime) Value { return valueProp(r.getGoError(), true, false, true) })
- t.putStr("eval", func(r *Runtime) Value { return valueProp(r.getEval(), true, false, true) })
- t.putStr("Math", func(r *Runtime) Value { return valueProp(r.getMath(), true, false, true) })
- t.putStr("JSON", func(r *Runtime) Value { return valueProp(r.getJSON(), true, false, true) })
- addTypedArrays(t)
- t.putStr("Symbol", func(r *Runtime) Value { return valueProp(r.getSymbol(), true, false, true) })
- t.putStr("WeakSet", func(r *Runtime) Value { return valueProp(r.getWeakSet(), true, false, true) })
- t.putStr("WeakMap", func(r *Runtime) Value { return valueProp(r.getWeakMap(), true, false, true) })
- t.putStr("Map", func(r *Runtime) Value { return valueProp(r.getMap(), true, false, true) })
- t.putStr("Set", func(r *Runtime) Value { return valueProp(r.getSet(), true, false, true) })
- t.putStr("Promise", func(r *Runtime) Value { return valueProp(r.getPromise(), true, false, true) })
- t.putStr("globalThis", func(r *Runtime) Value { return valueProp(r.globalObject, true, false, true) })
- t.putStr("NaN", func(r *Runtime) Value { return valueProp(_NaN, false, false, false) })
- t.putStr("undefined", func(r *Runtime) Value { return valueProp(_undefined, false, false, false) })
- t.putStr("Infinity", func(r *Runtime) Value { return valueProp(_positiveInf, false, false, false) })
- t.putStr("isNaN", func(r *Runtime) Value { return r.methodProp(r.builtin_isNaN, "isNaN", 1) })
- t.putStr("parseInt", func(r *Runtime) Value { return valueProp(r.getParseInt(), true, false, true) })
- t.putStr("parseFloat", func(r *Runtime) Value { return valueProp(r.getParseFloat(), true, false, true) })
- t.putStr("isFinite", func(r *Runtime) Value { return r.methodProp(r.builtin_isFinite, "isFinite", 1) })
- t.putStr("decodeURI", func(r *Runtime) Value { return r.methodProp(r.builtin_decodeURI, "decodeURI", 1) })
- t.putStr("decodeURIComponent", func(r *Runtime) Value { return r.methodProp(r.builtin_decodeURIComponent, "decodeURIComponent", 1) })
- t.putStr("encodeURI", func(r *Runtime) Value { return r.methodProp(r.builtin_encodeURI, "encodeURI", 1) })
- t.putStr("encodeURIComponent", func(r *Runtime) Value { return r.methodProp(r.builtin_encodeURIComponent, "encodeURIComponent", 1) })
- t.putStr("escape", func(r *Runtime) Value { return r.methodProp(r.builtin_escape, "escape", 1) })
- t.putStr("unescape", func(r *Runtime) Value { return r.methodProp(r.builtin_unescape, "unescape", 1) })
- // TODO: Annex B
- t.putSym(SymToStringTag, func(r *Runtime) Value { return valueProp(asciiString(classGlobal), false, false, true) })
- return t
- }
- var globalObjectTemplate *objectTemplate
- var globalObjectTemplateOnce sync.Once
- func getGlobalObjectTemplate() *objectTemplate {
- globalObjectTemplateOnce.Do(func() {
- globalObjectTemplate = createGlobalObjectTemplate()
- })
- return globalObjectTemplate
- }
- func (r *Runtime) getEval() *Object {
- ret := r.global.Eval
- if ret == nil {
- ret = r.newNativeFunc(r.builtin_eval, "eval", 1)
- r.global.Eval = ret
- }
- return ret
- }
- func digitVal(d byte) int {
- var v byte
- switch {
- case '0' <= d && d <= '9':
- v = d - '0'
- case 'a' <= d && d <= 'z':
- v = d - 'a' + 10
- case 'A' <= d && d <= 'Z':
- v = d - 'A' + 10
- default:
- return 36
- }
- return int(v)
- }
- // ECMAScript compatible version of strconv.ParseInt
- func parseInt(s string, base int) (Value, error) {
- var n int64
- var err error
- var cutoff, maxVal int64
- var sign bool
- i := 0
- if len(s) < 1 {
- err = strconv.ErrSyntax
- goto Error
- }
- switch s[0] {
- case '-':
- sign = true
- s = s[1:]
- case '+':
- s = s[1:]
- }
- if len(s) < 1 {
- err = strconv.ErrSyntax
- goto Error
- }
- // Look for hex prefix.
- if s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X') {
- if base == 0 || base == 16 {
- base = 16
- s = s[2:]
- }
- }
- switch {
- case len(s) < 1:
- err = strconv.ErrSyntax
- goto Error
- case 2 <= base && base <= 36:
- // valid base; nothing to do
- case base == 0:
- // Look for hex prefix.
- switch {
- case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'):
- if len(s) < 3 {
- err = strconv.ErrSyntax
- goto Error
- }
- base = 16
- s = s[2:]
- default:
- base = 10
- }
- default:
- err = errors.New("invalid base " + strconv.Itoa(base))
- goto Error
- }
- // Cutoff is the smallest number such that cutoff*base > maxInt64.
- // Use compile-time constants for common cases.
- switch base {
- case 10:
- cutoff = math.MaxInt64/10 + 1
- case 16:
- cutoff = math.MaxInt64/16 + 1
- default:
- cutoff = math.MaxInt64/int64(base) + 1
- }
- maxVal = math.MaxInt64
- for ; i < len(s); i++ {
- if n >= cutoff {
- // n*base overflows
- return parseLargeInt(float64(n), s[i:], base, sign)
- }
- v := digitVal(s[i])
- if v >= base {
- break
- }
- n *= int64(base)
- n1 := n + int64(v)
- if n1 < n || n1 > maxVal {
- // n+v overflows
- return parseLargeInt(float64(n)+float64(v), s[i+1:], base, sign)
- }
- n = n1
- }
- if i == 0 {
- err = strconv.ErrSyntax
- goto Error
- }
- if sign {
- n = -n
- }
- return intToValue(n), nil
- Error:
- return _NaN, err
- }
- func parseLargeInt(n float64, s string, base int, sign bool) (Value, error) {
- i := 0
- b := float64(base)
- for ; i < len(s); i++ {
- v := digitVal(s[i])
- if v >= base {
- break
- }
- n = n*b + float64(v)
- }
- if sign {
- n = -n
- }
- // We know it can't be represented as int, so use valueFloat instead of floatToValue
- return valueFloat(n), nil
- }
- var (
- uriUnescaped [256]bool
- uriReserved [256]bool
- uriReservedHash [256]bool
- uriReservedUnescapedHash [256]bool
- emptyEscapeSet [256]bool
- )
- func init() {
- for _, c := range "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.!~*'()" {
- uriUnescaped[c] = true
- }
- for _, c := range ";/?:@&=+$," {
- uriReserved[c] = true
- }
- for i := 0; i < 256; i++ {
- if uriUnescaped[i] || uriReserved[i] {
- uriReservedUnescapedHash[i] = true
- }
- uriReservedHash[i] = uriReserved[i]
- }
- uriReservedUnescapedHash['#'] = true
- uriReservedHash['#'] = true
- }
|