Skip to content

Commit

Permalink
Support accessing array element using an expression (#219)
Browse files Browse the repository at this point in the history
  • Loading branch information
dstepanov authored Jan 9, 2025
1 parent 29ebe1a commit 0162733
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public ArrayElementExpressionWriter(ExpressionDef.ArrayElement arrayElement) {
@Override
public void write(GeneratorAdapter generatorAdapter, MethodContext context) {
ExpressionWriter.writeExpression(generatorAdapter, context, arrayElement.expression());
generatorAdapter.push(arrayElement.index());
ExpressionWriter.writeExpression(generatorAdapter, context, arrayElement.indexExpression());
generatorAdapter.arrayLoad(TypeUtils.getType(arrayElement.type(), context.objectDef()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,107 @@

class ByteCodeWriterTest {

@Test
void arrayElement() {
ClassDef def = ClassDef.builder("example.Example")
.addModifiers(Modifier.PUBLIC)
.addMethod(MethodDef.builder("myMethod")
.addParameters(String[].class)
.build((aThis, methodParameters) -> methodParameters.get(0).arrayElement(1).returning()))
.build();

StringWriter bytecodeWriter = new StringWriter();
byte[] bytes = generateFile(def, bytecodeWriter);

String bytecode = bytecodeWriter.toString();
Assertions.assertEquals("""
// class version 61.0 (61)
// access flags 0x1
// signature Ljava/lang/Object;
// declaration: example/Example
public class example/Example {
// access flags 0x1
public <init>()V
ALOAD 0
INVOKESPECIAL java/lang/Object.<init> ()V
RETURN
// access flags 0x0
myMethod([Ljava/lang/String;)Ljava/lang/String;
L0
ALOAD 1
ICONST_1
AALOAD
ARETURN
L1
LOCALVARIABLE arg1 [Ljava/lang/String; L0 L1 1
}
""", bytecode);

Assertions.assertEquals("""
package example;
public class Example {
String myMethod(String[] arg1) {
return arg1[1];
}
}
""", decompileToJava(bytes));
}

@Test
void arrayElement2() {
ClassDef def = ClassDef.builder("example.Example")
.addModifiers(Modifier.PUBLIC)
.addMethod(MethodDef.builder("myMethod")
.addParameters(String[].class, int.class)
.build((aThis, methodParameters) -> methodParameters.get(0).arrayElement(methodParameters.get(1)).returning()))
.build();

StringWriter bytecodeWriter = new StringWriter();
byte[] bytes = generateFile(def, bytecodeWriter);

String bytecode = bytecodeWriter.toString();
Assertions.assertEquals("""
// class version 61.0 (61)
// access flags 0x1
// signature Ljava/lang/Object;
// declaration: example/Example
public class example/Example {
// access flags 0x1
public <init>()V
ALOAD 0
INVOKESPECIAL java/lang/Object.<init> ()V
RETURN
// access flags 0x0
myMethod([Ljava/lang/String;I)Ljava/lang/String;
L0
ALOAD 1
ILOAD 2
AALOAD
ARETURN
L1
LOCALVARIABLE arg1 [Ljava/lang/String; L0 L1 1
LOCALVARIABLE arg2 I L0 L1 2
}
""", bytecode);

Assertions.assertEquals("""
package example;
public class Example {
String myMethod(String[] arg1, int arg2) {
return arg1[arg2];
}
}
""", decompileToJava(bytes));
}

@Test
void testSynthetic() {
ClassDef def = ClassDef.builder("example.Example")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,20 @@ default ArrayElement arrayElement(int index) {
return new ArrayElement(this, index);
}

/**
* Check an array element.
*
* @param index The index
* @return The array element
* @since 1.5
*/
default ArrayElement arrayElement(ExpressionDef index) {
if (!index.type().equals(TypeDef.Primitive.INT)) {
throw new IllegalStateException("Illegal array index type: " + index.type());
}
return new ArrayElement(this, index);
}

/**
* Check if the instance is of the type.
*
Expand Down Expand Up @@ -1254,22 +1268,33 @@ record InstanceOf(ExpressionDef expression,
/**
* The get array element expression.
*
* @param expression The expression
* @param type The component type
* @param index The index
* @param expression The expression
* @param type The component type
* @param indexExpression The index expression
* @author Denis Stepanov
* @since 1.5
*/
@Experimental
record ArrayElement(ExpressionDef expression,
TypeDef type,
int index) implements ExpressionDef {
ExpressionDef indexExpression) implements ExpressionDef {

public ArrayElement(ExpressionDef expression,
TypeDef type,
int index) {
this(expression, type, ExpressionDef.constant(index));
}

public ArrayElement(ExpressionDef expression,
int index) {
this(expression, findComponentType(expression.type()), index);
}

public ArrayElement(ExpressionDef expression,
ExpressionDef indexExpression) {
this(expression, findComponentType(expression.type()), indexExpression);
}

private static TypeDef findComponentType(TypeDef arrayType) {
if (arrayType instanceof TypeDef.Array array) {
return array.componentType();
Expand Down

0 comments on commit 0162733

Please sign in to comment.