/**
* 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.newTypeError;
import static com.github.anba.es6draft.runtime.internal.Properties.createProperties;
import static com.github.anba.es6draft.runtime.internal.ScriptRuntime.EMPTY_ARRAY;
import static com.github.anba.es6draft.runtime.internal.ScriptRuntime.PrepareForTailCall;
import static com.github.anba.es6draft.runtime.types.Undefined.UNDEFINED;
import static com.github.anba.es6draft.runtime.types.builtins.BoundFunctionObject.BoundFunctionCreate;
import static com.github.anba.es6draft.runtime.types.builtins.OrdinaryFunction.SetFunctionName;
import com.github.anba.es6draft.runtime.ExecutionContext;
import com.github.anba.es6draft.runtime.Realm;
import com.github.anba.es6draft.runtime.internal.CompatibilityOption;
import com.github.anba.es6draft.runtime.internal.Initializable;
import com.github.anba.es6draft.runtime.internal.Messages;
import com.github.anba.es6draft.runtime.internal.Properties.Attributes;
import com.github.anba.es6draft.runtime.internal.Properties.CompatibilityExtension;
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.TailCall;
import com.github.anba.es6draft.runtime.internal.Properties.Value;
import com.github.anba.es6draft.runtime.types.BuiltinSymbol;
import com.github.anba.es6draft.runtime.types.Callable;
import com.github.anba.es6draft.runtime.types.Intrinsics;
import com.github.anba.es6draft.runtime.types.Property;
import com.github.anba.es6draft.runtime.types.Type;
import com.github.anba.es6draft.runtime.types.Undefined;
import com.github.anba.es6draft.runtime.types.builtins.BoundFunctionObject;
import com.github.anba.es6draft.runtime.types.builtins.BuiltinFunction;
import com.github.anba.es6draft.runtime.types.builtins.FunctionObject;
/**
* <h1>19 Fundamental Objects</h1><br>
* <h2>19.2 Function Objects</h2>
* <ul>
* <li>19.2.3 Properties of the Function Prototype Object
* <li>19.2.4 Properties of Function Instances
* </ul>
*/
public final class FunctionPrototype extends BuiltinFunction implements Initializable {
private static final int MAX_ARGUMENTS = 0x1ffff;
/**
* Constructs a new Function prototype object.
*
* @param realm
* the realm object
*/
public FunctionPrototype(Realm realm) {
super(realm, "", 0);
}
@Override
public void initialize(Realm realm) {
createProperties(realm, this, Properties.class);
createProperties(realm, this, AdditionalProperties.class);
}
@Override
public FunctionPrototype clone() {
return new FunctionPrototype(getRealm());
}
/**
* Returns the number of maximal supported arguments in {@code Function.prototype.apply}.
*
* @return the maximum number of supported arguments
*/
public static int getMaxArguments() {
return MAX_ARGUMENTS;
}
/**
* [[Call]]
*/
@Override
public Undefined call(ExecutionContext callerContext, Object thisValue, Object... args) {
return UNDEFINED;
}
/**
* 19.2.3 Properties of the Function Prototype Object
*/
public enum Properties {
;
@Prototype
public static final Intrinsics __proto__ = Intrinsics.ObjectPrototype;
@Value(name = "length", attributes = @Attributes(writable = false, enumerable = false,
configurable = true))
public static final int length = 0;
@Value(name = "name", attributes = @Attributes(writable = false, enumerable = false,
configurable = true))
public static final String name = "";
/**
* 19.2.3.4 Function.prototype.constructor
*/
@Value(name = "constructor")
public static final Intrinsics constructor = Intrinsics.Function;
/**
* 19.2.3.5 Function.prototype.toString ( )
*
* @param cx
* the execution context
* @param thisValue
* the function this-value
* @return the string representation
*/
@Function(name = "toString", arity = 0)
public static Object toString(ExecutionContext cx, Object thisValue) {
/* steps 1-2 */
if (thisValue instanceof Callable) {
return ((Callable) thisValue).toSource(cx);
}
/* step 3 */
throw newTypeError(cx, Messages.Key.IncompatibleObject);
}
/**
* 19.2.3.1 Function.prototype.apply (thisArg, argArray)
*
* @param cx
* the execution context
* @param thisValue
* the function this-value
* @param thisArg
* the this-argument
* @param argArray
* the arguments array
* @return the function invocation result
*/
@TailCall
@Function(name = "apply", arity = 2)
public static Object apply(ExecutionContext cx, Object thisValue, Object thisArg,
Object argArray) {
/* step 1 */
if (!IsCallable(thisValue)) {
throw newTypeError(cx, Messages.Key.IncompatibleObject);
}
Callable func = (Callable) thisValue;
/* step 2 */
if (Type.isUndefinedOrNull(argArray)) {
return PrepareForTailCall(func, thisArg, EMPTY_ARRAY);
}
/* steps 3-4 */
Object[] argList = CreateListFromArrayLike(cx, argArray);
/* steps 5-6 */
return PrepareForTailCall(func, thisArg, argList);
}
/**
* 19.2.3.3 Function.prototype.call (thisArg, ...args)
*
* @param cx
* the execution context
* @param thisValue
* the function this-value
* @param thisArg
* the this-argument
* @param args
* the arguments array
* @return the function invocation result
*/
@TailCall
@Function(name = "call", arity = 1)
public static Object call(ExecutionContext cx, Object thisValue, Object thisArg,
Object... args) {
/* step 1 */
if (!IsCallable(thisValue)) {
throw newTypeError(cx, Messages.Key.IncompatibleObject);
}
Callable func = (Callable) thisValue;
/* steps 2-5 */
return PrepareForTailCall(func, thisArg, args);
}
/**
* 19.2.3.2 Function.prototype.bind (thisArg, ...args)
*
* @param cx
* the execution context
* @param thisValue
* the function this-value
* @param thisArg
* the this-argument
* @param args
* the arguments array
* @return the bound function object
*/
@Function(name = "bind", arity = 1)
public static Object bind(ExecutionContext cx, Object thisValue, Object thisArg,
Object... args) {
/* step 2 */
if (!IsCallable(thisValue)) {
throw newTypeError(cx, Messages.Key.IncompatibleObject);
}
/* step 1 */
Callable target = (Callable) thisValue;
/* step 3 (not applicable) */
/* steps 4-5 */
BoundFunctionObject f = BoundFunctionCreate(cx, target, thisArg, args);
/* steps 6-7 */
boolean targetHasLength = HasOwnProperty(cx, target, "length");
/* steps 8-9 */
double l = 0;
if (targetHasLength) {
Object targetLen = Get(cx, target, "length");
if (Type.isNumber(targetLen)) {
double intLength = ToInteger(Type.numberValue(targetLen));
l = Math.max(0, intLength - args.length);
}
}
/* steps 10-11 */
f.infallibleDefineOwnProperty("length", new Property(l, false, false, true));
/* steps 12-13 */
Object targetName = Get(cx, target, "name");
/* step 14 */
if (!Type.isString(targetName)) {
targetName = "";
}
/* step 15 */
SetFunctionName(f, Type.stringValue(targetName).toString(), "bound");
/* step 16 */
return f;
}
/**
* 19.2.3.6 Function.prototype[@@hasInstance] (V)
*
* @param cx
* the execution context
* @param thisValue
* the function this-value
* @param v
* the value
* @return {@code true} if the value is an instance of this function
*/
@Function(
name = "[Symbol.hasInstance]",
arity = 1,
symbol = BuiltinSymbol.hasInstance,
attributes = @Attributes(writable = false, enumerable = false, configurable = false))
public static Object hasInstance(ExecutionContext cx, Object thisValue, Object v) {
return OrdinaryHasInstance(cx, thisValue, v);
}
}
/**
* Proposed ECMAScript 7 additions
*/
@CompatibilityExtension(CompatibilityOption.FunctionToMethod)
public enum AdditionalProperties {
;
/**
* 19.2.3.? Function.prototype.toMethod (newHome)
*
* @param cx
* the execution context
* @param thisValue
* the function this-value
* @param newHome
* the new home object
* @return the new function object
*/
@Function(name = "toMethod", arity = 1)
public static Object toMethod(ExecutionContext cx, Object thisValue, Object newHome) {
/* step 1 */
if (!(thisValue instanceof Callable)) {
throw newTypeError(cx, Messages.Key.IncompatibleObject);
}
Callable func = (Callable) thisValue;
/* step 2 */
if (!Type.isObject(newHome)) {
throw newTypeError(cx, Messages.Key.NotObjectType);
}
/* step 3 */
if (func instanceof FunctionObject) {
return ((FunctionObject) func).clone(cx, Type.objectValue(newHome));
}
/* steps 3-6 */
return func.clone(cx);
}
}
}