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

i-net-software / JWebAssembly / 529

pending completion
529

push

travis-ci-com

Horcrux7
fix Unsafe for array elements

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

5925 of 6780 relevant lines covered (87.39%)

0.87 hits per line

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

89.35
/src/de/inetsoftware/jwebassembly/module/WasmCodeBuilder.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 java.util.ArrayList;
19
import java.util.Collections;
20
import java.util.Iterator;
21
import java.util.List;
22
import java.util.function.Function;
23

24
import javax.annotation.Nonnegative;
25
import javax.annotation.Nonnull;
26
import javax.annotation.Nullable;
27

28
import de.inetsoftware.classparser.BootstrapMethod;
29
import de.inetsoftware.classparser.ClassFile;
30
import de.inetsoftware.classparser.ConstantClass;
31
import de.inetsoftware.classparser.FieldInfo;
32
import de.inetsoftware.classparser.LocalVariableTable;
33
import de.inetsoftware.classparser.Member;
34
import de.inetsoftware.classparser.MethodInfo;
35
import de.inetsoftware.jwebassembly.WasmException;
36
import de.inetsoftware.jwebassembly.javascript.NonGC;
37
import de.inetsoftware.jwebassembly.module.StackInspector.StackValue;
38
import de.inetsoftware.jwebassembly.module.TypeManager.LambdaType;
39
import de.inetsoftware.jwebassembly.module.WasmInstruction.Type;
40
import de.inetsoftware.jwebassembly.wasm.AnyType;
41
import de.inetsoftware.jwebassembly.wasm.ArrayOperator;
42
import de.inetsoftware.jwebassembly.wasm.ArrayType;
43
import de.inetsoftware.jwebassembly.wasm.MemoryOperator;
44
import de.inetsoftware.jwebassembly.wasm.NamedStorageType;
45
import de.inetsoftware.jwebassembly.wasm.NumericOperator;
46
import de.inetsoftware.jwebassembly.wasm.StructOperator;
47
import de.inetsoftware.jwebassembly.wasm.ValueType;
48
import de.inetsoftware.jwebassembly.wasm.ValueTypeParser;
49
import de.inetsoftware.jwebassembly.wasm.VariableOperator;
50
import de.inetsoftware.jwebassembly.wasm.WasmBlockOperator;
51

52
/**
53
 * Base class for Code Building.
54
 *
55
 * @author Volker Berlin
56
 */
