2018-06-01 19:44:40 +02:00
/ *
* Copyright 2017 - 2018 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.jwebassembly.module ;
import java.io.IOException ;
2018-06-21 18:49:55 +02:00
import java.util.List ;
2018-06-01 19:44:40 +02:00
import java.util.Map ;
import java.util.function.Consumer ;
import javax.annotation.Nonnull ;
2018-11-04 20:28:42 +01:00
import javax.annotation.Nullable ;
2018-06-01 19:44:40 +02:00
import de.inetsoftware.classparser.ClassFile ;
import de.inetsoftware.classparser.Code ;
import de.inetsoftware.classparser.CodeInputStream ;
2018-10-13 18:10:05 +02:00
import de.inetsoftware.classparser.LocalVariableTable ;
2018-06-01 19:44:40 +02:00
import de.inetsoftware.classparser.MethodInfo ;
import de.inetsoftware.jwebassembly.WasmException ;
/ * *
* Generate the WebAssembly output .
*
* @author Volker Berlin
* /
public class ModuleGenerator {
2018-11-04 20:28:42 +01:00
private final ModuleWriter writer ;
2018-06-01 19:44:40 +02:00
2018-11-04 20:28:42 +01:00
private final JavaMethodWasmCodeBuilder codeBuilder = new JavaMethodWasmCodeBuilder ( ) ;
2018-06-01 19:44:40 +02:00
2018-11-04 20:28:42 +01:00
private String sourceFile ;
2018-06-01 19:44:40 +02:00
2018-11-04 20:28:42 +01:00
private String className ;
2018-06-21 18:49:55 +02:00
2018-06-01 19:44:40 +02:00
/ * *
* Create a new generator .
*
* @param writer
* the target writer
* /
public ModuleGenerator ( @Nonnull ModuleWriter writer ) {
this . writer = writer ;
}
/ * *
* Prepare the content of the class .
*
* @param classFile
* the class file
* @throws WasmException
* if some Java code can ' t converted
* /
public void prepare ( ClassFile classFile ) {
iterateMethods ( classFile , m - > prepareMethod ( m ) ) ;
}
/ * *
* Finish the prepare after all classes / methods are prepare . This must be call before we can start with write the
* first method .
* /
public void prepareFinish ( ) {
writer . prepareFinish ( ) ;
}
/ * *
* Write the content of the class to the writer .
*
* @param classFile
* the class file
* @throws WasmException
* if some Java code can ' t converted
* /
public void write ( ClassFile classFile ) throws WasmException {
iterateMethods ( classFile , m - > writeMethod ( m ) ) ;
}
private void iterateMethods ( ClassFile classFile , Consumer < MethodInfo > handler ) throws WasmException {
sourceFile = null ; // clear previous value for the case an IO exception occur
2018-10-13 21:51:34 +02:00
className = null ;
2018-06-01 19:44:40 +02:00
try {
sourceFile = classFile . getSourceFile ( ) ;
2018-10-13 21:51:34 +02:00
className = classFile . getThisClass ( ) . getName ( ) ;
2018-06-01 19:44:40 +02:00
MethodInfo [ ] methods = classFile . getMethods ( ) ;
for ( MethodInfo method : methods ) {
Code code = method . getCode ( ) ;
2018-11-10 10:46:36 +01:00
if ( method . getName ( ) . equals ( " <init> " ) & & method . getType ( ) . equals ( " ()V " )
2018-06-01 19:44:40 +02:00
& & code . isSuperInitReturn ( classFile . getSuperClass ( ) ) ) {
continue ; //default constructor
}
handler . accept ( method ) ;
}
} catch ( IOException ioex ) {
2018-10-13 21:51:34 +02:00
throw WasmException . create ( ioex , sourceFile , className , - 1 ) ;
2018-06-01 19:44:40 +02:00
}
}
/ * *
* Prepare the method .
*
* @param method
* the method
* @throws WasmException
* if some Java code can ' t converted
* /
private void prepareMethod ( MethodInfo method ) throws WasmException {
try {
FunctionName name = new FunctionName ( method ) ;
2018-06-11 19:06:03 +02:00
Map < String , Object > annotationValues = method . getAnnotation ( " de.inetsoftware.jwebassembly.api.annotation.Import " ) ;
2018-06-01 19:44:40 +02:00
if ( annotationValues ! = null ) {
String impoarModule = ( String ) annotationValues . get ( " module " ) ;
String importName = ( String ) annotationValues . get ( " name " ) ;
writer . prepareImport ( name , impoarModule , importName ) ;
2018-11-04 20:28:42 +01:00
writeMethodSignature ( method , null , null ) ;
2018-06-01 19:44:40 +02:00
} else {
writer . prepareFunction ( name ) ;
}
2018-08-06 12:23:42 +02:00
} catch ( Exception ioex ) {
2018-10-13 21:51:34 +02:00
throw WasmException . create ( ioex , sourceFile , className , - 1 ) ;
2018-06-01 19:44:40 +02:00
}
}
/ * *
* Write the content of a method .
*
* @param method
* the method
* @throws WasmException
* if some Java code can ' t converted
* /
private void writeMethod ( MethodInfo method ) throws WasmException {
2018-06-03 11:54:45 +02:00
CodeInputStream byteCode = null ;
2018-06-01 19:44:40 +02:00
try {
Code code = method . getCode ( ) ;
2018-06-11 19:06:03 +02:00
if ( code ! = null & & method . getAnnotation ( " de.inetsoftware.jwebassembly.api.annotation.Import " ) = = null ) { // abstract methods and interface methods does not have code
2018-06-01 19:44:40 +02:00
FunctionName name = new FunctionName ( method ) ;
writeExport ( name , method ) ;
writer . writeMethodStart ( name ) ;
2018-11-10 10:46:36 +01:00
codeBuilder . buildCode ( code , ! method . getType ( ) . endsWith ( " )V " ) ) ;
2018-11-04 20:28:42 +01:00
writeMethodSignature ( method , code . getLocalVariableTable ( ) , codeBuilder ) ;
2018-06-01 19:44:40 +02:00
2018-11-04 20:28:42 +01:00
for ( WasmInstruction instruction : codeBuilder . getInstructions ( ) ) {
2018-06-21 18:49:55 +02:00
instruction . writeTo ( writer ) ;
}
2018-08-14 22:09:01 +02:00
writer . writeMethodFinish ( ) ;
2018-06-01 19:44:40 +02:00
}
} catch ( Exception ioex ) {
2018-06-03 11:54:45 +02:00
int lineNumber = byteCode = = null ? - 1 : byteCode . getLineNumber ( ) ;
2018-10-13 21:51:34 +02:00
throw WasmException . create ( ioex , sourceFile , className , lineNumber ) ;
2018-06-01 19:44:40 +02:00
}
}
/ * *
* Look for a Export annotation and if there write an export directive .
*
* @param name
* the function name
* @param method
* the method
*
* @throws IOException
* if any IOException occur
* /
private void writeExport ( FunctionName name , MethodInfo method ) throws IOException {
2018-06-11 19:06:03 +02:00
Map < String , Object > export = method . getAnnotation ( " de.inetsoftware.jwebassembly.api.annotation.Export " ) ;
2018-06-01 19:44:40 +02:00
if ( export ! = null ) {
String exportName = ( String ) export . get ( " name " ) ;
if ( exportName = = null ) {
exportName = method . getName ( ) ; // TODO naming conversion rule if no name was set
}
writer . writeExport ( name , exportName ) ;
}
}
/ * *
* Write the parameter and return signatures
*
* @param method
* the method
2018-11-04 20:28:42 +01:00
* @param variables
* Java variable table with names of the variables for debugging
* @param codeBuilder
* the calculated variables
2018-06-01 19:44:40 +02:00
* @throws IOException
* if any I / O error occur
* @throws WasmException
* if some Java code can ' t converted
* /
2018-11-04 20:28:42 +01:00
private void writeMethodSignature ( MethodInfo method , @Nullable LocalVariableTable variables , WasmCodeBuilder codeBuilder ) throws IOException , WasmException {
2018-11-10 10:46:36 +01:00
String signature = method . getType ( ) ;
2018-06-01 19:44:40 +02:00
String kind = " param " ;
int paramCount = 0 ;
ValueType type = null ;
for ( int i = 1 ; i < signature . length ( ) ; i + + ) {
if ( signature . charAt ( i ) = = ')' ) {
kind = " result " ;
continue ;
}
2018-10-13 18:10:05 +02:00
String name = null ;
2018-08-14 22:09:01 +02:00
if ( kind = = " param " ) {
2018-10-13 18:10:05 +02:00
if ( variables ! = null ) {
name = variables . getPosition ( paramCount ) . getName ( method . getConstantPool ( ) ) ;
}
2018-08-14 22:09:01 +02:00
paramCount + + ;
}
2018-08-14 12:14:36 +02:00
type = ValueType . getValueType ( signature , i ) ;
2018-06-01 19:44:40 +02:00
if ( type ! = null ) {
2018-10-13 18:10:05 +02:00
writer . writeMethodParam ( kind , type , name ) ;
2018-06-01 19:44:40 +02:00
}
}
2018-11-04 20:28:42 +01:00
if ( codeBuilder ! = null ) {
List < ValueType > localTypes = codeBuilder . getLocalTypes ( paramCount ) ;
for ( int i = 0 ; i < localTypes . size ( ) ; i + + ) {
type = localTypes . get ( i ) ;
String name = null ;
if ( variables ! = null ) {
name = variables . getPosition ( paramCount ) . getName ( method . getConstantPool ( ) ) ;
2018-06-01 19:44:40 +02:00
}
2018-11-04 20:28:42 +01:00
writer . writeMethodParam ( " local " , type , name ) ;
2018-06-01 19:44:40 +02:00
}
}
2018-11-04 20:28:42 +01:00
writer . writeMethodParamFinish ( ) ;
2018-06-01 19:44:40 +02:00
}
}