Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
810656f
jvm changes
kjw142857 Jun 15, 2026
b1593a3
include try/catch/finally support in code generator
kjw142857 Jun 16, 2026
12926df
fix try statement logic
kjw142857 Jun 24, 2026
b7a4b18
Merge branch 'main' into exception-handling
martin-henz Jun 24, 2026
8f7d8b4
add parser and type checker integration
kjw142857 Jul 1, 2026
79a4e6b
fix finally bug
kjw142857 Jul 1, 2026
2daa965
add tests and fix syntax error
kjw142857 Jul 8, 2026
8e933b6
Patch grammar logic for throws keyword
kjw142857 Jul 16, 2026
181ae61
Revert "Patch grammar logic for throws keyword"
kjw142857 Jul 16, 2026
ee94927
Patch grammar logic for throws keyword
kjw142857 Jul 16, 2026
63f0301
Add fix for execption table finally logic
kjw142857 Jul 21, 2026
df4fede
Add more tests
kjw142857 Jul 21, 2026
e35730a
Add java.lang.Throwable subclasses to compiler/type checker imports
kjw142857 Jul 21, 2026
e8985da
remove some hardcoding for typechecker
kjw142857 Jul 28, 2026
e941932
tighten selector criteria
kjw142857 Aug 10, 2026
89ccf30
add enum support
kjw142857 Aug 10, 2026
4e0b4d5
WIP: Add enum grammar rules and compiler tests
kjw142857 Aug 26, 2026
0625138
Add enum parsing and compiler support (partial)
kjw142857 Aug 26, 2026
954c172
Register enum synthetic methods in symbol table
kjw142857 Aug 26, 2026
b9a196c
Implement exception handling in java-slang (#96)
kjw142857 Aug 26, 2026
f2a823e
Add feature details in README (#88)
kjw142857 Aug 27, 2026
86fc930
Merge branch 'main' into switch-statements
kjw142857 Sep 2, 2026
4cfcdf1
fix exception table merge conflict
kjw142857 Sep 2, 2026
21c3f1f
integrate enum types into switch statements
kjw142857 Sep 2, 2026
d9d7665
add enum ordinals
kjw142857 Sep 2, 2026
9d3e723
correct enum grammar
kjw142857 Sep 2, 2026
af3648d
Merge branch 'main' into switch-statements
kjw142857 Sep 2, 2026
52781ca
remove excessively long logging statement
kjw142857 Sep 2, 2026
0ceb317
remove redundant color test
kjw142857 Sep 2, 2026
1c102bd
Merge branch 'main' into std-imports
kjw142857 Sep 7, 2026
f0f6980
update compiler to add importing of std classes
kjw142857 Sep 7, 2026
c827574
ensure imported classes for compiler are handled by jvm as well
kjw142857 Sep 7, 2026
dde0622
add enum support
kjw142857 Sep 7, 2026
6cd7a78
implement library loading and primitive unboxing
kjw142857 Sep 7, 2026
aa8a837
Merge branch 'std-imports' into switch-statements
kjw142857 Sep 7, 2026
99e7920
Return compiled classes in declaration order, not compilation order
kjw142857 Sep 7, 2026
ae58cef
Make generated enums genuinely extend java.lang.Enum
kjw142857 Sep 7, 2026
d05bc85
change compiler/jvm reference to Java 8
kjw142857 Sep 7, 2026
e17c26d
merge java 8 integration from std-imports
kjw142857 Sep 7, 2026
9c2fc3a
Merge branch 'main' into switch-statements
kjw142857 Sep 7, 2026
317ece6
fix dropping of initialiser and add helper for class collation
kjw142857 Sep 7, 2026
de7cd15
Refactor enum switch label error handling
kjw142857 Sep 9, 2026
55754df
Simplify error check for duplicate classes
kjw142857 Sep 9, 2026
1702d4d
Merge branch 'main' into switch-statements
kjw142857 Sep 9, 2026
8f4b3e8
fix bug with updating of fields
kjw142857 Sep 23, 2026
5f7b9ea
Merge remote-tracking branch 'origin/main' into inner-classes
kjw142857 Sep 23, 2026
9436019
merge from main
kjw142857 Sep 23, 2026
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
36 changes: 36 additions & 0 deletions src/ast/__tests__/class-extractor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,39 @@ describe("extract multiple NormalClassDeclaration correctly", () => {
expect(ast).toEqual(expectedAst);
});
});

