mirror of
				https://github.com/robertkrimen/otto
				synced 2025-10-26 20:28:49 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			682 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			682 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package otto
 | |
| 
 | |
| import (
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| )
 | |
| 
 | |
| // Array
 | |
| 
 | |
| func builtinArray(call FunctionCall) Value {
 | |
| 	return toValue_object(builtinNewArrayNative(call.runtime, call.ArgumentList))
 | |
| }
 | |
| 
 | |
| func builtinNewArray(self *_object, argumentList []Value) Value {
 | |
| 	return toValue_object(builtinNewArrayNative(self.runtime, argumentList))
 | |
| }
 | |
| 
 | |
| func builtinNewArrayNative(runtime *_runtime, argumentList []Value) *_object {
 | |
| 	if len(argumentList) == 1 {
 | |
| 		firstArgument := argumentList[0]
 | |
| 		if firstArgument.IsNumber() {
 | |
| 			return runtime.newArray(arrayUint32(runtime, firstArgument))
 | |
| 		}
 | |
| 	}
 | |
| 	return runtime.newArrayOf(argumentList)
 | |
| }
 | |
| 
 | |
| func builtinArray_toString(call FunctionCall) Value {
 | |
| 	thisObject := call.thisObject()
 | |
| 	join := thisObject.get("join")
 | |
| 	if join.isCallable() {
 | |
| 		join := join._object()
 | |
| 		return join.call(call.This, call.ArgumentList, false, nativeFrame)
 | |
| 	}
 | |
| 	return builtinObject_toString(call)
 | |
| }
 | |
| 
 | |
| func builtinArray_toLocaleString(call FunctionCall) Value {
 | |
| 	separator := ","
 | |
| 	thisObject := call.thisObject()
 | |
| 	length := int64(toUint32(thisObject.get("length")))
 | |
| 	if length == 0 {
 | |
| 		return toValue_string("")
 | |
| 	}
 | |
| 	stringList := make([]string, 0, length)
 | |
| 	for index := int64(0); index < length; index += 1 {
 | |
| 		value := thisObject.get(arrayIndexToString(index))
 | |
| 		stringValue := ""
 | |
| 		switch value.kind {
 | |
| 		case valueEmpty, valueUndefined, valueNull:
 | |
| 		default:
 | |
| 			object := call.runtime.toObject(value)
 | |
| 			toLocaleString := object.get("toLocaleString")
 | |
| 			if !toLocaleString.isCallable() {
 | |
| 				panic(call.runtime.panicTypeError())
 | |
| 			}
 | |
| 			stringValue = toLocaleString.call(call.runtime, toValue_object(object)).string()
 | |
| 		}
 | |
| 		stringList = append(stringList, stringValue)
 | |
| 	}
 | |
| 	return toValue_string(strings.Join(stringList, separator))
 | |
| }
 | |
| 
 | |
| func builtinArray_concat(call FunctionCall) Value {
 | |
| 	thisObject := call.thisObject()
 | |
| 	valueArray := []Value{}
 | |
| 	source := append([]Value{toValue_object(thisObject)}, call.ArgumentList...)
 | |
| 	for _, item := range source {
 | |
| 		switch item.kind {
 | |
| 		case valueObject:
 | |
| 			object := item._object()
 | |
| 			if isArray(object) {
 | |
| 				length := object.get("length").number().int64
 | |
| 				for index := int64(0); index < length; index += 1 {
 | |
| 					name := strconv.FormatInt(index, 10)
 | |
| 					if object.hasProperty(name) {
 | |
| 						valueArray = append(valueArray, object.get(name))
 | |
| 					} else {
 | |
| 						valueArray = append(valueArray, Value{})
 | |
| 					}
 | |
| 				}
 | |
| 				continue
 | |
| 			}
 | |
| 			fallthrough
 | |
| 		default:
 | |
| 			valueArray = append(valueArray, item)
 | |
| 		}
 | |
| 	}
 | |
| 	return toValue_object(call.runtime.newArrayOf(valueArray))
 | |
| }
 | |
| 
 | |
