mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-17 10:39:33 +02:00
Improvements to dispinterface property handling:
* Create implicit access methods, which hold type information for property parameters and allow parsing/typechecking occur the same way as for regular (non-dispinterface) properties. + Introduce separate proctypeoptions for property access methods. They are translated into correct dispatch call types and used to distinguish property access from regular method calls. * Bump PPU version because new information has been introduced. - Code specific to dispinterface properties in expression parser is no longer necessary, removed. * Allow access to default property with [] for dispinterfaces. + Extended the test with basic correctness checks for property dispatching. git-svn-id: trunk@16810 -
This commit is contained in:
parent
ba74d47081
commit
58f37dc952
@ -2601,6 +2601,7 @@ implementation
|
||||
is_const : boolean;
|
||||
statements : tstatementnode;
|
||||
converted_result_data : ttempcreatenode;
|
||||
calltype: tdispcalltype;
|
||||
label
|
||||
errorexit;
|
||||
begin
|
||||
@ -2994,6 +2995,12 @@ implementation
|
||||
{ dispinterface methode invoke? }
|
||||
if assigned(methodpointer) and is_dispinterface(methodpointer.resultdef) then
|
||||
begin
|
||||
case procdefinition.proctypeoption of
|
||||
potype_propgetter: calltype:=dct_propget;
|
||||
potype_propsetter: calltype:=dct_propput;
|
||||
else
|
||||
calltype:=dct_method;
|
||||
end;
|
||||
{ if the result is used, we've to insert a call to convert the type to be on the "safe side" }
|
||||
if (cnf_return_value_used in callnodeflags) and not is_void(procdefinition.returndef) then
|
||||
begin
|
||||
@ -3001,13 +3008,13 @@ implementation
|
||||
converted_result_data:=ctempcreatenode.create(procdefinition.returndef,sizeof(procdefinition.returndef),tt_persistent,true);
|
||||
addstatement(statements,converted_result_data);
|
||||
addstatement(statements,cassignmentnode.create(ctemprefnode.create(converted_result_data),
|
||||
ctypeconvnode.create_internal(translate_disp_call(methodpointer,parameters,dct_method,'',tprocdef(procdefinition).dispid,procdefinition.returndef),
|
||||
ctypeconvnode.create_internal(translate_disp_call(methodpointer,parameters,calltype,'',tprocdef(procdefinition).dispid,procdefinition.returndef),
|
||||
procdefinition.returndef)));
|
||||
addstatement(statements,ctempdeletenode.create_normal_temp(converted_result_data));
|
||||
addstatement(statements,ctemprefnode.create(converted_result_data));
|
||||
end
|
||||
else
|
||||
result:=translate_disp_call(methodpointer,parameters,dct_method,'',tprocdef(procdefinition).dispid,voidtype);
|
||||
result:=translate_disp_call(methodpointer,parameters,calltype,'',tprocdef(procdefinition).dispid,voidtype);
|
||||
|
||||
{ don't free reused nodes }
|
||||
methodpointer:=nil;
|
||||
|
@ -241,13 +241,30 @@ implementation
|
||||
(ppo_hasparameters in p.propoptions);
|
||||
end;
|
||||
|
||||
procedure parse_dispinterface(p : tpropertysym);
|
||||
procedure create_accessor_procsym(p: tpropertysym; pd: tprocdef; const prefix: string;
|
||||
accesstype: tpropaccesslisttypes);
|
||||
var
|
||||
sym: tprocsym;
|
||||
begin
|
||||
handle_calling_convention(pd);
|
||||
sym:=tprocsym.create(prefix+lower(p.realname));
|
||||
symtablestack.top.insert(sym);
|
||||
pd.procsym:=sym;
|
||||
include(pd.procoptions,po_dispid);
|
||||
include(pd.procoptions,po_global);
|
||||
pd.visibility:=vis_private;
|
||||
proc_add_definition(pd);
|
||||
p.propaccesslist[accesstype].addsym(sl_call,sym);
|
||||
p.propaccesslist[accesstype].procdef:=pd;
|
||||
end;
|
||||
|
||||
procedure parse_dispinterface(p : tpropertysym; readpd,writepd: tprocdef;
|
||||
var paranr: word);
|
||||
var
|
||||
{procsym: tprocsym;
|
||||
procdef: tprocdef;
|
||||
valuepara: tparavarsym;}
|
||||
hasread, haswrite: boolean;
|
||||
pt: tnode;
|
||||
hdispid: longint;
|
||||
hparavs: tparavarsym;
|
||||
begin
|
||||
p.propaccesslist[palt_read].clear;
|
||||
p.propaccesslist[palt_write].clear;
|
||||
@ -260,12 +277,6 @@ implementation
|
||||
else if try_to_consume(_WRITEONLY) then
|
||||
hasread:=false;
|
||||
|
||||
if hasread then
|
||||
include(p.propoptions, ppo_dispid_read);
|
||||
|
||||
if haswrite then
|
||||
include(p.propoptions, ppo_dispid_write);
|
||||
|
||||
if try_to_consume(_DISPID) then
|
||||
begin
|
||||
pt:=comp_expr(true,false);
|
||||
@ -273,16 +284,39 @@ implementation
|
||||
if (Tordconstnode(pt).value<int64(low(longint))) or (Tordconstnode(pt).value>int64(high(longint))) then
|
||||
message(parser_e_range_check_error)
|
||||
else
|
||||
p.dispid:=Tordconstnode(pt).value.svalue
|
||||
hdispid:=Tordconstnode(pt).value.svalue
|
||||
else
|
||||
Message(parser_e_dispid_must_be_ord_const);
|
||||
pt.free;
|
||||
end
|
||||
else
|
||||
p.dispid:=tobjectdef(astruct).get_next_dispid;
|
||||
hdispid:=tobjectdef(astruct).get_next_dispid;
|
||||
|
||||
{ COM property is simply a pair of methods, tagged with 'propertyget'
|
||||
and 'propertyset' flags (or a single method if access is restricted).
|
||||
Creating these implicit accessor methods also allows the rest of compiler
|
||||
to handle dispinterface properties the same way as regular ones. }
|
||||
if hasread then
|
||||
begin
|
||||
readpd.returndef:=p.propdef;
|
||||
readpd.dispid:=hdispid;
|
||||
readpd.proctypeoption:=potype_propgetter;
|
||||
create_accessor_procsym(p,readpd,'get$',palt_read);
|
||||
end;
|
||||
if haswrite then
|
||||
begin
|
||||
{ add an extra parameter, a placeholder of the value to set }
|
||||
inc(paranr);
|
||||
hparavs:=tparavarsym.create('$value',10*paranr,vs_value,p.propdef,[]);
|
||||
writepd.parast.insert(hparavs);
|
||||
|
||||
writepd.proctypeoption:=potype_propsetter;
|
||||
writepd.dispid:=hdispid;
|
||||
create_accessor_procsym(p,writepd,'put$',palt_write);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure add_index_parameter(var paranr: word; p: tpropertysym; readprocdef, writeprocdef: tprocvardef);
|
||||
procedure add_index_parameter(var paranr: word; p: tpropertysym; readprocdef, writeprocdef: tprocdef);
|
||||
var
|
||||
hparavs: tparavarsym;
|
||||
begin
|
||||
@ -310,21 +344,23 @@ implementation
|
||||
found : boolean;
|
||||
hreadparavs,
|
||||
hparavs : tparavarsym;
|
||||
storedprocdef,
|
||||
storedprocdef: tprocvardef;
|
||||
readprocdef,
|
||||
writeprocdef : tprocvardef;
|
||||
writeprocdef : tprocdef;
|
||||
begin
|
||||
{ Generate temp procvardefs to search for matching read/write
|
||||
{ Generate temp procdefs to search for matching read/write
|
||||
procedures. the readprocdef will store all definitions }
|
||||
paranr:=0;
|
||||
readprocdef:=tprocvardef.create(normal_function_level);
|
||||
writeprocdef:=tprocvardef.create(normal_function_level);
|
||||
readprocdef:=tprocdef.create(normal_function_level);
|
||||
writeprocdef:=tprocdef.create(normal_function_level);
|
||||
|
||||
{ make them method pointers }
|
||||
if assigned(astruct) and not is_classproperty then
|
||||
readprocdef.struct:=astruct;
|
||||
writeprocdef.struct:=astruct;
|
||||
|
||||
if assigned(astruct) and is_classproperty then
|
||||
begin
|
||||
include(readprocdef.procoptions,po_methodpointer);
|
||||
include(writeprocdef.procoptions,po_methodpointer);
|
||||
readprocdef.procoptions:=[po_staticmethod,po_classmethod];
|
||||
writeprocdef.procoptions:=[po_staticmethod,po_classmethod];
|
||||
end;
|
||||
|
||||
if token<>_ID then
|
||||
@ -577,7 +613,7 @@ implementation
|
||||
end;
|
||||
end
|
||||
else
|
||||
parse_dispinterface(p);
|
||||
parse_dispinterface(p,readprocdef,writeprocdef,paranr);
|
||||
|
||||
{ stored is not allowed for dispinterfaces, records or class properties }
|
||||
if assigned(astruct) and not(is_dispinterface(astruct) or is_record(astruct)) and not is_classproperty then
|
||||
@ -835,9 +871,11 @@ implementation
|
||||
message1(parser_e_implements_uses_non_implemented_interface,def.typename);
|
||||
end;
|
||||
|
||||
{ remove temporary procvardefs }
|
||||
readprocdef.owner.deletedef(readprocdef);
|
||||
writeprocdef.owner.deletedef(writeprocdef);
|
||||
{ remove unneeded procdefs }
|
||||
if readprocdef.proctypeoption<>potype_propgetter then
|
||||
readprocdef.owner.deletedef(readprocdef);
|
||||
if writeprocdef.proctypeoption<>potype_propsetter then
|
||||
writeprocdef.owner.deletedef(writeprocdef);
|
||||
|
||||
result:=p;
|
||||
end;
|
||||
|
@ -1053,8 +1053,6 @@ implementation
|
||||
callflags : tcallnodeflags;
|
||||
propaccesslist : tpropaccesslist;
|
||||
sym: tsym;
|
||||
statements : tstatementnode;
|
||||
converted_result_data : ttempcreatenode;
|
||||
begin
|
||||
{ property parameters? read them only if the property really }
|
||||
{ has parameters }
|
||||
@ -1121,16 +1119,6 @@ implementation
|
||||
end;
|
||||
end;
|
||||
end
|
||||
else
|
||||
if (ppo_dispid_write in propsym.propoptions) then
|
||||
begin
|
||||
consume(_ASSIGNMENT);
|
||||
p2:=comp_expr(true,false);
|
||||
{ concat value parameter too }
|
||||
p2:=ccallparanode.create(p2,paras);
|
||||
paras:=nil;
|
||||
p1:=translate_disp_call(p1,p2,dct_propput,'',propsym.dispid,voidtype);
|
||||
end
|
||||
else
|
||||
begin
|
||||
p1:=cerrornode.create;
|
||||
@ -1168,20 +1156,6 @@ implementation
|
||||
end;
|
||||
end;
|
||||
end
|
||||
else
|
||||
if (ppo_dispid_read in propsym.propoptions) then
|
||||
begin
|
||||
p2:=internalstatements(statements);
|
||||
converted_result_data:=ctempcreatenode.create(propsym.propdef,sizeof(propsym.propdef),tt_persistent,true);
|
||||
addstatement(statements,converted_result_data);
|
||||
addstatement(statements,cassignmentnode.create(ctemprefnode.create(converted_result_data),
|
||||
ctypeconvnode.create_internal(translate_disp_call(p1,paras,dct_propget,'',propsym.dispid,propsym.propdef),
|
||||
propsym.propdef)));
|
||||
addstatement(statements,ctempdeletenode.create_normal_temp(converted_result_data));
|
||||
addstatement(statements,ctemprefnode.create(converted_result_data));
|
||||
p1:=p2;
|
||||
paras:=nil;
|
||||
end
|
||||
else
|
||||
begin
|
||||
{ error, no function to read property }
|
||||
@ -1932,7 +1906,8 @@ implementation
|
||||
|
||||
_LECKKLAMMER:
|
||||
begin
|
||||
if is_class_or_interface_or_object(p1.resultdef) then
|
||||
if is_class_or_interface_or_object(p1.resultdef) or
|
||||
is_dispinterface(p1.resultdef) then
|
||||
begin
|
||||
{ default property }
|
||||
protsym:=search_default_property(tobjectdef(p1.resultdef));
|
||||
|
@ -43,7 +43,7 @@ type
|
||||
{$endif Test_Double_checksum}
|
||||
|
||||
const
|
||||
CurrentPPUVersion = 126;
|
||||
CurrentPPUVersion = 127;
|
||||
|
||||
{ buffer sizes }
|
||||
maxentrysize = 1024;
|
||||
|
@ -239,7 +239,9 @@ type
|
||||
potype_procedure,
|
||||
potype_function,
|
||||
potype_class_constructor, { class constructor }
|
||||
potype_class_destructor { class destructor }
|
||||
potype_class_destructor, { class destructor }
|
||||
potype_propgetter, { Dispinterface property accessors }
|
||||
potype_propsetter
|
||||
);
|
||||
tproctypeoptions=set of tproctypeoption;
|
||||
|
||||
@ -388,8 +390,8 @@ type
|
||||
ppo_hasparameters,
|
||||
ppo_implements,
|
||||
ppo_enumerator_current,
|
||||
ppo_dispid_read,
|
||||
ppo_dispid_write
|
||||
ppo_dispid_read, { no longer used }
|
||||
ppo_dispid_write { no longer used }
|
||||
);
|
||||
tpropertyoptions=set of tpropertyoption;
|
||||
|
||||
|
@ -22,12 +22,14 @@ type
|
||||
procedure DispArg1(Arg: IUnknown);
|
||||
procedure DispArg2(Arg: IDispatch);
|
||||
function DispArg3(var Arg: wordbool): widestring;
|
||||
property DispProp[index: OleVariant]: Integer;
|
||||
end;
|
||||
|
||||
var
|
||||
cur_dispid: longint;
|
||||
cur_argtype: byte;
|
||||
cur_restype: byte;
|
||||
cur_calltype: byte;
|
||||
|
||||
{$HINTS OFF}
|
||||
procedure DoDispCallByID(res: Pointer; const disp: IDispatch; desc: PDispDesc;
|
||||
@ -48,6 +50,25 @@ var
|
||||
halt($FF);
|
||||
end;
|
||||
|
||||
procedure DoDispCallByIDProp(res: Pointer; const disp: IDispatch; desc: PDispDesc;
|
||||
params: Pointer);
|
||||
begin
|
||||
if desc^.calldesc.calltype <> cur_calltype then
|
||||
halt(5);
|
||||
// put: arg #0 is value, arg #1 is index (in Delphi: vice-versa)
|
||||
// get: arg #0 is index
|
||||
if desc^.calldesc.argtypes[ord(cur_calltype=4)] <> cur_argtype then
|
||||
halt(6);
|
||||
if cur_calltype=4 then
|
||||
begin
|
||||
if desc^.calldesc.argcount <> 2 then
|
||||
halt(7);
|
||||
if desc^.calldesc.argtypes[0] <> cur_restype then
|
||||
halt(8);
|
||||
if desc^.restype <> 0 then
|
||||
halt(9);
|
||||
end;
|
||||
end;
|
||||
|
||||
{$HINTS ON}
|
||||
|
||||
@ -56,6 +77,7 @@ var
|
||||
B: wordbool;
|
||||
begin
|
||||
// check dispid values
|
||||
writeln('Testing dispid values...');
|
||||
DispCallByIDProc := @DoDispCallByID;
|
||||
cur_dispid := 300;
|
||||
II.Disp300;
|
||||
@ -66,6 +88,7 @@ begin
|
||||
cur_dispid := 402;
|
||||
II.Disp402 := True;
|
||||
// check arguments
|
||||
writeln('Testing arguments...');
|
||||
DispCallByIDProc := @DoDispCallByIDArg;
|
||||
cur_restype := varempty;
|
||||
cur_argtype := varunknown;
|
||||
@ -76,4 +99,17 @@ begin
|
||||
cur_argtype := varboolean or $80;
|
||||
B := False;
|
||||
II.DispArg3(B);
|
||||
|
||||
writeln('Testing properties...');
|
||||
DispCallByIDProc := @DoDispCallByIDProp;
|
||||
cur_calltype := 2; // propertyget
|
||||
cur_argtype := varvariant;
|
||||
cur_restype := varinteger;
|
||||
II.DispProp[1];
|
||||
II.DispProp['abc'];
|
||||
|
||||
cur_calltype := 4; // propertyput
|
||||
II.DispProp[1] := 11;
|
||||
II.DispProp['abc'] := 12;
|
||||
|
||||
end.
|
Loading…
Reference in New Issue
Block a user