aboutsummaryrefslogblamecommitdiffstats
path: root/vendor/github.com/robertkrimen/otto/builtin_array.go
blob: 557ffc02480e4e423c068f26cd0a2cb8b08784cf (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13












                                                                                     
                                                                 






                                                                                
                                                                                    









                                                     
                                                                                  














                                                                  
                                   




                                                                      
                                                                    
                         
                                                                                                        










                                                                                   
                                  


                                                
                                                                             






















                                                                                                 
                              


































                                                                            
                              











                                                               
                                                     










                                                                  
                                   

                                                           
                                                    


















































































































































































































                                                                                                                 

                                    









                                             
                                                                                     
































                                                              
                                                                                                           

                                                                               
                       
                                                   

                                                                                                          
                                                                


                                                                         
                                   



                                                                 

                 

                                                 



                                                                                  

                                                                                                  


                                                                              
                                                                        









                                                          
                                                    















                                                                            
                                                               


























                                                                        
                                                       





























                                                                                          
                                                                                                                                                  

                                                
                                                 

                         
                                
         
                                            









                                                                                          

                                                                                                                                                  


                                 
                                 
         
                                            









                                                                                          
                                                                                                                      

                         
                              
         
                                            










                                                                                          
                                                                                                                       
                                
                                                       



                                                                      
                                            











                                                                                          
                                                                                                     





                                                                      
                                            
























                                                                                                          
                                                                                                                                       




                                          
                                            















                                                                                                          
                                                       







                                                                                                  
                                                                                                                                       




                                          
                                            
 
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--
                        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())
}