Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Byte code generator #167

Open
wants to merge 3 commits into
base: 1.4.x
Choose a base branch
from
Open

Byte code generator #167

wants to merge 3 commits into from

Conversation

dstepanov
Copy link
Collaborator

Added a way to generate the bytecode directly; currently, if added, it will generate bytecode instead of JAVA sources.

I have added the bytecode checker by default to find bugs directly; it might be an optional feature in the future.

I have copied existing tests for Java generator. The missing features:

  • records (requires to generate precise class with all the dynamic stuff)
  • super builder - requires generation bridge-synthetic methods for overridden methods with variables just like Javac does.

Includes a lot of expression improvement and cleanup. Builders adjusted to invoke methods based on the original signature if generic.

I will experiment next in Core to see what is missing / not working.

@graemerocher graemerocher added the type: enhancement New feature or request label Oct 28, 2024
throw new IllegalStateException("Field " + field.name() + " is not available in [" + classDef + "]:" + classDef.getFields());
}
} else {
throw new IllegalStateException("Field access no supported on the object definition: " + context.objectDef);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
throw new IllegalStateException("Field access no supported on the object definition: " + context.objectDef);
throw new IllegalStateException("Field access not supported on the object definition: " + context.objectDef);

if (context.objectDef == null) {
throw new IllegalStateException("Accessing 'super' is not available");
}
generatorAdapter.loadThis();
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is it loadThis for super?

ExpressionDef left,
ExpressionDef right) implements ExpressionDef {
@Override
public TypeDef type() {
return TypeDef.Primitive.BOOLEAN;
return left.type();
Copy link
Contributor

Choose a reason for hiding this comment

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

Can this break something because 1.0f + 2.0d = 3.0d? I suppose users could explicitly cast to double if needed though.

* @author Denis Stepanov
* @since 1.4
*/
sealed interface ConditionExpressionDef extends ExpressionDef {
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe call this BooleanExpressionDef?

@dstepanov dstepanov marked this pull request as ready for review November 25, 2024 08:21
@dstepanov
Copy link
Collaborator Author

Please review it again.

  • Move the byte generator into its own module that can be used in Core without circular dependencies between sourcegen and core-processor
  • Added more TryCatchFinally, Synchronized statements that are not fully supported in Java generator

Let's merge it and move from there

Copy link
Contributor

@graemerocher graemerocher left a comment

Choose a reason for hiding this comment

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

Looks good, but I would refactor ByteCodeWriter to make it more maintainable since it is a monster now

@@ -1,4 +1,4 @@
projectVersion=1.4.2-SNAPSHOT
projectVersion=1.4.900-SNAPSHOT
Copy link
Contributor

Choose a reason for hiding this comment

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

might want to revert this not sure what its for

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I change it to publish a local version, which will not likely conflict with an existing one

* @param objectDef The object definition
*/
public void writeObject(ClassVisitor classVisitor, ObjectDef objectDef) {
if (objectDef instanceof ClassDef classDef) {
Copy link
Contributor

Choose a reason for hiding this comment

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

like here instead of all this if/else it would be good to use inheritance and have different implementations for records, classes, interfaces etc.

* @param statementDef The statement definition
* @param finallyBlock The runnable that should be invoked before any returning operation - return/throw
*/
public void pushStatement(GeneratorAdapter generatorAdapter,
Copy link
Contributor

Choose a reason for hiding this comment

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

this is another case that could do with refactoring. Something like:

StatementWriter.for(statementDef).write(generator, context, finallyBlock)

Comment on lines 1416 to 1422
// if (context.objectDef instanceof ClassDef classDef) {
// if (!classDef.hasField(field.name()) && classDef.getProperties().stream().noneMatch(prop -> prop.getName().equals(field.name()))) {
// throw new IllegalStateException("Field '" + field.name() + "' is not available in [" + classDef + "]:" + classDef.getFields());
// }
// } else {
// throw new IllegalStateException("Field access not supported on the object definition: " + context.objectDef);
// }
Copy link
Contributor

Choose a reason for hiding this comment

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

if this code is dead delete it

Comment on lines 1431 to 1440
// if (context.objectDef == null) {
// throw new IllegalStateException("Accessing 'this' is not available");
// }
// if (context.objectDef instanceof ClassDef classDef) {
// if (!classDef.hasField(field.name()) && classDef.getProperties().stream().noneMatch(prop -> prop.getName().equals(field.name()))) {
// throw new IllegalStateException("Field '" + field.name() + "' is not available in [" + classDef + "]:" + classDef.getFields());
// }
// } else {
// throw new IllegalStateException("Field access not supported on the object definition: " + context.objectDef);
// }
Copy link
Contributor

Choose a reason for hiding this comment

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

if this code is dead delete it

throw new UnsupportedOperationException("Unrecognized variable: " + variableDef);
}

private void pushConstant(GeneratorAdapter generatorAdapter,
Copy link
Contributor

Choose a reason for hiding this comment

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

Something like:

ConstantWriter.for(constant).write(generator)

would be good

@dstepanov
Copy link
Collaborator Author

@graemerocher Refactored out statement/expression writers. Do you want to make the ByteCodeWriter even smaller?

Copy link

sonarcloud bot commented Nov 25, 2024

Quality Gate Failed Quality Gate failed

Failed conditions
1 Security Hotspot
0.0% Coverage on New Code (required ≥ 70%)
3 New Blocker Issues (required ≤ 0)
6 New Bugs (required ≤ 0)
31 New Critical Issues (required ≤ 0)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: enhancement New feature or request
Projects
Status: No status
Development

Successfully merging this pull request may close these issues.

3 participants