/**
* Copyright (c) 2012-2016 André Bargull
* Alle Rechte vorbehalten / All Rights Reserved. Use is subject to license terms.
*
* <https://github.com/anba/es6draft>
*/
package com.github.anba.es6draft.runtime.objects;
import static com.github.anba.es6draft.runtime.AbstractOperations.*;
import static com.github.anba.es6draft.runtime.internal.Errors.newRangeError;
import static com.github.anba.es6draft.runtime.internal.Errors.newTypeError;
import static com.github.anba.es6draft.runtime.internal.Properties.createProperties;
import static com.github.anba.es6draft.runtime.types.builtins.ArrayObject.ArrayCreate;
import static com.github.anba.es6draft.runtime.types.builtins.ArrayObject.DenseArrayCreate;
import com.github.anba.es6draft.runtime.ExecutionContext;
import com.github.anba.es6draft.runtime.Realm;
import com.github.anba.es6draft.runtime.internal.Initializable;
import com.github.anba.es6draft.runtime.internal.Messages;
import com.github.anba.es6draft.runtime.internal.Properties.Accessor;
import com.github.anba.es6draft.runtime.internal.Properties.Attributes;
import com.github.anba.es6draft.runtime.internal.Properties.Function;
import com.github.anba.es6draft.runtime.internal.Properties.Prototype;
import com.github.anba.es6draft.runtime.internal.Properties.Value;
import com.github.anba.es6draft.runtime.internal.ScriptException;
import com.github.anba.es6draft.runtime.internal.ScriptIterator;
import com.github.anba.es6draft.runtime.types.BuiltinSymbol;
import com.github.anba.es6draft.runtime.types.Callable;
import com.github.anba.es6draft.runtime.types.Constructor;
import com.github.anba.es6draft.runtime.types.Intrinsics;
import com.github.anba.es6draft.runtime.types.ScriptObject;
import com.github.anba.es6draft.runtime.types.Type;
import com.github.anba.es6draft.runtime.types.builtins.ArrayObject;
import com.github.anba.es6draft.runtime.types.builtins.BuiltinConstructor;
/**
* <h1>22 Indexed Collections</h1><br>
* <h2>22.1 Array Objects</h2>
* <ul>
* <li>22.1.1 The Array Constructor
* <li>22.1.2 Properties of the Array Constructor
* </ul>
*/
public final class ArrayConstructor extends BuiltinConstructor implements Initializable {
/**
* Constructs a new Array constructor function.
*
* @param realm
* the realm object
*/
public ArrayConstructor(Realm realm) {
super(realm, "Array", 1);
}
@Override
public void initialize(Realm realm) {
createProperties(realm, this, Properties.class);
}
@Override
public ArrayConstructor clone() {
return new ArrayConstructor(getRealm());
}
/**
* 22.1.1.1 Array ( )<br>
* 22.1.1.2 Array (len)<br>
* 22.1.1.3 Array (...items )
*/
@Override
public ArrayObject call(ExecutionContext callerContext, Object thisValue, Object... args) {
/* steps 1-6/1-11/1-12 */
return construct(callerContext, this, args);
}
/**
* 22.1.1.1 Array ( )<br>
* 22.1.1.2 Array (len)<br>
* 22.1.1.3 Array (...items )
*/
@Override
public ArrayObject construct(ExecutionContext callerContext, Constructor newTarget,
Object... args) {
ExecutionContext calleeContext = calleeContext();
/* step 1 */
int numberOfArgs = args.length;
/* steps 2-3 (not applicable) */
/* steps 4-5 */
ScriptObject proto = GetPrototypeFromConstructor(calleeContext, newTarget,
Intrinsics.ArrayPrototype);
if (numberOfArgs == 0) {
// [22.1.1.1]
/* step 6 */
return ArrayCreate(calleeContext, 0, proto);
} else if (numberOfArgs == 1) {
// [22.1.1.2]
Object len = args[0];
/* steps 6-11 */
if (!Type.isNumber(len)) {
return DenseArrayCreate(calleeContext, proto, len);
} else {
double llen = Type.numberValue(len);
long intLen = ToUint32(llen);
if (intLen != llen) {
throw newRangeError(calleeContext, Messages.Key.InvalidArrayLength);
}
return ArrayCreate(calleeContext, intLen, proto);
}
} else {
// [22.1.1.3]
/* steps 6-12 */
return DenseArrayCreate(calleeContext, proto, args);
}
}
private static final long ARRAY_LENGTH_LIMIT = 0x1F_FFFF_FFFF_FFFFL;
/**
* 22.1.2 Properties of the Array Constructor
*/
public enum Properties {
;
@Prototype
public static final Intrinsics __proto__ = Intrinsics.FunctionPrototype;
@Value(name = "length", attributes = @Attributes(writable = false, enumerable = false,
configurable = true))
public static final int length = 1;
@Value(name = "name", attributes = @Attributes(writable = false, enumerable = false,
configurable = true))
public static final String name = "Array";
/**
* 22.1.2.4 Array.prototype
*/
@Value(name = "prototype", attributes = @Attributes(writable = false, enumerable = false,
configurable = false))
public static final Intrinsics prototype = Intrinsics.ArrayPrototype;
/**
* 22.1.2.2 Array.isArray ( arg )
*
* @param cx
* the execution context
* @param thisValue
* the function this-value
* @param arg
* the argument object
* @return {@code true} if the argument is an array object
*/
@Function(name = "isArray", arity = 1)
public static Object isArray(ExecutionContext cx, Object thisValue, Object arg) {
/* step 1 */
return IsArray(cx, arg);
}
/**
* 22.1.2.3 Array.of ( ...items )
*
* @param cx
* the execution context
* @param thisValue
* the function this-value
* @param items
* the element values
* @return the new array object
*/
@Function(name = "of", arity = 0)
public static Object of(ExecutionContext cx, Object thisValue, Object... items) {
/* steps 1-2 */
int len = items.length;
/* step 3 */
Object c = thisValue;
/* steps 4-6 */
ScriptObject a;
if (IsConstructor(c)) {
a = ((Constructor) c).construct(cx, (Constructor) c, len);
} else {
a = ArrayCreate(cx, len);
}
/* steps 7-8 */
for (int k = 0; k < len; ++k) {
int pk = k;
Object kValue = items[k];
CreateDataPropertyOrThrow(cx, a, pk, kValue);
}
/* steps 9-10 */
Set(cx, a, "length", len, true);
/* step 11 */
return a;
}
/**
* 22.1.2.1 Array.from ( items [ , mapfn [ , thisArg ] ] )
*
* @param cx
* the execution context
* @param thisValue
* the function this-value
* @param items
* the source object
* @param mapfn
* the optional mapper function
* @param thisArg
* the optional this-argument for the mapper
* @return the new array object
*/
@Function(name = "from", arity = 1)
public static Object from(ExecutionContext cx, Object thisValue, Object items,
Object mapfn, Object thisArg) {
/* step 1 */
Object c = thisValue;
/* steps 2-3 */
Callable mapper;
boolean mapping;
if (Type.isUndefined(mapfn)) {
mapper = null;
mapping = false;
} else {
if (!IsCallable(mapfn)) {
throw newTypeError(cx, Messages.Key.NotCallable);
}
mapper = (Callable) mapfn;
mapping = true;
}
/* steps 4-5 */
Callable usingIterator = GetMethod(cx, items, BuiltinSymbol.iterator.get());
/* step 6 */
if (usingIterator != null) {
/* steps 6.a-c */
ScriptObject a;
if (IsConstructor(c)) {
a = ((Constructor) c).construct(cx, (Constructor) c);
} else {
a = ArrayCreate(cx, 0);
}
/* steps 6.d-e */
ScriptIterator<?> iterator = GetScriptIterator(cx, items, usingIterator);
/* steps 6.f-g */
long k = 0;
try {
for (; iterator.hasNext(); ++k) {
// FIXME: spec bug - throw if `k` exceeds 2^53-1 limit
if (k >= ARRAY_LENGTH_LIMIT) {
throw newTypeError(cx, Messages.Key.InvalidArrayLength);
}
long pk = k;
Object nextValue = iterator.next();
Object mappedValue;
if (mapping) {
mappedValue = mapper.call(cx, thisArg, nextValue, k);
} else {
mappedValue = nextValue;
}
CreateDataPropertyOrThrow(cx, a, pk, mappedValue);
}
} catch (ScriptException e) {
iterator.close(e);
throw e;
}
/* step 6.g.iv */
assert k <= ARRAY_LENGTH_LIMIT;
Set(cx, a, "length", k, true);
return a;
}
/* step 7 (?) */
/* steps 8-9 */
ScriptObject arrayLike = ToObject(cx, items);
/* steps 10-11 */
long len = ToLength(cx, Get(cx, arrayLike, "length"));
/* steps 12-14 */
ScriptObject a;
if (IsConstructor(c)) {
a = ((Constructor) c).construct(cx, (Constructor) c, len);
} else {
a = ArrayCreate(cx, len);
}
/* steps 15-16 */
for (long k = 0; k < len; ++k) {
long pk = k;
Object kValue = Get(cx, arrayLike, pk);
Object mappedValue;
if (mapping) {
mappedValue = mapper.call(cx, thisArg, kValue, k);
} else {
mappedValue = kValue;
}
CreateDataPropertyOrThrow(cx, a, pk, mappedValue);
}
/* steps 17-18 */
Set(cx, a, "length", len, true);
/* step 19 */
return a;
}
/**
* 22.1.2.5 get Array [ @@species ]
*
* @param cx
* the execution context
* @param thisValue
* the function this-value
* @return the species object
*/
@Accessor(name = "get [Symbol.species]", symbol = BuiltinSymbol.species,
type = Accessor.Type.Getter)
public static Object species(ExecutionContext cx, Object thisValue) {
/* step 1 */
return thisValue;
}
}
}