| func builtinArray_shift(call FunctionCall) Value {
 | |
| 	thisObject := call.thisObject()
 | |
| 	length := int64(toUint32(thisObject.get("length")))
 | |
| 	if 0 == length {
 | |
| 		thisObject.put("length", toValue_int64(0), true)
 | |
| 		return Value{}
 | |
| 	}
 | |
| 	first := thisObject.get("0")
 | |
| 	for index := int64(1); index < length; index++ {
 | |
| 		from := arrayIndexToString(index)
 | |
| 		to := arrayIndexToString(index - 1)
 | |
| 		if thisObject.hasProperty(from) {
 | |
| 			thisObject.put(to, thisObject.get(from), true)
 | |
| 		} else {
 | |
| 			thisObject.delete(to, true)
 | |
| 		}
 | |
| 	}
 | |
| 	thisObject.delete(arrayIndexToString(length-1), true)
 | |
| 	thisObject.put("length", toValue_int64(length-1), true)
 | |
| 	return first
 | |
| }
 | |
| 
 | |
| func builtinArray_push(call FunctionCall) Value {
 | |
| 	thisObject := call.thisObject()
 | |
| 	itemList := call.ArgumentList
 | |
| 	index := int64(toUint32(thisObject.get("length")))
 | |
| 	for len(itemList) > 0 {
 | |
| 		thisObject.put(arrayIndexToString(index), itemList[0], true)
 | |
| 		itemList = itemList[1:]
 | |
| 		index += 1
 | |
| 	}
 | |
| 	length := toValue_int64(index)
 | |
| 	thisObject.put("length", length, true)
 | |
| 	return length
 | |
| }
 | |
| 
 | |
| func builtinArray_pop(call FunctionCall) Value {
 | |
| 	thisObject := call.thisObject()
 | |
| 	length := int64(toUint32(thisObject.get("length")))
 | |
| 	if 0 == length {
 | |
| 		thisObject.put("length", toValue_uint32(0), true)
 | |
| 		return Value{}
 | |
| 	}
 | |
| 	last := thisObject.get(arrayIndexToString(length - 1))
 | |
| 	thisObject.delete(arrayIndexToString(length-1), true)
 | |
| 	thisObject.put("length", toValue_int64(length-1), true)
 | |
| 	return last
 | |
| }
 | |
| 
 | |
| func builtinArray_join(call FunctionCall) Value {
 | |
| 	separator := ","
 | |
| 	{
 | |
| 		argument := call.Argument(0)
 | |
| 		if argument.IsDefined() {
 | |
| 			separator = argument.string()
 | |
| 		}
 | |
| 	}
 | |
| 	thisObject := call.thisObject()
 | |
| 	length := int64(toUint32(thisObject.get("length")))
 | |
| 	if length == 0 {
 | |
| 		return toValue_string("")
 | |
| 	}
 | |
| 	stringList := make([]string, 0, length)
 | |
| 	for index := int64(0); index < length; index += 1 {
 | |
| 		value := thisObject.get(arrayIndexToString(index))
 | |
| 		stringValue := ""
 | |
| 		switch value.kind {
 | |
| 		case valueEmpty, valueUndefined, valueNull:
 | |
| 		default:
 | |
| 			stringValue = value.string()
 | |
| 		}
 | |
| 		stringList = append(stringList, stringValue)
 | |
| 	}
 | |
| 	return toValue_string(strings.Join(stringList, separator))
 | |
| }
 | |
| 
 | |
