/**
 * Copyright (c) 2012-2013 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.throwTypeError;

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.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;

/**
 * <h1>9 ECMAScript Ordinary and Exotic Objects Behaviours</h1><br>
 * <h2>9.2 Built-in Exotic Object Internal Methods and Data Fields</h2>
 * <ul>
 * <li>9.2.1 Bound Function Exotic Objects
 * </ul>
 */
public class ExoticBoundFunction extends OrdinaryObject implements Callable {
    /** [[Realm]] */
    private final Realm realm;

    /** [[BoundTargetFunction]] */
    private Callable boundTargetFunction;

    /** [[BoundThis]] */
    private Object boundThis;

    /** [[BoundArguments]] */
    private Object[] boundArguments;

    private static class ConstructorExoticBoundFunction extends ExoticBoundFunction implements
            Constructor {
        public ConstructorExoticBoundFunction(Realm realm) {
            super(realm);
        }

        @Override
        public boolean isConstructor() {
            // ConstructorExoticBoundFunction is only created if [[BoundTargetFunction]] already has
            // [[Construct]]
            return true;
        }

        /**
         * 9.2.1.2 [[Construct]]
         */
        @Override
        public ScriptObject construct(ExecutionContext callerContext, Object... extraArgs) {
            /* step 1 */
            Callable target = getBoundTargetFunction();
            /* step 2 */
            if (!(target instanceof Constructor)) {
                throw throwTypeError(callerContext, Messages.Key.NotConstructor);
            }
            /* step 3 */
            Object[] boundArgs = getBoundArguments();
            /* step 4 */
            Object[] args = new Object[boundArgs.length + extraArgs.length];
            System.arraycopy(boundArgs, 0, args, 0, boundArgs.length);
            System.arraycopy(extraArgs, 0, args, boundArgs.length, extraArgs.length);
            /* step 5 */
            return ((Constructor) target).construct(callerContext, args);
        }
    }

    public ExoticBoundFunction(Realm realm) {
        super(realm);
        this.realm = realm;
    }

    /**
     * [[Realm]]
     */
    protected final Realm realm() {
        return realm;
    }

    /**
     * [[BoundTargetFunction]]
     */
    public Callable getBoundTargetFunction() {
        return boundTargetFunction;
    }

    /**
     * [[BoundThis]]
     */
    public Object getBoundThis() {
        return boundThis;
    }

    /**
     * [[BoundArguments]]
     */
    public Object[] getBoundArguments() {
        return boundArguments;
    }

    @Override
    public String toSource() {
        return "function BoundFunction() { /* native code */ }";
    }

    /**
     * 9.2.1.1 [[Call]]
     */
    @Override
    public Object call(ExecutionContext callerContext, Object thisValue, Object... argumentsList) {
        /* step 1 */
        Object[] boundArgs = getBoundArguments();
        /* step 2 */
        Object boundThis = getBoundThis();
        /* step 3 */
        Callable target = getBoundTargetFunction();
        /* step 4 */
        Object[] args = new Object[boundArgs.length + argumentsList.length];
        System.arraycopy(boundArgs, 0, args, 0, boundArgs.length);
        System.arraycopy(argumentsList, 0, args, boundArgs.length, argumentsList.length);
        /* step 5 */
        return target.call(callerContext, boundThis, args);
    }

    /**
     * 9.2.1.3 BoundFunctionCreate Abstract Operation
     */
    public static ExoticBoundFunction BoundFunctionCreate(ExecutionContext cx,
            Callable targetFunction, Object boundThis, Object[] boundArgs) {
        /* step 1 */
        ScriptObject proto = cx.getIntrinsic(Intrinsics.FunctionPrototype);
        /* steps 2-5 (implicit) */
        ExoticBoundFunction obj;
        if (IsConstructor(targetFunction)) {
            obj = new ConstructorExoticBoundFunction(cx.getRealm());
        } else {
            obj = new ExoticBoundFunction(cx.getRealm());
        }
        /* step 6 */
        obj.setPrototype(proto);
        /* step 7 (implicit) */
        /* step 8 */
        obj.boundTargetFunction = targetFunction;
        /* step 9 */
        obj.boundThis = boundThis;
        /* step 10 */
        obj.boundArguments = boundArgs;
        /* step 11 */
        return obj;
    }
}

@@ -309,87 +309,8 @@
 tor;
-%0Aimport static com.github.anba.es6draft.runtime.internal.Errors.throwTypeError;
 %0A%0Aim
@@ -416,67 +416,8 @@
 lm;%0A
-import com.github.anba.es6draft.runtime.internal.Messages;%0A
 impo
@@ -936,61 +936,8 @@
 e %7B%0A
-    /** %5B%5BRealm%5D%5D */%0A    private final Realm realm;%0A%0A
     
@@ -1868,143 +1868,37 @@
     
-if (!(target instanceof Constructor)) %7B%0A                throw throwTypeError(callerContext, Messages.Key.NotConstructor);%0A            %7D
+assert IsConstructor(target);
 %0A   
@@ -2386,32 +2386,32 @@
 (Realm realm) %7B%0A
+
         super(re
@@ -2420,134 +2420,8 @@
 m);%0A
-        this.realm = realm;%0A    %7D%0A%0A    /**%0A     * %5B%5BRealm%5D%5D%0A     */%0A    protected final Realm realm() %7B%0A        return realm;%0A
     

Remove unused [[Realm]] field in BoundFunction