From 45b5a4a955cb0b6695aff27a5a06fb6966e965af Mon Sep 17 00:00:00 2001 From: Volker Berlin Date: Sat, 25 Jan 2020 21:17:42 +0100 Subject: [PATCH] InvokeDynamic, WIP --- .../classparser/BootstrapMethod.java | 56 +++++++++++++++++++ .../inetsoftware/classparser/ClassFile.java | 26 +++++++++ .../classparser/ConstantInvokeDynamic.java | 36 +++++++----- .../module/JavaMethodWasmCodeBuilder.java | 18 +++++- 4 files changed, 118 insertions(+), 18 deletions(-) create mode 100644 src/de/inetsoftware/classparser/BootstrapMethod.java diff --git a/src/de/inetsoftware/classparser/BootstrapMethod.java b/src/de/inetsoftware/classparser/BootstrapMethod.java new file mode 100644 index 0000000..63f0541 --- /dev/null +++ b/src/de/inetsoftware/classparser/BootstrapMethod.java @@ -0,0 +1,56 @@ +/* + Copyright 2020 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. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +package de.inetsoftware.classparser; + +import java.io.DataInputStream; +import java.io.IOException; + +/** + * https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.23 + * + * @author Volker Berlin + */ +public class BootstrapMethod { + + /** + * Signature and return type of method to be implemented by the function object. + */ + private String samMethodType; + + /** + * A direct method handle describing the implementation method which should be called + */ + private ConstantMethodRef implMethod; + + /** + * The signature and return type that should be enforced dynamically at invocation time. This may be the same as + * {@code samMethodType}, or may be a specialization of it. + */ + private String instantiatedMethodType; + + BootstrapMethod( DataInputStream input, ConstantPool constantPool ) throws IOException { + 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; + + int argCount = input.readUnsignedShort(); // ever: 3 parameters + + // the 3 values + samMethodType = (String)constantPool.get( input.readUnsignedShort() ); + implMethod = (ConstantMethodRef)constantPool.get( input.readUnsignedShort() ); + instantiatedMethodType = (String)constantPool.get( input.readUnsignedShort() ); + } +} diff --git a/src/de/inetsoftware/classparser/ClassFile.java b/src/de/inetsoftware/classparser/ClassFile.java index aa4db6a..b48df60 100644 --- a/src/de/inetsoftware/classparser/ClassFile.java +++ b/src/de/inetsoftware/classparser/ClassFile.java @@ -62,6 +62,8 @@ public class ClassFile { private Map> annotations; + private BootstrapMethod[] bootstrapMethods; + /** * Load a class file and create a model of the class. * @@ -200,6 +202,30 @@ public class ClassFile { return annotations.get( annotation ); } + /** + * Get the x-the BootstrapMethod. Bootstrap methods are used for creating an lambda object. + * + * @param methodIdx + * the index of the method + * @return the method + * @throws IOException + * if any error occur + */ + public BootstrapMethod getBootstrapMethod( int methodIdx ) throws IOException { + if( bootstrapMethods == null ) { + AttributeInfo data = attributes.get( "BootstrapMethods" ); + if( data != null ) { + DataInputStream input = data.getDataInputStream(); + int count = input.readUnsignedShort(); + bootstrapMethods = new BootstrapMethod[count]; + for( int i = 0; i < count; i++ ) { + bootstrapMethods[i] = new BootstrapMethod( input, constantPool ); + } + } + } + return bootstrapMethods[methodIdx]; + } + public ConstantPool getConstantPool() { return constantPool; } diff --git a/src/de/inetsoftware/classparser/ConstantInvokeDynamic.java b/src/de/inetsoftware/classparser/ConstantInvokeDynamic.java index d8bf465..3e14b81 100644 --- a/src/de/inetsoftware/classparser/ConstantInvokeDynamic.java +++ b/src/de/inetsoftware/classparser/ConstantInvokeDynamic.java @@ -1,5 +1,5 @@ /* - Copyright 2019 Volker Berlin (i-net software) + Copyright 2019 - 2020 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. @@ -19,10 +19,12 @@ package de.inetsoftware.classparser; /** * @author Volker Berlin */ -public class ConstantInvokeDynamic implements Member { +public class ConstantInvokeDynamic { private final ConstantNameAndType nameAndType; + private final int bootstrapMethodIndex; + /** * Invoke dynamic info in the constant pool. * https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.4.10 @@ -33,31 +35,35 @@ public class ConstantInvokeDynamic implements Member { * the name and type */ ConstantInvokeDynamic( int bootstrapMethodAttrIndex, ConstantNameAndType nameAndType ) { + this.bootstrapMethodIndex = bootstrapMethodAttrIndex; this.nameAndType = nameAndType; } /** - * {@inheritDoc} + * The simple name of the generated method of the single function interface. + * + * @return the name */ - @Override public String getName() { return nameAndType.getName(); } /** - * {@inheritDoc} + * Get the signature of the factory method. For example "()Ljava.lang.Runnable;" for the lamba expression "Runnable + * run = () -> foo();" + * + * @return the type */ - @Override - public String getClassName() { - return null; - } - - /** - * Get the type of the method. For example "(Ljava.lang.String;)I" - */ - @Override public String getType() { return nameAndType.getType(); } -} \ No newline at end of file + /** + * Get the index to the bootstrap methods. + * + * @return the index + */ + public int getBootstrapMethodIndex() { + return bootstrapMethodIndex; + } +} diff --git a/src/de/inetsoftware/jwebassembly/module/JavaMethodWasmCodeBuilder.java b/src/de/inetsoftware/jwebassembly/module/JavaMethodWasmCodeBuilder.java index 526ba5e..097d45d 100644 --- a/src/de/inetsoftware/jwebassembly/module/JavaMethodWasmCodeBuilder.java +++ b/src/de/inetsoftware/jwebassembly/module/JavaMethodWasmCodeBuilder.java @@ -19,9 +19,12 @@ import java.io.IOException; import javax.annotation.Nonnull; +import de.inetsoftware.classparser.BootstrapMethod; +import de.inetsoftware.classparser.ClassFile; import de.inetsoftware.classparser.Code; import de.inetsoftware.classparser.CodeInputStream; import de.inetsoftware.classparser.ConstantClass; +import de.inetsoftware.classparser.ConstantInvokeDynamic; import de.inetsoftware.classparser.ConstantPool; import de.inetsoftware.classparser.ConstantRef; import de.inetsoftware.classparser.MethodInfo; @@ -62,7 +65,7 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder { byteCode = code.getByteCode(); boolean hasReturn = !method.getType().endsWith( ")V" ); - writeCode( byteCode, code.getConstantPool(), hasReturn ); + writeCode( byteCode, code.getConstantPool(), method.getDeclaringClassFile(), hasReturn ); calculateVariables(); } catch( Exception ioex ) { int lineNumber = byteCode == null ? -1 : byteCode.getLineNumber(); @@ -77,12 +80,14 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder { * a stream of byte code * @param constantPool * the constant pool of the the current class + * @param classFile + * the declaring class file * @param hasReturn * if the method has a return value * @throws WasmException * if some Java code can't converted */ - private void writeCode( CodeInputStream byteCode, ConstantPool constantPool, boolean hasReturn ) throws WasmException { + private void writeCode( CodeInputStream byteCode, ConstantPool constantPool, ClassFile classFile, boolean hasReturn ) throws WasmException { int lineNumber = -1; try { boolean wide = false; @@ -589,7 +594,14 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder { } break; //TODO case 185: // invokeinterface - //TODO case 186: // invokedynamic + case 186: // invokedynamic + idx = byteCode.readUnsignedShort(); + ConstantInvokeDynamic dynamic = (ConstantInvokeDynamic)constantPool.get( idx ); + idx = byteCode.readUnsignedShort(); // ever zero + idx = dynamic.getBootstrapMethodIndex(); + BootstrapMethod method = classFile.getBootstrapMethod( idx ); + throw new WasmException( "InvokeDynamic/Lambda is not supported.", lineNumber ); + //TODO break; case 187: // new String name = ((ConstantClass)constantPool.get( byteCode.readUnsignedShort() )).getName(); addStructInstruction( StructOperator.NEW_DEFAULT, name, null, codePos, lineNumber );