Skip to content

Enable method overloading and overriding#70

Open
AprupKale wants to merge 28 commits into
mainfrom
method-overloading-overriding
Open

Enable method overloading and overriding#70
AprupKale wants to merge 28 commits into
mainfrom
method-overloading-overriding

Conversation

@AprupKale
Copy link
Copy Markdown
Contributor

Enable running multiple classes in java-slang.

Enable method overloading and overriding to support code as follows:

public class Main {
  public void f(int x) {
    System.out.println("Instance int: " + x);
  }
  public void f(double x) {
    System.out.println("Instance double: " + x);
  }
  public static void main(String[] args) {
    Main obj = new Main();
    obj.f(5);
    obj.f(5.5);
  }
}

and

class Parent {
  public static void staticMethod() {
    System.out.println("Parent static method");
  }
  public void instanceMethod() {
    System.out.println("Parent instance method");
  }
}

class Child extends Parent {
  public static void staticMethod() {
    System.out.println("Child static method");
  }
  public void instanceMethod() {
    System.out.println("Child instance method");
  }
}

public class Main {
  public static void main(String[] args) {
    Parent.staticMethod(); // Parent static method
    Child.staticMethod(); // Child static method
    Parent ref = new Child();
    ref.instanceMethod(); // Child instance method
  }
}

@AprupKale AprupKale self-assigned this Apr 6, 2025
@AprupKale AprupKale added the enhancement New feature or request label Apr 6, 2025
@AprupKale AprupKale linked an issue Apr 6, 2025 that may be closed by this pull request
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 6, 2025

Coverage report

Caution

Test run failed

St.
Category Percentage Covered / Total
🟡 Statements
72.3% (-1.27% 🔻)
7381/10209
🔴 Branches
58.78% (-1.1% 🔻)
2461/4187
🟡 Functions
68.86% (-0.66% 🔻)
1316/1911
🟡 Lines
73.16% (-1.33% 🔻)
6947/9496
Show files with reduced coverage 🔻
St.
File Statements Branches Functions Lines
🟡
... / expression-extractor.ts
67.35% (-10.88% 🔻)
58.51% (-10.72% 🔻)
86.67% (-6.19% 🔻)
66.43% (-11.07% 🔻)
🟡
... / index.ts
70.56% (-1.12% 🔻)
47.31% (-2.31% 🔻)
91.67% (-3.99% 🔻)
79.47% (-1.59% 🔻)
🟢
... / compiler.ts
100%
83.33% (-9.52% 🔻)
100% 100%
🟢
... / symbol-table.ts
91.09% (-2.81% 🔻)
67.39% (-12.02% 🔻)
100%
90.58% (-3.05% 🔻)
🟡
... / code-generator.ts
68.39% (-19.85% 🔻)
61.04% (-16.58% 🔻)
65.43% (-27.9% 🔻)
69.23% (-19.23% 🔻)

Test suite run failed

Failed tests: 0/1115. Failed suites: 1/64.
  ● Test suite failed to run

    src/ast/__tests__/expression-extractor.test.ts:1078:27 - error TS2322: Type '{ kind: "CastExpression"; type: string; expression: { kind: "Literal"; literalType: { kind: "DecimalIntegerLiteral"; value: string; }; location: any; }; location: any; }' is not assignable to type 'VariableInitializer | undefined'.
      Object literal may only specify known properties, and 'type' does not exist in type 'CastExpression'.

    1078                           type: "char",
                                   ~~~~

      src/ast/types/blocks-and-statements.ts:127:3
        127   variableInitializer?: VariableInitializer;
              ~~~~~~~~~~~~~~~~~~~
        The expected type comes from property 'variableInitializer' which is declared here on type 'VariableDeclarator'
    src/ast/__tests__/expression-extractor.test.ts:1147:27 - error TS2322: Type '{ kind: "CastExpression"; type: string; expression: { kind: "Literal"; literalType: { kind: "DecimalFloatingPointLiteral"; value: string; }; }; location: any; }' is not assignable to type 'VariableInitializer | undefined'.
      Object literal may only specify known properties, and 'type' does not exist in type 'CastExpression'.

    1147                           type: "int",
                                   ~~~~

      src/ast/types/blocks-and-statements.ts:127:3
        127   variableInitializer?: VariableInitializer;
              ~~~~~~~~~~~~~~~~~~~
        The expected type comes from property 'variableInitializer' which is declared here on type 'VariableDeclarator'

Report generated by 🧪jest coverage report action from 805b448

@martin-henz
Copy link
Copy Markdown
Member

/gemini review

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements support for SwitchStatement and CastExpression, covering grammar updates, AST extraction, type checking, and bytecode generation. It also enhances method invocation to support overloading, overriding, and super calls, and enables the compiler to handle multiple class files. Feedback highlights several critical issues, including incorrect usage of the in operator and filter method in TypeScript, flawed JVM offset patching for switch instructions, and a missing string collision check in switch statements. Additionally, the reviewer identified logic errors in the final method override checks and inconsistent package naming conventions that could lead to lookup failures.

Comment on lines +241 to +243
if (!(fromType in ['J', 'D']) && toType in ['J', 'D']) {
return 1
} else if (!(toType in ['J', 'D']) && fromType in ['J', 'D']) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

In JavaScript/TypeScript, the in operator checks for the existence of a property name in an object. For an array, it checks for indices (e.g., '0', '1'), not values. Use Array.prototype.includes() instead.

Suggested change
if (!(fromType in ['J', 'D']) && toType in ['J', 'D']) {
return 1
} else if (!(toType in ['J', 'D']) && fromType in ['J', 'D']) {
if (!['J', 'D'].includes(fromType) && ['J', 'D'].includes(toType)) {
return 1
} else if (!['J', 'D'].includes(toType) && ['J', 'D'].includes(fromType)) {
return -1

Comment on lines +797 to +798
candidateMethods.filter(method =>
method.className == cg.symbolTable.queryClass(cg.currentClass).parentClassName)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The filter method returns a new array and does not mutate the original one. You must assign the result back to candidateMethods.

Suggested change
candidateMethods.filter(method =>
method.className == cg.symbolTable.queryClass(cg.currentClass).parentClassName)
candidateMethods = candidateMethods.filter(method =>
method.className == cg.symbolTable.queryClass(cg.currentClass).parentClassName)

Comment on lines +809 to +810
candidateMethods.filter(method =>
method.className == cg.currentClass)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The filter result is ignored here. Assign it back to candidateMethods.

Suggested change
candidateMethods.filter(method =>
method.className == cg.currentClass)
candidateMethods = candidateMethods.filter(method =>
method.className == cg.currentClass)

Comment on lines +1517 to +1521
cg.code[caseLabelIndex[0]] = caseLabels[caseLabels.length - 1].offset - positionOffset

for (let i = 1; i < caseLabelIndex.length; i++) {
cg.code[caseLabelIndex[i]] = caseLabels[i - 1].offset - positionOffset
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The patching logic for switch offsets is incorrect.

  1. JVM offsets for TABLESWITCH and LOOKUPSWITCH are 4-byte signed integers. Assigning to a single index in cg.code (which is a byte array) only updates the last byte of the offset.
  2. The mapping logic seems reversed or misaligned: caseLabelIndex[0] is being patched with the offset of the last label in caseLabels.
  3. caseLabelIndex contains indices relative to the start of the code, but the mapping should correspond to the specific case values or hash codes defined in the switch table.

Comment on lines +1612 to +1613
// TODO: check for actual String equality instead of just rely on hashCode equality
// (see the commented out code below)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Switching on a String based only on hashCode is insufficient because different strings can have the same hash code (collisions). You must implement a secondary check using String.equals() to ensure correctness, as noted in your TODO.

Comment on lines +211 to +224
for (let i = this.curClassIdx - 1; i > 0; i--) {
const parentTable = this.tables[i];
if (parentTable.has(key)) {
const parentMethods = parentTable.get(key)!.info;
if (Array.isArray(parentMethods)) {
for (const m of parentMethods) {
if (m.typeDescriptor === info.typeDescriptor && (m.accessFlags & METHOD_FLAGS.ACC_FINAL)
&& m.className == info.parentClassName) {
throw new OverrideFinalMethodError(info.name);
}
}
}
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This loop iterates through the scope stack (this.tables), but in your implementation, parent classes are not ancestors in this stack; they are stored as siblings in the root table. To correctly check if a method overrides a final method in a superclass, you should follow the parentClassName hierarchy and look up the corresponding class in the root table.

switchBlockStatementGroup(ctx: SwitchBlockStatementGroupCtx): SwitchCase {
const blockStatementExtractor = new BlockStatementExtractor();

console.log(ctx.switchLabel)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Remove this console.log statement before merging.


if (receiverStr === 'this') {
candidateMethods = cg.symbolTable.queryMethod(n.identifier.slice(5)) as MethodInfos
console.debug(candidateMethods)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Remove this console.debug statement.

Comment on lines +104 to +105
if (this.importedPackages.findIndex(e => e == p.packageName + '/') == -1)
this.importedPackages.push(p.packageName + '/')
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There is an inconsistency in package naming. Here you are using dots (e.g., java.lang/), while in handleImports (line 152) you use slashes (e.g., java/lang/). JVM internal names and descriptors typically use slashes. This inconsistency will likely cause queryClass to fail when looking up library classes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Compiler/type checker: Support for inheritance needed

3 participants