content: /*
   * 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 org.apache.cassandra.simulator.asm;
  
  import java.util.HashSet;
  import java.util.Set;
  
  import org.objectweb.asm.MethodVisitor;
  import org.objectweb.asm.Opcodes;
  
  import static org.apache.cassandra.simulator.asm.TransformationKind.FIELD_NEMESIS;
  import static org.apache.cassandra.simulator.asm.TransformationKind.SIGNAL_NEMESIS;
  
  /**
   * Insert nemesis points at all obvious thread signalling points (execution and blocking primitive methods),
   * as well as to any fields annotated with {@link org.apache.cassandra.utils.Nemesis}.
   *
   * If the annotated field is an AtomicX or AtomicXFieldUpdater, we insert nemesis points either side of the next
   * invocation of
   *
   * TODO (config): permit Nemesis on a class as well as a field, so as to mark all (at least volatile or atomic) members
   */
  class NemesisTransformer extends MethodVisitor
  {
      private final ClassTransformer transformer;
      final NemesisGenerator generator;
      final NemesisFieldKind.Selector nemesisFieldSelector;
  
      // for simplicity, we simply activate nemesis for all atomic operations on the relevant type once any such
      // field is loaded in a method
      Set<String> onForTypes;
  
      public NemesisTransformer(ClassTransformer transformer, int api, String name, MethodVisitor parent, NemesisGenerator generator, NemesisFieldKind.Selector nemesisFieldSelector)
      {
          super(api, parent);
          this.transformer = transformer;
          this.generator = generator;
          this.nemesisFieldSelector = nemesisFieldSelector;
          generator.newMethod(name);
      }
  
      @Override
      public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface)
      {
          boolean nemesisAfter = false;
          if (isInterface && opcode == Opcodes.INVOKEINTERFACE
          && (owner.startsWith("org/apache/cassandra/concurrent") || owner.startsWith("org/apache/cassandra/utils/concurrent")) && (
                 (owner.equals("org/apache/cassandra/utils/concurrent/CountDownLatch") && name.equals("decrement"))
              || (owner.equals("org/apache/cassandra/utils/concurrent/Condition") && name.equals("signal"))
              || (owner.equals("org/apache/cassandra/utils/concurrent/Semaphore") && name.equals("release"))
              || ((owner.equals("org/apache/cassandra/concurrent/ExecutorPlus")
                   || owner.equals("org/apache/cassandra/concurrent/LocalAwareExecutorPlus")
                   || owner.equals("org/apache/cassandra/concurrent/ScheduledExecutorPlus")
                   || owner.equals("org/apache/cassandra/concurrent/SequentialExecutorPlus")
                   || owner.equals("org/apache/cassandra/concurrent/LocalAwareSequentialExecutorPlus")
                  ) && (name.equals("execute") || name.equals("submit") || name.equals("maybeExecuteImmediately")))
          ))
          {
              generateAndCall(SIGNAL_NEMESIS);
          }
          else if ((opcode == Opcodes.INVOKESPECIAL || opcode == Opcodes.INVOKEVIRTUAL)
                   && (onForTypes != null && onForTypes.contains(owner)))
          {
              nemesisAfter = true;
              generateAndCall(FIELD_NEMESIS);
          }
  
          super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
          if (nemesisAfter)
              generateAndCall(FIELD_NEMESIS);
      }
  
      @Override
      public void visitFieldInsn(int opcode, String owner, String name, String descriptor)
      {
          boolean nemesisAfter = false;
          NemesisFieldKind nemesis = nemesisFieldSelector.get(owner, name);
          if (nemesis != null)
          {
              switch (nemesis)
              {
                  case SIMPLE:
                      switch (opcode)
                      {
                          default:
                              throw new AssertionError();
                          case Opcodes.PUTFIELD:
                          case Opcodes.PUTSTATIC:
                              generateAndCall(FIELD_NEMESIS);
                              break;
                          case Opcodes.GETFIELD:
                          case Opcodes.GETSTATIC:
                              nemesisAfter = true;
                      }
                      break;
                  case ATOMICX:
                  case ATOMICUPDATERX:
                      switch (opcode)
                      {
                          case Opcodes.GETFIELD:
                          case Opcodes.GETSTATIC:
                              if (onForTypes == null)
                                  onForTypes = new HashSet<>();
                              onForTypes.add(descriptor.substring(1, descriptor.length() - 1));
                      }
                      break;
              }
          }
          super.visitFieldInsn(opcode, owner, name, descriptor);
          if (nemesisAfter)
              generateAndCall(FIELD_NEMESIS);
      }
  
      private void generateAndCall(TransformationKind kind)
      {
          generator.generateAndCall(kind, transformer, mv);
      }
  }
  