402 lines
7.5 KiB
Go
402 lines
7.5 KiB
Go
package goja
|
|
|
|
import (
|
|
"hash/maphash"
|
|
"io"
|
|
"math"
|
|
"math/big"
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"apigo.cc/ai/ai/goja/unistring"
|
|
)
|
|
|
|
type asciiString string
|
|
|
|
type asciiRuneReader struct {
|
|
s asciiString
|
|
pos int
|
|
}
|
|
|
|
func (rr *asciiRuneReader) ReadRune() (r rune, size int, err error) {
|
|
if rr.pos < len(rr.s) {
|
|
r = rune(rr.s[rr.pos])
|
|
size = 1
|
|
rr.pos++
|
|
} else {
|
|
err = io.EOF
|
|
}
|
|
return
|
|
}
|
|
|
|
type asciiUtf16Reader struct {
|
|
s asciiString
|
|
pos int
|
|
}
|
|
|
|
func (rr *asciiUtf16Reader) readChar() (c uint16, err error) {
|
|
if rr.pos < len(rr.s) {
|
|
c = uint16(rr.s[rr.pos])
|
|
rr.pos++
|
|
} else {
|
|
err = io.EOF
|
|
}
|
|
return
|
|
}
|
|
|
|
func (rr *asciiUtf16Reader) ReadRune() (r rune, size int, err error) {
|
|
if rr.pos < len(rr.s) {
|
|
r = rune(rr.s[rr.pos])
|
|
rr.pos++
|
|
size = 1
|
|
} else {
|
|
err = io.EOF
|
|
}
|
|
return
|
|
}
|
|
|
|
func (s asciiString) Reader() io.RuneReader {
|
|
return &asciiRuneReader{
|
|
s: s,
|
|
}
|
|
}
|
|
|
|
func (s asciiString) utf16Reader() utf16Reader {
|
|
return &asciiUtf16Reader{
|
|
s: s,
|
|
}
|
|
}
|
|
|
|
func (s asciiString) utf16RuneReader() io.RuneReader {
|
|
return &asciiUtf16Reader{
|
|
s: s,
|
|
}
|
|
}
|
|
|
|
func (s asciiString) utf16Runes() []rune {
|
|
runes := make([]rune, len(s))
|
|
for i := 0; i < len(s); i++ {
|
|
runes[i] = rune(s[i])
|
|
}
|
|
return runes
|
|
}
|
|
|
|
// ss must be trimmed
|
|
func stringToInt(ss string) (int64, error) {
|
|
if ss == "" {
|
|
return 0, nil
|
|
}
|
|
if ss == "-0" {
|
|
return 0, strconv.ErrSyntax
|
|
}
|
|
if len(ss) > 2 {
|
|
switch ss[:2] {
|
|
case "0x", "0X":
|
|
return strconv.ParseInt(ss[2:], 16, 64)
|
|
case "0b", "0B":
|
|
return strconv.ParseInt(ss[2:], 2, 64)
|
|
case "0o", "0O":
|
|
return strconv.ParseInt(ss[2:], 8, 64)
|
|
}
|
|
}
|
|
return strconv.ParseInt(ss, 10, 64)
|
|
}
|
|
|
|
func (s asciiString) _toInt(trimmed string) (int64, error) {
|
|
return stringToInt(trimmed)
|
|
}
|
|
|
|
func isRangeErr(err error) bool {
|
|
if err, ok := err.(*strconv.NumError); ok {
|
|
return err.Err == strconv.ErrRange
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (s asciiString) _toFloat(trimmed string) (float64, error) {
|
|
if trimmed == "" {
|
|
return 0, nil
|
|
}
|
|
if trimmed == "-0" {
|
|
var f float64
|
|
return -f, nil
|
|
}
|
|
|
|
// Go allows underscores in numbers, when parsed as floats, but ECMAScript expect them to be interpreted as NaN.
|
|
if strings.ContainsRune(trimmed, '_') {
|
|
return 0, strconv.ErrSyntax
|
|
}
|
|
|
|
// Hexadecimal floats are not supported by ECMAScript.
|
|
if len(trimmed) >= 2 {
|
|
var prefix string
|
|
if trimmed[0] == '-' || trimmed[0] == '+' {
|
|
prefix = trimmed[1:]
|
|
} else {
|
|
prefix = trimmed
|
|
}
|
|
if len(prefix) >= 2 && prefix[0] == '0' && (prefix[1] == 'x' || prefix[1] == 'X') {
|
|
return 0, strconv.ErrSyntax
|
|
}
|
|
}
|
|
|
|
f, err := strconv.ParseFloat(trimmed, 64)
|
|
if err == nil && math.IsInf(f, 0) {
|
|
ss := strings.ToLower(trimmed)
|
|
if strings.HasPrefix(ss, "inf") || strings.HasPrefix(ss, "-inf") || strings.HasPrefix(ss, "+inf") {
|
|
// We handle "Infinity" separately, prevent from being parsed as Infinity due to strconv.ParseFloat() permissive syntax
|
|
return 0, strconv.ErrSyntax
|
|
}
|
|
}
|
|
if isRangeErr(err) {
|
|
err = nil
|
|
}
|
|
return f, err
|
|
}
|
|
|
|
func (s asciiString) ToInteger() int64 {
|
|
ss := strings.TrimSpace(string(s))
|
|
if ss == "" {
|
|
return 0
|
|
}
|
|
if ss == "Infinity" || ss == "+Infinity" {
|
|
return math.MaxInt64
|
|
}
|
|
if ss == "-Infinity" {
|
|
return math.MinInt64
|
|
}
|
|
i, err := s._toInt(ss)
|
|
if err != nil {
|
|
f, err := s._toFloat(ss)
|
|
if err == nil {
|
|
return int64(f)
|
|
}
|
|
}
|
|
return i
|
|
}
|
|
|
|
func (s asciiString) toString() String {
|
|
return s
|
|
}
|
|
|
|
func (s asciiString) ToString() Value {
|
|
return s
|
|
}
|
|
|
|
func (s asciiString) String() string {
|
|
return string(s)
|
|
}
|
|
|
|
func (s asciiString) ToFloat() float64 {
|
|
ss := strings.TrimSpace(string(s))
|
|
if ss == "" {
|
|
return 0
|
|
}
|
|
if ss == "Infinity" || ss == "+Infinity" {
|
|
return math.Inf(1)
|
|
}
|
|
if ss == "-Infinity" {
|
|
return math.Inf(-1)
|
|
}
|
|
f, err := s._toFloat(ss)
|
|
if err != nil {
|
|
i, err := s._toInt(ss)
|
|
if err == nil {
|
|
return float64(i)
|
|
}
|
|
f = math.NaN()
|
|
}
|
|
return f
|
|
}
|
|
|
|
func (s asciiString) ToBoolean() bool {
|
|
return s != ""
|
|
}
|
|
|
|
func (s asciiString) ToNumber() Value {
|
|
ss := strings.TrimSpace(string(s))
|
|
if ss == "" {
|
|
return intToValue(0)
|
|
}
|
|
if ss == "Infinity" || ss == "+Infinity" {
|
|
return _positiveInf
|
|
}
|
|
if ss == "-Infinity" {
|
|
return _negativeInf
|
|
}
|
|
|
|
if i, err := s._toInt(ss); err == nil {
|
|
return intToValue(i)
|
|
}
|
|
|
|
if f, err := s._toFloat(ss); err == nil {
|
|
return floatToValue(f)
|
|
}
|
|
|
|
return _NaN
|
|
}
|
|
|
|
func (s asciiString) ToObject(r *Runtime) *Object {
|
|
return r._newString(s, r.getStringPrototype())
|
|
}
|
|
|
|
func (s asciiString) SameAs(other Value) bool {
|
|
return s.StrictEquals(other)
|
|
}
|
|
|
|
func (s asciiString) Equals(other Value) bool {
|
|
if s.StrictEquals(other) {
|
|
return true
|
|
}
|
|
|
|
if o, ok := other.(valueInt); ok {
|
|
if o1, e := s._toInt(strings.TrimSpace(string(s))); e == nil {
|
|
return o1 == int64(o)
|
|
}
|
|
return false
|
|
}
|
|
|
|
if o, ok := other.(valueFloat); ok {
|
|
return s.ToFloat() == float64(o)
|
|
}
|
|
|
|
if o, ok := other.(valueBool); ok {
|
|
if o1, e := s._toFloat(strings.TrimSpace(string(s))); e == nil {
|
|
return o1 == o.ToFloat()
|
|
}
|
|
return false
|
|
}
|
|
|
|
if o, ok := other.(*valueBigInt); ok {
|
|
bigInt, err := stringToBigInt(s.toTrimmedUTF8())
|
|
if err != nil {
|
|
return false
|
|
}
|
|
return bigInt.Cmp((*big.Int)(o)) == 0
|
|
}
|
|
|
|
if o, ok := other.(*Object); ok {
|
|
return s.Equals(o.toPrimitive())
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (s asciiString) StrictEquals(other Value) bool {
|
|
if otherStr, ok := other.(asciiString); ok {
|
|
return s == otherStr
|
|
}
|
|
if otherStr, ok := other.(*importedString); ok {
|
|
if otherStr.u == nil {
|
|
return string(s) == otherStr.s
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (s asciiString) baseObject(r *Runtime) *Object {
|
|
ss := r.getStringSingleton()
|
|
ss.value = s
|
|
ss.setLength()
|
|
return ss.val
|
|
}
|
|
|
|
func (s asciiString) hash(hash *maphash.Hash) uint64 {
|
|
_, _ = hash.WriteString(string(s))
|
|
h := hash.Sum64()
|
|
hash.Reset()
|
|
return h
|
|
}
|
|
|
|
func (s asciiString) CharAt(idx int) uint16 {
|
|
return uint16(s[idx])
|
|
}
|
|
|
|
func (s asciiString) Length() int {
|
|
return len(s)
|
|
}
|
|
|
|
func (s asciiString) Concat(other String) String {
|
|
a, u := devirtualizeString(other)
|
|
if u != nil {
|
|
b := make([]uint16, len(s)+len(u))
|
|
b[0] = unistring.BOM
|
|
for i := 0; i < len(s); i++ {
|
|
b[i+1] = uint16(s[i])
|
|
}
|
|
copy(b[len(s)+1:], u[1:])
|
|
return unicodeString(b)
|
|
}
|
|
return s + a
|
|
}
|
|
|
|
func (s asciiString) Substring(start, end int) String {
|
|
return s[start:end]
|
|
}
|
|
|
|
func (s asciiString) CompareTo(other String) int {
|
|
switch other := other.(type) {
|
|
case asciiString:
|
|
return strings.Compare(string(s), string(other))
|
|
case unicodeString:
|
|
return strings.Compare(string(s), other.String())
|
|
case *importedString:
|
|
return strings.Compare(string(s), other.s)
|
|
default:
|
|
panic(newTypeError("Internal bug: unknown string type: %T", other))
|
|
}
|
|
}
|
|
|
|
func (s asciiString) index(substr String, start int) int {
|
|
a, u := devirtualizeString(substr)
|
|
if u == nil {
|
|
if start > len(s) {
|
|
return -1
|
|
}
|
|
p := strings.Index(string(s[start:]), string(a))
|
|
if p >= 0 {
|
|
return p + start
|
|
}
|
|
}
|
|
return -1
|
|
}
|
|
|
|
func (s asciiString) lastIndex(substr String, pos int) int {
|
|
a, u := devirtualizeString(substr)
|
|
if u == nil {
|
|
end := pos + len(a)
|
|
var ss string
|
|
if end > len(s) {
|
|
ss = string(s)
|
|
} else {
|
|
ss = string(s[:end])
|
|
}
|
|
return strings.LastIndex(ss, string(a))
|
|
}
|
|
return -1
|
|
}
|
|
|
|
func (s asciiString) toLower() String {
|
|
return asciiString(strings.ToLower(string(s)))
|
|
}
|
|
|
|
func (s asciiString) toUpper() String {
|
|
return asciiString(strings.ToUpper(string(s)))
|
|
}
|
|
|
|
func (s asciiString) toTrimmedUTF8() string {
|
|
return strings.TrimSpace(string(s))
|
|
}
|
|
|
|
func (s asciiString) string() unistring.String {
|
|
return unistring.String(s)
|
|
}
|
|
|
|
func (s asciiString) Export() interface{} {
|
|
return string(s)
|
|
}
|
|
|
|
func (s asciiString) ExportType() reflect.Type {
|
|
return reflectTypeString
|
|
}
|