550 * @return resulting NativeArray |
550 * @return resulting NativeArray |
551 */ |
551 */ |
552 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) |
552 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) |
553 public static Object concat(final Object self, final Object... args) { |
553 public static Object concat(final Object self, final Object... args) { |
554 final ArrayList<Object> list = new ArrayList<>(); |
554 final ArrayList<Object> list = new ArrayList<>(); |
555 final Object selfToObject = Global.toObject(self); |
555 concatToList(list, Global.toObject(self)); |
556 |
556 |
557 if (isArray(selfToObject)) { |
557 for (final Object obj : args) { |
558 final Iterator<Object> iter = arrayLikeIterator(selfToObject, true); |
558 concatToList(list, obj); |
559 while (iter.hasNext()) { |
559 } |
560 list.add(iter.next()); |
560 |
|
561 return new NativeArray(list.toArray()); |
|
562 } |
|
563 |
|
564 private static void concatToList(final ArrayList<Object> list, final Object obj) { |
|
565 final boolean isScriptArray = isArray(obj); |
|
566 final boolean isScriptObject = isScriptArray || obj instanceof ScriptObject; |
|
567 if (isScriptArray || obj instanceof Iterable || (obj != null && obj.getClass().isArray())) { |
|
568 final Iterator<Object> iter = arrayLikeIterator(obj, true); |
|
569 if (iter.hasNext()) { |
|
570 for(int i = 0; iter.hasNext(); ++i) { |
|
571 final Object value = iter.next(); |
|
572 if(value == ScriptRuntime.UNDEFINED && isScriptObject && !((ScriptObject)obj).has(i)) { |
|
573 // TODO: eventually rewrite arrayLikeIterator to use a three-state enum for handling |
|
574 // UNDEFINED instead of an "includeUndefined" boolean with states SKIP, INCLUDE, |
|
575 // RETURN_EMPTY. Until then, this is how we'll make sure that empty elements don't make it |
|
576 // into the concatenated array. |
|
577 list.add(ScriptRuntime.EMPTY); |
|
578 } else { |
|
579 list.add(value); |
|
580 } |
|
581 } |
|
582 } else if (!isScriptArray) { |
|
583 list.add(obj); // add empty object, but not an empty array |
561 } |
584 } |
562 } else { |
585 } else { |
563 // single element, add it |
586 // single element, add it |
564 list.add(selfToObject); |
587 list.add(obj); |
565 } |
588 } |
566 |
|
567 for (final Object obj : args) { |
|
568 if (isArray(obj) || obj instanceof Iterable || (obj != null && obj.getClass().isArray())) { |
|
569 final Iterator<Object> iter = arrayLikeIterator(obj, true); |
|
570 if (iter.hasNext()) { |
|
571 while (iter.hasNext()) { |
|
572 list.add(iter.next()); |
|
573 } |
|
574 } else if (!isArray(obj)) { |
|
575 list.add(obj); // add empty object, but not an empty array |
|
576 } |
|
577 } else { |
|
578 // single element, add it |
|
579 list.add(obj); |
|
580 } |
|
581 } |
|
582 |
|
583 return new NativeArray(list.toArray()); |
|
584 } |
589 } |
585 |
590 |
586 /** |
591 /** |
587 * ECMA 15.4.4.5 Array.prototype.join (separator) |
592 * ECMA 15.4.4.5 Array.prototype.join (separator) |
588 * |
593 * |