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