builtin_array.go 19 KB

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