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  }