This is an automated email from the ASF dual-hosted git repository. ifesdjeen pushed a commit to branch dev in repository https://gitbox.apache.org/repos/asf/cassandra-simulator.git
commit cb0a026027e1a4e6126740489d5c318b02745565 Author: Alex Petrov <[email protected]> AuthorDate: Thu Jul 23 19:10:29 2026 +0200 Avoid gating by global methods --- .../cassandra/simulator/asm/ClassTransformer.java | 8 ++- .../simulator/asm/GlobalMethodTransformer.java | 10 ++-- .../cassandra/simulator/asm/InterceptClasses.java | 3 +- .../cassandra/simulator/asm/InterceptRule.java | 5 ++ ...ethodOverrideOutsideConfiguredPackagesTest.java | 67 ++++++++++++++++++++++ 5 files changed, 85 insertions(+), 8 deletions(-) diff --git a/simulator-asm/src/main/java/org/apache/cassandra/simulator/asm/ClassTransformer.java b/simulator-asm/src/main/java/org/apache/cassandra/simulator/asm/ClassTransformer.java index 856f88d..e2f5209 100644 --- a/simulator-asm/src/main/java/org/apache/cassandra/simulator/asm/ClassTransformer.java +++ b/simulator-asm/src/main/java/org/apache/cassandra/simulator/asm/ClassTransformer.java @@ -212,7 +212,8 @@ class ClassTransformer extends ClassVisitor implements MethodWriterSink visitEachRefType(descriptor, dependentTypes); EnumSet<Flag> flags = this.flags; - if (flags.isEmpty() || ((access & ACC_SYNTHETIC) != 0 && (name.endsWith("$unsync") || name.endsWith("$catch") || name.endsWith("$nemesis")))) + if ((flags.isEmpty() && customRules.isEmpty()) + || ((access & ACC_SYNTHETIC) != 0 && (name.endsWith("$unsync") || name.endsWith("$catch") || name.endsWith("$nemesis")))) { MethodVisitor visitor = super.visitMethod(access, name, descriptor, signature, exceptions); if (dependentTypes != null && (access & (ACC_STATIC | ACC_SYNTHETIC)) != 0 && (name.equals("<clinit>") || name.startsWith("lambda$"))) @@ -253,7 +254,10 @@ class ClassTransformer extends ClassVisitor implements MethodWriterSink visitor = new MonitorEnterExitParkTransformer(this, api, visitor, className, monitorDelayChance); if (isToString) visitor = deterministicToString(visitor); - if (flags.contains(GLOBAL_METHODS) || flags.contains(Flag.LOCK_SUPPORT) || flags.contains(Flag.DETERMINISTIC)) + if (flags.contains(GLOBAL_METHODS) + || flags.contains(Flag.LOCK_SUPPORT) + || flags.contains(Flag.DETERMINISTIC) + || !customRules.isEmpty()) visitor = new GlobalMethodTransformer(flags, this, api, name, visitor, customRules); if (flags.contains(NEMESIS)) visitor = new NemesisTransformer(this, api, name, visitor, nemesis, nemesisFieldSelector); diff --git a/simulator-asm/src/main/java/org/apache/cassandra/simulator/asm/GlobalMethodTransformer.java b/simulator-asm/src/main/java/org/apache/cassandra/simulator/asm/GlobalMethodTransformer.java index 7f1de1e..0153473 100644 --- a/simulator-asm/src/main/java/org/apache/cassandra/simulator/asm/GlobalMethodTransformer.java +++ b/simulator-asm/src/main/java/org/apache/cassandra/simulator/asm/GlobalMethodTransformer.java @@ -49,8 +49,8 @@ class GlobalMethodTransformer extends MethodVisitor private final ClassTransformer transformer; private final String methodName; private final Set<InterceptRule> customRules; - /** Enables the bulk of redirects: Thread.start, Executors, UUID, Unsafe, Uninterruptibles, - * TimeUnit.sleep, Future.get, @Intercept factory/constructor rules. */ + /** Enables the bulk of built-in redirects: Thread.start, Executors, UUID, Unsafe, + * Uninterruptibles, TimeUnit.sleep, and Future.get. */ private boolean globalMethods; /** TimeUtils#timestampMicros -> SimulatedTime$Global.nextGlobalMonotonicMicros(). */ private boolean globalClock; @@ -113,7 +113,7 @@ class GlobalMethodTransformer extends MethodVisitor super.visitMethodInsn(Opcodes.INVOKESTATIC, "org/apache/cassandra/simulator/systems/InterceptorOfSystemMethods$Global", name, descriptor, false); } // TimeUnit#sleep(long) [INVOKEVIRTUAL, ~globalMethods] -> InterceptorOfSystemMethods$Global.sleep(TimeUnit, long) - else if (owner.equals("java/util/concurrent/TimeUnit") && name.equals("sleep")) + else if ((lockSupport || deterministic) && owner.equals("java/util/concurrent/TimeUnit") && name.equals("sleep")) { transformer.witness(GLOBAL_METHOD); super.visitMethodInsn(Opcodes.INVOKESTATIC, "org/apache/cassandra/simulator/systems/InterceptorOfSystemMethods$Global", "sleep", "(Ljava/util/concurrent/TimeUnit;J)V", false); @@ -174,7 +174,7 @@ class GlobalMethodTransformer extends MethodVisitor else super.visitMethodInsn(opcode, owner, name, descriptor, isInterface); } - else if (globalMethods && applyMethodRule(opcode, owner, name, descriptor, isFirstMethodInsn)) + else if (!customRules.isEmpty() && applyMethodRule(opcode, owner, name, descriptor, isFirstMethodInsn)) { // emitted by the matching rule } @@ -284,7 +284,7 @@ class GlobalMethodTransformer extends MethodVisitor @Override public void visitTypeInsn(int opcode, String type) { - if (globalMethods) + if (!customRules.isEmpty() && opcode == Opcodes.NEW) { for (InterceptRule rule : customRules) { diff --git a/simulator-asm/src/main/java/org/apache/cassandra/simulator/asm/InterceptClasses.java b/simulator-asm/src/main/java/org/apache/cassandra/simulator/asm/InterceptClasses.java index 433fa5a..f3bc092 100644 --- a/simulator-asm/src/main/java/org/apache/cassandra/simulator/asm/InterceptClasses.java +++ b/simulator-asm/src/main/java/org/apache/cassandra/simulator/asm/InterceptClasses.java @@ -51,6 +51,7 @@ public class InterceptClasses implements BiFunction<String, byte[], byte[]> public static final int BYTECODE_VERSION = Opcodes.ASM9; // TODO (cleanup): use annotations + // TODO (alexp, cleanup): this should be redundant by now; we need to slowly migrate off to use global method transformer / new InterceptRule API private static final Pattern MONITORS = Pattern.compile( "org[/.]apache[/.]cassandra[/.]utils[/.]concurrent[/.].*" + "|org[/.]apache[/.]cassandra[/.]concurrent[/.].*" + "|org[/.]apache[/.]cassandra[/.]simulator[/.]test.*" + @@ -264,7 +265,7 @@ public class InterceptClasses implements BiFunction<String, byte[], byte[]> flags.add(Flag.NEMESIS); } - if (flags.isEmpty() && hashcode == null) + if (flags.isEmpty() && hashcode == null && customRules.isEmpty()) { cache.put(internalName, peerGroup.unmodified); return input; diff --git a/simulator-asm/src/main/java/org/apache/cassandra/simulator/asm/InterceptRule.java b/simulator-asm/src/main/java/org/apache/cassandra/simulator/asm/InterceptRule.java index d911b44..d41262d 100644 --- a/simulator-asm/src/main/java/org/apache/cassandra/simulator/asm/InterceptRule.java +++ b/simulator-asm/src/main/java/org/apache/cassandra/simulator/asm/InterceptRule.java @@ -71,6 +71,11 @@ public abstract class InterceptRule this.descriptor = descriptor; } + public static FactoryMethod make(String fromClass, String methodName, String descriptor) + { + return new FactoryMethod(fromClass, methodName, descriptor); + } + @Override public boolean match(int opcode, String owner, String name, String descriptor) { diff --git a/simulator-core/src/test/java/com/example/MethodOverrideOutsideConfiguredPackagesTest.java b/simulator-core/src/test/java/com/example/MethodOverrideOutsideConfiguredPackagesTest.java new file mode 100644 index 0000000..b5f4688 --- /dev/null +++ b/simulator-core/src/test/java/com/example/MethodOverrideOutsideConfiguredPackagesTest.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 com.example; + +import org.apache.cassandra.simulator.Simulator; +import org.apache.cassandra.simulator.context.IIsolatedExecutor.SerializableRunnable; +import org.apache.cassandra.simulator.context.SharedTestState; +import org.apache.cassandra.simulator.utils.Intercept; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class MethodOverrideOutsideConfiguredPackagesTest +{ + @BeforeEach + void reset() + { + SharedTestState.reset(); + } + + @Test + void redirectsCallSiteOutsideConfiguredPackages() + { + try (Simulator simulator = new Simulator(42L)) + { + simulator.interceptMethods(Source.class, Redirects.class); + simulator.simulate((SerializableRunnable) () -> + SharedTestState.value1.set(new Source().value())); + } + + assertEquals(2, SharedTestState.value1.get()); + } + + public static class Source + { + public int value() + { + return 1; + } + } + + public static class Redirects + { + @Intercept + public static int value(Source receiver) + { + return 2; + } + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
