import { assertFalse, assertNever } from "../../../util/util"; import { ArrayDefaultInitializerOutlet, AtomicDefaultInitializerOutlet, ClassDefaultInitializerOutlet } from "../../../view/constructs/InitializerOutlet"; import { ConstructOutlet } from "../../../view/constructs/ConstructOutlet"; import { TranslationUnitContext } from "../../compilation/contexts"; import { ArraySubobjectEntity, FunctionEntity, ObjectEntity, UnboundReferenceEntity } from "../../compilation/entities"; import { CPPError } from "../../compilation/errors"; import { Simulation } from "../../runtime/Simulation"; import { CompiledTemporaryDeallocator } from "../TemporaryDeallocator"; import { AtomicType, BoundedArrayType, CompleteClassType, CompleteObjectType, FunctionType, VoidType } from "../../compilation/types"; import { RuntimeConstruct, SuccessfullyCompiled } from "../CPPConstruct"; import { overloadResolution } from "../../compilation/overloads"; import { CompiledFunctionCall, FunctionCall, RuntimeFunctionCall } from "../FunctionCall"; import { Initializer, RuntimeInitializer } from "./Initializer"; export abstract class DefaultInitializer extends Initializer { public static create(context: TranslationUnitContext, target: UnboundReferenceEntity): ReferenceDefaultInitializer; public static create(context: TranslationUnitContext, target: ObjectEntity): AtomicDefaultInitializer; public static create(context: TranslationUnitContext, target: ObjectEntity): ArrayDefaultInitializer; public static create(context: TranslationUnitContext, target: ObjectEntity): ClassDefaultInitializer; public static create(context: TranslationUnitContext, target: ObjectEntity): AtomicDefaultInitializer | ArrayDefaultInitializer | ClassDefaultInitializer; public static create(context: TranslationUnitContext, target: ObjectEntity | UnboundReferenceEntity): ReferenceDefaultInitializer | AtomicDefaultInitializer | ArrayDefaultInitializer | ClassDefaultInitializer; public static create(context: TranslationUnitContext, target: ObjectEntity | UnboundReferenceEntity): DefaultInitializer { if (target.type.isReferenceType()) { return new ReferenceDefaultInitializer(context, target); } else if (target.type.isAtomicType()) { return new AtomicDefaultInitializer(context, >target); } else if (target.type.isBoundedArrayType()) { return new ArrayDefaultInitializer(context, >target); } else if (target.type.isCompleteClassType()) { return new ClassDefaultInitializer(context, >target); } else { return assertNever(target.type); } } public readonly kind = "default"; public abstract createRuntimeInitializer(this: CompiledDefaultInitializer, parent: RuntimeConstruct): RuntimeDefaultInitializer; } export interface CompiledDefaultInitializer extends DefaultInitializer, SuccessfullyCompiled { readonly temporaryDeallocator?: CompiledTemporaryDeallocator; // to match CompiledPotentialFullExpression structure readonly target: ObjectEntity; } export abstract class RuntimeDefaultInitializer = CompiledDefaultInitializer> extends RuntimeInitializer { protected constructor(model: C, parent: RuntimeConstruct) { super(model, parent); } } export class ReferenceDefaultInitializer extends DefaultInitializer { public readonly construct_type = "ReferenceDefaultInitializer"; public readonly target: UnboundReferenceEntity; public constructor(context: TranslationUnitContext, target: UnboundReferenceEntity) { super(context, undefined); this.target = target; // Cannot default initialize a reference this.addNote(CPPError.declaration.init.referenceBind(this)); } public createRuntimeInitializer(parent: RuntimeConstruct): never { return assertFalse("A default initializer for a reference is not allowed."); } public createDefaultOutlet(element: JQuery, parent?: ConstructOutlet) { return assertFalse("Cannot create an outlet for a reference default initializer, since such an initializer is always ill-formed."); } public explain(sim: Simulation, rtConstruct: RuntimeConstruct) { let targetDesc = this.target.describe(); return { message: "As a reference, " + (targetDesc.name || targetDesc.message) + " must be bound to something. (It cannot be left un-initialized.)" }; } } // Note: No CompiledReferenceDefaultInitializer or RuntimeReferenceDefaultInitializer classes since // default initialization of a reference is always ill-formed. export class AtomicDefaultInitializer extends DefaultInitializer { public readonly construct_type = "AtomicDefaultInitializer"; public readonly target: ObjectEntity; public constructor(context: TranslationUnitContext, target: ObjectEntity) { super(context, undefined); this.target = target; } public createRuntimeInitializer(this: CompiledAtomicDefaultInitializer, parent: RuntimeConstruct): RuntimeAtomicDefaultInitializer; public createRuntimeInitializer(this: CompiledDefaultInitializer, parent: RuntimeConstruct): never; public createRuntimeInitializer(this: CompiledAtomicDefaultInitializer, parent: RuntimeConstruct): RuntimeAtomicDefaultInitializer { return new RuntimeAtomicDefaultInitializer(this, parent); } public createDefaultOutlet(this: CompiledAtomicDefaultInitializer, element: JQuery, parent?: ConstructOutlet): AtomicDefaultInitializerOutlet { return new AtomicDefaultInitializerOutlet(element, this, parent); } public explain(sim: Simulation, rtConstruct: RuntimeConstruct) { let targetDesc = this.target.describe(); return { message: "No initialization will take place. " + (targetDesc.name || targetDesc.message) + " will have a junk value." }; } } export interface CompiledAtomicDefaultInitializer extends AtomicDefaultInitializer, SuccessfullyCompiled { readonly temporaryDeallocator?: CompiledTemporaryDeallocator; // to match CompiledPotentialFullExpression structure readonly target: ObjectEntity; } export class RuntimeAtomicDefaultInitializer extends RuntimeDefaultInitializer> { public constructor(model: CompiledAtomicDefaultInitializer, parent: RuntimeConstruct) { super(model, parent); } protected upNextImpl() { // No initialization. Object has junk value. let target = this.model.target.runtimeLookup(this); target.beginLifetime(); this.observable.send("atomicObjectInitialized", this); this.startCleanup(); } public stepForwardImpl() { // do nothing } } export class ArrayDefaultInitializer extends DefaultInitializer { public readonly construct_type = "ArrayDefaultInitializer"; public readonly target: ObjectEntity; public readonly elementInitializers?: DefaultInitializer[]; public constructor(context: TranslationUnitContext, target: ObjectEntity) { super(context, undefined); this.target = target; // If it's an array of atomic types, do nothing. let type = this.target.type; if (type.elemType instanceof AtomicType) { // Do nothing // TODO: should I create the DefaultInitializers anyway for analysis purposes? } else { this.elementInitializers = []; for (let i = 0; i < type.numElems; ++i) { let elemInit = DefaultInitializer.create(context, new ArraySubobjectEntity(this.target, i)); this.elementInitializers.push(elemInit); this.attach(elemInit); if (elemInit.notes.hasErrors) { this.addNote(CPPError.declaration.init.array_default_init(this)); break; } } } } public createRuntimeInitializer(this: CompiledArrayDefaultInitializer, parent: RuntimeConstruct): RuntimeArrayDefaultInitializer; public createRuntimeInitializer(this: CompiledDefaultInitializer, parent: RuntimeConstruct): never; public createRuntimeInitializer(this: CompiledArrayDefaultInitializer, parent: RuntimeConstruct): RuntimeArrayDefaultInitializer { return new RuntimeArrayDefaultInitializer(this, parent); } public createDefaultOutlet(this: CompiledArrayDefaultInitializer, element: JQuery, parent?: ConstructOutlet): ArrayDefaultInitializerOutlet { return new ArrayDefaultInitializerOutlet(element, this, parent); } public explain(sim: Simulation, rtConstruct: RuntimeConstruct) { let targetDesc = this.target.describe(); let targetType = this.target.type; if (targetType.numElems === 0) { return { message: "No initialization is performed for " + (targetDesc.name || targetDesc.message) + "because the array has length 0." }; } else if (targetType.elemType instanceof AtomicType) { return { message: "No initialization will take place. The elements of " + (targetDesc.name || targetDesc.message) + " will have junk values." }; } else { return { message: "Each element of " + (targetDesc.name || targetDesc.message) + " will be default-initialized. For example, " + this.elementInitializers![0].explain(sim, rtConstruct) }; } } } export interface CompiledArrayDefaultInitializer extends ArrayDefaultInitializer, SuccessfullyCompiled { readonly temporaryDeallocator?: CompiledTemporaryDeallocator; // to match CompiledPotentialFullExpression structure readonly target: ObjectEntity; readonly elementInitializers?: CompiledDefaultInitializer[]; } export class RuntimeArrayDefaultInitializer extends RuntimeDefaultInitializer> { public readonly elementInitializers?: RuntimeDefaultInitializer[]; private index = 0; public constructor(model: CompiledArrayDefaultInitializer, parent: RuntimeConstruct) { super(model, parent); if (this.model.elementInitializers) { this.elementInitializers = this.model.elementInitializers.map((elemInit) => { return elemInit.createRuntimeInitializer(this); }); } } protected upNextImpl() { if (this.elementInitializers && this.index < this.elementInitializers.length) { this.sim.push(this.elementInitializers[this.index++]); } else { let target = this.model.target.runtimeLookup(this); target.beginLifetime(); this.observable.send("arrayObjectInitialized", this); this.startCleanup(); } } public stepForwardImpl() { // do nothing } } export class ClassDefaultInitializer extends DefaultInitializer { public readonly construct_type = "ClassDefaultInitializer"; public readonly target: ObjectEntity; public readonly ctor?: FunctionEntity>; public readonly ctorCall?: FunctionCall; public constructor(context: TranslationUnitContext, target: ObjectEntity) { super(context, undefined); this.target = target; // Try to find default constructor. Not using lookup because constructors have no name. // TODO: do I need to tell overloadResolution what the receiver type is here? they're all ctors i guess let overloadResult = overloadResolution(target.type.classDefinition.constructors, []); if (!overloadResult.selected) { this.addNote(CPPError.declaration.init.no_default_constructor(this, this.target)); return; } this.ctor = overloadResult.selected; this.ctorCall = new FunctionCall(context, this.ctor, [], target.type.cvUnqualified()); this.attach(this.ctorCall); // this.args = this.ctorCall.args; } public createRuntimeInitializer(this: CompiledClassDefaultInitializer, parent: RuntimeConstruct): RuntimeClassDefaultInitializer; public createRuntimeInitializer(this: CompiledDefaultInitializer, parent: RuntimeConstruct): never; public createRuntimeInitializer(this: CompiledClassDefaultInitializer, parent: RuntimeConstruct): RuntimeClassDefaultInitializer { return new RuntimeClassDefaultInitializer(this, parent); } public createDefaultOutlet(this: CompiledClassDefaultInitializer, element: JQuery, parent?: ConstructOutlet): ClassDefaultInitializerOutlet { return new ClassDefaultInitializerOutlet(element, this, parent); } public explain(sim: Simulation, rtConstruct: RuntimeConstruct) { let targetDesc = this.target.describe(); // TODO: what if there is an error that causes no ctor to be found/available return { message: (targetDesc.name || targetDesc.message) + " will be initialized using " + this.ctorCall!.describe(sim, rtConstruct).message }; } } export interface CompiledClassDefaultInitializer extends ClassDefaultInitializer, SuccessfullyCompiled { readonly temporaryDeallocator?: CompiledTemporaryDeallocator; // to match CompiledPotentialFullExpression structure readonly target: ObjectEntity; readonly ctor: FunctionEntity>; readonly ctorCall: CompiledFunctionCall>; } export class RuntimeClassDefaultInitializer extends RuntimeDefaultInitializer> { public readonly ctorCall: RuntimeFunctionCall>; private index = "callCtor"; public constructor(model: CompiledClassDefaultInitializer, parent: RuntimeConstruct) { super(model, parent); this.ctorCall = this.model.ctorCall.createRuntimeFunctionCall(this, this.model.target.runtimeLookup(this)); } protected upNextImpl() { if (this.index === "callCtor") { this.sim.push(this.ctorCall); this.index = "done"; } else { let target = this.model.target.runtimeLookup(this); target.beginLifetime(); this.observable.send("classObjectInitialized", this); this.startCleanup(); } } public stepForwardImpl() { // do nothing } }