diff --git a/src/de/inetsoftware/jwebassembly/module/TypeManager.java b/src/de/inetsoftware/jwebassembly/module/TypeManager.java index d69e7ab..f60be2b 100644 --- a/src/de/inetsoftware/jwebassembly/module/TypeManager.java +++ b/src/de/inetsoftware/jwebassembly/module/TypeManager.java @@ -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 + * Runnable run = () -> foo(); * @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 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 parser = new ValueTypeParser( factorySignature, this ); + ArrayList 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 params, StructType interfaceType, FunctionName syntheticLambdaFunctionName, String interfaceMethodName, @Nonnull TypeManager manager ) { + LambdaType( @Nonnull String name, @Nonnull BootstrapMethod method, ArrayList 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; } }; diff --git a/src/de/inetsoftware/jwebassembly/module/WasmCodeBuilder.java b/src/de/inetsoftware/jwebassembly/module/WasmCodeBuilder.java index 995cb4e..ad4ce64 100644 --- a/src/de/inetsoftware/jwebassembly/module/WasmCodeBuilder.java +++ b/src/de/inetsoftware/jwebassembly/module/WasmCodeBuilder.java @@ -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 * Runnable run = () -> foo(); * @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 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(); diff --git a/test/de/inetsoftware/jwebassembly/runtime/StructsNonGC.java b/test/de/inetsoftware/jwebassembly/runtime/StructsNonGC.java index 0c07fe8..f7dac9f 100644 --- a/test/de/inetsoftware/jwebassembly/runtime/StructsNonGC.java +++ b/test/de/inetsoftware/jwebassembly/runtime/StructsNonGC.java @@ -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();