github.com/nuvolaris/goja@v0.0.0-20230825100449-967811910c6d/string_imported.go (about)

     1  package goja
     2  
     3  import (
     4  	"hash/maphash"
     5  	"io"
     6  	"math"
     7  	"reflect"
     8  	"strings"
     9  	"unicode/utf16"
    10  	"unicode/utf8"
    11  
    12  	"github.com/nuvolaris/goja/parser"
    13  	"github.com/nuvolaris/goja/unistring"
    14  
    15  	"golang.org/x/text/cases"
    16  	"golang.org/x/text/language"
    17  )
    18  
    19  // Represents a string imported from Go. The idea is to delay the scanning for unicode characters and converting
    20  // to unicodeString until necessary. This way strings that are merely passed through never get scanned which
    21  // saves CPU and memory.
    22  // Currently, importedString is created in 2 cases: Runtime.ToValue() for strings longer than 16 bytes and as a result
    23  // of JSON.stringify() if it may contain unicode characters. More cases could be added in the future.
    24  type importedString struct {
    25  	s string
    26  	u unicodeString
    27  
    28  	scanned bool
    29  }
    30  
    31  func (i *importedString) scan() {
    32  	i.u = unistring.Scan(i.s)
    33  	i.scanned = true
    34  }
    35  
    36  func (i *importedString) ensureScanned() {
    37  	if !i.scanned {
    38  		i.scan()
    39  	}
    40  }
    41  
    42  func (i *importedString) ToInteger() int64 {
    43  	i.ensureScanned()
    44  	if i.u != nil {
    45  		return 0
    46  	}
    47  	return asciiString(i.s).ToInteger()
    48  }
    49  
    50  func (i *importedString) toString() String {
    51  	return i
    52  }
    53  
    54  func (i *importedString) string() unistring.String {
    55  	i.ensureScanned()
    56  	if i.u != nil {
    57  		return unistring.FromUtf16(i.u)
    58  	}
    59  	return unistring.String(i.s)
    60  }
    61  
    62  func (i *importedString) ToString() Value {
    63  	return i
    64  }
    65  
    66  func (i *importedString) String() string {
    67  	return i.s
    68  }
    69  
    70  func (i *importedString) ToFloat() float64 {
    71  	i.ensureScanned()
    72  	if i.u != nil {
    73  		return math.NaN()
    74  	}
    75  	return asciiString(i.s).ToFloat()
    76  }
    77  
    78  func (i *importedString) ToNumber() Value {
    79  	i.ensureScanned()
    80  	if i.u != nil {
    81  		return i.u.ToNumber()
    82  	}
    83  	return asciiString(i.s).ToNumber()
    84  }
    85  
    86  func (i *importedString) ToBoolean() bool {
    87  	return len(i.s) != 0
    88  }
    89  
    90  func (i *importedString) ToObject(r *Runtime) *Object {
    91  	return r._newString(i, r.global.StringPrototype)
    92  }
    93  
    94  func (i *importedString) SameAs(other Value) bool {
    95  	return i.StrictEquals(other)
    96  }
    97  
    98  func (i *importedString) Equals(other Value) bool {
    99  	if i.StrictEquals(other) {
   100  		return true
   101  	}
   102  	i.ensureScanned()
   103  	if i.u != nil {
   104  		return i.u.Equals(other)
   105  	}
   106  	return asciiString(i.s).Equals(other)
   107  }
   108  
   109  func (i *importedString) StrictEquals(other Value) bool {
   110  	switch otherStr := other.(type) {
   111  	case asciiString:
   112  		if i.u != nil {
   113  			return false
   114  		}
   115  		return i.s == string(otherStr)
   116  	case unicodeString:
   117  		i.ensureScanned()
   118  		if i.u != nil && i.u.equals(otherStr) {
   119  			return true
   120  		}
   121  	case *importedString:
   122  		return i.s == otherStr.s
   123  	}
   124  	return false
   125  }
   126  
   127  func (i *importedString) Export() interface{} {
   128  	return i.s
   129  }
   130  
   131  func (i *importedString) ExportType() reflect.Type {
   132  	return reflectTypeString
   133  }
   134  
   135  func (i *importedString) baseObject(r *Runtime) *Object {
   136  	i.ensureScanned()
   137  	if i.u != nil {
   138  		return i.u.baseObject(r)
   139  	}
   140  	return asciiString(i.s).baseObject(r)
   141  }
   142  
   143  func (i *importedString) hash(hasher *maphash.Hash) uint64 {
   144  	i.ensureScanned()
   145  	if i.u != nil {
   146  		return i.u.hash(hasher)
   147  	}
   148  	return asciiString(i.s).hash(hasher)
   149  }
   150  
   151  func (i *importedString) CharAt(idx int) uint16 {
   152  	i.ensureScanned()
   153  	if i.u != nil {
   154  		return i.u.CharAt(idx)
   155  	}
   156  	return asciiString(i.s).CharAt(idx)
   157  }
   158  
   159  func (i *importedString) Length() int {
   160  	i.ensureScanned()
   161  	if i.u != nil {
   162  		return i.u.Length()
   163  	}
   164  	return asciiString(i.s).Length()
   165  }
   166  
   167  func (i *importedString) Concat(v String) String {
   168  	if !i.scanned {
   169  		if v, ok := v.(*importedString); ok {
   170  			if !v.scanned {
   171  				return &importedString{s: i.s + v.s}
   172  			}
   173  		}
   174  		i.ensureScanned()
   175  	}
   176  	if i.u != nil {
   177  		return i.u.Concat(v)
   178  	}
   179  	return asciiString(i.s).Concat(v)
   180  }
   181  
   182  func (i *importedString) Substring(start, end int) String {
   183  	i.ensureScanned()
   184  	if i.u != nil {
   185  		return i.u.Substring(start, end)
   186  	}
   187  	return asciiString(i.s).Substring(start, end)
   188  }
   189  
   190  func (i *importedString) CompareTo(v String) int {
   191  	return strings.Compare(i.s, v.String())
   192  }
   193  
   194  func (i *importedString) Reader() io.RuneReader {
   195  	if i.scanned {
   196  		if i.u != nil {
   197  			return i.u.Reader()
   198  		}
   199  		return asciiString(i.s).Reader()
   200  	}
   201  	return strings.NewReader(i.s)
   202  }
   203  
   204  type stringUtf16Reader struct {
   205  	s      string
   206  	pos    int
   207  	second uint16
   208  }
   209  
   210  func (s *stringUtf16Reader) readChar() (c uint16, err error) {
   211  	if s.second != 0 {
   212  		c, s.second = s.second, 0
   213  		return
   214  	}
   215  	if s.pos < len(s.s) {
   216  		r1, size1 := utf8.DecodeRuneInString(s.s[s.pos:])
   217  		s.pos += size1
   218  		if r1 <= 0xFFFF {
   219  			c = uint16(r1)
   220  		} else {
   221  			first, second := utf16.EncodeRune(r1)
   222  			c, s.second = uint16(first), uint16(second)
   223  		}
   224  	} else {
   225  		err = io.EOF
   226  	}
   227  	return
   228  }
   229  
   230  func (s *stringUtf16Reader) ReadRune() (r rune, size int, err error) {
   231  	c, err := s.readChar()
   232  	if err != nil {
   233  		return
   234  	}
   235  	r = rune(c)
   236  	size = 1
   237  	return
   238  }
   239  
   240  func (i *importedString) utf16Reader() utf16Reader {
   241  	if i.scanned {
   242  		if i.u != nil {
   243  			return i.u.utf16Reader()
   244  		}
   245  		return asciiString(i.s).utf16Reader()
   246  	}
   247  	return &stringUtf16Reader{
   248  		s: i.s,
   249  	}
   250  }
   251  
   252  func (i *importedString) utf16RuneReader() io.RuneReader {
   253  	if i.scanned {
   254  		if i.u != nil {
   255  			return i.u.utf16RuneReader()
   256  		}
   257  		return asciiString(i.s).utf16RuneReader()
   258  	}
   259  	return &stringUtf16Reader{
   260  		s: i.s,
   261  	}
   262  }
   263  
   264  func (i *importedString) utf16Runes() []rune {
   265  	i.ensureScanned()
   266  	if i.u != nil {
   267  		return i.u.utf16Runes()
   268  	}
   269  	return asciiString(i.s).utf16Runes()
   270  }
   271  
   272  func (i *importedString) index(v String, start int) int {
   273  	i.ensureScanned()
   274  	if i.u != nil {
   275  		return i.u.index(v, start)
   276  	}
   277  	return asciiString(i.s).index(v, start)
   278  }
   279  
   280  func (i *importedString) lastIndex(v String, pos int) int {
   281  	i.ensureScanned()
   282  	if i.u != nil {
   283  		return i.u.lastIndex(v, pos)
   284  	}
   285  	return asciiString(i.s).lastIndex(v, pos)
   286  }
   287  
   288  func (i *importedString) toLower() String {
   289  	i.ensureScanned()
   290  	if i.u != nil {
   291  		return toLower(i.s)
   292  	}
   293  	return asciiString(i.s).toLower()
   294  }
   295  
   296  func (i *importedString) toUpper() String {
   297  	i.ensureScanned()
   298  	if i.u != nil {
   299  		caser := cases.Upper(language.Und)
   300  		return newStringValue(caser.String(i.s))
   301  	}
   302  	return asciiString(i.s).toUpper()
   303  }
   304  
   305  func (i *importedString) toTrimmedUTF8() string {
   306  	return strings.Trim(i.s, parser.WhitespaceChars)
   307  }