57
public abstract class WasmCodeBuilder {
58

59
    /** Java method name of of constructor */
60
    static final String         CONSTRUCTOR = "<init>";
61

62
    /** Java method name of static constructor or initialization method  */ 
63
    static final String         CLASS_INIT = "<clinit>";
64

65
    private final LocaleVariableManager localVariables;
66

67
    private final List<WasmInstruction> instructions;
68

69
    private TypeManager                 types;
70

71
    private FunctionManager             functions;
72

73
    private WasmOptions                 options;
74

75
    private StringManager               strings;
76

77
    private ClassFileLoader             classFileLoader;
78

79
    /**
80
     * Create a new instance of CodeBuilder
81
     */
82
    protected WasmCodeBuilder() {
1✔
83
        localVariables = new LocaleVariableManager();
1✔
84
        instructions = new ArrayList<>();
1✔
85
    }
1✔
86

87
    /**
88
     * Create a new instance with shared resources
89
     * 
90
     * @param codeBuilder
91
     *            other instance of CodeBuilder
92
     */
93
    WasmCodeBuilder( @Nonnull WasmCodeBuilder codeBuilder ) {
1✔
94
        localVariables = codeBuilder.localVariables;
1✔
95
        instructions = codeBuilder.instructions;
1✔
96
    }
1✔
97

98
    /**
99
     * Initialize the code builder;
100
     * 
101
     * @param options
102
     *            compiler properties
103
     * @param classFileLoader
104
     *            for loading the class files
105
     */
106
    void init( @Nonnull WasmOptions options, @Nonnull ClassFileLoader classFileLoader ) {
107
        this.localVariables.init( options.types );
1✔
108
        this.types = options.types;
1✔
109
        this.functions = options.functions;
1✔
110
        this.strings = options.strings; 
1✔
111
        this.options = options;
1✔
112
        this.classFileLoader = classFileLoader;
1✔
113
    }
1✔
114

115
    /**
116
     * Get the list of instructions
117
     * 
118
     * @return the list
119
     */
120
    @Nonnull
121
    List<WasmInstruction> getInstructions() {
122
        return instructions;
1✔
123
    }
124

125
    /**
126
     * Get the manager of local variables
127
     * @return the manager
128
     */
129
    @Nonnull
130
    LocaleVariableManager getLocalVariables() {
131
        return localVariables;
1✔
132
    }
133

134
    /**
135
     * Get the compiler settings
136
     * 
137
     * @return the settings
138
     */
139
    @Nonnull
140
    WasmOptions getOptions() {
141
        return options;
1✔
142
    }
143

144
    /**
145
     * Check if the last instruction is a return instruction
146
     * 
147
     * @return true, if a return
148
     */
149
    boolean isEndsWithReturn() {
150
        WasmInstruction instr = instructions.get( instructions.size() - 1 );
1✔
151
        if( instr.getType() == Type.Block ) {
1✔
152
            return ((WasmBlockInstruction)instr).getOperation() == WasmBlockOperator.RETURN;
1✔
153
        }
154
        return false;
×
155
    }
156

157
    /**
158
     * We need the value type from the stack.
159
     * 
160
     * @param count
161
     *            the count of values on the stack back. 1 means the last value. 2 means the penultimate value.
162
     * @param javaCodePos
163
     *            current code position for which the stack is inspected
164
     * @return the type of the last push value
165
     */
166
    @Nonnull
167
    AnyType findValueTypeFromStack( int count, int javaCodePos ) {
168
        return StackInspector.findInstructionThatPushValue( instructions, count, javaCodePos ).instr.getPushValueType();
1✔
169
    }
170

171
    /**
172
     * Find the array component type from stack.
173
     * 
174
     * @param count
175
     *            the count of values on the stack back. 1 means the last value. 2 means the penultimate value.
176
     * @param javaCodePos
177
     *            current code position for which the stack is inspected
178
     * @return the type
179
     */
180
    @Nonnull
181
    AnyType findArrayTypeFromStack( int count, int javaCodePos ) {
182
        StackValue stackValue = StackInspector.findInstructionThatPushValue( instructions, count, javaCodePos );
1✔
183
        AnyType type = stackValue.instr.getPushValueType();
1✔
184
        if( type instanceof ArrayType ) {
1✔
185
            return ((ArrayType)type).getArrayType();
1✔
186
        }
187

188
        if( stackValue.instr instanceof WasmLoadStoreInstruction ) {
×
189
            int slot = ((WasmLoadStoreInstruction)stackValue.instr).getSlot();
×
190
            ArrayType arrayType = types.arrayType( types.valueOf( "java/lang/Object" ) );
×
191
            localVariables.use( arrayType, slot, javaCodePos );
×
192
            return arrayType.getArrayType();
×
193
        }
194
        return ValueType.eqref;
×
195
    }
196

197
    /**
198
     * Find the instruction that push the x-th value to the stack.
199
     * 
200
     * @param count
201
     *            the count of values on the stack back. 1 means the last value. 2 means the penultimate value.
202
     * @param javaCodePos
203
     *            current code position for which the stack is inspected
204
     * @return the instruction
205
     */
206
    @Nonnull
207
    private WasmInstruction findInstructionThatPushValue( int count, int javaCodePos ) {
208
        return StackInspector.findInstructionThatPushValue( instructions, count, javaCodePos ).instr;
1✔
209
    }
210

211
    /**
212
     * Get the data types of the local variables. The value is only valid until the next call.
213
     * 
214
     * @param paramCount
215
     *            the count of method parameter which should be exclude
216
     * @return the reused list with fresh values
217
     */
218
    List<AnyType> getLocalTypes( int paramCount ) {
219
        return localVariables.getLocalTypes( paramCount );
1✔
220
    }
221

222
    /**
223
     * Get the name of the variable or null if no name available
224
     * 
225
     * @param idx
226
     *            the wasm variable index
227
     * @return the name
228
     */
229
    String getLocalName( int idx ) {
230
        return localVariables.getLocalName( idx );
1✔
231
    }
232

233
    /**
234
     * Get the slot of the temporary variable.
235
     * 
236
     * @param valueType
237
     *            the valueType for the variable
238
     * @param startCodePosition
239
     *            the start of the Java code position
240
     * @param endCodePosition
241
     *            the end of the Java code position
242
     * @return the slot
243
     */
244
    int getTempVariable( AnyType valueType, int startCodePosition, int endCodePosition ) {
245
        return localVariables.getTempVariable( valueType, startCodePosition, endCodePosition );
1✔
246
    }
247

248
    /**
249
     * Get the type manager.
250
     * 
251
     * @return the type manager
252
     */
253
    @Nonnull
254
    protected TypeManager getTypeManager() {
255
        return types;
1✔
256
    }
257

258
    /**
259
     * For loading missing class declarations
260
     * @return the loader
261
     */
262
    @Nonnull
263
    protected ClassFileLoader getClassFileLoader() {
264
        return classFileLoader;
×
265
    }
266

267
    /**
268
     * Reset the code builder.
269
     * 
270
     * @param variableTable
271
     *            variable table of the Java method.
272
     * @param method
273
     *            the method with signature as fallback for a missing variable table. If null signature is used and the method must be static.
274
     * @param signature
275
     *            alternative for method signature, can be null if method is set
276
     */
277
    protected void reset( LocalVariableTable variableTable, MethodInfo method, Iterator<AnyType> signature ) {
278
        instructions.clear();
1✔
279
        localVariables.reset( variableTable, method, signature );
1✔
280
    }
1✔
281

282
    /**
283
     * Calculate the index of the variables
284
     */
285
    protected void calculateVariables() {
286
        localVariables.calculate();
1✔
287
    }
1✔
288

289
    /**
290
     * Create a WasmLoadStoreInstruction.
291
     * 
292
     * @param valueType
293
     *            the value type
294
     * @param load
295
     *            true: if load
296
     * @param javaIdx
297
     *            the memory/slot index of the variable in Java byte code
298
     * @param javaCodePos
299
     *            the code position/offset in the Java method
300
     * @param lineNumber
301
     *            the line number in the Java source code
302
     */
303
    protected void addLoadStoreInstruction( AnyType valueType, boolean load, @Nonnegative int javaIdx, int javaCodePos, int lineNumber ) {
304
        localVariables.use( valueType, javaIdx, javaCodePos );
1✔
305
        instructions.add( new WasmLoadStoreInstruction( load ? VariableOperator.get : VariableOperator.set, javaIdx, localVariables, javaCodePos, lineNumber ) );
1✔
306
    }
1✔
307

308
    /**
309
     * Create a WasmLoadStoreInstruction local.get/local.set.
310
     * 
311
     * @param op
312
     *            the operation
313
     * @param wasmIdx
314
     *            the index of the variable
315
     * @param javaCodePos
316
     *            the code position/offset in the Java method
317
     * @param lineNumber
318
     *            the line number in the Java source code
319
     */
320
    protected void addLocalInstruction( VariableOperator op, @Nonnegative int wasmIdx, int javaCodePos, int lineNumber ) {
321
        switch( op ) {
1✔
322
            case set:
323
            case tee:
324
                AnyType valueType = findValueTypeFromStack( 1, javaCodePos );
1✔
325
                localVariables.useIndex( valueType, wasmIdx );
1✔
326
        }
327
        instructions.add( new WasmLocalInstruction( op, wasmIdx, localVariables, javaCodePos, lineNumber ) );
1✔
328
    }
1✔
329

330
    /**
331
     * Get a possible slot from the instruction
332
     * 
333
     * @param instr
334
     *            the instruction
335
     * @return the slot or -1 if there no slot
336
     */
337
    private static int getPossibleSlot( @Nonnull WasmInstruction instr ) {
338
        // if it is a GET to a local variable then we can use it
339
        if( instr.getType() == Type.Local ) {
1✔
340
            WasmLocalInstruction local1 = (WasmLocalInstruction)instr;
1✔
341
            switch( local1.getOperator() ) {
1✔
342
                case get:
343
                case tee:
344
                    return local1.getSlot();
1✔
345
                default:
346
            }
347
        }
348
        return -1;
1✔
349
    }
350

351
    /**
352
     * Create a WasmDupInstruction.
353
     * 
354
     * @param dup2
355
     *            call from dup2 instruction
356
     * @param javaCodePos
357
     *            the code position/offset in the Java method
358
     * @param lineNumber
359
     *            the line number in the Java source code
360
     */
361
    protected void addDupInstruction( boolean dup2, int javaCodePos, int lineNumber ) {
362
        WasmInstruction instr = findInstructionThatPushValue( 1, javaCodePos );
1✔
363
        AnyType type = instr.getPushValueType();
1✔
364
        int slot = getPossibleSlot( instr );
1✔
365

366
        // occur with:
367
        // int[] data = new int[x];
368
        // data[i] |= any;
369
        instr = dup2 && type != ValueType.i64 && type != ValueType.f64 ? //
1✔
370
                        findInstructionThatPushValue( 2, javaCodePos ) : null;
1✔
371

372
        int slot2;
373
        if( instr != null ) {
1✔
374
            slot2 = getPossibleSlot( instr );
1✔
375
            if( slot2 < 0 ) {
1✔
376
                slot2 = getTempVariable( instr.getPushValueType(), javaCodePos, javaCodePos + 1 );
1✔
377
                if( slot < 0 ) {
1✔
378
                    slot = getTempVariable( type, javaCodePos, javaCodePos + 1 );
1✔
379
                    instructions.add( new WasmLoadStoreInstruction( VariableOperator.set, slot, localVariables, javaCodePos, lineNumber ) );
1✔
380
                } else {
381
                    instructions.add( new WasmBlockInstruction( WasmBlockOperator.DROP, null, javaCodePos, lineNumber ) );
×
382
                }
383
                instructions.add( new WasmLoadStoreInstruction( VariableOperator.tee, slot2, localVariables, javaCodePos, lineNumber ) );
1✔
384
                instructions.add( new WasmLoadStoreInstruction( VariableOperator.get, slot, localVariables, javaCodePos, lineNumber ) );
1✔
385
            }
386
        } else {
387
            slot2 = -1; // for compiler only
1✔
388
        }
389

390
        //alternate we need to create a new locale variable
391
        if( slot < 0 ) {
1✔
392
            slot = getTempVariable( type, javaCodePos, javaCodePos + 1 );
1✔
393
            instructions.add( new WasmLoadStoreInstruction( VariableOperator.tee, slot, localVariables, javaCodePos, lineNumber ) );
1✔
394
        } else {
395
            localVariables.expandUse( slot, javaCodePos );
1✔
396
        }
397
        if( instr != null ) {
1✔
398
            instructions.add( new WasmLoadStoreInstruction( VariableOperator.get, slot2, localVariables, javaCodePos, lineNumber ) );
1✔
399
        }
400
        instructions.add( new WasmLoadStoreInstruction( VariableOperator.get, slot, localVariables, javaCodePos, lineNumber ) );
1✔
401
    }
1✔
402

403
    /**
404
     * Simulate the dup_x1 Java byte code instruction.<p>
405
     * 
406
     * ..., value2, value1 → ..., value1, value2, value1
407
     * 
408
     * @param javaCodePos
409
     *            the code position/offset in the Java method
410
     * @param lineNumber
411
     *            the line number in the Java source code
412
     */
413
    protected void addDupX1Instruction( int javaCodePos, int lineNumber ) {
414
        AnyType type1 = findValueTypeFromStack( 1, javaCodePos );
1✔
415
        AnyType type2 = findValueTypeFromStack( 2, javaCodePos );
1✔
416

417
        int varSlot1 = getTempVariable( type1, javaCodePos, javaCodePos + 1 );
1✔
418
        int varSlot2 = getTempVariable( type2, javaCodePos, javaCodePos + 1 );
1✔
419

420
        // save in temp variables
421
        instructions.add( new WasmLoadStoreInstruction( VariableOperator.set, varSlot1, localVariables, javaCodePos, lineNumber ) );
1✔
422
        instructions.add( new WasmLoadStoreInstruction( VariableOperator.set, varSlot2, localVariables, javaCodePos, lineNumber ) );
1✔
423

424
        // and restore it in new order on the stack
425
        instructions.add( new WasmLoadStoreInstruction( VariableOperator.get, varSlot1, localVariables, javaCodePos, lineNumber ) );
1✔
426
        instructions.add( new WasmLoadStoreInstruction( VariableOperator.get, varSlot2, localVariables, javaCodePos, lineNumber ) );
1✔
427
        instructions.add( new WasmLoadStoreInstruction( VariableOperator.get, varSlot1, localVariables, javaCodePos, lineNumber ) );
1✔
428
    }
1✔
429

430
    /**
431
     * Simulate the dup_x2 Java byte code instruction.<p>
432
     * 
433
     * ..., value3, value2, value1 → ..., value1, value3, value2, value1
434
     * 
435
     * @param javaCodePos
436
     *            the code position/offset in the Java method
437
     * @param lineNumber
438
     *            the line number in the Java source code
439
     */
440
    protected void addDupX2Instruction( int javaCodePos, int lineNumber ) {
441
        AnyType type1 = findValueTypeFromStack( 1, javaCodePos );
1✔
442
        AnyType type2 = findValueTypeFromStack( 2, javaCodePos );
1✔
443
        AnyType type3 = findValueTypeFromStack( 3, javaCodePos );
1✔
444

445
        int varSlot1 = getTempVariable( type1, javaCodePos, javaCodePos + 1 );
1✔
446
        int varSlot2 = getTempVariable( type2, javaCodePos, javaCodePos + 1 );
1✔
447
        int varSlot3 = getTempVariable( type3, javaCodePos, javaCodePos + 1 );
1✔
448

449
        // save in temp variables
450
        instructions.add( new WasmLoadStoreInstruction( VariableOperator.set, varSlot1, localVariables, javaCodePos, lineNumber ) );
1✔
451
        instructions.add( new WasmLoadStoreInstruction( VariableOperator.set, varSlot2, localVariables, javaCodePos, lineNumber ) );
1✔
452
        instructions.add( new WasmLoadStoreInstruction( VariableOperator.set, varSlot3, localVariables, javaCodePos, lineNumber ) );
1✔
453

454
        // and restore it in new order on the stack
455
        instructions.add( new WasmLoadStoreInstruction( VariableOperator.get, varSlot1, localVariables, javaCodePos, lineNumber ) );
1✔
456
        instructions.add( new WasmLoadStoreInstruction( VariableOperator.get, varSlot3, localVariables, javaCodePos, lineNumber ) );
1✔
457
        instructions.add( new WasmLoadStoreInstruction( VariableOperator.get, varSlot2, localVariables, javaCodePos, lineNumber ) );
1✔
458
        instructions.add( new WasmLoadStoreInstruction( VariableOperator.get, varSlot1, localVariables, javaCodePos, lineNumber ) );
1✔
459
    }
1✔
460

461
    /**
462
     * Add a global instruction
463
     * 
464
     * @param load
465
     *            true: if load
466
     * @param ref
467
     *            reference to a static field
468
     * @param javaCodePos
469
     *            the code position/offset in the Java method
470
     * @param lineNumber
471
     *            the line number in the Java source code
472
     */
473
    protected void addGlobalInstruction( boolean load, Member ref, int javaCodePos, int lineNumber ) {
474
        FunctionName name = new FunctionName( ref );
1✔
475
        AnyType type = new ValueTypeParser( ref.getType(), types ).next();
1✔
476
        FunctionName clinit;
477
        if( load ) {
1✔
478
            clinit = new FunctionName( name.className, CLASS_INIT, "()V" );
1✔
479
            if( !functions.isUsed( clinit ) ) {
1✔
480
                clinit = null;
1✔
481
            }
482
        } else {
483
            clinit = null;
1✔
484
        }
485
        addGlobalInstruction( load, name, type, clinit, javaCodePos, lineNumber );
1✔
486
    }
1✔
487

488
    /**
489
     * Add a global instruction
490
     * 
491
     * @param load
492
     *            true: if load
493
     * @param name
494
     *            reference to a static field
495
     * @param type
496
     *            the type of the static field
497
     * @param clinit
498
     *            a reference to the class/static constructor which should executed before access a static field
499
     * @param javaCodePos
500
     *            the code position/offset in the Java method
501
     * @param lineNumber
502
     *            the line number in the Java source code
503
     */
504
    protected void addGlobalInstruction( boolean load, FunctionName name, AnyType type, FunctionName clinit, int javaCodePos, int lineNumber ) {
505
        instructions.add( new WasmGlobalInstruction( load, name, type, clinit, javaCodePos, lineNumber ) );
1✔
506
        functions.markClassAsUsed( name.className );
1✔
507
    }
1✔
508

509
    /**
510
     * Add a global field access instruction
511
     * 
512
     * @param load
513
     *            true: if load (get)
514
     * @param fieldName
515
     *            the fieldName like java/lang/System.err
516
     * @param javaCodePos
517
     *            the code position/offset in the Java method
518
     * @param lineNumber
519
     *            the line number in the Java source code
520
     */
521
    protected void addGlobalInstruction( boolean load, String fieldName, int javaCodePos, int lineNumber ) {
522
        try {
523
            if( fieldName.startsWith( "$" ) ) {
1✔
524
                fieldName = fieldName.substring( 1 );
1✔
525
            }
526
            FunctionName name = new FunctionName( fieldName + "()V" );
1✔
527
            ClassFile classFile = classFileLoader.get( name.className );
1✔
528
            FieldInfo field = classFile.getField( name.methodName );
1✔
529
            AnyType type = new ValueTypeParser( field.getType(), types ).next();
1✔
530
            addGlobalInstruction( load, name, type, null, javaCodePos, lineNumber );
1✔
531
        } catch( Throwable ex ) {
×
532
            throw WasmException.create( ex, lineNumber );
×
533
        }
1✔
534
    }
1✔
535

536
    /**
537
     * Add a WasmTableInstruction table.get/table.set.
538
     * 
539
     * @param load
540
     *            true: if load
541
     * @param idx
542
     *            the index of the table
543
     * @param javaCodePos
544
     *            the code position/offset in the Java method
545
     * @param lineNumber
546
     *            the line number in the Java source code
547
     */
548
    protected void addTableInstruction( boolean load, @Nonnegative int idx, int javaCodePos, int lineNumber ) {
549
        instructions.add( new WasmTableInstruction( load, idx, javaCodePos, lineNumber ) );
1✔
550
    }
1✔
551

552
    /**
553
     * Add a constant instruction.
554
     * 
555
     * @param value
556
     *            the value
557
     * @param valueType
558
     *            the value type
559
     * @param javaCodePos
560
     *            the code position/offset in the Java method
561
     * @param lineNumber
562
     *            the line number in the Java source code
563
     */
564
    protected void addConstInstruction( Number value, ValueType valueType, int javaCodePos, int lineNumber ) {
565
        instructions.add( new WasmConstNumberInstruction( value, valueType, javaCodePos, lineNumber ) );
1✔
566
    }
1✔
567

568
    /**
569
     * Add a constant instruction with unknown value type.
570
     * 
571
     * @param value
572
     *            the value
573
     * @param javaCodePos
574
     *            the code position/offset in the Java method
575
     * @param lineNumber
576
     *            the line number in the Java source code
577
     */
578
    protected void addConstInstruction( Object value, int javaCodePos, int lineNumber ) {
579
        if( value.getClass() == String.class ) {
1✔
580
            instructions.add( new WasmConstStringInstruction( (String)value, strings, types, javaCodePos, lineNumber ) );
1✔
581
        } else if( value instanceof Number ) {
1✔
582
            instructions.add( new WasmConstNumberInstruction( (Number)value, javaCodePos, lineNumber ) );
1✔
583
        } else if( value instanceof ConstantClass ) {
1✔
584
            instructions.add( new WasmConstClassInstruction( (ConstantClass)value, types, javaCodePos, lineNumber ) );
1✔
585
        } else {
586
            //TODO There can be MethodType and MethodHandle
587
            throw new WasmException( "Class constants are not supported. : " + value, lineNumber );
×
588
        }
589
    }
1✔
590

591
    /**
592
     * Add a numeric operation instruction
593
     * 
594
     * @param numOp
595
     *            the operation
596
     * @param valueType
597
     *            the value type
598
     * @param javaCodePos
599
     *            the code position/offset in the Java method
600
     * @param lineNumber
601
     *            the line number in the Java source code
602
     * @return the added instruction
603
     */
604
    protected WasmNumericInstruction addNumericInstruction( @Nullable NumericOperator numOp, @Nullable ValueType valueType, int javaCodePos, int lineNumber ) {
605
        WasmNumericInstruction numeric = new WasmNumericInstruction( numOp, valueType, javaCodePos, lineNumber );
1✔
606
        instructions.add( numeric );
1✔
607
        if( !options.useGC() && options.ref_eq == null && (numOp == NumericOperator.ref_eq || numOp == NumericOperator.ref_ne ) ) {
1✔
608
            functions.markAsNeeded( options.ref_eq = getNonGC( "ref_eq", lineNumber ), false );
1✔
609
        }
610
        return numeric;
1✔
611
    }
612

613
    /**
614
     * Add a value convert/cast instruction.
615
     * 
616
     * @param conversion
617
     *            the conversion
618
     * @param javaCodePos
619
     *            the code position/offset in the Java method
620
     * @param lineNumber
621
     *            the line number in the Java source code
622
     */
623
    protected void addConvertInstruction( ValueTypeConvertion conversion, int javaCodePos, int lineNumber ) {
624
        instructions.add( new WasmConvertInstruction( conversion, javaCodePos, lineNumber ) );
1✔
625
    }
1✔
626

627
    /**
628
     * Add a static function call.
629
     * 
630
     * @param name
631
     *            the function name that should be called
632
     * @param needThisParameter
633
     *            true, if the hidden THIS parameter is needed, If it is an instance method call.
634
     * @param javaCodePos
635
     *            the code position/offset in the Java method
636
     * @param lineNumber
637
     *            the line number in the Java source code
638
     */
639
    protected void addCallInstruction( @Nonnull FunctionName name, boolean needThisParameter, int javaCodePos, int lineNumber ) {
640
        name = functions.markAsNeeded( name, needThisParameter );
1✔
641
        WasmCallInstruction instruction = new WasmCallInstruction( name, javaCodePos, lineNumber, types, needThisParameter );
1✔
642

643
        if( CONSTRUCTOR.equals( name.methodName ) ) {
1✔
644
            // check if there a factory for the constructor in JavaScript then we need to do some more complex patching
645
            Function<String, Object> importAnannotation = functions.getImportAnannotation( name );
1✔
646
            FunctionName factoryName = null;
1✔
647

648
            if( importAnannotation != null ) { // JavaScript replacement for a constructor via import
1✔
649
                // The new signature need a return value. The <init> of Java has ever a void return value
650
                String signature = name.signature;
×
651
                signature = signature.substring( 0, signature.length() - 1 ) + "Ljava/lang/Object;";
×
652
                factoryName = new ImportSyntheticFunctionName( "String", "init", signature, importAnannotation );
×
653
            } else {
×
654
                MethodInfo replace = functions.replace( name, null );
1✔
655
                if( replace != null && !CONSTRUCTOR.equals( replace.getName() ) ) {
1✔
656
                    // the constructor was replaced with a factory method. Typical this method called then a JavaScript replacement
657
                    factoryName = new FunctionName( replace );
×
658
                }
659
            }
660

661
            if( factoryName != null ) {
1✔
662
                // the constructor was replaced we need also replace the create instance instruction
663
                List<WasmInstruction> instructions = this.instructions;
×
664
                for( int i = instructions.size() - 1; i >= 0; i-- ) {
×
665
                    WasmInstruction instr = instructions.get( i );
×
666
                    if( instr.getType() == Type.Struct ) {
×
667
                        WasmStructInstruction struct = (WasmStructInstruction)instr;
×
668
                        if( struct.getOperator() == StructOperator.NEW_DEFAULT ) {
×
669
                            instructions.set( i, new WasmNopInstruction( struct.getCodePosition(), struct.getLineNumber() ) ); // replace NEW_DEFAULT with Nop, Nop because the code position can be needed for the branch manager
×
670
                            instr = instructions.get( ++i );
×
671
//                            if( instr.getType() == Type.Dup ) {
672
//                                instructions.remove( i ); // dup of the instance reference if it is later assign, missing if the object after the constructor is never assign
673
//                            }
674
                            break;
×
675
                        }
676
                    }
677
                }
678
                // the new instruction
679
                instruction = new WasmCallInstruction( factoryName, javaCodePos, lineNumber, types, false );
×
680
            }
681
        }
682

683
        instructions.add( instruction );
1✔
684
        functions.markClassAsUsed( name.className );
1✔
685
    }
1✔
686

687
    /**
688
     * Add indirect call to the instruction.
689
     * 
690
     * @param indirectCall
691
     *            the instruction
692
     */
693
    private void addCallIndirectInstruction( WasmCallIndirectInstruction indirectCall ) {
694
        //  For access to the vtable the THIS parameter must be duplicated on stack before the function parameters 
695

696
        // find the instruction that THIS push on the stack 
697
        int count = indirectCall.getPopCount();
1✔
698
        int javaCodePos = indirectCall.getCodePosition();
1✔
699
        StackValue stackValue = StackInspector.findInstructionThatPushValue( instructions, count, javaCodePos );
1✔
700
        WasmInstruction instr = stackValue.instr;
1✔
701
        int varSlot = -1;
1✔
702
        // if it is a GET to a local variable then we can use it
703
        if( instr.getType() == Type.Local ) {
1✔
704
            WasmLocalInstruction local1 = (WasmLocalInstruction)instr;
1✔
705
            if( local1.getOperator() == VariableOperator.get ) {
1✔
706
                varSlot = local1.getSlot();
1✔
707
            }
708
        }
709
        //alternate we need to create a new locale variable
710
        if( varSlot < 0 ) {
1✔
711
            varSlot = getTempVariable( indirectCall.getThisType(), instr.getCodePosition(), javaCodePos + 1 );
1✔
712
            int idx = count == 1 ? instructions.size() : stackValue.idx + 1;
1✔
713
            instructions.add( idx, new DupThis( indirectCall, varSlot, localVariables, instr.getCodePosition() + 1 ) );
1✔
714
        }
715
        indirectCall.setVariableSlotOfThis( varSlot, localVariables );
1✔
716
        instructions.add( indirectCall );
1✔
717
        options.registerGet_i32(); // for later access of the vtable
1✔
718
    }
1✔
719

720
    /**
721
     * Add a virtual/method function call.
722
     * 
723
     * @param name
724
     *            the function name that should be called
725
     * @param javaCodePos
726
     *            the code position/offset in the Java method
727
     * @param lineNumber
728
     *            the line number in the Java source code
729
     */
730
    protected void addCallVirtualInstruction( FunctionName name, int javaCodePos, int lineNumber ) {
731
        name = functions.markAsNeeded( name, true );
1✔
732
        addCallIndirectInstruction( new WasmCallVirtualInstruction( name, javaCodePos, lineNumber, types, options ) );
1✔
733
        options.getCallVirtual(); // mark the function as needed
1✔
734
        functions.markClassAsUsed( name.className );
1✔
735
    }
1✔
736

737
    /**
738
     * Add interface function call
739
     * @param name
740
     *            the function name that should be called
741
     * @param javaCodePos
742
     *            the code position/offset in the Java method
743
     * @param lineNumber
744
     *            the line number in the Java source code
745
     */
746
    protected void addCallInterfaceInstruction( FunctionName name, int javaCodePos, int lineNumber ) {
747
        name = functions.markAsNeeded( name, true );
1✔
748
        addCallIndirectInstruction( new WasmCallInterfaceInstruction( name, javaCodePos, lineNumber, types, options ) );
1✔
749
        options.getCallInterface(); // mark the function as needed
1✔
750
        functions.markClassAsUsed( name.className );
1✔
751
    }
1✔
752

753
    /**
754
     * Add a block operation.
755
     * 
756
     * @param op
757
     *            the operation
758
     * @param data
759
     *            extra data for some operations
760
     * @param javaCodePos
761
     *            the code position/offset in the Java method
762
     * @param lineNumber
763
     *            the line number in the Java source code
764
     */
765
    protected void addBlockInstruction( WasmBlockOperator op, @Nullable Object data, int javaCodePos, int lineNumber ) {
766
        instructions.add( new WasmBlockInstruction( op, data, javaCodePos, lineNumber ) );
1✔
767
    }
1✔
768

769
    /**
770
     * Add a no operation to the instruction list as marker on the code position. This instruction will not be write to
771
     * the output.
772
     * 
773
     * @param javaCodePos
774
     *            the code position/offset in the Java method
775
     * @param lineNumber
776
     *            the line number in the Java source code
777
     */
778
    protected void addNopInstruction( int javaCodePos, int lineNumber ) {
779
        instructions.add( new WasmNopInstruction( javaCodePos, lineNumber ) );
1✔
780
    }
1✔
781

782
    /**
783
     * Add a Jump instruction for later stack inspection
784
     * 
785
     * @param jumpPos
786
     *            the position of the jump
787
     * @param popCount
788
     *            the the count of values that are removed from the stack.
789
     * @param pushValueType
790
     *            optional type of a push value
791
     * @param javaCodePos
792
     *            the code position/offset in the Java method
793
     * @param lineNumber
794
     *            the line number in the Java source code
795
     */
796
    protected void addJumpPlaceholder( int jumpPos, int popCount, AnyType pushValueType, int javaCodePos, int lineNumber ) {
797
        instructions.add( new JumpInstruction( jumpPos, popCount, pushValueType, javaCodePos, lineNumber ) );
1✔
798
    }
1✔
799

800
    /**
801
     * Get a non GC polyfill function.
802
     * @param name the function name
803
     * @param lineNumber the line number for a possible error
804
     * @return the function name
805
     */
806
    private FunctionName getNonGC( String name, int lineNumber ) {
807
        try {
808
            ClassFile classFile = classFileLoader.get( NonGC.class.getName().replace( '.', '/' ) );
1✔
809
            for( MethodInfo method : classFile.getMethods() ) {
1✔
810
                if( name.equals( method.getName() ) ) {
1✔
811
                    return new FunctionName( method );
1✔
812
                }
813
            }
814
        } catch( Throwable ex ) {
×
815
            throw WasmException.create( ex, lineNumber );
×
816
        }
×
817
        throw new WasmException( "Not implemented NonGC polyfill function: " + name, lineNumber );
×
818
    }
819

820
    /**
821
     * Add an array operation to the instruction list as marker on the code position.
822
     * 
823
     * @param op
824
     *            the operation
825
     * @param type
826
     *            the array/component type of the array
827
     * @param javaCodePos
828
     *            the code position/offset in the Java method
829
     * @param lineNumber
830
     *            the line number in the Java source code
831
     */
832
    protected void addArrayInstruction( @Nonnull ArrayOperator op, @Nonnull AnyType type, int javaCodePos, int lineNumber ) {
833
        boolean useGC = options.useGC();
1✔
834
        if( useGC ) {
1✔
835
            // replace the the array wrapper on the stack with the native array 
836
            int idx;
837
            int javaPos = javaCodePos;
1✔
838
            switch( op ) {
1✔
839
                case GET:
840
                case GET_S:
841
                case GET_U:
842
                    StackValue stackValue = StackInspector.findInstructionThatPushValue( instructions, 1, javaCodePos );
1✔
843
                    idx = stackValue.idx;
1✔
844
                    javaPos = stackValue.instr.getCodePosition();
1✔
845
                    break;
1✔
846
                case SET:
847
                    stackValue = StackInspector.findInstructionThatPushValue( instructions, 2, javaCodePos );
1✔
848
                    idx = stackValue.idx;
1✔
849
                    javaPos = stackValue.instr.getCodePosition();
1✔
850
                    break;
1✔
851
                case LEN:
852
                    idx = instructions.size();
1✔
853
                    break;
1✔
854
                default:
855
                    idx = -1;
1✔
856
            }
857
            if( idx >= 0 ) {
1✔
858
                ArrayType arrayType = types.arrayType( type );
1✔
859
                instructions.add( idx, new WasmStructInstruction( StructOperator.GET, arrayType, arrayType.getNativeFieldName(), javaPos, lineNumber, types ) );
1✔
860
            }
861
        }
862

863
        WasmArrayInstruction arrayInst = new WasmArrayInstruction( op, type, types, javaCodePos, lineNumber );
1✔
864
        instructions.add( arrayInst );
1✔
865
        SyntheticFunctionName name = arrayInst.createNonGcFunction( useGC );
1✔
866
        if( name != null ) {
1✔
867
            functions.markAsNeeded( name, !name.istStatic() );
1✔
868
            functions.markAsImport( name, name.getAnnotation() );
1✔
869
        }
870
    }
1✔
871

872
    /**
873
     * Add a new multi dimensional array instruction
874
     * 
875
     * @param dim
876
     *            the dimension of the array &gt;= 2
877
     * @param typeName
878
     *            the full type name
879
     * @param javaCodePos
880
     *            the code position/offset in the Java method
881
     * @param lineNumber
882
     *            the line number in the Java source code
883
     */
884
    protected void addMultiNewArrayInstruction( int dim, String typeName, int javaCodePos, int lineNumber ) {
885
        ArrayType type = (ArrayType)new ValueTypeParser( typeName, types ).next();
1✔
886
        addMultiNewArrayInstruction( dim, type, javaCodePos, lineNumber );
1✔
887
    }
1✔
888

889
    /**
890
     * Add a new multi dimensional array instruction
891
     * 
892
     * @param dim
893
     *            the dimension of the array &gt;= 2
894
     * @param type
895
     *            the full type
896
     * @param javaCodePos
897
     *            the code position/offset in the Java method
898
     * @param lineNumber
899
     *            the line number in the Java source code
900
     */
901
    protected void addMultiNewArrayInstruction( int dim, ArrayType type, int javaCodePos, int lineNumber ) {
902
        MultiArrayFunctionName name = new MultiArrayFunctionName( dim, type );
1✔
903
        addCallInstruction( name, false, javaCodePos, lineNumber );
1✔
904
    }
1✔
905

906
    /**
907
     * Add a struct/object operation to the instruction list.
908
     * 
909
     * @param op
910
     *            the operation
911
     * @param typeName
912
     *            the type name like "java/lang/Object"
913
     * @param fieldName
914
     *            the name of field if needed for the operation
915
     * @param javaCodePos
916
     *            the code position/offset in the Java method
917
     * @param lineNumber
918
     *            the line number in the Java source code
919
     */
920
    protected void addStructInstruction( StructOperator op, @Nonnull String typeName, @Nullable NamedStorageType fieldName, int javaCodePos, int lineNumber ) {
921
        WasmStructInstruction structInst = new WasmStructInstruction( op, typeName, fieldName, javaCodePos, lineNumber, types );
1✔
922
        instructions.add( structInst );
1✔
923
        switch( op ) {
1✔
924
            case CAST:
925
            case INSTANCEOF:
926
                structInst.createNonGcFunction();
1✔
927
                break;
1✔
928
            case NEW_DEFAULT:
929
                if( options.useGC() ) {
1✔
930
                    addDupInstruction( false, javaCodePos, lineNumber );
1✔
931
                    addConstInstruction( structInst.getStructType().getVTable(), javaCodePos, lineNumber );
1✔
932
                    instructions.add( new WasmStructInstruction( StructOperator.SET, typeName, new NamedStorageType( ValueType.i32, "", TypeManager.FIELD_VTABLE ), javaCodePos, lineNumber, types ) );
1✔
933
                    break;
1✔
934
                }
935
                //$FALL-THROUGH$
936
            default:
937
                if( !options.useGC() ) {
1✔
938
                    SyntheticFunctionName name = structInst.createNonGcFunction();
1✔
939
                    if( name != null ) {
1✔
940
                        functions.markAsNeeded( name, !name.istStatic() );
1✔
941
                        functions.markAsImport( name, name.getAnnotation() );
1✔
942
                    }
943
                }
944
        }
945
    }
1✔
946

947
    /**
948
     * Add invoke dynamic operation. (Creating of a lambda expression)
949
     * 
950
     * @param method
951
     *            the BootstrapMethod, described the method that should be executed
952
     * @param factorySignature
953
     *            Get the signature of the factory method. For example "()Ljava.lang.Runnable;" for the lambda expression
954
     *            <code>Runnable run = () -&gt; foo();</code>
955
     * @param interfaceMethodName
956
     *            The simple name of the generated method of the single function interface.
957
     * @param javaCodePos
958
     *            the code position/offset in the Java method
959
     * @param lineNumber
960
     *            the line number in the Java source code
961
     */
962
    protected void addInvokeDynamic( BootstrapMethod method, String factorySignature, String interfaceMethodName, int javaCodePos, int lineNumber ) {
963
        // Create the synthetic lambda class that hold the lambda expression.
964
        LambdaType type = types.lambdaType( method, factorySignature, interfaceMethodName, lineNumber );
1✔
965
        functions.markAsNeeded( type.getLambdaMethod(), true );
1✔
966
        String lambdaTypeName = type.getName();
1✔
967

968
        // Create the instance of the synthetic lambda class and save the parameters in fields  
969
        ArrayList<NamedStorageType> paramFields = type.getParamFields();
1✔
970
        int paramCount = paramFields.size();
1✔
971
        if( paramCount == 0 ) {
1✔
972
            addStructInstruction( StructOperator.NEW_DEFAULT, lambdaTypeName, null, javaCodePos, lineNumber );
1✔
973
        } else {
974
            // Lambda with parameters from the stack
975
            int idx = StackInspector.findInstructionThatPushValue( instructions, paramCount, javaCodePos ).idx;
1✔
976
            int pos = instructions.size();
1✔
977
            addStructInstruction( StructOperator.NEW_DEFAULT, lambdaTypeName, null, javaCodePos, lineNumber );
1✔
978
            boolean extraDup = !options.useGC();
1✔
979
            if( extraDup ) {
1✔
980
                // Bringing forward the DUP operation of the parameter loop to get a slot
981
                addDupInstruction( false, javaCodePos, lineNumber );
1✔
982
            }
983
            int slot = ((WasmLocalInstruction)findInstructionThatPushValue( 1, javaCodePos )).getSlot();
1✔
984

985
            // move the creating of the lambda instance before the parameters on the stack
986
            Collections.rotate( instructions.subList( idx, instructions.size() ), idx - pos );
1✔
987

988
            for( int i = 0; i < paramCount; i++ ) {
1✔
989
                NamedStorageType field = paramFields.get( i );
1✔
990
                if( i > 0 || !extraDup ) {
1✔
991
                    idx = StackInspector.findInstructionThatPushValue( instructions, paramCount - i, javaCodePos ).idx;
1✔
992
                    instructions.add( idx, new WasmLoadStoreInstruction( VariableOperator.get, slot, localVariables, javaCodePos, lineNumber ) );
1✔
993
                }
994
                pos = instructions.size();
1✔
995
                idx = paramCount - i - 1;
1✔
996
                idx = idx == 0 ? pos : StackInspector.findInstructionThatPushValue( instructions, idx, javaCodePos ).idx;
1✔
997
                addStructInstruction( StructOperator.SET, lambdaTypeName, field, javaCodePos, lineNumber );
1✔
998
                if( idx < pos ) {
1✔
999
                    Collections.rotate( instructions.subList( idx, instructions.size() ), idx - pos );
1✔
1000
                }
1001
            }
1002
        }
1003
    }
1✔
1004

1005
    /**
1006
     * Create an instance of a load/store to the linear memory instruction
1007
     * 
1008
     * @param op
1009
     *            the operation
1010
     * @param type
1011
     *            the type of the static field
1012
     * @param offset
1013
     *            the base offset which will be added to the offset value on the stack
1014
     * @param alignment
1015
     *            the alignment of the value on the linear memory (0: 8 Bit; 1: 16 Bit; 2: 32 Bit)
1016
     * @param javaCodePos
1017
     *            the code position/offset in the Java method
1018
     * @param lineNumber
1019
     *            the line number in the Java source code
1020
     */
1021
    protected void addMemoryInstruction( MemoryOperator op, ValueType type, int offset, int alignment, int javaCodePos, int lineNumber ) {
1022
        instructions.add( new WasmMemoryInstruction( op, type, offset, alignment, javaCodePos, lineNumber ) );
1✔
1023
    }
1✔
1024
}
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