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

i-net-software / JWebAssembly / 534

pending completion
534

push

travis-ci-com

Horcrux7
replace static constructor für MethodHandles.

1 of 1 new or added line in 1 file covered. (100.0%)

5933 of 6826 relevant lines covered (86.92%)

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 - 2023 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.FIELDUPDATER:
177
                case UnsafeManager.VARHANDLE:
178
                case UnsafeManager.METHOD_HANDLES:
179
                    // Unsafe method call will be replaces by the UnsafeManager
180
                    return name;
1✔
181
            }
182
            if( isFinish ) {
1✔
183
                throw new WasmException( "Prepare was already finish: " + name.signatureName, -1 );
×
184
            }
185
            neededCount++;
1✔
186
            state.state = State.Needed;
1✔
187
            state.needThisParameter = needThisParameter;
1✔
188
            JWebAssembly.LOGGER.fine( "\t\tcall: " + name.signatureName );
1✔
189
            usedClasses.add( name.className );
1✔
190

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

523
        private State                    state     = State.None;
1✔
524

525
        private MethodInfo               method;
526

527
        private FunctionName             alias;
528

529
        private Function<String, Object> importAnannotation;
530

531
        private Map<String, Object>      exportAnannotation;
532

533
        private int                      vtableIdx = -1;
1✔
534

535
        private int                      itableIdx = -1;
1✔
536

537
        private boolean                  needThisParameter;
538
    }
539

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