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

     1  package goja
     2  
     3  import (
     4  	"io"
     5  	"strconv"
     6  	"strings"
     7  	"unicode/utf8"
     8  
     9  	"github.com/nuvolaris/goja/unistring"
    10  )
    11  
    12  const (
    13  	__proto__ = "__proto__"
    14  )
    15  
    16  var (
    17  	stringTrue        String = asciiString("true")
    18  	stringFalse       String = asciiString("false")
    19  	stringNull        String = asciiString("null")
    20  	stringUndefined   String = asciiString("undefined")
    21  	stringObjectC     String = asciiString("object")
    22  	stringFunction    String = asciiString("function")
    23  	stringBoolean     String = asciiString("boolean")
    24  	stringString      String = asciiString("string")
    25  	stringSymbol      String = asciiString("symbol")
    26  	stringNumber      String = asciiString("number")
    27  	stringNaN         String = asciiString("NaN")
    28  	stringInfinity           = asciiString("Infinity")
    29  	stringNegInfinity        = asciiString("-Infinity")
    30  	stringBound_      String = asciiString("bound ")
    31  	stringEmpty       String = asciiString("")
    32  
    33  	stringError          String = asciiString("Error")
    34  	stringAggregateError String = asciiString("AggregateError")
    35  	stringTypeError      String = asciiString("TypeError")
    36  	stringReferenceError String = asciiString("ReferenceError")
    37  	stringSyntaxError    String = asciiString("SyntaxError")
    38  	stringRangeError     String = asciiString("RangeError")
    39  	stringEvalError      String = asciiString("EvalError")
    40  	stringURIError       String = asciiString("URIError")
    41  	stringGoError        String = asciiString("GoError")
    42  
    43  	stringObjectNull      String = asciiString("[object Null]")
    44  	stringObjectUndefined String = asciiString("[object Undefined]")
    45  	stringInvalidDate     String = asciiString("Invalid Date")
    46  )
    47  
    48  type utf16Reader interface {
    49  	readChar() (c uint16, err error)
    50  }
    51  
    52  // String represents an ECMAScript string Value. Its internal representation depends on the contents of the
    53  // string, but in any case it is capable of holding any UTF-16 string, either valid or invalid.
    54  // Instances of this type, as any other primitive values, are goroutine-safe and can be passed between runtimes.
    55  // Strings can be created using Runtime.ToValue(goString) or StringFromUTF16.
    56  type String interface {
    57  	Value
    58  	CharAt(int) uint16
    59  	Length() int
    60  	Concat(String) String
    61  	Substring(start, end int) String
    62  	CompareTo(String) int
    63  	Reader() io.RuneReader
    64  	utf16Reader() utf16Reader
    65  	utf16RuneReader() io.RuneReader
    66  	utf16Runes() []rune
    67  	index(String, int) int
    68  	lastIndex(String, int) int
    69  	toLower() String
    70  	toUpper() String
    71  	toTrimmedUTF8() string
    72  }
    73  
    74  type stringIterObject struct {
    75  	baseObject
    76  	reader io.RuneReader
    77  }
    78  
    79  func isUTF16FirstSurrogate(c uint16) bool {
    80  	return c >= 0xD800 && c <= 0xDBFF
    81  }
    82  
    83  func isUTF16SecondSurrogate(c uint16) bool {
    84  	return c >= 0xDC00 && c <= 0xDFFF
    85  }
    86  
    87  func (si *stringIterObject) next() Value {
    88  	if si.reader == nil {
    89  		return si.val.runtime.createIterResultObject(_undefined, true)
    90  	}
    91  	r, _, err := si.reader.ReadRune()
    92  	if err == io.EOF {
    93  		si.reader = nil
    94  		return si.val.runtime.createIterResultObject(_undefined, true)
    95  	}
    96  	return si.val.runtime.createIterResultObject(stringFromRune(r), false)
    97  }
    98  
    99  func stringFromRune(r rune) String {
   100  	if r < utf8.RuneSelf {
   101  		var sb strings.Builder
   102  		sb.WriteByte(byte(r))
   103  		return asciiString(sb.String())
   104  	}
   105  	var sb unicodeStringBuilder
   106  	sb.WriteRune(r)
   107  	return sb.String()
   108  }
   109  
   110  func (r *Runtime) createStringIterator(s String) Value {
   111  	o := &Object{runtime: r}
   112  
   113  	si := &stringIterObject{
   114  		reader: &lenientUtf16Decoder{utf16Reader: s.utf16Reader()},
   115  	}
   116  	si.class = classObject
   117  	si.val = o
   118  	si.extensible = true
   119  	o.self = si
   120  	si.prototype = r.getStringIteratorPrototype()
   121  	si.init()
   122  
   123  	return o
   124  }
   125  
   126  type stringObject struct {
   127  	baseObject
   128  	value      String
   129  	length     int
   130  	lengthProp valueProperty
   131  }
   132  
   133  func newStringValue(s string) String {
   134  	if u := unistring.Scan(s); u != nil {
   135  		return unicodeString(u)
   136  	}
   137  	return asciiString(s)
   138  }
   139  
   140  func stringValueFromRaw(raw unistring.String) String {
   141  	if b := raw.AsUtf16(); b != nil {
   142  		return unicodeString(b)
   143  	}
   144  	return asciiString(raw)
   145  }
   146  
   147  func (s *stringObject) init() {
   148  	s.baseObject.init()
   149  	s.setLength()
   150  }
   151  
   152  func (s *stringObject) setLength() {
   153  	if s.value != nil {
   154  		s.length = s.value.Length()
   155  	}
   156  	s.lengthProp.value = intToValue(int64(s.length))
   157  	s._put("length", &s.lengthProp)
   158  }
   159  
   160  func (s *stringObject) getStr(name unistring.String, receiver Value) Value {
   161  	if i := strToGoIdx(name); i >= 0 && i < s.length {
   162  		return s._getIdx(i)
   163  	}
   164  	return s.baseObject.getStr(name, receiver)
   165  }
   166  
   167  func (s *stringObject) getIdx(idx valueInt, receiver Value) Value {
   168  	i := int(idx)
   169  	if i >= 0 && i < s.length {
   170  		return s._getIdx(i)
   171  	}
   172  	return s.baseObject.getStr(idx.string(), receiver)
   173  }
   174  
   175  func (s *stringObject) getOwnPropStr(name unistring.String) Value {
   176  	if i := strToGoIdx(name); i >= 0 && i < s.length {
   177  		val := s._getIdx(i)
   178  		return &valueProperty{
   179  			value:      val,
   180  			enumerable: true,
   181  		}
   182  	}
   183  
   184  	return s.baseObject.getOwnPropStr(name)
   185  }
   186  
   187  func (s *stringObject) getOwnPropIdx(idx valueInt) Value {
   188  	i := int64(idx)
   189  	if i >= 0 {
   190  		if i < int64(s.length) {
   191  			val := s._getIdx(int(i))
   192  			return &valueProperty{
   193  				value:      val,
   194  				enumerable: true,
   195  			}
   196  		}
   197  		return nil
   198  	}
   199  
   200  	return s.baseObject.getOwnPropStr(idx.string())
   201  }
   202  
   203  func (s *stringObject) _getIdx(idx int) Value {
   204  	return s.value.Substring(idx, idx+1)
   205  }
   206  
   207  func (s *stringObject) setOwnStr(name unistring.String, val Value, throw bool) bool {
   208  	if i := strToGoIdx(name); i >= 0 && i < s.length {
   209  		s.val.runtime.typeErrorResult(throw, "Cannot assign to read only property '%d' of a String", i)
   210  		return false
   211  	}
   212  
   213  	return s.baseObject.setOwnStr(name, val, throw)
   214  }
   215  
   216  func (s *stringObject) setOwnIdx(idx valueInt, val Value, throw bool) bool {
   217  	i := int64(idx)
   218  	if i >= 0 && i < int64(s.length) {
   219  		s.val.runtime.typeErrorResult(throw, "Cannot assign to read only property '%d' of a String", i)
   220  		return false
   221  	}
   222  
   223  	return s.baseObject.setOwnStr(idx.string(), val, throw)
   224  }
   225  
   226  func (s *stringObject) setForeignStr(name unistring.String, val, receiver Value, throw bool) (bool, bool) {
   227  	return s._setForeignStr(name, s.getOwnPropStr(name), val, receiver, throw)
   228  }
   229  
   230  func (s *stringObject) setForeignIdx(idx valueInt, val, receiver Value, throw bool) (bool, bool) {
   231  	return s._setForeignIdx(idx, s.getOwnPropIdx(idx), val, receiver, throw)
   232  }
   233  
   234  func (s *stringObject) defineOwnPropertyStr(name unistring.String, descr PropertyDescriptor, throw bool) bool {
   235  	if i := strToGoIdx(name); i >= 0 && i < s.length {
   236  		_, ok := s._defineOwnProperty(name, &valueProperty{enumerable: true}, descr, throw)
   237  		return ok
   238  	}
   239  
   240  	return s.baseObject.defineOwnPropertyStr(name, descr, throw)
   241  }
   242  
   243  func (s *stringObject) defineOwnPropertyIdx(idx valueInt, descr PropertyDescriptor, throw bool) bool {
   244  	i := int64(idx)
   245  	if i >= 0 && i < int64(s.length) {
   246  		s.val.runtime.typeErrorResult(throw, "Cannot redefine property: %d", i)
   247  		return false
   248  	}
   249  
   250  	return s.baseObject.defineOwnPropertyStr(idx.string(), descr, throw)
   251  }
   252  
   253  type stringPropIter struct {
   254  	str         String // separate, because obj can be the singleton
   255  	obj         *stringObject
   256  	idx, length int
   257  }
   258  
   259  func (i *stringPropIter) next() (propIterItem, iterNextFunc) {
   260  	if i.idx < i.length {
   261  		name := strconv.Itoa(i.idx)
   262  		i.idx++
   263  		return propIterItem{name: asciiString(name), enumerable: _ENUM_TRUE}, i.next
   264  	}
   265  
   266  	return i.obj.baseObject.iterateStringKeys()()
   267  }
   268  
   269  func (s *stringObject) iterateStringKeys() iterNextFunc {
   270  	return (&stringPropIter{
   271  		str:    s.value,
   272  		obj:    s,
   273  		length: s.length,
   274  	}).next
   275  }
   276  
   277  func (s *stringObject) stringKeys(all bool, accum []Value) []Value {
   278  	for i := 0; i < s.length; i++ {
   279  		accum = append(accum, asciiString(strconv.Itoa(i)))
   280  	}
   281  
   282  	return s.baseObject.stringKeys(all, accum)
   283  }
   284  
   285  func (s *stringObject) deleteStr(name unistring.String, throw bool) bool {
   286  	if i := strToGoIdx(name); i >= 0 && i < s.length {
   287  		s.val.runtime.typeErrorResult(throw, "Cannot delete property '%d' of a String", i)
   288  		return false
   289  	}
   290  
   291  	return s.baseObject.deleteStr(name, throw)
   292  }
   293  
   294  func (s *stringObject) deleteIdx(idx valueInt, throw bool) bool {
   295  	i := int64(idx)
   296  	if i >= 0 && i < int64(s.length) {
   297  		s.val.runtime.typeErrorResult(throw, "Cannot delete property '%d' of a String", i)
   298  		return false
   299  	}
   300  
   301  	return s.baseObject.deleteStr(idx.string(), throw)
   302  }
   303  
   304  func (s *stringObject) hasOwnPropertyStr(name unistring.String) bool {
   305  	if i := strToGoIdx(name); i >= 0 && i < s.length {
   306  		return true
   307  	}
   308  	return s.baseObject.hasOwnPropertyStr(name)
   309  }
   310  
   311  func (s *stringObject) hasOwnPropertyIdx(idx valueInt) bool {
   312  	i := int64(idx)
   313  	if i >= 0 && i < int64(s.length) {
   314  		return true
   315  	}
   316  	return s.baseObject.hasOwnPropertyStr(idx.string())
   317  }
   318  
   319  func devirtualizeString(s String) (asciiString, unicodeString) {
   320  	switch s := s.(type) {
   321  	case asciiString:
   322  		return s, nil
   323  	case unicodeString:
   324  		return "", s
   325  	case *importedString:
   326  		s.ensureScanned()
   327  		if s.u != nil {
   328  			return "", s.u
   329  		}
   330  		return asciiString(s.s), nil
   331  	default:
   332  		panic(unknownStringTypeErr(s))
   333  	}
   334  }
   335  
   336  func unknownStringTypeErr(v Value) interface{} {
   337  	return newTypeError("Internal bug: unknown string type: %T", v)
   338  }
   339  
   340  // StringFromUTF16 creates a string value from an array of UTF-16 code units. The result is a copy, so the initial
   341  // slice can be modified after calling this function (but it must not be modified while the function is running).
   342  // No validation of any kind is performed.
   343  func StringFromUTF16(chars []uint16) String {
   344  	isAscii := true
   345  	for _, c := range chars {
   346  		if c >= utf8.RuneSelf {
   347  			isAscii = false
   348  			break
   349  		}
   350  	}
   351  	if isAscii {
   352  		var sb strings.Builder
   353  		sb.Grow(len(chars))
   354  		for _, c := range chars {
   355  			sb.WriteByte(byte(c))
   356  		}
   357  		return asciiString(sb.String())
   358  	}
   359  	buf := make([]uint16, len(chars)+1)
   360  	buf[0] = unistring.BOM
   361  	copy(buf[1:], chars)
   362  	return unicodeString(buf)
   363  }