package com.oracle.truffle.js.nodes.function;
import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Cached.Shared;
import com.oracle.truffle.api.dsl.ReportPolymorphism;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.library.CachedLibrary;
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.api.profiles.BranchProfile;
import com.oracle.truffle.js.nodes.JSGuards;
import com.oracle.truffle.js.nodes.JavaScriptBaseNode;
import com.oracle.truffle.js.nodes.access.JSTargetableNode;
import com.oracle.truffle.js.nodes.access.PropertyNode;
import com.oracle.truffle.js.runtime.Errors;
import com.oracle.truffle.js.runtime.JSContext;
import com.oracle.truffle.js.runtime.JSRealm;
import com.oracle.truffle.js.runtime.JSRuntime;
import com.oracle.truffle.js.runtime.builtins.JSFunction;
import com.oracle.truffle.js.runtime.builtins.JSFunctionData;
import com.oracle.truffle.js.runtime.builtins.JSOrdinary;
import com.oracle.truffle.js.runtime.objects.JSObject;
import com.oracle.truffle.js.runtime.objects.JSObjectUtil;
import com.oracle.truffle.js.runtime.objects.Undefined;
public abstract class SpecializedNewObjectNode extends JavaScriptBaseNode {
protected final JSContext context;
protected final boolean isBuiltin;
protected final boolean isConstructor;
protected final boolean isGenerator;
protected final boolean isAsyncGenerator;
@Child private JSTargetableNode getPrototypeNode;
public SpecializedNewObjectNode(JSContext context, boolean isBuiltin, boolean isConstructor, boolean isGenerator, boolean isAsyncGenerator) {
this.context = context;
this.isBuiltin = isBuiltin;
this.isConstructor = isConstructor;
this.isGenerator = isGenerator;
this.isAsyncGenerator = isAsyncGenerator;
this.getPrototypeNode = (!isBuiltin && isConstructor) ? PropertyNode.createProperty(context, null, JSObject.PROTOTYPE) : null;
}
public static SpecializedNewObjectNode create(JSContext context, boolean isBuiltin, boolean isConstructor, boolean isGenerator, boolean isAsyncGenerator) {
return SpecializedNewObjectNodeGen.create(context, isBuiltin, isConstructor, isGenerator, isAsyncGenerator);
}
public static SpecializedNewObjectNode create(JSFunctionData functionData) {
return create(functionData.getContext(), functionData.isBuiltin(), functionData.isConstructor(), functionData.isGenerator(), functionData.isAsyncGenerator());
}
public final DynamicObject execute(VirtualFrame frame, DynamicObject newTarget) {
Object prototype = getPrototypeNode != null ? getPrototypeNode.executeWithTarget(frame, newTarget) : Undefined.instance;
return execute(newTarget, prototype);
}
protected abstract DynamicObject execute(DynamicObject newTarget, Object prototype);
protected Shape getProtoChildShape(Object prototype) {
CompilerAsserts.neverPartOfCompilation();
if (JSGuards.isJSObject(prototype)) {
return JSObjectUtil.getProtoChildShape((DynamicObject) prototype, JSOrdinary.INSTANCE, context);
}
return null;
}
@Specialization(guards = {"!isBuiltin", "isConstructor", "!context.isMultiContext()", "isJSObject(cachedPrototype)", "prototype == cachedPrototype"}, limit = "context.getPropertyCacheLimit()")
public DynamicObject doCachedProto(@SuppressWarnings("unused") DynamicObject target, @SuppressWarnings("unused") Object prototype,
@Cached("prototype") @SuppressWarnings("unused") Object cachedPrototype,
@Cached("getProtoChildShape(prototype)") Shape shape) {
return JSOrdinary.create(context, shape);
}
@ReportPolymorphism.Megamorphic
@Specialization(guards = {"!isBuiltin", "isConstructor", "!context.isMultiContext()", "isJSObject(prototype)"}, replaces = "doCachedProto")
public DynamicObject doUncachedProto(@SuppressWarnings("unused") DynamicObject target, DynamicObject prototype,
@Cached("create()") BranchProfile slowBranch) {
Shape shape = JSObjectUtil.getProtoChildShape(prototype, JSOrdinary.INSTANCE, context, slowBranch);
return JSOrdinary.create(context, shape);
}
@Specialization(guards = {"!isBuiltin", "isConstructor", "context.isMultiContext()", "prototypeClass != null", "prototypeClass.isInstance(prototype)"}, limit = "1")
public DynamicObject createWithProtoCachedClass(@SuppressWarnings("unused") DynamicObject target, Object prototype,
@CachedLibrary(limit = "3") @Shared("setProtoNode") DynamicObjectLibrary setProtoNode,
@Cached("getClassIfJSObject(prototype)") Class<?> prototypeClass) {
return createWithProto(target, (DynamicObject) prototypeClass.cast(prototype), setProtoNode);
}
@Specialization(guards = {"!isBuiltin", "isConstructor", "context.isMultiContext()", "isJSObject(prototype)"})
public DynamicObject createWithProto(@SuppressWarnings("unused") DynamicObject target, DynamicObject prototype,
@CachedLibrary(limit = "3") @Shared("setProtoNode") DynamicObjectLibrary setProtoNode) {
DynamicObject object = JSOrdinary.createWithoutPrototype(context);
setProtoNode.put(object, JSObject.HIDDEN_PROTO, prototype);
return object;
}
@Specialization(guards = {"!isBuiltin", "isConstructor", "!isJSObject(prototype)"})
public DynamicObject createDefaultProto(DynamicObject target, @SuppressWarnings("unused") Object prototype) {
JSRealm realm = JSRuntime.getFunctionRealm(target, context);
if (isAsyncGenerator) {
return JSOrdinary.createWithRealm(context, context.getAsyncGeneratorObjectFactory(), realm);
} else if (isGenerator) {
return JSOrdinary.createWithRealm(context, context.getGeneratorObjectFactory(), realm);
}
return JSOrdinary.create(context, realm);
}
@Specialization(guards = {"isBuiltin", "isConstructor"})
static DynamicObject builtinConstructor(@SuppressWarnings("unused") DynamicObject target, @SuppressWarnings("unused") Object proto) {
return JSFunction.CONSTRUCT;
}
@Specialization(guards = {"!isConstructor"})
public DynamicObject throwNotConstructorFunctionTypeError(DynamicObject target, @SuppressWarnings("unused") Object proto) {
throw Errors.createTypeErrorNotAConstructor(target, context);
}
}