2002-03-10 00:29:33 +01:00
|
|
|
#
|
|
|
|
# Copyright 1999, 2000, 2001 Patrik Stridvall
|
|
|
|
#
|
|
|
|
# This library is free software; you can redistribute it and/or
|
|
|
|
# modify it under the terms of the GNU Lesser General Public
|
|
|
|
# License as published by the Free Software Foundation; either
|
|
|
|
# version 2.1 of the License, or (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This library 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
|
|
|
|
# Lesser General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU Lesser General Public
|
|
|
|
# License along with this library; if not, write to the Free Software
|
|
|
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
#
|
|
|
|
|
1999-10-31 03:08:38 +01:00
|
|
|
package winapi_function;
|
2001-07-11 19:27:45 +02:00
|
|
|
use base qw(function);
|
1999-10-31 03:08:38 +01:00
|
|
|
|
|
|
|
use strict;
|
|
|
|
|
2001-07-13 00:22:05 +02:00
|
|
|
use config qw($current_dir $wine_dir);
|
|
|
|
use modules qw($modules);
|
2001-07-11 19:27:45 +02:00
|
|
|
use util qw(&normalize_set);
|
|
|
|
use winapi qw($win16api $win32api @winapis);
|
|
|
|
|
|
|
|
########################################################################
|
|
|
|
# constructor
|
|
|
|
#
|
|
|
|
|
1999-10-31 03:08:38 +01:00
|
|
|
sub new {
|
|
|
|
my $proto = shift;
|
|
|
|
my $class = ref($proto) || $proto;
|
|
|
|
my $self = {};
|
|
|
|
bless ($self, $class);
|
|
|
|
|
|
|
|
return $self;
|
|
|
|
}
|
|
|
|
|
2001-07-18 22:09:12 +02:00
|
|
|
########################################################################
|
|
|
|
# is_win
|
|
|
|
#
|
|
|
|
|
|
|
|
sub is_win16 { my $self = shift; return defined($self->_module($win16api, @_)); }
|
|
|
|
sub is_win32 { my $self = shift; return defined($self->_module($win32api, @_)); }
|
|
|
|
|
2001-07-11 19:27:45 +02:00
|
|
|
########################################################################
|
2001-07-13 00:22:05 +02:00
|
|
|
# external_name
|
2001-07-11 19:27:45 +02:00
|
|
|
#
|
1999-10-31 03:08:38 +01:00
|
|
|
|
2001-07-13 00:22:05 +02:00
|
|
|
sub _external_name {
|
2001-07-11 19:27:45 +02:00
|
|
|
my $self = shift;
|
2001-07-13 00:22:05 +02:00
|
|
|
my $winapi = shift;
|
|
|
|
|
|
|
|
my $file = $self->file;
|
2001-07-11 19:27:45 +02:00
|
|
|
my $internal_name = $self->internal_name;
|
1999-10-31 03:08:38 +01:00
|
|
|
|
2001-07-13 00:22:05 +02:00
|
|
|
my $external_name = $winapi->function_external_name($internal_name);
|
|
|
|
my $module = $winapi->function_internal_module($internal_name);
|
1999-10-31 03:08:38 +01:00
|
|
|
|
2001-07-13 00:22:05 +02:00
|
|
|
if(!defined($external_name) && !defined($module)) {
|
|
|
|
return undef;
|
2001-07-11 19:27:45 +02:00
|
|
|
}
|
1999-10-31 03:08:38 +01:00
|
|
|
|
2001-07-13 00:22:05 +02:00
|
|
|
my @external_names = split(/\s*&\s*/, $external_name);
|
|
|
|
my @modules = split(/\s*&\s*/, $module);
|
|
|
|
|
|
|
|
my @external_names2;
|
|
|
|
while(defined(my $external_name = shift @external_names) &&
|
|
|
|
defined(my $module = shift @modules))
|
|
|
|
{
|
|
|
|
if($modules->is_allowed_module_in_file($module, "$current_dir/$file")) {
|
|
|
|
push @external_names2, $external_name;
|
|
|
|
}
|
|
|
|
}
|
2001-07-08 22:33:20 +02:00
|
|
|
|
2001-07-13 00:22:05 +02:00
|
|
|
return join(" & ", @external_names2);
|
2001-07-11 19:27:45 +02:00
|
|
|
}
|
2001-07-08 22:33:20 +02:00
|
|
|
|
2001-07-13 00:22:05 +02:00
|
|
|
sub _external_names {
|
2001-07-11 19:27:45 +02:00
|
|
|
my $self = shift;
|
2001-07-13 00:22:05 +02:00
|
|
|
my $winapi = shift;
|
|
|
|
|
|
|
|
my $external_name = $self->_external_name($winapi);
|
2001-07-08 22:33:20 +02:00
|
|
|
|
2001-07-13 00:22:05 +02:00
|
|
|
if(defined($external_name)) {
|
|
|
|
return split(/\s*&\s*/, $external_name);
|
2001-07-11 19:27:45 +02:00
|
|
|
} else {
|
|
|
|
return ();
|
|
|
|
}
|
2001-07-08 22:33:20 +02:00
|
|
|
}
|
|
|
|
|
2001-07-24 01:20:56 +02:00
|
|
|
sub external_name {
|
|
|
|
my $self = shift;
|
|
|
|
|
|
|
|
foreach my $winapi (@winapis) {
|
|
|
|
my $external_name = $self->_external_name($winapi, @_);
|
|
|
|
|
|
|
|
if(defined($external_name)) {
|
|
|
|
return $external_name;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return undef;
|
|
|
|
}
|
|
|
|
|
2001-07-13 00:22:05 +02:00
|
|
|
sub external_name16 { my $self = shift; return $self->_external_name($win16api, @_); }
|
2001-07-14 02:48:41 +02:00
|
|
|
sub external_name32 { my $self = shift; return $self->_external_name($win32api, @_); }
|
2000-03-24 21:39:51 +01:00
|
|
|
|
2001-07-13 00:22:05 +02:00
|
|
|
sub external_names16 { my $self = shift; return $self->_external_names($win16api, @_); }
|
|
|
|
sub external_names32 { my $self = shift; return $self->_external_names($win32api, @_); }
|
2000-03-24 21:39:51 +01:00
|
|
|
|
2001-07-24 01:20:56 +02:00
|
|
|
sub external_names { my $self = shift; return ($self->external_names16, $self->external_names32); }
|
2000-03-24 21:39:51 +01:00
|
|
|
|
2001-07-13 00:22:05 +02:00
|
|
|
########################################################################
|
|
|
|
# module
|
|
|
|
#
|
|
|
|
|
|
|
|
sub _module {
|
1999-10-31 03:08:38 +01:00
|
|
|
my $self = shift;
|
2001-07-13 00:22:05 +02:00
|
|
|
my $winapi = shift;
|
|
|
|
|
|
|
|
my $file = $self->file;
|
2001-07-11 19:27:45 +02:00
|
|
|
my $internal_name = $self->internal_name;
|
1999-10-31 03:08:38 +01:00
|
|
|
|
2001-07-13 00:22:05 +02:00
|
|
|
my $module = $winapi->function_internal_module($internal_name);
|
|
|
|
if(!defined($module)) {
|
|
|
|
return undef;
|
|
|
|
}
|
|
|
|
|
|
|
|
my @modules;
|
|
|
|
foreach my $module (split(/\s*&\s*/, $module)) {
|
|
|
|
if($modules->is_allowed_module_in_file($module, "$current_dir/$file")) {
|
|
|
|
push @modules, $module;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return join(" & ", @modules);
|
2001-07-11 19:27:45 +02:00
|
|
|
}
|
1999-10-31 03:08:38 +01:00
|
|
|
|
2001-07-13 00:22:05 +02:00
|
|
|
sub _modules {
|
2001-07-11 19:27:45 +02:00
|
|
|
my $self = shift;
|
2001-07-13 00:22:05 +02:00
|
|
|
my $winapi = shift;
|
|
|
|
|
|
|
|
my $module = $self->_module($winapi);
|
1999-10-31 03:08:38 +01:00
|
|
|
|
2001-07-13 00:22:05 +02:00
|
|
|
if(defined($module)) {
|
|
|
|
return split(/\s*&\s*/, $module);
|
2001-07-11 19:27:45 +02:00
|
|
|
} else {
|
|
|
|
return ();
|
|
|
|
}
|
1999-10-31 03:08:38 +01:00
|
|
|
}
|
|
|
|
|
2001-07-13 00:22:05 +02:00
|
|
|
sub module16 { my $self = shift; return $self->_module($win16api, @_); }
|
|
|
|
sub module32 { my $self = shift; return $self->_module($win32api, @_); }
|
|
|
|
|
|
|
|
sub module { my $self = shift; return join (" & ", $self->modules); }
|
|
|
|
|
|
|
|
sub modules16 { my $self = shift; return $self->_modules($win16api, @_); }
|
|
|
|
sub modules32 { my $self = shift; return $self->_modules($win32api, @_); }
|
|
|
|
|
|
|
|
sub modules { my $self = shift; return ($self->modules16, $self->modules32); }
|
|
|
|
|
|
|
|
########################################################################
|
|
|
|
# ordinal
|
|
|
|
#
|
|
|
|
|
|
|
|
sub _ordinal {
|
1999-10-31 03:08:38 +01:00
|
|
|
my $self = shift;
|
2001-07-13 00:22:05 +02:00
|
|
|
my $winapi = shift;
|
|
|
|
|
|
|
|
my $file = $self->file;
|
2001-07-11 19:27:45 +02:00
|
|
|
my $internal_name = $self->internal_name;
|
1999-10-31 03:08:38 +01:00
|
|
|
|
2001-07-13 00:22:05 +02:00
|
|
|
my $ordinal = $winapi->function_internal_ordinal($internal_name);
|
|
|
|
my $module = $winapi->function_internal_module($internal_name);
|
1999-10-31 03:08:38 +01:00
|
|
|
|
2001-07-13 00:22:05 +02:00
|
|
|
if(!defined($ordinal) && !defined($module)) {
|
|
|
|
return undef;
|
|
|
|
}
|
|
|
|
|
|
|
|
my @ordinals = split(/\s*&\s*/, $ordinal);
|
|
|
|
my @modules = split(/\s*&\s*/, $module);
|
1999-10-31 03:08:38 +01:00
|
|
|
|
2001-07-13 00:22:05 +02:00
|
|
|
my @ordinals2;
|
|
|
|
while(defined(my $ordinal = shift @ordinals) &&
|
|
|
|
defined(my $module = shift @modules))
|
|
|
|
{
|
|
|
|
if($modules->is_allowed_module_in_file($module, "$current_dir/$file")) {
|
|
|
|
push @ordinals2, $ordinal;
|
|
|
|
}
|
2001-07-11 19:27:45 +02:00
|
|
|
}
|
2001-07-13 00:22:05 +02:00
|
|
|
|
|
|
|
return join(" & ", @ordinals2);
|
1999-10-31 03:08:38 +01:00
|
|
|
}
|
|
|
|
|
2001-07-13 00:22:05 +02:00
|
|
|
sub _ordinals {
|
1999-10-31 03:08:38 +01:00
|
|
|
my $self = shift;
|
2001-07-13 00:22:05 +02:00
|
|
|
my $winapi = shift;
|
1999-10-31 03:08:38 +01:00
|
|
|
|
2001-07-13 00:22:05 +02:00
|
|
|
my $ordinal = $self->_ordinal($winapi);
|
|
|
|
|
|
|
|
if(defined($ordinal)) {
|
|
|
|
return split(/\s*&\s*/, $ordinal);
|
2001-07-11 19:27:45 +02:00
|
|
|
} else {
|
2001-07-13 00:22:05 +02:00
|
|
|
return ();
|
2001-07-11 19:27:45 +02:00
|
|
|
}
|
2000-05-24 01:37:51 +02:00
|
|
|
}
|
|
|
|
|
2001-07-13 00:22:05 +02:00
|
|
|
sub ordinal16 { my $self = shift; return $self->_ordinal($win16api, @_); }
|
|
|
|
sub ordinal32 { my $self = shift; return $self->_ordinal($win32api, @_); }
|
2000-05-24 01:37:51 +02:00
|
|
|
|
2001-07-13 00:22:05 +02:00
|
|
|
sub ordinal { my $self = shift; return join (" & ", $self->ordinals); }
|
2000-05-24 01:37:51 +02:00
|
|
|
|
2001-07-13 00:22:05 +02:00
|
|
|
sub ordinals16 { my $self = shift; return $self->_ordinals($win16api, @_); }
|
|
|
|
sub ordinals32 { my $self = shift; return $self->_ordinals($win32api, @_); }
|
|
|
|
|
|
|
|
sub ordinals { my $self = shift; return ($self->ordinals16, $self->ordinals32); }
|
|
|
|
|
|
|
|
########################################################################
|
|
|
|
# prefix
|
|
|
|
#
|
2000-04-24 20:02:32 +02:00
|
|
|
|
2001-07-11 19:27:45 +02:00
|
|
|
sub prefix {
|
2000-04-24 20:02:32 +02:00
|
|
|
my $self = shift;
|
2001-07-11 19:27:45 +02:00
|
|
|
my $module16 = $self->module16;
|
|
|
|
my $module32 = $self->module32;
|
2000-04-24 20:02:32 +02:00
|
|
|
|
2001-07-13 00:22:05 +02:00
|
|
|
my $file = $self->file;
|
2001-07-18 22:09:12 +02:00
|
|
|
my $function_line = $self->function_line;
|
2001-07-11 19:27:45 +02:00
|
|
|
my $return_type = $self->return_type;
|
|
|
|
my $internal_name = $self->internal_name;
|
|
|
|
my $calling_convention = $self->calling_convention;
|
2000-04-24 20:02:32 +02:00
|
|
|
|
2001-07-24 01:20:56 +02:00
|
|
|
my $refargument_types = $self->argument_types;
|
|
|
|
my @argument_types = ();
|
|
|
|
if(defined($refargument_types)) {
|
|
|
|
@argument_types = @$refargument_types;
|
|
|
|
if($#argument_types < 0) {
|
|
|
|
@argument_types = ("void");
|
|
|
|
}
|
2001-07-11 19:27:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
my $prefix = "";
|
2001-07-13 00:22:05 +02:00
|
|
|
|
|
|
|
my @modules = ();
|
|
|
|
my %used;
|
|
|
|
foreach my $module ($self->modules) {
|
|
|
|
if($used{$module}) { next; }
|
|
|
|
push @modules, $module;
|
|
|
|
$used{$module}++;
|
|
|
|
}
|
2001-07-18 22:09:12 +02:00
|
|
|
$prefix .= "$file:";
|
|
|
|
if(defined($function_line)) {
|
|
|
|
$prefix .= "$function_line: ";
|
|
|
|
} else {
|
|
|
|
$prefix .= "<>: ";
|
|
|
|
}
|
2001-07-13 00:22:05 +02:00
|
|
|
if($#modules >= 0) {
|
|
|
|
$prefix .= join(" & ", @modules) . ": ";
|
2001-07-11 19:27:45 +02:00
|
|
|
} else {
|
|
|
|
$prefix .= "<>: ";
|
|
|
|
}
|
|
|
|
$prefix .= "$return_type ";
|
|
|
|
$prefix .= "$calling_convention " if $calling_convention;
|
|
|
|
$prefix .= "$internal_name(" . join(",", @argument_types) . "): ";
|
|
|
|
|
|
|
|
return $prefix;
|
1999-10-31 03:08:38 +01:00
|
|
|
}
|
|
|
|
|
2001-07-13 00:22:05 +02:00
|
|
|
########################################################################
|
|
|
|
# calling_convention
|
|
|
|
#
|
|
|
|
|
2001-07-11 19:27:45 +02:00
|
|
|
sub calling_convention16 {
|
1999-10-31 03:08:38 +01:00
|
|
|
my $self = shift;
|
2001-07-11 19:27:45 +02:00
|
|
|
my $return_kind16 = $self->return_kind16;
|
|
|
|
|
|
|
|
my $suffix;
|
|
|
|
if(!defined($return_kind16)) {
|
|
|
|
$suffix = undef;
|
|
|
|
} elsif($return_kind16 =~ /^(?:void|s_word|word)$/) {
|
|
|
|
$suffix = "16";
|
|
|
|
} elsif($return_kind16 =~ /^(?:long|ptr|segptr|segstr|str|wstr)$/) {
|
|
|
|
$suffix = "";
|
|
|
|
} else {
|
|
|
|
$suffix = undef;
|
|
|
|
}
|
1999-10-31 03:08:38 +01:00
|
|
|
|
2001-07-11 19:27:45 +02:00
|
|
|
local $_ = $self->calling_convention;
|
|
|
|
if(/^__cdecl$/) {
|
|
|
|
return "cdecl";
|
|
|
|
} elsif(/^VFWAPIV|WINAPIV$/) {
|
|
|
|
if(!defined($suffix)) { return undef; }
|
|
|
|
return "pascal$suffix"; # FIXME: Is this correct?
|
|
|
|
} elsif(/^__stdcall|VFWAPI|WINAPI|CALLBACK$/) {
|
|
|
|
if(!defined($suffix)) { return undef; }
|
|
|
|
return "pascal$suffix";
|
|
|
|
} elsif(/^__asm$/) {
|
|
|
|
return "asm";
|
|
|
|
} else {
|
|
|
|
return "cdecl";
|
|
|
|
}
|
2000-05-11 23:46:17 +02:00
|
|
|
}
|
|
|
|
|
2001-07-11 19:27:45 +02:00
|
|
|
sub calling_convention32 {
|
2000-05-11 23:46:17 +02:00
|
|
|
my $self = shift;
|
|
|
|
|
2001-07-11 19:27:45 +02:00
|
|
|
local $_ = $self->calling_convention;
|
|
|
|
if(/^__cdecl$/) {
|
|
|
|
return "cdecl";
|
|
|
|
} elsif(/^VFWAPIV|WINAPIV$/) {
|
|
|
|
return "varargs";
|
|
|
|
} elsif(/^__stdcall|VFWAPI|WINAPI|CALLBACK$/) {
|
|
|
|
return "stdcall";
|
|
|
|
} elsif(/^__asm$/) {
|
|
|
|
return "asm";
|
|
|
|
} else {
|
|
|
|
return "cdecl";
|
|
|
|
}
|
1999-10-31 03:08:38 +01:00
|
|
|
}
|
|
|
|
|
2001-07-11 19:27:45 +02:00
|
|
|
sub get_all_module_ordinal16 {
|
2001-02-12 04:44:41 +01:00
|
|
|
my $self = shift;
|
2001-07-11 19:27:45 +02:00
|
|
|
my $internal_name = $self->internal_name;
|
2001-02-12 04:44:41 +01:00
|
|
|
|
2001-07-11 19:27:45 +02:00
|
|
|
return winapi::get_all_module_internal_ordinal16($internal_name);
|
2001-02-12 04:44:41 +01:00
|
|
|
}
|
|
|
|
|
2001-07-11 19:27:45 +02:00
|
|
|
sub get_all_module_ordinal32 {
|
1999-10-31 03:08:38 +01:00
|
|
|
my $self = shift;
|
2001-07-11 19:27:45 +02:00
|
|
|
my $internal_name = $self->internal_name;
|
1999-10-31 03:08:38 +01:00
|
|
|
|
2001-07-11 19:27:45 +02:00
|
|
|
return winapi::get_all_module_internal_ordinal32($internal_name);
|
1999-10-31 03:08:38 +01:00
|
|
|
}
|
|
|
|
|
2001-07-11 19:27:45 +02:00
|
|
|
sub get_all_module_ordinal {
|
1999-10-31 03:08:38 +01:00
|
|
|
my $self = shift;
|
2001-07-11 19:27:45 +02:00
|
|
|
my $internal_name = $self->internal_name;
|
1999-10-31 03:08:38 +01:00
|
|
|
|
2001-07-11 19:27:45 +02:00
|
|
|
return winapi::get_all_module_internal_ordinal($internal_name);
|
1999-10-31 03:08:38 +01:00
|
|
|
}
|
|
|
|
|
2001-07-11 19:27:45 +02:00
|
|
|
sub _return_kind {
|
1999-10-31 03:08:38 +01:00
|
|
|
my $self = shift;
|
2001-07-11 19:27:45 +02:00
|
|
|
my $winapi = shift;
|
|
|
|
my $return_type = $self->return_type;
|
1999-10-31 03:08:38 +01:00
|
|
|
|
2001-07-11 19:27:45 +02:00
|
|
|
return $winapi->translate_argument($return_type);
|
|
|
|
}
|
1999-10-31 03:08:38 +01:00
|
|
|
|
2001-07-11 19:27:45 +02:00
|
|
|
sub return_kind16 {
|
|
|
|
my $self = shift; return $self->_return_kind($win16api, @_);
|
1999-10-31 03:08:38 +01:00
|
|
|
}
|
|
|
|
|
2001-07-11 19:27:45 +02:00
|
|
|
sub return_kind32 {
|
|
|
|
my $self = shift; return $self->_return_kind($win32api, @_);
|
|
|
|
}
|
1999-10-31 03:08:38 +01:00
|
|
|
|
2001-07-11 19:27:45 +02:00
|
|
|
sub _argument_kinds {
|
|
|
|
my $self = shift;
|
|
|
|
my $winapi = shift;
|
2001-07-24 01:20:56 +02:00
|
|
|
my $refargument_types = $self->argument_types;
|
|
|
|
|
|
|
|
if(!defined($refargument_types)) {
|
|
|
|
return undef;
|
|
|
|
}
|
2001-07-11 19:27:45 +02:00
|
|
|
|
|
|
|
my @argument_kinds;
|
2001-07-24 01:20:56 +02:00
|
|
|
foreach my $argument_type (@$refargument_types) {
|
2001-07-11 19:27:45 +02:00
|
|
|
my $argument_kind = $winapi->translate_argument($argument_type);
|
|
|
|
|
|
|
|
if(defined($argument_kind) && $argument_kind eq "longlong") {
|
|
|
|
push @argument_kinds, ("long", "long");
|
|
|
|
} else {
|
|
|
|
push @argument_kinds, $argument_kind;
|
|
|
|
}
|
1999-10-31 03:08:38 +01:00
|
|
|
}
|
2001-07-11 19:27:45 +02:00
|
|
|
|
|
|
|
return [@argument_kinds];
|
1999-10-31 03:08:38 +01:00
|
|
|
}
|
|
|
|
|
2001-07-11 19:27:45 +02:00
|
|
|
sub argument_kinds16 {
|
|
|
|
my $self = shift; return $self->_argument_kinds($win16api, @_);
|
|
|
|
}
|
|
|
|
|
|
|
|
sub argument_kinds32 {
|
|
|
|
my $self = shift; return $self->_argument_kinds($win32api, @_);
|
|
|
|
}
|
|
|
|
|
|
|
|
##############################################################################
|
|
|
|
# Accounting
|
|
|
|
#
|
|
|
|
|
1999-10-31 03:08:38 +01:00
|
|
|
sub function_called {
|
|
|
|
my $self = shift;
|
|
|
|
my $called_function_names = \%{$self->{CALLED_FUNCTION_NAMES}};
|
|
|
|
|
|
|
|
my $name = shift;
|
|
|
|
|
|
|
|
$$called_function_names{$name}++;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub function_called_by {
|
|
|
|
my $self = shift;
|
|
|
|
my $called_by_function_names = \%{$self->{CALLED_BY_FUNCTION_NAMES}};
|
|
|
|
|
|
|
|
my $name = shift;
|
|
|
|
|
|
|
|
$$called_by_function_names{$name}++;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub called_function_names {
|
|
|
|
my $self = shift;
|
|
|
|
my $called_function_names = \%{$self->{CALLED_FUNCTION_NAMES}};
|
|
|
|
|
|
|
|
return sort(keys(%$called_function_names));
|
|
|
|
}
|
|
|
|
|
|
|
|
sub called_by_function_names {
|
|
|
|
my $self = shift;
|
|
|
|
my $called_by_function_names = \%{$self->{CALLED_BY_FUNCTION_NAMES}};
|
|
|
|
|
|
|
|
return sort(keys(%$called_by_function_names));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
1;
|