/** * 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 java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import com.github.anba.es6draft.Executable; import com.github.anba.es6draft.runtime.ExecutionContext; import com.github.anba.es6draft.runtime.LexicalEnvironment; import com.github.anba.es6draft.runtime.Realm; import com.github.anba.es6draft.runtime.internal.RuntimeInfo; import com.github.anba.es6draft.runtime.internal.Source; import com.github.anba.es6draft.runtime.internal.TailCallInvocation; 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> * <ul> * <li>9.2 ECMAScript Function Objects * </ul> */ public abstract class FunctionObject extends OrdinaryObject implements Callable { /** [[Environment]] */ private LexicalEnvironment<?> environment; /** [[FunctionKind]] */ private FunctionKind functionKind; /** [[FormalParameters]] / [[Code]] */ private RuntimeInfo.Function function; /** [[ConstructorKind]] */ private ConstructorKind constructorKind; /** [[Realm]] */ private Realm realm; /** [[ThisMode]] */ private ThisMode thisMode; /** [[Strict]] */ private boolean strict; /** [[HomeObject]] */ private ScriptObject homeObject; private boolean isClone; private Executable executable; private String source; private MethodHandle callMethod; private MethodHandle tailCallMethod; private MethodHandle constructMethod; /** * Constructs a new Function object. * * @param realm * the realm object */ protected FunctionObject(Realm realm) { super(realm); } public enum FunctionKind { Normal, ClassConstructor, Method, Arrow } public enum ConstructorKind { Base, Derived } public enum ThisMode { Lexical, Strict, Global } @SuppressWarnings("unchecked") protected static final <E extends Throwable> E rethrow(Throwable e) throws E { throw (E) e; } /** * Returns the {@link MethodHandle} for the function call entry method. * * @return the call method handle */ public final MethodHandle getCallMethod() { return callMethod; } /** * Returns the {@link MethodHandle} for the function tail-call entry method. * * @return the tail-call method handle */ public final MethodHandle getTailCallMethod() { return tailCallMethod; } /** * Returns the {@link MethodHandle} for the function construct entry method. * * @return the call method handle */ public final MethodHandle getConstructMethod() { return constructMethod; } @Override public final String toSource(ExecutionContext cx) { String source = this.source; if (source == null) { this.source = source = FunctionSource.toSourceString(this); } return source; } @Override public final FunctionObject clone(ExecutionContext cx) { FunctionObject clone = allocateNew(); clone.isClone = true; clone.initialize(getFunctionKind(), getCode(), getEnvironment(), getExecutable()); return clone; } /** * Returns a copy of this function object with the same internal methods and internal slots. * * @param cx * the execution context * @param newHome * the new home object * @return the cloned function object */ public final FunctionObject clone(ExecutionContext cx, ScriptObject newHome) { FunctionObject clone = clone(cx); if (getHomeObject() != null) { clone.toMethod(newHome); } return clone; } /** * Allocates a new, uninitialized copy of this function object. * * @return a new uninitialized function object */ protected abstract FunctionObject allocateNew(); /** * 9.2.3 FunctionAllocate (functionPrototype, strict) * * @param realm * the realm instance * @param functionPrototype * the prototype object * @param strict * the strict mode flag * @param functionKind * the function kind * @param constructorKind * the constructor kind */ protected final void allocate(Realm realm, ScriptObject functionPrototype, boolean strict, FunctionKind functionKind, ConstructorKind constructorKind) { assert this.realm == null && realm != null : "function object already allocated"; /* step 9 */ this.constructorKind = constructorKind; /* step 10 */ this.strict = strict; /* step 11 */ this.functionKind = functionKind; /* step 12 */ this.setPrototype(functionPrototype); /* step 13 */ // f.[[Extensible]] = true (implicit) /* step 14 */ this.realm = realm; } /** * 9.2.4 FunctionInitialize (F, kind, ParameterList, Body, Scope) * * @param kind * the function kind * @param function * the function code * @param scope * the function scope * @param executable * the source executable */ protected final void initialize(FunctionKind kind, RuntimeInfo.Function function, LexicalEnvironment<?> scope, Executable executable) { assert this.function == null && function != null : "function object already initialized"; assert this.functionKind == kind : String.format("%s != %s", functionKind, kind); /* step 5 */ boolean strict = this.strict; /* step 6 */ this.environment = scope; /* steps 7-8 */ this.function = function; this.callMethod = tailCallAdapter(function, this); this.tailCallMethod = function.callMethod(); this.constructMethod = tailConstructAdapter(function); /* steps 9-11 */ if (kind == FunctionKind.Arrow) { this.thisMode = ThisMode.Lexical; } else if (strict) { this.thisMode = ThisMode.Strict; } else { this.thisMode = ThisMode.Global; } this.executable = executable; } /** * 9.2.10 MakeMethod ( F, homeObject) * * @param homeObject * the new home object */ protected final void toMethod(ScriptObject homeObject) { assert homeObject != null; assert this.homeObject == null : "function object already method"; this.homeObject = homeObject; } private static MethodHandle tailCallAdapter(RuntimeInfo.Function function, FunctionObject functionObject) { MethodHandle mh = function.callMethod(); if (function.is(RuntimeInfo.FunctionFlags.TailCall)) { assert !function.isGenerator() && !function.isAsync() && function.isStrict(); MethodHandle result = TailCallInvocation.getTailCallHandler(); result = MethodHandles.dropArguments(result, 1, functionObject.getClass()); result = MethodHandles.dropArguments(result, 3, Object.class, Object[].class); result = MethodHandles.foldArguments(result, mh); return result; } return mh; } private static MethodHandle tailConstructAdapter(RuntimeInfo.Function function) { MethodHandle mh = function.constructMethod(); if (mh != null && function.is(RuntimeInfo.FunctionFlags.TailConstruct)) { assert !function.isGenerator() && !function.isAsync() && function.isStrict(); MethodHandle result = TailCallInvocation.getTailConstructHandler(); result = MethodHandles.dropArguments(result, 1, OrdinaryConstructorFunction.class); result = MethodHandles.dropArguments(result, 3, Constructor.class, Object[].class); result = MethodHandles.foldArguments(result, mh); return result; } return mh; } /** * [[Environment]] * * @return the environment field */ public final LexicalEnvironment<?> getEnvironment() { return environment; } /** * [[FunctionKind]] * * @return the function kind field */ public final FunctionKind getFunctionKind() { return functionKind; } /** * [[Code]] * * @return the function code field */ public final RuntimeInfo.Function getCode() { return function; } /** * Returns the method info object. * * @return the method info object */ public final Object getMethodInfo() { return function.methodInfo(); } /** * Returns the executable object. * * @return the executable object */ public final Executable getExecutable() { return executable; } /** * Returns {@code true} if this is a cloned function object. * * @return {@code true} if cloned function */ public boolean isClone() { return isClone; } /** * [[Realm]] * * @return the realm field */ public final Realm getRealm() { return realm; } @Override public final Realm getRealm(ExecutionContext cx) { /* 7.3.22 GetFunctionRealm ( obj ) */ return realm; } /** * [[ThisMode]] * * @return the this-mode field */ public final ThisMode getThisMode() { return thisMode; } /** * [[Strict]] * * @return the strict mode field */ public final boolean isStrict() { return strict; } /** * [[HomeObject]] * * @return the home object field */ public final ScriptObject getHomeObject() { return homeObject; } /** * [[ConstructorKind]] * * @return the constructor kind field */ public final ConstructorKind getConstructorKind() { return constructorKind; } @Override public String toString() { Source source = executable.getSourceObject().toSource(); return String.format("%s, functionKind=%s, constructorKind=%s, thisMode=%s, cloned=%b, source=%s", super.toString(), functionKind, constructorKind, thisMode, isClone, source); } }