| func builtinArray_splice(call FunctionCall) Value {
 | |
| 	thisObject := call.thisObject()
 | |
| 	length := int64(toUint32(thisObject.get("length")))
 | |
| 
 | |
| 	start := valueToRangeIndex(call.Argument(0), length, false)
 | |
| 	deleteCount := valueToRangeIndex(call.Argument(1), int64(length)-start, true)
 | |
| 	valueArray := make([]Value, deleteCount)
 | |
| 
 | |
| 	for index := int64(0); index < deleteCount; index++ {
 | |
| 		indexString := arrayIndexToString(int64(start + index))
 | |
| 		if thisObject.hasProperty(indexString) {
 | |
| 			valueArray[index] = thisObject.get(indexString)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// 0, <1, 2, 3, 4>, 5, 6, 7
 | |
| 	// a, b
 | |
| 	// length 8 - delete 4 @ start 1
 | |
| 
 | |
| 	itemList := []Value{}
 | |
| 	itemCount := int64(len(call.ArgumentList))
 | |
| 	if itemCount > 2 {
 | |
| 		itemCount -= 2 // Less the first two arguments
 | |
| 		itemList = call.ArgumentList[2:]
 | |
| 	} else {
 | |
| 		itemCount = 0
 | |
| 	}
 | |
| 	if itemCount < deleteCount {
 | |
| 		// The Object/Array is shrinking
 | |
| 		stop := int64(length) - deleteCount
 | |
| 		// The new length of the Object/Array before
 | |
| 		// appending the itemList remainder
 | |
| 		// Stopping at the lower bound of the insertion:
 | |
| 		// Move an item from the after the deleted portion
 | |
| 		// to a position after the inserted portion
 | |
| 		for index := start; index < stop; index++ {
 | |
| 			from := arrayIndexToString(index + deleteCount) // Position just after deletion
 | |
| 			to := arrayIndexToString(index + itemCount)     // Position just after splice (insertion)
 | |
| 			if thisObject.hasProperty(from) {
 | |
| 				thisObject.put(to, thisObject.get(from), true)
 | |
| 			} else {
 | |
| 				thisObject.delete(to, true)
 | |
| 			}
 | |
| 		}
 | |
| 		// Delete off the end
 | |
| 		// We don't bother to delete below <stop + itemCount> (if any) since those
 | |
| 		// will be overwritten anyway
 | |
| 		for index := int64(length); index > (stop + itemCount); index-- {
 | |
| 			thisObject.delete(arrayIndexToString(index-1), true)
 | |
| 		}
 | |
| 	} else if itemCount > deleteCount {
 | |
| 		// The Object/Array is growing
 | |
| 		// The itemCount is greater than the deleteCount, so we do
 | |
| 		// not have to worry about overwriting what we should be moving
 | |
| 		// ---
 | |
| 		// Starting from the upper bound of the deletion:
 | |
| 		// Move an item from the after the deleted portion
 | |
| 		// to a position after the inserted portion
 | |
| 		for index := int64(length) - deleteCount; index > start; index-- {
 | |
| 			from := arrayIndexToString(index + deleteCount - 1)
 | |
| 			to := arrayIndexToString(index + itemCount - 1)
 | |
| 			if thisObject.hasProperty(from) {
 | |
| 				thisObject.put(to, thisObject.get(from), true)
 | |
| 			} else {
 | |
| 				thisObject.delete(to, true)
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	for index := int64(0); index < itemCount; index++ {
 | |
| 		thisObject.put(arrayIndexToString(index+start), itemList[index], true)
 | |
| 	}
 | |
| 	thisObject.put("length", toValue_int64(int64(length)+itemCount-deleteCount), true)
 | |
| 
 | |
| 	return toValue_object(call.runtime.newArrayOf(valueArray))
 | |
| }
 | |
| 
 | |
| func builtinArray_slice(call FunctionCall) Value {
 | |
| 	thisObject := call.thisObject()
 | |
| 
 | |
| 	length := int64(toUint32(thisObject.get("length")))
 | |
| 	start, end := rangeStartEnd(call.ArgumentList, length, false)
 | |
| 
 | |
| 	if start >= end {
 | |
| 		// Always an empty array
 | |
| 		return toValue_object(call.runtime.newArray(0))
 | |
| 	}
 | |
| 	sliceLength := end - start
 | |
| 	sliceValueArray := make([]Value, sliceLength)
 | |
| 
 | |
| 	for index := int64(0); index < sliceLength; index++ {
 | |
| 		from := arrayIndexToString(index + start)
 | |
| 		if thisObject.hasProperty(from) {
 | |
| 			sliceValueArray[index] = thisObject.get(from)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return toValue_object(call.runtime.newArrayOf(sliceValueArray))
 | |
| }
 | |
| 
 | |
| func builtinArray_unshift(call FunctionCall) Value {
 | |
| 	thisObject := call.thisObject()
 | |
| 	length := int64(toUint32(thisObject.get("length")))
 | |
| 	itemList := call.ArgumentList
 | |
| 	itemCount := int64(len(itemList))
 | |
| 
 | |
| 	for index := length; index > 0; index-- {
 | |
| 		from := arrayIndexToString(index - 1)
 | |
| 		to := arrayIndexToString(index + itemCount - 1)
 | |
| 		if thisObject.hasProperty(from) {
 | |
| 			thisObject.put(to, thisObject.get(from), true)
 | |
| 		} else {
 | |
| 			thisObject.delete(to, true)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	for index := int64(0); index < itemCount; index++ {
 | |
| 		thisObject.put(arrayIndexToString(index), itemList[index], true)
 | |
| 	}
 | |
| 
 | |
| 	newLength := toValue_int64(length + itemCount)
 | |
| 	thisObject.put("length", newLength, true)
 | |
| 	return newLength
 | |
| }
 | |
| 
 | |
| func builtinArray_reverse(call FunctionCall) Value {
 | |
| 	thisObject := call.thisObject()
 | |
| 	length := int64(toUint32(thisObject.get("length")))
 | |
| 
 | |
| 	lower := struct {
 | |
| 		name   string
 | |
| 		index  int64
 | |
| 		exists bool
 | |
| 	}{}
 | |
| 	upper := lower
 | |
| 
 | |
| 	lower.index = 0
 | |
| 	middle := length / 2 // Division will floor
 | |
| 
 | |
| 	for lower.index != middle {
 | |
| 		lower.name = arrayIndexToString(lower.index)
 | |
| 		upper.index = length - lower.index - 1
 | |
| 		upper.name = arrayIndexToString(upper.index)
 | |
| 
 | |
| 		lower.exists = thisObject.hasProperty(lower.name)
 | |
| 		upper.exists = thisObject.hasProperty(upper.name)
 | |
| 
 | |
| 		if lower.exists && upper.exists {
 | |
| 			lowerValue := thisObject.get(lower.name)
 | |
| 			upperValue := thisObject.get(upper.name)
 | |
| 			thisObject.put(lower.name, upperValue, true)
 | |
| 			thisObject.put(upper.name, lowerValue, true)
 | |
| 		} else if !lower.exists && upper.exists {
 | |
| 			value := thisObject.get(upper.name)
 | |
| 			thisObject.delete(upper.name, true)
 | |
| 			thisObject.put(lower.name, value, true)
 | |
| 		} else if lower.exists && !upper.exists {
 | |
| 			value := thisObject.get(lower.name)
 | |
| 			thisObject.delete(lower.name, true)
 | |
| 			thisObject.put(upper.name, value, true)
 | |
| 		} else {
 | |
| 			// Nothing happens.
 | |
| 		}
 | |
| 
 | |
| 		lower.index += 1
 | |
| 	}
 | |
| 
 | |
| 	return call.This
 | |
| }
 | |
| 
 | |
| func sortCompare(thisObject *_object, index0, index1 uint, compare *_object) int {
 | |
| 	j := struct {
 | |
| 		name    string
 | |
| 		exists  bool
 | |
| 		defined bool
 | |
| 		value   string
 | |
| 	}{}
 | |
| 	k := j
 | |
| 	j.name = arrayIndexToString(int64(index0))
 | |
| 	j.exists = thisObject.hasProperty(j.name)
 | |
| 	k.name = arrayIndexToString(int64(index1))
 | |
| 	k.exists = thisObject.hasProperty(k.name)
 | |
| 
 | |
| 	if !j.exists && !k.exists {
 | |
| 		return 0
 | |
| 	} else if !j.exists {
 | |
| 		return 1
 | |
| 	} else if !k.exists {
 | |
| 		return -1
 | |
| 	}
 | |
| 
 | |
| 	x := thisObject.get(j.name)
 | |
| 	y := thisObject.get(k.name)
 | |
| 	j.defined = x.IsDefined()
 | |
| 	k.defined = y.IsDefined()
 | |
| 
 | |
| 	if !j.defined && !k.defined {
 | |
| 		return 0
 | |
| 	} else if !j.defined {
 | |
| 		return 1
 | |
| 	} else if !k.defined {
 | |
| 		return -1
 | |
| 	}
 | |
| 
 | |
| 	if compare == nil {
 | |
| 		j.value = x.string()
 | |
| 		k.value = y.string()
 | |
| 
 | |
| 		if j.value == k.value {
 | |
| 			return 0
 | |
| 		} else if j.value < k.value {
 | |
| 			return -1
 | |
| 		}
 | |
| 
 | |
| 		return 1
 | |
| 	}
 | |
| 
 | |
| 	return int(toInt32(compare.call(Value{}, []Value{x, y}, false, nativeFrame)))
 | |
| }
 | |
| 
 | |
| func arraySortSwap(thisObject *_object, index0, index1 uint) {
 | |
| 
 | |
| 	j := struct {
 | |
| 		name   string
 | |
| 		exists bool
 | |
| 	}{}
 | |
| 	k := j
 | |
| 
 | |
| 	j.name = arrayIndexToString(int64(index0))
 | |
| 	j.exists = thisObject.hasProperty(j.name)
 | |
| 	k.name = arrayIndexToString(int64(index1))
 | |
| 	k.exists = thisObject.hasProperty(k.name)
 | |
| 
 | |
| 	if j.exists && k.exists {
 | |
| 		jValue := thisObject.get(j.name)
 | |
| 		kValue := thisObject.get(k.name)
 | |
| 		thisObject.put(j.name, kValue, true)
 | |
| 		thisObject.put(k.name, jValue, true)
 | |
| 	} else if !j.exists && k.exists {
 | |
| 		value := thisObject.get(k.name)
 | |
| 		thisObject.delete(k.name, true)
 | |
| 		thisObject.put(j.name, value, true)
 | |
| 	} else if j.exists && !k.exists {
 | |
| 		value := thisObject.get(j.name)
 | |
| 		thisObject.delete(j.name, true)
 | |
| 		thisObject.put(k.name, value, true)
 | |
| 	} else {
 | |
| 		// Nothing happens.
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func arraySortQuickPartition(thisObject *_object, left, right, pivot uint, compare *_object) (uint, uint) {
 | |
| 	arraySortSwap(thisObject, pivot, right) // Right is now the pivot value
 | |
| 	cursor := left
 | |
| 	cursor2 := left
 | |
| 	for index := left; index < right; index++ {
 | |
| 		comparison := sortCompare(thisObject, index, right, compare) // Compare to the pivot value
 | |
| 		if comparison < 0 {
 | |
| 			arraySortSwap(thisObject, index, cursor)
 | |
| 			if cursor < cursor2 {
 | |
| 				arraySortSwap(thisObject, index, cursor2)
 | |
| 			}
 | |
| 			cursor += 1
 | |
| 			cursor2 += 1
 | |
| 		} else if comparison == 0 {
 | |
| 			arraySortSwap(thisObject, index, cursor2)
 | |
| 			cursor2 += 1
 | |
| 		}
 | |
| 	}
 | |
| 	arraySortSwap(thisObject, cursor2, right)
 | |
| 	return cursor, cursor2
 | |
| }
 | |
| 
 | |
| func arraySortQuickSort(thisObject *_object, left, right uint, compare *_object) {
 | |
| 	if left < right {
 | |
| 		middle := left + (right-left)/2
 | |
| 		pivot, pivot2 := arraySortQuickPartition(thisObject, left, right, middle, compare)
 | |
| 		if pivot > 0 {
 | |
| 			arraySortQuickSort(thisObject, left, pivot-1, compare)
 | |
| 		}
 | |
| 		arraySortQuickSort(thisObject, pivot2+1, right, compare)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func builtinArray_sort(call FunctionCall) Value {
 | |
| 	thisObject := call.thisObject()
 | |
| 	length := uint(toUint32(thisObject.get("length")))
 | |
| 	compareValue := call.Argument(0)
 | |
| 	compare := compareValue._object()
 | |
| 	if compareValue.IsUndefined() {
 | |
| 	} else if !compareValue.isCallable() {
 | |
| 		panic(call.runtime.panicTypeError())
 | |
| 	}
 | |
| 	if length > 1 {
 | |
| 		arraySortQuickSort(thisObject, 0, length-1, compare)
 | |
| 	}
 | |
| 	return call.This
 | |
| }
 | |
| 
 | |
| func builtinArray_isArray(call FunctionCall) Value {
 | |
| 	return toValue_bool(isArray(call.Argument(0)._object()))
 | |
| }
 | |
| 
 | |
| func builtinArray_indexOf(call FunctionCall) Value {
 | |
| 	thisObject, matchValue := call.thisObject(), call.Argument(0)
 | |
| 	if length := int64(toUint32(thisObject.get("length"))); length > 0 {
 | |
| 		index := int64(0)
 | |
| 		if len(call.ArgumentList) > 1 {
 | |
| 			index = call.Argument(1).number().int64
 | |
| 		}
 | |
| 		if index < 0 {
 | |
| 			if index += length; index < 0 {
 | |
| 				index = 0
 | |
| 			}
 | |
| 		} else if index >= length {
 | |
| 			index = -1
 | |
| 		}
 | |
| 		for ; index >= 0 && index < length; index++ {
 | |
| 			name := arrayIndexToString(int64(index))
 | |
| 			if !thisObject.hasProperty(name) {
 | |
| 				continue
 | |
| 			}
 | |
| 			value := thisObject.get(name)
 | |
| 			if strictEqualityComparison(matchValue, value) {
 | |
| 				return toValue_uint32(uint32(index))
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return toValue_int(-1)
 | |
| }
 | |
| 
 | |
| func builtinArray_lastIndexOf(call FunctionCall) Value {
 | |
| 	thisObject, matchValue := call.thisObject(), call.Argument(0)
 | |
| 	length := int64(toUint32(thisObject.get("length")))
 | |
| 	index := length - 1
 | |
| 	if len(call.ArgumentList) > 1 {
 | |
| 		index = call.Argument(1).number().int64
 | |
| 	}
 | |
| 	if 0 > index {
 | |
| 		index += length
 | |
| 	}
 | |
| 	if index > length {
 | |
| 		index = length - 1
 | |
| 	} else if 0 > index {
 | |
| 		return toValue_int(-1)
 | |
| 	}
 | |
| 	for ; index >= 0; index-- {
 | |
| 		name := arrayIndexToString(int64(index))
 | |
| 		if !thisObject.hasProperty(name) {
 | |
| 			continue
 | |
| 		}
 | |
| 		value := thisObject.get(name)
 | |
| 		if strictEqualityComparison(matchValue, value) {
 | |
| 			return toValue_uint32(uint32(index))
 | |
| 		}
 | |
| 	}
 | |
| 	return toValue_int(-1)
 | |
| }
 | |
| 
 | |
| func builtinArray_every(call FunctionCall) Value {
 | |
| 	thisObject := call.thisObject()
 | |
| 	this := toValue_object(thisObject)
 | |
| 	if iterator := call.Argument(0); iterator.isCallable() {
 | |
| 		length := int64(toUint32(thisObject.get("length")))
 | |
| 		callThis := call.Argument(1)
 | |
| 		for index := int64(0); index < length; index++ {
 | |
| 			if key := arrayIndexToString(index); thisObject.hasProperty(key) {
 | |
| 				if value := thisObject.get(key); iterator.call(call.runtime, callThis, value, toValue_int64(index), this).bool() {
 | |
| 					continue
 | |
| 				}
 | |
| 				return falseValue
 | |
| 			}
 | |
| 		}
 | |
| 		return trueValue
 | |
| 	}
 | |
| 	panic(call.runtime.panicTypeError())
 | |
| }
 | |
| 
 | |
| func builtinArray_some(call FunctionCall) Value {
 | |
| 	thisObject := call.thisObject()
 | |
| 	this := toValue_object(thisObject)
 | |
| 	if iterator := call.Argument(0); iterator.isCallable() {
 | |
| 		length := int64(toUint32(thisObject.get("length")))
 | |
| 		callThis := call.Argument(1)
 | |
| 		for index := int64(0); index < length; index++ {
 | |
| 			if key := arrayIndexToString(index); thisObject.hasProperty(key) {
 | |
| 				if value := thisObject.get(key); iterator.call(call.runtime, callThis, value, toValue_int64(index), this).bool() {
 | |
| 					return trueValue
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		return falseValue
 | |
| 	}
 | |
| 	panic(call.runtime.panicTypeError())
 | |
| }
 | |
| 
 | |
| func builtinArray_forEach(call FunctionCall) Value {
 | |
| 	thisObject := call.thisObject()
 | |
| 	this := toValue_object(thisObject)
 | |
| 	if iterator := call.Argument(0); iterator.isCallable() {
 | |
| 		length := int64(toUint32(thisObject.get("length")))
 | |
| 		callThis := call.Argument(1)
 | |
| 		for index := int64(0); index < length; index++ {
 | |
| 			if key := arrayIndexToString(index); thisObject.hasProperty(key) {
 | |
| 				iterator.call(call.runtime, callThis, thisObject.get(key), toValue_int64(index), this)
 | |
| 			}
 | |
| 		}
 | |
| 		return Value{}
 | |
| 	}
 | |
| 	panic(call.runtime.panicTypeError())
 | |
| }
 | |
| 
 | |
| func builtinArray_map(call FunctionCall) Value {
 | |
| 	thisObject := call.thisObject()
 | |
| 	this := toValue_object(thisObject)
 | |
| 	if iterator := call.Argument(0); iterator.isCallable() {
 | |
| 		length := int64(toUint32(thisObject.get("length")))
 | |
| 		callThis := call.Argument(1)
 | |
| 		values := make([]Value, length)
 | |
| 		for index := int64(0); index < length; index++ {
 | |
| 			if key := arrayIndexToString(index); thisObject.hasProperty(key) {
 | |
| 				values[index] = iterator.call(call.runtime, callThis, thisObject.get(key), index, this)
 | |
| 			} else {
 | |
| 				values[index] = Value{}
 | |
| 			}
 | |
| 		}
 | |
| 		return toValue_object(call.runtime.newArrayOf(values))
 | |
| 	}
 | |
| 	panic(call.runtime.panicTypeError())
 | |
| }
 | |
| 
 | |
| func builtinArray_filter(call FunctionCall) Value {
 | |
| 	thisObject := call.thisObject()
 | |
| 	this := toValue_object(thisObject)
 | |
| 	if iterator := call.Argument(0); iterator.isCallable() {
 | |
| 		length := int64(toUint32(thisObject.get("length")))
 | |
| 		callThis := call.Argument(1)
 | |
| 		values := make([]Value, 0)
 | |
| 		for index := int64(0); index < length; index++ {
 | |
| 			if key := arrayIndexToString(index); thisObject.hasProperty(key) {
 | |
| 				value := thisObject.get(key)
 | |
| 				if iterator.call(call.runtime, callThis, value, index, this).bool() {
 | |
| 					values = append(values, value)
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		return toValue_object(call.runtime.newArrayOf(values))
 | |
| 	}
 | |
| 	panic(call.runtime.panicTypeError())
 | |
| }
 | |
| 
 | |
| func builtinArray_reduce(call FunctionCall) Value {
 | |
| 	thisObject := call.thisObject()
 | |
| 	this := toValue_object(thisObject)
 | |
| 	if iterator := call.Argument(0); iterator.isCallable() {
 | |
| 		initial := len(call.ArgumentList) > 1
 | |
| 		start := call.Argument(1)
 | |
| 		length := int64(toUint32(thisObject.get("length")))
 | |
| 		index := int64(0)
 | |
| 		if length > 0 || initial {
 | |
| 			var accumulator Value
 | |
| 			if !initial {
 | |
| 				for ; index < length; index++ {
 | |
| 					if key := arrayIndexToString(index); thisObject.hasProperty(key) {
 | |
| 						accumulator = thisObject.get(key)
 | |
| 						index++
 | |
| 						break
 | |
| 					}
 | |
| 				}
 | |
| 			} else {
 | |
| 				accumulator = start
 | |
| 			}
 | |
| 			for ; index < length; index++ {
 | |
| 				if key := arrayIndexToString(index); thisObject.hasProperty(key) {
 | |
| 					accumulator = iterator.call(call.runtime, Value{}, accumulator, thisObject.get(key), key, this)
 | |
| 				}
 | |
| 			}
 | |
| 			return accumulator
 | |
| 		}
 | |
| 	}
 | |
| 	panic(call.runtime.panicTypeError())
 | |
| }
 | |
| 
 | |
| func builtinArray_reduceRight(call FunctionCall) Value {
 | |
| 	thisObject := call.thisObject()
 | |
| 	this := toValue_object(thisObject)
 | |
| 	if iterator := call.Argument(0); iterator.isCallable() {
 | |
| 		initial := len(call.ArgumentList) > 1
 | |
| 		start := call.Argument(1)
 | |
| 		length := int64(toUint32(thisObject.get("length")))
 | |
| 		if length > 0 || initial {
 | |
| 			index := length - 1
 | |
| 			var accumulator Value
 | |
| 			if !initial {
 | |
| 				for ; index >= 0; index-- {
 | |
| 					if key := arrayIndexToString(index); thisObject.hasProperty(key) {
 | |
| 						accumulator = thisObject.get(key)
 | |
| 						index -= 1
 | |
| 						break
 | |
| 					}
 | |
| 				}
 | |
| 			} else {
 | |
| 				accumulator = start
 | |
| 			}
 | |
| 			for ; index >= 0; index-- {
 | |
| 				if key := arrayIndexToString(index); thisObject.hasProperty(key) {
 | |
| 					accumulator = iterator.call(call.runtime, Value{}, accumulator, thisObject.get(key), key, this)
 | |
| 				}
 | |
| 			}
 | |
| 			return accumulator
 | |
| 		}
 | |
| 	}
 | |
| 	panic(call.runtime.panicTypeError())
 | |
| }
 | 
