/* GNU Objective C Runtime method related functions.
  Copyright (C) 2010-2024 Free Software Foundation, Inc.
  Contributed by Nicola Pero

This file is part of GCC.

GCC is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.

GCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
details.

Under Section 7 of GPL version 3, you are granted additional
permissions described in the GCC Runtime Library Exception, version
3.1, as published by the Free Software Foundation.

You should have received a copy of the GNU General Public License and
a copy of the GCC Runtime Library Exception along with this program;
see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
<http://www.gnu.org/licenses/>.  */

#include "objc-private/common.h"
#include "objc/runtime.h"
#include "objc-private/module-abi-8.h" /* For runtime structures.   */
#include "objc/thr.h"
#include "objc-private/runtime.h"      /* For __objc_runtime_mutex.  */
#include <stdlib.h>                    /* For malloc.  */

SEL
method_getName (struct objc_method * method)
{
 if (method == NULL)
   return NULL;

 return method->method_name;
}

const char *
method_getTypeEncoding (struct objc_method * method)
{
 if (method == NULL)
   return NULL;

 return method->method_types;
}

IMP
method_getImplementation (struct objc_method * method)
{
 if (method == NULL)
   return NULL;

 return method->method_imp;
}

struct objc_method_description *
method_getDescription (struct objc_method * method)
{
 /* Note that the following returns NULL if method is NULL, which is
    fine.  */
 return (struct objc_method_description *)method;
}

struct objc_method **
class_copyMethodList (Class class_, unsigned int *numberOfReturnedMethods)
{
 unsigned int count = 0;
 struct objc_method **returnValue = NULL;
 struct objc_method_list* method_list;

 if (class_ == Nil)
   {
     if (numberOfReturnedMethods)
       *numberOfReturnedMethods = 0;
     return NULL;
   }

 /* Lock the runtime mutex because the class methods may be
    concurrently modified.  */
 objc_mutex_lock (__objc_runtime_mutex);

 /* Count how many methods we have.  */
 method_list = class_->methods;

 while (method_list)
   {
     count = count + method_list->method_count;
     method_list = method_list->method_next;
   }

 if (count != 0)
   {
     unsigned int i = 0;

     /* Allocate enough memory to hold them.  */
     returnValue
       = (struct objc_method **)(malloc (sizeof (struct objc_method *)
                                         * (count + 1)));

     /* Copy the methods.  */
     method_list = class_->methods;

     while (method_list)
       {
         int j;
         for (j = 0; j < method_list->method_count; j++)
           {
             returnValue[i] = &(method_list->method_list[j]);
             i++;
           }
         method_list = method_list->method_next;
       }

     returnValue[i] = NULL;
   }

 objc_mutex_unlock (__objc_runtime_mutex);

 if (numberOfReturnedMethods)
   *numberOfReturnedMethods = count;

 return returnValue;
}

IMP
method_setImplementation (struct objc_method * method, IMP implementation)
{
 IMP old_implementation;

 if (method == NULL  ||  implementation == NULL)
   return NULL;

 /* We lock the runtime mutex so that concurrent calls to change the
    same method won't conflict with each other.  */
 objc_mutex_lock (__objc_runtime_mutex);

 old_implementation = method->method_imp;
 method->method_imp = implementation;

 /* That was easy :-).  But now we need to find all classes that use
    this method, and update the IMP in the dispatch tables.  */
 __objc_update_classes_with_methods (method, NULL);

 objc_mutex_unlock (__objc_runtime_mutex);

 return old_implementation;
}

void
method_exchangeImplementations (struct objc_method * method_a, struct objc_method * method_b)
{
 IMP old_implementation_a;
 IMP old_implementation_b;

 if (method_a == NULL  ||  method_b == NULL)
   return;

 /* We lock the runtime mutex so that concurrent calls to exchange
    similar methods won't conflict with each other.  Each of them
    should be atomic.  */
 objc_mutex_lock (__objc_runtime_mutex);

 old_implementation_a = method_a->method_imp;
 old_implementation_b = method_b->method_imp;

 method_a->method_imp = old_implementation_b;
 method_b->method_imp = old_implementation_a;

 /* That was easy :-).  But now we need to find all classes that use
    these methods, and update the IMP in the dispatch tables.  */
 __objc_update_classes_with_methods (method_a, method_b);

 objc_mutex_unlock (__objc_runtime_mutex);
}