Skip to content

Codegen generates inefficient Java code for List parameters #282

@jkronegg

Description

@jkronegg

👓 What did you see?

For List parameters, the codegen generates Java code which looks like this:

public TableRow(
    Location location,
    java.util.List<TableCell> cells,
    String id
) {
    this.location = requireNonNull(location, "TableRow.location cannot be null");
    this.cells = unmodifiableList(new ArrayList<>(requireNonNull(cells, "TableRow.cells cannot be null")));
    this.id = requireNonNull(id, "TableRow.id cannot be null");
}

This means that the cells parameters is converted to ArrayList (a modifiable list), then to a non-modifiable list.
I think this is done to ensure a proper conversion from Collection -> ArrayList -> UnmodifiableList.
However, all list arguments are List and not Collection (see java.rb). This lead to inefficient code because two list allocations are done, no matter the input list is a modifiable or non-modifiable list.

The performance impact has been detected while working on cucumber/gherkin#361, using IntelliJ Profiler (it's hard to see the real impact because there is a lot of generated classes which suffer from this syndrome).

✅ What did you expect to see?

If the java.java.erb code generator see a List, it should generate convert it to a non-modifiable list using unmodifiableList(List) without doing a new ArrayList(Collection).
This will avoid memory allocation and list traversal, so will lead to faster code.

📦 Which tool/library version are you using?

gherkin 31.0.0 (which uses messages-27.2.0)

🔬 How could we reproduce it?

The test-case below shows the issue:

import io.cucumber.messages.types.Location;
import io.cucumber.messages.types.TableCell;
import io.cucumber.messages.types.TableRow;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class TableRowTest {
    @Test
    public void modifiable_list_converted_to_unmodifiable_list() {
        // Given
        List<TableCell> tableCells = new ArrayList<>();

        // When
        TableRow row = new TableRow(new Location(1L,1L), tableCells, "1");

        // Then
        Assertions.assertNotSame(tableCells, row.getCells());
        Assertions.assertThrows(UnsupportedOperationException.class, () -> row.getCells().add(null));
    }

    @Test
    public void unmodifiable_list_not_converted() {
        // Given
        List<TableCell> tableCells = Collections.unmodifiableList(new ArrayList<>());

        // When
        TableRow row = new TableRow(new Location(1L,1L), tableCells, "1");

        // Then
        Assertions.assertSame(tableCells, row.getCells()); // fails
    }
}

📚 Any additional context?

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions