package com.oracle.truffle.js.runtime.builtins;
import com.oracle.truffle.api.frame.MaterializedFrame;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.object.DynamicObjectLibrary;
import com.oracle.truffle.api.object.Shape;
import com.oracle.truffle.js.nodes.function.InitFunctionNode;
import com.oracle.truffle.js.runtime.JSContext;
import com.oracle.truffle.js.runtime.JSRealm;
import com.oracle.truffle.js.runtime.objects.Accessor;
import com.oracle.truffle.js.runtime.objects.JSAttributes;
import com.oracle.truffle.js.runtime.objects.JSProperty;
public abstract class JSFunctionFactory {
protected final JSContext context;
protected final JSObjectFactory objectFactory;
public static JSFunctionFactory create(JSContext context, DynamicObject prototype) {
Shape initialShape = JSFunction.makeFunctionShape(context, prototype, false, false);
JSObjectFactory factory = prototype == null ? JSObjectFactory.createUnbound(context, initialShape) : JSObjectFactory.createBound(context, prototype, initialShape);
return new JSFunctionFactory.Default(context, factory);
}
static JSFunctionFactory createIntrinsic(JSContext context, JSObjectFactory objectFactory,
boolean isStrict, boolean isConstructor, boolean isGenerator, boolean isBound, boolean isAsync) {
return new JSFunctionFactory.Intrinsic(context, objectFactory, isStrict, isConstructor, isGenerator, isBound, isAsync);
}
@SuppressWarnings("unused")
static Shape makeShape(JSContext context, DynamicObject prototype,
boolean isStrict, boolean isAnonymous, boolean isConstructor, boolean isGenerator, boolean isBound, boolean isAsync) {
return JSFunction.makeFunctionShape(context, prototype, isGenerator, isAsync);
}
protected JSFunctionFactory(JSContext context, JSObjectFactory objectFactory) {
this.context = context;
this.objectFactory = objectFactory;
}
public final DynamicObject create(JSFunctionData functionData, MaterializedFrame enclosingFrame, Object classPrototype, JSRealm realm) {
return createWithPrototype(functionData, enclosingFrame, classPrototype, realm, getPrototype(realm));
}
public final DynamicObject createWithPrototype(JSFunctionData functionData, MaterializedFrame enclosingFrame, Object classPrototype, JSRealm realm, DynamicObject prototype) {
Shape shape = getShape(realm, prototype);
assert functionData != null;
assert enclosingFrame != null;
assert shape.getDynamicType() == JSFunction.INSTANCE;
DynamicObject obj = JSFunctionObject.create(shape, functionData, enclosingFrame, realm, classPrototype);
objectFactory.initProto(obj, prototype);
initProperties(obj, functionData);
if (context.getEcmaScriptVersion() < 6 && functionData.hasStrictFunctionProperties()) {
initES5StrictProperties(obj, realm);
}
return obj;
}
protected abstract void initProperties(DynamicObject obj, JSFunctionData functionData);
public final DynamicObject createBound(JSFunctionData functionData, Object classPrototype, JSRealm realm, DynamicObject boundTargetFunction, Object boundThis, Object[] boundArguments) {
Shape shape = objectFactory.getShape(realm);
assert functionData != null;
assert shape.getDynamicType() == JSFunction.INSTANCE;
assert functionData.hasStrictFunctionProperties();
if (context.getEcmaScriptVersion() < 6) {
return createBoundES5(shape, functionData, classPrototype, realm, boundTargetFunction, boundThis, boundArguments);
}
DynamicObject obj = JSFunctionObject.createBound(shape, functionData, realm, classPrototype, boundTargetFunction, boundThis, boundArguments);
objectFactory.initProto(obj, realm);
initProperties(obj, functionData);
return obj;
}
private DynamicObject createBoundES5(Shape shape, JSFunctionData functionData, Object classPrototype, JSRealm realm,
DynamicObject boundTargetFunction, Object boundThis, Object[] boundArguments) {
DynamicObject obj = JSFunctionObject.createBound(shape, functionData, realm, classPrototype, boundTargetFunction, boundThis, boundArguments);
objectFactory.initProto(obj, realm);
initProperties(obj, functionData);
initES5StrictProperties(obj, realm);
return obj;
}
private static void initES5StrictProperties(DynamicObject obj, JSRealm realm) {
int propertyFlags = JSAttributes.notConfigurableNotEnumerable() | JSProperty.ACCESSOR;
Accessor throwerAccessor = realm.getThrowerAccessor();
DynamicObjectLibrary lib = DynamicObjectLibrary.getUncached();
lib.putWithFlags(obj, JSFunction.ARGUMENTS, throwerAccessor, propertyFlags);
lib.putWithFlags(obj, JSFunction.CALLER, throwerAccessor, propertyFlags);
}
protected abstract DynamicObject getPrototype(JSRealm realm);
protected abstract Shape getShape(JSRealm realm, DynamicObject prototype);
protected abstract boolean isInObjectProto();
private static final class Default extends JSFunctionFactory {
protected Default(JSContext context, JSObjectFactory objectFactory) {
super(context, objectFactory);
}
@Override
protected DynamicObject getPrototype(JSRealm realm) {
return realm.getFunctionPrototype();
}
@Override
protected Shape getShape(JSRealm realm, DynamicObject prototype) {
return objectFactory.getShape(realm, prototype);
}
@Override
protected boolean isInObjectProto() {
return objectFactory.isInObjectProto();
}
@Override
protected void initProperties(DynamicObject obj, JSFunctionData functionData) {
}
}
private static final class Intrinsic extends JSFunctionFactory {
private final InitFunctionNode initFunctionNode;
protected Intrinsic(JSContext context, JSObjectFactory objectFactory, boolean isStrict, boolean isConstructor, boolean isGenerator,
boolean isBound, @SuppressWarnings("unused") boolean isAsync) {
super(context, objectFactory);
this.initFunctionNode = context.adoptNode(InitFunctionNode.create(context, isStrict, isConstructor, isBound, isGenerator, false));
}
@Override
protected DynamicObject getPrototype(JSRealm realm) {
return objectFactory.getPrototype(realm);
}
@Override
protected Shape getShape(JSRealm realm, DynamicObject prototype) {
return objectFactory.getShape(realm, prototype);
}
@Override
protected boolean isInObjectProto() {
return objectFactory.isInObjectProto();
}
@Override
protected void initProperties(DynamicObject obj, JSFunctionData functionData) {
initFunctionNode.execute(obj, functionData);
}
}
}