/** * 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.types.builtins; import static com.github.anba.es6draft.runtime.AbstractOperations.IsConstructor; import static com.github.anba.es6draft.runtime.internal.Errors.newRangeError; import com.github.anba.es6draft.runtime.ExecutionContext; import com.github.anba.es6draft.runtime.Realm; import com.github.anba.es6draft.runtime.internal.Messages; import com.github.anba.es6draft.runtime.internal.ScriptRuntime; import com.github.anba.es6draft.runtime.objects.FunctionPrototype; import com.github.anba.es6draft.runtime.types.Callable; import com.github.anba.es6draft.runtime.types.Constructor; import com.github.anba.es6draft.runtime.types.ScriptObject; /** * <h1>9 Ordinary and Exotic Objects Behaviours</h1><br> * <h2>9.4 Built-in Exotic Object Internal Methods and Data Fields</h2> * <ul> * <li>9.4.1 Bound Function Exotic Objects * </ul> */ public class BoundFunctionObject extends OrdinaryObject implements Callable { /** [[BoundTargetFunction]] */ private Callable boundTargetFunction; private Callable flattenedTargetFunction; /** [[BoundThis]] */ private Object boundThis; /** [[BoundArguments]] */ private Object[] boundArguments; private static final class ConstructorBoundFunctionObject extends BoundFunctionObject implements Constructor { /** * Constructs a new Bound Function object. * * @param realm * the realm object */ public ConstructorBoundFunctionObject(Realm realm) { super(realm); } /** * 9.4.1.2 [[Construct]] (argumentsList, newTarget) */ @Override public ScriptObject construct(ExecutionContext callerContext, Constructor newTarget, Object... argumentsList) { /* step 1 */ Callable target = getFlattenedTargetFunction(); /* step 2 */ assert IsConstructor(target); /* step 3 */ Object[] boundArgs = getBoundArguments(); /* step 4 */ Object[] args = concatArguments(callerContext, boundArgs, argumentsList); /* step 5 */ if (this == newTarget) { newTarget = (Constructor) target; } /* step 6 */ return ((Constructor) target).construct(callerContext, newTarget, args); } } /** * Constructs a new Bound Function object. * * @param realm * the realm object */ public BoundFunctionObject(Realm realm) { super(realm); } /** * [[BoundTargetFunction]] * * @return the bound target function */ public final Callable getBoundTargetFunction() { return boundTargetFunction; } /** * [[BoundThis]] * * @return the bound this-value */ public final Object getBoundThis() { return boundThis; } /** * [[BoundArguments]] * * @return the bound function arguments */ public final Object[] getBoundArguments() { return boundArguments; } protected final Callable getFlattenedTargetFunction() { return flattenedTargetFunction; } @Override public final String toSource(ExecutionContext cx) { return FunctionSource.nativeCode("BoundFunction"); } /** * 9.4.1.1 [[Call]] (thisArgument, argumentsList) */ @Override public final Object call(ExecutionContext callerContext, Object thisValue, Object... argumentsList) { /* step 1 */ Callable target = getFlattenedTargetFunction(); /* step 2 */ Object boundThis = getBoundThis(); /* step 3 */ Object[] boundArgs = getBoundArguments(); /* step 4 */ Object[] args = concatArguments(callerContext, boundArgs, argumentsList); /* step 5 */ return target.call(callerContext, boundThis, args); } /** * 9.4.1.1 [[Call]] (thisArgument, argumentsList) */ @Override public final Object tailCall(ExecutionContext callerContext, Object thisValue, Object... argumentsList) throws Throwable { /* step 1 */ Callable target = getFlattenedTargetFunction(); /* step 2 */ Object boundThis = getBoundThis(); /* step 3 */ Object[] boundArgs = getBoundArguments(); /* step 4 */ Object[] args = concatArguments(callerContext, boundArgs, argumentsList); /* step 5 */ return target.tailCall(callerContext, boundThis, args); } @Override public final BoundFunctionObject clone(ExecutionContext cx) { /* step 1 (not applicable) */ /* steps 2-4 */ BoundFunctionObject clone; if (this instanceof ConstructorBoundFunctionObject) { clone = new ConstructorBoundFunctionObject(cx.getRealm()); } else { clone = new BoundFunctionObject(cx.getRealm()); } clone.setPrototype(getPrototype()); clone.boundTargetFunction = boundTargetFunction; clone.flattenedTargetFunction = flattenedTargetFunction; clone.boundThis = boundThis; clone.boundArguments = boundArguments; /* step 5 */ return clone; } @Override public final Realm getRealm(ExecutionContext cx) { /* 7.3.22 GetFunctionRealm ( obj ) */ return getFlattenedTargetFunction().getRealm(cx); } /** * 9.4.1.3 BoundFunctionCreate (targetFunction, boundThis, boundArgs) * * @param cx * the execution context * @param targetFunction * the target function * @param boundThis * the bound this-value * @param boundArgs * the bound function arguments * @return the new bound function object */ public static BoundFunctionObject BoundFunctionCreate(ExecutionContext cx, Callable targetFunction, Object boundThis, Object... boundArgs) { /* step 1 (not applicable) */ /* steps 2-3 */ ScriptObject proto = targetFunction.getPrototypeOf(cx); /* steps 4-7 (implicit) */ BoundFunctionObject obj; if (IsConstructor(targetFunction)) { obj = new ConstructorBoundFunctionObject(cx.getRealm()); } else { obj = new BoundFunctionObject(cx.getRealm()); } /* step 8 */ obj.setPrototype(proto); /* step 9 (implicit) */ // Flatten chain of bound function objects. if (targetFunction instanceof BoundFunctionObject) { BoundFunctionObject target = (BoundFunctionObject) targetFunction; /* step 10 */ obj.boundTargetFunction = target; obj.flattenedTargetFunction = target.flattenedTargetFunction; /* step 11 */ obj.boundThis = target.boundThis; /* step 12 */ obj.boundArguments = concatArguments(cx, target.boundArguments, intern(boundArgs)); } else { /* step 10 */ obj.boundTargetFunction = targetFunction; obj.flattenedTargetFunction = targetFunction; /* step 11 */ obj.boundThis = boundThis; /* step 12 */ obj.boundArguments = intern(boundArgs); } /* step 13 */ return obj; } private static Object[] concatArguments(ExecutionContext cx, Object[] boundArgs, Object[] argumentsList) { int argsLen = boundArgs.length + argumentsList.length; if (argsLen > FunctionPrototype.getMaxArguments()) { throw newRangeError(cx, Messages.Key.FunctionTooManyArguments); } if (boundArgs.length == 0) { return argumentsList; } if (argumentsList.length == 0) { return boundArgs; } Object[] args = new Object[argsLen]; System.arraycopy(boundArgs, 0, args, 0, boundArgs.length); System.arraycopy(argumentsList, 0, args, boundArgs.length, argumentsList.length); return args; } private static Object[] intern(Object[] arguments) { return arguments.length > 0 ? arguments : ScriptRuntime.EMPTY_ARRAY; } }