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

i-net-software / JWebAssembly / 524

pending completion
524

push

travis-ci-com

Horcrux7
AtomicReferenceFieldUpdater is another alternative for Unsafe

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

5870 of 6752 relevant lines covered (86.94%)

0.87 hits per line

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

44.81
/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
    /** Wrapper for Unsafe */
79
    static final String                              FIELDUPDATER = "java/util/concurrent/atomic/AtomicReferenceFieldUpdater";
80

81
    /** VARHANDLE as modern replacement of Unsafe */
82
    static final String                              VARHANDLE = "java/lang/invoke/VarHandle";
83

84
    @Nonnull
85
    private final FunctionManager                    functions;
86

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

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

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

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

232
    /**
233
     * Replace a call to Unsafe.getUnsafe() with a NOP operation.
234
     * 
235
     * @param instructions
236
     *            the instruction list of a function/method
237
     * @param idx
238
     *            the index in the instructions
239
     */
240
    private void patch_getUnsafe( List<WasmInstruction> instructions, int idx ) {
241
        WasmInstruction instr = instructions.get( idx + 1 );
1✔
242

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

245
        nop( instructions, idx, to );
1✔
246
    }
1✔
247

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

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

318
                    }
319
                }
320
                break;
×
321
            default:
322
        }
323
        return ((WasmConstClassInstruction)instr).getValue();
1✔
324
    }
325

326
    /**
327
     * Patch a method call to Unsafe.objectFieldOffset() and find the parameter for other patch operations.
328
     * 
329
     * @param instructions
330
     *            the instruction list
331
     * @param idx
332
     *            the index in the instructions
333
     * @param callInst
334
     *            the method call to Unsafe
335
     */
336
    private void patch_objectFieldOffset_Java8( List<WasmInstruction> instructions, int idx, WasmCallInstruction callInst ) {
337
        UnsafeState state = findUnsafeState( instructions, idx );
1✔
338

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

343
        StackValue stackValue = StackInspector.findInstructionThatPushValue( paramInstructions, 1, callInst.getCodePosition() );
1✔
344
        WasmInstruction instr = stackValue.instr;
1✔
345
        WasmCallInstruction fieldInst = (WasmCallInstruction)instr;
1✔
346

347
        FunctionName fieldFuncName = fieldInst.getFunctionName();
1✔
348
        switch( fieldFuncName.signatureName ) {
1✔
349
            case "java/lang/Class.getDeclaredField(Ljava/lang/String;)Ljava/lang/reflect/Field;":
350
                stackValue = StackInspector.findInstructionThatPushValue( instructions.subList( 0, stackValue.idx ), 1, fieldInst.getCodePosition() );
1✔
351
                state.fieldName = ((WasmConstStringInstruction)stackValue.instr).getValue();
1✔
352

353
                // find the class value on which getDeclaredField is called
354
                stackValue = StackInspector.findInstructionThatPushValue( instructions.subList( 0, stackValue.idx ), 1, fieldInst.getCodePosition() );
1✔
355
                state.typeName = getClassConst( instructions, stackValue );
1✔
356
                break;
1✔
357

358
            default:
359
                throw new WasmException( "Unsupported Unsafe method to get target field: " + fieldFuncName.signatureName, -1 );
×
360
        }
361

362
        nop( instructions, from, idx + 2 );
1✔
363
    }
1✔
364

365
    /**
366
     * Patch a method call to Unsafe.objectFieldOffset() and find the parameter for other patch operations.
367
     * 
368
     * @param instructions
369
     *            the instruction list
370
     * @param idx
371
     *            the index in the instructions
372
     * @param callInst
373
     *            the method call to Unsafe
374
     * @param isAtomicReferenceFieldUpdater
375
     *            true, if is AtomicReferenceFieldUpdater
376
     */
