fix lambda expressions with field access

This commit is contained in:
Volker Berlin 2022-07-31 12:00:07 +02:00
parent fbbbae0980
commit 024be2c50c
No known key found for this signature in database
GPG Key ID: 988423EF815BE4CB
3 changed files with 55 additions and 27 deletions

View File

@ -395,22 +395,35 @@ public class TypeManager {
*
* @param method
* the name BootstrapMethod from the parsed class file
* @param params
* the parameters of the constructor and type fields
* @param interfaceType
* the implemented interface
* @param factorySignature
* Get the signature of the factory method. For example "()Ljava.lang.Runnable;" for the lambda expression
* <code>Runnable run = () -&gt; foo();</code>
* @param interfaceMethodName
* the name of the implemented method in the interface
* @param lineNumber
* the line number in the Java source code
* @return the type
*/
LambdaType lambdaType( @Nonnull BootstrapMethod method, ArrayList<AnyType> params, StructType interfaceType, String interfaceMethodName ) {
LambdaType lambdaType( @Nonnull BootstrapMethod method, String factorySignature, String interfaceMethodName, int lineNumber ) {
ConstantRef implMethod = method.getImplMethod();
FunctionName methodName = new FunctionName( implMethod );
FunctionName syntheticLambdaFunctionName = new FunctionName( implMethod );
Iterator<AnyType> parser = new ValueTypeParser( factorySignature, this );
ArrayList<AnyType> params = new ArrayList<>();
do {
AnyType param = parser.next();
if( param == null ) {
break;
}
params.add( param );
} while( true );
StructType interfaceType = (StructType)parser.next();
String typeName = implMethod.getClassName() + "$$" + implMethod.getName() + "/" + Math.abs( implMethod.getName().hashCode() );
LambdaType type = (LambdaType)structTypes.get( typeName );
if( type == null ) {
type = new LambdaType( typeName, method, params, interfaceType, methodName, interfaceMethodName, this );
type = new LambdaType( typeName, method, params, interfaceType, syntheticLambdaFunctionName, interfaceMethodName, this, lineNumber );
structTypes.put( typeName, type );
}
@ -1215,8 +1228,10 @@ public class TypeManager {
* the name of the implemented method in the interface
* @param manager
* the manager which hold all StructTypes
* @param lineNumber
* the line number in the Java source code
*/
LambdaType( @Nonnull String name, @Nonnull BootstrapMethod method, ArrayList<AnyType> params, StructType interfaceType, FunctionName syntheticLambdaFunctionName, String interfaceMethodName, @Nonnull TypeManager manager ) {
LambdaType( @Nonnull String name, @Nonnull BootstrapMethod method, ArrayList<AnyType> params, StructType interfaceType, FunctionName syntheticLambdaFunctionName, String interfaceMethodName, @Nonnull TypeManager manager, int lineNumber ) {
super( name, StructTypeKind.lambda, manager );
this.paramFields = new ArrayList<>( params.size() );
for( int i = 0; i < params.size(); i++ ) {
@ -1246,10 +1261,10 @@ public class TypeManager {
}
watParser.reset( null, null, sig.iterator() );
// first add the values from the Lambda constructor which is saves in the syntetic class
// first add the values from the Lambda constructor which is saved in the synthetic class
for( int i = 0; i < paramFields.size(); i++ ) {
codebuilder.addLoadStoreInstruction( LambdaType.this, true, 0, 0, -1 );
codebuilder.addStructInstruction( StructOperator.GET, name, paramFields.get( i ), 0, -1 );
codebuilder.addStructInstruction( StructOperator.GET, name, paramFields.get( i ), 0, lineNumber );
}
// forward the parameter from the current call without the THIS parameter because the call lambda method is static
@ -1258,10 +1273,20 @@ public class TypeManager {
if( anyType == null ) {
break;
}
codebuilder.addLoadStoreInstruction( anyType, true, i, 0, -1 );
codebuilder.addLoadStoreInstruction( anyType, true, i, 0, lineNumber );
}
codebuilder.addCallInstruction( syntheticLambdaFunctionName, false, 0, -1 );
boolean needThisParameter = false;
try {
// a lambda expression function is mostly static else it need access to field.
ClassFile classFile = classFileLoader.get( syntheticLambdaFunctionName.className );
MethodInfo methodInfo = classFile.getMethod( syntheticLambdaFunctionName.methodName, syntheticLambdaFunctionName.signature );
needThisParameter = !methodInfo.isStatic();
} catch( Exception ex ) {
throw WasmException.create( ex, null, syntheticLambdaFunctionName.className, syntheticLambdaFunctionName.methodName, lineNumber );
}
codebuilder.addCallInstruction( syntheticLambdaFunctionName, needThisParameter, 0, lineNumber );
return watParser;
}
};

View File

@ -36,7 +36,6 @@ import de.inetsoftware.jwebassembly.WasmException;
import de.inetsoftware.jwebassembly.javascript.NonGC;
import de.inetsoftware.jwebassembly.module.StackInspector.StackValue;
import de.inetsoftware.jwebassembly.module.TypeManager.LambdaType;
import de.inetsoftware.jwebassembly.module.TypeManager.StructType;
import de.inetsoftware.jwebassembly.module.WasmInstruction.Type;
import de.inetsoftware.jwebassembly.wasm.AnyType;
import de.inetsoftware.jwebassembly.wasm.ArrayOperator;
@ -650,13 +649,14 @@ public abstract class WasmCodeBuilder {
*
* @param name
* the function name that should be called
* @param needThisParameter true, if the hidden THIS parameter is needed, If it is an instance method call.
* @param needThisParameter
* true, if the hidden THIS parameter is needed, If it is an instance method call.
* @param javaCodePos
* the code position/offset in the Java method
* @param lineNumber
* the line number in the Java source code
*/
protected void addCallInstruction( FunctionName name, boolean needThisParameter, int javaCodePos, int lineNumber ) {
protected void addCallInstruction( @Nonnull FunctionName name, boolean needThisParameter, int javaCodePos, int lineNumber ) {
name = functions.markAsNeeded( name, needThisParameter );
WasmCallInstruction instruction = new WasmCallInstruction( name, javaCodePos, lineNumber, types, needThisParameter );
@ -970,7 +970,7 @@ public abstract class WasmCodeBuilder {
* @param method
* the BootstrapMethod, described the method that should be executed
* @param factorySignature
* Get the signature of the factory method. For example "()Ljava.lang.Runnable;" for the lamba expression
* Get the signature of the factory method. For example "()Ljava.lang.Runnable;" for the lambda expression
* <code>Runnable run = () -&gt; foo();</code>
* @param interfaceMethodName
* The simple name of the generated method of the single function interface.
@ -981,17 +981,7 @@ public abstract class WasmCodeBuilder {
*/
protected void addInvokeDynamic( BootstrapMethod method, String factorySignature, String interfaceMethodName, int javaCodePos, int lineNumber ) {
// Create the synthetic lambda class that hold the lambda expression.
ValueTypeParser parser = new ValueTypeParser( factorySignature, types );
ArrayList<AnyType> params = new ArrayList<>();
do {
AnyType param = parser.next();
if( param == null ) {
break;
}
params.add( param );
} while( true );
StructType interfaceType = (StructType)parser.next();
LambdaType type = types.lambdaType( method, params, interfaceType, interfaceMethodName );
LambdaType type = types.lambdaType( method, factorySignature, interfaceMethodName, lineNumber );
functions.markAsNeeded( type.getLambdaMethod(), true );
String lambdaTypeName = type.getName();

View File

@ -76,6 +76,7 @@ public class StructsNonGC extends AbstractBaseTest {
addParam( list, script, "lambda1" );
addParam( list, script, "lambda2" );
addParam( list, script, "lambda3" );
addParam( list, script, "lambdaWithInstanceAccess" );
addParam( list, script, "simpleName_Object" );
addParam( list, script, "simpleName_Anonymous" );
addParam( list, script, "simpleName_Array" );
@ -336,6 +337,18 @@ public class StructsNonGC extends AbstractBaseTest {
return val.applyAsDouble( 13 );
}
@Export
public static int lambdaWithInstanceAccess() {
TestClass test = new TestClass();
return test.lambdaWithInstanceAccessImpl();
}
private int field;
private int lambdaWithInstanceAccessImpl() {
field = 13;
IntSupplier val = () -> field;
return val.getAsInt();
}
@Export
static boolean isPrimitive_int() {
return int.class.isPrimitive();