From dbfe682aa6e04ec598b8bc40630d126f477dc8a0 Mon Sep 17 00:00:00 2001 From: 1001mei <77192251+1001mei@users.noreply.github.com> Date: Sun, 8 Oct 2023 21:54:09 +0800 Subject: [PATCH 01/20] Standardize constant pool manager api --- src/compiler/compiler.ts | 40 +++++---------------- src/compiler/constant-pool-manager.ts | 50 ++++++++++++++++++++------- 2 files changed, 47 insertions(+), 43 deletions(-) diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 2c98bbd..f789574 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -35,10 +35,10 @@ export class Compiler { private compileClass(classNode: ClassDeclaration): ClassFile { const parentClassName = "java/lang/Object"; const className = classNode.typeIdentifier; - this.addMethodrefInfo(parentClassName, "", "()V"); - const superClassIndex = this.addClassInfo(parentClassName); - const thisClassIndex = this.addClassInfo(className); - this.addUtf8Info("Code"); + this.constantPoolManager.indexMethodrefInfo(parentClassName, "", "()V"); + const superClassIndex = this.constantPoolManager.indexClassInfo(parentClassName); + const thisClassIndex = this.constantPoolManager.indexClassInfo(className); + this.constantPoolManager.indexUtf8Info("Code"); classNode.classBody.forEach(m => this.compileMethod(m)); const constantPool = this.constantPoolManager.getPool(); @@ -62,37 +62,15 @@ export class Compiler { }; } - private addUtf8Info(value: string) { - return this.constantPoolManager.addUtf8Info({ value: value }); - } - - private addClassInfo(className: string) { - return this.constantPoolManager.addClassInfo({ - name: { value: className } - }); - } - - private addMethodrefInfo(className: string, methodName: string, descriptor: string) { - return this.constantPoolManager.addMethodrefInfo({ - class: { - name: { value: className, } - }, - nameAndType: { - name: { value: methodName }, - descriptor: { value: descriptor }, - } - }); - } - private compileMethod(methodNode: MethodDeclaration) { const header = methodNode.methodHeader; const body = methodNode.methodBody; - const methodName = header.methodDeclarator.identifier; - const params = header.methodDeclarator.formalParameterList; + const methodName = header.identifier; + const params = header.formalParameterList; - const nameIndex = this.addUtf8Info(methodName); + const nameIndex = this.constantPoolManager.indexUtf8Info(methodName); const descriptor = generateMethodDescriptor(params, header.result); - const descriptorIndex = this.addUtf8Info(descriptor); + const descriptorIndex = this.constantPoolManager.indexUtf8Info(descriptor); const attributes: Array = []; attributes.push(this.addCodeAttribute(body)); @@ -120,7 +98,7 @@ export class Compiler { const attributeLength = 12 + code.length + 8 * exceptionTable.length + attributes.map(attr => attr.attributeLength + 6).reduce((acc, val) => acc + val, 0); return { - attributeNameIndex: this.addUtf8Info("Code"), + attributeNameIndex: this.constantPoolManager.indexUtf8Info("Code"), attributeLength: attributeLength, maxStack: maxStack, maxLocals: maxLocals, diff --git a/src/compiler/constant-pool-manager.ts b/src/compiler/constant-pool-manager.ts index 3dfc7cc..add9518 100644 --- a/src/compiler/constant-pool-manager.ts +++ b/src/compiler/constant-pool-manager.ts @@ -32,28 +32,54 @@ export class ConstantPoolManager { return this.constantPool; } - addUtf8Info(value: ConstantUtf8Value) { - return this.writeIfAbsent(CONSTANT_TAG.Utf8, value); + indexUtf8Info(value: string) { + return this.writeIfAbsent(CONSTANT_TAG.Utf8, { + value: value + }); } - addClassInfo(value: ConstantClassValue) { - return this.writeIfAbsent(CONSTANT_TAG.Class, value); + indexClassInfo(className: string) { + return this.writeIfAbsent(CONSTANT_TAG.Class, { + name: { value: className } + }); } - addStringInfo(value: ConstantStringValue) { - return this.writeIfAbsent(CONSTANT_TAG.String, value); + indexStringInfo(string: string) { + return this.writeIfAbsent(CONSTANT_TAG.String, { + string: { value: string } + }); } - addNameAndTypeInfo(value: ConstantNameAndTypeValue) { - return this.writeIfAbsent(CONSTANT_TAG.NameAndType, value); + indexMethodrefInfo(className: string, methodName: string, descriptor: string) { + return this.writeIfAbsent(CONSTANT_TAG.Methodref, { + class: { + name: { value: className, } + }, + nameAndType: { + name: { value: methodName }, + descriptor: { value: descriptor }, + } + }); } - addFieldrefInfo(value: ConstantFieldrefValue) { - return this.writeIfAbsent(CONSTANT_TAG.Fieldref, value); + indexNameAndTypeInfo(name: string, type: string) { + return this.writeIfAbsent(CONSTANT_TAG.NameAndType, { + name: { value: name }, + descriptor: { value: type } + }); } - addMethodrefInfo(value: ConstantMethodrefValue) { - return this.writeIfAbsent(CONSTANT_TAG.Methodref, value); + + private addUtf8Info(value: ConstantUtf8Value) { + return this.writeIfAbsent(CONSTANT_TAG.Utf8, value); + } + + private addClassInfo(value: ConstantClassValue) { + return this.writeIfAbsent(CONSTANT_TAG.Class, value); + } + + private addNameAndTypeInfo(value: ConstantNameAndTypeValue) { + return this.writeIfAbsent(CONSTANT_TAG.NameAndType, value); } private writeIfAbsent(tag: CONSTANT_TAG, value: ConstantTypeValue) { From 118b09450ac6ada9fe2f92bbe1a255f396a7ca63 Mon Sep 17 00:00:00 2001 From: 1001mei <77192251+1001mei@users.noreply.github.com> Date: Mon, 9 Oct 2023 11:49:31 +0800 Subject: [PATCH 02/20] Update ast types --- src/ast/types/blocks-and-statements.ts | 31 +++++++++++++++++++++----- src/ast/types/classes.ts | 11 ++++----- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/ast/types/blocks-and-statements.ts b/src/ast/types/blocks-and-statements.ts index 3c11431..46e3149 100644 --- a/src/ast/types/blocks-and-statements.ts +++ b/src/ast/types/blocks-and-statements.ts @@ -1,12 +1,25 @@ import { BaseNode } from "./ast"; import { Identifier, UnannType } from "./classes"; -export type BlockStatement = LocalVariableDeclarationStatement; +export interface Block extends BaseNode { + blockStatements: Array +} +export type BlockStatement = LocalVariableDeclarationStatement | Statement; export interface LocalVariableDeclarationStatement extends BaseNode { localVariableType: LocalVariableType; variableDeclarationList: VariableDeclarator; } +export type Statement = StatementWithoutTrailingSubstatement; +export type StatementWithoutTrailingSubstatement = ExpressionStatement; +export type ExpressionStatement = MethodInvocation; +export interface MethodInvocation extends BaseNode { + identifier: Identifier + argumentList: ArgumentList; +} + +export type ArgumentList = Array; + export type LocalVariableType = UnannType; export interface VariableDeclarator { @@ -15,14 +28,20 @@ export interface VariableDeclarator { } export type VariableDeclaratorId = Identifier; export type VariableInitializer = Expression; -export type Expression = Literal | BinaryExpression; - -export interface Literal extends BaseNode { - value: number -}; +export type Expression = BinaryExpression | Primary; export interface BinaryExpression extends BaseNode { operator: string; left: Expression; right: Expression; } + +export type Primary = Literal | ExpressionName; + +export interface Literal extends BaseNode { + value: string +}; + +export interface ExpressionName extends BaseNode { + name: string; +} \ No newline at end of file diff --git a/src/ast/types/classes.ts b/src/ast/types/classes.ts index 9766d70..42659c6 100644 --- a/src/ast/types/classes.ts +++ b/src/ast/types/classes.ts @@ -1,4 +1,4 @@ -import { BlockStatement } from "./blocks-and-statements"; +import { Block } from "./blocks-and-statements"; export type ClassDeclaration = NormalClassDeclaration; @@ -41,15 +41,12 @@ export type MethodModifier = export interface MethodHeader { result: Result; - methodDeclarator: MethodDeclarator; -} - -export type Result = "void"; -export interface MethodDeclarator { identifier: Identifier; formalParameterList: Array; } +export type Result = "void"; + export interface FormalParameter { unannType: UnannType; variableDeclaratorId: Identifier; @@ -58,5 +55,5 @@ export interface FormalParameter { export type UnannType = string; export type VariableDeclaratorId = Identifier; -export type MethodBody = Array; +export type MethodBody = Block; export type Identifier = string; From 02b8140645ab306cbcde5eb1d47441598c23bf26 Mon Sep 17 00:00:00 2001 From: 1001mei <77192251+1001mei@users.noreply.github.com> Date: Mon, 9 Oct 2023 11:50:19 +0800 Subject: [PATCH 03/20] Add support for println with string argument --- src/compiler/code-generator.ts | 95 +++++++++++++++++++++++++++ src/compiler/compiler-utils.ts | 10 +-- src/compiler/compiler.ts | 61 ++++++++--------- src/compiler/constant-pool-manager.ts | 12 ++++ src/compiler/symbol-table.ts | 77 ++++++++++++++++++++++ 5 files changed, 214 insertions(+), 41 deletions(-) create mode 100644 src/compiler/code-generator.ts create mode 100644 src/compiler/symbol-table.ts diff --git a/src/compiler/code-generator.ts b/src/compiler/code-generator.ts new file mode 100644 index 0000000..436c0e6 --- /dev/null +++ b/src/compiler/code-generator.ts @@ -0,0 +1,95 @@ +import { OPCODE } from "../ClassFile/constants/instructions"; +import { ExceptionHandler, AttributeInfo } from "../ClassFile/types/attributes"; +import { BaseNode } from "../ast/types/ast"; +import { MethodInvocation, Literal, Block } from "../ast/types/blocks-and-statements"; +import { MethodDeclaration } from "../ast/types/classes"; +import { ConstantPoolManager } from "./constant-pool-manager"; +import { SymbolTable, SymbolType } from "./symbol-table" + +const codeGenerators: { [type: string]: (node: BaseNode, cg: CodeGenerator) => number } = { + Block: (node: BaseNode, cg: CodeGenerator) => { + const n = node as Block; + let maxStack = 0; + n.blockStatements.forEach(x => { + maxStack = Math.max(maxStack, codeGenerators[x.kind](x, cg)); + }) + return maxStack; + }, + + MethodInvocation: (node: BaseNode, cg: CodeGenerator) => { + const n = node as MethodInvocation; + let maxStack = 1; + + const { parentClassName: p1, typeDescriptor: t1 } = cg.symbolTable.query("out", SymbolType.CLASS); + const { parentClassName: p2, typeDescriptor: t2 } = cg.symbolTable.query("println", SymbolType.CLASS); + const out = cg.constantPoolManager.indexFieldrefInfo(p1 as string, "out", t1 as string); + const println = cg.constantPoolManager.indexMethodrefInfo(p2 as string, "println", t2 as string); + + cg.code.push(OPCODE.GETSTATIC, 0, out); + n.argumentList.forEach((x, i) => { + maxStack = Math.max(maxStack, i + 1 + codeGenerators[x.kind](x, cg)); + }) + cg.code.push(OPCODE.INVOKEVIRTUAL, 0, println); + return maxStack; + }, + + StringLiteral: (node: BaseNode, cg: CodeGenerator) => { + const n = node as Literal; + const strIdx = cg.constantPoolManager.indexStringInfo(n.value); + cg.code.push(OPCODE.LDC, strIdx); + return 1; + } +} + +class CodeGenerator { + symbolTable: SymbolTable; + constantPoolManager: ConstantPoolManager; + maxLocals = 0; + code: number[] = []; + + constructor(symbolTable: SymbolTable, constantPoolManager: ConstantPoolManager) { + this.symbolTable = symbolTable; + this.constantPoolManager = constantPoolManager; + } + + generateCode(methodNode: MethodDeclaration) { + this.symbolTable.extend(); + methodNode.methodHeader.formalParameterList.forEach(p => { + this.symbolTable.insert(p.variableDeclaratorId, SymbolType.VARIABLE, { + index: this.maxLocals + }); + this.maxLocals++; + }); + + const { methodBody } = methodNode; + const maxStack = Math.max(this.maxLocals, codeGenerators[methodBody.kind](methodBody, this)); + const exceptionTable: Array = []; + const attributes: Array = []; + + this.code.push(OPCODE.RETURN); + const codeBuf = new Uint8Array(this.code).buffer; + const dataView = new DataView(codeBuf); + this.code.forEach((x, i) => dataView.setUint8(i, x)); + + const attributeLength = 12 + this.code.length + 8 * exceptionTable.length + + attributes.map(attr => attr.attributeLength + 6).reduce((acc, val) => acc + val, 0); + return { + attributeNameIndex: this.constantPoolManager.indexUtf8Info("Code"), + attributeLength: attributeLength, + maxStack: maxStack, + maxLocals: this.maxLocals, + codeLength: this.code.length, + code: dataView, + exceptionTableLength: exceptionTable.length, + exceptionTable: exceptionTable, + attributesCount: attributes.length, + attributes: attributes + } + } +} + +export function generateCode(symbolTable: SymbolTable, constantPoolManager: ConstantPoolManager, + methodNode: MethodDeclaration) { + const codeGenerator = new CodeGenerator(symbolTable, constantPoolManager); + return codeGenerator.generateCode(methodNode); +} diff --git a/src/compiler/compiler-utils.ts b/src/compiler/compiler-utils.ts index da8de24..784e57e 100644 --- a/src/compiler/compiler-utils.ts +++ b/src/compiler/compiler-utils.ts @@ -1,6 +1,6 @@ import { ACCESS_FLAGS } from "../ClassFile/types"; import { METHOD_FLAGS } from "../ClassFile/types/methods"; -import { ClassModifier, FormalParameter, MethodModifier, UnannType } from "../ast/types/classes"; +import { ClassModifier, MethodModifier, UnannType } from "../ast/types/classes"; const typeMap = new Map([ ['byte', 'B'], @@ -27,16 +27,12 @@ export function generateFieldDescriptor(typeName: UnannType) { typeName = typeName.slice(0, last); if (typeName === "String") { typeName = "java/lang/String"; - } else if (typeName === "System") { - typeName = "java/lang/System"; - } else if (typeName === "PrintStream") { - typeName = "java/io/PrintStream"; } return "[".repeat(dim) + (typeMap.has(typeName) ? typeMap.get(typeName) : 'L' + typeName + ';'); } -export function generateMethodDescriptor(params: Array, result: string) { - const paramsDescriptor = params.map(p => generateFieldDescriptor(p.unannType)).join(","); +export function generateMethodDescriptor(paramsType: Array, result: string) { + const paramsDescriptor = paramsType.map(generateFieldDescriptor).join(","); const resultDescriptor = generateFieldDescriptor(result); return '(' + paramsDescriptor + ')' + resultDescriptor; diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index f789574..d772995 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -1,17 +1,25 @@ import { ClassFile } from "../ClassFile/types"; import { AST } from "../ast/types/packages-and-modules"; -import { ClassDeclaration, MethodBody, MethodDeclaration } from "../ast/types/classes"; -import { AttributeInfo, CodeAttribute, ExceptionHandler } from "../ClassFile/types/attributes"; +import { ClassDeclaration, MethodDeclaration } from "../ast/types/classes"; +import { AttributeInfo } from "../ClassFile/types/attributes"; import { FieldInfo } from "../ClassFile/types/fields"; import { MethodInfo } from "../ClassFile/types/methods"; import { ConstantPoolManager } from "./constant-pool-manager"; -import { generateClassAccessFlags, generateMethodAccessFlags, generateMethodDescriptor } from "./compiler-utils"; +import { + generateClassAccessFlags, + generateFieldDescriptor, + generateMethodAccessFlags, + generateMethodDescriptor +} from "./compiler-utils"; +import { SymbolTable, SymbolType } from "./symbol-table"; +import { generateCode } from "./code-generator"; const MAGIC = 0xcafebabe; const MINOR_VERSION = 0; const MAJOR_VERSION = 61; export class Compiler { + private symbolTable: SymbolTable; private constantPoolManager: ConstantPoolManager; private interfaces: Array; private fields: Array; @@ -19,11 +27,24 @@ export class Compiler { private attributes: Array; constructor() { + this.symbolTable = new SymbolTable(); this.constantPoolManager = new ConstantPoolManager(); this.interfaces = []; this.fields = []; this.methods = []; this.attributes = []; + this.setup(); + } + + private setup() { + this.symbolTable.insert("out", SymbolType.CLASS, { + parentClassName: "java/lang/System", + typeDescriptor: generateFieldDescriptor("java/io/PrintStream") + }); + this.symbolTable.insert("println", SymbolType.CLASS, { + parentClassName: "java/io/PrintStream", + typeDescriptor: generateMethodDescriptor(["java/lang/String"], "void") + }); } compile(ast: AST) { @@ -64,16 +85,16 @@ export class Compiler { private compileMethod(methodNode: MethodDeclaration) { const header = methodNode.methodHeader; - const body = methodNode.methodBody; const methodName = header.identifier; const params = header.formalParameterList; const nameIndex = this.constantPoolManager.indexUtf8Info(methodName); - const descriptor = generateMethodDescriptor(params, header.result); + const descriptor = generateMethodDescriptor(params.map(x => x.unannType), header.result); const descriptorIndex = this.constantPoolManager.indexUtf8Info(descriptor); const attributes: Array = []; - attributes.push(this.addCodeAttribute(body)); + attributes.push(generateCode(this.symbolTable, this.constantPoolManager, methodNode)); + this.methods.push({ accessFlags: generateMethodAccessFlags(methodNode.methodModifier), nameIndex: nameIndex, @@ -82,32 +103,4 @@ export class Compiler { attributes: attributes }); } - - private addCodeAttribute(block: MethodBody): CodeAttribute { - let maxStack = 0; - let maxLocals = 1; - const code: number[] = []; - const exceptionTable: Array = []; - const attributes: Array = []; - - code.push(0xb1); - const codeBuf = new Uint8Array(code).buffer; - const dataView = new DataView(codeBuf); - code.forEach((x, i) => dataView.setUint8(i, x)); - - const attributeLength = 12 + code.length + 8 * exceptionTable.length + - attributes.map(attr => attr.attributeLength + 6).reduce((acc, val) => acc + val, 0); - return { - attributeNameIndex: this.constantPoolManager.indexUtf8Info("Code"), - attributeLength: attributeLength, - maxStack: maxStack, - maxLocals: maxLocals, - codeLength: code.length, - code: dataView, - exceptionTableLength: exceptionTable.length, - exceptionTable: exceptionTable, - attributesCount: attributes.length, - attributes: attributes - } - } } diff --git a/src/compiler/constant-pool-manager.ts b/src/compiler/constant-pool-manager.ts index add9518..4d00a4c 100644 --- a/src/compiler/constant-pool-manager.ts +++ b/src/compiler/constant-pool-manager.ts @@ -50,6 +50,18 @@ export class ConstantPoolManager { }); } + indexFieldrefInfo(className: string, fieldName: string, descriptor: string) { + return this.writeIfAbsent(CONSTANT_TAG.Fieldref, { + class: { + name: { value: className, } + }, + nameAndType: { + name: { value: fieldName }, + descriptor: { value: descriptor }, + } + }); + } + indexMethodrefInfo(className: string, methodName: string, descriptor: string) { return this.writeIfAbsent(CONSTANT_TAG.Methodref, { class: { diff --git a/src/compiler/symbol-table.ts b/src/compiler/symbol-table.ts new file mode 100644 index 0000000..fae0acb --- /dev/null +++ b/src/compiler/symbol-table.ts @@ -0,0 +1,77 @@ +export type Symbol = { + name: string, + type: SymbolType +}; + +export enum SymbolType { + CLASS, + VARIABLE +} + +export interface SymbolInfo { + index?: number, + parentClassName?: string, + typeDescriptor?: string +}; + +type Table = Map; + +export class SymbolTable { + private tables: Array; + private curTable: Table; + private curIdx: number; + + constructor() { + this.tables = [this.getNewTable()]; + this.curTable = this.tables[0]; + this.curIdx = 0; + } + + private getNewTable() { + return new Map(); + } + + extend() { + const table = this.getNewTable(); + this.tables.push(table); + this.curTable = table; + this.curIdx++; + } + + teardown() { + this.tables.pop(); + this.curIdx--; + this.curTable = this.tables[this.curIdx]; + } + + insert(name: string, type: SymbolType, info: SymbolInfo) { + const symbol: Symbol = { + name: name, + type: type + }; + const key = JSON.stringify(symbol); + + if (this.curTable.has(key)) { + throw new Error("Same symbol already exists in the table"); + } + + this.curTable.set(key, info); + } + + query(name: string, type: SymbolType): SymbolInfo { + const symbol: Symbol = { + name: name, + type: type + }; + const key = JSON.stringify(symbol); + + for (let i = this.curIdx; i >= 0; i--) { + const table = this.tables[i]; + if (table.has(key)) { + return table.get(key) as SymbolInfo; + } + } + + throw new Error("Symbol " + name + " with type " + type + " is undefined"); + } +} \ No newline at end of file From ed4ca46d3b47f5416a7fd13c83e4522f52e9946b Mon Sep 17 00:00:00 2001 From: 1001mei <77192251+1001mei@users.noreply.github.com> Date: Mon, 9 Oct 2023 13:47:32 +0800 Subject: [PATCH 04/20] Update FormalParameter type of ast --- src/ast/types/classes.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ast/types/classes.ts b/src/ast/types/classes.ts index 42659c6..9a86544 100644 --- a/src/ast/types/classes.ts +++ b/src/ast/types/classes.ts @@ -1,3 +1,4 @@ +import { BaseNode } from "./ast"; import { Block } from "./blocks-and-statements"; export type ClassDeclaration = NormalClassDeclaration; @@ -47,9 +48,9 @@ export interface MethodHeader { export type Result = "void"; -export interface FormalParameter { +export interface FormalParameter extends BaseNode { unannType: UnannType; - variableDeclaratorId: Identifier; + identifier: Identifier; } export type UnannType = string; From c60f6e7c4f6eef70054b9023d6ceea22e49e0e64 Mon Sep 17 00:00:00 2001 From: 1001mei <77192251+1001mei@users.noreply.github.com> Date: Mon, 9 Oct 2023 13:48:22 +0800 Subject: [PATCH 05/20] Add support for integer arithmetic expression --- src/compiler/code-generator.ts | 35 +++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/src/compiler/code-generator.ts b/src/compiler/code-generator.ts index 436c0e6..b5e0647 100644 --- a/src/compiler/code-generator.ts +++ b/src/compiler/code-generator.ts @@ -1,11 +1,25 @@ import { OPCODE } from "../ClassFile/constants/instructions"; import { ExceptionHandler, AttributeInfo } from "../ClassFile/types/attributes"; import { BaseNode } from "../ast/types/ast"; -import { MethodInvocation, Literal, Block } from "../ast/types/blocks-and-statements"; +import { MethodInvocation, Literal, Block, BinaryExpression } from "../ast/types/blocks-and-statements"; import { MethodDeclaration } from "../ast/types/classes"; import { ConstantPoolManager } from "./constant-pool-manager"; import { SymbolTable, SymbolType } from "./symbol-table" +const opToOpcode: { [type: string]: OPCODE } = { + "+": OPCODE.IADD, + "-": OPCODE.ISUB, + "*": OPCODE.IMUL, + "/": OPCODE.IDIV, + "%": OPCODE.IREM, + "|": OPCODE.IOR, + "&": OPCODE.IAND, + "^": OPCODE.IXOR, + "<<": OPCODE.ISHL, + ">>": OPCODE.ISHR, + ">>>": OPCODE.IUSHR, +}; + const codeGenerators: { [type: string]: (node: BaseNode, cg: CodeGenerator) => number } = { Block: (node: BaseNode, cg: CodeGenerator) => { const n = node as Block; @@ -23,7 +37,8 @@ const codeGenerators: { [type: string]: (node: BaseNode, cg: CodeGenerator) => n const { parentClassName: p1, typeDescriptor: t1 } = cg.symbolTable.query("out", SymbolType.CLASS); const { parentClassName: p2, typeDescriptor: t2 } = cg.symbolTable.query("println", SymbolType.CLASS); const out = cg.constantPoolManager.indexFieldrefInfo(p1 as string, "out", t1 as string); - const println = cg.constantPoolManager.indexMethodrefInfo(p2 as string, "println", t2 as string); + const println = cg.constantPoolManager.indexMethodrefInfo( + p2 as string, "println", n.argumentList[0].kind === "StringLiteral" ? t2 as string : "(I)V"); cg.code.push(OPCODE.GETSTATIC, 0, out); n.argumentList.forEach((x, i) => { @@ -33,11 +48,25 @@ const codeGenerators: { [type: string]: (node: BaseNode, cg: CodeGenerator) => n return maxStack; }, + BinaryExpression: (node: BaseNode, cg: CodeGenerator) => { + const { left: left, right: right, operator: op } = node as BinaryExpression; + const lsize = codeGenerators[left.kind](left, cg); + const rsize = 1 + codeGenerators[right.kind](right, cg); + cg.code.push(opToOpcode[op]); + return Math.max(lsize, rsize); + }, + StringLiteral: (node: BaseNode, cg: CodeGenerator) => { const n = node as Literal; const strIdx = cg.constantPoolManager.indexStringInfo(n.value); cg.code.push(OPCODE.LDC, strIdx); return 1; + }, + + IntegerLiteral: (node: BaseNode, cg: CodeGenerator) => { + const n = node as Literal; + cg.code.push(OPCODE.BIPUSH, parseInt(n.value)); + return 1; } } @@ -55,7 +84,7 @@ class CodeGenerator { generateCode(methodNode: MethodDeclaration) { this.symbolTable.extend(); methodNode.methodHeader.formalParameterList.forEach(p => { - this.symbolTable.insert(p.variableDeclaratorId, SymbolType.VARIABLE, { + this.symbolTable.insert(p.identifier, SymbolType.VARIABLE, { index: this.maxLocals }); this.maxLocals++; From 2f2be95df808e5b6bfcf79eab3f77d2d4c7aa80a Mon Sep 17 00:00:00 2001 From: 1001mei <77192251+1001mei@users.noreply.github.com> Date: Mon, 9 Oct 2023 15:47:05 +0800 Subject: [PATCH 06/20] Add Assignment interface for ast types --- src/ast/types/blocks-and-statements.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/ast/types/blocks-and-statements.ts b/src/ast/types/blocks-and-statements.ts index 46e3149..873cbc4 100644 --- a/src/ast/types/blocks-and-statements.ts +++ b/src/ast/types/blocks-and-statements.ts @@ -7,7 +7,7 @@ export interface Block extends BaseNode { export type BlockStatement = LocalVariableDeclarationStatement | Statement; export interface LocalVariableDeclarationStatement extends BaseNode { localVariableType: LocalVariableType; - variableDeclarationList: VariableDeclarator; + variableDeclaratorList: Array; } export type Statement = StatementWithoutTrailingSubstatement; @@ -44,4 +44,12 @@ export interface Literal extends BaseNode { export interface ExpressionName extends BaseNode { name: string; -} \ No newline at end of file +} + +export interface Assignment extends BaseNode { + left: LeftHandSide; + operator: string; + right: Expression; +} + +export type LeftHandSide = ExpressionName; \ No newline at end of file From 0e29d443b558fa546e761b944076246e35fce756 Mon Sep 17 00:00:00 2001 From: 1001mei <77192251+1001mei@users.noreply.github.com> Date: Mon, 9 Oct 2023 15:47:44 +0800 Subject: [PATCH 07/20] Add support for int variable declaration --- src/compiler/code-generator.ts | 58 +++++++++++++++++++++++++-- src/compiler/constant-pool-manager.ts | 37 ++++++++++++----- src/compiler/constant-value-types.ts | 32 +++++++++------ 3 files changed, 101 insertions(+), 26 deletions(-) diff --git a/src/compiler/code-generator.ts b/src/compiler/code-generator.ts index b5e0647..156405b 100644 --- a/src/compiler/code-generator.ts +++ b/src/compiler/code-generator.ts @@ -1,7 +1,15 @@ import { OPCODE } from "../ClassFile/constants/instructions"; import { ExceptionHandler, AttributeInfo } from "../ClassFile/types/attributes"; import { BaseNode } from "../ast/types/ast"; -import { MethodInvocation, Literal, Block, BinaryExpression } from "../ast/types/blocks-and-statements"; +import { + MethodInvocation, + Literal, + Block, + BinaryExpression, + LocalVariableDeclarationStatement, + ExpressionName, + Assignment +} from "../ast/types/blocks-and-statements"; import { MethodDeclaration } from "../ast/types/classes"; import { ConstantPoolManager } from "./constant-pool-manager"; import { SymbolTable, SymbolType } from "./symbol-table" @@ -30,6 +38,21 @@ const codeGenerators: { [type: string]: (node: BaseNode, cg: CodeGenerator) => n return maxStack; }, + LocalVariableDeclarationStatement: (node: BaseNode, cg: CodeGenerator) => { + let maxStack = 0; + const { variableDeclaratorList: lst } = node as LocalVariableDeclarationStatement; + lst.forEach(v => { + const { variableDeclaratorId: identifier, variableInitializer: vi } = v; + const curIdx = cg.maxLocals++; + cg.symbolTable.insert(identifier, SymbolType.VARIABLE, { index: curIdx }); + if (vi) { + maxStack = Math.max(maxStack, codeGenerators[vi.kind](vi, cg)); + cg.code.push(OPCODE.ISTORE, curIdx); + } + }); + return maxStack; + }, + MethodInvocation: (node: BaseNode, cg: CodeGenerator) => { const n = node as MethodInvocation; let maxStack = 1; @@ -48,6 +71,18 @@ const codeGenerators: { [type: string]: (node: BaseNode, cg: CodeGenerator) => n return maxStack; }, + Assignment: (node: BaseNode, cg: CodeGenerator) => { + const { left: left, operator: op, right: right } = node as Assignment; + let maxStack = op === "=" ? 0 : codeGenerators[left.kind](left, cg); + codeGenerators[right.kind](right, cg); + if (op !== "=") { + cg.code.push(opToOpcode[op.substring(0, op.length - 1)]); + } + const { index: idx } = cg.symbolTable.query(left.name, SymbolType.VARIABLE); + cg.code.push(OPCODE.ISTORE, idx as number); + return maxStack; + }, + BinaryExpression: (node: BaseNode, cg: CodeGenerator) => { const { left: left, right: right, operator: op } = node as BinaryExpression; const lsize = codeGenerators[left.kind](left, cg); @@ -56,6 +91,13 @@ const codeGenerators: { [type: string]: (node: BaseNode, cg: CodeGenerator) => n return Math.max(lsize, rsize); }, + ExpressionName: (node: BaseNode, cg: CodeGenerator) => { + const { name: name } = node as ExpressionName; + const { index: idx } = cg.symbolTable.query(name, SymbolType.VARIABLE); + cg.code.push(OPCODE.ILOAD, idx as number); + return 1; + }, + StringLiteral: (node: BaseNode, cg: CodeGenerator) => { const n = node as Literal; const strIdx = cg.constantPoolManager.indexStringInfo(n.value); @@ -64,8 +106,16 @@ const codeGenerators: { [type: string]: (node: BaseNode, cg: CodeGenerator) => n }, IntegerLiteral: (node: BaseNode, cg: CodeGenerator) => { - const n = node as Literal; - cg.code.push(OPCODE.BIPUSH, parseInt(n.value)); + const { value: value } = node as Literal; + const n = parseInt(value); + if (-128 <= n && n < 128) { + cg.code.push(OPCODE.BIPUSH, n); + } else if (-32768 <= n && n < 32768) { + cg.code.push(OPCODE.SIPUSH, n >> 8, n & 0xff); + } else { + const idx = cg.constantPoolManager.indexIntegerInfo(n); + cg.code.push(OPCODE.LDC, idx); + } return 1; } } @@ -102,6 +152,8 @@ class CodeGenerator { const attributeLength = 12 + this.code.length + 8 * exceptionTable.length + attributes.map(attr => attr.attributeLength + 6).reduce((acc, val) => acc + val, 0); + this.symbolTable.teardown(); + return { attributeNameIndex: this.constantPoolManager.indexUtf8Info("Code"), attributeLength: attributeLength, diff --git a/src/compiler/constant-pool-manager.ts b/src/compiler/constant-pool-manager.ts index 4d00a4c..b0440d2 100644 --- a/src/compiler/constant-pool-manager.ts +++ b/src/compiler/constant-pool-manager.ts @@ -3,6 +3,7 @@ import { ConstantInfo } from "../ClassFile/types/constants"; import { ConstantClassValue, ConstantFieldrefValue, + ConstantIntegerValue, ConstantMethodrefValue, ConstantNameAndTypeValue, ConstantStringValue, @@ -38,6 +39,12 @@ export class ConstantPoolManager { }); } + indexIntegerInfo(value: number) { + return this.writeIfAbsent(CONSTANT_TAG.Integer, { + value: value + }) + } + indexClassInfo(className: string) { return this.writeIfAbsent(CONSTANT_TAG.Class, { name: { value: className } @@ -125,6 +132,9 @@ export class ConstantPoolManager { case CONSTANT_TAG.Utf8: this.writeUtf8Info(task.value as ConstantUtf8Value); break; + case CONSTANT_TAG.Integer: + this.writeIntegerInfo(task.value as ConstantIntegerValue); + break; case CONSTANT_TAG.Class: this.writeClassInfo(task.value as ConstantClassValue); break; @@ -152,6 +162,13 @@ export class ConstantPoolManager { }); } + private writeIntegerInfo(val: ConstantIntegerValue) { + this.constantPool.push({ + tag: CONSTANT_TAG.Integer, + value: val.value, + }); + } + private writeClassInfo(val: ConstantClassValue) { const nameIndex = this.addUtf8Info(val.name); this.constantPool.push({ @@ -168,16 +185,6 @@ export class ConstantPoolManager { }); } - private writeNameAndTypeInfo(val: ConstantNameAndTypeValue) { - const nameIndex = this.addUtf8Info(val.name); - const descriptorIndex = this.addUtf8Info(val.descriptor); - this.constantPool.push({ - tag: CONSTANT_TAG.NameAndType, - nameIndex: nameIndex, - descriptorIndex: descriptorIndex, - }); - } - private writeFieldrefInfo(val: ConstantFieldrefValue) { const classIndex = this.addClassInfo(val.class); const nameAndTypeIndex = this.addNameAndTypeInfo(val.nameAndType); @@ -197,4 +204,14 @@ export class ConstantPoolManager { nameAndTypeIndex: nameAndTypeIndex, }) } + + private writeNameAndTypeInfo(val: ConstantNameAndTypeValue) { + const nameIndex = this.addUtf8Info(val.name); + const descriptorIndex = this.addUtf8Info(val.descriptor); + this.constantPool.push({ + tag: CONSTANT_TAG.NameAndType, + nameIndex: nameIndex, + descriptorIndex: descriptorIndex, + }); + } } diff --git a/src/compiler/constant-value-types.ts b/src/compiler/constant-value-types.ts index 0b3e6c1..6a5e17c 100644 --- a/src/compiler/constant-value-types.ts +++ b/src/compiler/constant-value-types.ts @@ -1,13 +1,25 @@ +export interface ConstantUtf8Value { + value: string; +} + +export interface ConstantIntegerValue { + value: number; +} + export interface ConstantClassValue { name: ConstantUtf8Value; } -export interface ConstantMethodrefValue { +export interface ConstantStringValue { + string: ConstantUtf8Value +} + +export interface ConstantFieldrefValue { class: ConstantClassValue; nameAndType: ConstantNameAndTypeValue; } -export interface ConstantFieldrefValue { +export interface ConstantMethodrefValue { class: ConstantClassValue; nameAndType: ConstantNameAndTypeValue; } @@ -17,18 +29,12 @@ export interface ConstantNameAndTypeValue { descriptor: ConstantUtf8Value; } -export interface ConstantStringValue { - string: ConstantUtf8Value -} - -export interface ConstantUtf8Value { - value: string; -} - export type ConstantTypeValue = + | ConstantUtf8Value + | ConstantIntegerValue | ConstantClassValue - | ConstantMethodrefValue - | ConstantNameAndTypeValue | ConstantStringValue - | ConstantUtf8Value; + | ConstantFieldrefValue + | ConstantMethodrefValue + | ConstantNameAndTypeValue; From 980bda060cadaca8bbeca33f59cda764bc97c2a1 Mon Sep 17 00:00:00 2001 From: 1001mei <77192251+1001mei@users.noreply.github.com> Date: Thu, 19 Oct 2023 02:49:30 +0800 Subject: [PATCH 08/20] Add StackMapTable info for binary writer --- src/compiler/binaryWriter.ts | 45 ++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/src/compiler/binaryWriter.ts b/src/compiler/binaryWriter.ts index 49872fa..7f6a9e9 100644 --- a/src/compiler/binaryWriter.ts +++ b/src/compiler/binaryWriter.ts @@ -1,6 +1,15 @@ import { CONSTANT_TAG } from "../ClassFile/constants/constants"; import { ClassFile } from "../ClassFile/types"; -import { AttributeInfo, CodeAttribute, ExceptionHandler } from "../ClassFile/types/attributes"; +import { + AppendFrame, + AttributeInfo, + CodeAttribute, + ExceptionHandler, + ObjectVariableInfo, + StackMapTableAttribute, + UninitializedVariableInfo, + VerificationTypeInfo +} from "../ClassFile/types/attributes"; import { ConstantClassInfo, ConstantFieldrefInfo, @@ -130,6 +139,9 @@ export class BinaryWriter { case "Code": this.writeCodeAttribute(attribute as CodeAttribute); break; + case "StackMapTable": + this.writeStackMapTableAttribute(attribute as StackMapTableAttribute); + break; default: ; } } @@ -140,12 +152,37 @@ export class BinaryWriter { this.write(attribute.codeLength, u4); this.writeDataView(attribute.code); this.write(attribute.exceptionTableLength, u2); - attribute.exceptionTable.forEach(e => this.writeException(e)); + attribute.exceptionTable.forEach(e => this.writeExceptionHandler(e)); this.write(attribute.attributesCount, u2); attribute.attributes.forEach(a => this.writeAttribute(a)); } - private writeException(e: ExceptionHandler) { - e; + private writeExceptionHandler(e: ExceptionHandler) { + this.write(e.startPc, u2); + this.write(e.endPc, u2); + this.write(e.handlerPc, u2); + this.write(e.catchType, u2); + } + + private writeStackMapTableAttribute(attribute: StackMapTableAttribute) { + this.write(attribute.entries.length, u2); + attribute.entries.forEach(frame => { + const { frameType: frameType } = frame; + this.write(frameType); + if (252 <= frameType && frameType <= 254) { + const { offsetDelta: offsetDelta, locals: locals } = frame as AppendFrame; + this.write(offsetDelta, u2); + locals.forEach(l => this.writeVerificationTypeInfo(l)); + } + }); + } + + private writeVerificationTypeInfo(vtInfo: VerificationTypeInfo) { + this.write(vtInfo.tag); + if (vtInfo.tag == 7) { + this.write((vtInfo as ObjectVariableInfo).cpoolIndex, u2); + } else if (vtInfo.tag == 8) { + this.write((vtInfo as UninitializedVariableInfo).offset, u2); + } } } \ No newline at end of file From 33ea90b9c63e8b42034834a36d97dd2b9bc878c7 Mon Sep 17 00:00:00 2001 From: 1001mei <77192251+1001mei@users.noreply.github.com> Date: Thu, 19 Oct 2023 02:50:33 +0800 Subject: [PATCH 09/20] Add support for if else statement --- src/ClassFile/types/attributes.ts | 2 +- src/compiler/code-generator.ts | 128 ++++++++++++++++++++++++++++-- 2 files changed, 121 insertions(+), 9 deletions(-) diff --git a/src/ClassFile/types/attributes.ts b/src/ClassFile/types/attributes.ts index 0335e0c..bcb0ca8 100644 --- a/src/ClassFile/types/attributes.ts +++ b/src/ClassFile/types/attributes.ts @@ -101,7 +101,7 @@ export interface SameFrameExtended { export interface AppendFrame { frameType: number /* 252-254 */; offsetDelta: number; - stack: Array; + locals: Array; } export interface FullFrame { diff --git a/src/compiler/code-generator.ts b/src/compiler/code-generator.ts index 156405b..e42a74e 100644 --- a/src/compiler/code-generator.ts +++ b/src/compiler/code-generator.ts @@ -8,12 +8,18 @@ import { BinaryExpression, LocalVariableDeclarationStatement, ExpressionName, - Assignment + Assignment, + IfStatement } from "../ast/types/blocks-and-statements"; import { MethodDeclaration } from "../ast/types/classes"; import { ConstantPoolManager } from "./constant-pool-manager"; import { SymbolTable, SymbolType } from "./symbol-table" +type Label = { + offset: number; + pointedBy: number[]; +}; + const opToOpcode: { [type: string]: OPCODE } = { "+": OPCODE.IADD, "-": OPCODE.ISUB, @@ -26,13 +32,28 @@ const opToOpcode: { [type: string]: OPCODE } = { "<<": OPCODE.ISHL, ">>": OPCODE.ISHR, ">>>": OPCODE.IUSHR, + + "==": OPCODE.IF_ICMPEQ, + "!=": OPCODE.IF_ICMPNE, + "<": OPCODE.IF_ICMPLT, + "<=": OPCODE.IF_ICMPLE, + ">": OPCODE.IF_ICMPGT, + ">=": OPCODE.IF_ICMPGE, +}; + +const reverseLogicalOp: { [type: string]: OPCODE } = { + "==": OPCODE.IF_ICMPNE, + "!=": OPCODE.IF_ICMPEQ, + "<": OPCODE.IF_ICMPGE, + "<=": OPCODE.IF_ICMPGT, + ">": OPCODE.IF_ICMPLE, + ">=": OPCODE.IF_ICMPLT, }; const codeGenerators: { [type: string]: (node: BaseNode, cg: CodeGenerator) => number } = { Block: (node: BaseNode, cg: CodeGenerator) => { - const n = node as Block; let maxStack = 0; - n.blockStatements.forEach(x => { + (node as Block).blockStatements.forEach(x => { maxStack = Math.max(maxStack, codeGenerators[x.kind](x, cg)); }) return maxStack; @@ -53,6 +74,71 @@ const codeGenerators: { [type: string]: (node: BaseNode, cg: CodeGenerator) => n return maxStack; }, + IfStatement: (node: BaseNode, cg: CodeGenerator) => { + let maxStack = 1; + const { test: condition, consequent: consequent, alternate: alternate } = node as IfStatement; + + const elseLabel = cg.generateNewLabel(); + maxStack = Math.max(maxStack, codeGenerators["LogicalExpression"](condition, cg)); + maxStack = Math.max(maxStack, codeGenerators[consequent.kind](consequent, cg)); + + const endLabel = cg.generateNewLabel(); + if (alternate) { + cg.addBranchInstr(OPCODE.GOTO, endLabel); + } + + elseLabel.offset = cg.code.length; + if (alternate) { + maxStack = Math.max(maxStack, codeGenerators[alternate.kind](alternate, cg)); + endLabel.offset = cg.code.length; + } + + return maxStack; + }, + + LogicalExpression: (node: BaseNode, cg: CodeGenerator) => { + const f = (node: BaseNode, targetLabel: Label, onTrue: boolean): number => { + if (node.kind === "BooleanLiteral") { + // TODO: implement handling of boolean literal + } + + if (node.kind !== "BinaryExpression") { + return codeGenerators[node.kind](node, cg); + } + + const { left: left, right: right, operator: op } = node as BinaryExpression; + let lsize = 0; + let rsize = 0; + if (op === "&&") { + if (onTrue) { + const falseLabel = cg.generateNewLabel(); + lsize = f(left, falseLabel, false); + rsize = f(right, targetLabel, true); + falseLabel.offset = cg.code.length; + } else { + lsize = f(left, targetLabel, false); + rsize = f(right, targetLabel, false); + } + } else if (op === "||") { + if (onTrue) { + lsize = f(left, targetLabel, true); + rsize = f(right, targetLabel, true); + } else { + const falseLabel = cg.generateNewLabel(); + lsize = f(left, falseLabel, true); + rsize = f(right, targetLabel, false); + falseLabel.offset = cg.code.length; + } + } else { + lsize = f(left, targetLabel, onTrue); + rsize = f(right, targetLabel, onTrue); + cg.addBranchInstr(onTrue ? opToOpcode[op] : reverseLogicalOp[op], targetLabel); + } + return Math.max(lsize, 1 + rsize); + } + return f(node, cg.labels[cg.labels.length - 1], false); + }, + MethodInvocation: (node: BaseNode, cg: CodeGenerator) => { const n = node as MethodInvocation; let maxStack = 1; @@ -86,9 +172,9 @@ const codeGenerators: { [type: string]: (node: BaseNode, cg: CodeGenerator) => n BinaryExpression: (node: BaseNode, cg: CodeGenerator) => { const { left: left, right: right, operator: op } = node as BinaryExpression; const lsize = codeGenerators[left.kind](left, cg); - const rsize = 1 + codeGenerators[right.kind](right, cg); + const rsize = codeGenerators[right.kind](right, cg); cg.code.push(opToOpcode[op]); - return Math.max(lsize, rsize); + return Math.max(lsize, 1 + rsize); }, ExpressionName: (node: BaseNode, cg: CodeGenerator) => { @@ -99,8 +185,8 @@ const codeGenerators: { [type: string]: (node: BaseNode, cg: CodeGenerator) => n }, StringLiteral: (node: BaseNode, cg: CodeGenerator) => { - const n = node as Literal; - const strIdx = cg.constantPoolManager.indexStringInfo(n.value); + const { value: value } = node as Literal; + const strIdx = cg.constantPoolManager.indexStringInfo(value); cg.code.push(OPCODE.LDC, strIdx); return 1; }, @@ -123,7 +209,8 @@ const codeGenerators: { [type: string]: (node: BaseNode, cg: CodeGenerator) => n class CodeGenerator { symbolTable: SymbolTable; constantPoolManager: ConstantPoolManager; - maxLocals = 0; + maxLocals: number = 0; + labels: Label[] = []; code: number[] = []; constructor(symbolTable: SymbolTable, constantPoolManager: ConstantPoolManager) { @@ -131,6 +218,30 @@ class CodeGenerator { this.constantPoolManager = constantPoolManager; } + generateNewLabel(): Label { + const lable = { + offset: 0, + pointedBy: [], + }; + this.labels.push(lable); + return lable; + } + + addBranchInstr(opcode: OPCODE, label: Label) { + label.pointedBy.push(this.code.length); + this.code.push(opcode, 0, 0); + } + + resolveLabels() { + for (let label of this.labels) { + label.pointedBy.forEach(idx => { + const offset = label.offset - idx; + this.code[idx + 1] = offset >> 8; + this.code[idx + 2] = offset & 0xff; + }); + } + } + generateCode(methodNode: MethodDeclaration) { this.symbolTable.extend(); methodNode.methodHeader.formalParameterList.forEach(p => { @@ -146,6 +257,7 @@ class CodeGenerator { const attributes: Array = []; this.code.push(OPCODE.RETURN); + this.resolveLabels(); const codeBuf = new Uint8Array(this.code).buffer; const dataView = new DataView(codeBuf); this.code.forEach((x, i) => dataView.setUint8(i, x)); From fd8d2403b6c64b271ebc5ea0d9e1062b1ff27821 Mon Sep 17 00:00:00 2001 From: 1001mei <77192251+1001mei@users.noreply.github.com> Date: Thu, 19 Oct 2023 02:51:07 +0800 Subject: [PATCH 10/20] Add IfStatement to ast structure --- src/ast/types/blocks-and-statements.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/ast/types/blocks-and-statements.ts b/src/ast/types/blocks-and-statements.ts index 873cbc4..7acf957 100644 --- a/src/ast/types/blocks-and-statements.ts +++ b/src/ast/types/blocks-and-statements.ts @@ -10,9 +10,16 @@ export interface LocalVariableDeclarationStatement extends BaseNode { variableDeclaratorList: Array; } -export type Statement = StatementWithoutTrailingSubstatement; +export type Statement = StatementWithoutTrailingSubstatement | IfStatement; +export interface IfStatement extends BaseNode { + test: Expression; + consequent: Statement; + alternate: Statement; +} export type StatementWithoutTrailingSubstatement = ExpressionStatement; + export type ExpressionStatement = MethodInvocation; + export interface MethodInvocation extends BaseNode { identifier: Identifier argumentList: ArgumentList; From 16ee5c0eb0eba19af543dff4ef6c43238867a0ea Mon Sep 17 00:00:00 2001 From: 1001mei <77192251+1001mei@users.noreply.github.com> Date: Thu, 19 Oct 2023 14:39:04 +0800 Subject: [PATCH 11/20] Fix compile errors --- src/ast/astExtractor/ast-extractor.ts | 1 - src/ast/astExtractor/block-statement-extractor.ts | 12 +++++++----- src/ast/astExtractor/class-extractor.ts | 1 - src/ast/astExtractor/method-extractor.ts | 15 ++++++++------- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/ast/astExtractor/ast-extractor.ts b/src/ast/astExtractor/ast-extractor.ts index b6cffcc..955f78f 100644 --- a/src/ast/astExtractor/ast-extractor.ts +++ b/src/ast/astExtractor/ast-extractor.ts @@ -12,7 +12,6 @@ export class ASTExtractor extends BaseJavaCstVisitorWithDefaults { kind: "CompilationUnit", topLevelClassOrInterfaceDeclarations: [], }; - this.validateVisitor(); } extract(cst: CstNode): AST { diff --git a/src/ast/astExtractor/block-statement-extractor.ts b/src/ast/astExtractor/block-statement-extractor.ts index 34321c7..a99b5e8 100644 --- a/src/ast/astExtractor/block-statement-extractor.ts +++ b/src/ast/astExtractor/block-statement-extractor.ts @@ -31,7 +31,6 @@ export class BlockStatementExtractor extends BaseJavaCstVisitorWithDefaults { constructor() { super(); - this.validateVisitor(); } extract(cst: BlockStatementCstNode): BlockStatement { @@ -39,10 +38,13 @@ export class BlockStatementExtractor extends BaseJavaCstVisitorWithDefaults { return { kind: "LocalVariableDeclarationStatement", localVariableType: this.type, - variableDeclarationList: { - variableDeclaratorId: this.identifier, - variableInitializer: this.value, - }, + variableDeclaratorList: [ + { + kind: "VariableDeclarator", + variableDeclaratorId: this.identifier, + variableInitializer: this.value, + } + ], }; } diff --git a/src/ast/astExtractor/class-extractor.ts b/src/ast/astExtractor/class-extractor.ts index 27069eb..4e7e7cb 100644 --- a/src/ast/astExtractor/class-extractor.ts +++ b/src/ast/astExtractor/class-extractor.ts @@ -19,7 +19,6 @@ export class ClassExtractor extends BaseJavaCstVisitorWithDefaults { this.modifier = []; this.identifier = ''; this.body = []; - this.validateVisitor(); } extract(cst: CstNode): ClassDeclaration { diff --git a/src/ast/astExtractor/method-extractor.ts b/src/ast/astExtractor/method-extractor.ts index aa2dd75..9eacbff 100644 --- a/src/ast/astExtractor/method-extractor.ts +++ b/src/ast/astExtractor/method-extractor.ts @@ -28,7 +28,6 @@ export class MethodExtractor extends BaseJavaCstVisitorWithDefaults { this.identifier = ''; this.params = []; this.body = []; - this.validateVisitor(); } private getAndPop() { @@ -48,12 +47,13 @@ export class MethodExtractor extends BaseJavaCstVisitorWithDefaults { methodModifier: this.modifier, methodHeader: { result: "void", - methodDeclarator: { - identifier: this.identifier, - formalParameterList: this.params - } + identifier: this.identifier, + formalParameterList: this.params + }, + methodBody: { + kind: "Block", + blockStatements:this.body, }, - methodBody: this.body, }; } @@ -89,8 +89,9 @@ export class MethodExtractor extends BaseJavaCstVisitorWithDefaults { const argName = this.getAndPop(); const typeName = this.getAndPop(); this.params.push({ + kind: "FormalParameter", unannType: typeName, - variableDeclaratorId: argName, + identifier: argName, }); } From bfa98efb9fd619d5c0ff35ac70738f6a0e9986a8 Mon Sep 17 00:00:00 2001 From: 1001mei <77192251+1001mei@users.noreply.github.com> Date: Thu, 19 Oct 2023 15:04:35 +0800 Subject: [PATCH 12/20] Fix name of kind --- src/compiler/code-generator.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compiler/code-generator.ts b/src/compiler/code-generator.ts index df28ce5..f470e10 100644 --- a/src/compiler/code-generator.ts +++ b/src/compiler/code-generator.ts @@ -11,7 +11,7 @@ import { Assignment, IfStatement, StringLiteral, - IntegerLiteral + DecimalIntegerLiteral } from "../ast/types/blocks-and-statements"; import { MethodDeclaration } from "../ast/types/classes"; import { ConstantPoolManager } from "./constant-pool-manager"; @@ -200,8 +200,8 @@ const codeGenerators: { [type: string]: (node: Node, cg: CodeGenerator) => numbe return 1; }, - IntegerLiteral: (node: Node, cg: CodeGenerator) => { - const { value: value } = node as IntegerLiteral; + DecimalIntegerLiteral: (node: Node, cg: CodeGenerator) => { + const { value: value } = node as DecimalIntegerLiteral; const n = parseInt(value); if (-128 <= n && n < 128) { cg.code.push(OPCODE.BIPUSH, n); From faf7d40f10a207561c23e41f0a0baadf7fb9b14f Mon Sep 17 00:00:00 2001 From: 1001mei <77192251+1001mei@users.noreply.github.com> Date: Sun, 22 Oct 2023 18:02:24 +0800 Subject: [PATCH 13/20] Refactor binary writer --- src/compiler/{binaryWriter.ts => binary-writer.ts} | 14 +++++++++++--- src/compiler/compiler.ts | 11 ++++++----- src/compiler/utils.ts | 5 ----- 3 files changed, 17 insertions(+), 13 deletions(-) rename src/compiler/{binaryWriter.ts => binary-writer.ts} (95%) delete mode 100644 src/compiler/utils.ts diff --git a/src/compiler/binaryWriter.ts b/src/compiler/binary-writer.ts similarity index 95% rename from src/compiler/binaryWriter.ts rename to src/compiler/binary-writer.ts index 7f6a9e9..41927b7 100644 --- a/src/compiler/binaryWriter.ts +++ b/src/compiler/binary-writer.ts @@ -22,6 +22,8 @@ import { import { FieldInfo } from "../ClassFile/types/fields"; import { MethodInfo } from "../ClassFile/types/methods"; +import * as fs from "fs"; + const u1 = 1; const u2 = 2; const u4 = 4; @@ -35,7 +37,13 @@ export class BinaryWriter { this.constantPool = []; } - toBinary(classFile: ClassFile) { + writeBinary(classFile: ClassFile) { + const filename = "Main.class"; + const binary = this.toBinary(classFile); + fs.writeFileSync(filename, binary); + } + + private toBinary(classFile: ClassFile) { this.byteArray = []; this.constantPool = classFile.constantPool; @@ -62,7 +70,7 @@ export class BinaryWriter { private write(value: number, numOfBytes: number = u1) { - const bytes = []; + const bytes: Array = []; for (let i = 0; i < numOfBytes; i++) { bytes.push(value & 0xff); value >>>= 8; @@ -185,4 +193,4 @@ export class BinaryWriter { this.write((vtInfo as UninitializedVariableInfo).offset, u2); } } -} \ No newline at end of file +} diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index d772995..6e3c977 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -27,16 +27,16 @@ export class Compiler { private attributes: Array; constructor() { - this.symbolTable = new SymbolTable(); + this.setup(); + } + + private setup() { this.constantPoolManager = new ConstantPoolManager(); this.interfaces = []; this.fields = []; this.methods = []; this.attributes = []; - this.setup(); - } - - private setup() { + this.symbolTable = new SymbolTable(); this.symbolTable.insert("out", SymbolType.CLASS, { parentClassName: "java/lang/System", typeDescriptor: generateFieldDescriptor("java/io/PrintStream") @@ -48,6 +48,7 @@ export class Compiler { } compile(ast: AST) { + this.setup(); const classFiles: Array = []; ast.topLevelClassOrInterfaceDeclarations.forEach(x => classFiles.push(this.compileClass(x))); return classFiles[0]; diff --git a/src/compiler/utils.ts b/src/compiler/utils.ts deleted file mode 100644 index 4d6722b..0000000 --- a/src/compiler/utils.ts +++ /dev/null @@ -1,5 +0,0 @@ -import * as fs from "fs"; - -export function writeToFile(filename: string, binary: Uint8Array) { - fs.writeFileSync(filename, binary); -} From f116751a719193ce27d1535bd04220c1f9a620cd Mon Sep 17 00:00:00 2001 From: 1001mei <77192251+1001mei@users.noreply.github.com> Date: Sun, 22 Oct 2023 23:16:52 +0800 Subject: [PATCH 14/20] Modify api of binary writer --- src/compiler/binary-writer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/binary-writer.ts b/src/compiler/binary-writer.ts index 41927b7..a4eaf50 100644 --- a/src/compiler/binary-writer.ts +++ b/src/compiler/binary-writer.ts @@ -37,8 +37,8 @@ export class BinaryWriter { this.constantPool = []; } - writeBinary(classFile: ClassFile) { - const filename = "Main.class"; + writeBinary(classFile: ClassFile, filepath: string) { + const filename = filepath + "Main.class"; const binary = this.toBinary(classFile); fs.writeFileSync(filename, binary); } From 942087c60bbc4b49ee3412fdb2a388170d3e484b Mon Sep 17 00:00:00 2001 From: 1001mei <77192251+1001mei@users.noreply.github.com> Date: Mon, 23 Oct 2023 14:53:17 +0800 Subject: [PATCH 15/20] Add support for boolean literal --- src/compiler/code-generator.ts | 73 +++++++++++++++++++--------------- 1 file changed, 40 insertions(+), 33 deletions(-) diff --git a/src/compiler/code-generator.ts b/src/compiler/code-generator.ts index f470e10..bc5ef6b 100644 --- a/src/compiler/code-generator.ts +++ b/src/compiler/code-generator.ts @@ -100,43 +100,50 @@ const codeGenerators: { [type: string]: (node: Node, cg: CodeGenerator) => numbe LogicalExpression: (node: Node, cg: CodeGenerator) => { const f = (node: Node, targetLabel: Label, onTrue: boolean): number => { - //if (node.kind === "BooleanLiteral") { - // TODO: implement handling of boolean literal - //} - - if (node.kind !== "BinaryExpression") { - return codeGenerators[node.kind](node, cg); + if (node.kind === "Literal") { + const { literalType: { kind: kind, value: value } } = node as Literal; + const boolValue = value === "true"; + if (kind === "BooleanLiteral" && onTrue === boolValue) { + cg.addBranchInstr(OPCODE.GOTO, targetLabel); + return 0; + } } - const { left: left, right: right, operator: op } = node as BinaryExpression; - let lsize = 0; - let rsize = 0; - if (op === "&&") { - if (onTrue) { - const falseLabel = cg.generateNewLabel(); - lsize = f(left, falseLabel, false); - rsize = f(right, targetLabel, true); - falseLabel.offset = cg.code.length; - } else { - lsize = f(left, targetLabel, false); - rsize = f(right, targetLabel, false); + if (node.kind === "BinaryExpression") { + const { left: left, right: right, operator: op } = node as BinaryExpression; + let lsize = 0; + let rsize = 0; + if (op === "&&") { + if (onTrue) { + const falseLabel = cg.generateNewLabel(); + lsize = f(left, falseLabel, false); + rsize = f(right, targetLabel, true); + falseLabel.offset = cg.code.length; + } else { + lsize = f(left, targetLabel, false); + rsize = f(right, targetLabel, false); + } + return Math.max(lsize, 1 + rsize); + } else if (op === "||") { + if (onTrue) { + lsize = f(left, targetLabel, true); + rsize = f(right, targetLabel, true); + } else { + const falseLabel = cg.generateNewLabel(); + lsize = f(left, falseLabel, true); + rsize = f(right, targetLabel, false); + falseLabel.offset = cg.code.length; + } + return Math.max(lsize, 1 + rsize); + } else if (op in reverseLogicalOp) { + lsize = f(left, targetLabel, onTrue); + rsize = f(right, targetLabel, onTrue); + cg.addBranchInstr(onTrue ? opToOpcode[op] : reverseLogicalOp[op], targetLabel); + return Math.max(lsize, 1 + rsize); } - } else if (op === "||") { - if (onTrue) { - lsize = f(left, targetLabel, true); - rsize = f(right, targetLabel, true); - } else { - const falseLabel = cg.generateNewLabel(); - lsize = f(left, falseLabel, true); - rsize = f(right, targetLabel, false); - falseLabel.offset = cg.code.length; - } - } else { - lsize = f(left, targetLabel, onTrue); - rsize = f(right, targetLabel, onTrue); - cg.addBranchInstr(onTrue ? opToOpcode[op] : reverseLogicalOp[op], targetLabel); } - return Math.max(lsize, 1 + rsize); + + return codeGenerators[node.kind](node, cg); } return f(node, cg.labels[cg.labels.length - 1], false); }, From fd99b547f92edad12002209e3a511ec6a0c4252b Mon Sep 17 00:00:00 2001 From: 1001mei <77192251+1001mei@users.noreply.github.com> Date: Mon, 23 Oct 2023 16:27:11 +0800 Subject: [PATCH 16/20] Add ast types for while statements --- src/ast/types/blocks-and-statements.ts | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/ast/types/blocks-and-statements.ts b/src/ast/types/blocks-and-statements.ts index ab28a38..541743f 100644 --- a/src/ast/types/blocks-and-statements.ts +++ b/src/ast/types/blocks-and-statements.ts @@ -12,13 +12,31 @@ export interface LocalVariableDeclarationStatement { variableDeclaratorList: Array; } -export type Statement = StatementWithoutTrailingSubstatement | IfStatement; +export type Statement = StatementWithoutTrailingSubstatement | IfStatement | WhileStatement | DoStatement | ForStatement; + export interface IfStatement { kind: "IfStatement"; - test: Expression; + condition: Expression; consequent: Statement; alternate: Statement; } + +export interface WhileStatement { + kind: "WhileStatement"; + condition: Expression; + body: Statement; +} + +export interface DoStatement { + kind: "DoStatement"; + condition: Expression; + body: Statement; +} + +export interface ForStatement { + kind: "ForStatement"; +} + export type StatementWithoutTrailingSubstatement = ExpressionStatement; export type ExpressionStatement = MethodInvocation; From 530fdc8830c12ab920192e3392bef8265b4b81bd Mon Sep 17 00:00:00 2001 From: 1001mei <77192251+1001mei@users.noreply.github.com> Date: Mon, 23 Oct 2023 16:36:54 +0800 Subject: [PATCH 17/20] Add support for while statements --- src/compiler/code-generator.ts | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/src/compiler/code-generator.ts b/src/compiler/code-generator.ts index bc5ef6b..349729e 100644 --- a/src/compiler/code-generator.ts +++ b/src/compiler/code-generator.ts @@ -11,7 +11,8 @@ import { Assignment, IfStatement, StringLiteral, - DecimalIntegerLiteral + DecimalIntegerLiteral, + WhileStatement } from "../ast/types/blocks-and-statements"; import { MethodDeclaration } from "../ast/types/classes"; import { ConstantPoolManager } from "./constant-pool-manager"; @@ -76,9 +77,33 @@ const codeGenerators: { [type: string]: (node: Node, cg: CodeGenerator) => numbe return maxStack; }, + DoStatement: (node: Node, cg: CodeGenerator) => { + const { body } = node as WhileStatement; + codeGenerators[body.kind](body, cg); + + node.kind = "WhileStatement"; + return codeGenerators[node.kind](node, cg); + }, + + WhileStatement: (node: Node, cg: CodeGenerator) => { + let maxStack = 1; + const { condition, body } = node as WhileStatement; + + const startLabel = cg.generateNewLabel(); + startLabel.offset = cg.code.length; + const endLabel = cg.generateNewLabel(); + + maxStack = Math.max(maxStack, codeGenerators["LogicalExpression"](condition, cg)); + maxStack = Math.max(maxStack, codeGenerators[body.kind](body, cg)); + + cg.addBranchInstr(OPCODE.GOTO, startLabel); + endLabel.offset = cg.code.length; + return maxStack; + }, + IfStatement: (node: Node, cg: CodeGenerator) => { let maxStack = 1; - const { test: condition, consequent: consequent, alternate: alternate } = node as IfStatement; + const { condition: condition, consequent: consequent, alternate: alternate } = node as IfStatement; const elseLabel = cg.generateNewLabel(); maxStack = Math.max(maxStack, codeGenerators["LogicalExpression"](condition, cg)); @@ -208,7 +233,7 @@ const codeGenerators: { [type: string]: (node: Node, cg: CodeGenerator) => numbe }, DecimalIntegerLiteral: (node: Node, cg: CodeGenerator) => { - const { value: value } = node as DecimalIntegerLiteral; + const { value } = node as DecimalIntegerLiteral; const n = parseInt(value); if (-128 <= n && n < 128) { cg.code.push(OPCODE.BIPUSH, n); From cd84a00ebeb4decdaf50c6eb3ed00346f46f19c6 Mon Sep 17 00:00:00 2001 From: 1001mei <77192251+1001mei@users.noreply.github.com> Date: Mon, 23 Oct 2023 22:50:06 +0800 Subject: [PATCH 18/20] Refactor ast types --- src/ast/types/ast.ts | 26 ++------------------------ src/ast/types/blocks-and-statements.ts | 21 +++++++++++++++------ 2 files changed, 17 insertions(+), 30 deletions(-) diff --git a/src/ast/types/ast.ts b/src/ast/types/ast.ts index 1f60b07..73dca32 100644 --- a/src/ast/types/ast.ts +++ b/src/ast/types/ast.ts @@ -1,29 +1,13 @@ import { CompilationUnit } from "./packages-and-modules"; import { - Assignment, - BinaryExpression, Block, BlockStatement, - BooleanLiteral, - CharacterLiteral, + Expression, ExpressionName, - FloatingPointLiteral, - IntegerLiteral, Literal, LocalVariableDeclarationStatement, - NullLiteral, - StringLiteral, - UnaryExpression, } from "./blocks-and-statements"; -export interface ExpressionMap { - BinaryExpression: BinaryExpression; - Literal: Literal; - UnaryExpression: UnaryExpression; -} - -type Expression = ExpressionMap[keyof ExpressionMap]; - interface NodeMap { CompilationUnit: CompilationUnit; LocalVariableDeclarationStatement: LocalVariableDeclarationStatement; @@ -31,13 +15,7 @@ interface NodeMap { BlockStatement: BlockStatement; Expression: Expression; ExpresssionName: ExpressionName; - Assignment: Assignment; - IntegerLiteral: IntegerLiteral; - FloatingPointLiteral: FloatingPointLiteral; - BooleanLiteral: BooleanLiteral; - CharacterLiteral: CharacterLiteral; - StringLiteral: StringLiteral; - NullLiteral: NullLiteral; + Literal: Literal; } export type Node = NodeMap[keyof NodeMap]; diff --git a/src/ast/types/blocks-and-statements.ts b/src/ast/types/blocks-and-statements.ts index 541743f..2ee5c51 100644 --- a/src/ast/types/blocks-and-statements.ts +++ b/src/ast/types/blocks-and-statements.ts @@ -12,7 +12,7 @@ export interface LocalVariableDeclarationStatement { variableDeclaratorList: Array; } -export type Statement = StatementWithoutTrailingSubstatement | IfStatement | WhileStatement | DoStatement | ForStatement; +export type Statement = StatementWithoutTrailingSubstatement | IfStatement | WhileStatement | ForStatement; export interface IfStatement { kind: "IfStatement"; @@ -33,13 +33,22 @@ export interface DoStatement { body: Statement; } -export interface ForStatement { - kind: "ForStatement"; +export type ForStatement = BasicForStatement | EnhancedForStatement; +export interface BasicForStatement { + kind: "BasicForStatement"; + forInit: Array | LocalVariableDeclarationStatement; + condition: Expression; + forUpdate: Array; + body: Statement; +} + +export interface EnhancedForStatement { + kind: "EnhancedForStatement"; } -export type StatementWithoutTrailingSubstatement = ExpressionStatement; +export type StatementWithoutTrailingSubstatement = Block | ExpressionStatement | DoStatement; -export type ExpressionStatement = MethodInvocation; +export type ExpressionStatement = MethodInvocation | Assignment; export interface MethodInvocation { kind: "MethodInvocation"; @@ -61,7 +70,7 @@ export type VariableDeclaratorId = Identifier; export type VariableInitializer = Expression; export type Expression = Primary | BinaryExpression | UnaryExpression; -export type Primary = Literal | ExpressionName; +export type Primary = Literal | ExpressionName | Assignment; export interface Literal { kind: "Literal"; From f3762ad630c878a5a8843dd972c53ab8c13ba0a6 Mon Sep 17 00:00:00 2001 From: 1001mei <77192251+1001mei@users.noreply.github.com> Date: Mon, 23 Oct 2023 22:54:34 +0800 Subject: [PATCH 19/20] Add support for basic for loops --- src/compiler/code-generator.ts | 73 ++++++++++++++++++++++------------ 1 file changed, 47 insertions(+), 26 deletions(-) diff --git a/src/compiler/code-generator.ts b/src/compiler/code-generator.ts index 349729e..586cb70 100644 --- a/src/compiler/code-generator.ts +++ b/src/compiler/code-generator.ts @@ -10,9 +10,8 @@ import { ExpressionName, Assignment, IfStatement, - StringLiteral, - DecimalIntegerLiteral, - WhileStatement + WhileStatement, + BasicForStatement, } from "../ast/types/blocks-and-statements"; import { MethodDeclaration } from "../ast/types/classes"; import { ConstantPoolManager } from "./constant-pool-manager"; @@ -77,6 +76,29 @@ const codeGenerators: { [type: string]: (node: Node, cg: CodeGenerator) => numbe return maxStack; }, + BasicForStatement: (node: Node, cg: CodeGenerator) => { + let maxStack = 0; + const { forInit, condition, forUpdate, body: originalBody } = node as BasicForStatement; + + if (forInit instanceof Array) { + forInit.forEach(e => maxStack = Math.max(maxStack, codeGenerators[e.kind](e, cg))); + } else { + maxStack = Math.max(maxStack, codeGenerators[forInit.kind](forInit, cg)); + } + + const whileNode: WhileStatement = { + kind: "WhileStatement", + condition: condition, + body: { + kind: "Block", + blockStatements: [originalBody, ...forUpdate], + } + }; + maxStack = Math.max(maxStack, codeGenerators[whileNode.kind](whileNode, cg)); + + return maxStack; + }, + DoStatement: (node: Node, cg: CodeGenerator) => { const { body } = node as WhileStatement; codeGenerators[body.kind](body, cg); @@ -86,7 +108,7 @@ const codeGenerators: { [type: string]: (node: Node, cg: CodeGenerator) => numbe }, WhileStatement: (node: Node, cg: CodeGenerator) => { - let maxStack = 1; + let maxStack = 0; const { condition, body } = node as WhileStatement; const startLabel = cg.generateNewLabel(); @@ -102,7 +124,7 @@ const codeGenerators: { [type: string]: (node: Node, cg: CodeGenerator) => numbe }, IfStatement: (node: Node, cg: CodeGenerator) => { - let maxStack = 1; + let maxStack = 0; const { condition: condition, consequent: consequent, alternate: alternate } = node as IfStatement; const elseLabel = cg.generateNewLabel(); @@ -221,30 +243,29 @@ const codeGenerators: { [type: string]: (node: Node, cg: CodeGenerator) => numbe }, Literal: (node: Node, cg: CodeGenerator) => { - const { literalType: literal } = node as Literal; - return codeGenerators[literal.kind](literal, cg); - }, + const { kind, value } = (node as Literal).literalType; + switch (kind) { + case "StringLiteral": { + const strIdx = cg.constantPoolManager.indexStringInfo(value); + cg.code.push(OPCODE.LDC, strIdx); + return 1; + } + case "DecimalIntegerLiteral": { + const n = parseInt(value); + if (-128 <= n && n < 128) { + cg.code.push(OPCODE.BIPUSH, n); + } else if (-32768 <= n && n < 32768) { + cg.code.push(OPCODE.SIPUSH, n >> 8, n & 0xff); + } else { + const idx = cg.constantPoolManager.indexIntegerInfo(n); + cg.code.push(OPCODE.LDC, idx); + } + return 1; + } + } - StringLiteral: (node: Node, cg: CodeGenerator) => { - const { value: value } = node as StringLiteral; - const strIdx = cg.constantPoolManager.indexStringInfo(value); - cg.code.push(OPCODE.LDC, strIdx); return 1; }, - - DecimalIntegerLiteral: (node: Node, cg: CodeGenerator) => { - const { value } = node as DecimalIntegerLiteral; - const n = parseInt(value); - if (-128 <= n && n < 128) { - cg.code.push(OPCODE.BIPUSH, n); - } else if (-32768 <= n && n < 32768) { - cg.code.push(OPCODE.SIPUSH, n >> 8, n & 0xff); - } else { - const idx = cg.constantPoolManager.indexIntegerInfo(n); - cg.code.push(OPCODE.LDC, idx); - } - return 1; - } } class CodeGenerator { From f48524cf51f1642bca70b074dfff462d93d17292 Mon Sep 17 00:00:00 2001 From: 1001mei <77192251+1001mei@users.noreply.github.com> Date: Mon, 23 Oct 2023 23:51:23 +0800 Subject: [PATCH 20/20] Update Node type --- src/ast/types/ast.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/ast/types/ast.ts b/src/ast/types/ast.ts index 73dca32..4197e0a 100644 --- a/src/ast/types/ast.ts +++ b/src/ast/types/ast.ts @@ -3,19 +3,13 @@ import { Block, BlockStatement, Expression, - ExpressionName, - Literal, - LocalVariableDeclarationStatement, } from "./blocks-and-statements"; interface NodeMap { CompilationUnit: CompilationUnit; - LocalVariableDeclarationStatement: LocalVariableDeclarationStatement; Block: Block; BlockStatement: BlockStatement; Expression: Expression; - ExpresssionName: ExpressionName; - Literal: Literal; } export type Node = NodeMap[keyof NodeMap];