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