377
    private void patch_objectFieldOffset_Java11( List<WasmInstruction> instructions, int idx, WasmCallInstruction callInst, boolean isAtomicReferenceFieldUpdater ) {
378
        UnsafeState state = findUnsafeState( instructions, idx );
×
379

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

384
        StackValue stackValue = StackInspector.findInstructionThatPushValue( paramInstructions, 1, callInst.getCodePosition() );
×
385
        state.fieldName = ((WasmConstStringInstruction)stackValue.instr).getValue();
×
386

387
        // find the class value on which getDeclaredField is called
388
        int classParamIdx = isAtomicReferenceFieldUpdater ? 3 : 2;
×
389
        stackValue = StackInspector.findInstructionThatPushValue( paramInstructions, classParamIdx, callInst.getCodePosition() );
×
390
        state.typeName = getClassConst( instructions, stackValue );
×
391

392
        nop( instructions, from, idx + 2 );
×
393
    }
×
394

395
    /**
396
     * Patch a method call to Unsafe.arrayBaseOffset() and find the parameter for other patch operations.
397
     * 
398
     * @param instructions
399
     *            the instruction list
400
     * @param idx
401
     *            the index in the instructions
402
     * @param callInst
403
     *            the method call to Unsafe
404
     */
405
    private void patch_arrayBaseOffset( List<WasmInstruction> instructions, int idx, WasmCallInstruction callInst ) {
406
        UnsafeState state = findUnsafeState( instructions, idx );
×
407

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

412
        StackValue stackValue = StackInspector.findInstructionThatPushValue( paramInstructions, 1, callInst.getCodePosition() );
×
413
        state.typeName = getClassConst( instructions, stackValue );
×
414

415
        nop( instructions, from, idx );
×
416
        // we put the constant value 0 on the stack, we does not need array base offset in WASM
417
        instructions.set( idx, new WasmConstNumberInstruction( 0, callInst.getCodePosition(), callInst.getLineNumber() ) );
×
418
    }
×
419

420
    /**
421
     * Patch method call to Unsafe.arrayIndexScale()
422
     * 
423
     * @param instructions
424
     *            the instruction list
425
     * @param idx
426
     *            the index in the instructions
427
     * @param callInst
428
     *            the method call to Unsafe
429
     */
430
    private void patch_arrayIndexScale( List<WasmInstruction> instructions, int idx, WasmCallInstruction callInst ) {
431
        int from = StackInspector.findInstructionThatPushValue( instructions.subList( 0, idx ), 2, callInst.getCodePosition() ).idx;
×
432

433
        nop( instructions, from, idx );
×
434
        // we put the constant value 1 on the stack because we does not want shift array positions
435
        instructions.set( idx, new WasmConstNumberInstruction( 1, callInst.getCodePosition(), callInst.getLineNumber() ) );
×
436
    }
×
437

438
    /**
439
     * Patch an unsafe function that access a field
440
     * 
441
     * @param instructions
442
     *            the instruction list
443
     * @param idx
444
     *            the index in the instructions
445
     * @param callInst
446
     *            the method call to Unsafe
447
     * @param name
448
     *            the calling function
449
     * @param fieldNameParam
450
     *            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.
451
     */
452
    private void patchFieldFunction( List<WasmInstruction> instructions, int idx, final WasmCallInstruction callInst, FunctionName name, int fieldNameParam ) {
453
        StackValue stackValue = StackInspector.findInstructionThatPushValue( instructions.subList( 0, idx ), fieldNameParam, callInst.getCodePosition() );
1✔
454
        WasmInstruction instr = stackValue.instr;
1✔
455

456
        Set<FunctionName> fieldNames;
457
        FunctionName fieldNameWithOffset = null;
1✔
458
        if( instr.getType() == Type.Global ) {
1✔
459
            fieldNameWithOffset = ((WasmGlobalInstruction)instr).getFieldName();
1✔
460
            fieldNames = Collections.singleton( fieldNameWithOffset );
1✔
461
        } else {
462
            // java.util.concurrent.ConcurrentHashMap.tabAt() calculate a value with the field
463
            fieldNames = new HashSet<>();
×
464
            int pos2 = stackValue.idx;
×
465
            stackValue = StackInspector.findInstructionThatPushValue( instructions.subList( 0, idx ), fieldNameParam + 1, callInst.getCodePosition() );
×
466
            int i = stackValue.idx;
×
467
            for( ; i < pos2; i++ ) {
×
468
                instr = instructions.get( i );
×
469
                if( instr.getType() != Type.Global ) {
×
470
                    continue;
×
471
                }
472
                fieldNameWithOffset = ((WasmGlobalInstruction)instr).getFieldName();
×
473
                fieldNames.add( fieldNameWithOffset );
×
474
            }
475
        }
476

477
        WatCodeSyntheticFunctionName func =
1✔
478
                        new WatCodeSyntheticFunctionName( fieldNameWithOffset.className, '.' + name.methodName, name.signature, "", (AnyType[])null ) {
1✔
479
                            @Override
480
                            protected String getCode() {
481
                                UnsafeState state = null;
1✔
482
                                for(FunctionName fieldNameWithOffset : fieldNames ) {
1✔
483
                                    state = unsafes.get( fieldNameWithOffset );
1✔
484
                                    if( state != null ) {
1✔
485
                                        break;
1✔
486
                                    }
487
                                }
1✔
488
                                if( state == null ) {
1✔
489
                                    if( functions.isFinish() ) {
1✔
490
                                        throw new RuntimeException( name.signatureName );
×
491
                                    }
492
                                    // we are in the scan phase. The static code was not scanned yet.
493
                                    return "";
1✔
494
                                }
495
                                AnyType[] paramTypes = callInst.getPopValueTypes();
1✔
496
                                switch( name.methodName ) {
1✔
497
                                    case "compareAndSwapInt":
498
                                    case "compareAndSwapLong":
499
                                    case "compareAndSwapObject":
500
                                    case "compareAndSet": // AtomicReferenceFieldUpdater
501
                                        AnyType type = paramTypes[3];
1✔
502
                                        if( type.isRefType() ) {
1✔
503
                                            type = ValueType.ref;
1✔
504
                                        }
505
                                        return "local.get 0" // THIS
1✔
506
                                                        + " struct.get " + state.typeName + ' ' + state.fieldName //
507
                                                        + " local.get 2 " // expected
508
                                                        + type + ".eq" //
509
                                                        + " if" //
510
                                                        + "   local.get 0" // THIS
511
                                                        + "   local.get 3" // update
512
                                                        + "   struct.set " + state.typeName + ' ' + state.fieldName //
513
                                                        + "   i32.const 1" //
514
                                                        + "   return" //
515
                                                        + " end" //
516
                                                        + " i32.const 1" //
517
                                                        + " return";
518

519
                                    case "getAndAddInt":
520
                                    case "getAndAddLong":
521
                                        return "local.get 0" // THIS
1✔
522
                                                        + " local.get 0" // THIS
523
                                                        + " struct.get " + state.typeName + ' ' + state.fieldName //
524
                                                        + " local.tee 3" // temp
525
                                                        + " local.get 2 " // delta
526
                                                        + paramTypes[3] + ".add" //
527
                                                        + " struct.set " + state.typeName + ' ' + state.fieldName //
528
                                                        + " local.get 3" // temp
529
                                                        + " return";
530

531
                                    case "getAndSetInt":
532
                                    case "getAndSetLong":
533
                                    case "getAndSetObject":
534
                                        return "local.get 0" // THIS
1✔
535
                                                        + " struct.get " + state.typeName + ' ' + state.fieldName //
536
                                                        + " local.get 0" // THIS
537
                                                        + " local.get 2" // newValue
538
                                                        + " struct.set " + state.typeName + ' ' + state.fieldName //
539
                                                        + " return";
540

541
                                    case "putOrderedInt":
542
                                    case "putInt":
543
                                    case "putOrderedLong":
544
                                    case "putLong":
545
                                    case "putOrderedObject":
546
                                    case "putObjectVolatile":
547
                                    case "putObject":
548
                                        return "local.get 0" // THIS
1✔
549
                                                        + " local.get 2" // x
550
                                                        + " struct.set " + state.typeName + ' ' + state.fieldName;
551
                                }
552

553
                                throw new RuntimeException( name.signatureName );
×
554
                            }
555
                        };
556
        functions.markAsNeeded( func, false );
1✔
557
        WasmCallInstruction call = new WasmCallInstruction( func, callInst.getCodePosition(), callInst.getLineNumber(), callInst.getTypeManager(), false );
1✔
558
        instructions.set( idx, call );
1✔
559

560
        // a virtual method call has also a DUP of this because we need for virtual method dispatch the parameter 2 times.
561
        for( int i = idx; i >= 0; i-- ) {
1✔
562
            instr = instructions.get( i );
1✔
563
            if( instr.getType() == Type.DupThis && ((DupThis)instr).getValue() == callInst ) {
1✔
564
                nop( instructions, i, i + 1 );
1✔
565
                break;
1✔
566
            }
567
        }
568
    }
