Skip to content

Commit c66aad9

Browse files
committed
Properly name lambda captures too
Signed-off-by: TheSilkMiner <thesilkminer@outlook.com>
1 parent 46658d9 commit c66aad9

4 files changed

Lines changed: 203 additions & 119 deletions

File tree

JavaBytecodeCompiler/src/main/java/org/openzen/zenscript/javabytecode/compiler/lambda/LambdaIndyCompiler.java

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,7 @@
1515
import org.openzen.zenscript.javabytecode.compiler.JavaExpressionVisitor;
1616
import org.openzen.zenscript.javabytecode.compiler.JavaStatementVisitor;
1717
import org.openzen.zenscript.javabytecode.compiler.JavaWriter;
18-
import org.openzen.zenscript.javabytecode.compiler.lambda.capturing.JavaRedirectCapturesCapturedExpressionVisitor;
19-
import org.openzen.zenscript.javabytecode.compiler.lambda.capturing.JavaLoadCapturesOnIndyCapturedExpressionVisitor;
20-
import org.openzen.zenscript.javabytecode.compiler.lambda.capturing.JavaLoadThisOnIndyCapturedExpressionVisitor;
18+
import org.openzen.zenscript.javabytecode.compiler.lambda.capturing.*;
2119
import org.openzen.zenscript.javabytecode.compiler.definitions.JavaMemberVisitor;
2220
import org.openzen.zenscript.javashared.JavaClass;
2321
import org.openzen.zenscript.javashared.JavaCompiledModule;
@@ -87,18 +85,19 @@ private String computeLambdaDescriptor(final LambdaClosureInfo closureInfo, fina
8785

8886
private void generateLambdaMethod(final JavaCompilingMethod compilingLambdaMethod, final FunctionExpression lambdaExpression, final LambdaClosureInfo closureInfo) {
8987
final JavaWriter lambdaWriter = new JavaWriter(this.context.logger, lambdaExpression.position, this.writer.clazzVisitor, compilingLambdaMethod, null);
90-
final CapturedExpressionVisitor<Void> lambdaCapturesVisitor = new JavaRedirectCapturesCapturedExpressionVisitor(lambdaWriter, lambdaExpression, closureInfo);
88+
final CapturedExpressionVisitor<Void> lambdaCapturesVisitor = new JavaRedirectCapturesCapturedExpressionVisitor(lambdaWriter, lambdaExpression, closureInfo, this.context);
9189
final JavaExpressionVisitor lambdaExpressionVisitor = new JavaExpressionVisitor(this.context, this.module, lambdaWriter, this.mangler, lambdaCapturesVisitor);
9290
final JavaStatementVisitor lambdaStatementVisitor = new JavaStatementVisitor(this.context, lambdaExpressionVisitor, this.mangler);
9391

94-
this.generateLambdaMethodBody(compilingLambdaMethod, lambdaWriter, lambdaStatementVisitor, lambdaExpression);
92+
this.generateLambdaMethodBody(compilingLambdaMethod, lambdaWriter, lambdaStatementVisitor, lambdaExpression, closureInfo);
9593
}
9694

9795
private void generateLambdaMethodBody(
9896
final JavaCompilingMethod method,
9997
final JavaWriter writer,
10098
final JavaStatementVisitor statementVisitor,
101-
final FunctionExpression lambdaExpression
99+
final FunctionExpression lambdaExpression,
100+
final LambdaClosureInfo closureInfo
102101
) {
103102
final Label begin = new Label();
104103
final Label end = new Label();
@@ -110,7 +109,7 @@ private void generateLambdaMethodBody(
110109
writer.ret();
111110
writer.label(end);
112111

113-
this.loadLambdaVariables(writer, method, lambdaExpression, begin, end);
112+
this.loadLambdaVariables(writer, method, lambdaExpression, closureInfo, begin, end);
114113

115114
writer.end();
116115
}
@@ -119,9 +118,13 @@ private void loadLambdaVariables(
119118
final JavaWriter writer,
120119
final JavaCompilingMethod method,
121120
final FunctionExpression lambdaExpression,
121+
final LambdaClosureInfo closureInfo,
122122
final Label begin,
123123
final Label end
124124
) {
125+
final JavaCaptureDataFinderCapturedExpressionVisitor captureDataFinder = new JavaCaptureDataFinderCapturedExpressionVisitor(lambdaExpression, closureInfo, this.context);
126+
final LambdaCaptureData[] captures = lambdaExpression.closure.captures.stream().map(it -> it.accept(captureDataFinder)).toArray(LambdaCaptureData[]::new);
127+
125128
final Type[] methodArguments = Type.getArgumentTypes(method.compiled.descriptor);
126129
final FunctionParameter[] lambdaParameters = lambdaExpression.header.parameters;
127130

@@ -135,8 +138,15 @@ private void loadLambdaVariables(
135138
final String lambdaName = lambdaParameters[i - 1].name;
136139
name = lambdaName == null || lambdaName.isEmpty()? "$param" + (i - 1) : lambdaName;
137140
} else {
138-
// TODO("Maybe find the actual name of the variable among the captures?")
139-
name = "$capture$" + (i - p);
141+
LambdaCaptureData data = null;
142+
for (final LambdaCaptureData d : captures) {
143+
if (d.position() == i) {
144+
data = d;
145+
break;
146+
}
147+
}
148+
149+
name = "$capture$" + (data == null || data.name().isEmpty()? (i - p) : data.name());
140150
}
141151

142152
final JavaLocalVariableInfo info = new JavaLocalVariableInfo(type, localIndex, begin, name, end);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
package org.openzen.zenscript.javabytecode.compiler.lambda.capturing;
2+
3+
import org.objectweb.asm.Type;
4+
import org.openzen.zenscript.codemodel.FunctionParameter;
5+
import org.openzen.zenscript.codemodel.expression.FunctionExpression;
6+
import org.openzen.zenscript.codemodel.expression.captured.*;
7+
import org.openzen.zenscript.codemodel.type.TypeID;
8+
import org.openzen.zenscript.javabytecode.JavaBytecodeContext;
9+
import org.openzen.zenscript.javabytecode.compiler.lambda.LambdaClosureInfo;
10+
11+
import java.util.function.Predicate;
12+
import java.util.function.Supplier;
13+
14+
public final class JavaCaptureDataFinderCapturedExpressionVisitor implements CapturedExpressionVisitor<LambdaCaptureData> {
15+
private static final class ExtractedInfo {
16+
private final boolean matches;
17+
private final String name;
18+
19+
private ExtractedInfo(final boolean matches, final String name) {
20+
this.matches = matches;
21+
this.name = name;
22+
}
23+
24+
static ExtractedInfo noMatch() {
25+
return new ExtractedInfo(false, null);
26+
}
27+
28+
static ExtractedInfo match(final String name) {
29+
return new ExtractedInfo(true, name);
30+
}
31+
32+
boolean matches() {
33+
return this.matches;
34+
}
35+
36+
String name() {
37+
return this.name;
38+
}
39+
}
40+
41+
@FunctionalInterface
42+
private interface InfoExtractor<T extends CapturedExpression> {
43+
static <T extends CapturedExpression> InfoExtractor<T> byPredicating(final Supplier<String> name, final Predicate<T> predicate) {
44+
return it -> predicate.test(it)? ExtractedInfo.match(name.get()) : ExtractedInfo.noMatch();
45+
}
46+
47+
ExtractedInfo extractInfo(final T t);
48+
}
49+
50+
private final FunctionExpression functionExpression;
51+
private final LambdaClosureInfo closureInfo;
52+
private final JavaBytecodeContext context;
53+
54+
public JavaCaptureDataFinderCapturedExpressionVisitor(final FunctionExpression functionExpression, final LambdaClosureInfo closureInfo, final JavaBytecodeContext context) {
55+
this.functionExpression = functionExpression;
56+
this.closureInfo = closureInfo;
57+
this.context = context;
58+
}
59+
60+
@Override
61+
public LambdaCaptureData visitCapturedThis(final CapturedThisExpression expression) {
62+
// TODO("Remove null-check as this method should never return null")
63+
final Type type = this.closureInfo.thisType() == null? Type.getType(Void.class) : this.getTypeFrom(this.closureInfo.thisType());
64+
return LambdaCaptureData.of(0, type, "$this");
65+
}
66+
67+
@Override
68+
public LambdaCaptureData visitCapturedParameter(final CapturedParameterExpression expression) {
69+
return this.computeData(
70+
this.functionExpression,
71+
expression.parameter.type,
72+
InfoExtractor.byPredicating(
73+
() -> expression.parameter.name,
74+
capture -> capture instanceof CapturedParameterExpression && ((CapturedParameterExpression) capture).parameter == expression.parameter
75+
)
76+
);
77+
}
78+
79+
@Override
80+
public LambdaCaptureData visitCapturedLocal(final CapturedLocalVariableExpression expression) {
81+
final Predicate<CapturedExpression> matchVariable =
82+
capture -> capture instanceof CapturedLocalVariableExpression && ((CapturedLocalVariableExpression) capture).variable == expression.variable;
83+
84+
return this.computeData(
85+
this.functionExpression,
86+
expression.variable.type,
87+
InfoExtractor.byPredicating(
88+
() -> expression.variable.name,
89+
matchVariable.or(capture -> capture instanceof CapturedClosureExpression && matchVariable.test(((CapturedClosureExpression) capture).value))
90+
)
91+
);
92+
}
93+
94+
@Override
95+
public LambdaCaptureData visitRecaptured(final CapturedClosureExpression expression) {
96+
return this.computeData(
97+
this.functionExpression,
98+
expression.type,
99+
InfoExtractor.byPredicating(
100+
() -> expression.value.accept(this).name(),
101+
expression::equals
102+
)
103+
);
104+
}
105+
106+
private LambdaCaptureData computeData(
107+
final FunctionExpression expression,
108+
final TypeID varType,
109+
final InfoExtractor<CapturedExpression> extractor
110+
) {
111+
final Type type = this.getTypeFrom(varType);
112+
int h = this.findFirstValidCaptureIndex(expression);
113+
for (final CapturedExpression capture : expression.closure.captures) {
114+
final ExtractedInfo info = extractor.extractInfo(capture);
115+
if (info.matches()) {
116+
return LambdaCaptureData.of(h, type, info.name());
117+
}
118+
h += type.getSize();
119+
}
120+
throw new IllegalStateException(expression.position.toString() + ": Captured Statement error");
121+
}
122+
123+
private int findFirstValidCaptureIndex(final FunctionExpression expression) {
124+
int h = 1;
125+
for (final FunctionParameter parameter : expression.header.parameters) {
126+
h += this.getTypeFrom(parameter.type).getSize();
127+
}
128+
return h;
129+
}
130+
131+
private Type getTypeFrom(final TypeID type) {
132+
return this.context.getType(type);
133+
}
134+
}
Original file line numberDiff line numberDiff line change
@@ -1,142 +1,51 @@
11
package org.openzen.zenscript.javabytecode.compiler.lambda.capturing;
22

3-
import org.objectweb.asm.Type;
4-
import org.openzen.zenscript.codemodel.FunctionParameter;
53
import org.openzen.zenscript.codemodel.expression.FunctionExpression;
6-
import org.openzen.zenscript.codemodel.expression.GetLocalVariableExpression;
74
import org.openzen.zenscript.codemodel.expression.captured.*;
8-
import org.openzen.zenscript.codemodel.type.BasicTypeID;
9-
import org.openzen.zenscript.codemodel.type.TypeID;
5+
import org.openzen.zenscript.javabytecode.JavaBytecodeContext;
106
import org.openzen.zenscript.javabytecode.compiler.JavaWriter;
117
import org.openzen.zenscript.javabytecode.compiler.lambda.LambdaClosureInfo;
128

13-
import java.util.function.Predicate;
14-
159
public final class JavaRedirectCapturesCapturedExpressionVisitor implements CapturedExpressionVisitor<Void> {
16-
private static final class MemberData {
17-
private final int position;
18-
private final Type type;
19-
20-
MemberData(final int position, final Type type) {
21-
this.position = position;
22-
this.type = type;
23-
}
24-
25-
void load(final JavaWriter writer) {
26-
writer.load(this.type, this.position);
27-
}
28-
}
29-
30-
private final FunctionExpression functionExpression;
3110
private final JavaWriter javaWriter;
32-
private final LambdaClosureInfo closureInfo;
33-
34-
public JavaRedirectCapturesCapturedExpressionVisitor(final JavaWriter javaWriter, final FunctionExpression functionExpression, final LambdaClosureInfo closureInfo) {
11+
private final JavaCaptureDataFinderCapturedExpressionVisitor captureDataFinder;
12+
13+
public JavaRedirectCapturesCapturedExpressionVisitor(
14+
final JavaWriter javaWriter,
15+
final FunctionExpression functionExpression,
16+
final LambdaClosureInfo closureInfo,
17+
final JavaBytecodeContext context
18+
) {
3519
this.javaWriter = javaWriter;
36-
this.functionExpression = functionExpression;
37-
this.closureInfo = closureInfo;
20+
this.captureDataFinder = new JavaCaptureDataFinderCapturedExpressionVisitor(functionExpression, closureInfo, context);
3821
}
3922

4023
@Override
4124
public Void visitCapturedThis(final CapturedThisExpression expression) {
42-
// TODO("Remove null-check as this method should never return null")
43-
final Type type = this.closureInfo.thisType() == null? Type.getType(Void.class) : this.getTypeFrom(this.closureInfo.thisType());
44-
return this.loadByMemberData(new MemberData(0, type));
25+
return this.loadByCaptureData(expression);
4526
}
4627

4728
@Override
4829
public Void visitCapturedParameter(CapturedParameterExpression expression) {
49-
return this.loadByMemberData(this.calculateMemberData(expression, this.functionExpression));
30+
return this.loadByCaptureData(expression);
5031
}
5132

5233
@Override
5334
public Void visitCapturedLocal(CapturedLocalVariableExpression expression) {
54-
return this.loadByMemberData(this.calculateMemberData(new GetLocalVariableExpression(expression.position, expression.variable), this.functionExpression));
35+
return this.loadByCaptureData(expression);
5536
}
5637

5738
@Override
5839
public Void visitRecaptured(CapturedClosureExpression expression) {
59-
return this.loadByMemberData(this.findIndex(expression, this.functionExpression));
40+
return this.loadByCaptureData(expression);
6041
}
6142

62-
private Void loadByMemberData(final MemberData memberData) {
63-
memberData.load(this.javaWriter);
64-
return null;
43+
private Void loadByCaptureData(final CapturedExpression expression) {
44+
return this.loadByCaptureData(expression.accept(this.captureDataFinder));
6545
}
6646

67-
private MemberData findIndex(final CapturedExpression capturedExpression, final FunctionExpression expression) {
68-
return this.calculateMemberData(
69-
expression,
70-
capturedExpression.type,
71-
capturedExpression::equals
72-
);
73-
}
74-
75-
private MemberData calculateMemberData(final GetLocalVariableExpression localVariableExpression, final FunctionExpression expression) {
76-
return this.calculateMemberData(
77-
expression,
78-
localVariableExpression.type,
79-
capture -> {
80-
if (capture instanceof CapturedLocalVariableExpression) {
81-
return ((CapturedLocalVariableExpression) capture).variable == localVariableExpression.variable;
82-
} else if (capture instanceof CapturedClosureExpression) {
83-
final CapturedExpression value = ((CapturedClosureExpression) capture).value;
84-
return value instanceof CapturedLocalVariableExpression && ((CapturedLocalVariableExpression) value).variable == localVariableExpression.variable;
85-
}
86-
return false;
87-
});
88-
}
89-
90-
private MemberData calculateMemberData(final CapturedParameterExpression functionParameterExpression, final FunctionExpression expression) {
91-
return this.calculateMemberData(
92-
expression,
93-
functionParameterExpression.parameter.type,
94-
capture -> capture instanceof CapturedParameterExpression && ((CapturedParameterExpression) capture).parameter == functionParameterExpression.parameter
95-
);
96-
}
97-
98-
private MemberData calculateMemberData(final FunctionExpression expression, final TypeID varType, final Predicate<CapturedExpression> predicate) {
99-
final Type type = this.getTypeFrom(varType);
100-
int h = this.findFirstValidCaptureIndex(expression);
101-
for (final CapturedExpression capture : expression.closure.captures) {
102-
if (predicate.test(capture)) {
103-
return new MemberData(h, type);
104-
}
105-
h += type.getSize();
106-
}
107-
throw new IllegalStateException(expression.position.toString() + ": Captured Statement error");
108-
}
109-
110-
private int findFirstValidCaptureIndex(final FunctionExpression expression) {
111-
int h = 1;
112-
for (final FunctionParameter parameter : expression.header.parameters) {
113-
h += this.getTypeFrom(parameter.type).getSize();
114-
}
115-
return h;
116-
}
117-
118-
private Type getTypeFrom(final TypeID type) {
119-
if (type instanceof BasicTypeID) {
120-
switch ((BasicTypeID) type) {
121-
case VOID: return Type.VOID_TYPE;
122-
case BOOL: return Type.BOOLEAN_TYPE;
123-
case BYTE: return Type.BYTE_TYPE;
124-
case SBYTE: return Type.BYTE_TYPE; // TODO()
125-
case SHORT: return Type.SHORT_TYPE;
126-
case USHORT: return Type.SHORT_TYPE; // TODO()
127-
case INT: return Type.INT_TYPE;
128-
case UINT: return Type.INT_TYPE; // TODO()
129-
case LONG: return Type.LONG_TYPE;
130-
case ULONG: return Type.LONG_TYPE; // TODO()
131-
case USIZE: return Type.INT_TYPE;
132-
case FLOAT: return Type.FLOAT_TYPE;
133-
case DOUBLE: return Type.DOUBLE_TYPE;
134-
case CHAR: return Type.CHAR_TYPE;
135-
}
136-
}
137-
138-
// We don't care about the actual class contained in the type here, we only care that Java treats it as an
139-
// object, so we simply grab a random Java class
140-
return Type.getType(Void.class);
47+
private Void loadByCaptureData(final LambdaCaptureData data) {
48+
this.javaWriter.load(data.type(), data.position());
49+
return null;
14150
}
14251
}

0 commit comments

Comments
 (0)