Skip to content

Commit d2517b2

Browse files
committed
Fix bug to not allow final methods to be overriden
1 parent 0bc9b53 commit d2517b2

File tree

4 files changed

+164
-10
lines changed

4 files changed

+164
-10
lines changed

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ export function runTest(program: string, expectedLines: string[]) {
3030
console.log(inspect(ast, false, null, true))
3131
}
3232

33-
const classFiles = compile(ast as AST)
34-
for (let classFile of classFiles) {
35-
binaryWriter.writeBinary(classFile, pathToTestDir)
33+
const classes = compile(ast as AST)
34+
for (let c of classes) {
35+
binaryWriter.writeBinary(c.classFile, pathToTestDir)
3636
}
3737

3838
const prevDir = process.cwd()

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

Lines changed: 134 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ const testCases: testCase[] = [
119119
}
120120
}
121121
class Child extends Parent {
122-
public void show() {} // Uncommenting should cause compilation error
122+
// public void show() {} // Uncommenting should cause compilation error
123123
}
124124
public class Main {
125125
public static void main(String[] args) {
@@ -185,6 +185,139 @@ const testCases: testCase[] = [
185185
}
186186
`,
187187
expectedLines: ['Child secret']
188+
},
189+
{
190+
comment: 'Using this to call an instance method',
191+
program: `
192+
class Self {
193+
public void print() {
194+
System.out.println("Self print");
195+
}
196+
public void callSelf() {
197+
this.print();
198+
}
199+
}
200+
public class Main {
201+
public static void main(String[] args) {
202+
Self s = new Self();
203+
s.callSelf(); // Self print
204+
}
205+
}
206+
`,
207+
expectedLines: ['Self print']
208+
},
209+
{
210+
comment: 'Using super to invoke parent method',
211+
program: `
212+
class Base {
213+
public void greet() {
214+
System.out.println("Hello from Base");
215+
}
216+
}
217+
class Derived extends Base {
218+
public void greet() {
219+
super.greet();
220+
System.out.println("Hello from Derived");
221+
}
222+
}
223+
public class Main {
224+
public static void main(String[] args) {
225+
Derived d = new Derived();
226+
d.greet();
227+
// Expected:
228+
// Hello from Base
229+
// Hello from Derived
230+
}
231+
}
232+
`,
233+
expectedLines: ['Hello from Base', 'Hello from Derived']
234+
},
235+
{
236+
comment: 'Polymorphic call with dynamic dispatch',
237+
program: `
238+
class Animal {
239+
public void speak() {
240+
System.out.println("Animal sound");
241+
}
242+
}
243+
class Dog extends Animal {
244+
public void speak() {
245+
System.out.println("Bark");
246+
}
247+
public void callSuper() {
248+
super.speak();
249+
}
250+
}
251+
public class Main {
252+
public static void main(String[] args) {
253+
Dog d = new Dog();
254+
d.speak(); // Bark
255+
d.callSuper(); // Animal sound
256+
}
257+
}
258+
`,
259+
expectedLines: ['Bark', 'Animal sound']
260+
},
261+
{
262+
comment: 'Method overloading resolution',
263+
program: `
264+
class Overload {
265+
public void test(int a) {
266+
System.out.println("int");
267+
}
268+
public void test(double a) {
269+
System.out.println("double");
270+
}
271+
}
272+
public class Main {
273+
public static void main(String[] args) {
274+
Overload o = new Overload();
275+
o.test(5); // int
276+
o.test(5.0); // double
277+
}
278+
}
279+
`,
280+
expectedLines: ['int', 'double']
281+
},
282+
{
283+
comment: 'Overriding on a superclass reference',
284+
program: `
285+
class X {
286+
public void foo() {
287+
System.out.println("X foo");
288+
}
289+
}
290+
class Y extends X {
291+
public void foo() {
292+
System.out.println("Y foo");
293+
}
294+
}
295+
public class Main {
296+
public static void main(String[] args) {
297+
X x = new Y();
298+
x.foo(); // Y foo
299+
}
300+
}
301+
`,
302+
expectedLines: ['Y foo']
303+
},
304+
{
305+
comment: 'Implicit conversion (byte to int)',
306+
program: `
307+
class Implicit {
308+
public void process(int a) {
309+
System.out.println("Processed int");
310+
}
311+
}
312+
public class Main {
313+
public static void main(String[] args) {
314+
Implicit imp = new Implicit();
315+
byte b = (byte) 10;
316+
imp.process(b); // Processed int
317+
}
318+
}
319+
`,
320+
expectedLines: ['Processed int']
188321
}
189322
]
190323

src/compiler/error.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,9 @@ export class AmbiguousMethodCallError extends CompileError {
4545
super(`Ambiguous method call: ${signature}`)
4646
}
4747
}
48+
49+
export class OverrideFinalMethodError extends CompileError {
50+
constructor(name: string) {
51+
super(`Cannot override final method ${name}`)
52+
}
53+
}

src/compiler/symbol-table.ts

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@ import {
66
generateMethodAccessFlags
77
} from './compiler-utils'
88
import {
9-
InvalidMethodCallError,
9+
InvalidMethodCallError, OverrideFinalMethodError,
1010
SymbolCannotBeResolvedError,
1111
SymbolNotFoundError,
1212
SymbolRedeclarationError
1313
} from './error'
1414
import { libraries } from './import/libs'
15+
import { METHOD_FLAGS } from '../ClassFile/types/methods'
1516

1617
export const typeMap = new Map([
1718
['byte', 'B'],
@@ -103,11 +104,10 @@ export class SymbolTable {
103104
if (this.importedPackages.findIndex(e => e == p.packageName + '/') == -1)
104105
this.importedPackages.push(p.packageName + '/')
105106
p.classes.forEach(c => {
106-
this.insertClassInfo(
107-
{
108-
name: c.className,
109-
accessFlags: generateClassAccessFlags(c.accessFlags)
110-
})
107+
this.insertClassInfo({
108+
name: c.className,
109+
accessFlags: generateClassAccessFlags(c.accessFlags)
110+
})
111111
c.fields.forEach(f =>
112112
this.insertFieldInfo({
113113
name: f.fieldName,
@@ -208,6 +208,21 @@ export class SymbolTable {
208208
insertMethodInfo(info: MethodInfo) {
209209
const key = generateSymbol(info.name, SymbolType.METHOD)
210210

211+
for (let i = this.curClassIdx - 1; i > 0; i--) {
212+
const parentTable = this.tables[i];
213+
if (parentTable.has(key)) {
214+
const parentMethods = parentTable.get(key)!.info;
215+
if (Array.isArray(parentMethods)) {
216+
for (const m of parentMethods) {
217+
if (m.typeDescriptor === info.typeDescriptor && (m.accessFlags & METHOD_FLAGS.ACC_FINAL)
218+
&& m.className == info.parentClassName) {
219+
throw new OverrideFinalMethodError(info.name);
220+
}
221+
}
222+
}
223+
}
224+
}
225+
211226
this.curTable = this.tables[this.curIdx]
212227
if (!this.curTable.has(key)) {
213228
const symbolNode: SymbolNode = {

0 commit comments

Comments
 (0)