• 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

45.89
/src/de/inetsoftware/jwebassembly/module/UnsafeManager.java
1
/*
2
 * Copyright 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.Collections;
19
import java.util.HashMap;
20
import java.util.HashSet;
21
import java.util.List;
22
import java.util.Set;
23

24
import javax.annotation.Nonnull;
25

26
import de.inetsoftware.jwebassembly.WasmException;
27
import de.inetsoftware.jwebassembly.module.StackInspector.StackValue;
28
import de.inetsoftware.jwebassembly.module.WasmInstruction.Type;
29
import de.inetsoftware.jwebassembly.wasm.AnyType;
30
import de.inetsoftware.jwebassembly.wasm.ValueType;
31
import de.inetsoftware.jwebassembly.wasm.VariableOperator;
32

33
/**
34
 * Replace Unsafe operations with simpler WASM operations which does not need reflections.
35
 * 
36
 * In Java a typical Unsafe code look like:
37
 * 
38
 * <pre>
39
 * <code>
40
 * private static final Unsafe UNSAFE = Unsafe.getUnsafe();
41
 * private static final long FIELD_OFFSET = UNSAFE.objectFieldOffset(Foobar.class.getDeclaredField("field"));
42
 * 
43
 * ...
44
 * 
45
 * UNSAFE.compareAndSwapInt(this, FIELD_OFFSET, expect, update);
46
 * </code>
47
 * </pre>
48
 * 
49
 * Because WASM does not support reflection the native code of UNSAFE can't simple replaced. That this manager convert
50
 * this to the follow pseudo code in WASM:
51
 * 
52
 * <pre>
53
 * <code>
54
 * Foobar..compareAndSwapInt(this, FIELD_OFFSET, expect, update);
55
 * 
56
 * ...
57
 * 
58
 * boolean .compareAndSwapInt(Object obj, long fieldOffset, int expect, int update ) {
59
 *     if( obj.field == expect ) {
60
 *         obj.field = update;
61
 *         return true;
62
 *     }
63
 *     return false;
64
 * }
65
 * </code>
66
 * </pre>
67
 * 
68
 * @author Volker Berlin
69
 */
