Skip to content
114 changes: 112 additions & 2 deletions src/ast/__tests__/statement-extractor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ describe("extract ExpressionStatement correctly", () => {
kind: "NormalClassDeclaration",
classModifier: [],
typeIdentifier: "Test",
sclass: undefined,
classBody: [
{
kind: "MethodDeclaration",
Expand Down Expand Up @@ -203,8 +204,7 @@ describe("extract ExpressionStatement correctly", () => {
const ast = parse(programStr);
expect(ast).toEqual(expectedAst);
});

it("extract Assignment Expression simple ExpressionName correctly", () => {
it("extract Assignment LeftHandSide qualified ExpressionName correctly", () => {
const programStr = `
class Test {
void test() {
Expand Down Expand Up @@ -716,6 +716,7 @@ describe("extract ReturnStatement correctly", () => {
kind: "NormalClassDeclaration",
classModifier: [],
typeIdentifier: "Test",
sclass: undefined,
classBody: [
{
kind: "MethodDeclaration",
Expand Down Expand Up @@ -804,6 +805,115 @@ describe("extract ReturnStatement correctly", () => {
location: expect.anything(),
};

const ast = parse(programStr);
console.log(JSON.stringify(ast, null, 2));
expect(ast).toEqual(expectedAst);
});
});

describe("extract TryStatement and ThrowStatement correctly", () => {
it("extract ThrowStatement inside catch block correctly", () => {
const programStr = `
class Test {
void test() {
try {
throw new Exception();
} catch (Exception e) {
throw new Exception();
}
}
}
`;

const expectedAst: AST = {
kind: "CompilationUnit",
importDeclarations: [],
topLevelClassOrInterfaceDeclarations: [
{
kind: "NormalClassDeclaration",
classModifier: [],
typeIdentifier: "Test",
classBody: [
{
kind: "MethodDeclaration",
methodModifier: [],
methodHeader: {
result: "void",
identifier: "test",
formalParameterList: [],
},
methodBody: {
kind: "Block",
blockStatements: [
{
kind: "TryStatement",
block: {
kind: "Block",
blockStatements: [
{
kind: "ThrowStatement",
expression: {
kind: "ClassInstanceCreationExpression",
identifier: "Exception",
argumentList: [],
location: expect.anything(),
},
location: expect.anything(),
},
],
location: expect.anything(),
},
catches: {
kind: "Catches",
catchClauses: [
{
kind: "CatchClause",
catchFormalParameter: {
kind: "CatchFormalParameter",
catchType: {
kind: "CatchType",
unannClassType: "Exception",
location: expect.anything(),
},
variableDeclaratorId: "e",
location: expect.anything(),
},
block: {
kind: "Block",
blockStatements: [
{
kind: "ThrowStatement",
expression: {
kind: "ClassInstanceCreationExpression",
identifier: "Exception",
argumentList: [],
location: expect.anything(),
},
location: expect.anything(),
},
],
location: expect.anything(),
},
location: expect.anything(),
},
],
location: expect.anything(),
},
finally: undefined,
location: expect.anything(),
},
],
location: expect.anything(),
},
location: expect.anything(),
},
],
location: expect.anything(),
},
],
location: expect.anything(),
};

const ast = parse(programStr);
expect(ast).toEqual(expectedAst);
});
Expand Down
9 changes: 6 additions & 3 deletions src/ast/astExtractor/class-extractor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,17 @@ export class ClassExtractor extends BaseJavaCstVisitorWithDefaults {

extract(cst: ClassDeclarationCstNode): ClassDeclaration {
this.visit(cst);
return {
const result: NormalClassDeclaration = {
kind: "NormalClassDeclaration",
classModifier: this.modifier,
typeIdentifier: this.identifier,
classBody: this.body,
sclass: this.sclass,
location: cst.location,
} as NormalClassDeclaration;
};
if (this.sclass) {
result.sclass = this.sclass;
}
return result;
}

classModifier(ctx: ClassModifierCtx) {
Expand Down
85 changes: 83 additions & 2 deletions src/ast/astExtractor/statement-extractor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ import {
SwitchBlockCtx,
SwitchLabelCtx,
SwitchBlockStatementGroupCtx,
ThrowStatementCtx,
TryStatementCtx,
CatchClauseCtx,
CatchFormalParameterCtx,
CatchTypeCtx,
CatchesCtx,
FinallyCtx,
StatementCstNode,
StatementExpressionCtx,
StatementWithoutTrailingSubstatementCtx,
Expand Down Expand Up @@ -97,6 +104,10 @@ export class StatementExtractor extends BaseJavaCstVisitorWithDefaults {
exp: returnStatementExp,
location: ctx.returnStatement[0].location,
};
} else if (ctx.throwStatement) {
return this.visit(ctx.throwStatement);
} else if (ctx.tryStatement) {
return this.visit(ctx.tryStatement);
}
}

Expand Down Expand Up @@ -356,6 +367,69 @@ export class StatementExtractor extends BaseJavaCstVisitorWithDefaults {
return ctx.expression.map((e) => expressionExtractor.extract(e));
}

throwStatement(ctx: ThrowStatementCtx) {
const expressionExtractor = new ExpressionExtractor();
return {
kind: "ThrowStatement",
expression: expressionExtractor.extract(ctx.expression[0]),
location: ctx.Throw[0],
};
}
Comment thread
kjw142857 marked this conversation as resolved.

tryStatement(ctx: TryStatementCtx) {
return {
kind: "TryStatement",
block: ctx.block ? this.visit(ctx.block) : { kind: "Block", blockStatements: [], location: ctx.Try![0] },
catches: ctx.catches ? this.visit(ctx.catches) : undefined,
finally: ctx.finally ? this.visit(ctx.finally) : undefined,
location: ctx.Try![0],
};
}
Comment thread
kjw142857 marked this conversation as resolved.

catches(ctx: CatchesCtx) {
return {
kind: "Catches",
catchClauses: ctx.catchClause.map((catchClause) => this.visit(catchClause)),
location: ctx.catchClause[0].location,
};
}

catchClause(ctx: CatchClauseCtx) {
return {
kind: "CatchClause",
catchFormalParameter: this.visit(ctx.catchFormalParameter),
block: this.visit(ctx.block),
location: ctx.Catch[0],
};
}
Comment thread
kjw142857 marked this conversation as resolved.

catchFormalParameter(ctx: CatchFormalParameterCtx) {
return {
kind: "CatchFormalParameter",
catchType: this.visit(ctx.catchType[0]),
variableDeclaratorId:
ctx.variableDeclaratorId[0].children.Identifier[0].image,
location: ctx.catchType[0].location,
};
}

catchType(ctx: CatchTypeCtx) {
const result = new TypeExtractor().visit(ctx.unannClassType[0] as any);
return {
kind: "CatchType",
unannClassType: result,
location: ctx.unannClassType[0].location,
};
}

finally(ctx: FinallyCtx) {
return {
kind: "Finally",
block: this.visit(ctx.block),
location: ctx.Finally[0],
};
}
Comment thread
kjw142857 marked this conversation as resolved.

fqnOrRefType(ctx: FqnOrRefTypeCtx) {
// Assignment LHS, MethodInvocation identifier
let { name, location } = this.visit(ctx.fqnOrRefTypePartFirst);
Expand Down Expand Up @@ -419,8 +493,15 @@ export class StatementExtractor extends BaseJavaCstVisitorWithDefaults {
}

block(ctx: BlockCtx): Statement {
if (ctx.blockStatements) return this.visit(ctx.blockStatements);
return { kind: "EmptyStatement" };
const location =
(ctx.blockStatements?.[0] as any)?.location ||
(ctx.LCurly?.[0] as any)?.location ||
(ctx.RCurly?.[0] as any)?.location;
Comment thread
kjw142857 marked this conversation as resolved.
if (ctx.blockStatements) {
const block = this.visit(ctx.blockStatements) as Statement;
return { ...block, location };
}
return { kind: "EmptyStatement", location };
}

blockStatements(ctx: BlockStatementsCtx): Statement {
Expand Down
43 changes: 42 additions & 1 deletion src/ast/types/blocks-and-statements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,48 @@ export type StatementWithoutTrailingSubstatement =
| DoStatement
| ReturnStatement
| BreakStatement
| ContinueStatement;
| ContinueStatement
| ThrowStatement
| TryStatement;

export interface ThrowStatement extends BaseNode {
kind: "ThrowStatement";
expression: Expression;
}

export interface CatchClause extends BaseNode {
kind: "CatchClause";
catchFormalParameter: CatchFormalParameter;
block: Block;
}

export interface Catches extends BaseNode {
kind: "Catches";
catchClauses: Array<CatchClause>;
}

export interface CatchFormalParameter extends BaseNode {
kind: "CatchFormalParameter";
catchType: CatchType;
variableDeclaratorId: Identifier;
}

export interface CatchType extends BaseNode {
kind: "CatchType";
unannClassType: UnannType;
}

export interface Finally extends BaseNode {
kind: "Finally";
block: Block;
}

export interface TryStatement extends BaseNode {
kind: "TryStatement";
block: Block;
catches?: Catches;
finally?: Finally;
}

export interface ExpressionStatement extends BaseNode {
kind: "ExpressionStatement";
Expand Down
Loading
Loading