builtin_array.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684
  1. package otto
  2. import (
  3. "strconv"
  4. "strings"
  5. )
  6. // Array
  7. func builtinArray(call FunctionCall) Value {
  8. return toValue_object(builtinNewArrayNative(call.runtime, call.ArgumentList))
  9. }
  10. func builtinNewArray(self *_object, argumentList []Value) Value {
  11. return toValue_object(builtinNewArrayNative(self.runtime, argumentList))
  12. }
  13. func builtinNewArrayNative(runtime *_runtime, argumentList []Value) *_object {
  14. if len(argumentList) == 1 {
  15. firstArgument := argumentList[0]
  16. if firstArgument.IsNumber() {
  17. return runtime.newArray(arrayUint32(runtime, firstArgument))
  18. }
  19. }
  20. return runtime.newArrayOf(argumentList)
  21. }
  22. func builtinArray_toString(call FunctionCall) Value {
  23. thisObject := call.thisObject()
  24. join := thisObject.get("join")
  25. if join.isCallable() {
  26. join := join._object()
  27. return join.call(call.This, call.ArgumentList, false, nativeFrame)
  28. }
  29. return builtinObject_toString(call)
  30. }
  31. func builtinArray_toLocaleString(call FunctionCall) Value {
  32. separator := ","
  33. thisObject := call.thisObject()
  34. length := int64(toUint32(thisObject.get(propertyLength)))
  35. if length == 0 {
  36. return toValue_string("")
  37. }
  38. stringList := make([]string, 0, length)
  39. for index := int64(0); index < length; index += 1 {
  40. value := thisObject.get(arrayIndexToString(index))
  41. stringValue := ""
  42. switch value.kind {
  43. case valueEmpty, valueUndefined, valueNull:
  44. default:
  45. object := call.runtime.toObject(value)
  46. toLocaleString := object.get("toLocaleString")
  47. if !toLocaleString.isCallable() {
  48. panic(call.runtime.panicTypeError())
  49. }
  50. stringValue = toLocaleString.call(call.runtime, toValue_object(object)).string()
  51. }
  52. stringList = append(stringList, stringValue)
  53. }
  54. return toValue_string(strings.Join(stringList, separator))
  55. }
  56. func builtinArray_concat(call FunctionCall) Value {
  57. thisObject := call.thisObject()
  58. valueArray := []Value{}
  59. source := append([]Value{toValue_object(thisObject)}, call.ArgumentList...)
  60. for _, item := range source {
  61. switch item.kind {
  62. case valueObject:
  63. object := item._object()
  64. if isArray(object) {
  65. length := object.get(propertyLength).number().int64
  66. for index := int64(0); index < length; index += 1 {
  67. name := strconv.FormatInt(index, 10)
  68. if object.hasProperty(name) {
  69. valueArray = append(valueArray, object.get(name))
  70. } else {
  71. valueArray = append(valueArray, Value{})
  72. }
  73. }
  74. continue
  75. }
  76. fallthrough
  77. default:
  78. valueArray = append(valueArray, item)
  79. }
  80. }
  81. return toValue_object(call.runtime.newArrayOf(valueArray))
  82. }
  83. func builtinArray_shift(call FunctionCall) Value {
  84. thisObject := call.thisObject()
  85. length := int64(toUint32(thisObject.get(propertyLength)))
  86. if 0 == length {
  87. thisObject.put(propertyLength, toValue_int64(0), true)
  88. return Value{}
  89. }
  90. first := thisObject.get("0")
  91. for index := int64(1); index < length; index++ {
  92. from := arrayIndexToString(index)
  93. to := arrayIndexToString(index - 1)
  94. if thisObject.hasProperty(from) {
  95. thisObject.put(to, thisObject.get(from), true)
  96. } else {
  97. thisObject.delete(to, true)
  98. }
  99. }
  100. thisObject.delete(arrayIndexToString(length-1), true)
  101. thisObject.put(propertyLength, toValue_int64(length-1), true)
  102. return first
  103. }
  104. func builtinArray_push(call FunctionCall) Value {
  105. thisObject := call.thisObject()
  106. itemList := call.ArgumentList
  107. index := int64(toUint32(thisObject.get(propertyLength)))
  108. for len(itemList) > 0 {
  109. thisObject.put(arrayIndexToString(index), itemList[0], true)
  110. itemList = itemList[1:]
  111. index += 1
  112. }
  113. length := toValue_int64(index)
  114. thisObject.put(propertyLength, length, true)
  115. return length
  116. }
  117. func builtinArray_pop(call FunctionCall) Value {
  118. thisObject := call.thisObject()
  119. length := int64(toUint32(thisObject.get(propertyLength)))
  120. if 0 == length {
  121. thisObject.put(propertyLength, toValue_uint32(0), true)
  122. return Value{}
  123. }
  124. last := thisObject.get(arrayIndexToString(length - 1))
  125. thisObject.delete(arrayIndexToString(length-1), true)
  126. thisObject.put(propertyLength, toValue_int64(length-1), true)
  127. return last
  128. }
  129. func builtinArray_join(call FunctionCall) Value {
  130. separator := ","
  131. {
  132. argument := call.Argument(0)
  133. if argument.IsDefined() {
  134. separator = argument.string()
  135. }
  136. }
  137. thisObject := call.thisObject()
  138. length := int64(toUint32(thisObject.get(propertyLength)))
  139. if length == 0 {
  140. return toValue_string("")
  141. }
  142. stringList := make([]string, 0, length)
  143. for index := int64(0); index < length; index += 1 {
  144. value := thisObject.get(arrayIndexToString(index))
  145. stringValue := ""
  146. switch value.kind {
  147. case valueEmpty, valueUndefined, valueNull:
  148. default:
  149. stringValue = value.string()
  150. }
  151. stringList = append(stringList, stringValue)
  152. }
  153. return toValue_string(strings.Join(stringList, separator))
  154. }
  155. func builtinArray_splice(call FunctionCall) Value {
  156. thisObject := call.thisObject()
  157. length := int64(toUint32(thisObject.get(propertyLength)))
  158. start := valueToRangeIndex(call.Argument(0), length, false)
  159. deleteCount := length - start
  160. if arg, ok := call.getArgument(1); ok {
  161. deleteCount = valueToRangeIndex(arg, length-start, true)
  162. }
  163. valueArray := make([]Value, deleteCount)
  164. for index := int64(0); index < deleteCount; index++ {
  165. indexString := arrayIndexToString(int64(start + index))
  166. if thisObject.hasProperty(indexString) {
  167. valueArray[index] = thisObject.get(indexString)
  168. }
  169. }
  170. // 0, <1, 2, 3, 4>, 5, 6, 7
  171. // a, b
  172. // length 8 - delete 4 @ start 1
  173. itemList := []Value{}
  174. itemCount := int64(len(call.ArgumentList))
  175. if itemCount > 2 {
  176. itemCount -= 2 // Less the first two arguments
  177. itemList = call.ArgumentList[2:]
  178. } else {
  179. itemCount = 0
  180. }
  181. if itemCount < deleteCount {
  182. // The Object/Array is shrinking
  183. stop := int64(length) - deleteCount
  184. // The new length of the Object/Array before
  185. // appending the itemList remainder
  186. // Stopping at the lower bound of the insertion:
  187. // Move an item from the after the deleted portion
  188. // to a position after the inserted portion
  189. for index := start; index < stop; index++ {
  190. from := arrayIndexToString(index + deleteCount) // Position just after deletion
  191. to := arrayIndexToString(index + itemCount) // Position just after splice (insertion)
  192. if thisObject.hasProperty(from) {
  193. thisObject.put(to, thisObject.get(from), true)
  194. } else {
  195. thisObject.delete(to, true)
  196. }
  197. }
  198. // Delete off the end
  199. // We don't bother to delete below <stop + itemCount> (if any) since those
  200. // will be overwritten anyway
  201. for index := int64(length); index > (stop + itemCount); index-- {
  202. thisObject.delete(arrayIndexToString(index-1), true)
  203. }
  204. } else if itemCount > deleteCount {
  205. // The Object/Array is growing
  206. // The itemCount is greater than the deleteCount, so we do
  207. // not have to worry about overwriting what we should be moving
  208. // ---
  209. // Starting from the upper bound of the deletion:
  210. // Move an item from the after the deleted portion
  211. // to a position after the inserted portion
  212. for index := int64(length) - deleteCount; index > start; index-- {
  213. from := arrayIndexToString(index + deleteCount - 1)
  214. to := arrayIndexToString(index + itemCount - 1)
  215. if thisObject.hasProperty(from) {
  216. thisObject.put(to, thisObject.get(from), true)
  217. } else {
  218. thisObject.delete(to, true)
  219. }
  220. }
  221. }
  222. for index := int64(0); index < itemCount; index++ {
  223. thisObject.put(arrayIndexToString(index+start), itemList[index], true)
  224. }
  225. thisObject.put(propertyLength, toValue_int64(int64(length)+itemCount-deleteCount), true)
  226. return toValue_object(call.runtime.newArrayOf(valueArray))
  227. }
  228. func builtinArray_slice(call FunctionCall) Value {
  229. thisObject := call.thisObject()
  230. length := int64(toUint32(thisObject.get(propertyLength)))
  231. start, end := rangeStartEnd(call.ArgumentList, length, false)
  232. if start >= end {
  233. // Always an empty array
  234. return toValue_object(call.runtime.newArray(0))
  235. }
  236. sliceLength := end - start
  237. sliceValueArray := make([]Value, sliceLength)
  238. for index := int64(0); index < sliceLength; index++ {
  239. from := arrayIndexToString(index + start)
  240. if thisObject.hasProperty(from) {
  241. sliceValueArray[index] = thisObject.get(from)
  242. }
  243. }
  244. return toValue_object(call.runtime.newArrayOf(sliceValueArray))
  245. }
  246. func builtinArray_unshift(call FunctionCall) Value {
  247. thisObject := call.thisObject()
  248. length := int64(toUint32(thisObject.get(propertyLength)))
  249. itemList := call.ArgumentList
  250. itemCount := int64(len(itemList))
  251. for index := length; index > 0; index-- {
  252. from := arrayIndexToString(index - 1)
  253. to := arrayIndexToString(index + itemCount - 1)
  254. if thisObject.hasProperty(from) {
  255. thisObject.put(to, thisObject.get(from), true)
  256. } else {
  257. thisObject.delete(to, true)
  258. }
  259. }
  260. for index := int64(0); index < itemCount; index++ {
  261. thisObject.put(arrayIndexToString(index), itemList[index], true)
  262. }
  263. newLength := toValue_int64(length + itemCount)
  264. thisObject.put(propertyLength, newLength, true)
  265. return newLength
  266. }
  267. func builtinArray_reverse(call FunctionCall) Value {
  268. thisObject := call.thisObject()
  269. length := int64(toUint32(thisObject.get(propertyLength)))
  270. lower := struct {
  271. name string
  272. index int64
  273. exists bool
  274. }{}
  275. upper := lower
  276. lower.index = 0
  277. middle := length / 2 // Division will floor
  278. for lower.index != middle {
  279. lower.name = arrayIndexToString(lower.index)
  280. upper.index = length - lower.index - 1
  281. upper.name = arrayIndexToString(upper.index)
  282. lower.exists = thisObject.hasProperty(lower.name)
  283. upper.exists = thisObject.hasProperty(upper.name)
  284. if lower.exists && upper.exists {
  285. lowerValue := thisObject.get(lower.name)
  286. upperValue := thisObject.get(upper.name)
  287. thisObject.put(lower.name, upperValue, true)
  288. thisObject.put(upper.name, lowerValue, true)
  289. } else if !lower.exists && upper.exists {
  290. value := thisObject.get(upper.name)
  291. thisObject.delete(upper.name, true)
  292. thisObject.put(lower.name, value, true)
  293. } else if lower.exists && !upper.exists {
  294. value := thisObject.get(lower.name)
  295. thisObject.delete(lower.name, true)
  296. thisObject.put(upper.name, value, true)
  297. } else {
  298. // Nothing happens.
  299. }
  300. lower.index += 1
  301. }
  302. return call.This
  303. }
  304. func sortCompare(thisObject *_object, index0, index1 uint, compare *_object) int {
  305. j := struct {
  306. name string
  307. exists bool
  308. defined bool
  309. value string
  310. }{}
  311. k := j
  312. j.name = arrayIndexToString(int64(index0))
  313. j.exists = thisObject.hasProperty(j.name)
  314. k.name = arrayIndexToString(int64(index1))
  315. k.exists = thisObject.hasProperty(k.name)
  316. if !j.exists && !k.exists {
  317. return 0
  318. } else if !j.exists {
  319. return 1
  320. } else if !k.exists {
  321. return -1
  322. }
  323. x := thisObject.get(j.name)
  324. y := thisObject.get(k.name)
  325. j.defined = x.IsDefined()
  326. k.defined = y.IsDefined()
  327. if !j.defined && !k.defined {
  328. return 0
  329. } else if !j.defined {
  330. return 1
  331. } else if !k.defined {
  332. return -1
  333. }
  334. if compare == nil {
  335. j.value = x.string()
  336. k.value = y.string()
  337. if j.value == k.value {
  338. return 0
  339. } else if j.value < k.value {
  340. return -1
  341. }
  342. return 1
  343. }
  344. return toIntSign(compare.call(Value{}, []Value{x, y}, false, nativeFrame))
  345. }
  346. func arraySortSwap(thisObject *_object, index0, index1 uint) {
  347. j := struct {
  348. name string
  349. exists bool
  350. }{}
  351. k := j
  352. j.name = arrayIndexToString(int64(index0))
  353. j.exists = thisObject.hasProperty(j.name)
  354. k.name = arrayIndexToString(int64(index1))
  355. k.exists = thisObject.hasProperty(k.name)
  356. if j.exists && k.exists {
  357. jValue := thisObject.get(j.name)
  358. kValue := thisObject.get(k.name)
  359. thisObject.put(j.name, kValue, true)
  360. thisObject.put(k.name, jValue, true)
  361. } else if !j.exists && k.exists {
  362. value := thisObject.get(k.name)
  363. thisObject.delete(k.name, true)
  364. thisObject.put(j.name, value, true)
  365. } else if j.exists && !k.exists {
  366. value := thisObject.get(j.name)
  367. thisObject.delete(j.name, true)
  368. thisObject.put(k.name, value, true)
  369. } else {
  370. // Nothing happens.
  371. }
  372. }
  373. func arraySortQuickPartition(thisObject *_object, left, right, pivot uint, compare *_object) (uint, uint) {
  374. arraySortSwap(thisObject, pivot, right) // Right is now the pivot value
  375. cursor := left
  376. cursor2 := left
  377. for index := left; index < right; index++ {
  378. comparison := sortCompare(thisObject, index, right, compare) // Compare to the pivot value
  379. if comparison < 0 {
  380. arraySortSwap(thisObject, index, cursor)
  381. if cursor < cursor2 {
  382. arraySortSwap(thisObject, index, cursor2)
  383. }
  384. cursor += 1
  385. cursor2 += 1
  386. } else if comparison == 0 {
  387. arraySortSwap(thisObject, index, cursor2)
  388. cursor2 += 1
  389. }
  390. }
  391. arraySortSwap(thisObject, cursor2, right)
  392. return cursor, cursor2
  393. }
  394. func arraySortQuickSort(thisObject *_object, left, right uint, compare *_object) {
  395. if left < right {
  396. middle := left + (right-left)/2
  397. pivot, pivot2 := arraySortQuickPartition(thisObject, left, right, middle, compare)
  398. if pivot > 0 {
  399. arraySortQuickSort(thisObject, left, pivot-1, compare)
  400. }
  401. arraySortQuickSort(thisObject, pivot2+1, right, compare)
  402. }
  403. }
  404. func builtinArray_sort(call FunctionCall) Value {
  405. thisObject := call.thisObject()
  406. length := uint(toUint32(thisObject.get(propertyLength)))
  407. compareValue := call.Argument(0)
  408. compare := compareValue._object()
  409. if compareValue.IsUndefined() {
  410. } else if !compareValue.isCallable() {
  411. panic(call.runtime.panicTypeError())
  412. }
  413. if length > 1 {
  414. arraySortQuickSort(thisObject, 0, length-1, compare)
  415. }
  416. return call.This
  417. }
  418. func builtinArray_isArray(call FunctionCall) Value {
  419. return toValue_bool(isArray(call.Argument(0)._object()))
  420. }
  421. func builtinArray_indexOf(call FunctionCall) Value {
  422. thisObject, matchValue := call.thisObject(), call.Argument(0)
  423. if length := int64(toUint32(thisObject.get(propertyLength))); length > 0 {
  424. index := int64(0)
  425. if len(call.ArgumentList) > 1 {
  426. index = call.Argument(1).number().int64
  427. }
  428. if index < 0 {
  429. if index += length; index < 0 {
  430. index = 0
  431. }
  432. } else if index >= length {
  433. index = -1
  434. }
  435. for ; index >= 0 && index < length; index++ {
  436. name := arrayIndexToString(int64(index))
  437. if !thisObject.hasProperty(name) {
  438. continue
  439. }
  440. value := thisObject.get(name)
  441. if strictEqualityComparison(matchValue, value) {
  442. return toValue_uint32(uint32(index))
  443. }
  444. }
  445. }
  446. return toValue_int(-1)
  447. }
  448. func builtinArray_lastIndexOf(call FunctionCall) Value {
  449. thisObject, matchValue := call.thisObject(), call.Argument(0)
  450. length := int64(toUint32(thisObject.get(propertyLength)))
  451. index := length - 1
  452. if len(call.ArgumentList) > 1 {
  453. index = call.Argument(1).number().int64
  454. }
  455. if 0 > index {
  456. index += length
  457. }
  458. if index > length {
  459. index = length - 1
  460. } else if 0 > index {
  461. return toValue_int(-1)
  462. }
  463. for ; index >= 0; index-- {
  464. name := arrayIndexToString(int64(index))
  465. if !thisObject.hasProperty(name) {
  466. continue
  467. }
  468. value := thisObject.get(name)
  469. if strictEqualityComparison(matchValue, value) {
  470. return toValue_uint32(uint32(index))
  471. }
  472. }
  473. return toValue_int(-1)
  474. }
  475. func builtinArray_every(call FunctionCall) Value {
  476. thisObject := call.thisObject()
  477. this := toValue_object(thisObject)
  478. if iterator := call.Argument(0); iterator.isCallable() {
  479. length := int64(toUint32(thisObject.get(propertyLength)))
  480. callThis := call.Argument(1)
  481. for index := int64(0); index < length; index++ {
  482. if key := arrayIndexToString(index); thisObject.hasProperty(key) {
  483. if value := thisObject.get(key); iterator.call(call.runtime, callThis, value, toValue_int64(index), this).bool() {
  484. continue
  485. }
  486. return falseValue
  487. }
  488. }
  489. return trueValue
  490. }
  491. panic(call.runtime.panicTypeError())
  492. }
  493. func builtinArray_some(call FunctionCall) Value {
  494. thisObject := call.thisObject()
  495. this := toValue_object(thisObject)
  496. if iterator := call.Argument(0); iterator.isCallable() {
  497. length := int64(toUint32(thisObject.get(propertyLength)))
  498. callThis := call.Argument(1)
  499. for index := int64(0); index < length; index++ {
  500. if key := arrayIndexToString(index); thisObject.hasProperty(key) {
  501. if value := thisObject.get(key); iterator.call(call.runtime, callThis, value, toValue_int64(index), this).bool() {
  502. return trueValue
  503. }
  504. }
  505. }
  506. return falseValue
  507. }
  508. panic(call.runtime.panicTypeError())
  509. }
  510. func builtinArray_forEach(call FunctionCall) Value {
  511. thisObject := call.thisObject()
  512. this := toValue_object(thisObject)
  513. if iterator := call.Argument(0); iterator.isCallable() {
  514. length := int64(toUint32(thisObject.get(propertyLength)))
  515. callThis := call.Argument(1)
  516. for index := int64(0); index < length; index++ {
  517. if key := arrayIndexToString(index); thisObject.hasProperty(key) {
  518. iterator.call(call.runtime, callThis, thisObject.get(key), toValue_int64(index), this)
  519. }
  520. }
  521. return Value{}
  522. }
  523. panic(call.runtime.panicTypeError())
  524. }
  525. func builtinArray_map(call FunctionCall) Value {
  526. thisObject := call.thisObject()
  527. this := toValue_object(thisObject)
  528. if iterator := call.Argument(0); iterator.isCallable() {
  529. length := int64(toUint32(thisObject.get(propertyLength)))
  530. callThis := call.Argument(1)
  531. values := make([]Value, length)
  532. for index := int64(0); index < length; index++ {
  533. if key := arrayIndexToString(index); thisObject.hasProperty(key) {
  534. values[index] = iterator.call(call.runtime, callThis, thisObject.get(key), index, this)
  535. } else {
  536. values[index] = Value{}
  537. }
  538. }
  539. return toValue_object(call.runtime.newArrayOf(values))
  540. }
  541. panic(call.runtime.panicTypeError())
  542. }
  543. func builtinArray_filter(call FunctionCall) Value {
  544. thisObject := call.thisObject()
  545. this := toValue_object(thisObject)
  546. if iterator := call.Argument(0); iterator.isCallable() {
  547. length := int64(toUint32(thisObject.get(propertyLength)))
  548. callThis := call.Argument(1)
  549. values := make([]Value, 0)
  550. for index := int64(0); index < length; index++ {
  551. if key := arrayIndexToString(index); thisObject.hasProperty(key) {
  552. value := thisObject.get(key)
  553. if iterator.call(call.runtime, callThis, value, index, this).bool() {
  554. values = append(values, value)
  555. }
  556. }
  557. }
  558. return toValue_object(call.runtime.newArrayOf(values))
  559. }
  560. panic(call.runtime.panicTypeError())
  561. }
  562. func builtinArray_reduce(call FunctionCall) Value {
  563. thisObject := call.thisObject()
  564. this := toValue_object(thisObject)
  565. if iterator := call.Argument(0); iterator.isCallable() {
  566. initial := len(call.ArgumentList) > 1
  567. start := call.Argument(1)
  568. length := int64(toUint32(thisObject.get(propertyLength)))
  569. index := int64(0)
  570. if length > 0 || initial {
  571. var accumulator Value
  572. if !initial {
  573. for ; index < length; index++ {
  574. if key := arrayIndexToString(index); thisObject.hasProperty(key) {
  575. accumulator = thisObject.get(key)
  576. index++
  577. break
  578. }
  579. }
  580. } else {
  581. accumulator = start
  582. }
  583. for ; index < length; index++ {
  584. if key := arrayIndexToString(index); thisObject.hasProperty(key) {
  585. accumulator = iterator.call(call.runtime, Value{}, accumulator, thisObject.get(key), key, this)
  586. }
  587. }
  588. return accumulator
  589. }
  590. }
  591. panic(call.runtime.panicTypeError())
  592. }
  593. func builtinArray_reduceRight(call FunctionCall) Value {
  594. thisObject := call.thisObject()
  595. this := toValue_object(thisObject)
  596. if iterator := call.Argument(0); iterator.isCallable() {
  597. initial := len(call.ArgumentList) > 1
  598. start := call.Argument(1)
  599. length := int64(toUint32(thisObject.get(propertyLength)))
  600. if length > 0 || initial {
  601. index := length - 1
  602. var accumulator Value
  603. if !initial {
  604. for ; index >= 0; index-- {
  605. if key := arrayIndexToString(index); thisObject.hasProperty(key) {
  606. accumulator = thisObject.get(key)
  607. index--
  608. break
  609. }
  610. }
  611. } else {
  612. accumulator = start
  613. }
  614. for ; index >= 0; index-- {
  615. if key := arrayIndexToString(index); thisObject.hasProperty(key) {
  616. accumulator = iterator.call(call.runtime, Value{}, accumulator, thisObject.get(key), key, this)
  617. }
  618. }
  619. return accumulator
  620. }
  621. }
  622. panic(call.runtime.panicTypeError())
  623. }