From 8cd3c5b9d5741db1650f7274b7bfcf3e0d7eb398 Mon Sep 17 00:00:00 2001 From: Volker Berlin Date: Sun, 30 Apr 2023 20:54:09 +0200 Subject: [PATCH] more replacement for VarHandle --- .../jwebassembly/module/FunctionManager.java | 1 + .../jwebassembly/module/UnsafeManager.java | 92 ++++++++++++++++++- 2 files changed, 89 insertions(+), 4 deletions(-) diff --git a/src/de/inetsoftware/jwebassembly/module/FunctionManager.java b/src/de/inetsoftware/jwebassembly/module/FunctionManager.java index adc25c9..e1c5487 100644 --- a/src/de/inetsoftware/jwebassembly/module/FunctionManager.java +++ b/src/de/inetsoftware/jwebassembly/module/FunctionManager.java @@ -175,6 +175,7 @@ class FunctionManager { case UnsafeManager.UNSAFE_11: case UnsafeManager.FIELDUPDATER: case UnsafeManager.VARHANDLE: + case UnsafeManager.METHOD_HANDLES: // Unsafe method call will be replaces by the UnsafeManager return name; } diff --git a/src/de/inetsoftware/jwebassembly/module/UnsafeManager.java b/src/de/inetsoftware/jwebassembly/module/UnsafeManager.java index c1962bd..ffc0d2c 100644 --- a/src/de/inetsoftware/jwebassembly/module/UnsafeManager.java +++ b/src/de/inetsoftware/jwebassembly/module/UnsafeManager.java @@ -84,6 +84,8 @@ class UnsafeManager { /** VARHANDLE as modern replacement of Unsafe */ static final String VARHANDLE = "java/lang/invoke/VarHandle"; + static final String METHOD_HANDLES = "java/lang/invoke/MethodHandles$Lookup"; + @Nonnull private final FunctionManager functions; @@ -137,6 +139,7 @@ class UnsafeManager { patch( instructions, i, callInst ); break; case VARHANDLE: + case METHOD_HANDLES: patchVarHandle( instructions, i, callInst ); break; } @@ -746,7 +749,7 @@ class UnsafeManager { } /** - * Patch an unsafe function that access a field + * Remove an unsafe function that access a field * * @param instructions * the instruction list @@ -754,6 +757,8 @@ class UnsafeManager { * the index in the instructions * @param callInst * the method call to Unsafe + * @param paramCount + * the count of params that must be removed from stack (including THIS if instance method) */ private void remove( List instructions, int idx, final WasmCallInstruction callInst, int paramCount ) { int from = StackInspector.findInstructionThatPushValue( instructions.subList( 0, idx ), paramCount, callInst.getCodePosition() ).idx; @@ -778,15 +783,70 @@ class UnsafeManager { } } - private void patchVarHandle( List instructions, int idx, WasmCallInstruction callInst ) { + private void patchVarHandle( List instructions, int idx, WasmCallInstruction callInst ) throws IOException { FunctionName name = callInst.getFunctionName(); switch( name.methodName ) { - case "getAndSet": - patchVarHandleFieldFunction( instructions, idx, callInst, name, 3 ); + case "findVarHandle": + patch_findVarHandle( instructions, idx, callInst ); break; + case "arrayElementVarHandle": // java/lang/invoke/MethodHandles + UnsafeState state = findUnsafeState( instructions, idx ); + int from = StackInspector.findInstructionThatPushValue( instructions, 2, callInst.getCodePosition() ).idx; + + StackValue stackValue = StackInspector.findInstructionThatPushValue( instructions, 1, callInst.getCodePosition() ); + state.typeName = getClassConst( instructions, stackValue ); + break; + case "getAndSet": + case "set": + case "getAcquire": + case "getAndAdd": + case "getAndBitwiseOr": + case "get": + case "compareAndSet": + case "weakCompareAndSet": + case "setVolatile": + case "setRelease": + case "setOpaque": + patchVarHandleFieldFunction( instructions, idx, callInst, name, callInst.getPopCount() ); + break; + case "releaseFence": + nop( instructions, idx, idx + 1 ); + break; + default: + throw new WasmException( "Unsupported VarHandle method: " + name.signatureName, -1 ); } } + /** + * Patch a method call to Unsafe.objectFieldOffset() and find the parameter for other patch operations. + * + * @param instructions + * the instruction list + * @param idx + * the index in the instructions + * @param callInst + * the method call to Unsafe + * @throws IOException + * If any I/O error occur + */ + private void patch_findVarHandle( List instructions, int idx, WasmCallInstruction callInst ) throws IOException { + UnsafeState state = findUnsafeState( instructions, idx ); + + // objectFieldOffset() has 3 parameters THIS(Unsafe), class and the fieldname + List paramInstructions = instructions.subList( 0, idx ); + int from = StackInspector.findInstructionThatPushValue( paramInstructions, 4, callInst.getCodePosition() ).idx; + + StackValue stackValue = StackInspector.findInstructionThatPushValue( paramInstructions, 2, callInst.getCodePosition() ); + state.fieldName = ((WasmConstStringInstruction)stackValue.instr).getValue(); + + // find the class value on which getDeclaredField is called + stackValue = StackInspector.findInstructionThatPushValue( paramInstructions, 3, callInst.getCodePosition() ); + state.typeName = getClassConst( instructions, stackValue ); + + useFieldName( state ); + nop( instructions, from, idx + 2 ); + } + /** * Patch an unsafe function that access a field * @@ -815,6 +875,26 @@ class UnsafeManager { } AnyType[] paramTypes = callInst.getPopValueTypes(); switch( name.methodName ) { + case "compareAndSet": + case "weakCompareAndSet": + AnyType type = paramTypes[3]; + if( type.isRefType() ) { + type = ValueType.ref; + } + return "local.get 1" // THIS + + " struct.get " + state.typeName + ' ' + state.fieldName // + + " local.get 2 " // expected + + " " + type + ".eq" // + + " if" // + + " local.get 1" // THIS + + " local.get 3 " // update + + " struct.set " + state.typeName + ' ' + state.fieldName // + + " i32.const 1" // + + " return" // + + " end" // + + " i32.const 1" // + + " return"; + case "getAndSet": return "local.get 1" // THIS + " struct.get " + state.typeName + ' ' + state.fieldName // @@ -823,6 +903,10 @@ class UnsafeManager { + " struct.set " + state.typeName + ' ' + state.fieldName // + " return"; + case "set": + return "local.get 1" // THIS + + " local.get 2" // x + + " struct.set " + state.typeName + ' ' + state.fieldName; } throw new RuntimeException( name.signatureName );