mirror of
https://github.com/i-net-software/JWebAssembly.git
synced 2025-03-14 18:43:27 +01:00
More Unsafe stuff. First step for VarHandle in Java 9 and higher.
This commit is contained in:
parent
588c9563f6
commit
bc16b18083
@ -173,6 +173,7 @@ class FunctionManager {
|
||||
switch( name.className ) {
|
||||
case UnsafeManager.UNSAFE_8:
|
||||
case UnsafeManager.UNSAFE_11:
|
||||
case UnsafeManager.VARHANDLE:
|
||||
// Unsafe method call will be replaces by the UnsafeManager
|
||||
return name;
|
||||
}
|
||||
|
@ -15,8 +15,11 @@
|
||||
*/
|
||||
package de.inetsoftware.jwebassembly.module;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
@ -72,6 +75,9 @@ class UnsafeManager {
|
||||
/** Unsafe class bane in Java 11 */
|
||||
static final String UNSAFE_11 = "jdk/internal/misc/Unsafe";
|
||||
|
||||
/** VARHANDLE as modern replacement of Unsafe */
|
||||
static final String VARHANDLE = "java/lang/invoke/VarHandle";
|
||||
|
||||
@Nonnull
|
||||
private final FunctionManager functions;
|
||||
|
||||
@ -106,6 +112,9 @@ class UnsafeManager {
|
||||
case UNSAFE_11:
|
||||
patch( instructions, i, callInst );
|
||||
break;
|
||||
case VARHANDLE:
|
||||
patchVarHandle( instructions, i, callInst );
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -145,14 +154,21 @@ class UnsafeManager {
|
||||
case "jdk/internal/misc/Unsafe.arrayIndexScale(Ljava/lang/Class;)I":
|
||||
patch_arrayIndexScale( instructions, idx, callInst );
|
||||
break;
|
||||
case "sun/misc/Unsafe.getObjectVolatile(Ljava/lang/Object;J)Ljava/lang/Object;":
|
||||
case "sun/misc/Unsafe.getInt(Ljava/lang/Object;J)I":
|
||||
patchFieldFunction( instructions, idx, callInst, name, 1 );
|
||||
break;
|
||||
case "sun/misc/Unsafe.getAndAddInt(Ljava/lang/Object;JI)I":
|
||||
case "sun/misc/Unsafe.getAndSetInt(Ljava/lang/Object;JI)I":
|
||||
case "sun/misc/Unsafe.putOrderedInt(Ljava/lang/Object;JI)V":
|
||||
case "sun/misc/Unsafe.putInt(Ljava/lang/Object;JI)V":
|
||||
case "sun/misc/Unsafe.getAndAddLong(Ljava/lang/Object;JJ)J":
|
||||
case "sun/misc/Unsafe.getAndSetLong(Ljava/lang/Object;JJ)J":
|
||||
case "sun/misc/Unsafe.putOrderedLong(Ljava/lang/Object;JJ)V":
|
||||
case "sun/misc/Unsafe.getObjectVolatile(Ljava/lang/Object;J)Ljava/lang/Object;":
|
||||
case "sun/misc/Unsafe.putLong(Ljava/lang/Object;JJ)V":
|
||||
case "sun/misc/Unsafe.putOrderedObject(Ljava/lang/Object;JLjava/lang/Object;)V":
|
||||
case "sun/misc/Unsafe.putObjectVolatile(Ljava/lang/Object;JLjava/lang/Object;)V":
|
||||
case "sun/misc/Unsafe.putObject(Ljava/lang/Object;JLjava/lang/Object;)V":
|
||||
case "sun/misc/Unsafe.getAndSetObject(Ljava/lang/Object;JLjava/lang/Object;)Ljava/lang/Object;":
|
||||
case "jdk/internal/misc/Unsafe.getAndAddInt(Ljava/lang/Object;JI)I":
|
||||
case "jdk/internal/misc/Unsafe.getAndSetInt(Ljava/lang/Object;JI)I":
|
||||
@ -190,8 +206,10 @@ class UnsafeManager {
|
||||
break;
|
||||
case "jdk/internal/misc/Unsafe.ensureClassInitialized(Ljava/lang/Class;)V":
|
||||
case "jdk/internal/misc/Unsafe.unpark(Ljava/lang/Object;)V":
|
||||
case "sun/misc/Unsafe.unpark(Ljava/lang/Object;)V":
|
||||
remove( instructions, idx, callInst, 2 );
|
||||
break;
|
||||
case "sun/misc/Unsafe.park(ZJ)V":
|
||||
case "jdk/internal/misc/Unsafe.park(ZJ)V":
|
||||
remove( instructions, idx, callInst, 3 );
|
||||
break;
|
||||
@ -415,17 +433,44 @@ class UnsafeManager {
|
||||
* @param name
|
||||
* the calling function
|
||||
* @param fieldNameParam
|
||||
* the function parameter with the field offset. This must be a long (Java signature "J"). The THIS
|
||||
* parameter has the index 0.
|
||||
* 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.
|
||||
*/
|
||||
private void patchFieldFunction( List<WasmInstruction> instructions, int idx, final WasmCallInstruction callInst, FunctionName name, int fieldNameParam ) {
|
||||
StackValue stackValue = StackInspector.findInstructionThatPushValue( instructions.subList( 0, idx ), fieldNameParam, callInst.getCodePosition() );
|
||||
FunctionName fieldNameWithOffset = ((WasmGlobalInstruction)stackValue.instr).getFieldName();
|
||||
WasmInstruction instr = stackValue.instr;
|
||||
|
||||
Set<FunctionName> fieldNames;
|
||||
FunctionName fieldNameWithOffset = null;
|
||||
if( instr.getType() == Type.Global ) {
|
||||
fieldNameWithOffset = ((WasmGlobalInstruction)instr).getFieldName();
|
||||
fieldNames = Collections.singleton( fieldNameWithOffset );
|
||||
} else {
|
||||
// java.util.concurrent.ConcurrentHashMap.tabAt() calculate a value with the field
|
||||
fieldNames = new HashSet<>();
|
||||
int pos2 = stackValue.idx;
|
||||
stackValue = StackInspector.findInstructionThatPushValue( instructions.subList( 0, idx ), fieldNameParam + 1, callInst.getCodePosition() );
|
||||
int i = stackValue.idx;
|
||||
for( ; i < pos2; i++ ) {
|
||||
instr = instructions.get( i );
|
||||
if( instr.getType() != Type.Global ) {
|
||||
continue;
|
||||
}
|
||||
fieldNameWithOffset = ((WasmGlobalInstruction)instr).getFieldName();
|
||||
fieldNames.add( fieldNameWithOffset );
|
||||
}
|
||||
}
|
||||
|
||||
WatCodeSyntheticFunctionName func =
|
||||
new WatCodeSyntheticFunctionName( fieldNameWithOffset.className, '.' + name.methodName, name.signature, "", (AnyType[])null ) {
|
||||
@Override
|
||||
protected String getCode() {
|
||||
UnsafeState state = unsafes.get( fieldNameWithOffset );
|
||||
UnsafeState state = null;
|
||||
for(FunctionName fieldNameWithOffset : fieldNames ) {
|
||||
state = unsafes.get( fieldNameWithOffset );
|
||||
if( state != null ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( state == null ) {
|
||||
// we are in the scan phase. The static code was not scanned yet.
|
||||
return "";
|
||||
@ -476,8 +521,12 @@ class UnsafeManager {
|
||||
+ " return";
|
||||
|
||||
case "putOrderedInt":
|
||||
case "putInt":
|
||||
case "putOrderedLong":
|
||||
case "putLong":
|
||||
case "putOrderedObject":
|
||||
case "putObjectVolatile":
|
||||
case "putObject":
|
||||
return "local.get 0" // THIS
|
||||
+ " local.get 2" // x
|
||||
+ " struct.set " + state.typeName + ' ' + state.fieldName;
|
||||
@ -492,7 +541,7 @@ class UnsafeManager {
|
||||
|
||||
// a virtual method call has also a DUP of this because we need for virtual method dispatch the parameter 2 times.
|
||||
for( int i = idx; i >= 0; i-- ) {
|
||||
WasmInstruction instr = instructions.get( i );
|
||||
instr = instructions.get( i );
|
||||
if( instr.getType() == Type.DupThis && ((DupThis)instr).getValue() == callInst ) {
|
||||
nop( instructions, i, i + 1 );
|
||||
break;
|
||||
@ -588,6 +637,70 @@ class UnsafeManager {
|
||||
}
|
||||
}
|
||||
|
||||
private void patchVarHandle( List<WasmInstruction> instructions, int idx, WasmCallInstruction callInst ) {
|
||||
FunctionName name = callInst.getFunctionName();
|
||||
switch( name.methodName ) {
|
||||
case "getAndSet":
|
||||
patchVarHandleFieldFunction( instructions, idx, callInst, name, 3 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Patch an unsafe function that access a field
|
||||
*
|
||||
* @param instructions
|
||||
* the instruction list
|
||||
* @param idx
|
||||
* the index in the instructions
|
||||
* @param callInst
|
||||
* the method call to Unsafe
|
||||
* @param name
|
||||
* the calling function
|
||||
* @param fieldNameParam
|
||||
* the function parameter with the field offset on the stack. This must be a long (Java signature "J") for Unsafe.
|
||||
*/
|
||||
private void patchVarHandleFieldFunction( List<WasmInstruction> instructions, int idx, final WasmCallInstruction callInst, FunctionName name, int fieldNameParam ) {
|
||||
StackValue stackValue = StackInspector.findInstructionThatPushValue( instructions.subList( 0, idx ), fieldNameParam, callInst.getCodePosition() );
|
||||
FunctionName fieldNameWithOffset = ((WasmGlobalInstruction)stackValue.instr).getFieldName();
|
||||
WatCodeSyntheticFunctionName func =
|
||||
new WatCodeSyntheticFunctionName( fieldNameWithOffset.className, '.' + name.methodName, name.signature, "", (AnyType[])null ) {
|
||||
@Override
|
||||
protected String getCode() {
|
||||
UnsafeState state = unsafes.get( fieldNameWithOffset );
|
||||
if( state == null ) {
|
||||
// we are in the scan phase. The static code was not scanned yet.
|
||||
return "";
|
||||
}
|
||||
AnyType[] paramTypes = callInst.getPopValueTypes();
|
||||
switch( name.methodName ) {
|
||||
case "getAndSet":
|
||||
return "local.get 1" // THIS
|
||||
+ " struct.get " + state.typeName + ' ' + state.fieldName //
|
||||
+ " local.get 1" // THIS
|
||||
+ " local.get 2" // newValue
|
||||
+ " struct.set " + state.typeName + ' ' + state.fieldName //
|
||||
+ " return";
|
||||
|
||||
}
|
||||
|
||||
throw new RuntimeException( name.signatureName );
|
||||
}
|
||||
};
|
||||
functions.markAsNeeded( func, false );
|
||||
WasmCallInstruction call = new WasmCallInstruction( func, callInst.getCodePosition(), callInst.getLineNumber(), callInst.getTypeManager(), false );
|
||||
instructions.set( idx, call );
|
||||
|
||||
// a virtual method call has also a DUP of this because we need for virtual method dispatch the parameter 2 times.
|
||||
for( int i = idx; i >= 0; i-- ) {
|
||||
WasmInstruction instr = instructions.get( i );
|
||||
if( instr.getType() == Type.DupThis && ((DupThis)instr).getValue() == callInst ) {
|
||||
nop( instructions, i, i + 1 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hold the state from declaring of Unsafe address
|
||||
*/
|
||||
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
Copyright 2023 Volker Berlin (i-net software)
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
package de.inetsoftware.jwebassembly.module.nativecode;
|
||||
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
|
||||
import de.inetsoftware.jwebassembly.api.annotation.Replace;
|
||||
|
||||
/**
|
||||
* Replacement for java.lang.invoke.MethodHandles
|
||||
*
|
||||
* @author Volker Berlin
|
||||
*/
|
||||
public class ReplacementForMethodHandles {
|
||||
|
||||
/**
|
||||
* Replacement for static lookup().
|
||||
*/
|
||||
@Replace( "java/lang/invoke/MethodHandles.lookup()Ljava/lang/invoke/MethodHandles$Lookup;" )
|
||||
static Lookup lookup() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replacement for static lookup().
|
||||
*/
|
||||
@Replace( "java/lang/invoke/MethodHandles$Lookup.findVarHandle(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/invoke/VarHandle;" )
|
||||
Object findVarHandle(Class<?> recv, String name, Class<?> type) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -19,10 +19,13 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
|
||||
import de.inetsoftware.jwebassembly.JWebAssembly;
|
||||
import de.inetsoftware.jwebassembly.ScriptEngine;
|
||||
import de.inetsoftware.jwebassembly.WasmRule;
|
||||
import de.inetsoftware.jwebassembly.api.annotation.Export;
|
||||
@ -51,8 +54,14 @@ public class UnsafeTest extends AbstractBaseTest {
|
||||
addParam( list, script, "getAndAddLong" );
|
||||
addParam( list, script, "getAndSetLong" );
|
||||
addParam( list, script, "lazySetLong" );
|
||||
addParam( list, script, "compareAndSwapReference" );
|
||||
addParam( list, script, "getAndSetReference" );
|
||||
addParam( list, script, "lazySetReference" );
|
||||
}
|
||||
rule.setTestParameters( list );
|
||||
JWebAssembly.LOGGER.setLevel( Level.FINE );
|
||||
rule.setProperty( JWebAssembly.IGNORE_NATIVE, "true" );
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
@ -135,5 +144,33 @@ public class UnsafeTest extends AbstractBaseTest {
|
||||
obj.lazySet( 42 );
|
||||
return obj.get();
|
||||
}
|
||||
|
||||
@Export
|
||||
static int compareAndSwapReference() {
|
||||
AtomicReference<Integer> obj = new AtomicReference<>();
|
||||
if( obj.compareAndSet( null, 25 ) ) {
|
||||
return obj.get();
|
||||
} else {
|
||||
return 42;
|
||||
}
|
||||
}
|
||||
|
||||
@Export
|
||||
static int getAndSetReference() {
|
||||
AtomicReference<Integer> obj = new AtomicReference<>();
|
||||
obj.set( 13 );
|
||||
if( obj.getAndSet( 25 ) == 13 ) {
|
||||
return obj.get();
|
||||
} else {
|
||||
return 42;
|
||||
}
|
||||
}
|
||||
|
||||
@Export
|
||||
static int lazySetReference() {
|
||||
AtomicReference<Integer> obj = new AtomicReference<>();
|
||||
obj.lazySet( 42 );
|
||||
return obj.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user