import { SimulationEvent } from "../../runtime/Simulation"; import { IntegralType, PointerType, ArrayPointerType, isIntegralType, isPointerType, PointerToCompleteType as PointerToCompleteObjectType } from "../../compilation/types"; import { ExpressionContext, ConstructDescription } from "../../compilation/contexts"; import { SuccessfullyCompiled, RuntimeConstruct } from "../CPPConstruct"; import { CPPError } from "../../compilation/errors"; import { VCResultTypes, CompiledExpression, TypedExpression, t_TypedExpression } from "./Expression"; import { RuntimeExpression } from "./RuntimeExpression"; import { Predicates } from "../../../analysis/predicates"; import { CompiledTemporaryDeallocator } from "../TemporaryDeallocator"; import { ArithmeticBinaryOperatorExpressionASTNode } from "../../../ast/ast_expressions"; import { SimpleRuntimeExpression } from "./SimpleRuntimeExpression"; import { BinaryOperatorExpression } from "./BinaryOperatorExpression"; import { createRuntimeExpression } from "./expressions"; export class PointerOffsetExpression extends BinaryOperatorExpression { public readonly construct_type = "pointer_offset_expression"; public readonly type?: PointerType; public readonly left: TypedExpression | TypedExpression; public readonly right: TypedExpression | TypedExpression; public readonly pointer?: TypedExpression; public readonly offset?: TypedExpression; public readonly pointerOnLeft?: boolean; public readonly operator!: "+" | "-"; // Narrows type from base public constructor(context: ExpressionContext, ast: ArithmeticBinaryOperatorExpressionASTNode, left: TypedExpression | TypedExpression, right: TypedExpression | TypedExpression, operator: "+" | "-") { super(context, ast, operator); // NOT NEEDED ASSUMING THEY COME IN ALREADY WELL TYPED AS APPROPRIATE FOR POINTER OFFSET // if (left.isWellTyped() && right.isWellTyped()) { // left = convertToPRValue(left); // right = convertToPRValue(right); // } this.attach(this.left = left); this.attach(this.right = right); if (!left.isWellTyped() || !right.isWellTyped()) { return; } if (Predicates.isTypedExpression(left, isPointerType) && Predicates.isTypedExpression(right, isIntegralType)) { this.pointerOnLeft = true; this.pointer = left; this.offset = right; this.type = this.pointer.type; if (!left.type.isPointerToCompleteObjectType()) { this.addNote(CPPError.expr.pointer_offset.incomplete_pointed_type(this, left.type)); } } else if (Predicates.isTypedExpression(left, isIntegralType) && Predicates.isTypedExpression(right, isPointerType)) { this.pointerOnLeft = false; this.pointer = right; this.offset = left; this.type = this.pointer.type; if (!right.type.isPointerToCompleteObjectType()) { this.addNote(CPPError.expr.pointer_offset.incomplete_pointed_type(this, right.type)); } } else { this.addNote(CPPError.expr.invalid_binary_operands(this, this.operator, left, right)); } } // public createRuntimeExpression(this: CompiledPointerOffsetExpression, parent: RuntimeConstruct) : RuntimePointerOffset; // public createRuntimeExpression(this: Compiled>, parent: RuntimeConstruct) : never; // public createRuntimeExpression(this: CompiledPointerOffsetExpression, parent: RuntimeConstruct) : RuntimePointerOffset { // return new RuntimePointerOffset(this, parent); // } public describeEvalResult(depth: number): ConstructDescription { throw new Error("Method not implemented."); } } export interface TypedPointerOffsetExpression extends PointerOffsetExpression, t_TypedExpression { readonly type: T; readonly left: TypedExpression | TypedExpression; readonly right: TypedExpression | TypedExpression; readonly pointer: TypedExpression; readonly offset: TypedExpression; } export interface CompiledPointerOffsetExpression extends TypedPointerOffsetExpression, SuccessfullyCompiled { readonly temporaryDeallocator?: CompiledTemporaryDeallocator; // to match CompiledPotentialFullExpression structure readonly left: CompiledExpression | CompiledExpression; readonly right: CompiledExpression | CompiledExpression; readonly pointer: CompiledExpression; readonly offset: CompiledExpression; readonly pointerOnLeft?: boolean; } export class RuntimePointerOffset extends SimpleRuntimeExpression> { public readonly left: RuntimeExpression | RuntimeExpression; public readonly right: RuntimeExpression | RuntimeExpression; public readonly pointer: RuntimeExpression; public readonly offset: RuntimeExpression; public constructor(model: CompiledPointerOffsetExpression, parent: RuntimeConstruct) { super(model, parent); this.pointer = createRuntimeExpression(this.model.pointer, this); this.offset = createRuntimeExpression(this.model.offset, this); if (model.pointerOnLeft) { this.left = this.pointer; this.right = this.offset; } else { this.left = this.offset; this.right = this.pointer; } this.setSubexpressions([this.left, this.right]); } public operate() { // code below computes the new address after pointer addition, while preserving RTTI // result = pointer + offset * pointerSize let result = this.pointer.evalResult.pointerOffset(this.offset.evalResult, this.model.operator === "-"); this.setEvalResult(>result); // not sure why cast is necessary here let resultType = result.type; if (resultType.isType(ArrayPointerType)) { // Check that we haven't run off the array if (result.rawValue < resultType.min()) { //sim.alert("Oops. That pointer just wandered off the beginning of its array."); } else if (resultType.onePast() < result.rawValue) { //sim.alert("Oops. That pointer just wandered off the end of its array."); } } else { // If the RTTI works well enough, this should always be unsafe this.sim.eventOccurred(SimulationEvent.UNDEFINED_BEHAVIOR, "Uh, I don't think you're supposed to do arithmetic with that pointer. It's not pointing into an array.", true); } } }