com.oracle.truffle.js.runtime.builtins.JSFunction Maven / Gradle / Ivy
/*
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
*
* Subject to the condition set forth below, permission is hereby granted to any
* person obtaining a copy of this software, associated documentation and/or
* data (collectively the "Software"), free of charge and under any and all
* copyright rights in the Software, and any and all patent rights owned or
* freely licensable by each licensor hereunder covering either (i) the
* unmodified Software as contributed to or provided by such licensor, or (ii)
* the Larger Works (as defined below), to deal in both
*
* (a) the Software, and
*
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
* one is included with the Software each a "Larger Work" to which the Software
* is contributed by such licensors),
*
* without restriction, including without limitation the rights to copy, create
* derivative works of, display, perform, and distribute the Software and make,
* use, sell, offer for sale, import, export, have made, and have sold the
* Software and the Larger Work(s), and to sublicense the foregoing rights on
* either these or other terms.
*
* This license is subject to the following condition:
*
* The above copyright notice and either this complete permission notice or at a
* minimum a reference to the UPL must be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
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.HostCompilerDirectives.InliningCutoff;
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.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.profiles.InlinedConditionProfile;
import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.api.source.SourceSection;
import com.oracle.truffle.api.strings.TruffleString;
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.nodes.binary.InstanceofNode;
import com.oracle.truffle.js.nodes.function.FunctionRootNode;
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.Strings;
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;
public final class JSFunction extends JSNonProxy {
public static final TruffleString TYPE_NAME = Strings.FUNCTION;
public static final TruffleString CLASS_NAME = Strings.constant("Function");
public static final TruffleString PROTOTYPE_NAME = Strings.constant("Function.prototype");
public static final TruffleString GENERATOR_FUNCTION_NAME = Strings.constant("GeneratorFunction");
public static final TruffleString GENERATOR_NAME = Strings.constant("Generator");
public static final TruffleString ASYNC_FUNCTION_NAME = Strings.constant("AsyncFunction");
public static final TruffleString ASYNC_GENERATOR_FUNCTION_NAME = Strings.constant("AsyncGeneratorFunction");
public static final TruffleString ASYNC_GENERATOR_NAME = Strings.constant("AsyncGenerator");
public static final TruffleString ENUMERATE_ITERATOR_PROTOTYPE_NAME = Strings.constant("[[Enumerate]].prototype");
public static final TruffleString CALLER = Strings.CALLER;
public static final TruffleString ARGUMENTS = Strings.ARGUMENTS;
public static final TruffleString LENGTH = Strings.LENGTH;
public static final TruffleString NAME = Strings.NAME;
public static final TruffleString ORDINARY_HAS_INSTANCE = Strings.constant("OrdinaryHasInstance");
public static final String PROGRAM_FUNCTION_NAME = ":program";
public static final String BUILTIN_SOURCE_NAME = "";
public static final TruffleString TS_BUILTIN_SOURCE_NAME = Strings.constant(BUILTIN_SOURCE_NAME);
public static final SourceSection BUILTIN_SOURCE_SECTION = createBuiltinSourceSection(BUILTIN_SOURCE_NAME);
public static final TruffleString ASYNC_FROM_SYNC_ITERATOR_PROTOTYPE_NAME = Strings.constant("%AsyncFromSyncIteratorPrototype%");
public static final PropertyProxy PROTOTYPE_PROXY = new ClassPrototypeProxyProperty();
public static final class FunctionLengthPropertyProxy extends PropertyProxy {
@Override
public Object get(JSDynamicObject store) {
assert JSFunction.isJSFunction(store);
if (store instanceof JSFunctionObject.BoundOrWrapped) {
return ((JSFunctionObject.BoundOrWrapped) store).getBoundLength();
}
return JSFunction.getLength(store);
}
public static int getProfiled(JSDynamicObject store, BranchProfile isBoundBranch) {
assert JSFunction.isJSFunction(store);
if (store instanceof JSFunctionObject.BoundOrWrapped) {
isBoundBranch.enter();
return ((JSFunctionObject.BoundOrWrapped) store).getBoundLength();
}
return JSFunction.getLength(store);
}
}
public static final PropertyProxy LENGTH_PROXY = new FunctionLengthPropertyProxy();
public static final class FunctionNamePropertyProxy extends PropertyProxy {
@Override
public TruffleString get(JSDynamicObject store) {
assert JSFunction.isJSFunction(store);
if (store instanceof JSFunctionObject.BoundOrWrapped) {
return ((JSFunctionObject.BoundOrWrapped) store).getBoundName();
}
return JSFunction.getName(store);
}
public static Object getProfiled(JSDynamicObject store, BranchProfile isBoundBranch) {
assert JSFunction.isJSFunction(store);
if (store instanceof JSFunctionObject.BoundOrWrapped) {
isBoundBranch.enter();
return ((JSFunctionObject.BoundOrWrapped) store).getBoundName();
}
return JSFunction.getName(store);
}
}
public static final PropertyProxy NAME_PROXY = new FunctionNamePropertyProxy();
public static final PropertyProxy ARGUMENTS_PROXY = new ArgumentsProxyProperty();
public static final PropertyProxy CALLER_PROXY = new CallerProxyProperty();
/** Placeholder for lazy initialization of the prototype property. */
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_ELEMENTS_ID = new HiddenKey("Elements");
public static final HiddenKey CLASS_INITIALIZERS_ID = new HiddenKey("Initializers");
public static final HiddenKey PRIVATE_BRAND_ID = new HiddenKey("PrivateBrand");
/** Marker property to ensure generator function shapes are distinct from normal functions. */
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");
/** Slot for scope object passed to inline parsed scripts. */
public static final HiddenKey DEBUG_SCOPE_ID = new HiddenKey("Scope");
public enum GeneratorState {
SuspendedStart,
SuspendedYield,
Executing,
Completed,
}
public enum AsyncGeneratorState {
SuspendedStart,
SuspendedYield,
Executing,
AwaitingReturn,
Completed,
}
private JSFunction() {
}
public static CallTarget getCallTarget(JSDynamicObject obj) {
return getFunctionData(obj).getCallTarget();
}
public static MaterializedFrame getEnclosingFrame(JSDynamicObject obj) {
assert isJSFunction(obj);
return ((JSFunctionObject) obj).getEnclosingFrame();
}
public static JSFunctionData getFunctionData(JSDynamicObject obj) {
assert isJSFunction(obj);
return ((JSFunctionObject) obj).getFunctionData();
}
public static JSFunctionData getFunctionData(JSFunctionObject obj) {
return obj.getFunctionData();
}
private static Object getClassPrototypeField(JSDynamicObject obj) {
assert isJSFunction(obj);
return ((JSFunctionObject) obj).getClassPrototype();
}
private static void setClassPrototypeField(JSDynamicObject obj, Object classPrototype) {
assert isJSFunction(obj);
((JSFunctionObject) obj).setClassPrototype(classPrototype);
}
public static JSRealm getRealm(JSDynamicObject obj) {
assert isJSFunction(obj);
return ((JSFunctionObject) obj).getRealm();
}
public static JSRealm getRealm(JSFunctionObject obj) {
return obj.getRealm();
}
/**
* Version optimized for a single Realm.
*/
public static JSRealm getRealm(JSFunctionObject functionObj, JSContext context, Node node) {
assert isJSFunction(functionObj);
JSRealm realm;
if (context.isSingleRealm()) {
realm = JSRealm.get(node);
assert realm == getRealm(functionObj);
} else {
realm = getRealm(functionObj);
}
return realm;
}
public static JSFunctionObject create(JSRealm realm, JSFunctionData functionData) {
return create(realm, functionData, JSFrameUtil.NULL_MATERIALIZED_FRAME);
}
public static JSFunctionObject create(JSRealm realm, JSFunctionData functionData, MaterializedFrame enclosingFrame) {
return createDefault(functionData, enclosingFrame, CLASS_PROTOTYPE_PLACEHOLDER, realm);
}
public static JSFunctionObject createWithPrototype(JSFunctionFactory factory, JSRealm realm, JSFunctionData functionData, MaterializedFrame enclosingFrame, JSDynamicObject prototype) {
return createWithPrototype(factory, functionData, enclosingFrame, CLASS_PROTOTYPE_PLACEHOLDER, realm, prototype);
}
public static JSFunctionObject createLexicalThis(JSRealm realm, JSFunctionData functionData, MaterializedFrame enclosingFrame, Object lexicalThis) {
return createDefault(functionData, enclosingFrame, lexicalThis, realm);
}
@InliningCutoff
private static JSFunctionObject createDefault(JSFunctionData functionData, MaterializedFrame enclosingFrame, Object classPrototype, JSRealm realm) {
JSFunctionFactory factory = initialFactory(functionData);
return factory.create(functionData, enclosingFrame, classPrototype, realm);
}
private static JSFunctionObject createWithPrototype(JSFunctionFactory factory, JSFunctionData functionData, MaterializedFrame enclosingFrame, Object classPrototype, JSRealm realm,
JSDynamicObject prototype) {
return factory.createWithPrototype(functionData, enclosingFrame, classPrototype, realm, prototype);
}
public static JSFunctionObject createBound(JSContext context, JSRealm realm, JSFunctionData functionData, Object boundTargetFunction, Object boundThis, Object[] boundArguments) {
JSFunctionFactory factory = context.getBoundFunctionFactory(functionData);
return factory.createBound(functionData, CLASS_PROTOTYPE_PLACEHOLDER, realm, boundTargetFunction, boundThis, boundArguments);
}
public static JSFunctionObject createWrapped(JSContext context, JSRealm realm, JSFunctionData functionData, Object wrappedTargetFunction) {
JSFunctionFactory factory = context.getWrappedFunctionFactory();
return factory.createWrapped(functionData, realm, wrappedTargetFunction);
}
private static JSFunctionFactory initialFactory(JSFunctionData functionData) {
return functionData.getContext().getFunctionFactory(functionData);
}
public static TruffleString getName(JSDynamicObject obj) {
return getFunctionData(obj).getName();
}
public static Object call(JSFunctionObject 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((JSFunctionObject) JSArguments.getFunctionObject(jsArguments)).call(jsArguments);
}
public static Object construct(JSFunctionObject functionObject, Object[] argumentValues) {
assert isJSFunction(functionObject) && isConstructor(functionObject);
Object[] arguments = JSArguments.create(CONSTRUCT, functionObject, argumentValues);
return getConstructTarget(functionObject).call(arguments);
}
@TruffleBoundary
public static JSFunctionObject bind(JSRealm realm, JSFunctionObject thisFnObj, Object thisArg, Object[] boundArguments) {
assert JSFunction.isJSFunction(thisFnObj);
JSContext context = realm.getContext();
JSDynamicObject proto = JSObject.getPrototype(thisFnObj);
JSFunctionObject boundFunction = boundFunctionCreate(context, thisFnObj, thisArg, boundArguments, proto,
InlinedConditionProfile.getUncached(), InlinedConditionProfile.getUncached(), InlinedConditionProfile.getUncached(), 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((Number) targetLen);
length = Math.max(0, targetLenInt - boundArguments.length);
if (targetLenInt == getLength(thisFnObj)) {
mustSetLength = false;
}
}
}
if (mustSetLength) {
setFunctionLength(boundFunction, JSRuntime.longToIntOrDouble(length));
}
TruffleString targetName = getFunctionName(thisFnObj);
if (!targetName.equals(getName(thisFnObj))) {
setBoundFunctionName(boundFunction, targetName);
}
return boundFunction;
}
public static JSFunctionObject boundFunctionCreate(JSContext context, JSFunctionObject boundTargetFunction, Object boundThis, Object[] boundArguments, JSDynamicObject proto,
InlinedConditionProfile isConstructorProfile, InlinedConditionProfile isAsyncProfile, InlinedConditionProfile setProtoProfile, Node node) {
CompilerAsserts.partialEvaluationConstant(context);
JSFunctionData targetFunctionData = JSFunction.getFunctionData(boundTargetFunction);
boolean constructor = isConstructorProfile.profile(node, targetFunctionData.isConstructor());
boolean isAsync = isAsyncProfile.profile(node, targetFunctionData.isAsync());
JSFunctionData boundFunctionData = context.getBoundFunctionData(constructor, isAsync);
JSRealm realm = getRealm(boundTargetFunction, context, node);
JSFunctionObject boundFunction = JSFunction.createBound(context, realm, boundFunctionData, boundTargetFunction, boundThis, boundArguments);
boolean needSetProto = proto != realm.getFunctionPrototype();
if (setProtoProfile.profile(node, needSetProto)) {
JSObject.setPrototype(boundFunction, proto);
}
assert JSObject.getPrototype(boundFunction) == proto;
return boundFunction;
}
@TruffleBoundary
private static TruffleString getFunctionName(JSDynamicObject thisFnObj) {
Object name = JSObject.get(thisFnObj, NAME);
if (!(name instanceof TruffleString nameStr)) {
return Strings.EMPTY_STRING;
}
return nameStr;
}
@TruffleBoundary
public static void setFunctionLength(JSDynamicObject functionObj, Number length) {
JSObject.defineOwnProperty(functionObj, JSFunction.LENGTH, PropertyDescriptor.createData(length, false, false, true));
}
@TruffleBoundary
public static void setBoundFunctionName(JSDynamicObject boundFunction, TruffleString targetName) {
JSObject.defineOwnProperty(boundFunction, JSFunction.NAME, PropertyDescriptor.createData(Strings.concat(Strings.BOUND_SPC, targetName), false, false, true));
}
public static boolean isStrict(JSDynamicObject obj) {
return getFunctionData(obj).isStrict();
}
public static boolean isBuiltin(JSDynamicObject obj) {
return getFunctionData(obj).isBuiltin();
}
public static boolean isConstructor(JSDynamicObject obj) {
assert JSFunction.isJSFunction(obj);
return getFunctionData(obj).isConstructor();
}
public static boolean isConstructor(Object obj) {
return JSFunction.isJSFunction(obj) && getFunctionData((JSDynamicObject) obj).isConstructor();
}
public static boolean isGenerator(JSDynamicObject obj) {
return getFunctionData(obj).isGenerator();
}
public static boolean needsParentFrame(JSDynamicObject obj) {
return getFunctionData(obj).needsParentFrame();
}
public static int getLength(JSDynamicObject obj) {
return getFunctionData(obj).getLength();
}
public static boolean isClassPrototypeInitialized(JSDynamicObject thisObj) {
return getClassPrototypeField(thisObj) != CLASS_PROTOTYPE_PLACEHOLDER;
}
public static boolean isBoundFunction(Object function) {
return function instanceof JSFunctionObject.Bound;
}
public static boolean isAsyncFunction(JSDynamicObject function) {
return isJSFunction(function) && getFunctionData(function).isAsync();
}
public static Object getLexicalThis(JSDynamicObject thisObj) {
return getClassPrototypeInitialized(thisObj);
}
public static Object getClassPrototypeInitialized(JSDynamicObject thisObj) {
Object classPrototype = getClassPrototypeField(thisObj);
assert classPrototype != CLASS_PROTOTYPE_PLACEHOLDER;
return classPrototype;
}
public static Object getClassPrototype(JSDynamicObject thisObj) {
Object classPrototype = getClassPrototypeField(thisObj);
if (CompilerDirectives.injectBranchProbability(CompilerDirectives.SLOWPATH_PROBABILITY, classPrototype == CLASS_PROTOTYPE_PLACEHOLDER)) {
initializeClassPrototype(thisObj);
classPrototype = getClassPrototypeField(thisObj);
}
return classPrototype;
}
private static void initializeClassPrototype(JSDynamicObject thisObj) {
assert !isClassPrototypeInitialized(thisObj);
setClassPrototypeField(thisObj, createPrototype(thisObj));
}
@TruffleBoundary
private static JSDynamicObject createPrototype(JSDynamicObject constructor) {
JSFunctionData functionData = getFunctionData(constructor);
JSRealm realm = getRealm(constructor);
JSContext context = functionData.getContext();
if (!functionData.isGenerator()) {
JSDynamicObject prototype = JSOrdinary.create(context, realm);
JSObjectUtil.putConstructorProperty(prototype, constructor);
return prototype;
} else {
assert functionData.isGenerator();
if (functionData.isAsync()) {
return JSOrdinary.createWithRealm(context, context.getAsyncGeneratorObjectPrototypeFactory(), realm);
} else {
return JSOrdinary.createWithRealm(context, context.getGeneratorObjectPrototypeFactory(), realm);
}
}
}
public static void setClassPrototype(JSDynamicObject thisObj, Object value) {
assert value != null;
setClassPrototypeField(thisObj, value);
}
public static final class ClassPrototypeProxyProperty extends PropertyProxy {
private ClassPrototypeProxyProperty() {
}
@Override
public boolean set(JSDynamicObject store, Object value) {
assert JSFunction.isJSFunction(store);
JSFunction.setClassPrototype(store, value);
return true;
}
@Override
public Object get(JSDynamicObject 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();
protected final ConditionProfile jsFunctionProfile = ConditionProfile.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();
JSFunctionObject.Bound boundFunction = (JSFunctionObject.Bound) JSArguments.getFunctionObject(originalArguments);
Object boundTargetFunction = boundFunction.getBoundTargetFunction();
Object[] boundArguments = boundFunction.getBoundArguments();
Object boundThis = boundFunction.getBoundThis();
Object[] argumentValues = JSArguments.extractUserArguments(originalArguments);
Object[] arguments = prependBoundArguments(boundArguments, argumentValues);
if (jsFunctionProfile.profile(JSFunction.isJSFunction(boundTargetFunction))) {
Object[] newArguments = JSArguments.create(boundThis, boundTargetFunction, arguments);
return callNode.call(JSFunction.getFunctionData((JSFunctionObject) boundTargetFunction).getCallTarget(initProfile), newArguments);
} else {
return JSRuntime.call(boundTargetFunction, boundThis, arguments);
}
}
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;
}
}
static final class BoundConstructRootNode extends BoundRootNode {
BoundConstructRootNode(JSContext context) {
super(context);
}
@Override
public Object execute(VirtualFrame frame) {
Object[] originalArguments = frame.getArguments();
JSFunctionObject.Bound boundFunction = (JSFunctionObject.Bound) JSArguments.getFunctionObject(originalArguments);
Object boundTargetFunction = boundFunction.getBoundTargetFunction();
Object[] boundArguments = boundFunction.getBoundArguments();
Object[] argumentValues = JSArguments.extractUserArguments(originalArguments);
Object[] arguments = prependBoundArguments(boundArguments, argumentValues);
Object originalThis = JSArguments.getThisObject(originalArguments);
if (jsFunctionProfile.profile(JSFunction.isJSFunction(boundTargetFunction))) {
Object[] newArguments = JSArguments.create(originalThis, boundTargetFunction, arguments);
return callNode.call(JSFunction.getFunctionData((JSFunctionObject) boundTargetFunction).getConstructTarget(initProfile), newArguments);
} else {
return JSRuntime.construct(boundTargetFunction, arguments);
}
}
}
static final class BoundConstructNewTargetRootNode extends BoundRootNode {
BoundConstructNewTargetRootNode(JSContext context) {
super(context);
}
@Override
public Object execute(VirtualFrame frame) {
Object[] originalArguments = frame.getArguments();
JSFunctionObject.Bound boundFunction = (JSFunctionObject.Bound) JSArguments.getFunctionObject(originalArguments);
Object boundTargetFunction = boundFunction.getBoundTargetFunction();
Object[] boundArguments = boundFunction.getBoundArguments();
Object[] argumentValues = JSArguments.extractUserArguments(originalArguments, 1);
Object[] arguments = prependBoundArguments(boundArguments, argumentValues);
Object originalThis = JSArguments.getThisObject(originalArguments);
if (jsFunctionProfile.profile(JSFunction.isJSFunction(boundTargetFunction))) {
Object newTarget = JSArguments.getNewTarget(originalArguments);
if (newTarget == boundFunction) {
newTarget = boundTargetFunction;
}
Object[] newArguments = JSArguments.createWithNewTarget(originalThis, boundTargetFunction, newTarget, arguments);
return callNode.call(JSFunction.getFunctionData((JSFunctionObject) boundTargetFunction).getConstructNewTarget(initProfile), newArguments);
} else {
return JSRuntime.construct(boundTargetFunction, arguments);
}
}
}
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 JSFunctionObject createFunctionPrototype(JSRealm realm, JSDynamicObject objectPrototype) {
JSContext context = realm.getContext();
Shape protoShape = JSShape.createPrototypeShape(context, INSTANCE, objectPrototype);
JSFunctionObject proto = JSFunctionObject.create(protoShape, objectPrototype, createEmptyFunctionData(context), JSFrameUtil.NULL_MATERIALIZED_FRAME, realm, CLASS_PROTOTYPE_PLACEHOLDER);
JSObjectUtil.setOrVerifyPrototype(context, proto, objectPrototype);
JSObjectUtil.putDataProperty(proto, LENGTH, 0, JSAttributes.configurableNotEnumerableNotWritable());
JSObjectUtil.putDataProperty(proto, NAME, Strings.EMPTY_STRING, JSAttributes.configurableNotEnumerableNotWritable());
return proto;
}
public static void addRestrictedFunctionProperties(JSRealm realm, JSDynamicObject obj) {
JSObjectUtil.putBuiltinAccessorProperty(obj, CALLER, realm.getThrowerAccessor());
JSObjectUtil.putBuiltinAccessorProperty(obj, ARGUMENTS, realm.getThrowerAccessor());
}
public static JSFunctionData createNamedEmptyFunctionData(JSContext context, TruffleString name) {
return context.getNamedEmptyFunctionData(name);
}
public static JSFunctionData createEmptyFunctionData(JSContext context) {
return createNamedEmptyFunctionData(context, Strings.EMPTY_STRING);
}
public static JSFunctionObject createNamedEmptyFunction(JSRealm realm, TruffleString name) {
return JSFunction.create(realm, createNamedEmptyFunctionData(realm.getContext(), name));
}
public static JSFunctionObject createEmptyFunction(JSRealm realm) {
return JSFunction.create(realm, createEmptyFunctionData(realm.getContext()));
}
public static void fillFunctionPrototype(JSRealm realm) {
JSContext ctx = realm.getContext();
JSObjectUtil.putConstructorProperty(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, JSDynamicObject 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 JSFunctionObject createFunctionConstructor(JSRealm realm) {
JSFunctionObject functionConstructor = realm.lookupFunction(ConstructorBuiltins.BUILTINS, CLASS_NAME);
JSObjectUtil.putDataProperty(functionConstructor, JSObject.PROTOTYPE, realm.getFunctionPrototype(), JSAttributes.notConfigurableNotEnumerableNotWritable());
return functionConstructor;
}
@Override
public boolean hasOnlyShapeProperties(JSDynamicObject obj) {
return true;
}
public static CallTarget getConstructTarget(JSDynamicObject obj) {
return getFunctionData(obj).getConstructTarget();
}
public static CallTarget getConstructNewTarget(JSDynamicObject obj) {
return getFunctionData(obj).getConstructNewTarget();
}
/**
* Construct token. Passed from the {@code new} node as {@code this} argument to built-in
* functions to differentiate between a constructor and a normal call (i.e., [[Construct]] and
* [[Call]] internal methods, see ES5 13.2.1 and 13.2.2). Must not be passed anywhere else.
*/
public static final JSDynamicObject CONSTRUCT = new Nullish();
public static boolean isJSFunction(Object obj) {
return obj instanceof JSFunctionObject;
}
// ##### Generator functions
public static JSObject createGeneratorFunctionPrototype(JSRealm realm, JSDynamicObject constructor) {
// intrinsic object %Generator%
JSObject prototype = JSObjectUtil.createOrdinaryPrototypeObject(realm, realm.getFunctionPrototype());
JSObjectUtil.putDataProperty(prototype, JSObject.CONSTRUCTOR, constructor, JSAttributes.configurableNotEnumerableNotWritable());
JSObjectUtil.putDataProperty(prototype, JSObject.PROTOTYPE, createGeneratorPrototype(realm, prototype), JSAttributes.configurableNotEnumerableNotWritable());
JSObjectUtil.putToStringTag(prototype, GENERATOR_FUNCTION_NAME);
return prototype;
}
private static JSObject createGeneratorPrototype(JSRealm realm, JSDynamicObject constructor) {
// intrinsic object %GeneratorPrototype%
JSObject generatorPrototype = JSObjectUtil.createOrdinaryPrototypeObject(realm, realm.getIteratorPrototype());
JSObjectUtil.putFunctionsFromContainer(realm, generatorPrototype, GeneratorPrototypeBuiltins.BUILTINS);
JSObjectUtil.putDataProperty(generatorPrototype, JSObject.CONSTRUCTOR, constructor, JSAttributes.configurableNotEnumerableNotWritable());
JSObjectUtil.putToStringTag(generatorPrototype, GENERATOR_NAME);
return generatorPrototype;
}
public static JSConstructor createGeneratorFunctionConstructor(JSRealm realm) {
// intrinsic object %GeneratorFunction%
JSFunctionObject constructor = realm.lookupFunction(ConstructorBuiltins.BUILTINS, GENERATOR_FUNCTION_NAME);
JSObject.setPrototype(constructor, realm.getFunctionConstructor());
JSObject prototype = createGeneratorFunctionPrototype(realm, constructor);
JSObjectUtil.putDataProperty(constructor, JSObject.PROTOTYPE, prototype, JSAttributes.notConfigurableNotEnumerableNotWritable());
return new JSConstructor(constructor, prototype);
}
// ##### Async functions
public static JSObject createAsyncFunctionPrototype(JSRealm realm, JSDynamicObject constructor) {
// intrinsic object %AsyncFunctionPrototype%
JSObject prototype = JSObjectUtil.createOrdinaryPrototypeObject(realm, realm.getFunctionPrototype());
JSObjectUtil.putDataProperty(prototype, JSObject.CONSTRUCTOR, constructor, JSAttributes.configurableNotEnumerableNotWritable());
JSObjectUtil.putToStringTag(prototype, ASYNC_FUNCTION_NAME);
return prototype;
}
public static JSConstructor createAsyncFunctionConstructor(JSRealm realm) {
// intrinsic constructor %AsyncFunction%
JSFunctionObject constructor = realm.lookupFunction(ConstructorBuiltins.BUILTINS, ASYNC_FUNCTION_NAME);
JSObject.setPrototype(constructor, realm.getFunctionConstructor());
JSObject prototype = createAsyncFunctionPrototype(realm, constructor);
JSObjectUtil.putDataProperty(constructor, JSObject.PROTOTYPE, prototype, JSAttributes.notConfigurableNotEnumerableNotWritable());
return new JSConstructor(constructor, prototype);
}
/**
* Creates the %AsyncIteratorPrototype% object (ES2018 11.1.2).
*/
public static JSObject createAsyncIteratorPrototype(JSRealm realm) {
JSContext context = realm.getContext();
JSObject prototype = JSObjectUtil.createOrdinaryPrototypeObject(realm);
JSFunctionData functionData = realm.getContext().getOrCreateBuiltinFunctionData(BuiltinFunctionKey.FunctionAsyncIterator, (c) -> {
return JSFunctionData.createCallOnly(context, new JavaScriptRootNode(context.getLanguage(), null, null) {
@Override
public Object execute(VirtualFrame frame) {
return JSFrameUtil.getThisObj(frame);
}
}.getCallTarget(), 0, Symbol.SYMBOL_ASYNC_ITERATOR.toFunctionNameString());
});
JSFunctionObject asyncIterator = JSFunction.create(realm, functionData);
JSObjectUtil.putDataProperty(prototype, Symbol.SYMBOL_ASYNC_ITERATOR, asyncIterator, JSAttributes.getDefaultNotEnumerable());
return prototype;
}
/**
* Creates the %AsyncFromSyncIteratorPrototype% object (ES2018 11.1.3.2).
*/
public static JSObject createAsyncFromSyncIteratorPrototype(JSRealm realm) {
JSObject prototype = JSObjectUtil.createOrdinaryPrototypeObject(realm);
JSObjectUtil.putFunctionsFromContainer(realm, prototype, AsyncFromSyncIteratorPrototypeBuiltins.BUILTINS);
return prototype;
}
public static JSObject createAsyncGeneratorFunctionPrototype(JSRealm realm, JSDynamicObject constructor) {
// intrinsic object %AsyncGenerator%
JSObject prototype = JSObjectUtil.createOrdinaryPrototypeObject(realm, realm.getFunctionPrototype());
JSObjectUtil.putDataProperty(prototype, JSObject.CONSTRUCTOR, constructor, JSAttributes.configurableNotEnumerableNotWritable());
JSObjectUtil.putDataProperty(prototype, JSObject.PROTOTYPE, createAsyncGeneratorPrototype(realm, prototype), JSAttributes.configurableNotEnumerableNotWritable());
JSObjectUtil.putToStringTag(prototype, ASYNC_GENERATOR_FUNCTION_NAME);
return prototype;
}
private static JSObject createAsyncGeneratorPrototype(JSRealm realm, JSDynamicObject constructor) {
// intrinsic object %AsyncGeneratorPrototype%
JSObject prototype = JSObjectUtil.createOrdinaryPrototypeObject(realm, realm.getAsyncIteratorPrototype());
JSObjectUtil.putFunctionsFromContainer(realm, prototype, AsyncGeneratorPrototypeBuiltins.BUILTINS);
JSObjectUtil.putDataProperty(prototype, JSObject.CONSTRUCTOR, constructor, JSAttributes.configurableNotEnumerableNotWritable());
JSObjectUtil.putToStringTag(prototype, ASYNC_GENERATOR_NAME);
return prototype;
}
public static JSConstructor createAsyncGeneratorFunctionConstructor(JSRealm realm) {
// intrinsic constructor %AsyncGeneratorFunction%
JSFunctionObject constructor = realm.lookupFunction(ConstructorBuiltins.BUILTINS, ASYNC_GENERATOR_FUNCTION_NAME);
JSObject.setPrototype(constructor, realm.getFunctionConstructor());
JSObject prototype = createAsyncGeneratorFunctionPrototype(realm, constructor);
JSObjectUtil.putDataProperty(constructor, JSObject.PROTOTYPE, prototype, JSAttributes.notConfigurableNotEnumerableNotWritable());
return new JSConstructor(constructor, prototype);
}
// ##### Bound functions and enumerate iterator
public static JSDynamicObject createEnumerateIteratorPrototype(JSRealm realm) {
JSDynamicObject iteratorPrototype = realm.getIteratorPrototype();
JSDynamicObject enumerateIteratorPrototype = JSObjectUtil.createOrdinaryPrototypeObject(realm, iteratorPrototype);
JSObjectUtil.putFunctionsFromContainer(realm, enumerateIteratorPrototype, EnumerateIteratorPrototypeBuiltins.BUILTINS);
return enumerateIteratorPrototype;
}
public static Shape makeInitialEnumerateIteratorShape(JSContext context, JSDynamicObject enumerateIteratorPrototype) {
return JSObjectUtil.getProtoChildShape(enumerateIteratorPrototype, JSOrdinary.INSTANCE, context);
}
public static JSDynamicObject createForInIteratorPrototype(JSRealm realm) {
JSDynamicObject iteratorPrototype = realm.getIteratorPrototype();
JSDynamicObject enumerateIteratorPrototype = JSObjectUtil.createOrdinaryPrototypeObject(realm, iteratorPrototype);
JSObjectUtil.putFunctionsFromContainer(realm, enumerateIteratorPrototype, ForInIteratorPrototypeBuiltins.BUILTINS);
return enumerateIteratorPrototype;
}
public static Shape makeInitialForInIteratorShape(JSContext context, JSDynamicObject iteratorPrototype) {
return JSObjectUtil.getProtoChildShape(iteratorPrototype, JSOrdinary.INSTANCE, context);
}
public static JSDynamicObject createOrdinaryHasInstanceFunction(JSRealm realm) {
JSContext ctx = realm.getContext();
return JSFunction.create(realm, ctx.getOrCreateBuiltinFunctionData(BuiltinFunctionKey.OrdinaryHasInstance, c -> {
return JSFunctionData.createCallOnly(c, new InstanceofNode.OrdinaryHasInstanceRootNode(c).getCallTarget(), 1, ORDINARY_HAS_INSTANCE);
}));
}
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, JSDynamicObject function) {
return function == realm.getApplyFunctionObject() || function == realm.getCallFunctionObject() || function == realm.getReflectApplyFunctionObject() ||
function == realm.getReflectConstructFunctionObject();
}
/**
* V8 compatibility mode: retrieves the function's arguments from the stack.
*/
public static final class ArgumentsProxyProperty extends PropertyProxy {
private ArgumentsProxyProperty() {
}
@Override
public Object get(JSDynamicObject thiz) {
JSFunctionObject thisFunction = (JSFunctionObject) thiz;
assert !getFunctionData(thisFunction).hasStrictFunctionProperties() && getFunctionData(thisFunction).getContext().isOptionV8CompatibilityMode();
return JSRuntime.toJSNull(createArguments(thisFunction));
}
@TruffleBoundary
private static Object createArguments(JSFunctionObject thisFunction) {
JSFunctionData thisFunctionData = getFunctionData(thisFunction);
return Truffle.getRuntime().iterateFrames(new FrameInstanceVisitor<>() {
@Override
public Object visitFrame(FrameInstance frameInstance) {
CompilerAsserts.neverPartOfCompilation();
RootNode rootNode = getFrameRootNode(frameInstance);
if (rootNode instanceof FunctionRootNode) {
if (((FunctionRootNode) rootNode).getFunctionData() == thisFunctionData) {
Frame frame = frameInstance.getFrame(FrameInstance.FrameAccess.READ_WRITE);
Object function = JSArguments.getFunctionObject(frame.getArguments());
if (function == thisFunction) {
JSRealm realm = JSRealm.get(null);
Object[] userArguments = JSArguments.extractUserArguments(frame.getArguments());
return JSArgumentsArray.createNonStrictSlow(realm, userArguments, (JSFunctionObject) function);
}
}
}
return null;
}
});
}
}
/**
* V8 compatibility mode: retrieves the function's caller from the stack.
*/
public static final class CallerProxyProperty extends PropertyProxy {
private CallerProxyProperty() {
}
@Override
public Object get(JSDynamicObject thiz) {
JSFunctionObject thisFunction = (JSFunctionObject) thiz;
assert !getFunctionData(thisFunction).hasStrictFunctionProperties() && getFunctionData(thisFunction).getContext().isOptionV8CompatibilityMode();
return JSRuntime.toJSNull(findCaller(thisFunction));
}
@TruffleBoundary
private static Object findCaller(JSFunctionObject thisFunction) {
JSFunctionData thisFunctionData = getFunctionData(thisFunction);
return Truffle.getRuntime().iterateFrames(new FrameInstanceVisitor<>() {
private boolean seenThisFunction = false;
@Override
public Object visitFrame(FrameInstance frameInstance) {
CompilerAsserts.neverPartOfCompilation();
RootNode rootNode = getFrameRootNode(frameInstance);
if (rootNode instanceof FunctionRootNode) {
if (seenThisFunction) {
Frame frame = frameInstance.getFrame(FrameInstance.FrameAccess.READ_WRITE);
Object function = JSArguments.getFunctionObject(frame.getArguments());
if (!isJSFunction(function)) {
return null;
}
JSFunctionObject callerFunction = (JSFunctionObject) function;
SourceSection ss = rootNode.getSourceSection();
if (ss == null) {
return null;
}
if (ss.getSource().isInternal() && !JSFunction.isBuiltinSourceSection(ss)) {
return null;
}
JSFunctionData functionData = JSFunction.getFunctionData(callerFunction);
if (JSFunction.isBuiltinSourceSection(ss)) {
JSRealm realm = JSRealm.get(null);
if (callerFunction == realm.getEvalFunctionObject()) {
return null; // skip eval()
}
if (isBuiltinThatShouldNotAppearInStackTrace(realm, callerFunction)) {
return null;
}
if (Strings.startsWith(functionData.getName(), Strings.BRACKET_SYMBOL_DOT)) {
return null;
}
if (isStrictBuiltin(callerFunction, realm)) {
return Null.instance; // do not go beyond a strict builtin
}
} else if (functionData.isStrict()) {
return Null.instance;
}
if (!PROGRAM_FUNCTION_NAME.equals(rootNode.getName())) {
return callerFunction;
}
} else {
if (((FunctionRootNode) rootNode).getFunctionData() == thisFunctionData) {
Frame frame = frameInstance.getFrame(FrameInstance.FrameAccess.READ_WRITE);
Object function = JSArguments.getFunctionObject(frame.getArguments());
if (function == thisFunction) {
seenThisFunction = true;
}
}
}
}
return null;
}
});
}
}
// V8 distinguishes strict and sloppy builtins, see mjsunit/function-caller.js
public static boolean isStrictBuiltin(JSDynamicObject function, JSRealm realm) {
JSFunctionData functionData = JSFunction.getFunctionData(function);
PropertyDescriptor desc = JSObject.getOwnProperty(realm.getArrayPrototype(), functionData.getName());
return desc != null && desc.isDataDescriptor() && desc.getValue() == function;
}
public static Source getCallerSource() {
RootNode callerRootNode = Truffle.getRuntime().iterateFrames(JSFunction::getFrameRootNode, 1);
if (callerRootNode != null) {
SourceSection callerSourceSection = callerRootNode.getSourceSection();
if (callerSourceSection != null && callerSourceSection.isAvailable()) {
return callerSourceSection.getSource();
}
}
return null;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy