2018-06-01 19:44:40 +02:00
/ *
2022-02-20 18:29:13 +01:00
* Copyright 2017 - 2022 Volker Berlin ( i - net software )
2018-06-01 19:44:40 +02:00
*
* 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 ;
2022-03-08 15:13:19 +01:00
import static de.inetsoftware.jwebassembly.module.WasmCodeBuilder.CLASS_INIT ;
2019-02-24 20:02:36 +01:00
import java.io.BufferedInputStream ;
import java.io.File ;
2018-06-01 19:44:40 +02:00
import java.io.IOException ;
2018-11-21 19:44:05 +01:00
import java.net.URL ;
import java.net.URLClassLoader ;
2019-02-24 20:02:36 +01:00
import java.nio.file.Files ;
import java.nio.file.Path ;
2022-04-10 11:35:20 +02:00
import java.util.HashSet ;
2019-01-18 17:48:12 +01:00
import java.util.Iterator ;
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 ;
2019-09-07 21:24:17 +02:00
import java.util.function.Function ;
2019-11-26 19:12:13 +01:00
import java.util.logging.Level ;
2019-02-24 20:02:36 +01:00
import java.util.zip.ZipEntry ;
import java.util.zip.ZipInputStream ;
2018-06-01 19:44:40 +02:00
import javax.annotation.Nonnull ;
2019-05-11 20:53:30 +02:00
import javax.annotation.Nullable ;
2018-06-01 19:44:40 +02:00
import de.inetsoftware.classparser.ClassFile ;
import de.inetsoftware.classparser.Code ;
2019-04-21 21:33:22 +02:00
import de.inetsoftware.classparser.ConstantClass ;
2018-06-01 19:44:40 +02:00
import de.inetsoftware.classparser.MethodInfo ;
2018-11-16 17:46:10 +01:00
import de.inetsoftware.jwebassembly.JWebAssembly ;
2018-06-01 19:44:40 +02:00
import de.inetsoftware.jwebassembly.WasmException ;
2019-07-03 20:09:22 +02:00
import de.inetsoftware.jwebassembly.javascript.JavaScriptWriter ;
2019-01-06 16:29:26 +01:00
import de.inetsoftware.jwebassembly.module.TypeManager.StructType ;
2021-08-29 13:44:29 +02:00
import de.inetsoftware.jwebassembly.module.nativecode.ReplacementForEnums ;
2019-01-14 20:09:00 +01:00
import de.inetsoftware.jwebassembly.wasm.AnyType ;
2020-03-20 20:35:13 +01:00
import de.inetsoftware.jwebassembly.wasm.FunctionType ;
2018-12-03 21:09:22 +01:00
import de.inetsoftware.jwebassembly.wasm.ValueType ;
2020-04-26 00:08:50 +02:00
import de.inetsoftware.jwebassembly.wasm.ValueTypeParser ;
2018-11-16 20:38:37 +01:00
import de.inetsoftware.jwebassembly.watparser.WatParser ;
2018-06-01 19:44:40 +02:00
/ * *
* 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
2019-07-03 20:09:22 +02:00
private final JavaScriptWriter javaScript ;
2020-01-02 15:15:21 +01:00
private final ClassFileLoader classFileLoader ;
2018-11-21 19:44:05 +01:00
2019-08-08 19:28:27 +02:00
private final JavaMethodWasmCodeBuilder javaCodeBuilder ;
2018-11-16 20:38:37 +01:00
2019-08-08 19:28:27 +02:00
private final WatParser watParser ;
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
2020-04-10 22:22:45 +02:00
private String methodName ;
2020-02-24 21:08:29 +01:00
private final FunctionManager functions ;
2018-11-24 21:54:30 +01:00
2020-02-24 21:08:29 +01:00
private final TypeManager types ;
2018-12-19 20:10:26 +01:00
2020-02-24 21:08:29 +01:00
private final StringManager strings ;
2019-11-10 12:57:58 +01:00
2020-02-24 21:08:29 +01:00
private final CodeOptimizer optimizer ;
2019-02-12 21:18:42 +01:00
2022-03-06 14:41:53 +01:00
private final StaticCodeBuilder staticCodeBuilder ;
2022-04-10 11:35:20 +02:00
private final HashSet < String > exportNames = new HashSet < > ( ) ;
2018-06-01 19:44:40 +02:00
/ * *
* Create a new generator .
*
* @param writer
* the target writer
2019-07-03 20:09:22 +02:00
* @param target
* the target for the module data
2018-11-21 19:44:05 +01:00
* @param libraries
* libraries
2018-06-01 19:44:40 +02:00
* /
2019-09-10 17:49:05 +02:00
public ModuleGenerator ( @Nonnull ModuleWriter writer , WasmTarget target , @Nonnull List < URL > libraries ) {
2019-08-14 20:07:39 +02:00
this . watParser = new WatParser ( ) ;
2022-02-27 20:44:42 +01:00
this . javaCodeBuilder = new JavaMethodWasmCodeBuilder ( watParser ) ;
2018-06-01 19:44:40 +02:00
this . writer = writer ;
2019-07-03 20:09:22 +02:00
this . javaScript = new JavaScriptWriter ( target ) ;
2020-01-02 15:15:21 +01:00
this . classFileLoader = new ClassFileLoader ( new URLClassLoader ( libraries . toArray ( new URL [ libraries . size ( ) ] ) ) ) ;
2019-09-10 17:49:05 +02:00
WasmOptions options = writer . options ;
2020-02-24 21:08:29 +01:00
functions = options . functions ;
types = options . types ;
strings = options . strings ;
optimizer = options . optimizer ;
javaCodeBuilder . init ( options , classFileLoader ) ;
( ( WasmCodeBuilder ) watParser ) . init ( options , classFileLoader ) ;
2021-03-28 15:16:45 +02:00
types . init ( classFileLoader ) ;
2022-03-06 14:41:53 +01:00
staticCodeBuilder = new StaticCodeBuilder ( writer . options , classFileLoader , javaCodeBuilder ) ;
2021-03-28 15:16:45 +02:00
2019-02-24 20:02:36 +01:00
scanLibraries ( libraries ) ;
}
/ * *
* Scan the libraries for annotated methods
*
* @param libraries
* libraries
* /
private void scanLibraries ( @Nonnull List < URL > libraries ) {
// search for replacement methods in the libraries
for ( URL url : libraries ) {
try {
File file = new File ( url . toURI ( ) ) ;
if ( file . isDirectory ( ) ) {
for ( Iterator < Path > iterator = Files . walk ( file . toPath ( ) ) . iterator ( ) ; iterator . hasNext ( ) ; ) {
Path path = iterator . next ( ) ;
if ( path . toString ( ) . endsWith ( " .class " ) ) {
ClassFile classFile = new ClassFile ( new BufferedInputStream ( Files . newInputStream ( path ) ) ) ;
prepare ( classFile ) ;
}
2019-03-31 13:29:40 +02:00
}
2019-02-24 20:02:36 +01:00
}
} catch ( Exception e ) {
e . printStackTrace ( ) ;
}
try ( ZipInputStream input = new ZipInputStream ( url . openStream ( ) ) ) {
do {
ZipEntry entry = input . getNextEntry ( ) ;
if ( entry = = null ) {
break ;
}
if ( entry . getName ( ) . endsWith ( " .class " ) ) {
2019-11-26 19:12:13 +01:00
try {
ClassFile classFile = new ClassFile ( new BufferedInputStream ( input ) {
@Override
public void close ( ) {
} // does not close the zip stream
} ) ;
prepare ( classFile ) ;
} catch ( Throwable th ) {
JWebAssembly . LOGGER . log ( Level . SEVERE , " Parsing error with " + entry . getName ( ) + " in " + url , th ) ;
}
2019-02-24 20:02:36 +01:00
}
} while ( true ) ;
} catch ( IOException e ) {
e . printStackTrace ( ) ;
}
}
2018-06-01 19:44:40 +02:00
}
/ * *
* Prepare the content of the class .
*
* @param classFile
* the class file
* @throws WasmException
* if some Java code can ' t converted
2020-01-03 19:51:58 +01:00
* @throws IOException
* if any I / O error occur
2018-06-01 19:44:40 +02:00
* /
2020-12-12 20:42:46 +01:00
public void prepare ( @Nonnull ClassFile classFile ) throws IOException {
2020-01-07 19:54:05 +01:00
classFileLoader . cache ( classFile ) ;
2020-01-03 19:51:58 +01:00
// check if this class replace another class
Map < String , Object > annotationValues ;
if ( ( annotationValues = classFile . getAnnotation ( JWebAssembly . REPLACE_ANNOTATION ) ) ! = null ) {
String signatureName = ( String ) annotationValues . get ( " value " ) ;
if ( signatureName ! = null ) {
classFileLoader . replace ( signatureName , classFile ) ;
}
}
2020-01-19 15:15:01 +01:00
// check if this class extends another class with partial code
if ( ( annotationValues = classFile . getAnnotation ( JWebAssembly . PARTIAL_ANNOTATION ) ) ! = null ) {
String signatureName = ( String ) annotationValues . get ( " value " ) ;
if ( signatureName ! = null ) {
classFileLoader . partial ( signatureName , classFile ) ;
}
}
2021-08-29 13:44:29 +02:00
2018-06-01 19:44:40 +02:00
iterateMethods ( classFile , m - > prepareMethod ( m ) ) ;
}
/ * *
2020-01-11 18:31:07 +01:00
* Scan all needed methods / functions in a loop . If the scan find more needed content then the loop continue .
2019-06-10 11:39:19 +02:00
*
2019-05-09 22:13:02 +02:00
* @throws IOException
* if any I / O error occur
2018-06-01 19:44:40 +02:00
* /
2020-01-11 18:31:07 +01:00
private void scanFunctions ( ) throws IOException {
2019-05-09 22:13:02 +02:00
FunctionName next ;
2019-05-12 18:38:11 +02:00
NEXT :
2019-05-09 22:13:02 +02:00
while ( ( next = functions . nextScannLater ( ) ) ! = null ) {
2020-02-23 17:50:23 +01:00
className = next . className ;
2020-04-10 22:22:45 +02:00
methodName = next . methodName ;
2020-04-05 21:03:13 +02:00
JWebAssembly . LOGGER . fine ( " scan " + next . signatureName ) ;
2020-01-12 19:13:20 +01:00
if ( next instanceof SyntheticFunctionName ) {
SyntheticFunctionName synth = ( SyntheticFunctionName ) next ;
if ( synth . hasWasmCode ( ) ) {
2020-02-29 16:14:32 +01:00
synth . getCodeBuilder ( watParser ) ;
2020-01-12 19:13:20 +01:00
} else {
functions . markAsImport ( synth , synth . getAnnotation ( ) ) ;
}
2021-04-17 20:27:28 +02:00
functions . markAsScanned ( next ) ;
2020-01-12 19:13:20 +01:00
continue ;
}
MethodInfo method = null ;
2020-01-02 15:15:21 +01:00
ClassFile classFile = classFileLoader . get ( next . className ) ;
2020-01-12 19:13:20 +01:00
if ( classFile ! = null ) {
2022-03-19 22:12:30 +01:00
//temporary Hack because the generated Enum.valueOf(String) use Reflection which currently is not supported
if ( classFile . isEnum ( ) & & " valueOf " . equals ( next . methodName ) & & next . signature . startsWith ( " (Ljava/lang/String;) " ) ) {
String replaceForEnums = ReplacementForEnums . class . getName ( ) . replace ( " . " , " / " ) ;
ClassFile file = classFileLoader . get ( replaceForEnums ) ;
classFileLoader . partial ( next . className , file ) ;
MethodInfo method2 = classFile . getMethod ( " valueOf_ " , next . signature ) ;
functions . addReplacement ( next , method2 ) ;
}
2020-02-23 17:50:23 +01:00
sourceFile = classFile . getSourceFile ( ) ;
className = classFile . getThisClass ( ) . getName ( ) ;
2020-01-12 19:13:20 +01:00
method = classFile . getMethod ( next . methodName , next . signature ) ;
}
if ( method = = null ) {
method = functions . replace ( next , null ) ;
}
if ( method ! = null ) {
2020-02-29 16:14:32 +01:00
createInstructions ( functions . replace ( next , method ) ) ;
2021-04-17 20:27:28 +02:00
functions . markAsScanned ( next ) ;
if ( functions . needThisParameter ( next ) ) {
2020-03-15 20:51:19 +01:00
types . valueOf ( next . className ) ; // for the case that the type unknown yet
}
2020-01-12 19:13:20 +01:00
continue ;
}
2021-05-30 21:29:50 +02:00
if ( classFile = = null ) {
// all extends from object, also arrays
classFile = classFileLoader . get ( " java/lang/Object " ) ;
}
2020-01-12 19:13:20 +01:00
// search if there is a super class with the same signature
ClassFile superClassFile = classFile ;
while ( superClassFile ! = null ) {
method = superClassFile . getMethod ( next . methodName , next . signature ) ;
if ( method ! = null ) {
FunctionName name = new FunctionName ( method ) ;
2021-04-17 20:27:28 +02:00
functions . markAsNeeded ( name , ! method . isStatic ( ) ) ;
2020-01-12 19:13:20 +01:00
functions . setAlias ( next , name ) ;
continue NEXT ; // we have found a super method
2019-05-11 20:53:30 +02:00
}
2020-01-12 19:13:20 +01:00
ConstantClass superClass = superClassFile . getSuperClass ( ) ;
superClassFile = superClass = = null ? null : classFileLoader . get ( superClass . getName ( ) ) ;
2019-05-09 22:13:02 +02:00
}
2019-05-12 18:38:11 +02:00
2020-01-12 19:13:20 +01:00
// search if there is a default implementation in an interface
superClassFile = classFile ;
while ( superClassFile ! = null ) {
2021-11-14 13:41:20 +01:00
if ( scanFunctionInterfaces ( superClassFile , next ) ) {
continue NEXT ; // we have found a super method
2020-01-12 17:13:52 +01:00
}
2020-01-12 19:13:20 +01:00
ConstantClass superClass = superClassFile . getSuperClass ( ) ;
superClassFile = superClass = = null ? null : classFileLoader . get ( superClass . getName ( ) ) ;
2019-05-11 20:53:30 +02:00
}
2020-01-12 19:13:20 +01:00
throw new WasmException ( " Missing function: " + next . signatureName , - 1 ) ;
2019-05-11 20:53:30 +02:00
}
2019-06-10 11:39:19 +02:00
}
2019-05-19 11:14:32 +02:00
2021-11-14 13:41:20 +01:00
/ * *
* Search if there is a default implementation in an interface for the given method .
*
* @param classFile
* the class to scan
* @param next
* the method to scan
* @return true , if method was found
* @throws IOException
* if any I / O error occur
* /
private boolean scanFunctionInterfaces ( ClassFile classFile , FunctionName next ) throws IOException {
// first scan direct interfaces
for ( ConstantClass iface : classFile . getInterfaces ( ) ) {
ClassFile iClassFile = classFileLoader . get ( iface . getName ( ) ) ;
MethodInfo method = iClassFile . getMethod ( next . methodName , next . signature ) ;
if ( method ! = null ) {
FunctionName name = new FunctionName ( method ) ;
functions . markAsNeeded ( name , ! method . isStatic ( ) ) ;
functions . setAlias ( next , name ) ;
return true ; // we have found a super method
}
}
// then possible super interfaces
for ( ConstantClass iface : classFile . getInterfaces ( ) ) {
ClassFile iClassFile = classFileLoader . get ( iface . getName ( ) ) ;
if ( scanFunctionInterfaces ( iClassFile , next ) ) {
return true ; // we have found a super method
}
}
return false ;
}
2019-06-10 11:39:19 +02:00
/ * *
* Finish the prepare after all classes / methods are prepare . This must be call before we can start with write the
* first method .
* @throws IOException
* if any I / O error occur
* /
public void prepareFinish ( ) throws IOException {
2020-03-15 20:51:19 +01:00
int functCount ;
do {
scanFunctions ( ) ;
2022-05-22 18:36:36 +02:00
functCount = functions . getNeededCount ( ) ; // scan the functions can find new needed types or only new needed fields in the known types
2020-04-26 20:30:49 +02:00
scanForClinit ( ) ;
2021-03-28 15:16:45 +02:00
types . scanTypeHierarchy ( ) ; // scan the type hierarchy can find new functions
2022-05-22 18:36:36 +02:00
} while ( functCount < functions . getNeededCount ( ) ) ;
2019-06-23 20:50:11 +02:00
// write only the needed imports to the output
for ( Iterator < FunctionName > iterator = functions . getNeededImports ( ) ; iterator . hasNext ( ) ; ) {
FunctionName name = iterator . next ( ) ;
functions . markAsWritten ( name ) ;
2019-09-07 21:24:17 +02:00
Function < String , Object > importAnannotation = functions . getImportAnannotation ( name ) ;
String importModule = ( String ) importAnannotation . apply ( " module " ) ;
2019-08-08 17:47:57 +02:00
if ( importModule = = null | | importModule . isEmpty ( ) ) {
2019-08-09 21:58:42 +02:00
// use className if module is not set
importModule = name . className . substring ( name . className . lastIndexOf ( '/' ) + 1 ) ;
2019-08-08 17:47:57 +02:00
}
2019-09-07 21:24:17 +02:00
String importName = ( String ) importAnannotation . apply ( " name " ) ;
2019-08-08 17:47:57 +02:00
if ( importName = = null | | importName . isEmpty ( ) ) {
2019-08-09 21:58:42 +02:00
// use method name as function if not set
importName = name . methodName ;
2019-08-08 17:47:57 +02:00
}
writer . prepareImport ( name , importModule , importName ) ;
2020-03-20 20:35:13 +01:00
writeMethodSignature ( name , FunctionType . Import , null ) ;
2019-08-08 17:47:57 +02:00
javaScript . addImport ( importModule , importName , importAnannotation ) ;
2019-06-23 20:50:11 +02:00
}
2020-04-02 21:01:11 +02:00
// add a start method for the static class constructors
prepareStartFunction ( ) ;
2019-06-23 20:50:11 +02:00
// init/write the function types
2020-03-18 21:37:56 +01:00
for ( Iterator < FunctionName > iterator = functions . getWriteLater ( ) ; iterator . hasNext ( ) ; ) {
2019-06-23 20:50:11 +02:00
FunctionName name = iterator . next ( ) ;
2020-03-20 20:35:13 +01:00
writeMethodSignature ( name , FunctionType . Code , null ) ;
2019-06-23 20:50:11 +02:00
}
2020-03-18 21:37:56 +01:00
// register types of abstract and interface methods
for ( Iterator < FunctionName > iterator = functions . getAbstractedFunctions ( ) ; iterator . hasNext ( ) ; ) {
FunctionName name = iterator . next ( ) ;
2020-03-20 20:35:13 +01:00
writeMethodSignature ( name , FunctionType . Abstract , null ) ;
2020-03-18 21:37:56 +01:00
}
2020-08-08 23:20:09 +02:00
// scan again if there are new types or new needed fields
2021-03-28 15:16:45 +02:00
types . scanTypeHierarchy ( ) ;
2020-06-13 19:51:33 +02:00
JWebAssembly . LOGGER . fine ( " scan finish " ) ;
2022-03-19 22:08:43 +01:00
writer . prepareFinish ( ) ;
2021-03-28 15:16:45 +02:00
types . prepareFinish ( writer ) ;
2019-06-10 11:39:19 +02:00
functions . prepareFinish ( ) ;
2019-11-10 12:57:58 +01:00
strings . prepareFinish ( writer ) ;
2019-05-11 20:53:30 +02:00
}
2020-04-05 21:03:13 +02:00
/ * *
* Scan for needed static constructors . The static code of all classes that used in any form must be executed .
*
* @throws IOException
* if any I / O error occur
* /
private void scanForClinit ( ) throws IOException {
JWebAssembly . LOGGER . fine ( " scan for needed <clinit> " ) ;
for ( Iterator < String > iterator = functions . getUsedClasses ( ) ; iterator . hasNext ( ) ; ) {
String className = iterator . next ( ) ;
ClassFile classFile = classFileLoader . get ( className ) ;
if ( classFile ! = null ) {
2022-03-08 15:13:19 +01:00
MethodInfo method = classFile . getMethod ( CLASS_INIT , " ()V " ) ;
2020-04-05 21:03:13 +02:00
if ( method ! = null ) {
2021-04-17 20:27:28 +02:00
functions . markAsNeeded ( new FunctionName ( method ) , false ) ;
2020-04-05 21:03:13 +02:00
}
}
}
}
2020-04-02 21:01:11 +02:00
/ * *
* Add a start method for the static class constructors
*
* @throws IOException
* if any I / O error occur
* /
private void prepareStartFunction ( ) throws IOException {
// add the start function/section only if there are static code
2022-03-06 14:41:53 +01:00
Iterator < FunctionName > writeLaterClinit = functions . getWriteLaterClinit ( ) ;
if ( writeLaterClinit . hasNext ( ) ) {
FunctionName start = staticCodeBuilder . createStartFunction ( writeLaterClinit ) ;
2021-04-17 20:27:28 +02:00
functions . markAsNeeded ( start , false ) ;
2020-04-10 11:40:07 +02:00
writeMethodSignature ( start , FunctionType . Start , null ) ;
2020-04-02 21:01:11 +02:00
}
}
2018-11-24 21:54:30 +01:00
/ * *
* Finish the code generation .
2018-11-25 15:11:42 +01:00
*
* @throws IOException
* if any I / O error occur
2018-11-24 21:54:30 +01:00
* /
2018-11-25 15:11:42 +01:00
public void finish ( ) throws IOException {
2019-11-24 14:44:56 +01:00
for ( Iterator < FunctionName > it = functions . getWriteLater ( ) ; it . hasNext ( ) ; ) {
FunctionName next = it . next ( ) ;
2020-02-23 20:17:37 +01:00
sourceFile = null ; // clear previous value for the case an IO exception occur
className = next . className ;
2020-04-10 22:22:45 +02:00
methodName = next . methodName ;
2019-11-24 14:44:56 +01:00
if ( next instanceof SyntheticFunctionName ) {
2020-01-11 20:31:05 +01:00
writeMethodImpl ( next , ( ( SyntheticFunctionName ) next ) . getCodeBuilder ( watParser ) ) ;
2019-01-20 10:41:33 +01:00
} else {
2020-01-02 15:15:21 +01:00
ClassFile classFile = classFileLoader . get ( next . className ) ;
2019-11-24 14:44:56 +01:00
if ( classFile = = null ) {
throw new WasmException ( " Missing function: " + next . signatureName , - 1 ) ;
} else {
2019-12-30 23:02:25 +01:00
sourceFile = classFile . getSourceFile ( ) ;
className = classFile . getThisClass ( ) . getName ( ) ;
2019-11-24 14:44:56 +01:00
MethodInfo method = classFile . getMethod ( next . methodName , next . signature ) ;
if ( method ! = null ) {
try {
Map < String , Object > wat = method . getAnnotation ( JWebAssembly . TEXTCODE_ANNOTATION ) ;
if ( wat ! = null ) {
String signature = ( String ) wat . get ( " signature " ) ;
if ( signature = = null ) {
signature = method . getType ( ) ;
}
next = new FunctionName ( method , signature ) ;
} else {
method = functions . replace ( next , method ) ;
}
if ( functions . needToWrite ( next ) ) {
writeMethod ( next , method ) ;
2019-01-20 10:41:33 +01:00
}
2020-02-23 20:17:37 +01:00
} catch ( Throwable ex ) {
2020-04-10 22:22:45 +02:00
throw WasmException . create ( ex , sourceFile , className , methodName , - 1 ) ;
2018-11-25 15:11:42 +01:00
}
2019-11-24 14:44:56 +01:00
} else {
if ( functions . needToWrite ( next ) ) {
throw new WasmException ( " Missing function: " + next . signatureName , - 1 ) ;
2019-01-20 10:41:33 +01:00
}
2018-11-25 15:11:42 +01:00
}
2019-11-24 14:44:56 +01:00
}
2018-11-25 15:11:42 +01:00
}
}
2019-07-03 20:09:22 +02:00
javaScript . finish ( ) ;
2018-11-24 21:54:30 +01:00
}
/ * *
* Iterate over all methods of the classFile and run the handler .
*
* @param classFile
* the classFile
* @param handler
* the handler
* @throws WasmException
* if some Java code can ' t converted
* /
2018-06-01 19:44:40 +02:00
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 ) {
handler . accept ( method ) ;
}
} catch ( IOException ioex ) {
2020-04-10 22:22:45 +02:00
throw WasmException . create ( ioex , sourceFile , className , methodName , - 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 ) ;
2020-04-10 22:22:45 +02:00
methodName = name . methodName ;
2019-11-17 17:04:35 +01:00
if ( functions . isKnown ( name ) ) {
return ;
}
2019-02-24 20:02:36 +01:00
Map < String , Object > annotationValues ;
2019-06-23 20:50:11 +02:00
if ( ( annotationValues = method . getAnnotation ( JWebAssembly . REPLACE_ANNOTATION ) ) ! = null ) {
2020-01-11 20:31:05 +01:00
functions . needThisParameter ( name ) ; // register this class that process the annotation of this replacement function not a second time. iSKnown() returns true now.
2019-06-23 20:50:11 +02:00
String signatureName = ( String ) annotationValues . get ( " value " ) ;
name = new FunctionName ( signatureName ) ;
functions . addReplacement ( name , method ) ;
}
2019-02-24 20:02:36 +01:00
if ( ( annotationValues = method . getAnnotation ( JWebAssembly . IMPORT_ANNOTATION ) ) ! = null ) {
2018-12-13 22:32:51 +01:00
if ( ! method . isStatic ( ) ) {
throw new WasmException ( " Import method must be static: " + name . fullName , - 1 ) ;
}
2019-06-23 20:50:11 +02:00
functions . markAsImport ( name , annotationValues ) ;
2019-02-24 20:02:36 +01:00
return ;
}
if ( ( annotationValues = method . getAnnotation ( JWebAssembly . EXPORT_ANNOTATION ) ) ! = null ) {
if ( ! method . isStatic ( ) ) {
throw new WasmException ( " Export method must be static: " + name . fullName , - 1 ) ;
2018-12-13 20:05:13 +01:00
}
2022-04-03 13:58:43 +02:00
functions . markAsExport ( name , annotationValues ) ;
2019-02-24 20:02:36 +01:00
return ;
2018-06-01 19:44:40 +02:00
}
2018-08-06 12:23:42 +02:00
} catch ( Exception ioex ) {
2020-04-10 22:22:45 +02:00
throw WasmException . create ( ioex , sourceFile , className , methodName , - 1 ) ;
2018-06-01 19:44:40 +02:00
}
}
/ * *
* Write the content of a method .
*
2019-02-25 18:26:33 +01:00
* @param name
* the function name that should be written . This can be differ from the value in the MethodInfo
2018-06-01 19:44:40 +02:00
* @param method
* the method
* @throws WasmException
* if some Java code can ' t converted
2019-05-11 20:53:30 +02:00
* @throws IOException
* if any I / O error occur
2018-06-01 19:44:40 +02:00
* /
2019-04-14 14:29:09 +02:00
private void writeMethod ( FunctionName name , MethodInfo method ) throws WasmException , IOException {
2019-05-11 20:53:30 +02:00
WasmCodeBuilder codeBuilder = createInstructions ( method ) ;
if ( codeBuilder = = null ) {
return ;
}
writeExport ( name , method ) ;
2020-01-11 20:31:05 +01:00
writeMethodImpl ( name , codeBuilder ) ;
2019-05-11 20:53:30 +02:00
}
/ * *
* Create the instructions in a code builder
*
* @param method
* the method to parse
* @return the CodeBuilder or null if it is an import function
* @throws IOException
* if any I / O error occur
* /
@Nullable
private WasmCodeBuilder createInstructions ( MethodInfo method ) throws IOException {
2019-04-14 14:29:09 +02:00
Code code = null ;
2018-06-01 19:44:40 +02:00
try {
2019-08-08 17:47:57 +02:00
Map < String , Object > annotationValues ;
if ( ( annotationValues = method . getAnnotation ( JWebAssembly . IMPORT_ANNOTATION ) ) ! = null ) {
2020-04-26 00:08:50 +02:00
FunctionName name = new FunctionName ( method ) ;
functions . markAsImport ( name , annotationValues ) ;
// Scan also the types of used imports
for ( Iterator < AnyType > it = new ValueTypeParser ( name . signature , types ) ; it . hasNext ( ) ; ) {
it . next ( ) ;
}
2019-05-11 20:53:30 +02:00
return null ;
2018-11-16 17:46:10 +01:00
}
2019-04-14 14:29:09 +02:00
code = method . getCode ( ) ;
2019-05-11 20:53:30 +02:00
if ( method . getAnnotation ( JWebAssembly . TEXTCODE_ANNOTATION ) ! = null ) {
Map < String , Object > wat = method . getAnnotation ( JWebAssembly . TEXTCODE_ANNOTATION ) ;
2018-11-16 20:38:37 +01:00
String watCode = ( String ) wat . get ( " value " ) ;
2018-11-25 15:11:42 +01:00
String signature = ( String ) wat . get ( " signature " ) ;
2018-11-18 12:50:54 +01:00
if ( signature = = null ) {
signature = method . getType ( ) ;
}
2020-03-27 21:10:02 +01:00
watParser . parse ( watCode , method , null , code = = null ? - 1 : code . getFirstLineNr ( ) ) ;
2019-05-11 20:53:30 +02:00
return watParser ;
2022-02-20 18:29:13 +01:00
} else if ( code ! = null ) {
2019-09-23 20:33:21 +02:00
javaCodeBuilder . buildCode ( code , method ) ;
2019-05-11 20:53:30 +02:00
return javaCodeBuilder ;
2022-02-20 18:29:13 +01:00
} else if ( method . isAbstract ( ) ) { // abstract methods and interface methods does not have code
2020-03-18 21:37:56 +01:00
functions . markAsAbstract ( new FunctionName ( method ) ) ; // there is nothing to write for an abstract method
return null ;
2018-11-16 20:38:37 +01:00
} else {
2020-03-14 19:21:37 +01:00
FunctionName name = new FunctionName ( method ) ;
2020-03-14 23:02:58 +01:00
if ( " java/lang/Class.typeTableMemoryOffset()I " . equals ( name . signatureName ) ) {
2020-03-15 12:00:15 +01:00
strings . getStringConstantFunction ( ) ; // we will need also the string constant function for the Class Name, in the other case a program with only new Object().getClass().getName() will fail to compile
2020-03-14 19:21:37 +01:00
return types . getTypeTableMemoryOffsetFunctionName ( ) . getCodeBuilder ( watParser ) ;
}
2020-04-05 21:03:13 +02:00
if ( " de/inetsoftware/jwebassembly/module/StringManager.stringsMemoryOffset()I " . equals ( name . signatureName ) ) {
strings . getStringConstantFunction ( ) ;
return null ;
}
2021-11-02 19:14:25 +01:00
throw new WasmException ( " Abstract or native method can not be used: " + name . signatureName + " \ nIf you want to use classes with native code, you must use a library that implements these native methods, such as 'de.inetsoftware:jwebassembly-api:+'. " , - 1 ) ;
2018-11-16 20:38:37 +01:00
}
2018-06-01 19:44:40 +02:00
} catch ( Exception ioex ) {
2019-04-14 14:29:09 +02:00
int lineNumber = code = = null ? - 1 : code . getFirstLineNr ( ) ;
2020-04-10 22:22:45 +02:00
throw WasmException . create ( ioex , sourceFile , className , methodName , lineNumber ) ;
2018-06-01 19:44:40 +02:00
}
}
2019-05-11 20:53:30 +02:00
/ * *
* Write the method instruction to the Wasm writer .
*
* @param name
* the name of the function
* @param codeBuilder
* the code builder with instructions
* @throws WasmException
* if some Java code can ' t converted
* @throws IOException
* if an i / O error occur
* /
2020-01-11 20:31:05 +01:00
private void writeMethodImpl ( FunctionName name , WasmCodeBuilder codeBuilder ) throws WasmException , IOException {
2019-03-28 18:26:25 +01:00
writer . writeMethodStart ( name , sourceFile ) ;
2019-05-09 22:13:02 +02:00
functions . markAsWritten ( name ) ;
2020-03-20 20:35:13 +01:00
writeMethodSignature ( name , FunctionType . Code , codeBuilder ) ;
2019-01-20 10:41:33 +01:00
2019-02-12 21:18:42 +01:00
List < WasmInstruction > instructions = codeBuilder . getInstructions ( ) ;
2020-05-25 16:40:25 +02:00
optimizer . optimize ( instructions ) ;
2019-03-26 18:21:20 +01:00
2019-03-31 13:29:40 +02:00
int lastJavaSourceLine = - 1 ;
2019-02-12 21:18:42 +01:00
for ( WasmInstruction instruction : instructions ) {
2019-04-14 14:29:09 +02:00
try {
2019-04-20 21:41:46 +02:00
// add source-map information
int javaSourceLine = instruction . getLineNumber ( ) ;
if ( javaSourceLine > = 0 & & javaSourceLine ! = lastJavaSourceLine ) {
writer . markSourceLine ( javaSourceLine ) ;
lastJavaSourceLine = javaSourceLine ;
}
2019-04-14 14:29:09 +02:00
switch ( instruction . getType ( ) ) {
case Block :
switch ( ( ( WasmBlockInstruction ) instruction ) . getOperation ( ) ) {
case TRY :
case CATCH :
2020-01-05 21:54:17 +01:00
case THROW :
case RETHROW :
2020-01-12 12:42:31 +01:00
if ( writer . options . useEH ( ) ) {
writer . writeException ( ) ;
}
2019-04-14 14:29:09 +02:00
break ;
default :
}
break ;
default :
}
2019-04-20 21:41:46 +02:00
2019-04-14 14:29:09 +02:00
instruction . writeTo ( writer ) ;
} catch ( Throwable th ) {
throw WasmException . create ( th , instruction . getLineNumber ( ) ) ;
2019-03-26 18:21:20 +01:00
}
2019-01-20 10:41:33 +01:00
}
writer . writeMethodFinish ( ) ;
}
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 {
2022-04-03 13:58:43 +02:00
Map < String , Object > export = functions . getExportAnannotation ( name ) ;
2018-06-01 19:44:40 +02:00
if ( export ! = null ) {
String exportName = ( String ) export . get ( " name " ) ;
2022-04-10 11:35:20 +02:00
if ( exportName = = null | | exportName . isEmpty ( ) ) {
2018-06-01 19:44:40 +02:00
exportName = method . getName ( ) ; // TODO naming conversion rule if no name was set
}
2022-04-10 11:35:20 +02:00
JWebAssembly . LOGGER . fine ( " Export " + name . fullName + " as ' " + exportName + " ' " ) ;
if ( ! exportNames . add ( exportName ) ) {
throw new WasmException ( " Duplicate export name ' " + exportName + " ' for " + name . fullName + " . Rename the method or use the 'name' attribute of the @Export annotation to create a unique export name. " , - 1 ) ;
}
2018-06-01 19:44:40 +02:00
writer . writeExport ( name , exportName ) ;
}
}
/ * *
* Write the parameter and return signatures
*
2018-11-25 15:11:42 +01:00
* @param name
2018-11-18 12:50:54 +01:00
* the Java signature , typical method . getType ( ) ;
2020-03-20 20:35:13 +01:00
* @param funcType
* the type of function
2018-11-04 20:28:42 +01:00
* @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
* /
2020-03-20 20:35:13 +01:00
private void writeMethodSignature ( @Nonnull FunctionName name , @Nonnull FunctionType funcType , @Nullable WasmCodeBuilder codeBuilder ) throws IOException , WasmException {
writer . writeMethodParamStart ( name , funcType ) ;
2018-06-01 19:44:40 +02:00
int paramCount = 0 ;
2020-01-11 20:31:05 +01:00
if ( functions . needThisParameter ( name ) ) {
2019-04-24 21:41:33 +02:00
StructType instanceType = types . valueOf ( name . className ) ;
writer . writeMethodParam ( " param " , instanceType , " this " ) ;
2019-12-24 13:22:25 +01:00
paramCount + + ;
2018-12-13 22:32:51 +01:00
}
2019-08-14 20:07:39 +02:00
Iterator < AnyType > parser = name . getSignature ( types ) ;
2019-01-14 20:09:00 +01:00
AnyType type ;
2018-11-29 18:35:56 +01:00
for ( String kind : new String [ ] { " param " , " result " } ) {
2019-01-18 17:48:12 +01:00
while ( parser . hasNext ( ) & & ( type = parser . next ( ) ) ! = null ) {
2018-11-29 18:35:56 +01:00
String paramName = null ;
if ( kind = = " param " ) {
2019-04-14 14:29:09 +02:00
if ( codeBuilder ! = null ) {
paramName = codeBuilder . getLocalName ( paramCount ) ;
2018-11-29 18:35:56 +01:00
}
paramCount + + ;
}
if ( type ! = ValueType . empty ) {
writer . writeMethodParam ( kind , type , paramName ) ;
2018-10-13 18:10:05 +02:00
}
2018-06-01 19:44:40 +02:00
}
}
2018-11-04 20:28:42 +01:00
if ( codeBuilder ! = null ) {
2019-01-20 19:58:23 +01:00
List < AnyType > localTypes = codeBuilder . getLocalTypes ( paramCount ) ;
2018-11-04 20:28:42 +01:00
for ( int i = 0 ; i < localTypes . size ( ) ; i + + ) {
type = localTypes . get ( i ) ;
2019-04-14 14:29:09 +02:00
int idx = paramCount + i ;
String paramName = codeBuilder . getLocalName ( idx ) ;
2018-11-25 15:11:42 +01:00
writer . writeMethodParam ( " local " , type , paramName ) ;
2018-06-01 19:44:40 +02:00
}
}
2019-06-02 11:44:28 +02:00
writer . writeMethodParamFinish ( name ) ;
2018-06-01 19:44:40 +02:00
}
}