All Downloads are FREE. Search and download functionalities are using the official Maven repository.

es.urjc.etsii.grafo.patches.PatchMathRandom Maven / Gradle / Ivy

package es.urjc.etsii.grafo.patches;

import es.urjc.etsii.grafo.config.BlockConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import jakarta.annotation.PostConstruct;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.lang.reflect.Field;
import java.lang.reflect.InaccessibleObjectException;
import java.lang.reflect.Modifier;

/**
 * Block calls to Math.random() as it uses an internal random that breaks experiment reproducibility
 * Use RandomManager.getRandom().nextDouble() instead.
 */
@Service
public class PatchMathRandom {

    private static final Logger log = LoggerFactory.getLogger(PatchMathRandom.class);

    private final boolean isEnabled;

    /**
     * Initialize patch with config
     *
     * @param config configuration
     */
    public PatchMathRandom(BlockConfig config){
        this.isEnabled = config.isBlockMathRandom();
    }

    /**
     * Execute patch
     */
    @PostConstruct
    public void patch(){
        if(!isEnabled){
            log.info("Skipping Math.random() patch");
            return;
        }

        try {
            var internalClass = Class.forName("java.lang.Math$RandomNumberGeneratorHolder");
            var internalRandom = internalClass.getDeclaredField("randomNumberGenerator");
            internalRandom.setAccessible(true);
            makeNonFinal(internalRandom);
            internalRandom.set(null, new FailRandom());
            log.info("Math.random() patched successfully");
        } catch (NoSuchFieldException | IllegalAccessException | ClassNotFoundException | InaccessibleObjectException | UnsupportedOperationException e) {
            // Log as warning, but do not stop application when failing to patch, it is not critical
            log.warn("Failed to patch Math.random(), probably due to missing opens, see: https://mork-optimization.readthedocs.io/en/latest/quickstart/troubleshooting/. Cause: {}", e.getMessage());
        }
    }

    private void makeNonFinal(Field field) throws NoSuchFieldException, IllegalAccessException {
        var lookup = MethodHandles.privateLookupIn(Field.class, MethodHandles.lookup());
        VarHandle modifiers = lookup.findVarHandle(Field.class, "modifiers", int.class);
        int mods = field.getModifiers();
        if (Modifier.isFinal(mods)) {
            modifiers.set(field, mods & ~Modifier.FINAL);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy