Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 28 additions & 45 deletions src/codegen/expressions/arrow-functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
ReturnStatement,
IfStatement,
Expression,
ConditionalExpressionNode,
} from "../../ast/types.js";
import {
ClosureAnalyzer,
Expand Down Expand Up @@ -264,18 +265,17 @@ export class ArrowFunctionExpressionGenerator extends BaseGenerator {
}

private inferReturnTypeFromBody(body: ArrowFunctionNode["body"]): string | null {
const bodyTyped = body as { type: string };
if (bodyTyped.type === "object") {
if (body.type === "object") {
return "object";
}
if (
bodyTyped.type === "string" ||
bodyTyped.type === "string_literal" ||
bodyTyped.type === "template_literal"
body.type === "string" ||
(body.type as string) === "string_literal" ||
body.type === "template_literal"
) {
return "string";
}
if (bodyTyped.type === "binary") {
if (body.type === "binary") {
const binExpr = body as BinaryNode;
if (binExpr.op === "+") {
const leftType = this.inferReturnTypeFromBody(binExpr.left as ArrowFunctionNode["body"]);
Expand All @@ -285,74 +285,57 @@ export class ArrowFunctionExpressionGenerator extends BaseGenerator {
}
}
}
if (bodyTyped.type === "array") {
if (body.type === "array") {
return "array";
}
if (bodyTyped.type === "conditional") {
const condTyped = body as {
type: string;
condition: unknown;
consequent: unknown;
alternate: unknown;
};
const consequent = condTyped.consequent;
const alternate = condTyped.alternate;
if (consequent) {
const consequentTyped = consequent as { type: string };
if (consequentTyped.type === "object") {
return "object";
}
if (consequentTyped.type === "string_literal") {
return "string";
}
if (body.type === "conditional") {
const cond = body as ConditionalExpressionNode;
if (cond.consequent.type === "object") {
return "object";
}
if (alternate) {
const alternateTyped = alternate as { type: string };
if (alternateTyped.type === "object") {
return "object";
}
if ((cond.consequent.type as string) === "string_literal") {
return "string";
}
if (cond.alternate.type === "object") {
return "object";
}
}
if (bodyTyped.type === "block") {
if (body.type === "block") {
const blockTyped = body as BlockStatement;
const blockStatements = blockTyped.statements;
if (blockStatements) {
for (let i = 0; i < blockStatements.length; i++) {
const stmt = blockStatements[i];
const stmtBase = stmt as { type: string };
if (stmtBase.type === "return") {
if (stmt.type === "return") {
const stmtTyped = stmt as ReturnStatement;
if (stmtTyped.value) {
const returnValueTyped = stmtTyped.value as { type: string };
if (returnValueTyped.type === "object") {
if (stmtTyped.value.type === "object") {
return "object";
}
if (
returnValueTyped.type === "string" ||
returnValueTyped.type === "string_literal" ||
returnValueTyped.type === "template_literal"
stmtTyped.value.type === "string" ||
(stmtTyped.value.type as string) === "string_literal" ||
stmtTyped.value.type === "template_literal"
) {
return "string";
}
}
}
if (stmtBase.type === "if") {
if (stmt.type === "if") {
const ifStmt = stmt as IfStatement;
const thenBlock = ifStmt.thenBlock;
const thenBlockStatements = thenBlock ? thenBlock.statements : null;
if (thenBlockStatements) {
for (let j = 0; j < thenBlockStatements.length; j++) {
const innerStmt = thenBlockStatements[j];
const innerStmtTyped = innerStmt as ReturnStatement;
const innerStmtTyped = thenBlockStatements[j] as ReturnStatement;
if (innerStmtTyped.type === "return" && innerStmtTyped.value) {
const innerReturnValueTyped = innerStmtTyped.value as { type: string };
if (innerReturnValueTyped.type === "object") {
if (innerStmtTyped.value.type === "object") {
return "object";
}
if (
innerReturnValueTyped.type === "string" ||
innerReturnValueTyped.type === "string_literal" ||
innerReturnValueTyped.type === "template_literal"
innerStmtTyped.value.type === "string" ||
(innerStmtTyped.value.type as string) === "string_literal" ||
innerStmtTyped.value.type === "template_literal"
) {
return "string";
}
Expand Down
6 changes: 2 additions & 4 deletions src/codegen/expressions/calls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,15 +136,13 @@ export class CallExpressionGenerator {

// Handle test() - built-in test runner (only when called with string + arrow/function callback)
if (expr.name === "test" && expr.args.length >= 2) {
const secondArg = expr.args[1] as { type: string };
if (secondArg.type === "arrow_function" || secondArg.type === "variable") {
if (expr.args[1].type === "arrow_function" || expr.args[1].type === "variable") {
return this.generateTest(expr, params);
}
}

if (expr.name === "describe" && expr.args.length >= 2) {
const secondArg = expr.args[1] as { type: string };
if (secondArg.type === "arrow_function" || secondArg.type === "variable") {
if (expr.args[1].type === "arrow_function" || expr.args[1].type === "variable") {
return this.generateDescribe(expr, params);
}
}
Expand Down
1 change: 0 additions & 1 deletion src/codegen/expressions/method-calls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,6 @@ export class MethodCallGenerator {
return this.generateOptionalMethodCall(expr, params);
}

const objBase = expr.object as { type: string };
const method = expr.method;

// Handle Promise static methods (Promise.resolve, Promise.reject, Promise.all)
Expand Down
6 changes: 2 additions & 4 deletions src/codegen/expressions/operators/binary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,10 +296,8 @@ export class BinaryExpressionGenerator {
leftExpr: Expression,
rightExpr: Expression,
): string {
const leftExprTyped = leftExpr as { type: string };
const rightExprTyped = rightExpr as { type: string };
const leftIsNullish = leftExprTyped.type === "null" || leftExprTyped.type === "undefined";
const rightIsNullish = rightExprTyped.type === "null" || rightExprTyped.type === "undefined";
const leftIsNullish = leftExpr.type === "null" || leftExpr.type === "undefined";
const rightIsNullish = rightExpr.type === "null" || rightExpr.type === "undefined";
if (leftIsNullish || rightIsNullish) {
return this.generatePointerNullComparison(op, leftValue, rightValue);
}
Expand Down
52 changes: 25 additions & 27 deletions src/codegen/expressions/orchestrator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,7 @@ export class ExpressionGenerator {
* Delegates to appropriate sub-generator based on expression type
*/
generate(expr: Expression, params: string[]): string {
const exprTyped = expr as { type: string };
if (!exprTyped.type || exprTyped.type.length === 0) {
if (!expr.type || expr.type.length === 0) {
// Hard error: expressions must have a type. An empty type indicates a parser
// or AST construction bug. Previously this silently generated a null pointer,
// which LLVM -O2 could exploit as UB to prune unrelated code paths.
Expand All @@ -129,37 +128,37 @@ export class ExpressionGenerator {
);
}
// Literals
if (exprTyped.type === "number") {
if (expr.type === "number") {
const numExpr = expr as NumberNode;
return this.literalGen.generateNumber(numExpr.value);
}

if (exprTyped.type === "boolean") {
if (expr.type === "boolean") {
const boolExpr = expr as BooleanNode;
return this.literalGen.generateBoolean(boolExpr.value);
}

if (exprTyped.type === "string") {
if (expr.type === "string") {
const strExpr = expr as StringNode;
return this.literalGen.generateString(strExpr.value);
}

if (exprTyped.type === "null" || exprTyped.type === "undefined") {
if (expr.type === "null" || expr.type === "undefined") {
this.ctx.setVariableType("null", "i8*");
return "null";
}

if (exprTyped.type.indexOf("spread:") === 0) {
const varName = exprTyped.type.substr(7);
if (expr.type.indexOf("spread:") === 0) {
const varName = expr.type.substr(7);
return this.variableGen.generate(varName);
}

if (exprTyped.type === "regex") {
if (expr.type === "regex") {
const regexExpr = expr as RegexNode;
return this.literalGen.generateRegex(regexExpr.pattern, regexExpr.flags);
}

if (exprTyped.type === "array") {
if (expr.type === "array") {
return this.literalGen.generateArray(expr as ArrayNode, params);
}

Expand All @@ -180,45 +179,45 @@ export class ExpressionGenerator {
return this.literalGen.generateNew(newExpr.className, newExpr.args, params, newExpr.typeArgs);
}

if (exprTyped.type === "this") {
if (expr.type === "this") {
return this.literalGen.generateThis();
}

// Variables
if (exprTyped.type === "variable") {
if (expr.type === "variable") {
const varExpr = expr as VariableNode;
return this.variableGen.generate(varExpr.name);
}

// Unary operators
if (exprTyped.type === "unary") {
if (expr.type === "unary") {
const unaryExpr = expr as UnaryNode;
return this.unaryGen.generate(unaryExpr.op, unaryExpr.operand, params);
}

// Binary operators
if (exprTyped.type === "binary") {
if (expr.type === "binary") {
const binExpr = expr as BinaryNode;
return this.binaryGen.generate(binExpr.op, binExpr.left, binExpr.right, params);
}

// Call expressions
if (exprTyped.type === "call") {
if (expr.type === "call") {
return this.callGen.generate(expr as CallNode, params);
}

// Index access
if (exprTyped.type === "index_access") {
if (expr.type === "index_access") {
return this.indexAccessGen.generate(expr as IndexAccessNode, params);
}

// Member access
if (exprTyped.type === "member_access") {
if (expr.type === "member_access") {
return this.memberAccessGen.generate(expr as MemberAccessNode, params);
}

// Arrow functions
if (exprTyped.type === "arrow_function") {
if (expr.type === "arrow_function") {
const scopeVarsResult = this.ctx.symbolTable.getScopeVarsArraysForClosure();
const scopeVarsTyped = scopeVarsResult as {
names: string[];
Expand Down Expand Up @@ -282,22 +281,22 @@ export class ExpressionGenerator {
}

// Conditional (ternary) expressions
if (exprTyped.type === "conditional") {
if (expr.type === "conditional") {
return this.conditionalGen.generate(expr as ConditionalExpressionNode, params);
}

// Template literals
if (exprTyped.type === "template_literal") {
if (expr.type === "template_literal") {
return this.templateLiteralGen.generate(expr as TemplateLiteralNode, params);
}

// Method calls
if (exprTyped.type === "method_call") {
if (expr.type === "method_call") {
return this.methodCallGen.generate(expr as MethodCallNode, params);
}

// Await expressions
if (exprTyped.type === "await") {
if (expr.type === "await") {
const awaitExpr = expr as AwaitExpressionNode;
const promiseReg = this.generate(awaitExpr.argument, params);
const valueReg = this.ctx.nextTemp();
Expand All @@ -311,10 +310,9 @@ export class ExpressionGenerator {
// When the inner expression is a variable, record its name so that
// allocateDeclaredInterface can inherit the source variable's field order
// (the asserted type may reorder fields relative to the object literal layout).
if (exprTyped.type === "type_assertion") {
if (expr.type === "type_assertion") {
const assertExpr = expr as TypeAssertionNode;
const innerBase = assertExpr.expression as { type: string };
if (innerBase.type === "variable") {
if (assertExpr.expression.type === "variable") {
const innerVar = assertExpr.expression as VariableNode;
this.ctx.setLastTypeAssertionSourceVar(innerVar.name);
} else {
Expand All @@ -324,14 +322,14 @@ export class ExpressionGenerator {
}

// Index access assignment (arr[i] = value)
if (exprTyped.type === "index_access_assignment") {
if (expr.type === "index_access_assignment") {
return this.indexAccessGen.generateAssignment(expr as IndexAccessAssignmentNode, params);
}

// Hard error: unsupported expression types must not silently produce null pointers.
// A null here would be UB that LLVM -O2 can exploit to prune unrelated code.
this.ctx.emitError(
"unsupported expression type: " + exprTyped.type,
"unsupported expression type: " + expr.type,
(expr as { loc?: { line: number; column: number } }).loc,
);
}
Expand Down
3 changes: 1 addition & 2 deletions src/codegen/expressions/templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,8 @@ export class TemplateLiteralGenerator {
partValue = this.ctx.stringGen.doCreateStringConstant(partAsObj.value || "");
} else {
const exprPart = part as Expression;
const exprPartObj = exprPart as { type: string };
const exprValue = this.ctx.generateExpression(exprPart, params);
if (exprPartObj.type === "boolean") {
if (exprPart.type === "boolean") {
partValue = this.booleanToString(exprValue);
} else if (
this.ctx.isStringExpression(exprPart) ||
Expand Down
6 changes: 4 additions & 2 deletions src/codegen/infrastructure/assignment-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,8 @@ export class AssignmentGenerator {
this.ctx.setCurrentDeclaredSetType(undefined);

let instancePtr: string | null = null;
const objType = object.type;
const objectTyped = object as { type: string };
const objType = objectTyped.type;
if (objType === "variable") {
const varName = (object as VariableNode).name;
if (this.ctx.symbolTable.isClass(varName)) {
Expand Down Expand Up @@ -570,7 +571,8 @@ export class AssignmentGenerator {

let arrayType = "%StringArray";
const currentClass = this.ctx.getCurrentClassName();
if (arrayExpr.object.type === "this" && currentClass) {
const arrayExprObj = arrayExpr.object;
if (arrayExprObj.type === "this" && currentClass) {
const fieldInfo = this.ctx.classGenGetFieldInfo(currentClass, arrayExpr.property);
if (fieldInfo) {
const fi = fieldInfo as { index: number; type: string; tsType: string };
Expand Down
Loading
Loading