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  }