• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

i-net-software / JWebAssembly / 535

pending completion
535

push

travis-ci-com

Horcrux7
imports

5932 of 6826 relevant lines covered (86.9%)

0.87 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.9
/src/de/inetsoftware/jwebassembly/module/ModuleGenerator.java
1
/*
2
 * Copyright 2017 - 2022 Volker Berlin (i-net software)
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
package de.inetsoftware.jwebassembly.module;
17

18
import static de.inetsoftware.jwebassembly.module.WasmCodeBuilder.CLASS_INIT;
19

20
import java.io.BufferedInputStream;
21
import java.io.File;
22
import java.io.IOException;
23
import java.net.URL;
24
import java.net.URLClassLoader;
25
import java.nio.file.Files;
26
import java.nio.file.Path;
27
import java.util.HashSet;
28
import java.util.Iterator;
29
import java.util.List;
30
import java.util.Map;
31
import java.util.function.Consumer;
32
import java.util.function.Function;
33
import java.util.logging.Level;
34
import java.util.zip.ZipEntry;
35
import java.util.zip.ZipInputStream;
36

37
import javax.annotation.Nonnull;
38
import javax.annotation.Nullable;
39

40
import de.inetsoftware.classparser.ClassFile;
41
import de.inetsoftware.classparser.Code;
42
import de.inetsoftware.classparser.ConstantClass;
43
import de.inetsoftware.classparser.MethodInfo;
44
import de.inetsoftware.jwebassembly.JWebAssembly;
45
import de.inetsoftware.jwebassembly.WasmException;
46
import de.inetsoftware.jwebassembly.javascript.JavaScriptWriter;
47
import de.inetsoftware.jwebassembly.module.TypeManager.StructType;
48
import de.inetsoftware.jwebassembly.module.nativecode.ReplacementForEnums;
49
import de.inetsoftware.jwebassembly.wasm.AnyType;
50
import de.inetsoftware.jwebassembly.wasm.FunctionType;
51
import de.inetsoftware.jwebassembly.wasm.ValueType;
52
import de.inetsoftware.jwebassembly.wasm.ValueTypeParser;
53
import de.inetsoftware.jwebassembly.watparser.WatParser;
54

55
/**
56
 * Generate the WebAssembly output.
57
 * 
58
 * @author Volker Berlin
59
 */
