mirror of
				https://gitlab.com/freepascal.org/fpc/source.git
				synced 2025-10-31 20:31:51 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			1055 lines
		
	
	
		
			40 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
			
		
		
	
	
			1055 lines
		
	
	
		
			40 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
| {
 | |
|     Copyright (c) 1998-2002 by Florian Klaempfl
 | |
| 
 | |
|     Does object types for Free Pascal
 | |
| 
 | |
|     This program 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 2 of the License, or
 | |
|     (at your option) any later version.
 | |
| 
 | |
|     This program 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.
 | |
| 
 | |
|     You should have received a copy of the GNU General Public License
 | |
|     along with this program; if not, write to the Free Software
 | |
|     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 | |
| 
 | |
|  ****************************************************************************
 | |
| }
 | |
| unit pdecobj;
 | |
| 
 | |
| {$i fpcdefs.inc}
 | |
| 
 | |
| interface
 | |
| 
 | |
|     uses
 | |
|       cclasses,
 | |
|       globtype,symconst,symtype,symdef;
 | |
| 
 | |
|     { parses a object declaration }
 | |
|     function object_dec(objecttype:tobjecttyp;const n:tidstring;genericdef:tstoreddef;genericlist:TFPObjectList;fd : tobjectdef) : tobjectdef;
 | |
| 
 | |
| implementation
 | |
| 
 | |
|     uses
 | |
|       cutils,
 | |
|       globals,verbose,systems,tokens,
 | |
|       symbase,symsym,symtable,
 | |
|       node,nld,nmem,ncon,ncnv,ncal,
 | |
|       fmodule,scanner,
 | |
|       pbase,pexpr,pdecsub,pdecvar,ptype,pdecl,ppu
 | |
|       ;
 | |
| 
 | |
|     const
 | |
|       { Please leave this here, this module should NOT use
 | |
|         these variables.
 | |
|         Declaring it as string here results in an error when compiling (PFV) }
 | |
|       current_procinfo = 'error';
 | |
| 
 | |
| 
 | |
|     function class_constructor_head:tprocdef;
 | |
|       var
 | |
|         pd : tprocdef;
 | |
|       begin
 | |
|         result:=nil;
 | |
|         consume(_CONSTRUCTOR);
 | |
|         { must be at same level as in implementation }
 | |
|         parse_proc_head(current_objectdef,potype_class_constructor,pd);
 | |
|         if not assigned(pd) then
 | |
|           begin
 | |
|             consume(_SEMICOLON);
 | |
|             exit;
 | |
|           end;
 | |
|         pd.calcparas;
 | |
|         if (pd.maxparacount>0) then
 | |
|           Message(parser_e_no_paras_for_class_constructor);
 | |
|         consume(_SEMICOLON);
 | |
|         include(current_objectdef.objectoptions,oo_has_class_constructor);
 | |
|         current_module.flags:=current_module.flags or uf_classinits;
 | |
|         { no return value }
 | |
|         pd.returndef:=voidtype;
 | |
|         result:=pd;
 | |
|       end;
 | |
| 
 | |
|     function constructor_head:tprocdef;
 | |
|       var
 | |
|         pd : tprocdef;
 | |
|       begin
 | |
|         result:=nil;
 | |
|         consume(_CONSTRUCTOR);
 | |
|         { must be at same level as in implementation }
 | |
|         parse_proc_head(current_objectdef,potype_constructor,pd);
 | |
|         if not assigned(pd) then
 | |
|           begin
 | |
|             consume(_SEMICOLON);
 | |
|             exit;
 | |
|           end;
 | |
|         if (cs_constructor_name in current_settings.globalswitches) and
 | |
|            (pd.procsym.name<>'INIT') then
 | |
|           Message(parser_e_constructorname_must_be_init);
 | |
|         consume(_SEMICOLON);
 | |
|         include(current_objectdef.objectoptions,oo_has_constructor);
 | |
|         { Set return type, class constructors return the
 | |
|           created instance, object constructors return boolean }
 | |
|         if is_class(pd._class) then
 | |
|           pd.returndef:=pd._class
 | |
|         else
 | |
| {$ifdef CPU64bitaddr}
 | |
|           pd.returndef:=bool64type;
 | |
| {$else CPU64bitaddr}
 | |
|           pd.returndef:=bool32type;
 | |
| {$endif CPU64bitaddr}
 | |
|         result:=pd;
 | |
|       end;
 | |
| 
 | |
| 
 | |
|     procedure property_dec(is_classproperty:boolean);
 | |
|       var
 | |
|         p : tpropertysym;
 | |
|       begin
 | |
|         { check for a class }
 | |
|         if not((is_class_or_interface_or_dispinterface(current_objectdef)) or
 | |
|            (not(m_tp7 in current_settings.modeswitches) and (is_object(current_objectdef)))) then
 | |
|           Message(parser_e_syntax_error);
 | |
|         consume(_PROPERTY);
 | |
|         p:=read_property_dec(is_classproperty, current_objectdef);
 | |
|         consume(_SEMICOLON);
 | |
|         if try_to_consume(_DEFAULT) then
 | |
|           begin
 | |
|             if oo_has_default_property in current_objectdef.objectoptions then
 | |
|               message(parser_e_only_one_default_property);
 | |
|             include(current_objectdef.objectoptions,oo_has_default_property);
 | |
|             include(p.propoptions,ppo_defaultproperty);
 | |
|             if not(ppo_hasparameters in p.propoptions) then
 | |
|               message(parser_e_property_need_paras);
 | |
|             if (token=_COLON) then
 | |
|               begin
 | |
|                 Message(parser_e_field_not_allowed_here);
 | |
|                 consume_all_until(_SEMICOLON);
 | |
|               end;
 | |
|             consume(_SEMICOLON);
 | |
|           end;
 | |
|         { parse possible enumerator modifier }
 | |
|         if try_to_consume(_ENUMERATOR) then
 | |
|           begin
 | |
|             if (token = _ID) then
 | |
|             begin
 | |
|               if pattern='CURRENT' then
 | |
|               begin
 | |
|                 if oo_has_enumerator_current in current_objectdef.objectoptions then
 | |
|                   message(parser_e_only_one_enumerator_current);
 | |
|                 if not p.propaccesslist[palt_read].empty then
 | |
|                 begin
 | |
|                   include(current_objectdef.objectoptions,oo_has_enumerator_current);
 | |
|                   include(p.propoptions,ppo_enumerator_current);
 | |
|                 end
 | |
|                 else
 | |
|                   Message(parser_e_enumerator_current_is_not_valid) // property has no reader
 | |
|               end
 | |
|               else
 | |
|                 Message1(parser_e_invalid_enumerator_identifier, pattern);
 | |
|               consume(token);
 | |
|             end
 | |
|             else
 | |
|               Message(parser_e_enumerator_identifier_required);
 | |
|             consume(_SEMICOLON);
 | |
|           end;
 | |
|         { hint directives, these can be separated by semicolons here,
 | |
|           that needs to be handled here with a loop (PFV) }
 | |
|         while try_consume_hintdirective(p.symoptions,p.deprecatedmsg) do
 | |
|           Consume(_SEMICOLON);
 | |
|       end;
 | |
| 
 | |
| 
 | |
|     function class_destructor_head:tprocdef;
 | |
|       var
 | |
|         pd : tprocdef;
 | |
|       begin
 | |
|         result:=nil;
 | |
|         consume(_DESTRUCTOR);
 | |
|         parse_proc_head(current_objectdef,potype_class_destructor,pd);
 | |
|         if not assigned(pd) then
 | |
|           begin
 | |
|             consume(_SEMICOLON);
 | |
|             exit;
 | |
|           end;
 | |
|         pd.calcparas;
 | |
|         if (pd.maxparacount>0) then
 | |
|           Message(parser_e_no_paras_for_class_destructor);
 | |
|         consume(_SEMICOLON);
 | |
|         include(current_objectdef.objectoptions,oo_has_class_destructor);
 | |
|         current_module.flags:=current_module.flags or uf_classinits;
 | |
|         { no return value }
 | |
|         pd.returndef:=voidtype;
 | |
|         result:=pd;
 | |
|       end;
 | |
| 
 | |
|     function destructor_head:tprocdef;
 | |
|       var
 | |
|         pd : tprocdef;
 | |
|       begin
 | |
|         result:=nil;
 | |
|         consume(_DESTRUCTOR);
 | |
|         parse_proc_head(current_objectdef,potype_destructor,pd);
 | |
|         if not assigned(pd) then
 | |
|           begin
 | |
|             consume(_SEMICOLON);
 | |
|             exit;
 | |
|           end;
 | |
|         if (cs_constructor_name in current_settings.globalswitches) and
 | |
|            (pd.procsym.name<>'DONE') then
 | |
|           Message(parser_e_destructorname_must_be_done);
 | |
|         pd.calcparas;
 | |
|         if not(pd.maxparacount=0) and
 | |
|            (m_fpc in current_settings.modeswitches) then
 | |
|           Message(parser_e_no_paras_for_destructor);
 | |
|         consume(_SEMICOLON);
 | |
|         include(current_objectdef.objectoptions,oo_has_destructor);
 | |
|         { no return value }
 | |
|         pd.returndef:=voidtype;
 | |
|         result:=pd;
 | |
|       end;
 | |
| 
 | |
| 
 | |
|     procedure setinterfacemethodoptions;
 | |
|       var
 | |
|         i   : longint;
 | |
|         def : tdef;
 | |
|       begin
 | |
|         include(current_objectdef.objectoptions,oo_has_virtual);
 | |
|         for i:=0 to current_objectdef.symtable.DefList.count-1 do
 | |
|           begin
 | |
|             def:=tdef(current_objectdef.symtable.DefList[i]);
 | |
|             if assigned(def) and
 | |
|                (def.typ=procdef) then
 | |
|               begin
 | |
|                 include(tprocdef(def).procoptions,po_virtualmethod);
 | |
|                 tprocdef(def).forwarddef:=false;
 | |
|               end;
 | |
|           end;
 | |
|       end;
 | |
| 
 | |
| 
 | |
|     procedure setobjcclassmethodoptions;
 | |
|       var
 | |
|         i   : longint;
 | |
|         def : tdef;
 | |
|       begin
 | |
|         for i:=0 to current_objectdef.symtable.DefList.count-1 do
 | |
|           begin
 | |
|             def:=tdef(current_objectdef.symtable.DefList[i]);
 | |
|             if assigned(def) and
 | |
|                (def.typ=procdef) then
 | |
|               begin
 | |
|                 include(tprocdef(def).procoptions,po_virtualmethod);
 | |
|               end;
 | |
|           end;
 | |
|       end;
 | |
| 
 | |
| 
 | |
|     procedure handleImplementedInterface(intfdef : tobjectdef);
 | |
|       begin
 | |
|         if not is_interface(intfdef) then
 | |
|           begin
 | |
|              Message1(type_e_interface_type_expected,intfdef.typename);
 | |
|              exit;
 | |
|           end;
 | |
|         if current_objectdef.find_implemented_interface(intfdef)<>nil then
 | |
|           Message1(sym_e_duplicate_id,intfdef.objname^)
 | |
|         else
 | |
|           begin
 | |
|             { allocate and prepare the GUID only if the class
 | |
|               implements some interfaces. }
 | |
|             if current_objectdef.ImplementedInterfaces.count = 0 then
 | |
|               current_objectdef.prepareguid;
 | |
|             current_objectdef.ImplementedInterfaces.Add(TImplementedInterface.Create(intfdef));
 | |
|           end;
 | |
|       end;
 | |
| 
 | |
| 
 | |
|     procedure handleImplementedProtocol(intfdef : tobjectdef);
 | |
|       begin
 | |
|         if not is_objcprotocol(intfdef) then
 | |
|           begin
 | |
|              Message1(type_e_protocol_type_expected,intfdef.typename);
 | |
|              exit;
 | |
|           end;
 | |
|         if current_objectdef.find_implemented_interface(intfdef)<>nil then
 | |
|           Message1(sym_e_duplicate_id,intfdef.objname^)
 | |
|         else
 | |
|           begin
 | |
|             current_objectdef.ImplementedInterfaces.Add(TImplementedInterface.Create(intfdef));
 | |
|           end;
 | |
|       end;
 | |
| 
 | |
| 
 | |
|     procedure readImplementedInterfacesAndProtocols(intf: boolean);
 | |
|       var
 | |
|         hdef : tdef;
 | |
|       begin
 | |
|         while try_to_consume(_COMMA) do
 | |
|           begin
 | |
|              id_type(hdef,false);
 | |
|              if (hdef.typ<>objectdef) then
 | |
|                begin
 | |
|                   if intf then
 | |
|                     Message1(type_e_interface_type_expected,hdef.typename)
 | |
|                   else
 | |
|                     Message1(type_e_protocol_type_expected,hdef.typename);
 | |
|                   continue;
 | |
|                end;
 | |
|              if intf then
 | |
|                handleImplementedInterface(tobjectdef(hdef))
 | |
|              else
 | |
|                handleImplementedProtocol(tobjectdef(hdef));
 | |
|           end;
 | |
|       end;
 | |
| 
 | |
| 
 | |
|     procedure readinterfaceiid;
 | |
|       var
 | |
|         p : tnode;
 | |
|         valid : boolean;
 | |
|       begin
 | |
|         p:=comp_expr(true);
 | |
|         if p.nodetype=stringconstn then
 | |
|           begin
 | |
|             stringdispose(current_objectdef.iidstr);
 | |
|             current_objectdef.iidstr:=stringdup(strpas(tstringconstnode(p).value_str));
 | |
|             valid:=string2guid(current_objectdef.iidstr^,current_objectdef.iidguid^);
 | |
|             if (current_objectdef.objecttype in [odt_interfacecom,odt_dispinterface]) and
 | |
|                not valid then
 | |
|               Message(parser_e_improper_guid_syntax);
 | |
|             include(current_objectdef.objectoptions,oo_has_valid_guid);
 | |
|           end
 | |
|         else
 | |
|           Message(parser_e_illegal_expression);
 | |
|         p.free;
 | |
|       end;
 | |
| 
 | |
|     procedure parse_object_options;
 | |
|       begin
 | |
|         if current_objectdef.objecttype in [odt_object,odt_class] then
 | |
|           begin
 | |
|             while true do
 | |
|               begin
 | |
|                 if try_to_consume(_ABSTRACT) then
 | |
|                   include(current_objectdef.objectoptions,oo_is_abstract)
 | |
|                 else
 | |
|                 if try_to_consume(_SEALED) then
 | |
|                   include(current_objectdef.objectoptions,oo_is_sealed)
 | |
|                 else
 | |
|                   break;
 | |
|               end;
 | |
|             if [oo_is_abstract, oo_is_sealed] * current_objectdef.objectoptions = [oo_is_abstract, oo_is_sealed] then
 | |
|               Message(parser_e_abstract_and_sealed_conflict);
 | |
|           end;
 | |
|       end;
 | |
| 
 | |
|     procedure parse_parent_classes;
 | |
|       var
 | |
|         intfchildof,
 | |
|         childof : tobjectdef;
 | |
|         hdef : tdef;
 | |
|         hasparentdefined : boolean;
 | |
|       begin
 | |
|         childof:=nil;
 | |
|         intfchildof:=nil;
 | |
|         hasparentdefined:=false;
 | |
| 
 | |
|         { reads the parent class }
 | |
|         if (token=_LKLAMMER) or
 | |
|            is_objccategory(current_objectdef) then
 | |
|           begin
 | |
|             consume(_LKLAMMER);
 | |
|             { use single_type instead of id_type for specialize support }
 | |
|             single_type(hdef,false,false);
 | |
|             if (not assigned(hdef)) or
 | |
|                (hdef.typ<>objectdef) then
 | |
|               begin
 | |
|                 if assigned(hdef) then
 | |
|                   Message1(type_e_class_type_expected,hdef.typename)
 | |
|                 else if is_objccategory(current_objectdef) then
 | |
|                   { a category must specify the class to extend }
 | |
|                   Message(type_e_objcclass_type_expected);
 | |
|               end
 | |
|             else
 | |
|               begin
 | |
|                 childof:=tobjectdef(hdef);
 | |
|                 { a mix of class, interfaces, objects and cppclasses
 | |
|                   isn't allowed }
 | |
|                 case current_objectdef.objecttype of
 | |
|                    odt_class:
 | |
|                      if not(is_class(childof)) then
 | |
|                        begin
 | |
|                           if is_interface(childof) then
 | |
|                             begin
 | |
|                                { we insert the interface after the child
 | |
|                                  is set, see below
 | |
|                                }
 | |
|                                intfchildof:=childof;
 | |
|                                childof:=class_tobject;
 | |
|                             end
 | |
|                           else
 | |
|                             Message(parser_e_mix_of_classes_and_objects);
 | |
|                        end
 | |
|                      else
 | |
|                        if oo_is_sealed in childof.objectoptions then
 | |
|                          Message1(parser_e_sealed_descendant,childof.typename);
 | |
|                    odt_interfacecorba,
 | |
|                    odt_interfacecom:
 | |
|                      begin
 | |
|                        if not(is_interface(childof)) then
 | |
|                          Message(parser_e_mix_of_classes_and_objects);
 | |
|                        current_objectdef.objecttype:=childof.objecttype;
 | |
|                      end;
 | |
|                    odt_cppclass:
 | |
|                      if not(is_cppclass(childof)) then
 | |
|                        Message(parser_e_mix_of_classes_and_objects);
 | |
|                    odt_objcclass:
 | |
|                      if not(is_objcclass(childof) or
 | |
|                         is_objccategory(childof)) then
 | |
|                        begin
 | |
|                          if is_objcprotocol(childof) then
 | |
|                            begin
 | |
|                              if not(oo_is_classhelper in current_objectdef.objectoptions) then
 | |
|                                begin
 | |
|                                  intfchildof:=childof;
 | |
|                                  childof:=nil;
 | |
|                                  CGMessage(parser_h_no_objc_parent);
 | |
|                                end
 | |
|                              else
 | |
|                                { a category must specify the class to extend }
 | |
|                                CGMessage(type_e_objcclass_type_expected);
 | |
|                            end
 | |
|                          else
 | |
|                            Message(parser_e_mix_of_classes_and_objects);
 | |
|                        end;
 | |
|                    odt_objcprotocol:
 | |
|                      begin
 | |
|                        if not(is_objcprotocol(childof)) then
 | |
|                          Message(parser_e_mix_of_classes_and_objects);
 | |
|                        intfchildof:=childof;
 | |
|                        childof:=nil;
 | |
|                      end;
 | |
|                    odt_object:
 | |
|                      if not(is_object(childof)) then
 | |
|                        Message(parser_e_mix_of_classes_and_objects)
 | |
|                      else
 | |
|                        if oo_is_sealed in childof.objectoptions then
 | |
|                          Message1(parser_e_sealed_descendant,childof.typename);
 | |
|                    odt_dispinterface:
 | |
|                      Message(parser_e_dispinterface_cant_have_parent);
 | |
|                 end;
 | |
|               end;
 | |
|             hasparentdefined:=true;
 | |
|           end;
 | |
| 
 | |
|         { no generic as parents }
 | |
|         if assigned(childof) and
 | |
|            (df_generic in childof.defoptions) then
 | |
|           begin
 | |
|             Message(parser_e_no_generics_as_types);
 | |
|             childof:=nil;
 | |
|           end;
 | |
| 
 | |
|         { if no parent class, then a class get tobject as parent }
 | |
|         if not assigned(childof) then
 | |
|           begin
 | |
|             case current_objectdef.objecttype of
 | |
|               odt_class:
 | |
|                 if current_objectdef<>class_tobject then
 | |
|                   childof:=class_tobject;
 | |
|               odt_interfacecom:
 | |
|                 if current_objectdef<>interface_iunknown then
 | |
|                   childof:=interface_iunknown;
 | |
|               odt_objcclass:
 | |
|                 CGMessage(parser_h_no_objc_parent);
 | |
|             end;
 | |
|           end;
 | |
| 
 | |
|         if assigned(childof) then
 | |
|           begin
 | |
|             { Forbid not completly defined objects to be used as parents. This will
 | |
|               also prevent circular loops of classes, because we set the forward flag
 | |
|               at the start of the new definition and will reset it below after the
 | |
|               parent has been set }
 | |
|             if not(oo_is_forward in childof.objectoptions) then
 | |
|               current_objectdef.set_parent(childof)
 | |
|             else
 | |
|               Message1(parser_e_forward_declaration_must_be_resolved,childof.objrealname^);
 | |
|           end;
 | |
| 
 | |
|         { remove forward flag, is resolved }
 | |
|         exclude(current_objectdef.objectoptions,oo_is_forward);
 | |
| 
 | |
|         if hasparentdefined then
 | |
|           begin
 | |
|             if current_objectdef.objecttype in [odt_class,odt_objcclass,odt_objcprotocol] then
 | |
|               begin
 | |
|                 if assigned(intfchildof) then
 | |
|                   if current_objectdef.objecttype=odt_class then
 | |
|                     handleImplementedInterface(intfchildof)
 | |
|                   else
 | |
|                     handleImplementedProtocol(intfchildof);
 | |
|                 readImplementedInterfacesAndProtocols(current_objectdef.objecttype=odt_class);
 | |
|               end;
 | |
|             consume(_RKLAMMER);
 | |
|           end;
 | |
|       end;
 | |
| 
 | |
| 
 | |
|     procedure parse_guid;
 | |
|       begin
 | |
|         { read GUID }
 | |
|         if (current_objectdef.objecttype in [odt_interfacecom,odt_interfacecorba,odt_dispinterface]) and
 | |
|            try_to_consume(_LECKKLAMMER) then
 | |
|           begin
 | |
|             readinterfaceiid;
 | |
|             consume(_RECKKLAMMER);
 | |
|           end
 | |
|         else if (current_objectdef.objecttype=odt_dispinterface) then
 | |
|           message(parser_e_dispinterface_needs_a_guid);
 | |
|       end;
 | |
| 
 | |
| 
 | |
|     procedure insert_generic_parameter_types(genericdef:tstoreddef;genericlist:TFPObjectList);
 | |
|       var
 | |
|         i : longint;
 | |
|         generictype : ttypesym;
 | |
|       begin
 | |
|         current_objectdef.genericdef:=genericdef;
 | |
|         if not assigned(genericlist) then
 | |
|           exit;
 | |
|         for i:=0 to genericlist.count-1 do
 | |
|           begin
 | |
|             generictype:=ttypesym(genericlist[i]);
 | |
|             if generictype.typedef.typ=undefineddef then
 | |
|               include(current_objectdef.defoptions,df_generic)
 | |
|             else
 | |
|               include(current_objectdef.defoptions,df_specialization);
 | |
|             symtablestack.top.insert(generictype);
 | |
|           end;
 | |
|        end;
 | |
| 
 | |
| 
 | |
|     procedure parse_object_members;
 | |
| 
 | |
|       procedure chkobjc(pd: tprocdef);
 | |
|         begin
 | |
|           if is_objc_class_or_protocol(pd._class) then
 | |
|             begin
 | |
|               include(pd.procoptions,po_objc);
 | |
|             end;
 | |
|         end;
 | |
| 
 | |
| 
 | |
|         procedure chkcpp(pd:tprocdef);
 | |
|           begin
 | |
|             { nothing currently }
 | |
|           end;
 | |
| 
 | |
|         procedure maybe_parse_hint_directives(pd:tprocdef);
 | |
|         var
 | |
|           dummysymoptions : tsymoptions;
 | |
|           deprecatedmsg : pshortstring;
 | |
|         begin
 | |
|           dummysymoptions:=[];
 | |
|           deprecatedmsg:=nil;
 | |
|           while try_consume_hintdirective(dummysymoptions,deprecatedmsg) do
 | |
|             Consume(_SEMICOLON);
 | |
|           if assigned(pd) then
 | |
|             begin
 | |
|               pd.symoptions:=pd.symoptions+dummysymoptions;
 | |
|               pd.deprecatedmsg:=deprecatedmsg;
 | |
|             end
 | |
|           else
 | |
|             stringdispose(deprecatedmsg);
 | |
|         end;
 | |
| 
 | |
|       var
 | |
|         pd : tprocdef;
 | |
|         has_destructor,
 | |
|         oldparse_only,
 | |
|         old_parse_generic : boolean;
 | |
|         object_member_blocktype : tblock_type;
 | |
|         fields_allowed, is_classdef, classfields: boolean;
 | |
|         vdoptions: tvar_dec_options;
 | |
|       begin
 | |
|         { empty class declaration ? }
 | |
|         if (current_objectdef.objecttype in [odt_class,odt_objcclass]) and
 | |
|            (token=_SEMICOLON) then
 | |
|           exit;
 | |
| 
 | |
|         old_parse_generic:=parse_generic;
 | |
| 
 | |
|         parse_generic:=(df_generic in current_objectdef.defoptions);
 | |
|         { in "publishable" classes the default access type is published }
 | |
|         if (oo_can_have_published in current_objectdef.objectoptions) then
 | |
|           current_objectdef.symtable.currentvisibility:=vis_published
 | |
|         else
 | |
|           current_objectdef.symtable.currentvisibility:=vis_public;
 | |
|         testcurobject:=1;
 | |
|         has_destructor:=false;
 | |
|         fields_allowed:=true;
 | |
|         is_classdef:=false;
 | |
|         classfields:=false;
 | |
|         object_member_blocktype:=bt_general;
 | |
|         repeat
 | |
|           case token of
 | |
|             _TYPE :
 | |
|               begin
 | |
|                 if (([df_generic,df_specialization]*current_objectdef.defoptions)=[]) and
 | |
|                    not(current_objectdef.objecttype in [odt_class,odt_object]) then
 | |
|                   Message(parser_e_type_var_const_only_in_generics_and_classes);
 | |
|                  consume(_TYPE);
 | |
|                  object_member_blocktype:=bt_type;
 | |
|               end;
 | |
|             _VAR :
 | |
|               begin
 | |
|                 if (([df_generic,df_specialization]*current_objectdef.defoptions)=[]) and
 | |
|                    not(current_objectdef.objecttype in [odt_class,odt_object]) then
 | |
|                   Message(parser_e_type_var_const_only_in_generics_and_classes);
 | |
|                 consume(_VAR);
 | |
|                 fields_allowed:=true;
 | |
|                 object_member_blocktype:=bt_general;
 | |
|                 classfields:=is_classdef;
 | |
|                 is_classdef:=false;
 | |
|               end;
 | |
|             _CONST:
 | |
|               begin
 | |
|                 if (([df_generic,df_specialization]*current_objectdef.defoptions)=[]) and
 | |
|                    not(current_objectdef.objecttype in [odt_class,odt_object]) then
 | |
|                   Message(parser_e_type_var_const_only_in_generics_and_classes);
 | |
|                 consume(_CONST);
 | |
|                 object_member_blocktype:=bt_const;
 | |
|               end;
 | |
|             _ID :
 | |
|               begin
 | |
|                 if is_objcprotocol(current_objectdef) and
 | |
|                    ((idtoken=_REQUIRED) or
 | |
|                     (idtoken=_OPTIONAL)) then
 | |
|                   begin
 | |
|                     current_objectdef.symtable.currentlyoptional:=(idtoken=_OPTIONAL);
 | |
|                     consume(idtoken)
 | |
|                   end
 | |
|                 else case idtoken of
 | |
|                   _PRIVATE :
 | |
|                     begin
 | |
|                       if is_interface(current_objectdef) or
 | |
|                          is_objc_protocol_or_category(current_objectdef) then
 | |
|                         Message(parser_e_no_access_specifier_in_interfaces);
 | |
|                        consume(_PRIVATE);
 | |
|                        current_objectdef.symtable.currentvisibility:=vis_private;
 | |
|                        include(current_objectdef.objectoptions,oo_has_private);
 | |
|                        fields_allowed:=true;
 | |
|                      end;
 | |
|                    _PROTECTED :
 | |
|                      begin
 | |
|                        if is_interface(current_objectdef) or
 | |
|                           is_objc_protocol_or_category(current_objectdef) then
 | |
|                          Message(parser_e_no_access_specifier_in_interfaces);
 | |
|                        consume(_PROTECTED);
 | |
|                        current_objectdef.symtable.currentvisibility:=vis_protected;
 | |
|                        include(current_objectdef.objectoptions,oo_has_protected);
 | |
|                        fields_allowed:=true;
 | |
|                      end;
 | |
|                    _PUBLIC :
 | |
|                      begin
 | |
|                        if is_interface(current_objectdef) or
 | |
|                           is_objc_protocol_or_category(current_objectdef) then
 | |
|                          Message(parser_e_no_access_specifier_in_interfaces);
 | |
|                        consume(_PUBLIC);
 | |
|                        current_objectdef.symtable.currentvisibility:=vis_public;
 | |
|                        fields_allowed:=true;
 | |
|                      end;
 | |
|                    _PUBLISHED :
 | |
|                      begin
 | |
|                        { we've to check for a pushlished section in non-  }
 | |
|                        { publishable classes later, if a real declaration }
 | |
|                        { this is the way, delphi does it                  }
 | |
|                        if is_interface(current_objectdef) then
 | |
|                          Message(parser_e_no_access_specifier_in_interfaces);
 | |
|                        { Objective-C classes do not support "published",
 | |
|                          as basically everything is published.  }
 | |
|                        if is_objc_class_or_protocol(current_objectdef) then
 | |
|                          Message(parser_e_no_objc_published);
 | |
|                        consume(_PUBLISHED);
 | |
|                        current_objectdef.symtable.currentvisibility:=vis_published;
 | |
|                        fields_allowed:=true;
 | |
|                      end;
 | |
|                    _STRICT :
 | |
|                      begin
 | |
|                        if is_interface(current_objectdef) or
 | |
|                           is_objc_protocol_or_category(current_objectdef) then
 | |
|                           Message(parser_e_no_access_specifier_in_interfaces);
 | |
|                         consume(_STRICT);
 | |
|                         if token=_ID then
 | |
|                           begin
 | |
|                             case idtoken of
 | |
|                               _PRIVATE:
 | |
|                                 begin
 | |
|                                   consume(_PRIVATE);
 | |
|                                   current_objectdef.symtable.currentvisibility:=vis_strictprivate;
 | |
|                                   include(current_objectdef.objectoptions,oo_has_strictprivate);
 | |
|                                 end;
 | |
|                               _PROTECTED:
 | |
|                                 begin
 | |
|                                   consume(_PROTECTED);
 | |
|                                   current_objectdef.symtable.currentvisibility:=vis_strictprotected;
 | |
|                                   include(current_objectdef.objectoptions,oo_has_strictprotected);
 | |
|                                 end;
 | |
|                               else
 | |
|                                 message(parser_e_protected_or_private_expected);
 | |
|                             end;
 | |
|                           end
 | |
|                         else
 | |
|                           message(parser_e_protected_or_private_expected);
 | |
|                         fields_allowed:=true;
 | |
|                       end;
 | |
|                     else
 | |
|                       begin
 | |
|                         if object_member_blocktype=bt_general then
 | |
|                           begin
 | |
|                             if is_interface(current_objectdef) or
 | |
|                                is_objc_protocol_or_category(current_objectdef) then
 | |
|                               Message(parser_e_no_vars_in_interfaces);
 | |
| 
 | |
|                             if (current_objectdef.symtable.currentvisibility=vis_published) and
 | |
|                                not(oo_can_have_published in current_objectdef.objectoptions) then
 | |
|                               Message(parser_e_cant_have_published);
 | |
|                             if (not fields_allowed) then
 | |
|                               Message(parser_e_field_not_allowed_here);
 | |
| 
 | |
|                             vdoptions:=[vd_object];
 | |
|                             if classfields then
 | |
|                               include(vdoptions,vd_class);
 | |
|                             read_record_fields(vdoptions);
 | |
|                           end
 | |
|                         else if object_member_blocktype=bt_type then
 | |
|                           types_dec(true)
 | |
|                         else if object_member_blocktype=bt_const then
 | |
|                           consts_dec(true)
 | |
|                         else
 | |
|                           internalerror(201001110);
 | |
|                       end;
 | |
|                 end;
 | |
|               end;
 | |
|             _PROPERTY :
 | |
|               begin
 | |
|                 property_dec(is_classdef);
 | |
|                 fields_allowed:=false;
 | |
|                 is_classdef:=false;
 | |
|               end;
 | |
|             _CLASS:
 | |
|               begin
 | |
|                 is_classdef:=false;
 | |
|                 { read class method }
 | |
|                 if try_to_consume(_CLASS) then
 | |
|                  begin
 | |
|                    { class modifier is only allowed for procedures, functions, }
 | |
|                    { constructors, destructors, fields and properties          }
 | |
|                    if not(token in [_FUNCTION,_PROCEDURE,_PROPERTY,_VAR,_CONSTRUCTOR,_DESTRUCTOR]) then
 | |
|                      Message(parser_e_procedure_or_function_expected);
 | |
| 
 | |
|                    if is_interface(current_objectdef) then
 | |
|                      Message(parser_e_no_static_method_in_interfaces)
 | |
|                    else
 | |
|                      { class methods are also allowed for Objective-C protocols }
 | |
|                      is_classdef:=true;
 | |
|                  end;
 | |
|               end;
 | |
|             _PROCEDURE,
 | |
|             _FUNCTION:
 | |
|               begin
 | |
|                 if (current_objectdef.symtable.currentvisibility=vis_published) and
 | |
|                    not(oo_can_have_published in current_objectdef.objectoptions) then
 | |
|                   Message(parser_e_cant_have_published);
 | |
| 
 | |
|                 oldparse_only:=parse_only;
 | |
|                 parse_only:=true;
 | |
|                 pd:=parse_proc_dec(is_classdef, current_objectdef);
 | |
| 
 | |
|                 { this is for error recovery as well as forward }
 | |
|                 { interface mappings, i.e. mapping to a method  }
 | |
|                 { which isn't declared yet                      }
 | |
|                 if assigned(pd) then
 | |
|                   begin
 | |
|                     parse_object_proc_directives(pd);
 | |
| 
 | |
|                     { check if dispid is set }
 | |
|                     if is_dispinterface(pd._class) and not (po_dispid in pd.procoptions) then
 | |
|                       begin
 | |
|                         pd.dispid:=pd._class.get_next_dispid;
 | |
|                         include(pd.procoptions, po_dispid);
 | |
|                       end;
 | |
| 
 | |
|                     { all Macintosh Object Pascal methods are virtual.  }
 | |
|                     { this can't be a class method, because macpas mode }
 | |
|                     { has no m_class                                    }
 | |
|                     if (m_mac in current_settings.modeswitches) then
 | |
|                       include(pd.procoptions,po_virtualmethod);
 | |
| 
 | |
|                     handle_calling_convention(pd);
 | |
| 
 | |
|                     { add definition to procsym }
 | |
|                     proc_add_definition(pd);
 | |
| 
 | |
|                     { add procdef options to objectdef options }
 | |
|                     if (po_msgint in pd.procoptions) then
 | |
|                       include(current_objectdef.objectoptions,oo_has_msgint);
 | |
|                     if (po_msgstr in pd.procoptions) then
 | |
|                       include(current_objectdef.objectoptions,oo_has_msgstr);
 | |
|                     if (po_virtualmethod in pd.procoptions) then
 | |
|                       include(current_objectdef.objectoptions,oo_has_virtual);
 | |
| 
 | |
|                     chkcpp(pd);
 | |
|                     chkobjc(pd);
 | |
|                   end;
 | |
| 
 | |
|                 maybe_parse_hint_directives(pd);
 | |
| 
 | |
|                 parse_only:=oldparse_only;
 | |
|                 fields_allowed:=false;
 | |
|                 is_classdef:=false;
 | |
|               end;
 | |
|             _CONSTRUCTOR :
 | |
|               begin
 | |
|                 if (current_objectdef.symtable.currentvisibility=vis_published) and
 | |
|                   not(oo_can_have_published in current_objectdef.objectoptions) then
 | |
|                   Message(parser_e_cant_have_published);
 | |
| 
 | |
|                 if not is_classdef and not(current_objectdef.symtable.currentvisibility in [vis_public,vis_published]) then
 | |
|                   Message(parser_w_constructor_should_be_public);
 | |
| 
 | |
|                 if is_interface(current_objectdef) then
 | |
|                   Message(parser_e_no_con_des_in_interfaces);
 | |
| 
 | |
|                 { Objective-C does not know the concept of a constructor }
 | |
|                 if is_objc_class_or_protocol(current_objectdef) then
 | |
|                   Message(parser_e_objc_no_constructor_destructor);
 | |
| 
 | |
|                 { only 1 class constructor is allowed }
 | |
|                 if is_classdef and (oo_has_class_constructor in current_objectdef.objectoptions) then
 | |
|                   Message1(parser_e_only_one_class_constructor_allowed, current_objectdef.objrealname^);
 | |
| 
 | |
|                 oldparse_only:=parse_only;
 | |
|                 parse_only:=true;
 | |
|                 if is_classdef then
 | |
|                   pd:=class_constructor_head
 | |
|                 else
 | |
|                   pd:=constructor_head;
 | |
|                 parse_object_proc_directives(pd);
 | |
|                 handle_calling_convention(pd);
 | |
| 
 | |
|                 { add definition to procsym }
 | |
|                 proc_add_definition(pd);
 | |
| 
 | |
|                 { add procdef options to objectdef options }
 | |
|                 if (po_virtualmethod in pd.procoptions) then
 | |
|                   include(current_objectdef.objectoptions,oo_has_virtual);
 | |
|                 chkcpp(pd);
 | |
|                 maybe_parse_hint_directives(pd);
 | |
| 
 | |
|                 parse_only:=oldparse_only;
 | |
|                 fields_allowed:=false;
 | |
|                 is_classdef:=false;
 | |
|               end;
 | |
|             _DESTRUCTOR :
 | |
|               begin
 | |
|                 if (current_objectdef.symtable.currentvisibility=vis_published) and
 | |
|                    not(oo_can_have_published in current_objectdef.objectoptions) then
 | |
|                   Message(parser_e_cant_have_published);
 | |
| 
 | |
|                 if not is_classdef then
 | |
|                   if has_destructor then
 | |
|                     Message(parser_n_only_one_destructor)
 | |
|                   else
 | |
|                     has_destructor:=true;
 | |
| 
 | |
|                 if is_interface(current_objectdef) then
 | |
|                   Message(parser_e_no_con_des_in_interfaces);
 | |
| 
 | |
|                 if not is_classdef and (current_objectdef.symtable.currentvisibility<>vis_public) then
 | |
|                   Message(parser_w_destructor_should_be_public);
 | |
| 
 | |
|                 { Objective-C does not know the concept of a destructor }
 | |
|                 if is_objc_class_or_protocol(current_objectdef) then
 | |
|                   Message(parser_e_objc_no_constructor_destructor);
 | |
| 
 | |
|                 { only 1 class destructor is allowed }
 | |
|                 if is_classdef and (oo_has_class_destructor in current_objectdef.objectoptions) then
 | |
|                   Message1(parser_e_only_one_class_destructor_allowed, current_objectdef.objrealname^);
 | |
| 
 | |
|                 oldparse_only:=parse_only;
 | |
|                 parse_only:=true;
 | |
|                 if is_classdef then
 | |
|                   pd:=class_destructor_head
 | |
|                 else
 | |
|                   pd:=destructor_head;
 | |
|                 parse_object_proc_directives(pd);
 | |
|                 handle_calling_convention(pd);
 | |
| 
 | |
|                 { add definition to procsym }
 | |
|                 proc_add_definition(pd);
 | |
| 
 | |
|                 { add procdef options to objectdef options }
 | |
|                 if (po_virtualmethod in pd.procoptions) then
 | |
|                   include(current_objectdef.objectoptions,oo_has_virtual);
 | |
| 
 | |
|                 chkcpp(pd);
 | |
|                 maybe_parse_hint_directives(pd);
 | |
| 
 | |
|                 parse_only:=oldparse_only;
 | |
|                 fields_allowed:=false;
 | |
|                 is_classdef:=false;
 | |
|               end;
 | |
|             _END :
 | |
|               begin
 | |
|                 consume(_END);
 | |
|                 break;
 | |
|               end;
 | |
|             else
 | |
|               consume(_ID); { Give a ident expected message, like tp7 }
 | |
|           end;
 | |
|         until false;
 | |
| 
 | |
|         { restore }
 | |
|         testcurobject:=0;
 | |
|         parse_generic:=old_parse_generic;
 | |
|       end;
 | |
| 
 | |
| 
 | |
|     function object_dec(objecttype:tobjecttyp;const n:tidstring;genericdef:tstoreddef;genericlist:TFPObjectList;fd : tobjectdef) : tobjectdef;
 | |
|       var
 | |
|         old_current_objectdef : tobjectdef;
 | |
|       begin
 | |
|         old_current_objectdef:=current_objectdef;
 | |
| 
 | |
|         current_objectdef:=nil;
 | |
| 
 | |
|         { objects and class types can't be declared local }
 | |
|         if not(symtablestack.top.symtabletype in [globalsymtable,staticsymtable,objectsymtable]) and
 | |
|            not assigned(genericlist) then
 | |
|           Message(parser_e_no_local_objects);
 | |
| 
 | |
|         { reuse forward objectdef? }
 | |
|         if assigned(fd) then
 | |
|           begin
 | |
|             if fd.objecttype<>objecttype then
 | |
|               begin
 | |
|                 Message(parser_e_forward_mismatch);
 | |
|                 { recover }
 | |
|                 current_objectdef:=tobjectdef.create(current_objectdef.objecttype,n,nil);
 | |
|                 include(current_objectdef.objectoptions,oo_is_forward);
 | |
|               end
 | |
|             else
 | |
|               current_objectdef:=fd
 | |
|           end
 | |
|         else
 | |
|           begin
 | |
|             { anonym objects aren't allow (o : object a : longint; end;) }
 | |
|             if n='' then
 | |
|               Message(parser_f_no_anonym_objects);
 | |
| 
 | |
|             { create new class }
 | |
|             current_objectdef:=tobjectdef.create(objecttype,n,nil);
 | |
| 
 | |
|             { include always the forward flag, it'll be removed after the parent class have been
 | |
|               added. This is to prevent circular childof loops }
 | |
|             include(current_objectdef.objectoptions,oo_is_forward);
 | |
| 
 | |
|             if (cs_compilesystem in current_settings.moduleswitches) then
 | |
|               begin
 | |
|                 case current_objectdef.objecttype of
 | |
|                   odt_interfacecom :
 | |
|                     if (current_objectdef.objname^='IUNKNOWN') then
 | |
|                       interface_iunknown:=current_objectdef;
 | |
|                   odt_class :
 | |
|                     if (current_objectdef.objname^='TOBJECT') then
 | |
|                       class_tobject:=current_objectdef;
 | |
|                 end;
 | |
|               end;
 | |
|             if (current_module.modulename^='OBJCBASE') then
 | |
|               begin
 | |
|                 case current_objectdef.objecttype of
 | |
|                   odt_objcclass:
 | |
|                     if (current_objectdef.objname^='Protocol') then
 | |
|                       objc_protocoltype:=current_objectdef;
 | |
|                 end;
 | |
|               end;
 | |
|           end;
 | |
| 
 | |
|         { set published flag in $M+ mode, it can also be inherited and will
 | |
|           be added when the parent class set with tobjectdef.set_parent (PFV) }
 | |
|         if (cs_generate_rtti in current_settings.localswitches) and
 | |
|            (current_objectdef.objecttype in [odt_interfacecom,odt_class]) then
 | |
|           include(current_objectdef.objectoptions,oo_can_have_published);
 | |
| 
 | |
|         { forward def? }
 | |
|         if not assigned(fd) and
 | |
|            (token=_SEMICOLON) then
 | |
|           begin
 | |
|             { add to the list of definitions to check that the forward
 | |
|               is resolved. this is required for delphi mode }
 | |
|             current_module.checkforwarddefs.add(current_objectdef);
 | |
|           end
 | |
|         else
 | |
|           begin
 | |
|             { change objccategories into objcclass helpers }
 | |
|             if (objecttype=odt_objccategory) then
 | |
|               begin
 | |
|                 current_objectdef.objecttype:=odt_objcclass;
 | |
|                 include(current_objectdef.objectoptions,oo_is_classhelper);
 | |
|               end;
 | |
| 
 | |
|             { parse list of options (abstract / sealed) }
 | |
|             parse_object_options;
 | |
| 
 | |
|             { parse list of parent classes }
 | |
|             parse_parent_classes;
 | |
| 
 | |
|             { parse optional GUID for interfaces }
 | |
|             parse_guid;
 | |
| 
 | |
|             { parse and insert object members }
 | |
|             symtablestack.push(current_objectdef.symtable);
 | |
|             insert_generic_parameter_types(genericdef,genericlist);
 | |
|             parse_object_members;
 | |
|             symtablestack.pop(current_objectdef.symtable);
 | |
|           end;
 | |
| 
 | |
|         { generate vmt space if needed }
 | |
|         if not(oo_has_vmt in current_objectdef.objectoptions) and
 | |
|            not(oo_is_forward in current_objectdef.objectoptions) and
 | |
|            (
 | |
|             ([oo_has_virtual,oo_has_constructor,oo_has_destructor]*current_objectdef.objectoptions<>[]) or
 | |
|             (current_objectdef.objecttype in [odt_class])
 | |
|            ) then
 | |
|           current_objectdef.insertvmt;
 | |
| 
 | |
|         { for implemented classes with a vmt check if there is a constructor }
 | |
|         if (oo_has_vmt in current_objectdef.objectoptions) and
 | |
|            not(oo_is_forward in current_objectdef.objectoptions) and
 | |
|            not(oo_has_constructor in current_objectdef.objectoptions) and
 | |
|            not is_objc_class_or_protocol(current_objectdef) then
 | |
|           Message1(parser_w_virtual_without_constructor,current_objectdef.objrealname^);
 | |
| 
 | |
|         if is_interface(current_objectdef) or
 | |
|            is_objcprotocol(current_objectdef) then
 | |
|           setinterfacemethodoptions
 | |
|         else if is_objcclass(current_objectdef) then
 | |
|           setobjcclassmethodoptions;
 | |
| 
 | |
|         { return defined objectdef }
 | |
|         result:=current_objectdef;
 | |
| 
 | |
|         { restore old state }
 | |
|         current_objectdef:=old_current_objectdef;
 | |
|       end;
 | |
| 
 | |
| end.
 | 
