Browse Source

Fixed some edge cases

Dmitry Panov 4 years ago
parent
commit
19cfe0fd8a
11 changed files with 93 additions and 47 deletions
  1. 13 6
      builtin_array.go
  2. 1 1
      builtin_date.go
  3. 1 3
      builtin_global.go
  4. 5 1
      builtin_number.go
  5. 30 11
      builtin_regexp.go
  6. 3 1
      builtin_string.go
  7. 4 3
      builtin_typedarrays.go
  8. 5 2
      object.go
  9. 25 8
      regexp.go
  10. 0 1
      string.go
  11. 6 10
      typedarrays.go

+ 13 - 6
builtin_array.go

@@ -267,6 +267,9 @@ func (r *Runtime) arrayproto_concat_append(a *Object, item Value) {
 	aLength := toLength(a.self.getStr("length", nil))
 	if obj, ok := item.(*Object); ok && isConcatSpreadable(obj) {
 		length := toLength(obj.self.getStr("length", nil))
+		if aLength+length >= maxInt {
+			panic(r.NewTypeError("Invalid array length"))
+		}
 		for i := int64(0); i < length; i++ {
 			v := obj.self.getIdx(valueInt(i), nil)
 			if v != nil {
@@ -516,9 +519,11 @@ func (r *Runtime) arrayproto_indexOf(call FunctionCall) Value {
 
 	for ; n < length; n++ {
 		idx := valueInt(n)
-		if val := o.self.getIdx(idx, nil); val != nil {
-			if searchElement.StrictEquals(val) {
-				return idx
+		if o.self.hasPropertyIdx(idx) {
+			if val := o.self.getIdx(idx, nil); val != nil {
+				if searchElement.StrictEquals(val) {
+					return idx
+				}
 			}
 		}
 	}
@@ -601,9 +606,11 @@ func (r *Runtime) arrayproto_lastIndexOf(call FunctionCall) Value {
 
 	for k := fromIndex; k >= 0; k-- {
 		idx := valueInt(k)
-		if val := o.self.getIdx(idx, nil); val != nil {
-			if searchElement.StrictEquals(val) {
-				return idx
+		if o.self.hasPropertyIdx(idx) {
+			if val := o.self.getIdx(idx, nil); val != nil {
+				if searchElement.StrictEquals(val) {
+					return idx
+				}
 			}
 		}
 	}

+ 1 - 1
builtin_date.go

@@ -140,7 +140,7 @@ func (r *Runtime) dateproto_toISOString(call FunctionCall) Value {
 }
 
 func (r *Runtime) dateproto_toJSON(call FunctionCall) Value {
-	obj := r.toObject(call.This)
+	obj := call.This.ToObject(r)
 	tv := obj.toPrimitiveNumber()
 	if f, ok := tv.(valueFloat); ok {
 		f := float64(f)

+ 1 - 3
builtin_global.go

@@ -343,9 +343,7 @@ func (r *Runtime) initGlobalObject() {
 	o._putProp("escape", r.newNativeFunc(r.builtin_escape, nil, "escape", nil, 1), true, false, true)
 	o._putProp("unescape", r.newNativeFunc(r.builtin_unescape, nil, "unescape", nil, 1), true, false, true)
 
-	o._putProp("toString", r.newNativeFunc(func(FunctionCall) Value {
-		return stringGlobalObject
-	}, nil, "toString", nil, 0), false, false, false)
+	o._putSym(SymToStringTag, valueProp(asciiString(classGlobal), false, false, true))
 
 	// TODO: Annex B
 

+ 5 - 1
builtin_number.go

@@ -169,7 +169,11 @@ func (r *Runtime) number_isNaN(call FunctionCall) Value {
 }
 
 func (r *Runtime) number_isSafeInteger(call FunctionCall) Value {
-	if i, ok := call.Argument(0).(valueInt); ok && i >= -(maxInt-1) && i <= maxInt-1 {
+	arg := call.Argument(0)
+	if i, ok := arg.(valueInt); ok && i >= -(maxInt-1) && i <= maxInt-1 {
+		return valueTrue
+	}
+	if arg == _negativeZero {
 		return valueTrue
 	}
 	return valueFalse

+ 30 - 11
builtin_regexp.go

@@ -298,15 +298,15 @@ func (r *Runtime) builtin_newRegExp(args []Value, proto *Object) *Object {
 func (r *Runtime) newRegExp(patternVal, flagsVal Value, proto *Object) *Object {
 	var pattern valueString
 	var flags string
-	if obj, ok := patternVal.(*Object); ok {
-		if rx, ok := obj.self.(*regexpObject); ok {
-			if flagsVal == nil || flagsVal == _undefined {
-				return rx.clone()
+	if isRegexp(patternVal) { // this may have side effects so need to call it anyway
+		if obj, ok := patternVal.(*Object); ok {
+			if rx, ok := obj.self.(*regexpObject); ok {
+				if flagsVal == nil || flagsVal == _undefined {
+					return rx.clone()
+				} else {
+					return r._newRegExp(rx.source, flagsVal.toString().String(), proto)
+				}
 			} else {
-				return r._newRegExp(rx.source, flagsVal.toString().String(), proto)
-			}
-		} else {
-			if isRegexp(patternVal) {
 				pattern = nilSafe(obj.self.getStr("source", nil)).toString()
 				if flagsVal == nil || flagsVal == _undefined {
 					flags = nilSafe(obj.self.getStr("flags", nil)).toString().String()
@@ -660,8 +660,8 @@ func (r *Runtime) getGlobalRegexpMatches(rxObj *Object, s valueString) []Value {
 		a = append(a, res)
 		matchStr := nilSafe(r.toObject(res).self.getIdx(valueInt(0), nil)).toString()
 		if matchStr.length() == 0 {
-			thisIndex := toIntStrict(nilSafe(rxObj.self.getStr("lastIndex", nil)).ToInteger())
-			rxObj.self.setOwnStr("lastIndex", valueInt(advanceStringIndex(s, thisIndex, fullUnicode)), true)
+			thisIndex := toLength(rxObj.self.getStr("lastIndex", nil))
+			rxObj.self.setOwnStr("lastIndex", valueInt(advanceStringIndex64(s, thisIndex, fullUnicode)), true)
 		}
 	}
 
@@ -855,6 +855,24 @@ func advanceStringIndex(s valueString, pos int, unicode bool) int {
 	return next + 1
 }
 
+func advanceStringIndex64(s valueString, pos int64, unicode bool) int64 {
+	next := pos + 1
+	if !unicode {
+		return next
+	}
+	l := int64(s.length())
+	if next >= l {
+		return next
+	}
+	if !isUTF16FirstSurrogate(s.charAt(int(pos))) {
+		return next
+	}
+	if !isUTF16SecondSurrogate(s.charAt(int(next))) {
+		return next
+	}
+	return next + 1
+}
+
 func (r *Runtime) regexpproto_stdSplitter(call FunctionCall) Value {
 	rxObj := r.toObject(call.This)
 	s := call.Argument(0).toString()
@@ -877,6 +895,7 @@ func (r *Runtime) regexpproto_stdSplitter(call FunctionCall) Value {
 		}
 	}
 
+	pattern := search.pattern // toUint32() may recompile the pattern, but we still need to use the original
 	limit := -1
 	if limitValue != _undefined {
 		limit = int(toUint32(limitValue))
@@ -891,7 +910,7 @@ func (r *Runtime) regexpproto_stdSplitter(call FunctionCall) Value {
 	lastIndex := 0
 	found := 0
 
-	result := search.pattern.findAllSubmatchIndex(s, 0, -1, false)
+	result := pattern.findAllSubmatchIndex(s, 0, -1, false)
 	if targetLength == 0 {
 		if result == nil {
 			valueArray = append(valueArray, s)

+ 3 - 1
builtin_string.go

@@ -735,6 +735,8 @@ func (r *Runtime) stringproto_split(call FunctionCall) Value {
 		limit = int(toUint32(limitValue))
 	}
 
+	separatorValue = separatorValue.ToString()
+
 	if limit == 0 {
 		return r.newArrayValues(nil)
 	}
@@ -743,7 +745,7 @@ func (r *Runtime) stringproto_split(call FunctionCall) Value {
 		return r.newArrayValues([]Value{s})
 	}
 
-	separator := separatorValue.toString().String()
+	separator := separatorValue.String()
 
 	excess := false
 	str := s.String()

+ 4 - 3
builtin_typedarrays.go

@@ -407,6 +407,7 @@ func (r *Runtime) typedArrayProto_copyWithin(call FunctionCall) Value {
 		offset := ta.offset
 		elemSize := ta.elemSize
 		if final > from {
+			ta.viewedArrayBuf.ensureNotDetached()
 			copy(data[(offset+to)*elemSize:], data[(offset+from)*elemSize:(offset+final)*elemSize])
 		}
 		return call.This
@@ -828,7 +829,7 @@ func (r *Runtime) typedArrayProto_reverse(call FunctionCall) Value {
 
 func (r *Runtime) typedArrayProto_set(call FunctionCall) Value {
 	if ta, ok := r.toObject(call.This).self.(*typedArrayObject); ok {
-		srcObj := r.toObject(call.Argument(0))
+		srcObj := call.Argument(0).ToObject(r)
 		targetOffset := toIntStrict(call.Argument(1).ToInteger())
 		if targetOffset < 0 {
 			panic(r.newError(r.global.RangeError, "offset should be >= 0"))
@@ -963,8 +964,8 @@ func (r *Runtime) typedArrayProto_sort(call FunctionCall) Value {
 		ta.viewedArrayBuf.ensureNotDetached()
 		var compareFn func(FunctionCall) Value
 
-		if arg, ok := call.Argument(0).(*Object); ok {
-			compareFn, _ = arg.self.assertCallable()
+		if arg := call.Argument(0); arg != _undefined {
+			compareFn = r.toCallable(arg)
 		}
 
 		ctx := typedArraySortCtx{

+ 5 - 2
object.go

@@ -27,6 +27,7 @@ const (
 	classRegExp   = "RegExp"
 	classDate     = "Date"
 	classJSON     = "JSON"
+	classGlobal   = "global"
 
 	classArrayIterator  = "Array Iterator"
 	classMapIterator    = "Map Iterator"
@@ -485,12 +486,14 @@ func (o *baseObject) setProto(proto *Object, throw bool) bool {
 		o.val.runtime.typeErrorResult(throw, "%s is not extensible", o.val)
 		return false
 	}
-	for p := proto; p != nil; {
+	for p := proto; p != nil; p = p.self.proto() {
 		if p.SameAs(o.val) {
 			o.val.runtime.typeErrorResult(throw, "Cyclic __proto__ value")
 			return false
 		}
-		p = p.self.proto()
+		if _, ok := p.self.(*proxyObject); ok {
+			break
+		}
 	}
 	o.prototype = proto
 	return true

+ 25 - 8
regexp.go

@@ -608,6 +608,17 @@ func (r *regexpObject) defineOwnPropertyStr(name unistring.String, desc Property
 	return res
 }
 
+func (r *regexpObject) defineOwnPropertySym(name *Symbol, desc PropertyDescriptor, throw bool) bool {
+	res := r.baseObject.defineOwnPropertySym(name, desc, throw)
+	if res && r.standard {
+		switch name {
+		case SymMatch, SymSearch, SymSplit, SymReplace:
+			r.standard = false
+		}
+	}
+	return res
+}
+
 func (r *regexpObject) deleteStr(name unistring.String, throw bool) bool {
 	res := r.baseObject.deleteStr(name, throw)
 	if res {
@@ -617,14 +628,20 @@ func (r *regexpObject) deleteStr(name unistring.String, throw bool) bool {
 }
 
 func (r *regexpObject) setOwnStr(name unistring.String, value Value, throw bool) bool {
-	if r.standard {
-		if name == "exec" {
-			res := r.baseObject.setOwnStr(name, value, throw)
-			if res {
-				r.standard = false
-			}
-			return res
+	res := r.baseObject.setOwnStr(name, value, throw)
+	if res && r.standard && name == "exec" {
+		r.standard = false
+	}
+	return res
+}
+
+func (r *regexpObject) setOwnSym(name *Symbol, value Value, throw bool) bool {
+	res := r.baseObject.setOwnSym(name, value, throw)
+	if res && r.standard {
+		switch name {
+		case SymMatch, SymSearch, SymSplit, SymReplace:
+			r.standard = false
 		}
 	}
-	return r.baseObject.setOwnStr(name, value, throw)
+	return res
 }

+ 0 - 1
string.go

@@ -44,7 +44,6 @@ var (
 	stringObjectNull      valueString = asciiString("[object Null]")
 	stringObjectObject    valueString = asciiString("[object Object]")
 	stringObjectUndefined valueString = asciiString("[object Undefined]")
-	stringGlobalObject    valueString = asciiString("Global Object")
 	stringInvalidDate     valueString = asciiString("Invalid Date")
 )
 

+ 6 - 10
typedarrays.go

@@ -385,19 +385,15 @@ func (a *float32Array) setRaw(idx int, v uint64) {
 func typedFloatLess(x, y float64) bool {
 	xNan := math.IsNaN(x)
 	yNan := math.IsNaN(y)
-	if xNan && yNan {
-		return false
-	}
-	if xNan {
-		return false
-	}
 	if yNan {
-		return true
-	}
-	if x >= y {
+		return !xNan
+	} else if xNan {
 		return false
 	}
-	return true
+	if x == 0 && y == 0 { // handle neg zero
+		return math.Signbit(x)
+	}
+	return x < y
 }
 
 func (a *float32Array) less(i, j int) bool {