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

i-net-software / JWebAssembly / 522

pending completion
522

push

travis-ci-com

Horcrux7
Github action status badge

5876 of 6747 relevant lines covered (87.09%)

0.87 hits per line

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

90.83
/src/de/inetsoftware/jwebassembly/module/FunctionManager.java
1
/*
2
 * Copyright 2018 - 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.util.Collections;
21
import java.util.Iterator;
22
import java.util.LinkedHashMap;
23
import java.util.LinkedHashSet;
24
import java.util.Map;
25
import java.util.Map.Entry;
26
import java.util.Set;
27
import java.util.function.Function;
28
import java.util.function.Predicate;
29

30
import javax.annotation.Nonnull;
31
import javax.annotation.Nullable;
32

33
import de.inetsoftware.classparser.MethodInfo;
34
import de.inetsoftware.jwebassembly.JWebAssembly;
35
import de.inetsoftware.jwebassembly.WasmException;
36

37
/**
38
 * Manage the required function/methods
39
 * 
40
 * @author Volker Berlin
41
 */
42
class FunctionManager {
1✔
43

44
    private final Map<FunctionName, FunctionState> states      = new LinkedHashMap<>();
1✔
45

46
    private final Set<String>                      usedClasses = new LinkedHashSet<>();
1✔
47

48
    private int                                    neededCount;
49

50
    private boolean                                isFinish;
51

52
    /**
53
     * Finish the prepare. Now no new function should be added.
54
     */
55
    void prepareFinish() {
56
        isFinish = true;
1✔
57
    }
1✔
58

59
    /**
60
     * Get an existing state or create one.
61
     * 
62
     * @param name
63
     *            the FunctionName
64
     * @return the state
65
     */
66
    @Nonnull
67
    private FunctionState getOrCreate( @Nonnull FunctionName name ) {
68
        FunctionState state = states.get( name );
1✔
69
        if( state == null ) {
1✔
70
            states.put( name, state = new FunctionState() );
1✔
71
        }
72
        return state;
1✔
73
    }
74

75
    /**
76
     * Get the count of needed functions
77
     * 
78
     * @return the count
79
     */
80
    int getNeededCount() {
81
        return neededCount;
1✔
82
    }
83

84
    /**
85
     * Check if this function is already known/registered.
86
     * 
87
     * @param name
88
     *            the function name
89
     * @return true, if known
90
     */
91
    boolean isKnown( @Nonnull FunctionName name ) {
92
        return states.get( name ) != null;
1✔
93
    }
94

95
    /**
96
     * Mark a class as used. This means the static initializer must be used.
97
     * 
98
     * @param className
99
     *            the name of the class like "java/lang/Object"
100
     */
101
    void markClassAsUsed( String className ) {
102
        if( usedClasses.add( className ) ) {
1✔
103
            JWebAssembly.LOGGER.fine( "\t\tused: " + className );
1✔
104
        }
105
    }
1✔
106

107
    /**
108
     * Mark the a function as a import function. Only if the function is also needed then it will imported from
109
     * compiler.
110
     * 
111
     * @param name
112
     *            the function name
113
     * @param importAnannotation
114
     *            the annotation of the import
115
     */
116
    void markAsImport( @Nonnull FunctionName name, @Nonnull Map<String, Object> importAnannotation ) {
117
        markAsImport( name, ( key ) -> importAnannotation.get( key ) );
1✔
118
    }
1✔
119

120
    /**
121
     * Mark the a function as a import function. Only if the function is also needed then it will imported from
122
     * compiler.
123
     * 
124
     * @param name
125
     *            the function name
126
     * @param importAnannotation
127
     *            the annotation of the import
128
     */
129
    void markAsImport( @Nonnull FunctionName name, Function<String, Object> importAnannotation ) {
130
        getOrCreate( name ).importAnannotation = importAnannotation;
1✔
131
    }
1✔
132

133
    /**
134
     * Mark the function as export function and as needed.
135
     * 
136
     * @param name
137
     *            the function name
138
     * @param exportAnannotation
139
     *            the annotation of the export
140
     */
141
    void markAsExport( @Nonnull FunctionName name, @Nonnull Map<String, Object> exportAnannotation ) {
142
        markAsNeeded( name, false );
1✔
143
        getOrCreate( name ).exportAnannotation = exportAnannotation;
1✔
144
    }
1✔
145

146
    /**
147
     * Same like markAsNeeded but it will replace the function name if already registered.
148
     * 
149
     * @param name
150
     *            the function name
151
     */
152
    void markAsNeededAndReplaceIfExists( @Nonnull SyntheticFunctionName name ) {
153
        FunctionState state = states.get( name );
1✔
154
        if( state != null ) {
1✔
155
            states.remove( name );
×
156
            states.put( name, state );
×
157
        }
158
        markAsNeeded( name, !name.istStatic() );
1✔
159
    }
1✔
160

161
    /**
162
     * Mark a function as used/called and return the real name if there is an alias.
163
     * 
164
     * @param name
165
     *            the function name
166
     * @param needThisParameter
167
     *            if this function need additional to the parameter of the signature an extra "this" parameter
168
     * @return the real function name
169
     */
170
    FunctionName markAsNeeded( @Nonnull FunctionName name, boolean needThisParameter ) {
171
        FunctionState state = getOrCreate( name );
1✔
172
        if( state.state == State.None ) {
1✔
173
            switch( name.className ) {
1✔
174
                case UnsafeManager.UNSAFE_8:
175
                case UnsafeManager.UNSAFE_11:
176
                case UnsafeManager.VARHANDLE:
177
                    // Unsafe method call will be replaces by the UnsafeManager
178
                    return name;
1✔
179
            }
180
            if( isFinish ) {
1✔
181
                throw new WasmException( "Prepare was already finish: " + name.signatureName, -1 );
×
182
            }
183
            neededCount++;
1✔
184
            state.state = State.Needed;
1✔
185
            state.needThisParameter = needThisParameter;
1✔
186
            JWebAssembly.LOGGER.fine( "\t\tcall: " + name.signatureName );
1✔
187
            usedClasses.add( name.className );
1✔
188

189
            if( state.importAnannotation != null ) {
1✔
190
                // register possible callbacks of imports as needed methods
191
                Object callbacks = state.importAnannotation.apply( "callbacks" );
1✔
192
                if( callbacks != null ) {
1✔
193
                    for( Object callback : (Object[])callbacks ) {
×
194
                        name = new FunctionName( (String)callback );
×
195
                        markAsExport( name, Collections.emptyMap() );
×
196
                    }
197
                }
198

199
            }
200
        }
201
        return state.alias == null ? name : state.alias;
1✔
202
    }
203

204
    /**
205
     * If the function manager is finish
206
     * @return the finish flag
207
     */
208
    boolean isFinish() {
209
        return isFinish;
1✔
210
    }
211

212
    /**
213
     * Mark the a function as scanned in the prepare phase. This should only occur with needed functions.
214
     * 
215
     * @param name
216
     *            the function name
217
     */
218
    void markAsScanned( @Nonnull FunctionName name ) {
219
        FunctionState state = getOrCreate( name );
1✔
220
        switch( state.state ) {
1✔
221
            case None:
222
            case Needed:
223
                state.state = State.Scanned;
1✔
224
                break;
225
        }
226
    }
1✔
227

228
    /**
229
     * Mark the a function as written to the wasm file.
230
     * 
231
     * @param name
232
     *            the function name
233
     */
234
    void markAsWritten( @Nonnull FunctionName name ) {
235
        getOrCreate( name ).state = State.Written;
1✔
236
    }
1✔
237

238
    /**
239
     * Mark the a function as abstract or interface. This function can be called but will not be write to the wasm file.
240
     * 
241
     * @param name
242
     *            the function name
243
     */
244
    void markAsAbstract( @Nonnull FunctionName name ) {
245
        getOrCreate( name ).state = State.Abstract;
1✔
246
    }
1✔
247

248
    /**
249
     * Get all FunctionNames that need imported
250
     * 
251
     * @return an iterator
252
     */
253
    Iterator<FunctionName> getNeededImports() {
254
        return states.entrySet().stream().filter( entry -> {
1✔
255
            FunctionState state = entry.getValue();
1✔
256
            switch( state.state ) {
1✔
257
                case Needed:
258
                case Scanned:
259
                    return state.importAnannotation != null;
1✔
260
                default:
261
            }
262
            return false;
1✔
263
        } ).map( entry -> entry.getKey() ).iterator();
1✔
264
    }
265

266
    /**
267
     * Get the annotation of an import function
268
     * 
269
     * @param name
270
     *            the function name
271
     * @return the annotation or null
272
     */
273
    Function<String, Object> getImportAnannotation( FunctionName name ) {
274
        return getOrCreate( name ).importAnannotation;
1✔
275
    }
276

277
    /**
278
     * Get the annotation of an export function
279
     * 
280
     * @param name
281
     *            the function name
282
     * @return the annotation or null
283
     */
284
    Map<String, Object> getExportAnannotation( FunctionName name ) {
285
        return getOrCreate( name ).exportAnannotation;
1✔
286
    }
287

288
    /**
289
     * Get the first FunctionName that is required but was not scanned.
290
     * 
291
     * @return the FunctionName or null
292
     */
293
    @Nullable
294
    FunctionName nextScannLater() {
295
        for( Entry<FunctionName, FunctionState> entry : states.entrySet() ) {
1✔
296
            if( entry.getValue().state == State.Needed ) {
1✔
297
                return entry.getKey();
1✔
298
            }
299
        }
1✔
300
        return null;
1✔
301
    }
302

303
    /**
304
     * Get all used classes.
305
     * 
306
     * @return an iterator
307
     */
308
    @Nonnull
309
    Iterator<String> getUsedClasses() {
310
        return usedClasses.iterator();
1✔
311
    }
312

313
    /**
314
     * Get all static constructor FunctionName of used classes.
315
     * 
316
     * @return an iterator
317
     */
318
    @Nonnull
319
    Iterator<FunctionName> getWriteLaterClinit() {
320
        return iterator( entry -> entry.getKey().methodName.equals( CLASS_INIT ) && entry.getValue().state != State.None );
1✔
321
    }
322

323
    /**
324
     * Get all FunctionName that is required but was not written.
325
     * 
326
     * @return an iterator
327
     */
328
    @Nonnull
329
    Iterator<FunctionName> getWriteLater() {
330
        return iterator( entry -> {
1✔
331
            switch( entry.getValue().state ) {
1✔
332
                case Needed:
333
                case Scanned:
334
                    return true;
1✔
335
                default:
336
                    return false;
1✔
337
            }
338
        } );
339
    }
340

341
    /**
342
     * Get all FunctionNames that are abstract and used.
343
     * 
344
     * @return an iterator
345
     */
346
    Iterator<FunctionName> getAbstractedFunctions() {
347
        return iterator( entry -> {
1✔
348
            switch( entry.getValue().state ) {
1✔
349
                case Abstract:
350
                    return true;
1✔
351
                default:
352
                    return false;
1✔
353
            }
354
        } );
355
    }
356

357
    /**
358
     * get a iterator for function names
359
     * 
360
     * @param filter
361
     *            the filter
362
     * @return the iterator
363
     */
364
    @Nonnull
365
    private Iterator<FunctionName> iterator( Predicate<Entry<FunctionName, FunctionState>> filter ) {
366
        return states.entrySet().stream().filter( filter ).map( entry -> entry.getKey() ).iterator();
1✔
367
    }
368

369
    /**
370
     * if the given function is required but was not scanned.
371
     * 
372
     * @param name
373
     *            the function name
374
     * @return true, if the function on the to do list
375
     */
376
    boolean needToScan( @Nonnull FunctionName name ) {
377
        switch( getOrCreate( name ).state ) {
×
378
            case Needed:
379
                return true;
×
380
            default:
381
                return false;
×
382
        }
383
    }
384

385
    /**
386
     * if the given function is required but was not written.
387
     * 
388
     * @param name
389
     *            the function name
390
     * @return true, if the function on the to do list
391
     */
392
    boolean needToWrite( @Nonnull FunctionName name ) {
393
        switch( getOrCreate( name ).state ) {
1✔
394
            case Needed:
395
            case Scanned:
396
                return true;
1✔
397
            default:
398
                return false;
×
399
        }
400
    }
401

402
    /**
403
     * Test if the function is called anywhere.
404
     * 
405
     * @param name
406
     *            the function name
407
     * @return true, if used
408
     */
409
    boolean isUsed( @Nonnull FunctionName name ) {
410
        FunctionState state = states.get( name );
1✔
411
        return state != null && state.state != State.None;
1✔
412
    }
413

414
    /**
415
     * If this function need additional to the parameter of the signature an extra "this" parameter.
416
     * 
417
     * @param name
418
     *            the function name
419
     * @return true, if the function is static
420
     */
421
    boolean needThisParameter( @Nonnull FunctionName name ) {
422
        return getOrCreate( name ).needThisParameter;
1✔
423
    }
424

425
    /**
426
     * Add a replacement for a method
427
     * 
428
     * @param name
429
     *            the name of the method which should be replaced
430
     * @param method
431
     *            the new implementation
432
     */
433
    void addReplacement( @Nonnull FunctionName name, MethodInfo method ) {
434
        FunctionState state = getOrCreate( name );
1✔
435
        if( state.method == null ) { // ignore redefinition replacements and use the first instance in the library path
1✔
436
            state.method = method;
1✔
437
        }
438
    }
1✔
439

440
    /**
441
     * Set an alias for the method. If this method should be called then the alias method should be really called. This
442
     * is typical a virtual super method.
443
     * 
444
     * @param name
445
     *            the original name
446
     * @param alias
447
     *            the new name.
448
     */
449
    void setAlias( @Nonnull FunctionName name, FunctionName alias ) {
450
        FunctionState state = getOrCreate( name );
1✔
451
        state.alias = alias;
1✔
452
        state.state = State.Written;
1✔
453
    }
1✔
454

455
    /**
456
     * Check if there is a replacement method
457
     * 
458
     * @param name
459
     *            the name
460
     * @param method
461
     *            the current method
462
     * @return the method that should be write
463
     */
464
    @Nonnull
465
    MethodInfo replace( @Nonnull FunctionName name, MethodInfo method ) {
466
        MethodInfo newMethod = getOrCreate( name ).method;
1✔
467
        return newMethod != null ? newMethod : method;
1✔
468
    }
469

470
    /**
471
     * Set the index of a virtual function in a type.
472
     * 
473
     * @param name
474
     *            the name
475
     * @param vtableIdx
476
     *            the index in the vtable
477
     */
478
    void setVTableIndex( @Nonnull FunctionName name, int vtableIdx ) {
479
        getOrCreate( name ).vtableIdx = vtableIdx;
1✔
480
    }
1✔
481

482
    /**
483
     * Get the index of a virtual function in a type.
484
     * 
485
     * @param name
486
     *            the name
487
     * @return the index
488
     */
489
    int getVTableIndex( @Nonnull FunctionName name ) {
490
        return getOrCreate( name ).vtableIdx;
1✔
491
    }
492

493
    /**
494
     * Set the index of a function in an interface.
495
     * 
496
     * @param name
497
     *            the name
498
     * @param itableIdx
499
     *            the index in the itable
500
     */
501
    void setITableIndex( @Nonnull FunctionName name, int itableIdx ) {
502
        getOrCreate( name ).itableIdx = itableIdx;
1✔
503
    }
1✔
504

505
    /**
506
     * Get the index of a function in an interface.
507
     * 
508
     * @param name
509
     *            the name
510
     * @return the index in the itable
511
     */
512
    int getITableIndex( @Nonnull FunctionName name ) {
513
        return getOrCreate( name ).itableIdx;
1✔
514
    }
515

516
    /**
517
     * State of a function/method
518
     */
519
    private static class FunctionState {
1✔
520

521
        private State                    state     = State.None;
1✔
522

523
        private MethodInfo               method;
524

525
        private FunctionName             alias;
526

527
        private Function<String, Object> importAnannotation;
528

529
        private Map<String, Object>      exportAnannotation;
530

531
        private int                      vtableIdx = -1;
1✔
532

533
        private int                      itableIdx = -1;
1✔
534

535
        private boolean                  needThisParameter;
536
    }
537

538
    private static enum State {
1✔
539
        None, Needed, Scanned, Written, Abstract;
1✔
540
    }
541
}
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