mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-11-02 08:29:32 +01:00
* treat methods added via an objccategory to an objcclass in exactly the
same way as regular methods as far as overriding is concerned ("override"
is now allowed, and even required, to override them in a descendent class;
and similarly, "reintroduce" must be used if a category wants to replace
a method in a child class if that method was added by another category in
a parent class)
* print the name of owning objcclass/category of the original method in case
override/reintroduce is missing for objc methods, since this is no longer
always the parent class
git-svn-id: trunk@16035 -
This commit is contained in:
parent
15ab85fee5
commit
1633341906
2
.gitattributes
vendored
2
.gitattributes
vendored
@ -9412,6 +9412,8 @@ tests/test/tobjc35f.pp svneol=native#text/plain
|
||||
tests/test/tobjc35g.pp svneol=native#text/plain
|
||||
tests/test/tobjc35h.pp svneol=native#text/plain
|
||||
tests/test/tobjc35i.pp svneol=native#text/plain
|
||||
tests/test/tobjc36.pp svneol=native#text/plain
|
||||
tests/test/tobjc36a.pp svneol=native#text/plain
|
||||
tests/test/tobjc4.pp svneol=native#text/plain
|
||||
tests/test/tobjc4a.pp svneol=native#text/plain
|
||||
tests/test/tobjc5.pp svneol=native#text/plain
|
||||
|
||||
@ -1247,14 +1247,16 @@ parser_e_no_objc_published=03271_E_Objective-C classes cannot have published sec
|
||||
parser_f_need_objc=03272_F_This module requires an Objective-C mode switch to be compiled
|
||||
% This error indicates the use of Objective-C language features without an Objective-C mode switch
|
||||
% active. Enable one via the -M command line switch, or the {\$modeswitch x} directive.
|
||||
parser_e_must_use_override_objc=03273_E_Inherited methods can only be overridden in Objective-C, add "override".
|
||||
parser_h_should_use_override_objc=03274_H_Inherited methods can only be overridden in Objective-C, add "override".
|
||||
parser_e_must_use_override_objc=03273_E_Inherited methods can only be overridden in Objective-C, add "override" (inherited method defined in $1)
|
||||
parser_h_should_use_override_objc=03274_H_Inherited methods can only be overridden in Objective-C, add "override" (inherited method defined in $1).
|
||||
% It is not possible to \var{reintroduce} methods in Objective-C like in Object Pascal. Methods with the same
|
||||
% name always map to the same virtual method entry. In order to make this clear in the source code,
|
||||
% the compiler always requires the \var{override} directive to be specified when implementing overriding
|
||||
% Objective-C methods in Pascal. If the implementation is external, this rule is relaxed because Objective-C
|
||||
% does not have any \var{override}-style keyword (since it's the default and only behaviour in that language),
|
||||
% which makes it hard for automated header conversion tools to include it everywhere.
|
||||
% The type in which the inherited method is defined is explicitly mentioned, because this may either
|
||||
% be an objcclass or an objccategory.
|
||||
parser_e_objc_message_name_changed=03275_E_Message name "$1" in inherited class is different from message name "$2" in current class.
|
||||
% An overriding Objective-C method cannot have a different message name than an inherited method. The reason
|
||||
% is that these message names uniquely define the message to the Objective-C runtime, which means that
|
||||
@ -1266,8 +1268,8 @@ parser_e_no_category_as_types=03277_E_Objective-C categories cannot be used as t
|
||||
% It is not possible to declare a variable as an instance of an Objective-C category. A
|
||||
% category adds methods to the scope of an existing class, but does not define a type by itself.
|
||||
parser_e_no_category_override=03278_E_Categories do not override, but replace methods. Use "reintroduce" instead.
|
||||
parser_e_must_use_reintroduce_objc=03279_E_Replaced methods can only be reintroduced in Objective-C, add "reintroduce".
|
||||
parser_h_should_use_reintroduce_objc=03280_H_Replaced methods can only be reintroduced in Objective-C, add "reintroduce".
|
||||
parser_e_must_use_reintroduce_objc=03279_E_Replaced methods can only be reintroduced in Objective-C, add "reintroduce" (replaced method defined in $1).
|
||||
parser_h_should_use_reintroduce_objc=03280_H_Replaced methods can only be reintroduced in Objective-C, add "reintroduce" (replaced method defined in $1).
|
||||
% A category replaces an existing method in an Objective-C class, rather than that it overrides it.
|
||||
% Calling an inherited method from an category method will call that method in
|
||||
% the extended class' parent, not in the extended class itself. The
|
||||
@ -1275,6 +1277,8 @@ parser_h_should_use_reintroduce_objc=03280_H_Replaced methods can only be reintr
|
||||
% called or referred to. This behaviour corresponds somewhat more closely to
|
||||
% \var{reintroduce} than to \var{override} (although in case of \var{reintroduce}
|
||||
% in Object Pascal, hidden methods are still reachable via inherited).
|
||||
% The type in which the inherited method is defined is explicitly mentioned, because this may either
|
||||
% be an objcclass or an objccategory.
|
||||
parser_e_implements_getter_not_default_cc=03281_E_Getter for implements interface must use the target's default calling convention.
|
||||
% Interface getters are called via a helper in the run time library, and hence
|
||||
% have to use the default calling convention for the target (\var{register} on
|
||||
|
||||
@ -869,7 +869,7 @@ const
|
||||
option_info=11024;
|
||||
option_help_pages=11025;
|
||||
|
||||
MsgTxtSize = 57371;
|
||||
MsgTxtSize = 57500;
|
||||
|
||||
MsgIdxMax : array[1..20] of longint=(
|
||||
24,88,297,97,82,53,111,22,202,63,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -38,8 +38,8 @@ interface
|
||||
private
|
||||
_Class : tobjectdef;
|
||||
handledprotocols: tfpobjectlist;
|
||||
function is_new_vmt_entry(pd:tprocdef):boolean;
|
||||
procedure add_new_vmt_entry(pd:tprocdef);
|
||||
function is_new_vmt_entry(pd:tprocdef; out overridesclasshelper: boolean):boolean;
|
||||
procedure add_new_vmt_entry(pd:tprocdef; allowoverridingmethod: boolean);
|
||||
function check_msg_str(vmtpd, pd: tprocdef):boolean;
|
||||
function intf_search_procdef_by_name(proc: tprocdef;const name: string): tprocdef;
|
||||
procedure intf_get_procdefs(ImplIntf:TImplementedInterface;IntfDef:TObjectDef);
|
||||
@ -137,14 +137,17 @@ implementation
|
||||
end;
|
||||
|
||||
|
||||
procedure TVMTBuilder.add_new_vmt_entry(pd:tprocdef);
|
||||
procedure TVMTBuilder.add_new_vmt_entry(pd:tprocdef; allowoverridingmethod: boolean);
|
||||
var
|
||||
i : longint;
|
||||
vmtentry : pvmtentry;
|
||||
vmtpd : tprocdef;
|
||||
begin
|
||||
{ new entry is needed, override was not possible }
|
||||
if (po_overridingmethod in pd.procoptions) then
|
||||
{ Allowed when overriding a category method for a parent class in a
|
||||
descendent Objective-C class }
|
||||
if not allowoverridingmethod and
|
||||
(po_overridingmethod in pd.procoptions) then
|
||||
MessagePos1(pd.fileinfo,parser_e_nothing_to_be_overridden,pd.fullprocname(false));
|
||||
|
||||
{ check that all methods have overload directive }
|
||||
@ -223,7 +226,7 @@ implementation
|
||||
end;
|
||||
|
||||
|
||||
function TVMTBuilder.is_new_vmt_entry(pd:tprocdef):boolean;
|
||||
function TVMTBuilder.is_new_vmt_entry(pd:tprocdef; out overridesclasshelper: boolean):boolean;
|
||||
const
|
||||
po_comp = [po_classmethod,po_virtualmethod,po_staticmethod,po_interrupt,po_iocheck,po_msgint,
|
||||
po_exports,po_varargs,po_explicitparaloc,po_nostackframe];
|
||||
@ -232,8 +235,208 @@ implementation
|
||||
hasequalpara,
|
||||
hasoverloads,
|
||||
pdoverload : boolean;
|
||||
vmtentry : pvmtentry;
|
||||
vmtpd : tprocdef;
|
||||
srsym : tsym;
|
||||
st : tsymtable;
|
||||
|
||||
// returns true if we can stop checking, false if we have to continue
|
||||
function found_entry(var vmtpd: tprocdef; var vmtentryvis: tvisibility; updatevalues: boolean): boolean;
|
||||
begin
|
||||
result:=false;
|
||||
overridesclasshelper:=false;
|
||||
|
||||
{ ignore hidden entries (e.g. virtual overridden by a static) that are not visible anymore }
|
||||
if vmtentryvis=vis_hidden then
|
||||
exit;
|
||||
|
||||
{ ignore different names }
|
||||
if vmtpd.procsym.name<>pd.procsym.name then
|
||||
exit;
|
||||
|
||||
{ hide private methods that are not visible anymore. For this check we
|
||||
must override the visibility with the highest value in the override chain.
|
||||
This is required for case (see tw3292) with protected-private-protected where the
|
||||
same vmtentry is used (PFV) }
|
||||
if not is_visible_for_object(vmtpd.owner,vmtentryvis,_class) then
|
||||
exit;
|
||||
|
||||
{ inherit overload }
|
||||
if (po_overload in vmtpd.procoptions) then
|
||||
begin
|
||||
include(pd.procoptions,po_overload);
|
||||
pdoverload:=true;
|
||||
end;
|
||||
|
||||
{ compare parameter types only, no specifiers yet }
|
||||
hasequalpara:=(compare_paras(vmtpd.paras,pd.paras,cp_none,[cpo_ignoreuniv])>=te_equal);
|
||||
|
||||
{ check that we are not trying to override a final method }
|
||||
if (po_finalmethod in vmtpd.procoptions) and
|
||||
hasequalpara and (po_overridingmethod in pd.procoptions) and is_class(_class) then
|
||||
MessagePos1(pd.fileinfo,parser_e_final_can_no_be_overridden,pd.fullprocname(false))
|
||||
else
|
||||
{ old definition has virtual
|
||||
new definition has no virtual or override }
|
||||
if (po_virtualmethod in vmtpd.procoptions) and
|
||||
(
|
||||
not(po_virtualmethod in pd.procoptions) or
|
||||
(
|
||||
{ new one does not have reintroduce in case of an objccategory }
|
||||
(is_objccategory(_class) and not(po_reintroduce in pd.procoptions)) or
|
||||
{ new one does not have override in case of objpas/objc class/intf/proto }
|
||||
(is_class_or_interface_or_objc(_class) and not is_objccategory(_class) and not(po_overridingmethod in pd.procoptions))
|
||||
)
|
||||
) then
|
||||
begin
|
||||
if (
|
||||
not(pdoverload or hasoverloads) or
|
||||
hasequalpara
|
||||
) then
|
||||
begin
|
||||
if not(po_reintroduce in pd.procoptions) then
|
||||
if not(is_objc_class_or_protocol(_class)) then
|
||||
MessagePos1(pd.fileinfo,parser_w_should_use_override,pd.fullprocname(false))
|
||||
else
|
||||
begin
|
||||
{ In Objective-C, you cannot create a new VMT entry to
|
||||
start a new inheritance tree. We therefore give an
|
||||
error when the class is implemented in Pascal, to
|
||||
avoid confusion due to things working differently
|
||||
with Object Pascal classes.
|
||||
|
||||
In case of external classes, we only give a hint,
|
||||
because requiring override everywhere may make
|
||||
automated header translation tools too complex. }
|
||||
if not(oo_is_external in _class.objectoptions) then
|
||||
if not is_objccategory(_class) then
|
||||
MessagePos1(pd.fileinfo,parser_e_must_use_override_objc,FullTypeName(tdef(vmtpd.owner.defowner),nil))
|
||||
else
|
||||
MessagePos1(pd.fileinfo,parser_e_must_use_reintroduce_objc,FullTypeName(tdef(vmtpd.owner.defowner),nil))
|
||||
{ there may be a lot of these in auto-translated
|
||||
heaeders, so only calculate the fulltypename if
|
||||
the hint will be shown }
|
||||
else if CheckVerbosity(V_Hint) then
|
||||
if not is_objccategory(_class) then
|
||||
MessagePos1(pd.fileinfo,parser_h_should_use_override_objc,FullTypeName(tdef(vmtpd.owner.defowner),nil))
|
||||
else
|
||||
MessagePos1(pd.fileinfo,parser_h_should_use_reintroduce_objc,FullTypeName(tdef(vmtpd.owner.defowner),nil));
|
||||
{ no new entry, but copy the message name if any from
|
||||
the procdef in the parent class }
|
||||
check_msg_str(vmtpd,pd);
|
||||
result:=true;
|
||||
exit;
|
||||
end;
|
||||
{ disable/hide old VMT entry }
|
||||
if updatevalues then
|
||||
vmtentryvis:=vis_hidden;
|
||||
end;
|
||||
end
|
||||
{ both are virtual? }
|
||||
else if (po_virtualmethod in pd.procoptions) and
|
||||
(po_virtualmethod in vmtpd.procoptions) then
|
||||
begin
|
||||
{ same parameter and return types (parameter specifiers will be checked below) }
|
||||
if hasequalpara and
|
||||
compatible_childmethod_resultdef(vmtpd.returndef,pd.returndef) then
|
||||
begin
|
||||
{ inherite calling convention when it was explicit and the
|
||||
current definition has none explicit set }
|
||||
if (po_hascallingconvention in vmtpd.procoptions) and
|
||||
not(po_hascallingconvention in pd.procoptions) then
|
||||
begin
|
||||
pd.proccalloption:=vmtpd.proccalloption;
|
||||
include(pd.procoptions,po_hascallingconvention);
|
||||
end;
|
||||
|
||||
{ All parameter specifiers and some procedure the flags have to match
|
||||
except abstract and override }
|
||||
if (compare_paras(vmtpd.paras,pd.paras,cp_all,[cpo_ignoreuniv])<te_equal) or
|
||||
(vmtpd.proccalloption<>pd.proccalloption) or
|
||||
(vmtpd.proctypeoption<>pd.proctypeoption) or
|
||||
((vmtpd.procoptions*po_comp)<>(pd.procoptions*po_comp)) then
|
||||
begin
|
||||
MessagePos1(pd.fileinfo,parser_e_header_dont_match_forward,pd.fullprocname(false));
|
||||
tprocsym(vmtpd.procsym).write_parameter_lists(pd);
|
||||
end;
|
||||
|
||||
check_msg_str(vmtpd,pd);
|
||||
|
||||
{ Give a note if the new visibility is lower. For a higher
|
||||
visibility update the vmt info }
|
||||
if vmtentryvis>pd.visibility then
|
||||
MessagePos4(pd.fileinfo,parser_n_ignore_lower_visibility,pd.fullprocname(false),
|
||||
visibilityname[pd.visibility],tobjectdef(vmtpd.owner.defowner).objrealname^,visibilityname[vmtentryvis])
|
||||
else if pd.visibility>vmtentryvis then
|
||||
begin
|
||||
if updatevalues then
|
||||
vmtentryvis:=pd.visibility;
|
||||
end;
|
||||
|
||||
{ override old virtual method in VMT }
|
||||
if updatevalues then
|
||||
begin
|
||||
if (vmtpd.extnumber<>i) then
|
||||
internalerror(200611084);
|
||||
pd.extnumber:=vmtpd.extnumber;
|
||||
vmtpd:=pd;
|
||||
end;
|
||||
result:=true;
|
||||
exit;
|
||||
end
|
||||
{ different parameters }
|
||||
else
|
||||
begin
|
||||
{ when we got an override directive then can search futher for
|
||||
the procedure to override.
|
||||
If we are starting a new virtual tree then hide the old tree }
|
||||
if not(po_overridingmethod in pd.procoptions) and
|
||||
not(pdoverload or hasoverloads) then
|
||||
begin
|
||||
if not(po_reintroduce in pd.procoptions) then
|
||||
begin
|
||||
if not is_object(_class) and
|
||||
not is_objc_class_or_protocol(_class) then
|
||||
MessagePos1(pd.fileinfo,parser_w_should_use_override,pd.fullprocname(false))
|
||||
else
|
||||
{ objects don't allow starting a new virtual tree
|
||||
and neither does Objective-C }
|
||||
MessagePos1(pd.fileinfo,parser_e_header_dont_match_forward,vmtpd.fullprocname(false));
|
||||
end;
|
||||
{ disable/hide old VMT entry }
|
||||
if updatevalues then
|
||||
vmtentryvis:=vis_hidden;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function found_category_method(st: tsymtable): boolean;
|
||||
var
|
||||
entrycount, procdefcount: longint;
|
||||
cat: tobjectdef;
|
||||
vmtpd: tprocdef;
|
||||
vmtvis: tvisibility;
|
||||
begin
|
||||
result:=false;
|
||||
if is_objccategory(tdef(st.defowner)) then
|
||||
begin
|
||||
cat:=tobjectdef(st.defowner);
|
||||
{ go through all of the category's methods to find the
|
||||
vmtentry corresponding to the procdef we are handling }
|
||||
for entrycount:=0 to cat.vmtentries.Count-1 do
|
||||
begin
|
||||
vmtpd:=pvmtentry(cat.vmtentries[entrycount])^.procdef;
|
||||
vmtvis:=pvmtentry(cat.vmtentries[entrycount])^.visibility;
|
||||
{ don't change the vmtentry of the category }
|
||||
if found_entry(vmtpd,vmtvis,false) then
|
||||
begin
|
||||
result:=true;
|
||||
exit;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
begin
|
||||
result:=false;
|
||||
{ Load other values for easier readability }
|
||||
@ -243,163 +446,22 @@ implementation
|
||||
{ compare with all stored definitions }
|
||||
for i:=0 to _class.vmtentries.Count-1 do
|
||||
begin
|
||||
vmtentry:=pvmtentry(_class.vmtentries[i]);
|
||||
vmtpd:=tprocdef(vmtentry^.procdef);
|
||||
|
||||
{ ignore hidden entries (e.g. virtual overridden by a static) that are not visible anymore }
|
||||
if vmtentry^.visibility=vis_hidden then
|
||||
continue;
|
||||
|
||||
{ ignore different names }
|
||||
if vmtpd.procsym.name<>pd.procsym.name then
|
||||
continue;
|
||||
|
||||
{ hide private methods that are not visible anymore. For this check we
|
||||
must override the visibility with the highest value in the override chain.
|
||||
This is required for case (see tw3292) with protected-private-protected where the
|
||||
same vmtentry is used (PFV) }
|
||||
if not is_visible_for_object(vmtpd.owner,vmtentry^.visibility,_class) then
|
||||
continue;
|
||||
|
||||
{ inherit overload }
|
||||
if (po_overload in vmtpd.procoptions) then
|
||||
begin
|
||||
include(pd.procoptions,po_overload);
|
||||
pdoverload:=true;
|
||||
end;
|
||||
|
||||
{ compare parameter types only, no specifiers yet }
|
||||
hasequalpara:=(compare_paras(vmtpd.paras,pd.paras,cp_none,[cpo_ignoreuniv])>=te_equal);
|
||||
|
||||
{ check that we are not trying to override a final method }
|
||||
if (po_finalmethod in vmtpd.procoptions) and
|
||||
hasequalpara and (po_overridingmethod in pd.procoptions) and is_class(_class) then
|
||||
MessagePos1(pd.fileinfo,parser_e_final_can_no_be_overridden,pd.fullprocname(false))
|
||||
else
|
||||
{ old definition has virtual
|
||||
new definition has no virtual or override }
|
||||
if (po_virtualmethod in vmtpd.procoptions) and
|
||||
(
|
||||
not(po_virtualmethod in pd.procoptions) or
|
||||
(
|
||||
{ new one does not have reintroduce in case of an objccategory }
|
||||
(is_objccategory(_class) and not(po_reintroduce in pd.procoptions)) or
|
||||
{ new one does not have override in case of objpas/objc class/intf/proto }
|
||||
(is_class_or_interface_or_objc(_class) and not is_objccategory(_class) and not(po_overridingmethod in pd.procoptions))
|
||||
)
|
||||
) then
|
||||
begin
|
||||
if (
|
||||
not(pdoverload or hasoverloads) or
|
||||
hasequalpara
|
||||
) then
|
||||
begin
|
||||
if not(po_reintroduce in pd.procoptions) then
|
||||
if not(is_objc_class_or_protocol(_class)) then
|
||||
MessagePos1(pd.fileinfo,parser_w_should_use_override,pd.fullprocname(false))
|
||||
else
|
||||
begin
|
||||
{ In Objective-C, you cannot create a new VMT entry to
|
||||
start a new inheritance tree. We therefore give an
|
||||
error when the class is implemented in Pascal, to
|
||||
avoid confusion due to things working differently
|
||||
with Object Pascal classes.
|
||||
|
||||
In case of external classes, we only give a hint,
|
||||
because requiring override everywhere may make
|
||||
automated header translation tools too complex. }
|
||||
if not(oo_is_external in _class.objectoptions) then
|
||||
if not is_objccategory(_class) then
|
||||
MessagePos1(pd.fileinfo,parser_e_must_use_override_objc,pd.fullprocname(false))
|
||||
else
|
||||
MessagePos1(pd.fileinfo,parser_e_must_use_reintroduce_objc,pd.fullprocname(false))
|
||||
{ there may be a lot of these in auto-translated
|
||||
heaeders, so only calculate the fullprocname if
|
||||
the hint will be shown }
|
||||
else if CheckVerbosity(V_Hint) then
|
||||
if not is_objccategory(_class) then
|
||||
MessagePos1(pd.fileinfo,parser_h_should_use_override_objc,pd.fullprocname(false))
|
||||
else
|
||||
MessagePos1(pd.fileinfo,parser_h_should_use_reintroduce_objc,pd.fullprocname(false));
|
||||
{ no new entry, but copy the message name if any from
|
||||
the procdef in the parent class }
|
||||
check_msg_str(vmtpd,pd);
|
||||
exit;
|
||||
end;
|
||||
{ disable/hide old VMT entry }
|
||||
vmtentry^.visibility:=vis_hidden;
|
||||
end;
|
||||
end
|
||||
{ both are virtual? }
|
||||
else if (po_virtualmethod in pd.procoptions) and
|
||||
(po_virtualmethod in vmtpd.procoptions) then
|
||||
begin
|
||||
{ same parameter and return types (parameter specifiers will be checked below) }
|
||||
if hasequalpara and
|
||||
compatible_childmethod_resultdef(vmtpd.returndef,pd.returndef) then
|
||||
begin
|
||||
{ inherite calling convention when it was explicit and the
|
||||
current definition has none explicit set }
|
||||
if (po_hascallingconvention in vmtpd.procoptions) and
|
||||
not(po_hascallingconvention in pd.procoptions) then
|
||||
begin
|
||||
pd.proccalloption:=vmtpd.proccalloption;
|
||||
include(pd.procoptions,po_hascallingconvention);
|
||||
end;
|
||||
|
||||
{ All parameter specifiers and some procedure the flags have to match
|
||||
except abstract and override }
|
||||
if (compare_paras(vmtpd.paras,pd.paras,cp_all,[cpo_ignoreuniv])<te_equal) or
|
||||
(vmtpd.proccalloption<>pd.proccalloption) or
|
||||
(vmtpd.proctypeoption<>pd.proctypeoption) or
|
||||
((vmtpd.procoptions*po_comp)<>(pd.procoptions*po_comp)) then
|
||||
begin
|
||||
MessagePos1(pd.fileinfo,parser_e_header_dont_match_forward,pd.fullprocname(false));
|
||||
tprocsym(vmtpd.procsym).write_parameter_lists(pd);
|
||||
end;
|
||||
|
||||
check_msg_str(vmtpd,pd);
|
||||
|
||||
{ Give a note if the new visibility is lower. For a higher
|
||||
visibility update the vmt info }
|
||||
if vmtentry^.visibility>pd.visibility then
|
||||
MessagePos4(pd.fileinfo,parser_n_ignore_lower_visibility,pd.fullprocname(false),
|
||||
visibilityname[pd.visibility],tobjectdef(vmtpd.owner.defowner).objrealname^,visibilityname[vmtentry^.visibility])
|
||||
else if pd.visibility>vmtentry^.visibility then
|
||||
vmtentry^.visibility:=pd.visibility;
|
||||
|
||||
{ override old virtual method in VMT }
|
||||
if (vmtpd.extnumber<>i) then
|
||||
internalerror(200611084);
|
||||
pd.extnumber:=vmtpd.extnumber;
|
||||
vmtentry^.procdef:=pd;
|
||||
exit;
|
||||
end
|
||||
{ different parameters }
|
||||
else
|
||||
begin
|
||||
{ when we got an override directive then can search futher for
|
||||
the procedure to override.
|
||||
If we are starting a new virtual tree then hide the old tree }
|
||||
if not(po_overridingmethod in pd.procoptions) and
|
||||
not(pdoverload or hasoverloads) then
|
||||
begin
|
||||
if not(po_reintroduce in pd.procoptions) then
|
||||
begin
|
||||
if not is_object(_class) and
|
||||
not is_objc_class_or_protocol(_class) then
|
||||
MessagePos1(pd.fileinfo,parser_w_should_use_override,pd.fullprocname(false))
|
||||
else
|
||||
{ objects don't allow starting a new virtual tree
|
||||
and neither does Objective-C }
|
||||
MessagePos1(pd.fileinfo,parser_e_header_dont_match_forward,vmtpd.fullprocname(false));
|
||||
end;
|
||||
{ disable/hide old VMT entry }
|
||||
vmtentry^.visibility:=vis_hidden;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
if found_entry(pvmtentry(_class.vmtentries[i])^.procdef, pvmtentry(_class.vmtentries[i])^.visibility,true) then
|
||||
exit;
|
||||
end;
|
||||
|
||||
{ in case of Objective-C, also check the categories that apply to this
|
||||
class' *parent* for methods to override (don't allow class X to
|
||||
"override" a method added by a category to class X itself, since in
|
||||
that case the category method will in fact replace class X'
|
||||
"overriding" method }
|
||||
if is_objcclass(_class) and
|
||||
assigned(_class.childof) and
|
||||
search_class_helper(_class.childof,pd.procsym.name,srsym,st) then
|
||||
begin
|
||||
overridesclasshelper:=found_category_method(st);
|
||||
end;
|
||||
|
||||
{ No entry found, we need to create a new entry }
|
||||
result:=true;
|
||||
end;
|
||||
@ -671,6 +733,7 @@ implementation
|
||||
i : longint;
|
||||
def : tdef;
|
||||
old_current_objectdef : tobjectdef;
|
||||
overridesclasshelper : boolean;
|
||||
begin
|
||||
old_current_objectdef:=current_objectdef;
|
||||
current_objectdef:=_class;
|
||||
@ -694,8 +757,8 @@ implementation
|
||||
if def.typ=procdef then
|
||||
begin
|
||||
{ VMT entry }
|
||||
if is_new_vmt_entry(tprocdef(def)) then
|
||||
add_new_vmt_entry(tprocdef(def));
|
||||
if is_new_vmt_entry(tprocdef(def),overridesclasshelper) then
|
||||
add_new_vmt_entry(tprocdef(def),overridesclasshelper);
|
||||
end;
|
||||
end;
|
||||
build_interface_mappings;
|
||||
|
||||
@ -15,8 +15,8 @@ type
|
||||
ta = objcclass(NSObject)
|
||||
a: longint;
|
||||
procedure taproc; message 'taproc';
|
||||
function tabaseproc(cp: longint): double; message 'tabaseproc:'; //override; -- override doesn't work, the compiler doesn't treat the category as part of NSObject
|
||||
class function taclassproc: longint; message 'taclassproc'; //override; -- idem
|
||||
function tabaseproc(cp: longint): double; message 'tabaseproc:'; override;
|
||||
class function taclassproc: longint; message 'taclassproc'; override;
|
||||
end;
|
||||
|
||||
ca = objccategory(ta)
|
||||
|
||||
47
tests/test/tobjc36.pp
Normal file
47
tests/test/tobjc36.pp
Normal file
@ -0,0 +1,47 @@
|
||||
{ %target=darwin }
|
||||
{ %cpu=powerpc,powerpc64,i386,x86_64,arm }
|
||||
|
||||
{ Written by Jonas Maebe in 2010, released into the public domain }
|
||||
|
||||
{$mode objfpc}
|
||||
{$modeswitch objectivec1}
|
||||
|
||||
// check whether we can override methods added to a class via a category
|
||||
// (mainly required because the way Apple deprecates methods is by moving
|
||||
// them from class definitions to NSDeprecated category definitions)
|
||||
|
||||
type
|
||||
MyCategory = objccategory(NSObject)
|
||||
procedure extraproc(a: longint); message 'extraproc:';
|
||||
end;
|
||||
|
||||
MyObject = objcclass(NSObject)
|
||||
// overrides extraproc added to NSObject
|
||||
procedure extraproc(a: longint); override;
|
||||
end;
|
||||
|
||||
procedure MyCategory.extraproc(a: longint);
|
||||
begin
|
||||
if a<>1 then
|
||||
halt(1);
|
||||
end;
|
||||
|
||||
procedure MyObject.extraproc(a: longint);
|
||||
begin
|
||||
if a<>2 then
|
||||
halt(2);
|
||||
inherited extraproc(1);
|
||||
end;
|
||||
|
||||
|
||||
var
|
||||
a: NSObject;
|
||||
b: MyObject;
|
||||
begin
|
||||
a:=NSObject.alloc.init;
|
||||
a.extraproc(1);
|
||||
a.release;
|
||||
b:=MyObject.alloc.init;
|
||||
b.extraproc(2);
|
||||
b.release;
|
||||
end.
|
||||
46
tests/test/tobjc36a.pp
Normal file
46
tests/test/tobjc36a.pp
Normal file
@ -0,0 +1,46 @@
|
||||
{ %target=darwin }
|
||||
{ %cpu=powerpc,powerpc64,i386,x86_64,arm }
|
||||
{ %fail }
|
||||
|
||||
{ Written by Jonas Maebe in 2010, released into the public domain }
|
||||
|
||||
{$mode objfpc}
|
||||
{$modeswitch objectivec1}
|
||||
|
||||
type
|
||||
MyObject2 = objcclass(NSObject)
|
||||
end;
|
||||
|
||||
MyCategory = objccategory(MyObject2)
|
||||
procedure extraproc(a: longint); message 'extraproc:';
|
||||
end;
|
||||
|
||||
MyObject = objcclass(NSObject)
|
||||
// overrides extraproc added to NSObject
|
||||
procedure extraproc(a: longint); override; message 'extraproc:';
|
||||
end;
|
||||
|
||||
procedure MyCategory.extraproc(a: longint);
|
||||
begin
|
||||
if a<>1 then
|
||||
halt(1);
|
||||
end;
|
||||
|
||||
procedure MyObject.extraproc(a: longint);
|
||||
begin
|
||||
if a<>2 then
|
||||
halt(2);
|
||||
end;
|
||||
|
||||
|
||||
var
|
||||
a: NSObject;
|
||||
b: MyObject;
|
||||
begin
|
||||
a:=NSObject.alloc.init;
|
||||
a.extraproc(1);
|
||||
a.release;
|
||||
b:=MyObject.alloc.init;
|
||||
b.extraproc(2);
|
||||
b.release;
|
||||
end.
|
||||
Loading…
Reference in New Issue
Block a user