/**
* 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.ToFlatString;
import static com.github.anba.es6draft.runtime.internal.Properties.createProperties;
import static com.github.anba.es6draft.runtime.types.builtins.OrdinaryConstructorFunction.FunctionAllocate;
import static com.github.anba.es6draft.runtime.types.builtins.OrdinaryFunction.FunctionInitialize;
import static com.github.anba.es6draft.runtime.types.builtins.OrdinaryFunction.MakeConstructor;
import static com.github.anba.es6draft.runtime.types.builtins.OrdinaryFunction.SetFunctionName;
import com.github.anba.es6draft.Executable;
import com.github.anba.es6draft.compiler.CompilationException;
import com.github.anba.es6draft.compiler.CompiledObject;
import com.github.anba.es6draft.parser.ParserException;
import com.github.anba.es6draft.runtime.ExecutionContext;
import com.github.anba.es6draft.runtime.GlobalEnvironmentRecord;
import com.github.anba.es6draft.runtime.LexicalEnvironment;
import com.github.anba.es6draft.runtime.Realm;
import com.github.anba.es6draft.runtime.internal.DebugInfo;
import com.github.anba.es6draft.runtime.internal.Initializable;
import com.github.anba.es6draft.runtime.internal.Properties.Attributes;
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.RuntimeInfo;
import com.github.anba.es6draft.runtime.internal.ScriptLoader;
import com.github.anba.es6draft.runtime.internal.Source;
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.builtins.BuiltinConstructor;
import com.github.anba.es6draft.runtime.types.builtins.FunctionObject;
import com.github.anba.es6draft.runtime.types.builtins.FunctionObject.ConstructorKind;
import com.github.anba.es6draft.runtime.types.builtins.FunctionObject.FunctionKind;
import com.github.anba.es6draft.runtime.types.builtins.LegacyConstructorFunction;
import com.github.anba.es6draft.runtime.types.builtins.OrdinaryConstructorFunction;
/**
* <h1>19 Fundamental Objects</h1><br>
* <h2>19.2 Function Objects</h2>
* <ul>
* <li>19.2.1 The Function Constructor
* <li>19.2.2 Properties of the Function Constructor
* </ul>
*/
public final class FunctionConstructor extends BuiltinConstructor implements Initializable {
/**
* Constructs a new Function constructor function.
*
* @param realm
* the realm object
*/
public FunctionConstructor(Realm realm) {
super(realm, "Function", 1);
}
@Override
public void initialize(Realm realm) {
createProperties(realm, this, Properties.class);
}
@Override
public FunctionConstructor clone() {
return new FunctionConstructor(getRealm());
}
/**
* 19.2.1.1 Function (p1, p2, ... , pn, body)
*/
@Override
public FunctionObject call(ExecutionContext callerContext, Object thisValue, Object... args) {
/* steps 1-3 */
return CreateDynamicFunction(callerContext, calleeContext(), this, args);
}
/**
* 19.2.1.1 Function (p1, p2, ... , pn, body)
*/
@Override
public FunctionObject construct(ExecutionContext callerContext, Constructor newTarget,
Object... args) {
/* steps 1-3 */
return CreateDynamicFunction(callerContext, calleeContext(), newTarget, args);
}
/**
* 19.2.1.1.1 RuntimeSemantics: CreateDynamicFunction(constructor, newTarget, kind, args)
*
* @param callerContext
* the caller execution context
* @param cx
* the execution context
* @param newTarget
* the newTarget constructor function
* @param args
* the function arguments
* @return the new function object
*/
private static FunctionObject CreateDynamicFunction(
ExecutionContext callerContext, ExecutionContext cx, Constructor newTarget,
Object... args) {
/* step 1 (not applicable) */
/* step 2 */
Intrinsics fallbackProto = Intrinsics.FunctionPrototype;
/* step 3 (not applicable) */
/* steps 4-10 */
String[] sourceText = functionSourceText(cx, args);
String parameters = sourceText[0], bodyText = sourceText[1];
/* steps 11, 13-20 */
Source source = functionSource(SourceKind.Function, cx.getRealm(), callerContext);
RuntimeInfo.Function function;
try {
ScriptLoader scriptLoader = cx.getRealm().getScriptLoader();
function = scriptLoader.function(source, parameters, bodyText).getFunction();
} catch (ParserException | CompilationException e) {
throw e.toScriptException(cx);
}
/* steps 12, 21-30 */
return CreateDynamicFunction(cx, source, function, newTarget, fallbackProto);
}
/**
* 19.2.1.1.1 RuntimeSemantics: CreateDynamicFunction(constructor, newTarget, kind, args)
*
* @param cx
* the execution context
* @param source
* the source object
* @param function
* the compiled function
* @return the new function object
*/
public static FunctionObject CreateDynamicFunction(ExecutionContext cx,
Source source, RuntimeInfo.Function function) {
return CreateDynamicFunction(cx, source, function,
(Constructor) cx.getIntrinsic(Intrinsics.Function), Intrinsics.FunctionPrototype);
}
/**
* 19.2.1.1.1 RuntimeSemantics: CreateDynamicFunction(constructor, newTarget, kind, args)
*
* @param cx
* the execution context
* @param source
* the source object
* @param function
* the compiled function
* @param newTarget
* the newTarget constructor function
* @param fallbackProto
* the fallback prototype
* @return the new function object
*/
private static FunctionObject CreateDynamicFunction(ExecutionContext cx,
Source source, RuntimeInfo.Function function, Constructor newTarget,
Intrinsics fallbackProto) {
/* steps 1-11, 13-20 (not applicable) */
/* step 12 */
boolean strict = function.isStrict();
/* steps 21-22 */
ScriptObject proto = GetPrototypeFromConstructor(cx, newTarget, fallbackProto);
/* step 23 */
FunctionObject f;
if (function.is(RuntimeInfo.FunctionFlags.Legacy)) {
assert !strict;
f = LegacyConstructorFunction.FunctionAllocate(cx, proto);
} else {
f = FunctionAllocate(cx, proto, strict, FunctionKind.Normal, ConstructorKind.Base);
}
/* steps 24-25 */
LexicalEnvironment<GlobalEnvironmentRecord> scope = f.getRealm().getGlobalEnv();
/* step 26 */
FunctionInitialize(f, FunctionKind.Normal, function, scope, newFunctionExecutable(source));
/* step 27 (not applicable) */
/* step 28 */
// MakeConstructor(cx, uncheckedCast(f));
// Work around for: https://bugs.eclipse.org/bugs/show_bug.cgi?id=479802
if (f instanceof LegacyConstructorFunction) {
MakeConstructor(cx, (LegacyConstructorFunction) f);
} else {
MakeConstructor(cx, (OrdinaryConstructorFunction) f);
}
/* step 29 */
SetFunctionName(f, "anonymous");
/* step 30 */
return f;
}
@SuppressWarnings({ "unchecked", "unused" })
private static <T extends FunctionObject & Constructor> T uncheckedCast(FunctionObject f) {
return (T) f;
}
/**
* 19.2.1.1.1 RuntimeSemantics: CreateDynamicFunction(constructor, newTarget, kind, args)
*
* @param cx
* the execution context
* @param args
* the function arguments
* @return the function source text as a tuple {@code <parameters, body>}
*/
public static String[] functionSourceText(ExecutionContext cx, Object... args) {
/* steps 4-10 */
int argCount = args.length;
String p, bodyText;
if (argCount == 0) {
p = "";
bodyText = "";
} else if (argCount == 1) {
p = "";
bodyText = ToFlatString(cx, args[0]);
} else {
StringBuilder sb = new StringBuilder();
Object firstArg = args[0];
sb.append(ToFlatString(cx, firstArg));
int k = 2;
for (; k < argCount; ++k) {
Object nextArg = args[k - 1];
String nextArgString = ToFlatString(cx, nextArg);
sb.append(',').append(nextArgString);
}
p = sb.toString();
bodyText = ToFlatString(cx, args[k - 1]);
}
return new String[] { p, bodyText };
}
public enum SourceKind {
Function, Generator, AsyncFunction, AsyncGenerator
}
/**
* Creates a {@link Source} object for a dynamic function.
*
* @param kind
* the function kind
* @param realm
* the realm
* @param caller
* the caller execution context
* @return the function source object
*/
public static Source functionSource(SourceKind kind, Realm realm, ExecutionContext caller) {
Source baseSource = realm.sourceInfo(caller);
String sourceName;
if (baseSource != null) {
sourceName = String.format("<%s> (%s)", kind.name(), baseSource.getName());
} else {
sourceName = String.format("<%s>", kind.name());
}
return new Source(baseSource, sourceName, 1);
}
/**
* Creates a new executable object for a dynamic function.
*
* @param source
* the function source object
* @return a new executable object
*/
public static Executable newFunctionExecutable(Source source) {
return new CompiledFunction(source);
}
private static final class CompiledFunction extends CompiledObject {
CompiledFunction(Source source) {
super(new FunctionSourceObject(source));
}
}
private static final class FunctionSourceObject implements RuntimeInfo.SourceObject {
private final Source source;
FunctionSourceObject(Source source) {
this.source = source;
}
@Override
public Source toSource() {
return source;
}
@Override
public DebugInfo debugInfo() {
return null;
}
}
/**
* 19.2.2 Properties of the Function Constructor
*/
public enum Properties {
;
@Prototype
public static final Intrinsics __proto__ = Intrinsics.FunctionPrototype;
/**
* 19.2.2.1 Function.length
*/
@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 = "Function";
/**
* 19.2.2.2 Function.prototype
*/
@Value(name = "prototype", attributes = @Attributes(writable = false, enumerable = false,
configurable = false))
public static final Intrinsics prototype = Intrinsics.FunctionPrototype;
}
}