60
public class ModuleGenerator {
61

62
    private final ModuleWriter              writer;
63

64
    private final JavaScriptWriter          javaScript;
65

66
    private final ClassFileLoader           classFileLoader;
67

68
    private final JavaMethodWasmCodeBuilder javaCodeBuilder;
69

70
    private final WatParser                 watParser;
71

72
    private String                          sourceFile;
73

74
    private String                          className;
75

76
    private String                          methodName;
77

78
    private final FunctionManager           functions;
79

80
    private final TypeManager               types;
81

82
    private final StringManager             strings;
83

84
    private final CodeOptimizer             optimizer;
85

86
    private final StaticCodeBuilder         staticCodeBuilder;
87

88
    private final HashSet<String>           exportNames = new HashSet<>();
1✔
89

90
    /**
91
     * Create a new generator.
92
     * 
93
     * @param writer
94
     *            the target writer
95
     * @param target
96
     *            the target for the module data
97
     * @param libraries
98
     *            libraries 
99
     */
100
    public ModuleGenerator( @Nonnull ModuleWriter writer, WasmTarget target, @Nonnull List<URL> libraries ) {
1✔
101
        this.watParser = new WatParser();
1✔
102
        this.javaCodeBuilder = new JavaMethodWasmCodeBuilder( watParser );
1✔
103
        this.writer = writer;
1✔
104
        this.javaScript = new JavaScriptWriter( target );
1✔
105
        this.classFileLoader = new ClassFileLoader( new URLClassLoader( libraries.toArray( new URL[libraries.size()] ) ) );
1✔
106
        WasmOptions options = writer.options;
1✔
107
        functions = options.functions;
1✔
108
        types = options.types;
1✔
109
        strings = options.strings;
1✔
110
        optimizer = options.optimizer;
1✔
111
        javaCodeBuilder.init( options, classFileLoader );
1✔
112
        ((WasmCodeBuilder)watParser).init( options, classFileLoader );
1✔
113
        types.init( classFileLoader );
1✔
114
        staticCodeBuilder = new StaticCodeBuilder( writer.options, classFileLoader, javaCodeBuilder );
1✔
115

116
        scanLibraries( libraries );
1✔
117

118
        //register some synthetic functions
119
        types.getTypeTableMemoryOffsetFunctionName();
1✔
120
    }
1✔
121

122
    /**
123
     * Scan the libraries for annotated methods
124
     * 
125
     * @param libraries
126
     *            libraries
127
     */
128
    private void scanLibraries( @Nonnull List<URL> libraries ) {
129
        // search for replacement methods in the libraries
130
        for( URL url : libraries ) {
1✔
131
            try {
132
                File file = new File(url.toURI());
1✔
133
                if( file.isDirectory() ) {
1✔
134
                    for( Iterator<Path> iterator = Files.walk( file.toPath() ).iterator(); iterator.hasNext(); ) {
1✔
135
                        Path path = iterator.next();
1✔
136
                        if( path.toString().endsWith( ".class" ) ) {
1✔
137
                            ClassFile classFile = new ClassFile( new BufferedInputStream( Files.newInputStream( path ) ) );
1✔
138
                            prepare( classFile );
1✔
139
                        }
140
                    }
1✔
141
                }
142
            } catch( Exception e ) {
×
143
                e.printStackTrace();
×
144
            }
1✔
145

146
            try (ZipInputStream input = new ZipInputStream( url.openStream() )) {
1✔
147
                do {
148
                    ZipEntry entry = input.getNextEntry();
1✔
149
                    if( entry == null ) {
1✔
150
                        break;
1✔
151
                    }
152
                    if( entry.getName().endsWith( ".class" ) ) {
1✔
153
                        try {
154
                            ClassFile classFile = new ClassFile( new BufferedInputStream( input ) {
1✔
155
                                @Override
156
                                public void close() {
157
                                } // does not close the zip stream
1✔
158
                            } );
159
                            prepare( classFile );
1✔
160
                        } catch( Throwable th ) {
×
161
                            JWebAssembly.LOGGER.log( Level.SEVERE, "Parsing error with " + entry.getName() + " in " + url, th );
×
162
                        }
1✔
163
                    }
164
                } while( true );
1✔
165
            } catch( IOException e ) {
×
166
                e.printStackTrace();
×
167
            }
1✔
168
        }
1✔
169
    }
1✔
170

171
    /**
172
     * Prepare the content of the class.
173
     * 
174
     * @param classFile
175
     *            the class file
176
     * @throws WasmException
177
     *             if some Java code can't converted
178
     * @throws IOException
179
     *             if any I/O error occur
180
     */
181
    public void prepare( @Nonnull ClassFile classFile ) throws IOException {
182
        classFileLoader.cache( classFile );
1✔
183
        // check if this class replace another class
184
        Map<String,Object> annotationValues;
185
        if( (annotationValues = classFile.getAnnotation( JWebAssembly.REPLACE_ANNOTATION )) != null ) {
1✔
186
            String signatureName = (String)annotationValues.get( "value" );
1✔
187
            if( signatureName != null ) {
1✔
188
                classFileLoader.replace( signatureName, classFile );
1✔
189
            }
190
        }
191

192
        // check if this class extends another class with partial code
193
        if( (annotationValues = classFile.getAnnotation( JWebAssembly.PARTIAL_ANNOTATION )) != null ) {
1✔
194
            String signatureName = (String)annotationValues.get( "value" );
1✔
195
            if( signatureName != null ) {
1✔
196
                classFileLoader.partial( signatureName, classFile );
1✔
197
            }
198
        }
199

200

201
        iterateMethods( classFile, m -> prepareMethod( m ) );
1✔
202
    }
1✔
203

204
    /**
205
     * Scan all needed methods/functions in a loop. If the scan find more needed content then the loop continue.
206
     * 
207
     * @throws IOException
208
     *             if any I/O error occur
209
     */
210
    private void scanFunctions() throws IOException {
211
        FunctionName next;
212
        NEXT:
213
        while( (next = functions.nextScannLater()) != null ) {
1✔
214
            className = next.className;
1✔
215
            methodName = next.methodName;
1✔
216
            JWebAssembly.LOGGER.fine( "scan " + next.signatureName );
1✔
217
            if( next instanceof SyntheticFunctionName ) {
1✔
218
                SyntheticFunctionName synth = (SyntheticFunctionName)next;
1✔
219
                if( synth.hasWasmCode() ) {
1✔
220
                    synth.getCodeBuilder( watParser );
1✔
221
                } else {
222
                    functions.markAsImport( synth, synth.getAnnotation() );
1✔
223
                }
224
                functions.markAsScanned( next );
1✔
225
                continue;
1✔
226
            }
227

228
            MethodInfo method = null;
1✔
229
            ClassFile classFile = classFileLoader.get( next.className );
1✔
230
            if( classFile != null ) {
1✔
231

232
                //temporary Hack because the generated Enum.valueOf(String) use Reflection which currently is not supported
233
                if( classFile.isEnum() && "valueOf".equals( next.methodName ) && next.signature.startsWith( "(Ljava/lang/String;)" ) ) {
1✔
234
                    String replaceForEnums = ReplacementForEnums.class.getName().replace( ".", "/" );
1✔
235
                    ClassFile file = classFileLoader.get( replaceForEnums );
1✔
236
                    classFileLoader.partial( next.className, file );
1✔
237
                    MethodInfo method2 = classFile.getMethod( "valueOf_", next.signature );
1✔
238
                    functions.addReplacement( next, method2 );
1✔
239
                }
240

241
                sourceFile = classFile.getSourceFile();
1✔
242
                className = classFile.getThisClass().getName();
1✔
243
                method = classFile.getMethod( next.methodName, next.signature );
1✔
244
            }
245
            if( method == null ) {
1✔
246
                method = functions.replace( next, null );
1✔
247
            }
248
            if( method != null ) {
1✔
249
                createInstructions( functions.replace( next, method ) );
1✔
250
                functions.markAsScanned( next );
1✔
251
                if( functions.needThisParameter( next ) ) {
1✔
252
                    types.valueOf( next.className ); // for the case that the type unknown yet
1✔
253
                }
254
                continue;
255
            }
256

257
            if( classFile == null ) {
1✔
258
                // all extends from object, also arrays
259
                classFile = classFileLoader.get( "java/lang/Object" );
1✔
260
            }
261

262
            // search if there is a super class with the same signature
263
            ClassFile superClassFile = classFile;
1✔
264
            while( superClassFile != null ) {
1✔
265
                method = superClassFile.getMethod( next.methodName, next.signature );
1✔
266
                if( method != null ) {
1✔
267
                    FunctionName name = new FunctionName( method );
1✔
268
                    functions.markAsNeeded( name, !method.isStatic() );
1✔
269
                    functions.setAlias( next, name );
1✔
270
                    continue NEXT; // we have found a super method
1✔
271
                }
272
                ConstantClass superClass = superClassFile.getSuperClass();
1✔
273
                superClassFile = superClass == null ? null : classFileLoader.get( superClass.getName() );
1✔
274
            }
1✔
275

276
            // search if there is a default implementation in an interface
277
            superClassFile = classFile;
1✔
278
            while( superClassFile != null ) {
1✔
279
                if( scanFunctionInterfaces( superClassFile, next ) ) {
1✔
280
                    continue NEXT; // we have found a super method
1✔
281
                }
282
                ConstantClass superClass = superClassFile.getSuperClass();
1✔
283
                superClassFile = superClass == null ? null : classFileLoader.get( superClass.getName() );
1✔
284
            }
1✔
285

286
            throw new WasmException( "Missing function: " + next.signatureName, -1 );
×
287
        }
288
    }
1✔
289

290
    /**
291
     * Search if there is a default implementation in an interface for the given method.
292
     * 
293
     * @param classFile
294
     *            the class to scan
295
     * @param next
296
     *            the method to scan
297
     * @return true, if method was found
298
     * @throws IOException
299
     *             if any I/O error occur
300
     */
301
    private boolean scanFunctionInterfaces( ClassFile classFile, FunctionName next ) throws IOException {
302
        // first scan direct interfaces
303
        for( ConstantClass iface : classFile.getInterfaces() ) {
1✔
304
            ClassFile iClassFile = classFileLoader.get( iface.getName() );
1✔
305
            MethodInfo method = iClassFile.getMethod( next.methodName, next.signature );
1✔
306
            if( method != null ) {
1✔
307
                FunctionName name = new FunctionName( method );
1✔
308
                functions.markAsNeeded( name, !method.isStatic() );
1✔
309
                functions.setAlias( next, name );
1✔
310
                return true; // we have found a super method
1✔
311
            }
312
        }
313
        // then possible super interfaces
314
        for( ConstantClass iface : classFile.getInterfaces() ) {
1✔
315
            ClassFile iClassFile = classFileLoader.get( iface.getName() );
×
316
            if( scanFunctionInterfaces( iClassFile, next ) ) {
×
317
                return true; // we have found a super method
×
318
            }
319
        }
320
        return false;
1✔
321
    }
322

323
    /**
324
     * Finish the prepare after all classes/methods are prepare. This must be call before we can start with write the
325
     * first method.
326
     * @throws IOException
327
     *             if any I/O error occur
328
     */
329
    public void prepareFinish() throws IOException {
330
        int functCount;
331
        do {
332
            scanFunctions();
1✔
333
            functCount = functions.getNeededCount();    // scan the functions can find new needed types or only new needed fields in the known types
1✔
334
            scanForClinit();
1✔
335
            types.scanTypeHierarchy();                  // scan the type hierarchy can find new functions
1✔
336
        } while( functCount < functions.getNeededCount() );
1✔
337

338
        // write only the needed imports to the output
339
        for( Iterator<FunctionName> iterator = functions.getNeededImports(); iterator.hasNext(); ) {
1✔
340
            FunctionName name = iterator.next();
1✔
341

342
            functions.markAsWritten( name );
1✔
343
            Function<String, Object> importAnannotation = functions.getImportAnannotation( name );
1✔
344
            String importModule = (String)importAnannotation.apply( "module" );
1✔
345
            if( importModule == null || importModule.isEmpty() ) {
1✔
346
                // use className if module is not set 
347
                importModule = name.className.substring( name.className.lastIndexOf( '/' ) + 1 );
1✔
348
            }
349
            String importName = (String)importAnannotation.apply( "name" );
1✔
350
            if( importName == null || importName.isEmpty() ) {
1✔
351
                // use method name as function if not set
352
                importName = name.methodName;
1✔
353
            }
354
            writer.prepareImport( name, importModule, importName );
1✔
355
            writeMethodSignature( name, FunctionType.Import, null );
1✔
356
            javaScript.addImport( importModule, importName, importAnannotation );
1✔
357
        }
1✔
358

359
        // add a start method for the static class constructors
360
        prepareStartFunction();
1✔
361

362
        // init/write the function types
363
        for( Iterator<FunctionName> iterator = functions.getWriteLater(); iterator.hasNext(); ) {
1✔
364
            FunctionName name = iterator.next();
1✔
365
            writeMethodSignature( name, FunctionType.Code, null );
1✔
366
        }
1✔
367

368
        // register types of abstract and interface methods
369
        for( Iterator<FunctionName> iterator = functions.getAbstractedFunctions(); iterator.hasNext(); ) {
1✔
370
            FunctionName name = iterator.next();
1✔
371
            writeMethodSignature( name, FunctionType.Abstract, null );
1✔
372
        }
1✔
373

374
        // scan again if there are new types or new needed fields
375
        types.scanTypeHierarchy();
1✔
376

377
        JWebAssembly.LOGGER.fine( "scan finish" );
1✔
378
        writer.prepareFinish();
1✔
379
        types.prepareFinish( writer );
1✔
380
        functions.prepareFinish();
1✔
381
        strings.prepareFinish( writer );
1✔
382
    }
1✔
383

384
    /**
385
     * Scan for needed static constructors. The static code of all classes that used in any form must be executed.
386
     * 
387
     * @throws IOException
388
     *             if any I/O error occur
389
     */
390
    private void scanForClinit() throws IOException {
391
        JWebAssembly.LOGGER.fine( "scan for needed <clinit>" );
1✔
392
        for( Iterator<String> iterator = functions.getUsedClasses(); iterator.hasNext(); ) {
1✔
393
            String className = iterator.next();
1✔
394
            ClassFile classFile = classFileLoader.get( className );
1✔
395
            if( classFile != null ) {
1✔
396
                MethodInfo method = classFile.getMethod( CLASS_INIT, "()V" );
1✔
397
                if( method != null ) {
1✔
398
                    functions.markAsNeeded( new FunctionName( method ), false );
1✔
399
                }
400
            }
401
        }
1✔
402
    }
1✔
403

404
    /**
405
     * Add a start method for the static class constructors
406
     * 
407
     * @throws IOException
408
     *             if any I/O error occur
409
     */
410
    private void prepareStartFunction() throws IOException {
411
        // add the start function/section only if there are static code
412
        Iterator<FunctionName> writeLaterClinit = functions.getWriteLaterClinit();
1✔
413
        if( writeLaterClinit.hasNext() ) {
1✔
414
            FunctionName start = staticCodeBuilder.createStartFunction( writeLaterClinit );
1✔
415
            functions.markAsNeeded( start, false );
1✔
416
            writeMethodSignature( start, FunctionType.Start, null );
1✔
417
        }
418
    }
1✔
419

420
    /**
421
     * Finish the code generation.
422
     * 
423
     * @throws IOException
424
     *             if any I/O error occur
425
     */
426
    public void finish() throws IOException {
427
        for( Iterator<FunctionName> it = functions.getWriteLater(); it.hasNext(); ) {
1✔
428
            FunctionName next = it.next();
1✔
429
            sourceFile = null; // clear previous value for the case an IO exception occur
1✔
430
            className = next.className;
1✔
431
            methodName = next.methodName;
1✔
432
            if( next instanceof SyntheticFunctionName ) {
1✔
433
                writeMethodImpl( next, ((SyntheticFunctionName)next).getCodeBuilder( watParser ) );
1✔
434
            } else {
435
                ClassFile classFile = classFileLoader.get( next.className );
1✔
436
                if( classFile == null ) {
1✔
437
                    throw new WasmException( "Missing function: " + next.signatureName, -1 );
×
438
                } else {
439
                    sourceFile = classFile.getSourceFile();
1✔
440
                    className = classFile.getThisClass().getName();
1✔
441
                    MethodInfo method = classFile.getMethod( next.methodName, next.signature );
1✔
442
                    if( method != null ) {
1✔
443
                        try {
444
                            Map<String, Object> wat = method.getAnnotation( JWebAssembly.TEXTCODE_ANNOTATION  );
1✔
445
                            if( wat != null ) {
1✔
446
                                String signature = (String)wat.get( "signature" );
1✔
447
                                if( signature == null ) {
1✔
448
                                    signature = method.getType();
1✔
449
                                }
450
                                next = new FunctionName( method, signature );
1✔
451
                            } else {
1✔
452
                                method = functions.replace( next, method );
1✔
453
                            }
454
                            if( functions.needToWrite( next ) ) {
1✔
455
                                writeMethod( next, method );
1✔
456
                            }
457
                        } catch (Throwable ex){
×
458
                            throw WasmException.create( ex, sourceFile, className, methodName, -1 );
×
459
                        }
1✔
460
                    } else {
461
                        if( functions.needToWrite( next ) ) {
×
462
                            throw new WasmException( "Missing function: " + next.signatureName, -1 );
×
463
                        }
464
                    }
465
                }
466
            }
467
        }
1✔
468
        javaScript.finish();
1✔
469
    }
1✔
470

471
    /**
472
     * Iterate over all methods of the classFile and run the handler.
473
     * 
474
     * @param classFile
475
     *            the classFile
476
     * @param handler
477
     *            the handler
478
     * @throws WasmException
479
     *             if some Java code can't converted
480
     */
481
    private void iterateMethods( ClassFile classFile, Consumer<MethodInfo> handler ) throws WasmException {
482
        sourceFile = null; // clear previous value for the case an IO exception occur
1✔
483
        className = null;
1✔
484
        try {
485
            sourceFile = classFile.getSourceFile();
1✔
486
            className = classFile.getThisClass().getName();
1✔
487
            MethodInfo[] methods = classFile.getMethods();
1✔
488
            for( MethodInfo method : methods ) {
1✔
489
                handler.accept( method );
1✔
490
            }
491
        } catch( Throwable ioex ) {
1✔
492
            throw WasmException.create( ioex, sourceFile, className, methodName, -1 );
1✔
493
        }
1✔
494
    }
1✔
495

496
    /**
497
     * Prepare the method.
498
     * 
499
     * @param method
500
     *            the method
501
     * @throws WasmException
502
     *             if some Java code can't converted
503
     */
504
    private void prepareMethod( MethodInfo method ) throws WasmException {
505
        try {
506
            FunctionName name = new FunctionName( method );
1✔
507
            methodName = name.methodName;
1✔
508
            if( functions.isKnown( name ) ) {
1✔
509
                return;
×
510
            }
511
            Map<String,Object> annotationValues;
512
            if( (annotationValues = method.getAnnotation( JWebAssembly.REPLACE_ANNOTATION )) != null ) {
1✔
513
                functions.needThisParameter( name); // register this class that process the annotation of this replacement function not a second time. iSKnown() returns true now.
1✔
514
                String signatureName = (String)annotationValues.get( "value" );
1✔
515
                name = new FunctionName( signatureName );
1✔
516
                functions.addReplacement( name, method );
1✔
517
            }
518
            if( (annotationValues = method.getAnnotation( JWebAssembly.IMPORT_ANNOTATION )) != null ) {
1✔
519
                if( !method.isStatic() ) {
1✔
520
                    throw new WasmException( "Import method must be static: " + name.fullName, -1 );
1✔
521
                }
522
                functions.markAsImport( name, annotationValues );
1✔
523
                return;
1✔
524
            }
525
            if( (annotationValues = method.getAnnotation( JWebAssembly.EXPORT_ANNOTATION )) != null ) {
1✔
526
                if( !method.isStatic() ) {
1✔
527
                    throw new WasmException( "Export method must be static: " + name.fullName, -1 );
1✔
528
                }
529
                functions.markAsExport( name, annotationValues );
1✔
530
                return;
1✔
531
            }
532
        } catch( Throwable ioex ) {
1✔
533
            throw WasmException.create( ioex, sourceFile, className, methodName, -1 );
1✔
534
        }
1✔
535
    }
1✔
536

537
    /**
538
     * Write the content of a method.
539
     * 
540
     * @param name
541
     *            the function name that should be written. This can be differ from the value in the MethodInfo
542
     * @param method
543
     *            the method
544
     * @throws WasmException
545
     *             if some Java code can't converted
546
     * @throws IOException
547
     *             if any I/O error occur
548
     */
549
    private void writeMethod( FunctionName name, MethodInfo method ) throws WasmException, IOException {
550
        WasmCodeBuilder codeBuilder = createInstructions( method );
1✔
551
        if( codeBuilder == null ) {
1✔
552
            return;
×
553
        }
554
        writeExport( name, method );
1✔
555
        writeMethodImpl( name, codeBuilder );
1✔
556
    }
1✔
557

558
    /**
559
     * Create the instructions in a code builder
560
     * 
561
     * @param method
562
     *            the method to parse
563
     * @return the CodeBuilder or null if it is an import function
564
     * @throws IOException
565
     *             if any I/O error occur
566
     */
567
    @Nullable
568
    private WasmCodeBuilder createInstructions( MethodInfo method ) throws IOException {
569
        Code code = null;
1✔
570
        try {
571
            Map<String,Object> annotationValues;
572
            if( (annotationValues = method.getAnnotation( JWebAssembly.IMPORT_ANNOTATION )) != null ) {
1✔
573
                FunctionName name = new FunctionName( method );
1✔
574
                functions.markAsImport( name, annotationValues );
1✔
575

576
                // Scan also the types of used imports
577
                for( Iterator<AnyType> it = new ValueTypeParser( name.signature, types ); it.hasNext(); ) {
1✔
578
                    it.next();
1✔
579
                }
580
                return null;
1✔
581
            }
582
            code = method.getCode();
1✔
583
            if( method.getAnnotation( JWebAssembly.TEXTCODE_ANNOTATION ) != null ) {
1✔
584
                Map<String, Object> wat = method.getAnnotation( JWebAssembly.TEXTCODE_ANNOTATION );
1✔
585
                String watCode = (String)wat.get( "value" );
1✔
586
                String signature = (String)wat.get( "signature" );
1✔
587
                if( signature == null ) {
1✔
588
                    signature = method.getType();
1✔
589
                }
590
                watParser.parse( watCode, method, null, code == null ? -1 : code.getFirstLineNr() );
1✔
591
                return watParser;
1✔
592
            } else if( code != null ) {
1✔
593
                javaCodeBuilder.buildCode( code, method );
1✔
594
                return javaCodeBuilder;
1✔
595
            } else if( method.isAbstract() ) { // abstract methods and interface methods does not have code
1✔
596
                functions.markAsAbstract( new FunctionName( method ) ); // there is nothing to write for an abstract method
1✔
597
                return null;
1✔
598
            } else {
599
                FunctionName name = new FunctionName( method );
1✔
600

601
                if( writer.options.ignoreNative() ) {
1✔
602
                    JWebAssembly.LOGGER.severe( "Native method will throw an exception at runtime: " + name.signatureName );
×
603
                    WatCodeSyntheticFunctionName nativeStub = new WatCodeSyntheticFunctionName( name.className, name.methodName, name.signature, "unreachable", (AnyType[])null );
×
604
                    functions.markAsNeededAndReplaceIfExists( nativeStub );
×
605
                    return null;
×
606
                }
607

608
                throw new WasmException( "Native methods cannot be compiled to WebAssembly: " + 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:+' or implements this native method self.", -1 );
1✔
609
            }
610
        } catch( Throwable ioex ) {
1✔
611
            int lineNumber = code == null ? -1 : code.getFirstLineNr();
1✔
612
            throw WasmException.create( ioex, sourceFile, className, methodName, lineNumber );
1✔
613
        }
614
    }
615

616
    /**
617
     * Write the method instruction to the Wasm writer.
618
     * 
619
     * @param name
620
     *            the name of the function
621
     * @param codeBuilder
622
     *            the code builder with instructions
623
     * @throws WasmException
624
     *             if some Java code can't converted
625
     * @throws IOException
626
     *             if an i/O error occur
627
     */
628
    private void writeMethodImpl( FunctionName name, WasmCodeBuilder codeBuilder ) throws WasmException, IOException {
629
        writer.writeMethodStart( name, sourceFile );
1✔
630
        functions.markAsWritten( name );
1✔
631
        writeMethodSignature( name, FunctionType.Code, codeBuilder );
1✔
632

633
        List<WasmInstruction> instructions = codeBuilder.getInstructions();
1✔
634
        optimizer.optimize( instructions );
1✔
635

636
        int lastJavaSourceLine = -1;
1✔
637
        for( WasmInstruction instruction : instructions ) {
1✔
638
            try {
639
                // add source-map information
640
                int javaSourceLine = instruction.getLineNumber();
1✔
641
                if( javaSourceLine >= 0 && javaSourceLine != lastJavaSourceLine ) {
1✔
642
                    writer.markSourceLine( javaSourceLine );
1✔
643
                    lastJavaSourceLine = javaSourceLine;
1✔
644
                }
645

646
                switch( instruction.getType() ) {
1✔
647
                    case Block:
648
                        switch( ((WasmBlockInstruction)instruction).getOperation() ) {
1✔
649
                            case TRY:
650
                            case CATCH:
651
                            case THROW:
652
                            case RETHROW:
653
                                if( writer.options.useEH() ) {
1✔
654
                                    writer.writeException();
1✔
655
                                }
656
                                break;
657
                            default:
658
                        }
659
                        break;
1✔
660
                    default:
661
                }
662

663
                instruction.writeTo( writer );
1✔
664
            } catch( Throwable th ) {
×
665
                throw WasmException.create( th, instruction.getLineNumber() );
×
666
            }
1✔
667
        }
1✔
668
        writer.writeMethodFinish();
1✔
669
    }
1✔
670

671
    /**
672
     * Look for a Export annotation and if there write an export directive.
673
     * 
674
     * @param name
675
     *            the function name
676
     * @param method
677
     *            the method
678
     * 
679
     * @throws IOException
680
     *             if any IOException occur
681
     */
682
    private void writeExport( FunctionName name, MethodInfo method ) throws IOException {
683
        Map<String,Object> export = functions.getExportAnannotation( name );
1✔
684
        if( export != null ) {
1✔
685
            String exportName = (String)export.get( "name" );
1✔
686
            if( exportName == null || exportName.isEmpty() ) {
1✔
687
                exportName = method.getName();  // TODO naming conversion rule if no name was set
1✔
688
            }
689
            JWebAssembly.LOGGER.fine( "Export " + name.fullName + " as '" + exportName + "'" );
1✔
690
            if( !exportNames.add( exportName ) ) {
1✔
691
                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 );
×
692
            }
693
            writer.writeExport( name, exportName );
1✔
694
        }
695
    }
1✔
696

697
    /**
698
     * Write the parameter and return signatures
699
     * 
700
     * @param name
701
     *            the Java signature, typical method.getType();
702
     * @param funcType
703
     *            the type of function
704
     * @param codeBuilder
705
     *            the calculated variables 
706
     * @throws IOException
707
     *             if any I/O error occur
708
     * @throws WasmException
709
     *             if some Java code can't converted
710
     */
711
    private void writeMethodSignature( @Nonnull FunctionName name, @Nonnull FunctionType funcType, @Nullable WasmCodeBuilder codeBuilder ) throws IOException, WasmException {
712
        writer.writeMethodParamStart( name, funcType );
1✔
713
        int paramCount = 0;
1✔
714
        if( functions.needThisParameter( name ) ) {
1✔
715
            StructType instanceType = types.valueOf( name.className );
1✔
716
            writer.writeMethodParam( "param", instanceType, "this" );
1✔
717
            paramCount++;
1✔
718
        }
719
        Iterator<AnyType> parser = name.getSignature( types );
1✔
720
        AnyType type;
721
        for( String kind : new String[] {"param","result"}) {
1✔
722
            while( parser.hasNext() && (type = parser.next()) != null ) {
1✔
723
                String paramName = null;
1✔
724
                if( kind == "param" ) {
1✔
725
                    if( codeBuilder != null ) {
1✔
726
                        paramName = codeBuilder.getLocalName( paramCount );
1✔
727
                    }
728
                    paramCount++;
1✔
729
                }
730
                if( type != ValueType.empty ) {
1✔
731
                    writer.writeMethodParam( kind, type, paramName );
1✔
732
                }
733
            }
1✔
734
        }
735
        if( codeBuilder != null ) {
1✔
736
            List<AnyType> localTypes = codeBuilder.getLocalTypes( paramCount );
1✔
737
            for( int i = 0; i < localTypes.size(); i++ ) {
1✔
738
                type = localTypes.get( i );
1✔
739
                int idx = paramCount + i;
1✔
740
                String paramName = codeBuilder.getLocalName( idx );
1✔
741
                writer.writeMethodParam( "local", type, paramName );
1✔
742
            }
743
        }
744
        writer.writeMethodParamFinish( name );
1✔
745
    }
1✔
746

747
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc