mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-08-18 12:49:12 +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;
|
is_const : boolean;
|
||||||
statements : tstatementnode;
|
statements : tstatementnode;
|
||||||
converted_result_data : ttempcreatenode;
|
converted_result_data : ttempcreatenode;
|
||||||
|
calltype: tdispcalltype;
|
||||||
label
|
label
|
||||||
errorexit;
|
errorexit;
|
||||||
begin
|
begin
|
||||||
@ -2994,6 +2995,12 @@ implementation
|
|||||||
{ dispinterface methode invoke? }
|
{ dispinterface methode invoke? }
|
||||||
if assigned(methodpointer) and is_dispinterface(methodpointer.resultdef) then
|
if assigned(methodpointer) and is_dispinterface(methodpointer.resultdef) then
|
||||||
begin
|
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 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
|
if (cnf_return_value_used in callnodeflags) and not is_void(procdefinition.returndef) then
|
||||||
begin
|
begin
|
||||||
@ -3001,13 +3008,13 @@ implementation
|
|||||||
converted_result_data:=ctempcreatenode.create(procdefinition.returndef,sizeof(procdefinition.returndef),tt_persistent,true);
|
converted_result_data:=ctempcreatenode.create(procdefinition.returndef,sizeof(procdefinition.returndef),tt_persistent,true);
|
||||||
addstatement(statements,converted_result_data);
|
addstatement(statements,converted_result_data);
|
||||||
addstatement(statements,cassignmentnode.create(ctemprefnode.create(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)));
|
procdefinition.returndef)));
|
||||||
addstatement(statements,ctempdeletenode.create_normal_temp(converted_result_data));
|
addstatement(statements,ctempdeletenode.create_normal_temp(converted_result_data));
|
||||||
addstatement(statements,ctemprefnode.create(converted_result_data));
|
addstatement(statements,ctemprefnode.create(converted_result_data));
|
||||||
end
|
end
|
||||||
else
|
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 }
|
{ don't free reused nodes }
|
||||||
methodpointer:=nil;
|
methodpointer:=nil;
|
||||||
|
@ -241,13 +241,30 @@ implementation
|
|||||||
(ppo_hasparameters in p.propoptions);
|
(ppo_hasparameters in p.propoptions);
|
||||||
end;
|
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
|
var
|
||||||
{procsym: tprocsym;
|
|
||||||
procdef: tprocdef;
|
|
||||||
valuepara: tparavarsym;}
|
|
||||||
hasread, haswrite: boolean;
|
hasread, haswrite: boolean;
|
||||||
pt: tnode;
|
pt: tnode;
|
||||||
|
hdispid: longint;
|
||||||
|
hparavs: tparavarsym;
|
||||||
begin
|
begin
|
||||||
p.propaccesslist[palt_read].clear;
|
p.propaccesslist[palt_read].clear;
|
||||||
p.propaccesslist[palt_write].clear;
|
p.propaccesslist[palt_write].clear;
|
||||||
@ -260,12 +277,6 @@ implementation
|
|||||||
else if try_to_consume(_WRITEONLY) then
|
else if try_to_consume(_WRITEONLY) then
|
||||||
hasread:=false;
|
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
|
if try_to_consume(_DISPID) then
|
||||||
begin
|
begin
|
||||||
pt:=comp_expr(true,false);
|
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
|
if (Tordconstnode(pt).value<int64(low(longint))) or (Tordconstnode(pt).value>int64(high(longint))) then
|
||||||
message(parser_e_range_check_error)
|
message(parser_e_range_check_error)
|
||||||
else
|
else
|
||||||
p.dispid:=Tordconstnode(pt).value.svalue
|
hdispid:=Tordconstnode(pt).value.svalue
|
||||||
else
|
else
|
||||||
Message(parser_e_dispid_must_be_ord_const);
|
Message(parser_e_dispid_must_be_ord_const);
|
||||||
pt.free;
|
pt.free;
|
||||||
end
|
end
|
||||||
else
|
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;
|
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
|
var
|
||||||
hparavs: tparavarsym;
|
hparavs: tparavarsym;
|
||||||
begin
|
begin
|
||||||
@ -310,21 +344,23 @@ implementation
|
|||||||
found : boolean;
|
found : boolean;
|
||||||
hreadparavs,
|
hreadparavs,
|
||||||
hparavs : tparavarsym;
|
hparavs : tparavarsym;
|
||||||
storedprocdef,
|
storedprocdef: tprocvardef;
|
||||||
readprocdef,
|
readprocdef,
|
||||||
writeprocdef : tprocvardef;
|
writeprocdef : tprocdef;
|
||||||
begin
|
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 }
|
procedures. the readprocdef will store all definitions }
|
||||||
paranr:=0;
|
paranr:=0;
|
||||||
readprocdef:=tprocvardef.create(normal_function_level);
|
readprocdef:=tprocdef.create(normal_function_level);
|
||||||
writeprocdef:=tprocvardef.create(normal_function_level);
|
writeprocdef:=tprocdef.create(normal_function_level);
|
||||||
|
|
||||||
{ make them method pointers }
|
readprocdef.struct:=astruct;
|
||||||
if assigned(astruct) and not is_classproperty then
|
writeprocdef.struct:=astruct;
|
||||||
|
|
||||||
|
if assigned(astruct) and is_classproperty then
|
||||||
begin
|
begin
|
||||||
include(readprocdef.procoptions,po_methodpointer);
|
readprocdef.procoptions:=[po_staticmethod,po_classmethod];
|
||||||
include(writeprocdef.procoptions,po_methodpointer);
|
writeprocdef.procoptions:=[po_staticmethod,po_classmethod];
|
||||||
end;
|
end;
|
||||||
|
|
||||||
if token<>_ID then
|
if token<>_ID then
|
||||||
@ -577,7 +613,7 @@ implementation
|
|||||||
end;
|
end;
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
parse_dispinterface(p);
|
parse_dispinterface(p,readprocdef,writeprocdef,paranr);
|
||||||
|
|
||||||
{ stored is not allowed for dispinterfaces, records or class properties }
|
{ 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
|
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);
|
message1(parser_e_implements_uses_non_implemented_interface,def.typename);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ remove temporary procvardefs }
|
{ remove unneeded procdefs }
|
||||||
readprocdef.owner.deletedef(readprocdef);
|
if readprocdef.proctypeoption<>potype_propgetter then
|
||||||
writeprocdef.owner.deletedef(writeprocdef);
|
readprocdef.owner.deletedef(readprocdef);
|
||||||
|
if writeprocdef.proctypeoption<>potype_propsetter then
|
||||||
|
writeprocdef.owner.deletedef(writeprocdef);
|
||||||
|
|
||||||
result:=p;
|
result:=p;
|
||||||
end;
|
end;
|
||||||
|
@ -1053,8 +1053,6 @@ implementation
|
|||||||
callflags : tcallnodeflags;
|
callflags : tcallnodeflags;
|
||||||
propaccesslist : tpropaccesslist;
|
propaccesslist : tpropaccesslist;
|
||||||
sym: tsym;
|
sym: tsym;
|
||||||
statements : tstatementnode;
|
|
||||||
converted_result_data : ttempcreatenode;
|
|
||||||
begin
|
begin
|
||||||
{ property parameters? read them only if the property really }
|
{ property parameters? read them only if the property really }
|
||||||
{ has parameters }
|
{ has parameters }
|
||||||
@ -1121,16 +1119,6 @@ implementation
|
|||||||
end;
|
end;
|
||||||
end;
|
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
|
else
|
||||||
begin
|
begin
|
||||||
p1:=cerrornode.create;
|
p1:=cerrornode.create;
|
||||||
@ -1168,20 +1156,6 @@ implementation
|
|||||||
end;
|
end;
|
||||||
end;
|
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
|
else
|
||||||
begin
|
begin
|
||||||
{ error, no function to read property }
|
{ error, no function to read property }
|
||||||
@ -1932,7 +1906,8 @@ implementation
|
|||||||
|
|
||||||
_LECKKLAMMER:
|
_LECKKLAMMER:
|
||||||
begin
|
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
|
begin
|
||||||
{ default property }
|
{ default property }
|
||||||
protsym:=search_default_property(tobjectdef(p1.resultdef));
|
protsym:=search_default_property(tobjectdef(p1.resultdef));
|
||||||
|
@ -43,7 +43,7 @@ type
|
|||||||
{$endif Test_Double_checksum}
|
{$endif Test_Double_checksum}
|
||||||
|
|
||||||
const
|
const
|
||||||
CurrentPPUVersion = 126;
|
CurrentPPUVersion = 127;
|
||||||
|
|
||||||
{ buffer sizes }
|
{ buffer sizes }
|
||||||
maxentrysize = 1024;
|
maxentrysize = 1024;
|
||||||
|
@ -239,7 +239,9 @@ type
|
|||||||
potype_procedure,
|
potype_procedure,
|
||||||
potype_function,
|
potype_function,
|
||||||
potype_class_constructor, { class constructor }
|
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;
|
tproctypeoptions=set of tproctypeoption;
|
||||||
|
|
||||||
@ -388,8 +390,8 @@ type
|
|||||||
ppo_hasparameters,
|
ppo_hasparameters,
|
||||||
ppo_implements,
|
ppo_implements,
|
||||||
ppo_enumerator_current,
|
ppo_enumerator_current,
|
||||||
ppo_dispid_read,
|
ppo_dispid_read, { no longer used }
|
||||||
ppo_dispid_write
|
ppo_dispid_write { no longer used }
|
||||||
);
|
);
|
||||||
tpropertyoptions=set of tpropertyoption;
|
tpropertyoptions=set of tpropertyoption;
|
||||||
|
|
||||||
|
@ -22,12 +22,14 @@ type
|
|||||||
procedure DispArg1(Arg: IUnknown);
|
procedure DispArg1(Arg: IUnknown);
|
||||||
procedure DispArg2(Arg: IDispatch);
|
procedure DispArg2(Arg: IDispatch);
|
||||||
function DispArg3(var Arg: wordbool): widestring;
|
function DispArg3(var Arg: wordbool): widestring;
|
||||||
|
property DispProp[index: OleVariant]: Integer;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
var
|
var
|
||||||
cur_dispid: longint;
|
cur_dispid: longint;
|
||||||
cur_argtype: byte;
|
cur_argtype: byte;
|
||||||
cur_restype: byte;
|
cur_restype: byte;
|
||||||
|
cur_calltype: byte;
|
||||||
|
|
||||||
{$HINTS OFF}
|
{$HINTS OFF}
|
||||||
procedure DoDispCallByID(res: Pointer; const disp: IDispatch; desc: PDispDesc;
|
procedure DoDispCallByID(res: Pointer; const disp: IDispatch; desc: PDispDesc;
|
||||||
@ -48,6 +50,25 @@ var
|
|||||||
halt($FF);
|
halt($FF);
|
||||||
end;
|
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}
|
{$HINTS ON}
|
||||||
|
|
||||||
@ -56,6 +77,7 @@ var
|
|||||||
B: wordbool;
|
B: wordbool;
|
||||||
begin
|
begin
|
||||||
// check dispid values
|
// check dispid values
|
||||||
|
writeln('Testing dispid values...');
|
||||||
DispCallByIDProc := @DoDispCallByID;
|
DispCallByIDProc := @DoDispCallByID;
|
||||||
cur_dispid := 300;
|
cur_dispid := 300;
|
||||||
II.Disp300;
|
II.Disp300;
|
||||||
@ -66,6 +88,7 @@ begin
|
|||||||
cur_dispid := 402;
|
cur_dispid := 402;
|
||||||
II.Disp402 := True;
|
II.Disp402 := True;
|
||||||
// check arguments
|
// check arguments
|
||||||
|
writeln('Testing arguments...');
|
||||||
DispCallByIDProc := @DoDispCallByIDArg;
|
DispCallByIDProc := @DoDispCallByIDArg;
|
||||||
cur_restype := varempty;
|
cur_restype := varempty;
|
||||||
cur_argtype := varunknown;
|
cur_argtype := varunknown;
|
||||||
@ -76,4 +99,17 @@ begin
|
|||||||
cur_argtype := varboolean or $80;
|
cur_argtype := varboolean or $80;
|
||||||
B := False;
|
B := False;
|
||||||
II.DispArg3(B);
|
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.
|
end.
|
Loading…
Reference in New Issue
Block a user