describe("extract nested NormalClassDeclaration correctly", () => {
it("extract a static nested class inside a class body correctly", () => {
const programStr = `
class Outer {
static class Inner {}
}
`;

const expectedAst: AST = {
kind: "CompilationUnit",
importDeclarations: [],
topLevelClassOrInterfaceDeclarations: [
{
kind: "NormalClassDeclaration",
classModifier: [],
typeIdentifier: "Outer",
classBody: [
{
kind: "NormalClassDeclaration",
classModifier: ["static"],
typeIdentifier: "Inner",
classBody: [],
location: expect.anything(),
},
],
location: expect.anything(),
},
],
location: expect.anything(),
};

const ast = parse(programStr);
expect(ast).toEqual(expectedAst);
});
});
7 changes: 7 additions & 0 deletions src/ast/astExtractor/class-extractor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,5 +91,12 @@ export class ClassExtractor extends BaseJavaCstVisitorWithDefaults {
this.body.push(methodNode);
})
}
if (ctx.classDeclaration) {
ctx.classDeclaration.forEach(x => {
const classExtractor = new ClassExtractor();
const classNode = classExtractor.extract(x);
this.body.push(classNode);
})
}
}
}
2 changes: 1 addition & 1 deletion src/ast/types/classes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export type ClassModifier =
| "strictfp"
| "enum";

export type ClassBodyDeclaration = ClassMemberDeclaration | ConstructorDeclaration | EnumDeclaration;
export type ClassBodyDeclaration = ClassMemberDeclaration | ConstructorDeclaration | EnumDeclaration | NormalClassDeclaration;
export type ClassMemberDeclaration = MethodDeclaration | FieldDeclaration;

export interface ConstructorDeclaration extends BaseNode {
Expand Down
6 changes: 4 additions & 2 deletions src/compiler/__tests__/classOrdering.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ describe('compiled class ordering', () => {
}
`
const classes = compileFromSource(program)
expect(classes.map(c => c.className)).toEqual(['Main', 'Day'])
expect(classes.map(c => c.className)).toEqual(['Main', 'Main$Day'])
})

it('keeps top-level declaration order, with member enums appended', () => {
Expand All @@ -24,7 +24,9 @@ describe('compiled class ordering', () => {
`
const classes = compileFromSource(program)
expect(classes[0].className).toBe('Main')
expect(new Set(classes.map(c => c.className))).toEqual(new Set(['Main', 'A', 'B']))
expect(new Set(classes.map(c => c.className))).toEqual(
new Set(['Main', 'Main$A', 'Main$B'])
)
})

