Skip to content

Commit d41aaa6

Browse files
committed
Fix conditional expression bug
1 parent 75d24fd commit d41aaa6

File tree

4 files changed

+88
-22
lines changed

4 files changed

+88
-22
lines changed

src/compiler/__tests__/__utils__/test-utils.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { inspect } from "util";
2-
import { Compiler } from "../../compiler";
2+
//import { Compiler } from "../../compiler";
3+
import { compile } from "../../index";
34
import { BinaryWriter } from "../../binary-writer";
45
import { AST } from "../../../ast/types/packages-and-modules";
56
import { javaPegGrammar } from "../../grammar"
@@ -19,7 +20,7 @@ const pathToTestDir = "./src/compiler/__tests__/";
1920
const parser = peggy.generate(javaPegGrammar, {
2021
allowedStartRules: ["CompilationUnit"],
2122
});
22-
const compiler = new Compiler();
23+
//const compiler = new Compiler();
2324
const binaryWriter = new BinaryWriter();
2425

2526
export function runTest(program: string, expectedLines: string[]) {
@@ -30,7 +31,7 @@ export function runTest(program: string, expectedLines: string[]) {
3031
console.log(inspect(ast, false, null, true));
3132
}
3233

33-
const classFile = compiler.compile(ast as AST);
34+
const classFile = compile(ast as AST);
3435
binaryWriter.writeBinary(classFile, pathToTestDir);
3536

3637
const prevDir = process.cwd();

src/compiler/__tests__/tests/ifElse.test.ts

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,43 @@ const testCases: testCase[] = [
4242
`,
4343
expectedLines: ["ok", "done"],
4444
},
45+
{
46+
comment: "if and else, single variable conditional expression",
47+
program: `
48+
public class Main {
49+
public static void main(String[] args) {
50+
boolean b = true;
51+
if (b) {
52+
System.out.println("This is expected");
53+
} else {
54+
System.out.println("This is incorrect");
55+
}
56+
57+
boolean c = false;
58+
if (c) {
59+
System.out.println("Incorrect");
60+
} else {
61+
System.out.println("Correct");
62+
}
63+
64+
boolean g = false;
65+
if (!g) {
66+
System.out.println("This is expected");
67+
} else {
68+
System.out.println("This is incorrect");
69+
}
70+
71+
boolean h = true;
72+
if (!h) {
73+
System.out.println("Incorrect");
74+
} else {
75+
System.out.println("Correct");
76+
}
77+
}
78+
}
79+
`,
80+
expectedLines: ["This is expected", "Correct", "This is expected", "Correct"],
81+
},
4582
{
4683
comment: "if and else",
4784
program: `
@@ -200,10 +237,22 @@ const testCases: testCase[] = [
200237
} else {
201238
System.out.println("Yes2");
202239
}
240+
241+
boolean b = true;
242+
boolean c = !b;
243+
244+
if (!(!(!(!c)))) {
245+
System.out.println("Hmm");
246+
} else {
247+
System.out.println("Yes3");
248+
}
249+
250+
boolean d = !(!(!(b == c)));
251+
System.out.println(d);
203252
}
204253
}
205254
`,
206-
expectedLines: ["Yes1", "Yes2"],
255+
expectedLines: ["Yes1", "Yes2", "Yes3", "true"],
207256
},
208257
{
209258
comment: "complex conditional expression",

src/compiler/binary-writer.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,17 @@ export class BinaryWriter {
4444
}
4545

4646
writeBinary(classFile: ClassFile, filepath: string) {
47-
const filename = filepath + 'Main.class'
47+
const filename = filepath + this.getClassName(classFile) + '.class'
4848
const binary = this.toBinary(classFile)
4949
fs.writeFileSync(filename, binary)
5050
}
5151

52+
private getClassName(classFile: ClassFile) {
53+
const classInfo = classFile.constantPool[classFile.thisClass - 1] as ConstantClassInfo;
54+
const classNameInfo = classFile.constantPool[classInfo.nameIndex - 1] as ConstantUtf8Info;
55+
return classNameInfo.value;
56+
}
57+
5258
private toBinary(classFile: ClassFile) {
5359
this.byteArray = []
5460
this.constantPool = classFile.constantPool

src/compiler/code-generator.ts

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -181,11 +181,11 @@ const codeGenerators: { [type: string]: (node: Node, cg: CodeGenerator) => Compi
181181
cg.symbolTable.extend()
182182
let maxStack = 0
183183
let resultType = ''
184-
;(node as Block).blockStatements.forEach(x => {
185-
const { stackSize: stackSize, resultType: type } = compile(x, cg)
186-
maxStack = Math.max(maxStack, stackSize)
187-
resultType = type
188-
})
184+
; (node as Block).blockStatements.forEach(x => {
185+
const { stackSize: stackSize, resultType: type } = compile(x, cg)
186+
maxStack = Math.max(maxStack, stackSize)
187+
resultType = type
188+
})
189189
cg.symbolTable.teardown()
190190

191191
return { stackSize: maxStack, resultType }
@@ -418,8 +418,10 @@ const codeGenerators: { [type: string]: (node: Node, cg: CodeGenerator) => Compi
418418
literalType: { kind: kind, value: value }
419419
} = node
420420
const boolValue = value === 'true'
421-
if (kind === 'BooleanLiteral' && onTrue === boolValue) {
422-
cg.addBranchInstr(OPCODE.GOTO, targetLabel)
421+
if (kind === 'BooleanLiteral') {
422+
if (onTrue === boolValue) {
423+
cg.addBranchInstr(OPCODE.GOTO, targetLabel)
424+
}
423425
return { stackSize: 0, resultType: cg.symbolTable.generateFieldDescriptor('boolean') }
424426
}
425427
}
@@ -470,20 +472,20 @@ const codeGenerators: { [type: string]: (node: Node, cg: CodeGenerator) => Compi
470472
} else if (op in reverseLogicalOp) {
471473
if (isNullLiteral(left)) {
472474
// still use l to represent the first argument pushed onto stack
473-
l = f(right, targetLabel, onTrue)
475+
l = compile(right, cg)
474476
cg.addBranchInstr(
475477
onTrue !== (op === '!=') ? OPCODE.IFNULL : OPCODE.IFNONNULL,
476478
targetLabel
477479
)
478480
} else if (isNullLiteral(right)) {
479-
l = f(left, targetLabel, onTrue)
481+
l = compile(left, cg)
480482
cg.addBranchInstr(
481483
onTrue !== (op === '!=') ? OPCODE.IFNULL : OPCODE.IFNONNULL,
482484
targetLabel
483485
)
484486
} else {
485-
l = f(left, targetLabel, onTrue)
486-
r = f(right, targetLabel, onTrue)
487+
l = compile(left, cg)
488+
r = compile(right, cg)
487489
cg.addBranchInstr(onTrue ? logicalOp[op] : reverseLogicalOp[op], targetLabel)
488490
}
489491
return {
@@ -496,7 +498,9 @@ const codeGenerators: { [type: string]: (node: Node, cg: CodeGenerator) => Compi
496498
}
497499
}
498500

499-
return compile(node, cg)
501+
const res = compile(node, cg)
502+
cg.addBranchInstr(onTrue ? OPCODE.IFNE : OPCODE.IFEQ, cg.labels[cg.labels.length - 1])
503+
return res
500504
}
501505
return f(node, cg.labels[cg.labels.length - 1], false)
502506
},
@@ -799,9 +803,10 @@ const codeGenerators: { [type: string]: (node: Node, cg: CodeGenerator) => Compi
799803
field
800804
)
801805
}
806+
const fetchedFieldTypeDescriptor = (fieldInfos[fieldInfos.length - 1] as FieldInfo).typeDescriptor
802807
return {
803-
stackSize: 1,
804-
resultType: (fieldInfos[fieldInfos.length - 1] as FieldInfo).typeDescriptor
808+
stackSize: 1 + (['D', 'J'].includes(fetchedFieldTypeDescriptor) ? 1 : 0),
809+
resultType: fetchedFieldTypeDescriptor
805810
}
806811
} else {
807812
cg.code.push(
@@ -917,14 +922,19 @@ class CodeGenerator {
917922
}
918923

919924
methodNode.methodHeader.formalParameterList.forEach(p => {
920-
this.symbolTable.insertVariableInfo({
925+
const paramInfo = {
921926
name: p.identifier,
922927
accessFlags: 0,
923928
index: this.maxLocals,
924929
typeName: p.unannType,
925930
typeDescriptor: this.symbolTable.generateFieldDescriptor(p.unannType)
926-
})
927-
this.maxLocals++
931+
}
932+
this.symbolTable.insertVariableInfo(paramInfo)
933+
if (['D', 'J'].includes(paramInfo.typeDescriptor)) {
934+
this.maxLocals += 2
935+
} else {
936+
this.maxLocals++
937+
}
928938
})
929939

930940
if (methodNode.methodHeader.identifier === '<init>') {

0 commit comments

Comments
 (0)