1✔
569

570
    /**
571
     * Patch an unsafe function that access a field
572
     * 
573
     * @param instructions
574
     *            the instruction list
575
     * @param idx
576
     *            the index in the instructions
577
     * @param callInst
578
     *            the method call to Unsafe
579
     */
580
    private void patch_getLongUnaligned( List<WasmInstruction> instructions, int idx, final WasmCallInstruction callInst, FunctionName name ) {
581
        WatCodeSyntheticFunctionName func = new WatCodeSyntheticFunctionName( "", name.methodName, name.signature, "unreachable", (AnyType[])null );
×
582
        functions.markAsNeeded( func, false );
×
583
        WasmCallInstruction call = new WasmCallInstruction( func, callInst.getCodePosition(), callInst.getLineNumber(), callInst.getTypeManager(), false );
×
584
        instructions.set( idx, call );
×
585
    }
×
586

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

602
        // on x86 use little endian
603
        instructions.set( idx, new WasmConstNumberInstruction( 0, callInst.getCodePosition(), callInst.getLineNumber() ) );
×
604
    }
×
605

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

619
        nop( instructions, from, idx );
×
620

621
        // on x86 use little endian
622
        instructions.set( idx, new WasmConstNumberInstruction( number, callInst.getCodePosition(), callInst.getLineNumber() ) );
×
623
    }
×
624

625
    /**
626
     * Patch an unsafe function that access a field
627
     * 
628
     * @param instructions
629
     *            the instruction list
630
     * @param idx
631
     *            the index in the instructions
632
     * @param callInst
633
     *            the method call to Unsafe
634
     */
635
    private void remove( List<WasmInstruction> instructions, int idx, final WasmCallInstruction callInst, int paramCount ) {
636
        int from = StackInspector.findInstructionThatPushValue( instructions.subList( 0, idx ), paramCount, callInst.getCodePosition() ).idx;
×
637

638
        nop( instructions, from, idx + 1 );
×
639
    }
×
640

641
    /**
642
     * Replace the instructions with NOP operations
643
     * 
644
     * @param instructions
645
     *            the instruction list
646
     * @param from
647
     *            starting index
648
     * @param to
649
     *            end index
650
     */
651
    private void nop( List<WasmInstruction> instructions, int from, int to ) {
652
        for( int i = from; i < to; i++ ) {
1✔
653
            WasmInstruction instr = instructions.get( i );
1✔
654
            instructions.set( i, new WasmNopInstruction( instr.getCodePosition(), instr.getLineNumber() ) );
1✔
655
        }
656
    }
1✔
657

658
    private void patchVarHandle( List<WasmInstruction> instructions, int idx, WasmCallInstruction callInst ) {
659
        FunctionName name = callInst.getFunctionName();
×
660
        switch( name.methodName ) {
×
661
            case "getAndSet":
662
                patchVarHandleFieldFunction( instructions, idx, callInst, name, 3 );
×
663
                break;
664
        }
665
    }
×
666

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

703
                                }
704

705
                                throw new RuntimeException( name.signatureName );
×
706
                            }
707
                        };
708
        functions.markAsNeeded( func, false );
×
709
        WasmCallInstruction call = new WasmCallInstruction( func, callInst.getCodePosition(), callInst.getLineNumber(), callInst.getTypeManager(), false );
×
710
        instructions.set( idx, call );
×
711

712
        // a virtual method call has also a DUP of this because we need for virtual method dispatch the parameter 2 times.
713
        for( int i = idx; i >= 0; i-- ) {
×
714
            WasmInstruction instr = instructions.get( i );
×
715
            if( instr.getType() == Type.DupThis && ((DupThis)instr).getValue() == callInst ) {
×
716
                nop( instructions, i, i + 1 );
×
717
                break;
×
718
            }
719
        }
720
    }
×
721

722
    /**
723
     * Hold the state from declaring of Unsafe address
724
     */
725
    static class UnsafeState {
1✔
726
        String fieldName;
727

728
        String typeName;
729
    }
730
}
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