70
class UnsafeManager {
71

72
    /** Unsafe class bane in Java 8 */
73
    static final String                              UNSAFE_8  = "sun/misc/Unsafe";
74

75
    /** Unsafe class bane in Java 11 */
76
    static final String                              UNSAFE_11 = "jdk/internal/misc/Unsafe";
77

78
    /** VARHANDLE as modern replacement of Unsafe */
79
    static final String                              VARHANDLE = "java/lang/invoke/VarHandle";
80

81
    @Nonnull
82
    private final FunctionManager                    functions;
83

84
    private final HashMap<FunctionName, UnsafeState> unsafes   = new HashMap<>();
1✔
85

86
    /**
87
     * Create an instance of the manager
88
     * 
89
     * @param functions
90
     *            The function manager to register the synthetic functions.
91
     */
92
    UnsafeManager( @Nonnull FunctionManager functions ) {
1✔
93
        this.functions = functions;
1✔
94
    }
1✔
95

96
    /**
97
     * Replace any Unsafe API call with direct field access.
98
     * 
99
     * @param instructions
100
     *            the instruction list of a function/method
101
     */
102
    void replaceUnsafe( List<WasmInstruction> instructions ) {
103
        // search for Unsafe function calls
104
        for( int i = 0; i < instructions.size(); i++ ) {
1✔
105
            WasmInstruction instr = instructions.get( i );
1✔
106
            switch( instr.getType() ) {
1✔
107
                case CallVirtual:
108
                case Call:
109
                    WasmCallInstruction callInst = (WasmCallInstruction)instr;
1✔
110
                    switch( callInst.getFunctionName().className ) {
1✔
111
                        case UNSAFE_8:
112
                        case UNSAFE_11:
113
                            patch( instructions, i, callInst );
1✔
114
                            break;
1✔
115
                        case VARHANDLE:
116
                            patchVarHandle( instructions, i, callInst );
×
117
                            break;
118
                    }
119
                    break;
1✔
120
                default:
121
            }
122
        }
123
    }
1✔
124

125
    /**
126
     * Patch in the instruction list an Unsafe method call. It does not change the count of instructions.
127
     * 
128
     * @param instructions
129
     *            the instruction list of a function/method
130
     * @param idx
131
     *            the index in the instructions
132
     * @param callInst
133
     *            the method call to Unsafe
134
     */
135
    private void patch( List<WasmInstruction> instructions, int idx, WasmCallInstruction callInst ) {
136
        FunctionName name = callInst.getFunctionName();
1✔
137
        switch( name.signatureName ) {
1✔
138
            case "sun/misc/Unsafe.getUnsafe()Lsun/misc/Unsafe;":
139
            case "jdk/internal/misc/Unsafe.getUnsafe()Ljdk/internal/misc/Unsafe;":
140
                patch_getUnsafe( instructions, idx );
1✔
141
                break;
1✔
142
            case "sun/misc/Unsafe.objectFieldOffset(Ljava/lang/reflect/Field;)J":
143
            case "jdk/internal/misc/Unsafe.objectFieldOffset(Ljava/lang/reflect/Field;)J":
144
                patch_objectFieldOffset_Java8( instructions, idx, callInst );
1✔
145
                break;
1✔
146
            case "jdk/internal/misc/Unsafe.objectFieldOffset(Ljava/lang/Class;Ljava/lang/String;)J":
147
                patch_objectFieldOffset_Java11( instructions, idx, callInst );
×
148
                break;
×
149
            case "sun/misc/Unsafe.arrayBaseOffset(Ljava/lang/Class;)I":
150
            case "jdk/internal/misc/Unsafe.arrayBaseOffset(Ljava/lang/Class;)I":
151
                patch_arrayBaseOffset( instructions, idx, callInst );
×
152
                break;
×
153
            case "sun/misc/Unsafe.arrayIndexScale(Ljava/lang/Class;)I":
154
            case "jdk/internal/misc/Unsafe.arrayIndexScale(Ljava/lang/Class;)I":
155
                patch_arrayIndexScale( instructions, idx, callInst );
×
156
                break;
×
157
            case "sun/misc/Unsafe.getObjectVolatile(Ljava/lang/Object;J)Ljava/lang/Object;":
158
            case "sun/misc/Unsafe.getInt(Ljava/lang/Object;J)I":
159
                patchFieldFunction( instructions, idx, callInst, name, 1 );
×
160
                break;
×
161
            case "sun/misc/Unsafe.getAndAddInt(Ljava/lang/Object;JI)I":
162
            case "sun/misc/Unsafe.getAndSetInt(Ljava/lang/Object;JI)I":
163
            case "sun/misc/Unsafe.putOrderedInt(Ljava/lang/Object;JI)V":
164
            case "sun/misc/Unsafe.putInt(Ljava/lang/Object;JI)V":
165
            case "sun/misc/Unsafe.getAndAddLong(Ljava/lang/Object;JJ)J":
166
            case "sun/misc/Unsafe.getAndSetLong(Ljava/lang/Object;JJ)J":
167
            case "sun/misc/Unsafe.putOrderedLong(Ljava/lang/Object;JJ)V":
168
            case "sun/misc/Unsafe.putLong(Ljava/lang/Object;JJ)V":
169
            case "sun/misc/Unsafe.putOrderedObject(Ljava/lang/Object;JLjava/lang/Object;)V":
170
            case "sun/misc/Unsafe.putObjectVolatile(Ljava/lang/Object;JLjava/lang/Object;)V":
171
            case "sun/misc/Unsafe.putObject(Ljava/lang/Object;JLjava/lang/Object;)V":
172
            case "sun/misc/Unsafe.getAndSetObject(Ljava/lang/Object;JLjava/lang/Object;)Ljava/lang/Object;":
173
            case "jdk/internal/misc/Unsafe.getAndAddInt(Ljava/lang/Object;JI)I":
174
            case "jdk/internal/misc/Unsafe.getAndSetInt(Ljava/lang/Object;JI)I":
175
            case "jdk/internal/misc/Unsafe.putIntRelease(Ljava/lang/Object;JI)V":
176
            case "jdk/internal/misc/Unsafe.getAndAddLong(Ljava/lang/Object;JJ)J":
177
            case "jdk/internal/misc/Unsafe.getAndSetLong(Ljava/lang/Object;JJ)J":
178
            case "jdk/internal/misc/Unsafe.putLongRelease(Ljava/lang/Object;JJ)V":
179
            case "jdk/internal/misc/Unsafe.putLongVolatile(Ljava/lang/Object;JJ)V":
180
            case "jdk/internal/misc/Unsafe.putObject(Ljava/lang/Object;JLjava/lang/Object;)V":
181
            case "jdk/internal/misc/Unsafe.getObjectAcquire(Ljava/lang/Object;J)Ljava/lang/Object;":
182
                patchFieldFunction( instructions, idx, callInst, name, 2 );
1✔
183
                break;
1✔
184
            case "sun/misc/Unsafe.compareAndSwapInt(Ljava/lang/Object;JII)Z":
185
            case "sun/misc/Unsafe.compareAndSwapLong(Ljava/lang/Object;JJJ)Z":
186
            case "sun/misc/Unsafe.compareAndSwapObject(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z":
187
            case "jdk/internal/misc/Unsafe.compareAndSetInt(Ljava/lang/Object;JII)Z":
188
            case "jdk/internal/misc/Unsafe.compareAndSetLong(Ljava/lang/Object;JJJ)Z":
189
            case "jdk/internal/misc/Unsafe.compareAndSetObject(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z":
190
                patchFieldFunction( instructions, idx, callInst, name, 3 );
1✔
191
                break;
1✔
192
            case "jdk/internal/misc/Unsafe.getLongUnaligned(Ljava/lang/Object;J)J":
193
            case "jdk/internal/misc/Unsafe.getIntUnaligned(Ljava/lang/Object;J)I":
194
            case "jdk/internal/misc/Unsafe.getCharUnaligned(Ljava/lang/Object;JZ)C":
195
            case "jdk/internal/misc/Unsafe.getIntUnaligned(Ljava/lang/Object;JZ)I":
196
                patch_getLongUnaligned( instructions, idx, callInst, name );
×
197
                break;
×
198
            case "jdk/internal/misc/Unsafe.isBigEndian()Z":
199
                patch_isBigEndian( instructions, idx, callInst );
×
200
                break;
×
201
            case "jdk/internal/misc/Unsafe.shouldBeInitialized(Ljava/lang/Class;)Z":
202
                replaceWithConstNumber( instructions, idx, callInst, 2, 0 );
×
203
                break;
×
204
            case "jdk/internal/misc/Unsafe.storeFence()V":
205
                remove( instructions, idx, callInst, 1 );
×
206
                break;
×
207
            case "jdk/internal/misc/Unsafe.ensureClassInitialized(Ljava/lang/Class;)V":
208
            case "jdk/internal/misc/Unsafe.unpark(Ljava/lang/Object;)V":
209
            case "sun/misc/Unsafe.unpark(Ljava/lang/Object;)V":
210
                remove( instructions, idx, callInst, 2 );
×
211
                break;
×
212
            case "sun/misc/Unsafe.park(ZJ)V":
213
            case "jdk/internal/misc/Unsafe.park(ZJ)V":
214
                remove( instructions, idx, callInst, 3 );
×
215
                break;
×
216
            default:
217
                throw new WasmException( "Unsupported Unsafe method: " + name.signatureName, -1 );
×
218
        }
219
    }
1✔
220

221
    /**
222
     * Replace a call to Unsafe.getUnsafe() with a NOP operation.
223
     * 
224
     * @param instructions
225
     *            the instruction list of a function/method
226
     * @param idx
227
     *            the index in the instructions
228
     */
229
    private void patch_getUnsafe( List<WasmInstruction> instructions, int idx ) {
230
        WasmInstruction instr = instructions.get( idx + 1 );
1✔
231

232
        int to = idx + (instr.getType() == Type.Global ? 2 : 1);
1✔
233

234
        nop( instructions, idx, to );
1✔
235
    }
1✔
236

237
    /**
238
     * Find the field on which the offset is assign: long FIELD_OFFSET = UNSAFE.objectFieldOffset(...
239
     * 
240
     * @param instructions
241
     *            the instruction list of a function/method
242
     * @param idx
243
     *            the index in the instructions
244
     * @return the state
245
     */
246
    @Nonnull
247
    private UnsafeState findUnsafeState( List<WasmInstruction> instructions, int idx ) {
248
        // find the field on which the offset is assign: long FIELD_OFFSET = UNSAFE.objectFieldOffset(...
249
        WasmInstruction instr;
250
        idx++;
1✔
251
        INSTR: do {
252
            instr = instructions.get( idx );
1✔
253
            switch( instr.getType() ) {
1✔
254
                case Convert:
255
                    idx++;
×
256
                    continue INSTR;
×
257
                case Global:
258
                    break;
1✔
259
                case Jump:
260
                    int pos = ((JumpInstruction)instr).getJumpPosition();
×
261
                    for( idx++; idx < instructions.size(); idx++) {
×
262
                        instr = instructions.get( idx );
×
263
                        if( instr.getCodePosition() >= pos ) {
×
264
                            break;
×
265
                        }
266
                    }
267
                    continue INSTR;
268
                default:
269
                    throw new WasmException( "Unsupported assign operation for Unsafe field offset: " + instr.getType(), -1 );
×
270
            }
271
            break;
272
        } while( true );
273
        FunctionName fieldNameWithOffset = ((WasmGlobalInstruction)instr).getFieldName();
1✔
274
        UnsafeState state = unsafes.get( fieldNameWithOffset );
1✔
275
        if( state == null ) {
1✔
276
            unsafes.put( fieldNameWithOffset, state = new UnsafeState() );
1✔
277
        }
278
        return state;
1✔
279
    }
280

281
    /**
282
     * Get the class name from the stack value. It is searching a WasmConstClassInstruction that produce the value of
283
     * the stack value.
284
     * 
285
     * @param instructions
286
     *            the instruction list of a function/method
287
     * @param stackValue
288
     *            the stack value (instruction and position that produce an stack value)
289
     * @return the class name like: java/lang/String
290
     */
291
    @Nonnull
292
    private static String getClassConst( List<WasmInstruction> instructions, StackValue stackValue ) {
293
        WasmInstruction instr = stackValue.instr;
1✔
294
        switch( instr.getType() ) {
1✔
295
            case Local:
296
                int slot = ((WasmLocalInstruction)instr).getSlot();
×
297
                for( int i = stackValue.idx - 1; i >= 0; i-- ) {
×
298
                    instr = instructions.get( i );
×
299
                    if( instr.getType() == Type.Local ) {
×
300
                        WasmLocalInstruction loadInstr = (WasmLocalInstruction)instr;
×
301
                        if( loadInstr.getSlot() == slot && loadInstr.getOperator() == VariableOperator.set ) {
×
302
                            stackValue = StackInspector.findInstructionThatPushValue( instructions.subList( 0, i ), 1, instr.getCodePosition() );
×
303
                            instr = stackValue.instr;
×
304
                            break;
×
305
                        }
306

307
                    }
308
                }
309
                break;
×
310
            default:
311
        }
312
        return ((WasmConstClassInstruction)instr).getValue();
1✔
313
    }
314

315
    /**
316
     * Patch a method call to Unsafe.objectFieldOffset() and find the parameter for other patch operations.
317
     * 
318
     * @param instructions
319
     *            the instruction list
320
     * @param idx
321
     *            the index in the instructions
322
     * @param callInst
323
     *            the method call to Unsafe
324
     */
325
    private void patch_objectFieldOffset_Java8( List<WasmInstruction> instructions, int idx, WasmCallInstruction callInst ) {
326
        UnsafeState state = findUnsafeState( instructions, idx );
1✔
327

328
        // objectFieldOffset() has 2 parameters THIS(Unsafe) and a Field
329
        List<WasmInstruction> paramInstructions = instructions.subList( 0, idx );
1✔
330
        int from = StackInspector.findInstructionThatPushValue( paramInstructions, 2, callInst.getCodePosition() ).idx;
1✔
331

332
        StackValue stackValue = StackInspector.findInstructionThatPushValue( paramInstructions, 1, callInst.getCodePosition() );
1✔
333
        WasmInstruction instr = stackValue.instr;
1✔
334
        WasmCallInstruction fieldInst = (WasmCallInstruction)instr;
1✔
335

336
        FunctionName fieldFuncName = fieldInst.getFunctionName();
1✔
337
        switch( fieldFuncName.signatureName ) {
1✔
338
            case "java/lang/Class.getDeclaredField(Ljava/lang/String;)Ljava/lang/reflect/Field;":
339
                stackValue = StackInspector.findInstructionThatPushValue( instructions.subList( 0, stackValue.idx ), 1, fieldInst.getCodePosition() );
1✔
340
                state.fieldName = ((WasmConstStringInstruction)stackValue.instr).getValue();
1✔
341

342
                // find the class value on which getDeclaredField is called
343
                stackValue = StackInspector.findInstructionThatPushValue( instructions.subList( 0, stackValue.idx ), 1, fieldInst.getCodePosition() );
1✔
344
                state.typeName = getClassConst( instructions, stackValue );
1✔
345
                break;
1✔
346

347
            default:
348
                throw new WasmException( "Unsupported Unsafe method to get target field: " + fieldFuncName.signatureName, -1 );
×
349
        }
350

351
        nop( instructions, from, idx + 2 );
1✔
352
    }
1✔
353

354
    /**
355
     * Patch a method call to Unsafe.objectFieldOffset() and find the parameter for other patch operations.
356
     * 
357
     * @param instructions
358
     *            the instruction list
359
     * @param idx
360
     *            the index in the instructions
361
     * @param callInst
362
     *            the method call to Unsafe
363
     */
364
    private void patch_objectFieldOffset_Java11( List<WasmInstruction> instructions, int idx, WasmCallInstruction callInst ) {
365
        UnsafeState state = findUnsafeState( instructions, idx );
×
366

367
        // objectFieldOffset() has 3 parameters THIS(Unsafe), class and the fieldname
368
        List<WasmInstruction> paramInstructions = instructions.subList( 0, idx );
×
369
        int from = StackInspector.findInstructionThatPushValue( paramInstructions, 3, callInst.getCodePosition() ).idx;
×
370

371
        StackValue stackValue = StackInspector.findInstructionThatPushValue( paramInstructions, 1, callInst.getCodePosition() );
×
372
        state.fieldName = ((WasmConstStringInstruction)stackValue.instr).getValue();
×
373

374
        // find the class value on which getDeclaredField is called
375
        stackValue = StackInspector.findInstructionThatPushValue( paramInstructions, 2, callInst.getCodePosition() );
×
376
        state.typeName = getClassConst( instructions, stackValue );
×
377

378
        nop( instructions, from, idx + 2 );
×
379
    }
×
380

381
    /**
382
     * Patch a method call to Unsafe.arrayBaseOffset() and find the parameter for other patch operations.
383
     * 
384
     * @param instructions
385
     *            the instruction list
386
     * @param idx
387
     *            the index in the instructions
388
     * @param callInst
389
     *            the method call to Unsafe
390
     */
391
    private void patch_arrayBaseOffset( List<WasmInstruction> instructions, int idx, WasmCallInstruction callInst ) {
392
        UnsafeState state = findUnsafeState( instructions, idx );
×
393

394
        // objectFieldOffset() has 2 parameters THIS(Unsafe) and a Class from an array
395
        List<WasmInstruction> paramInstructions = instructions.subList( 0, idx );
×
396
        int from = StackInspector.findInstructionThatPushValue( paramInstructions, 2, callInst.getCodePosition() ).idx;
×
397

398
        StackValue stackValue = StackInspector.findInstructionThatPushValue( paramInstructions, 1, callInst.getCodePosition() );
×
399
        state.typeName = getClassConst( instructions, stackValue );
×
400

401
        nop( instructions, from, idx );
×
402
        // we put the constant value 0 on the stack, we does not need array base offset in WASM
403
        instructions.set( idx, new WasmConstNumberInstruction( 0, callInst.getCodePosition(), callInst.getLineNumber() ) );
×
404
    }
×
405

406
    /**
407
     * Patch method call to Unsafe.arrayIndexScale()
408
     * 
409
     * @param instructions
410
     *            the instruction list
411
     * @param idx
412
     *            the index in the instructions
413
     * @param callInst
414
     *            the method call to Unsafe
415
     */
416
    private void patch_arrayIndexScale( List<WasmInstruction> instructions, int idx, WasmCallInstruction callInst ) {
417
        int from = StackInspector.findInstructionThatPushValue( instructions.subList( 0, idx ), 2, callInst.getCodePosition() ).idx;
×
418

419
        nop( instructions, from, idx );
×
420
        // we put the constant value 1 on the stack because we does not want shift array positions
421
        instructions.set( idx, new WasmConstNumberInstruction( 1, callInst.getCodePosition(), callInst.getLineNumber() ) );
×
422
    }
×
423

424
    /**
425
     * Patch an unsafe function that access a field
426
     * 
427
     * @param instructions
428
     *            the instruction list
429
     * @param idx
430
     *            the index in the instructions
431
     * @param callInst
432
     *            the method call to Unsafe
433
     * @param name
434
     *            the calling function
435
     * @param fieldNameParam
436
     *            the function parameter on the stack with the field offset on the stack. This must be a long (Java signature "J") for Unsafe. This is the parameter count from right.
437
     */
438
    private void patchFieldFunction( List<WasmInstruction> instructions, int idx, final WasmCallInstruction callInst, FunctionName name, int fieldNameParam ) {
439
        StackValue stackValue = StackInspector.findInstructionThatPushValue( instructions.subList( 0, idx ), fieldNameParam, callInst.getCodePosition() );
1✔
440
        WasmInstruction instr = stackValue.instr;
1✔
441

442
        Set<FunctionName> fieldNames;
443
        FunctionName fieldNameWithOffset = null;
1✔
444
        if( instr.getType() == Type.Global ) {
1✔
445
            fieldNameWithOffset = ((WasmGlobalInstruction)instr).getFieldName();
1✔
446
            fieldNames = Collections.singleton( fieldNameWithOffset );
1✔
447
        } else {
448
            // java.util.concurrent.ConcurrentHashMap.tabAt() calculate a value with the field
449
            fieldNames = new HashSet<>();
×
450
            int pos2 = stackValue.idx;
×
451
            stackValue = StackInspector.findInstructionThatPushValue( instructions.subList( 0, idx ), fieldNameParam + 1, callInst.getCodePosition() );
×
452
            int i = stackValue.idx;
×
453
            for( ; i < pos2; i++ ) {
×
454
                instr = instructions.get( i );
×
455
                if( instr.getType() != Type.Global ) {
×
456
                    continue;
×
457
                }
458
                fieldNameWithOffset = ((WasmGlobalInstruction)instr).getFieldName();
×
459
                fieldNames.add( fieldNameWithOffset );
×
460
            }
461
        }
462

463
        WatCodeSyntheticFunctionName func =
1✔
464
                        new WatCodeSyntheticFunctionName( fieldNameWithOffset.className, '.' + name.methodName, name.signature, "", (AnyType[])null ) {
1✔
465
                            @Override
466
                            protected String getCode() {
467
                                UnsafeState state = null;
1✔
468
                                for(FunctionName fieldNameWithOffset : fieldNames ) {
1✔
469
                                    state = unsafes.get( fieldNameWithOffset );
1✔
470
                                    if( state != null ) {
1✔
471
                                        break;
1✔
472
                                    }
473
                                }
1✔
474
                                if( state == null ) {
1✔
475
                                    if( functions.isFinish() ) {
1✔
476
                                        throw new RuntimeException( name.signatureName );
×
477
                                    }
478
                                    // we are in the scan phase. The static code was not scanned yet.
479
                                    return "";
1✔
480
                                }
481
                                AnyType[] paramTypes = callInst.getPopValueTypes();
1✔
482
                                switch( name.methodName ) {
1✔
483
                                    case "compareAndSwapInt":
484
                                    case "compareAndSwapLong":
485
                                    case "compareAndSwapObject":
486
                                        AnyType type = paramTypes[3];
1✔
487
                                        if( type.isRefType() ) {
1✔
488
                                            type = ValueType.ref;
1✔
489
                                        }
490
                                        return "local.get 0" // THIS
1✔
491
                                                        + " struct.get " + state.typeName + ' ' + state.fieldName //
492
                                                        + " local.get 2 " // expected
493
                                                        + type + ".eq" //
494
                                                        + " if" //
495
                                                        + "   local.get 0" // THIS
496
                                                        + "   local.get 3" // update
497
                                                        + "   struct.set " + state.typeName + ' ' + state.fieldName //
498
                                                        + "   i32.const 1" //
499
                                                        + "   return" //
500
                                                        + " end" //
501
                                                        + " i32.const 1" //
502
                                                        + " return";
503

504
                                    case "getAndAddInt":
505
                                    case "getAndAddLong":
506
                                        return "local.get 0" // THIS
1✔
507
                                                        + " local.get 0" // THIS
508
                                                        + " struct.get " + state.typeName + ' ' + state.fieldName //
509
                                                        + " local.tee 3" // temp
510
                                                        + " local.get 2 " // delta
511
                                                        + paramTypes[3] + ".add" //
512
                                                        + " struct.set " + state.typeName + ' ' + state.fieldName //
513
                                                        + " local.get 3" // temp
514
                                                        + " return";
515

516
                                    case "getAndSetInt":
517
                                    case "getAndSetLong":
518
                                    case "getAndSetObject":
519
                                        return "local.get 0" // THIS
1✔
520
                                                        + " struct.get " + state.typeName + ' ' + state.fieldName //
521
                                                        + " local.get 0" // THIS
522
                                                        + " local.get 2" // newValue
523
                                                        + " struct.set " + state.typeName + ' ' + state.fieldName //
524
                                                        + " return";
525

526
                                    case "putOrderedInt":
527
                                    case "putInt":
528
                                    case "putOrderedLong":
529
                                    case "putLong":
530
                                    case "putOrderedObject":
531
                                    case "putObjectVolatile":
532
                                    case "putObject":
533
                                        return "local.get 0" // THIS
1✔
534
                                                        + " local.get 2" // x
535
                                                        + " struct.set " + state.typeName + ' ' + state.fieldName;
536
                                }
537

538
                                throw new RuntimeException( name.signatureName );
×
539
                            }
540
                        };
541
        functions.markAsNeeded( func, false );
1✔
542
        WasmCallInstruction call = new WasmCallInstruction( func, callInst.getCodePosition(), callInst.getLineNumber(), callInst.getTypeManager(), false );
1✔
543
        instructions.set( idx, call );
1✔
544

545
        // a virtual method call has also a DUP of this because we need for virtual method dispatch the parameter 2 times.
546
        for( int i = idx; i >= 0; i-- ) {
1✔
547
            instr = instructions.get( i );
1✔
548
            if( instr.getType() == Type.DupThis && ((DupThis)instr).getValue() == callInst ) {
1✔
549
                nop( instructions, i, i + 1 );
1✔
550
                break;
1✔
551
            }
552
        }
553
    }
1✔
554

555
    /**
556
     * Patch an unsafe function that access a field
557
     * 
558
     * @param instructions
559
     *            the instruction list
560
     * @param idx
561
     *            the index in the instructions
562
     * @param callInst
563
     *            the method call to Unsafe
564
     */
565
    private void patch_getLongUnaligned( List<WasmInstruction> instructions, int idx, final WasmCallInstruction callInst, FunctionName name ) {
566
        WatCodeSyntheticFunctionName func = new WatCodeSyntheticFunctionName( "", name.methodName, name.signature, "unreachable", (AnyType[])null );
×
567
        functions.markAsNeeded( func, false );
×
568
        WasmCallInstruction call = new WasmCallInstruction( func, callInst.getCodePosition(), callInst.getLineNumber(), callInst.getTypeManager(), false );
×
569
        instructions.set( idx, call );
×
570
    }
×
571

572
    /**
573
     * Patch an unsafe function that access a field
574
     * 
575
     * @param instructions
576
     *            the instruction list
577
     * @param idx
578
     *            the index in the instructions
579
     * @param callInst
580
     *            the method call to Unsafe
581
     */
582
    private void patch_isBigEndian( List<WasmInstruction> instructions, int idx, final WasmCallInstruction callInst ) {
583
//        int from = StackInspector.findInstructionThatPushValue( instructions.subList( 0, idx ), 1, callInst.getCodePosition() ).idx;
584
//
585
//        nop( instructions, from, idx );
586

587
        // on x86 use little endian
588
        instructions.set( idx, new WasmConstNumberInstruction( 0, callInst.getCodePosition(), callInst.getLineNumber() ) );
×
589
    }
×
590

591
    /**
592
     * Patch an unsafe function that access a field
593
     * 
594
     * @param instructions
595
     *            the instruction list
596
     * @param idx
597
     *            the index in the instructions
598
     * @param callInst
599
     *            the method call to Unsafe
600
     */
601
    private void replaceWithConstNumber( List<WasmInstruction> instructions, int idx, final WasmCallInstruction callInst, int paramCount, int number ) {
602
        int from = StackInspector.findInstructionThatPushValue( instructions.subList( 0, idx ), 1, callInst.getCodePosition() ).idx;
×
603

604
        nop( instructions, from, idx );
×
605

606
        // on x86 use little endian
607
        instructions.set( idx, new WasmConstNumberInstruction( number, callInst.getCodePosition(), callInst.getLineNumber() ) );
×
608
    }
×
609

610
    /**
611
     * Patch an unsafe function that access a field
612
     * 
613
     * @param instructions
614
     *            the instruction list
615
     * @param idx
616
     *            the index in the instructions
617
     * @param callInst
618
     *            the method call to Unsafe
619
     */
620
    private void remove( List<WasmInstruction> instructions, int idx, final WasmCallInstruction callInst, int paramCount ) {
621
        int from = StackInspector.findInstructionThatPushValue( instructions.subList( 0, idx ), paramCount, callInst.getCodePosition() ).idx;
×
622

623
        nop( instructions, from, idx + 1 );
×
624
    }
×
625

626
    /**
627
     * Replace the instructions with NOP operations
628
     * 
629
     * @param instructions
630
     *            the instruction list
631
     * @param from
632
     *            starting index
633
     * @param to
634
     *            end index
635
     */
636
    private void nop( List<WasmInstruction> instructions, int from, int to ) {
637
        for( int i = from; i < to; i++ ) {
1✔
638
            WasmInstruction instr = instructions.get( i );
1✔
639
            instructions.set( i, new WasmNopInstruction( instr.getCodePosition(), instr.getLineNumber() ) );
1✔
640
        }
641
    }
1✔
642

643
    private void patchVarHandle( List<WasmInstruction> instructions, int idx, WasmCallInstruction callInst ) {
644
        FunctionName name = callInst.getFunctionName();
×
645
        switch( name.methodName ) {
×
646
            case "getAndSet":
647
                patchVarHandleFieldFunction( instructions, idx, callInst, name, 3 );
×
648
                break;
649
        }
650
    }
×
651

652
    /**
653
     * Patch an unsafe function that access a field
654
     * 
655
     * @param instructions
656
     *            the instruction list
657
     * @param idx
658
     *            the index in the instructions
659
     * @param callInst
660
     *            the method call to Unsafe
661
     * @param name
662
     *            the calling function
663
     * @param fieldNameParam
664
     *            the function parameter with the field offset on the stack. This must be a long (Java signature "J") for Unsafe.
665
     */
666
    private void patchVarHandleFieldFunction( List<WasmInstruction> instructions, int idx, final WasmCallInstruction callInst, FunctionName name, int fieldNameParam ) {
667
        StackValue stackValue = StackInspector.findInstructionThatPushValue( instructions.subList( 0, idx ), fieldNameParam, callInst.getCodePosition() );
×
668
        FunctionName fieldNameWithOffset = ((WasmGlobalInstruction)stackValue.instr).getFieldName();
×
669
        WatCodeSyntheticFunctionName func =
×
670
                        new WatCodeSyntheticFunctionName( fieldNameWithOffset.className, '.' + name.methodName, name.signature, "", (AnyType[])null ) {
×
671
                            @Override
672
                            protected String getCode() {
673
                                UnsafeState state = unsafes.get( fieldNameWithOffset );
×
674
                                if( state == null ) {
×
675
                                    // we are in the scan phase. The static code was not scanned yet.
676
                                    return "";
×
677
                                }
678
                                AnyType[] paramTypes = callInst.getPopValueTypes();
×
679
                                switch( name.methodName ) {
×
680
                                    case "getAndSet":
681
                                        return "local.get 1" // THIS
×
682
                                                        + " struct.get " + state.typeName + ' ' + state.fieldName //
683
                                                        + " local.get 1" // THIS
684
                                                        + " local.get 2" // newValue
685
                                                        + " struct.set " + state.typeName + ' ' + state.fieldName //
686
                                                        + " return";
687

688
                                }
689

690
                                throw new RuntimeException( name.signatureName );
×
691
                            }
692
                        };
693
        functions.markAsNeeded( func, false );
×
694
        WasmCallInstruction call = new WasmCallInstruction( func, callInst.getCodePosition(), callInst.getLineNumber(), callInst.getTypeManager(), false );
×
695
        instructions.set( idx, call );
×
696

697
        // a virtual method call has also a DUP of this because we need for virtual method dispatch the parameter 2 times.
698
        for( int i = idx; i >= 0; i-- ) {
×
699
            WasmInstruction instr = instructions.get( i );
×
700
            if( instr.getType() == Type.DupThis && ((DupThis)instr).getValue() == callInst ) {
×
701
                nop( instructions, i, i + 1 );
×
702
                break;
×
703
            }
704
        }
705
    }
×
706

707
    /**
708
     * Hold the state from declaring of Unsafe address
709
     */
710
    static class UnsafeState {
1✔
711
        String fieldName;
712

713
        String typeName;
714
    }
715
}
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