diff --git a/src/de/inetsoftware/classparser/BootstrapMethod.java b/src/de/inetsoftware/classparser/BootstrapMethod.java index 63f0541..6a37655 100644 --- a/src/de/inetsoftware/classparser/BootstrapMethod.java +++ b/src/de/inetsoftware/classparser/BootstrapMethod.java @@ -1,5 +1,5 @@ /* - Copyright 2020 Volker Berlin (i-net software) + Copyright 2020 - 2021 Volker Berlin (i-net software) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -42,7 +42,11 @@ public class BootstrapMethod { */ private String instantiatedMethodType; + /** + * Create an instance. + */ BootstrapMethod( DataInputStream input, ConstantPool constantPool ) throws IOException { + //TODO check that it is a known implementation type int ref = input.readUnsignedShort(); //ConstantMethodRef method = (ConstantMethodRef)constantPool.get( ref ); // ever: java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; @@ -53,4 +57,13 @@ public class BootstrapMethod { implMethod = (ConstantMethodRef)constantPool.get( input.readUnsignedShort() ); instantiatedMethodType = (String)constantPool.get( input.readUnsignedShort() ); } + + /** + * The real method in the parent class that implements the lambda expression + * + * @return the method + */ + public ConstantMethodRef getImplMethod() { + return implMethod; + } } diff --git a/src/de/inetsoftware/jwebassembly/module/JavaMethodWasmCodeBuilder.java b/src/de/inetsoftware/jwebassembly/module/JavaMethodWasmCodeBuilder.java index 2b9b095..d090018 100644 --- a/src/de/inetsoftware/jwebassembly/module/JavaMethodWasmCodeBuilder.java +++ b/src/de/inetsoftware/jwebassembly/module/JavaMethodWasmCodeBuilder.java @@ -631,10 +631,11 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder { idx = byteCode.readUnsignedShort(); // ever zero idx = dynamic.getBootstrapMethodIndex(); BootstrapMethod method = classFile.getBootstrapMethod( idx ); - throw new WasmException( "InvokeDynamic/Lambda is not supported.", lineNumber ); - //TODO break; + String name = dynamic.getType(); + addInvokeDynamic( method, name, dynamic.getName(), codePos, lineNumber ); + break; case 187: // new - String name = ((ConstantClass)constantPool.get( byteCode.readUnsignedShort() )).getName(); + name = ((ConstantClass)constantPool.get( byteCode.readUnsignedShort() )).getName(); addStructInstruction( StructOperator.NEW_DEFAULT, name, null, codePos, lineNumber ); break; case 188: // newarray diff --git a/src/de/inetsoftware/jwebassembly/module/TypeManager.java b/src/de/inetsoftware/jwebassembly/module/TypeManager.java index 938be36..9d850df 100644 --- a/src/de/inetsoftware/jwebassembly/module/TypeManager.java +++ b/src/de/inetsoftware/jwebassembly/module/TypeManager.java @@ -357,6 +357,29 @@ public class TypeManager { return type; } + /** + * Create a lambda type + * + * @param typeName + * the name (className) of the lambda class + * @param interfaceType + * the implemented interface + * @param methodName + * the real method in the parent class that implements the lambda expression + * @param interfaceMethodName + * the name of the implemented method in the interface + * @return the type + */ + StructType lambdaType( String typeName, StructType interfaceType, FunctionName methodName, String interfaceMethodName ) { + StructType type = structTypes.get( typeName ); + if( type == null ) { + type = new LambdaType( typeName, interfaceType, methodName, interfaceMethodName, this ); + + structTypes.put( typeName, type ); + } + return type; + } + /** * Create the FunctionName for a virtual call. The function has 2 parameters (THIS, * virtualfunctionIndex) and returns the index of the function. @@ -506,7 +529,7 @@ public class TypeManager { * @author Volker Berlin */ public static enum StructTypeKind { - primitive, normal, array, array_native; + primitive, normal, array, array_native, lambda; } /** @@ -602,6 +625,15 @@ public class TypeManager { case array_native: fields.add( new NamedStorageType( ((ArrayType)this).getArrayType(), null, null ) ); break; + case lambda: + allNeededFields = new HashSet<>(); + listStructFields( "java/lang/Object", functions, types, classFileLoader, allNeededFields ); + LambdaType lambda = (LambdaType)this; + List iMethods = new ArrayList<>(); + iMethods.add( lambda.getLambdaMethod() ); + interfaceMethods.put( lambda.getInterfaceType(), iMethods ); + functions.setITableIndex( new FunctionName( lambda.getInterfaceType().name, lambda.getInterfaceMethodName(), lambda.getLambdaMethod().signature ), 2 ); + break; default: // add all interfaces to the instanceof set listInterfaces( functions, types, classFileLoader ); @@ -1029,4 +1061,64 @@ public class TypeManager { return "$" + name; } } + + /** + * A generated type that represent a lambda expression + */ + class LambdaType extends StructType { + + private StructType interfaceType; + + private FunctionName methodName; + + private String interfaceMethodName; + + /** + * Create a lambda type + * + * @param name + * the Java class name + * @param interfaceType + * the implemented interface type + * @param methodName + * the real method in the parent class that implements the lambda expression + * @param interfaceMethodName + * the name of the implemented method in the interface + * @param manager + * the manager which hold all StructTypes + */ + LambdaType( String name, StructType interfaceType, FunctionName methodName, String interfaceMethodName, TypeManager manager ) { + super( name, StructTypeKind.lambda, manager ); + this.interfaceType = interfaceType; + this.methodName = methodName; + this.interfaceMethodName = interfaceMethodName; + } + + /** + * The implemented interface type + * + * @return the interface type + */ + StructType getInterfaceType() { + return interfaceType; + } + + /** + * The real method in the parent class that implements the lambda expression + * + * @return the function name + */ + FunctionName getLambdaMethod() { + return methodName; + } + + /** + * The name of the implemented method in the interface + * + * @return the name + */ + String getInterfaceMethodName() { + return interfaceMethodName; + } + } } diff --git a/src/de/inetsoftware/jwebassembly/module/WasmCodeBuilder.java b/src/de/inetsoftware/jwebassembly/module/WasmCodeBuilder.java index 3aa590a..e1998e1 100644 --- a/src/de/inetsoftware/jwebassembly/module/WasmCodeBuilder.java +++ b/src/de/inetsoftware/jwebassembly/module/WasmCodeBuilder.java @@ -25,14 +25,17 @@ import javax.annotation.Nonnegative; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import de.inetsoftware.classparser.BootstrapMethod; import de.inetsoftware.classparser.ClassFile; import de.inetsoftware.classparser.ConstantClass; +import de.inetsoftware.classparser.ConstantMethodRef; import de.inetsoftware.classparser.LocalVariableTable; import de.inetsoftware.classparser.Member; import de.inetsoftware.classparser.MethodInfo; import de.inetsoftware.jwebassembly.WasmException; import de.inetsoftware.jwebassembly.javascript.NonGC; import de.inetsoftware.jwebassembly.module.StackInspector.StackValue; +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; @@ -844,6 +847,36 @@ public abstract class WasmCodeBuilder { } } + /** + * Add invoke dynamic operation. + * + * @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 + * Runnable run = () -> foo(); + * @param interfaceMethodName + * The simple name of the generated method of the single function interface. + * @param javaCodePos + * the code position/offset in the Java method + * @param lineNumber + * the line number in the Java source code + */ + protected void addInvokeDynamic( BootstrapMethod method, String factorySignature, String interfaceMethodName, int javaCodePos, int lineNumber ) { + ConstantMethodRef implMethod = method.getImplMethod(); + FunctionName name = new FunctionName( implMethod ); + functions.markAsNeeded( name ); + String typeName = implMethod.getClassName() + "$$" + implMethod.getName() + "/"; + ValueTypeParser parser = new ValueTypeParser( factorySignature, types ); + while( parser.next() != null ) { + // skip parameters TODO + } + StructType interfaceType = (StructType)parser.next(); + StructType type = types.lambdaType( typeName, interfaceType, name, interfaceMethodName ); + addStructInstruction( StructOperator.NEW_DEFAULT, typeName, null, javaCodePos, lineNumber ); + throw new WasmException( "InvokeDynamic/Lambda is not supported.", lineNumber ); + } + /** * Create an instance of a load/store to the linear memory instruction *