package com.oracle.truffle.js.runtime.builtins;
import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.frame.Frame;
import com.oracle.truffle.api.frame.FrameInstance;
import com.oracle.truffle.api.frame.FrameInstanceVisitor;
import com.oracle.truffle.api.frame.MaterializedFrame;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.IndirectCallNode;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.RootNode;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.object.HiddenKey;
import com.oracle.truffle.api.object.Shape;
import com.oracle.truffle.api.profiles.BranchProfile;
import com.oracle.truffle.api.profiles.ConditionProfile;
import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.api.source.SourceSection;
import com.oracle.truffle.js.builtins.AsyncFromSyncIteratorPrototypeBuiltins;
import com.oracle.truffle.js.builtins.AsyncGeneratorPrototypeBuiltins;
import com.oracle.truffle.js.builtins.ConstructorBuiltins;
import com.oracle.truffle.js.builtins.EnumerateIteratorPrototypeBuiltins;
import com.oracle.truffle.js.builtins.ForInIteratorPrototypeBuiltins;
import com.oracle.truffle.js.builtins.FunctionPrototypeBuiltins;
import com.oracle.truffle.js.builtins.GeneratorPrototypeBuiltins;
import com.oracle.truffle.js.lang.JavaScriptLanguage;
import com.oracle.truffle.js.runtime.Errors;
import com.oracle.truffle.js.runtime.JSArguments;
import com.oracle.truffle.js.runtime.JSContext;
import com.oracle.truffle.js.runtime.JSContext.BuiltinFunctionKey;
import com.oracle.truffle.js.runtime.JSFrameUtil;
import com.oracle.truffle.js.runtime.JSRealm;
import com.oracle.truffle.js.runtime.JSRuntime;
import com.oracle.truffle.js.runtime.JavaScriptRootNode;
import com.oracle.truffle.js.runtime.Symbol;
import com.oracle.truffle.js.runtime.objects.JSAttributes;
import com.oracle.truffle.js.runtime.objects.JSDynamicObject;
import com.oracle.truffle.js.runtime.objects.JSObject;
import com.oracle.truffle.js.runtime.objects.JSObjectUtil;
import com.oracle.truffle.js.runtime.objects.JSShape;
import com.oracle.truffle.js.runtime.objects.Null;
import com.oracle.truffle.js.runtime.objects.Nullish;
import com.oracle.truffle.js.runtime.objects.PropertyDescriptor;
import com.oracle.truffle.js.runtime.objects.PropertyProxy;
import com.oracle.truffle.js.runtime.objects.Undefined;
public final class JSFunction extends JSNonProxy {
public static final String TYPE_NAME = "function";
public static final String CLASS_NAME = "Function";
public static final String CLASS_NAME_NASHORN_COMPAT = "FunctionNashornCompat";
public static final String PROTOTYPE_NAME = "Function.prototype";
public static final String GENERATOR_FUNCTION_NAME = "GeneratorFunction";
public static final String GENERATOR_NAME = "Generator";
public static final String GENERATOR_PROTOTYPE_NAME = "Generator.prototype";
public static final String ASYNC_FUNCTION_NAME = "AsyncFunction";
public static final String ASYNC_GENERATOR_FUNCTION_NAME = "AsyncGeneratorFunction";
public static final String ASYNC_GENERATOR_NAME = "AsyncGenerator";
public static final String ASYNC_GENERATOR_PROTOTYPE_NAME = "AsyncGenerator.prototype";
public static final String ENUMERATE_ITERATOR_PROTOTYPE_NAME = "[[Enumerate]].prototype";
public static final String FOR_IN_ITERATOR_PROTOYPE_NAME = "%ForInIteratorPrototype%";
public static final String CALLER = "caller";
public static final String ARGUMENTS = "arguments";
public static final String LENGTH = "length";
public static final String NAME = "name";
public static final String PROGRAM_FUNCTION_NAME = ":program";
public static final String BUILTIN_SOURCE_NAME = "<builtin>";
public static final SourceSection BUILTIN_SOURCE_SECTION = createBuiltinSourceSection(BUILTIN_SOURCE_NAME);
public static final HiddenKey ASYNC_FROM_SYNC_ITERATOR_KEY = new HiddenKey("SyncIterator");
public static final String ASYNC_FROM_SYNC_ITERATOR_PROTOTYPE_NAME = "%AsyncFromSyncIteratorPrototype%";
public static final PropertyProxy PROTOTYPE_PROXY = new ClassPrototypeProxyProperty();
public static class FunctionLengthPropertyProxy implements PropertyProxy {
@Override
public Object get(DynamicObject store) {
assert JSFunction.isJSFunction(store);
if (JSFunction.isBoundFunction(store)) {
return getBoundFunctionLength(store);
}
return JSFunction.getLength(store);
}
public int getProfiled(DynamicObject store, BranchProfile isBoundBranch) {
assert JSFunction.isJSFunction(store);
if (JSFunction.isBoundFunction(store)) {
isBoundBranch.enter();
return getBoundFunctionLength(store);
}
return JSFunction.getLength(store);
}
@TruffleBoundary
private int getBoundFunctionLength(DynamicObject store) {
if (JSFunction.isBoundFunction(store)) {
return Math.max(0, getBoundFunctionLength(JSFunction.getBoundTargetFunction(store)) - JSFunction.getBoundArguments(store).length);
} else {
return JSFunction.getLength(store);
}
}
}
public static final PropertyProxy LENGTH_PROXY = new FunctionLengthPropertyProxy();
public static class FunctionNamePropertyProxy implements PropertyProxy {
@Override
public Object get(DynamicObject store) {
assert JSFunction.isJSFunction(store);
if (JSFunction.isBoundFunction(store)) {
return getBoundFunctionName(store);
}
return JSFunction.getName(store);
}
public String getProfiled(DynamicObject store, BranchProfile isBoundBranch) {
assert JSFunction.isJSFunction(store);
if (JSFunction.isBoundFunction(store)) {
isBoundBranch.enter();
return getBoundFunctionName(store);
}
return JSFunction.getName(store);
}
@TruffleBoundary
private String getBoundFunctionName(DynamicObject store) {
if (JSFunction.isBoundFunction(store)) {
return "bound " + getBoundFunctionName(JSFunction.getBoundTargetFunction(store));
} else {
return JSFunction.getName(store);
}
}
}
public static final PropertyProxy NAME_PROXY = new FunctionNamePropertyProxy();
public static final Object CLASS_PROTOTYPE_PLACEHOLDER = new Object();
public static final JSFunction INSTANCE = new JSFunction();
public static final HiddenKey HOME_OBJECT_ID = new HiddenKey("HomeObject");
public static final HiddenKey CLASS_FIELDS_ID = new HiddenKey("Fields");
public static final HiddenKey PRIVATE_BRAND_ID = new HiddenKey("PrivateBrand");
public static final HiddenKey GENERATOR_STATE_ID = new HiddenKey("GeneratorState");
public static final HiddenKey GENERATOR_CONTEXT_ID = new HiddenKey("GeneratorContext");
public static final HiddenKey GENERATOR_TARGET_ID = new HiddenKey("GeneratorTarget");
public static final HiddenKey ASYNC_GENERATOR_STATE_ID = new HiddenKey("AsyncGeneratorState");
public static final HiddenKey ASYNC_GENERATOR_CONTEXT_ID = new HiddenKey("AsyncGeneratorContext");
public static final HiddenKey ASYNC_GENERATOR_QUEUE_ID = new HiddenKey("AsyncGeneratorQueue");
public static final HiddenKey ASYNC_GENERATOR_TARGET_ID = new HiddenKey("AsyncGeneratorTarget");
private static final HiddenKey GENERATOR_FUNCTION_MARKER_ID = new HiddenKey("generator function");
private static final HiddenKey ASYNC_GENERATOR_FUNCTION_MARKER_ID = new HiddenKey("async generator function");
public enum GeneratorState {
SuspendedStart,
SuspendedYield,
Executing,
Completed,
}
public enum AsyncGeneratorState {
SuspendedStart,
SuspendedYield,
Executing,
AwaitingReturn,
Completed,
}
private JSFunction() {
}
public static CallTarget getCallTarget(DynamicObject obj) {
return getFunctionData(obj).getCallTarget();
}
public static MaterializedFrame getEnclosingFrame(DynamicObject obj) {
assert isJSFunction(obj);
return ((JSFunctionObject) obj).getEnclosingFrame();
}
public static JSFunctionData getFunctionData(DynamicObject obj) {
assert isJSFunction(obj);
return ((JSFunctionObject) obj).getFunctionData();
}
private static Object getClassPrototypeField(DynamicObject obj) {
assert isJSFunction(obj);
return ((JSFunctionObject) obj).getClassPrototype();
}
private static void setClassPrototypeField(DynamicObject obj, Object classPrototype) {
assert isJSFunction(obj);
((JSFunctionObject) obj).setClassPrototype(classPrototype);
}
public static JSRealm getRealm(DynamicObject obj) {
assert isJSFunction(obj);
return ((JSFunctionObject) obj).getRealm();
}
public static JSRealm getRealm(DynamicObject functionObj, JSContext context) {
assert isJSFunction(functionObj);
JSRealm realm;
if (context.isSingleRealm()) {
realm = context.getRealm();
assert realm == getRealm(functionObj);
} else {
realm = getRealm(functionObj);
}
return realm;
}
public static DynamicObject create(JSRealm realm, JSFunctionData functionData) {
return create(realm, functionData, JSFrameUtil.NULL_MATERIALIZED_FRAME);
}
public static DynamicObject create(JSRealm realm, JSFunctionData functionData, MaterializedFrame enclosingFrame) {
return createDefault(functionData, enclosingFrame, CLASS_PROTOTYPE_PLACEHOLDER, realm);
}
public static DynamicObject createWithPrototype(JSFunctionFactory factory, JSRealm realm, JSFunctionData functionData, MaterializedFrame enclosingFrame, DynamicObject prototype) {
return createWithPrototype(factory, functionData, enclosingFrame, CLASS_PROTOTYPE_PLACEHOLDER, realm, prototype);
}
public static DynamicObject createLexicalThis(JSRealm realm, JSFunctionData functionData, MaterializedFrame enclosingFrame, Object lexicalThis) {
return createDefault(functionData, enclosingFrame, lexicalThis, realm);
}
private static DynamicObject createDefault(JSFunctionData functionData, MaterializedFrame enclosingFrame, Object classPrototype, JSRealm realm) {
JSFunctionFactory factory = initialFactory(functionData);
return factory.create(functionData, enclosingFrame, classPrototype, realm);
}
private static DynamicObject createWithPrototype(JSFunctionFactory factory, JSFunctionData functionData, MaterializedFrame enclosingFrame, Object classPrototype, JSRealm realm,
DynamicObject prototype) {
return factory.createWithPrototype(functionData, enclosingFrame, classPrototype, realm, prototype);
}
public static DynamicObject createBound(JSContext context, JSRealm realm, JSFunctionData functionData, DynamicObject boundTargetFunction, Object boundThis, Object[] boundArguments) {
assert functionData != null;
JSFunctionFactory factory = context.getBoundFunctionFactory(functionData);
return factory.createBound(functionData, CLASS_PROTOTYPE_PLACEHOLDER, realm, boundTargetFunction, boundThis, boundArguments);
}
private static JSFunctionFactory initialFactory(JSFunctionData functionData) {
return functionData.getContext().getFunctionFactory(functionData);
}
public static String getName(DynamicObject obj) {
return getFunctionData(obj).getName();
}
public static Object call(DynamicObject functionObject, Object thisObject, Object[] argumentValues) {
assert JSFunction.isJSFunction(functionObject);
assert thisObject != null;
Object[] arguments = JSArguments.create(thisObject, functionObject, argumentValues);
return getCallTarget(functionObject).call(arguments);
}
public static Object call(Object[] jsArguments) {
assert JSFunction.isJSFunction(JSArguments.getFunctionObject(jsArguments));
assert JSArguments.getThisObject(jsArguments) != null;
return getCallTarget((DynamicObject) JSArguments.getFunctionObject(jsArguments)).call(jsArguments);
}
public static Object construct(DynamicObject functionObject, Object[] argumentValues) {
assert isJSFunction(functionObject) && isConstructor(functionObject);
Object[] arguments = JSArguments.create(CONSTRUCT, functionObject, argumentValues);
return getConstructTarget(functionObject).call(arguments);
}
@TruffleBoundary
public static DynamicObject bind(JSRealm realm, DynamicObject thisFnObj, Object thisArg, Object[] boundArguments) {
assert JSFunction.isJSFunction(thisFnObj);
JSContext context = realm.getContext();
DynamicObject proto = JSObject.getPrototype(thisFnObj);
DynamicObject boundFunction = boundFunctionCreate(context, thisFnObj, thisArg, boundArguments, proto, null, null);
long length = 0;
boolean targetHasLength = JSObject.hasOwnProperty(thisFnObj, JSFunction.LENGTH);
boolean mustSetLength = true;
if (targetHasLength) {
Object targetLen = JSObject.get(thisFnObj, JSFunction.LENGTH);
if (JSRuntime.isNumber(targetLen)) {
long targetLenInt = JSRuntime.toInteger(targetLen);
length = Math.max(0, targetLenInt - boundArguments.length);
if (targetLenInt == getLength(thisFnObj)) {
mustSetLength = false;
}
}
}
if (mustSetLength) {
setFunctionLength(boundFunction, JSRuntime.longToIntOrDouble(length));
}
String targetName = getFunctionName(thisFnObj);
if (!targetName.equals(getName(thisFnObj))) {
setBoundFunctionName(boundFunction, targetName);
}
return boundFunction;
}
public static DynamicObject boundFunctionCreate(JSContext context, DynamicObject boundTargetFunction, Object boundThis, Object[] boundArguments, DynamicObject proto,
ConditionProfile isAsyncProfile, ConditionProfile setProtoProfile) {
assert JSFunction.isJSFunction(boundTargetFunction);
CompilerAsserts.partialEvaluationConstant(context);
boolean constructor = JSFunction.isConstructor(boundTargetFunction);
JSFunctionData functionData = context.getBoundFunctionData(constructor);
boolean isAsync = JSFunction.getFunctionData(boundTargetFunction).isAsync();
if ((isAsyncProfile == null ? isAsync : isAsyncProfile.profile(isAsync))) {
int length = Math.max(0, JSFunction.getLength(boundTargetFunction) - boundArguments.length);
functionData = makeBoundFunctionData(context, length, constructor, isAsync, functionData.getName());
}
JSRealm realm = getRealm(boundTargetFunction, context);
DynamicObject boundFunction = JSFunction.createBound(context, realm, functionData, boundTargetFunction, boundThis, boundArguments);
boolean needSetProto = proto != realm.getFunctionPrototype();
if ((setProtoProfile == null ? needSetProto : setProtoProfile.profile(needSetProto))) {
JSObject.setPrototype(boundFunction, proto);
}
assert JSObject.getPrototype(boundFunction) == proto;
return boundFunction;
}
@TruffleBoundary
private static JSFunctionData makeBoundFunctionData(JSContext context, int length, boolean constructor, boolean isAsync, String name) {
return JSFunctionData.create(context,
context.getBoundFunctionCallTarget(), context.getBoundFunctionConstructTarget(), context.getBoundFunctionConstructNewTarget(),
length, name, constructor, false, true, false, false, false, isAsync, false, true, false, true);
}
@TruffleBoundary
private static String getFunctionName(DynamicObject thisFnObj) {
Object name = JSObject.get(thisFnObj, NAME);
if (!JSRuntime.isString(name)) {
name = "";
}
return name.toString();
}
@TruffleBoundary
public static void setFunctionLength(DynamicObject functionObj, Number length) {
JSObject.defineOwnProperty(functionObj, JSFunction.LENGTH, PropertyDescriptor.createData(length, false, false, true));
}
@TruffleBoundary
public static void setBoundFunctionName(DynamicObject boundFunction, String targetName) {
JSObject.defineOwnProperty(boundFunction, JSFunction.NAME, PropertyDescriptor.createData("bound " + targetName, false, false, true));
}
public static boolean isStrict(DynamicObject obj) {
return getFunctionData(obj).isStrict();
}
public static boolean isBuiltin(DynamicObject obj) {
return getFunctionData(obj).isBuiltin();
}
public static boolean isConstructor(DynamicObject obj) {
assert JSFunction.isJSFunction(obj);
return getFunctionData(obj).isConstructor();
}
public static boolean isConstructor(Object obj) {
return JSFunction.isJSFunction(obj) && getFunctionData((DynamicObject) obj).isConstructor();
}
public static boolean isGenerator(DynamicObject obj) {
return getFunctionData(obj).isGenerator();
}
public static boolean needsParentFrame(DynamicObject obj) {
return getFunctionData(obj).needsParentFrame();
}
public static int getLength(DynamicObject obj) {
return getFunctionData(obj).getLength();
}
public static boolean isClassPrototypeInitialized(DynamicObject thisObj) {
return getClassPrototypeField(thisObj) != CLASS_PROTOTYPE_PLACEHOLDER;
}
public static boolean isBoundFunction(DynamicObject function) {
return isJSFunction(function) && getFunctionData(function).isBound();
}
public static boolean isAsyncFunction(DynamicObject function) {
return isJSFunction(function) && getFunctionData(function).isAsync();
}
public static Object getBoundThis(DynamicObject function) {
assert isBoundFunction(function);
return ((JSFunctionObject.Bound) function).getBoundThis();
}
public static DynamicObject getBoundTargetFunction(DynamicObject function) {
assert isBoundFunction(function);
return ((JSFunctionObject.Bound) function).getBoundTargetFunction();
}
public static Object[] getBoundArguments(DynamicObject function) {
assert isBoundFunction(function);
return ((JSFunctionObject.Bound) function).getBoundArguments();
}
public static Object getLexicalThis(DynamicObject thisObj) {
return getClassPrototypeInitialized(thisObj);
}
public static Object getClassPrototypeInitialized(DynamicObject thisObj) {
Object classPrototype = getClassPrototypeField(thisObj);
assert classPrototype != CLASS_PROTOTYPE_PLACEHOLDER;
return classPrototype;
}
public static Object getClassPrototype(DynamicObject thisObj) {
Object classPrototype = getClassPrototypeField(thisObj);
if (classPrototype == CLASS_PROTOTYPE_PLACEHOLDER) {
CompilerDirectives.transferToInterpreterAndInvalidate();
initializeClassPrototype(thisObj);
}
return getClassPrototypeField(thisObj);
}
private static void initializeClassPrototype(DynamicObject thisObj) {
setClassPrototypeField(thisObj, createPrototype(thisObj));
}
private static DynamicObject createPrototype(DynamicObject constructor) {
JSFunctionData functionData = getFunctionData(constructor);
JSRealm realm = getRealm(constructor);
JSContext context = functionData.getContext();
if (!functionData.isGenerator()) {
DynamicObject prototype = JSOrdinary.create(context, realm);
JSObjectUtil.putConstructorProperty(context, prototype, constructor);
return prototype;
} else {
assert functionData.isGenerator();
if (functionData.isAsync()) {
return JSOrdinary.createWithRealm(context, context.getAsyncGeneratorObjectFactory(), realm);
} else {
return JSOrdinary.createWithRealm(context, context.getGeneratorObjectFactory(), realm);
}
}
}
public static void setClassPrototype(DynamicObject thisObj, Object value) {
assert value != null;
setClassPrototypeField(thisObj, value);
}
public static final class ClassPrototypeProxyProperty implements PropertyProxy {
private ClassPrototypeProxyProperty() {
}
@Override
public boolean set(DynamicObject store, Object value) {
assert JSFunction.isJSFunction(store);
JSFunction.setClassPrototype(store, value);
return true;
}
@Override
public Object get(DynamicObject store) {
assert JSFunction.isJSFunction(store);
return JSFunction.getClassPrototype(store);
}
}
static class BoundRootNode extends JavaScriptRootNode {
private static final SourceSection SOURCE_SECTION = createBuiltinSourceSection("bound function");
@Child protected IndirectCallNode callNode;
protected final BranchProfile initProfile = BranchProfile.create();
BoundRootNode(JSContext context) {
super(context.getLanguage(), SOURCE_SECTION, null);
this.callNode = Truffle.getRuntime().createIndirectCallNode();
}
@Override
public Object execute(VirtualFrame frame) {
Object[] originalArguments = frame.getArguments();
DynamicObject boundFunction = castBoundFunction(JSArguments.getFunctionObject(originalArguments));
DynamicObject boundTargetFunction = getBoundTargetFunction(boundFunction);
Object[] boundArguments = getBoundArguments(boundFunction);
Object boundThis = getBoundThis(boundFunction);
Object[] argumentValues = JSArguments.extractUserArguments(originalArguments);
Object[] arguments = prependBoundArguments(boundArguments, argumentValues);
Object[] newArguments = JSArguments.create(boundThis, boundTargetFunction, arguments);
return callNode.call(JSFunction.getFunctionData(boundTargetFunction).getCallTarget(initProfile), newArguments);
}
protected static Object[] prependBoundArguments(Object[] boundArguments, Object[] argumentValues) {
Object[] arguments = new Object[boundArguments.length + argumentValues.length];
System.arraycopy(boundArguments, 0, arguments, 0, boundArguments.length);
System.arraycopy(argumentValues, 0, arguments, boundArguments.length, argumentValues.length);
return arguments;
}
protected static DynamicObject castBoundFunction(Object functionObj) {
DynamicObject boundFunction = (DynamicObject) functionObj;
if (!isBoundFunction(boundFunction)) {
throw Errors.shouldNotReachHere();
}
return boundFunction;
}
}
static final class BoundConstructRootNode extends BoundRootNode {
BoundConstructRootNode(JSContext context) {
super(context);
}
@Override
public Object execute(VirtualFrame frame) {
Object[] originalArguments = frame.getArguments();
DynamicObject boundFunction = castBoundFunction(JSArguments.getFunctionObject(originalArguments));
DynamicObject boundTargetFunction = getBoundTargetFunction(boundFunction);
Object[] boundArguments = getBoundArguments(boundFunction);
Object[] argumentValues = JSArguments.extractUserArguments(originalArguments);
Object[] arguments = prependBoundArguments(boundArguments, argumentValues);
Object originalThis = JSArguments.getThisObject(originalArguments);
Object[] newArguments = JSArguments.create(originalThis, boundTargetFunction, arguments);
return callNode.call(JSFunction.getFunctionData(boundTargetFunction).getConstructTarget(initProfile), newArguments);
}
}
static final class BoundConstructNewTargetRootNode extends BoundRootNode {
BoundConstructNewTargetRootNode(JSContext context) {
super(context);
}
@Override
public Object execute(VirtualFrame frame) {
Object[] originalArguments = frame.getArguments();
DynamicObject boundFunction = castBoundFunction(JSArguments.getFunctionObject(originalArguments));
DynamicObject boundTargetFunction = getBoundTargetFunction(boundFunction);
Object[] boundArguments = getBoundArguments(boundFunction);
Object[] argumentValues = JSArguments.extractUserArguments(originalArguments, 1);
Object[] arguments = prependBoundArguments(boundArguments, argumentValues);
Object originalThis = JSArguments.getThisObject(originalArguments);
Object newTarget = JSArguments.getNewTarget(originalArguments);
if (newTarget == boundFunction) {
newTarget = boundTargetFunction;
}
Object[] newArguments = JSArguments.createWithNewTarget(originalThis, boundTargetFunction, newTarget, arguments);
return callNode.call(JSFunction.getFunctionData(boundTargetFunction).getConstructNewTarget(initProfile), newArguments);
}
}
public static RootNode createBoundRootNode(JSContext context, boolean construct, boolean newTarget) {
if (newTarget) {
return new BoundConstructNewTargetRootNode(context);
} else if (construct) {
return new BoundConstructRootNode(context);
} else {
return new BoundRootNode(context);
}
}
public static DynamicObject createFunctionPrototype(JSRealm realm, DynamicObject objectPrototype) {
JSContext context = realm.getContext();
Shape protoShape = JSShape.createPrototypeShape(context, INSTANCE, objectPrototype);
DynamicObject proto = JSFunctionObject.create(protoShape, createEmptyFunctionData(context), JSFrameUtil.NULL_MATERIALIZED_FRAME, realm, CLASS_PROTOTYPE_PLACEHOLDER);
JSObjectUtil.setOrVerifyPrototype(context, proto, objectPrototype);
JSObjectUtil.putDataProperty(context, proto, LENGTH, 0, JSAttributes.configurableNotEnumerableNotWritable());
JSObjectUtil.putDataProperty(context, proto, NAME, "", JSAttributes.configurableNotEnumerableNotWritable());
return proto;
}
public static void addRestrictedFunctionProperties(JSRealm realm, DynamicObject obj) {
JSObjectUtil.putBuiltinAccessorProperty(obj, CALLER, realm.getThrowerFunction(), realm.getThrowerFunction());
JSObjectUtil.putBuiltinAccessorProperty(obj, ARGUMENTS, realm.getThrowerFunction(), realm.getThrowerFunction());
}
public static JSFunctionData createNamedEmptyFunctionData(JSContext context, String name) {
return JSFunctionData.createCallOnly(context, context.getEmptyFunctionCallTarget(), 0, name);
}
public static JSFunctionData createEmptyFunctionData(JSContext context) {
return createNamedEmptyFunctionData(context, "");
}
public static DynamicObject createNamedEmptyFunction(JSRealm realm, String name) {
return JSFunction.create(realm, createNamedEmptyFunctionData(realm.getContext(), name));
}
public static DynamicObject createEmptyFunction(JSRealm realm) {
return JSFunction.create(realm, createEmptyFunctionData(realm.getContext()));
}
public static void fillFunctionPrototype(JSRealm realm) {
JSContext ctx = realm.getContext();
JSObjectUtil.putConstructorProperty(ctx, realm.getFunctionPrototype(), realm.getFunctionConstructor());
JSObjectUtil.putFunctionsFromContainer(realm, realm.getFunctionPrototype(), FunctionPrototypeBuiltins.BUILTINS);
if (ctx.getEcmaScriptVersion() >= 6) {
addRestrictedFunctionProperties(realm, realm.getFunctionPrototype());
}
if (ctx.isOptionNashornCompatibilityMode()) {
JSObjectUtil.putFunctionsFromContainer(realm, realm.getFunctionPrototype(), FunctionPrototypeBuiltins.BUILTINS_NASHORN_COMPAT);
}
}
public static Shape makeFunctionShape(JSContext context, DynamicObject prototype, boolean isGenerator, boolean isAsync) {
Shape initialShape = JSObjectUtil.getProtoChildShape(prototype, INSTANCE, context);
if (isGenerator) {
initialShape = Shape.newBuilder(initialShape).addConstantProperty(isAsync ? ASYNC_GENERATOR_FUNCTION_MARKER_ID : GENERATOR_FUNCTION_MARKER_ID, null, 0).build();
}
return initialShape;
}
public static DynamicObject createFunctionConstructor(JSRealm realm) {
JSContext ctx = realm.getContext();
DynamicObject functionConstructor = realm.lookupFunction(ConstructorBuiltins.BUILTINS, CLASS_NAME);
JSObjectUtil.putDataProperty(ctx, functionConstructor, JSObject.PROTOTYPE, realm.getFunctionPrototype(), JSAttributes.notConfigurableNotEnumerableNotWritable());
return functionConstructor;
}
@Override
public String getClassName(DynamicObject object) {
return CLASS_NAME;
}
@Override
public String getBuiltinToStringTag(DynamicObject object) {
return getClassName(object);
}
@Override
@TruffleBoundary
public String toDisplayStringImpl(DynamicObject obj, int depth, boolean allowSideEffects, JSContext context) {
RootNode rn = ((RootCallTarget) JSFunction.getCallTarget(obj)).getRootNode();
SourceSection ssect = rn.getSourceSection();
String source;
if (ssect == null || !ssect.isAvailable() || ssect.getSource().isInternal()) {
source = "function " + JSFunction.getName(obj) + "() { [native code] }";
} else if (depth <= 0) {
source = "function " + JSFunction.getName(obj) + "() {...}";
} else {
if (ssect.getCharacters().length() > 200) {
source = ssect.getCharacters().subSequence(0, 195) + "...<omitted>...\n}";
} else {
source = ssect.getCharacters().toString();
}
}
return source;
}
@Override
public boolean hasOnlyShapeProperties(DynamicObject obj) {
return true;
}
public static CallTarget getConstructTarget(DynamicObject obj) {
return getFunctionData(obj).getConstructTarget();
}
public static CallTarget getConstructNewTarget(DynamicObject obj) {
return getFunctionData(obj).getConstructNewTarget();
}
public static final JSDynamicObject CONSTRUCT = new Nullish();
public static boolean isJSFunction(Object obj) {
return obj instanceof JSFunctionObject;
}
public static DynamicObject createGeneratorFunctionPrototype(JSRealm realm, DynamicObject constructor) {
JSContext ctx = realm.getContext();
DynamicObject prototype = JSObjectUtil.createOrdinaryPrototypeObject(realm, realm.getFunctionPrototype());
JSObjectUtil.putDataProperty(ctx, prototype, JSObject.CONSTRUCTOR, constructor, JSAttributes.configurableNotEnumerableNotWritable());
JSObjectUtil.putDataProperty(ctx, prototype, JSObject.PROTOTYPE, createGeneratorPrototype(realm, prototype), JSAttributes.configurableNotEnumerableNotWritable());
JSObjectUtil.putToStringTag(prototype, GENERATOR_FUNCTION_NAME);
return prototype;
}
private static DynamicObject createGeneratorPrototype(JSRealm realm, DynamicObject constructor) {
JSContext ctx = realm.getContext();
DynamicObject generatorPrototype = JSObjectUtil.createOrdinaryPrototypeObject(realm, realm.getIteratorPrototype());
JSObjectUtil.putFunctionsFromContainer(realm, generatorPrototype, GeneratorPrototypeBuiltins.BUILTINS);
JSObjectUtil.putDataProperty(ctx, generatorPrototype, JSObject.CONSTRUCTOR, constructor, JSAttributes.configurableNotEnumerableNotWritable());
JSObjectUtil.putToStringTag(generatorPrototype, GENERATOR_NAME);
return generatorPrototype;
}
public static JSConstructor createGeneratorFunctionConstructor(JSRealm realm) {
JSContext ctx = realm.getContext();
DynamicObject constructor = realm.lookupFunction(ConstructorBuiltins.BUILTINS, GENERATOR_FUNCTION_NAME);
JSObject.setPrototype(constructor, realm.getFunctionConstructor());
DynamicObject prototype = createGeneratorFunctionPrototype(realm, constructor);
JSObjectUtil.putDataProperty(ctx, constructor, JSObject.PROTOTYPE, prototype, JSAttributes.notConfigurableNotEnumerableNotWritable());
return new JSConstructor(constructor, prototype);
}
public static DynamicObject createAsyncFunctionPrototype(JSRealm realm, DynamicObject constructor) {
JSContext ctx = realm.getContext();
DynamicObject prototype = JSObjectUtil.createOrdinaryPrototypeObject(realm, realm.getFunctionPrototype());
JSObjectUtil.putDataProperty(ctx, prototype, JSObject.CONSTRUCTOR, constructor, JSAttributes.configurableNotEnumerableNotWritable());
JSObjectUtil.putToStringTag(prototype, ASYNC_FUNCTION_NAME);
return prototype;
}
public static JSConstructor createAsyncFunctionConstructor(JSRealm realm) {
JSContext ctx = realm.getContext();
DynamicObject constructor = realm.lookupFunction(ConstructorBuiltins.BUILTINS, ASYNC_FUNCTION_NAME);
JSObject.setPrototype(constructor, realm.getFunctionConstructor());
DynamicObject prototype = createAsyncFunctionPrototype(realm, constructor);
JSObjectUtil.putDataProperty(ctx, constructor, JSObject.PROTOTYPE, prototype, JSAttributes.notConfigurableNotEnumerableNotWritable());
return new JSConstructor(constructor, prototype);
}
public static DynamicObject createAsyncIteratorPrototype(JSRealm realm) {
JSContext context = realm.getContext();
DynamicObject prototype = JSObjectUtil.createOrdinaryPrototypeObject(realm);
JSFunctionData functionData = realm.getContext().getOrCreateBuiltinFunctionData(BuiltinFunctionKey.FunctionAsyncIterator, (c) -> {
return JSFunctionData.createCallOnly(context, Truffle.getRuntime().createCallTarget(new JavaScriptRootNode(context.getLanguage(), null, null) {
@Override
public Object execute(VirtualFrame frame) {
return JSFrameUtil.getThisObj(frame);
}
}), 0, Symbol.SYMBOL_ASYNC_ITERATOR.toFunctionNameString());
});
DynamicObject asyncIterator = JSFunction.create(realm, functionData);
JSObjectUtil.putDataProperty(context, prototype, Symbol.SYMBOL_ASYNC_ITERATOR, asyncIterator, JSAttributes.getDefaultNotEnumerable());
return prototype;
}
public static DynamicObject createAsyncFromSyncIteratorPrototype(JSRealm realm) {
DynamicObject prototype = JSObjectUtil.createOrdinaryPrototypeObject(realm);
JSObjectUtil.putFunctionsFromContainer(realm, prototype, AsyncFromSyncIteratorPrototypeBuiltins.BUILTINS);
return prototype;
}
public static DynamicObject createAsyncGeneratorFunctionPrototype(JSRealm realm, DynamicObject constructor) {
JSContext ctx = realm.getContext();
DynamicObject prototype = JSObjectUtil.createOrdinaryPrototypeObject(realm, realm.getFunctionPrototype());
JSObjectUtil.putDataProperty(ctx, prototype, JSObject.CONSTRUCTOR, constructor, JSAttributes.configurableNotEnumerableNotWritable());
JSObjectUtil.putDataProperty(ctx, prototype, JSObject.PROTOTYPE, createAsyncGeneratorPrototype(realm, prototype), JSAttributes.configurableNotEnumerableNotWritable());
JSObjectUtil.putToStringTag(prototype, ASYNC_GENERATOR_FUNCTION_NAME);
return prototype;
}
private static DynamicObject createAsyncGeneratorPrototype(JSRealm realm, DynamicObject constructor) {
JSContext ctx = realm.getContext();
DynamicObject prototype = JSObjectUtil.createOrdinaryPrototypeObject(realm, realm.getAsyncIteratorPrototype());
JSObjectUtil.putFunctionsFromContainer(realm, prototype, AsyncGeneratorPrototypeBuiltins.BUILTINS);
JSObjectUtil.putDataProperty(ctx, prototype, JSObject.CONSTRUCTOR, constructor, JSAttributes.configurableNotEnumerableNotWritable());
JSObjectUtil.putToStringTag(prototype, ASYNC_GENERATOR_NAME);
return prototype;
}
public static JSConstructor createAsyncGeneratorFunctionConstructor(JSRealm realm) {
JSContext ctx = realm.getContext();
DynamicObject constructor = realm.lookupFunction(ConstructorBuiltins.BUILTINS, ASYNC_GENERATOR_FUNCTION_NAME);
JSObject.setPrototype(constructor, realm.getFunctionConstructor());
DynamicObject prototype = createAsyncGeneratorFunctionPrototype(realm, constructor);
JSObjectUtil.putDataProperty(ctx, constructor, JSObject.PROTOTYPE, prototype, JSAttributes.notConfigurableNotEnumerableNotWritable());
return new JSConstructor(constructor, prototype);
}
public static DynamicObject createEnumerateIteratorPrototype(JSRealm realm) {
DynamicObject iteratorPrototype = realm.getIteratorPrototype();
DynamicObject enumerateIteratorPrototype = JSObjectUtil.createOrdinaryPrototypeObject(realm, iteratorPrototype);
JSObjectUtil.putFunctionsFromContainer(realm, enumerateIteratorPrototype, EnumerateIteratorPrototypeBuiltins.BUILTINS);
return enumerateIteratorPrototype;
}
public static Shape makeInitialEnumerateIteratorShape(JSContext context, DynamicObject enumerateIteratorPrototype) {
return JSObjectUtil.getProtoChildShape(enumerateIteratorPrototype, JSOrdinary.INSTANCE, context);
}
public static DynamicObject createForInIteratorPrototype(JSRealm realm) {
DynamicObject iteratorPrototype = realm.getIteratorPrototype();
DynamicObject enumerateIteratorPrototype = JSObjectUtil.createOrdinaryPrototypeObject(realm, iteratorPrototype);
JSObjectUtil.putFunctionsFromContainer(realm, enumerateIteratorPrototype, ForInIteratorPrototypeBuiltins.BUILTINS);
return enumerateIteratorPrototype;
}
public static Shape makeInitialForInIteratorShape(JSContext context, DynamicObject iteratorPrototype) {
return JSObjectUtil.getProtoChildShape(iteratorPrototype, JSOrdinary.INSTANCE, context);
}
public static RootNode getFrameRootNode(FrameInstance frameInstance) {
Node callNode = frameInstance.getCallNode();
if (callNode != null) {
return callNode.getRootNode();
}
CallTarget callTarget = frameInstance.getCallTarget();
if (callTarget instanceof RootCallTarget) {
return ((RootCallTarget) callTarget).getRootNode();
}
return null;
}
public static SourceSection createBuiltinSourceSection(String name) {
return Source.newBuilder(JavaScriptLanguage.ID, "", name).internal(true).build().createUnavailableSection();
}
public static boolean isBuiltinSourceSection(SourceSection sourceSection) {
return sourceSection == BUILTIN_SOURCE_SECTION;
}
public static boolean isBuiltinThatShouldNotAppearInStackTrace(JSRealm realm, DynamicObject function) {
return function == realm.getApplyFunctionObject() || function == realm.getCallFunctionObject() || function == realm.getReflectApplyFunctionObject() ||
function == realm.getReflectConstructFunctionObject();
}
public static class ArgumentsProxyProperty implements PropertyProxy {
private final JSContext context;
public ArgumentsProxyProperty(JSContext context) {
this.context = context;
}
@Override
public Object get(DynamicObject thiz) {
if (context.isOptionV8CompatibilityMode()) {
return JSRuntime.toJSNull(createArguments(thiz));
} else {
return Undefined.instance;
}
}
@TruffleBoundary
private static Object createArguments(DynamicObject thiz) {
return Truffle.getRuntime().iterateFrames(new FrameInstanceVisitor<Object>() {
@Override
public Object visitFrame(FrameInstance frameInstance) {
RootNode rootNode = getFrameRootNode(frameInstance);
if (JSRuntime.isJSFunctionRootNode(rootNode)) {
Frame frame = frameInstance.getFrame(FrameInstance.FrameAccess.READ_WRITE);
DynamicObject function = (DynamicObject) JSArguments.getFunctionObject(frame.getArguments());
if (function == thiz) {
JSFunctionData functionData = JSFunction.getFunctionData(function);
JSContext context = functionData.getContext();
JSRealm realm = context.getRealm();
Object[] userArguments = JSArguments.extractUserArguments(frame.getArguments());
return JSArgumentsArray.createNonStrictSlow(realm, userArguments, function);
}
}
return null;
}
});
}
}
public static class CallerProxyProperty implements PropertyProxy {
private final JSContext context;
public CallerProxyProperty(JSContext context) {
this.context = context;
}
@Override
public Object get(DynamicObject thiz) {
if (context.isOptionV8CompatibilityMode()) {
return JSRuntime.toJSNull(findCaller(thiz));
} else {
return Undefined.instance;
}
}
@TruffleBoundary
private static Object findCaller(DynamicObject thiz) {
return Truffle.getRuntime().iterateFrames(new FrameInstanceVisitor<Object>() {
private boolean seenThis = false;
@Override
public Object visitFrame(FrameInstance frameInstance) {
RootNode rootNode = getFrameRootNode(frameInstance);
if (JSRuntime.isJSFunctionRootNode(rootNode)) {
Frame frame = frameInstance.getFrame(FrameInstance.FrameAccess.READ_WRITE);
DynamicObject function = (DynamicObject) JSArguments.getFunctionObject(frame.getArguments());
if (seenThis) {
SourceSection ss = rootNode.getSourceSection();
if (ss == null) {
return null;
}
if (ss.getSource().isInternal() && !JSFunction.isBuiltinSourceSection(ss)) {
return null;
}
JSFunctionData functionData = JSFunction.getFunctionData(function);
if (JSFunction.isBuiltinSourceSection(ss)) {
JSRealm realm = functionData.getContext().getRealm();
if (function == realm.getEvalFunctionObject()) {
return null;
}
if (isBuiltinThatShouldNotAppearInStackTrace(realm, function)) {
return null;
}
if (functionData.getName().startsWith("[Symbol.")) {
return null;
}
if (isStrictBuiltin(function)) {
return Null.instance;
}
} else if (functionData.isStrict()) {
return Null.instance;
}
if (!PROGRAM_FUNCTION_NAME.equals(rootNode.getName())) {
return function;
}
} else if (function == thiz) {
seenThis = true;
}
}
return null;
}
});
}
}
public static boolean isStrictBuiltin(DynamicObject function) {
JSFunctionData functionData = JSFunction.getFunctionData(function);
JSRealm realm = functionData.getContext().getRealm();
PropertyDescriptor desc = JSObject.getOwnProperty(realm.getArrayPrototype(), functionData.getName());
return desc != null && desc.isDataDescriptor() && desc.getValue() == function;
}
}