it('is unchanged when there is no enum', () => {
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/__tests__/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { importTest } from "./tests/import.test";
import { arrayTest } from "./tests/array.test";
import { classTest } from "./tests/class.test";
import { enumTest } from "./tests/enum.test";
import { nestedClassesTest } from "./tests/nestedClasses.test";
import { typeConversionTest } from "./tests/typeConversion.test";

describe("compiler tests", () => {
Expand All @@ -25,5 +26,6 @@ describe("compiler tests", () => {
arrayTest();
classTest();
enumTest();
nestedClassesTest();
typeConversionTest();
})
13 changes: 13 additions & 0 deletions src/compiler/__tests__/tests/class.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,19 @@ const testCases: testCase[] = [
`,
expectedLines: ["in f"],
},
{
comment: "instance field with an inline initializer",
program: `
public class Main {
public int one = 1;
public static void main(String[] args) {
Main m = new Main();
System.out.println(m.one);
}
}
`,
expectedLines: ["1"],
},
{
comment: "instance field",
program: `
Expand Down
99 changes: 99 additions & 0 deletions src/compiler/__tests__/tests/nestedClasses.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import {
runTest,
testCase,
} from "../__utils__/test-utils";

const testCases: testCase[] = [
{
comment: "basic static nested class instantiation and usage",
program: `
public class Main {
public static class Inner {
public int value;
public Inner(int v) {
this.value = v;
}
public int getValue() {
return this.value;
}
}

public static void main(String[] args) {
Inner inner = new Inner(42);
System.out.println(inner.getValue());
}
}
`,
expectedLines: ["42"],
},
{
comment: "one nested class field typed as a sibling nested class",
program: `
public class Main {
public static class Box {
public int value;
public Box(int v) {
this.value = v;
}
}

public static class Holder {
public Box box;
public Holder(Box b) {
this.box = b;
}
}

public static void main(String[] args) {
Box box = new Box(9);
Holder holder = new Holder(box);
System.out.println(holder.box.value);
}
}
`,
expectedLines: ["9"],
},
{
comment: "nested class instance field with an inline initializer",
program: `
public class Main {
static class Inner1 {
public int one = 1;
}
public static void main(String[] args) {
Inner1 inner1 = new Inner1();
int n = inner1.one;
System.out.println(n);
}
}
`,
expectedLines: ["1"],
},
{
comment: "two levels of static nested class",
program: `
public class Main {
public static class Middle {
public static class Inner {
public String greet() {
return "hello";
}
}
}

public static void main(String[] args) {
Inner inner = new Inner();
System.out.println(inner.greet());
}
}
`,
expectedLines: ["hello"],
},
];

export const nestedClassesTest = () => describe("static nested classes", () => {
for (let testCase of testCases) {
const { comment: comment, program: program, expectedLines: expectedLines } = testCase;
it(comment, () => runTest(program, expectedLines));
}
});
34 changes: 29 additions & 5 deletions src/compiler/code-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1098,7 +1098,13 @@ const codeGenerators: { [type: string]: (node: Node, cg: CodeGenerator) => Compi
}
}
const res = compile(expr, cg)
const classInfoIndex = cg.constantPoolManager.indexClassInfo(ct)
let castClassName = ct
try {
castClassName = cg.symbolTable.queryClass(ct).name
} catch (e) {
castClassName = ct.includes('/') ? ct : ct.replace(/\./g, '/')
}
const classInfoIndex = cg.constantPoolManager.indexClassInfo(castClassName)
cg.code.push(OPCODE.CHECKCAST, 0, classInfoIndex)
return res
},
Expand All @@ -1107,7 +1113,18 @@ const codeGenerators: { [type: string]: (node: Node, cg: CodeGenerator) => Compi
const { identifier: id, argumentList: argLst } = node as ClassInstanceCreationExpression
let maxStack = 2

cg.code.push(OPCODE.NEW, 0, cg.constantPoolManager.indexClassInfo(id), OPCODE.DUP)
let instantiatedClassName = id
try {
instantiatedClassName = cg.symbolTable.queryClass(id).name
} catch (e) {
instantiatedClassName = id.includes('/') ? id : id.replace(/\./g, '/')
}
cg.code.push(
OPCODE.NEW,
0,
cg.constantPoolManager.indexClassInfo(instantiatedClassName),
OPCODE.DUP
)

const argTypes: Array<UnannType> = []
argLst.forEach((x, i) => {
Expand All @@ -1120,7 +1137,10 @@ const codeGenerators: { [type: string]: (node: Node, cg: CodeGenerator) => Compi
const methodInfos = cg.symbolTable.queryMethod('<init>') as MethodInfos
for (let i = 0; i < methodInfos.length; i++) {
const methodInfo = methodInfos[i]
if (methodInfo.typeDescriptor.includes(argDescriptor) && methodInfo.className == id) {
if (
methodInfo.typeDescriptor.includes(argDescriptor) &&
methodInfo.className == instantiatedClassName
) {
const method = cg.constantPoolManager.indexMethodrefInfo(
methodInfo.className,
methodInfo.name,
Expand All @@ -1131,7 +1151,7 @@ const codeGenerators: { [type: string]: (node: Node, cg: CodeGenerator) => Compi
}
}

return { stackSize: maxStack, resultType: id }
return { stackSize: maxStack, resultType: instantiatedClassName }
},

ArrayAccess: (node: Node, cg: CodeGenerator) => {
Expand Down Expand Up @@ -1768,7 +1788,11 @@ const codeGenerators: { [type: string]: (node: Node, cg: CodeGenerator) => Compi
return field.ordinal
})()
: label.expression.kind === 'ExpressionName'
? (() => { throw new Error(`Identifier case labels are only supported for enum switch selectors: ${label.expression.name}`) })()
? (() => {
throw new Error(
`Identifier case labels are only supported for enum switch selectors: ${label.expression.name}`
)
})()
: parseInt((label.expression as Literal).literalType.value)
caseValues.push(value)
caseLabelMap.set(value, caseLabels[index])
Expand Down
Loading
Loading