pastojs: helpers: external method apply to helped type

git-svn-id: trunk@41707 -
This commit is contained in:
Mattias Gaertner 2019-03-14 22:04:33 +00:00
parent 19e1336043
commit 1ea55d0a74
4 changed files with 147 additions and 49 deletions

View File

@ -2024,7 +2024,7 @@ type
function GetFunctionType(El: TPasElement): TPasFunctionType; function GetFunctionType(El: TPasElement): TPasFunctionType;
function MethodIsStatic(El: TPasProcedure): boolean; function MethodIsStatic(El: TPasProcedure): boolean;
function IsMethod(El: TPasProcedure): boolean; function IsMethod(El: TPasProcedure): boolean;
function IsHelperMethod(El: TPasElement): boolean; function IsHelperMethod(El: TPasElement): boolean; virtual;
function IsHelper(El: TPasElement): boolean; function IsHelper(El: TPasElement): boolean;
function IsExternalClass_Name(aClass: TPasClassType; const ExtName: string): boolean; function IsExternalClass_Name(aClass: TPasClassType; const ExtName: string): boolean;
function IsProcedureType(const ResolvedEl: TPasResolverResult; HasValue: boolean): boolean; function IsProcedureType(const ResolvedEl: TPasResolverResult; HasValue: boolean): boolean;

View File

@ -1455,6 +1455,8 @@ type
function IsForInExtArray(Loop: TPasImplForLoop; const VarResolved, function IsForInExtArray(Loop: TPasImplForLoop; const VarResolved,
InResolved: TPasResolverResult; out ArgResolved, LengthResolved, InResolved: TPasResolverResult; out ArgResolved, LengthResolved,
PropResultResolved: TPasResolverResult): boolean; PropResultResolved: TPasResolverResult): boolean;
function IsHelperMethod(El: TPasElement): boolean; override;
function IsHelperForMember(El: TPasElement): boolean;
end; end;
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -3987,19 +3989,25 @@ begin
RaiseMsg(20180329141108,nInvalidXModifierY, RaiseMsg(20180329141108,nInvalidXModifierY,
sInvalidXModifierY,[Proc.ElementTypeName,ModifierNames[pm]],Proc); sInvalidXModifierY,[Proc.ElementTypeName,ModifierNames[pm]],Proc);
end; end;
okClassHelper: okClassHelper,okRecordHelper,okTypeHelper:
begin begin
HelperForType:=ResolveAliasType(AClass.HelperForType); HelperForType:=ResolveAliasType(AClass.HelperForType);
if HelperForType.ClassType<>TPasClassType then if HelperForType.ClassType=TPasClassType then
RaiseNotYetImplemented(20190201165157,El);
if TPasClassType(HelperForType).IsExternal then
begin begin
// method of a class helper for external class if TPasClassType(HelperForType).IsExternal then
if IsClassMethod(El) and not (ptmStatic in El.Modifiers) then begin
RaiseMsg(20190201165259,nHelperClassMethodForExtClassMustBeStatic, // method of a class helper for external class
sHelperClassMethodForExtClassMustBeStatic,[],El); if IsClassMethod(El) and not (ptmStatic in El.Modifiers) then
if El.ClassType=TPasConstructor then RaiseMsg(20190201165259,nHelperClassMethodForExtClassMustBeStatic,
RaiseNotYetImplemented(20190206153655,El); sHelperClassMethodForExtClassMustBeStatic,[],El);
if El.ClassType=TPasConstructor then
RaiseNotYetImplemented(20190206153655,El);
end;
end;
if Proc.IsExternal then
begin
if not (HelperForType is TPasMembersType) then
RaiseMsg(20190314225457,nNotSupportedX,sNotSupportedX,['external method in type helper'],El);
end; end;
end; end;
end; end;
@ -5886,6 +5894,26 @@ begin
CheckAssignResCompatibility(VarResolved,PropResultResolved,Loop.VariableName,true); CheckAssignResCompatibility(VarResolved,PropResultResolved,Loop.VariableName,true);
end; end;
function TPas2JSResolver.IsHelperMethod(El: TPasElement): boolean;
begin
Result:=inherited IsHelperMethod(El);
if not Result then exit;
Result:=not TPasProcedure(El).IsExternal;
end;
function TPas2JSResolver.IsHelperForMember(El: TPasElement): boolean;
begin
if (El=nil) or (El.Parent=nil) or (El.Parent.ClassType<>TPasClassType)
or (TPasClassType(El.Parent).HelperForType=nil) then
exit(false);
if El is TPasProcedure then
Result:=TPasProcedure(El).IsExternal
else if El is TPasVariable then
Result:=vmExternal in TPasVariable(El).VarModifiers
else
Result:=true;
end;
{ TParamContext } { TParamContext }
constructor TParamContext.Create(PasEl: TPasElement; JSEl: TJSElement; constructor TParamContext.Create(PasEl: TPasElement; JSEl: TJSElement;
@ -7896,7 +7924,8 @@ begin
if aResolver.IsHelper(RightRefDecl.Parent) then if aResolver.IsHelper(RightRefDecl.Parent) then
begin begin
// LeftJS.HelperMember // LeftJS.HelperMember
if RightRefDecl is TPasVariable then if (RightRefDecl is TPasVariable)
and not (vmExternal in TPasVariable(RightRefDecl).VarModifiers) then
begin begin
// LeftJS.HelperField -> HelperType.HelperField // LeftJS.HelperField -> HelperType.HelperField
if Assigned(OnConvertRight) then if Assigned(OnConvertRight) then
@ -7907,7 +7936,10 @@ begin
end end
else if RightRefDecl is TPasProcedure then else if RightRefDecl is TPasProcedure then
begin begin
if rrfNoImplicitCallWithoutParams in RightRef.Flags then Proc:=TPasProcedure(RightRefDecl);
if Proc.IsExternal then
// normal call
else if rrfNoImplicitCallWithoutParams in RightRef.Flags then
begin begin
Result:=CreateReferencePathExpr(RightRefDecl,AContext); Result:=CreateReferencePathExpr(RightRefDecl,AContext);
exit; exit;
@ -7915,7 +7947,6 @@ begin
else else
begin begin
// call helper method // call helper method
Proc:=TPasProcedure(RightRefDecl);
Result:=CreateCallHelperMethod(Proc,El,AContext); Result:=CreateCallHelperMethod(Proc,El,AContext);
exit; exit;
end; end;
@ -8295,7 +8326,7 @@ begin
Decl:=aResolver.GetPasPropertySetter(Prop); Decl:=aResolver.GetPasPropertySetter(Prop);
if Decl is TPasProcedure then if Decl is TPasProcedure then
begin begin
if aResolver.IsHelper(Decl.Parent) then if aResolver.IsHelperMethod(Decl) then
begin begin
Result:=CreateCallHelperMethod(TPasProcedure(Decl),El,AContext); Result:=CreateCallHelperMethod(TPasProcedure(Decl),El,AContext);
exit; exit;
@ -9768,7 +9799,7 @@ begin
end end
else if C.InheritsFrom(TPasProcedure) then else if C.InheritsFrom(TPasProcedure) then
begin begin
if aResolver.IsHelper(Decl.Parent) then if aResolver.IsHelperMethod(Decl) then
begin begin
// calling a helper method // calling a helper method
Result:=CreateCallHelperMethod(TPasProcedure(Decl),El.Value,AContext); Result:=CreateCallHelperMethod(TPasProcedure(Decl),El.Value,AContext);
@ -16187,7 +16218,7 @@ begin
Result:=CreateReferencePathExpr(Proc,AContext); Result:=CreateReferencePathExpr(Proc,AContext);
exit; exit;
end; end;
IsHelper:=aResolver.IsHelper(Proc.Parent); IsHelper:=aResolver.IsHelperMethod(Proc);
NeedClass:=aResolver.IsClassMethod(Proc) and not aResolver.MethodIsStatic(Proc); NeedClass:=aResolver.IsClassMethod(Proc) and not aResolver.MethodIsStatic(Proc);
// an of-object method -> create "rtl.createCallback(Target,func)" // an of-object method -> create "rtl.createCallback(Target,func)"
@ -16599,7 +16630,7 @@ begin
if Decl is TPasFunction then if Decl is TPasFunction then
begin begin
// call function // call function
if aResolver.IsHelper(Decl.Parent) then if aResolver.IsHelperMethod(Decl) then
begin begin
if (Expr=nil) then if (Expr=nil) then
// implicit property read, e.g. enumerator property Current // implicit property read, e.g. enumerator property Current
@ -21304,9 +21335,16 @@ var
begin begin
if (Ref=nil) or (Ref.WithExprScope=nil) then exit(false); if (Ref=nil) or (Ref.WithExprScope=nil) then exit(false);
Parent:=El.Parent; Parent:=El.Parent;
if (Parent<>nil) and (Parent.ClassType=TPasClassType) if (Parent.ClassType=TPasClassType)
and (TPasClassType(Parent).HelperForType<>nil) then and (TPasClassType(Parent).HelperForType<>nil) then
exit(false); begin
// e.g. with Obj do HelperMethod
if aResolver.IsHelperForMember(El) then
// e.g. with Obj do HelperExternalMethod -> Obj.HelperCall
else
// e.g. with Obj do HelperMethod -> THelper.HelperCall
exit(false);
end;
Result:=true; Result:=true;
end; end;
@ -21403,6 +21441,8 @@ var
begin begin
Result:=''; Result:='';
{$IFDEF VerbosePas2JS} {$IFDEF VerbosePas2JS}
if SameText(El.Name,'Fly') then
writeln('AAA1 TPasToJSConverter.CreateReferencePath START El=',GetObjName(El),' Parent=',GetObjName(El.Parent),' Context=',GetObjName(AContext),' SelfContext=',GetObjName(AContext.GetSelfContext));
//writeln('TPasToJSConverter.CreateReferencePath START El=',GetObjName(El),' Parent=',GetObjName(El.Parent),' Context=',GetObjName(AContext),' SelfContext=',GetObjName(AContext.GetSelfContext)); //writeln('TPasToJSConverter.CreateReferencePath START El=',GetObjName(El),' Parent=',GetObjName(El.Parent),' Context=',GetObjName(AContext),' SelfContext=',GetObjName(AContext.GetSelfContext));
//AContext.WriteStack; //AContext.WriteStack;
{$ENDIF} {$ENDIF}
@ -21484,6 +21524,7 @@ begin
else else
begin begin
// need full path // need full path
writeln('AAA2 TPasToJSConverter.CreateReferencePath ');
if El.Parent=nil then if El.Parent=nil then
RaiseNotSupported(El,AContext,20170201172141,GetObjName(El)); RaiseNotSupported(El,AContext,20170201172141,GetObjName(El));
El:=ImplToDecl(El); El:=ImplToDecl(El);
@ -21493,38 +21534,26 @@ begin
begin begin
ParentEl:=ImplToDecl(ParentEl); ParentEl:=ImplToDecl(ParentEl);
IsClassRec:=(ParentEl.ClassType=TPasClassType)
or (ParentEl.ClassType=TPasRecordType);
// check if ParentEl has a JS var // check if ParentEl has a JS var
ShortName:=AContext.GetLocalName(ParentEl); ShortName:=AContext.GetLocalName(ParentEl);
//writeln('TPasToJSConverter.CreateReferencePath El=',GetObjName(El),' ParentEl=',GetObjName(ParentEl),' ShortName=',ShortName); //writeln('TPasToJSConverter.CreateReferencePath El=',GetObjName(El),' ParentEl=',GetObjName(ParentEl),' ShortName=',ShortName);
IsClassRec:=(ParentEl.ClassType=TPasClassType) if IsClassRec then
or (ParentEl.ClassType=TPasRecordType);
if (ShortName<>'') and not IsClassRec then
begin
Prepend(Result,ShortName);
break;
end
else if ParentEl.ClassType=TImplementationSection then
begin
// element is in an implementation section (not program/library section)
// in other unit -> use pas.unitname.$impl
FoundModule:=El.GetModule;
if FoundModule=nil then
RaiseInconsistency(20161024192755,El);
Prepend(Result,TransformModuleName(FoundModule,true,AContext)
+'.'+GetBIName(pbivnImplementation));
break;
end
else if ParentEl is TPasModule then
begin
// element is in an unit interface or program/library section
Prepend(Result,TransformModuleName(TPasModule(ParentEl),true,AContext));
break;
end
else if IsClassRec then
begin begin
// parent is a class or record declaration // parent is a class or record declaration
writeln('AAA3 TPasToJSConverter.CreateReferencePath ',GetObjName(ParentEl));
if (ParentEl.ClassType=TPasClassType)
and (TPasClassType(ParentEl).HelperForType<>nil)
and aResolver.IsHelperForMember(El) then
begin
// redirect to helper-for-type
ParentEl:=aResolver.ResolveAliasType(TPasClassType(ParentEl).HelperForType);
ShortName:=AContext.GetLocalName(ParentEl);
end;
if Full then if Full then
Prepend(Result,ParentEl.Name) Prepend(Result,ParentEl.Name)
else else
@ -21541,8 +21570,10 @@ begin
Prepend(Result,ParentEl.Name) Prepend(Result,ParentEl.Name)
else if (ParentEl.ClassType=TPasClassType) else if (ParentEl.ClassType=TPasClassType)
and (TPasClassType(ParentEl).HelperForType<>nil) then and (TPasClassType(ParentEl).HelperForType<>nil) then
begin
// helpers have no self // helpers have no self
Prepend(Result,ParentEl.Name) Prepend(Result,ParentEl.Name);
end
else if (SelfContext<>nil) else if (SelfContext<>nil)
and IsA(TPasType(SelfContext.ThisPas),TPasMembersType(ParentEl)) then and IsA(TPasType(SelfContext.ThisPas),TPasMembersType(ParentEl)) then
begin begin
@ -21575,6 +21606,28 @@ begin
break; break;
end; end;
end end
else if (ShortName<>'') then
begin
Prepend(Result,ShortName);
break;
end
else if ParentEl.ClassType=TImplementationSection then
begin
// element is in an implementation section (not program/library section)
// in other unit -> use pas.unitname.$impl
FoundModule:=El.GetModule;
if FoundModule=nil then
RaiseInconsistency(20161024192755,El);
Prepend(Result,TransformModuleName(FoundModule,true,AContext)
+'.'+GetBIName(pbivnImplementation));
break;
end
else if ParentEl is TPasModule then
begin
// element is in an unit interface or program/library section
Prepend(Result,TransformModuleName(TPasModule(ParentEl),true,AContext));
break;
end
else if ParentEl.ClassType=TPasEnumType then else if ParentEl.ClassType=TPasEnumType then
begin begin
if (ShortName<>'') and not Full then if (ShortName<>'') and not Full then

View File

@ -680,6 +680,7 @@ type
Procedure TestTypeHelper_ClassProperty; Procedure TestTypeHelper_ClassProperty;
Procedure TestTypeHelper_ClassProperty_Array; Procedure TestTypeHelper_ClassProperty_Array;
Procedure TestTypeHelper_ClassMethod; Procedure TestTypeHelper_ClassMethod;
Procedure TestTypeHelper_ExtClassMethodFail;
Procedure TestTypeHelper_Constructor; Procedure TestTypeHelper_Constructor;
Procedure TestTypeHelper_Word; Procedure TestTypeHelper_Word;
Procedure TestTypeHelper_Double; Procedure TestTypeHelper_Double;
@ -21197,12 +21198,15 @@ begin
Add([ Add([
'{$modeswitch externalclass}', '{$modeswitch externalclass}',
'type', 'type',
' TFly = function(w: word): word of object;',
' TExtA = class external name ''ExtObj''', ' TExtA = class external name ''ExtObj''',
' procedure Run(w: word = 10);', ' procedure Run(w: word = 10);',
' end;', ' end;',
' THelper = class helper for TExtA', ' THelper = class helper for TExtA',
' function Foo(w: word = 1): word;', ' function Foo(w: word = 1): word;',
' function Fly(w: word = 2): word; external name ''Fly'';',
' end;', ' end;',
'var p: TFly;',
'function THelper.foo(w: word): word;', 'function THelper.foo(w: word): word;',
'begin', 'begin',
' Run;', ' Run;',
@ -21214,22 +21218,32 @@ begin
' Self.Foo;', ' Self.Foo;',
' Self.Foo();', ' Self.Foo();',
' Self.Foo(13);', ' Self.Foo(13);',
' Fly;',
' Fly();',
' with Self do begin', ' with Self do begin',
' Foo;', ' Foo;',
' Foo();', ' Foo();',
' Foo(14);', ' Foo(14);',
' Fly;',
' Fly();',
' end;', ' end;',
' p:=@Fly;',
'end;', 'end;',
'var Obj: TExtA;', 'var Obj: TExtA;',
'begin', 'begin',
' obj.Foo;', ' obj.Foo;',
' obj.Foo();', ' obj.Foo();',
' obj.Foo(21);', ' obj.Foo(21);',
' obj.Fly;',
' obj.Fly();',
' with obj do begin', ' with obj do begin',
' Foo;', ' Foo;',
' Foo();', ' Foo();',
' Foo(22);', ' Foo(22);',
' Fly;',
' Fly();',
' end;', ' end;',
' p:=@obj.Fly;',
'']); '']);
ConvertProgram; ConvertProgram;
CheckSource('TestExtClassHelper_Method_Call', CheckSource('TestExtClassHelper_Method_Call',
@ -21246,22 +21260,33 @@ begin
' $mod.THelper.Foo.call(this, 1);', ' $mod.THelper.Foo.call(this, 1);',
' $mod.THelper.Foo.call(this, 1);', ' $mod.THelper.Foo.call(this, 1);',
' $mod.THelper.Foo.call(this, 13);', ' $mod.THelper.Foo.call(this, 13);',
' this.Fly(2);',
' this.Fly(2);',
' $mod.THelper.Foo.call(this, 1);', ' $mod.THelper.Foo.call(this, 1);',
' $mod.THelper.Foo.call(this, 1);', ' $mod.THelper.Foo.call(this, 1);',
' $mod.THelper.Foo.call(this, 14);', ' $mod.THelper.Foo.call(this, 14);',
' this.Fly(2);',
' this.Fly(2);',
' $mod.p = rtl.createCallback(this, "Fly");',
' return Result;', ' return Result;',
' };', ' };',
'});', '});',
'this.p = null;',
'this.Obj = null;', 'this.Obj = null;',
'']), '']),
LinesToStr([ // $mod.$main LinesToStr([ // $mod.$main
'$mod.THelper.Foo.call($mod.Obj, 1);', '$mod.THelper.Foo.call($mod.Obj, 1);',
'$mod.THelper.Foo.call($mod.Obj, 1);', '$mod.THelper.Foo.call($mod.Obj, 1);',
'$mod.THelper.Foo.call($mod.Obj, 21);', '$mod.THelper.Foo.call($mod.Obj, 21);',
'$mod.Obj.Fly(2);',
'$mod.Obj.Fly(2);',
'var $with1 = $mod.Obj;', 'var $with1 = $mod.Obj;',
'$mod.THelper.Foo.call($with1, 1);', '$mod.THelper.Foo.call($with1, 1);',
'$mod.THelper.Foo.call($with1, 1);', '$mod.THelper.Foo.call($with1, 1);',
'$mod.THelper.Foo.call($with1, 22);', '$mod.THelper.Foo.call($with1, 22);',
'$with1.Fly(2);',
'$with1.Fly(2);',
'$mod.p = rtl.createCallback($mod.Obj, "Fly");',
''])); '']));
end; end;
@ -23022,6 +23047,23 @@ begin
''])); '']));
end; end;
procedure TTestModule.TestTypeHelper_ExtClassMethodFail;
begin
StartProgram(false);
Add([
'{$modeswitch typehelpers}',
'type',
' THelper = type helper for word',
' procedure Run; external name ''Run'';',
' end;',
'var w: word;',
'begin',
' w.Run;',
'']);
SetExpectedPasResolverError('Not supported: external method in type helper',nNotSupportedX);
ConvertProgram;
end;
procedure TTestModule.TestTypeHelper_Constructor; procedure TTestModule.TestTypeHelper_Constructor;
begin begin
StartProgram(false); StartProgram(false);

View File

@ -1867,8 +1867,9 @@ function(){
<li>A <b>record helper</b> can "extend" a record type. In $mode delphi a <li>A <b>record helper</b> can "extend" a record type. In $mode delphi a
record helper can extend other types as well, see <i>type helper</i></li> record helper can extend other types as well, see <i>type helper</i></li>
<li>A <b>type helper</b> can extend all base types like integer, string, <li>A <b>type helper</b> can extend all base types like integer, string,
char, boolean, double, currency, and some user types like enumeration, char, boolean, double, currency, and user types like enumeration,
set, range and array types. It cannot extend interfaces or helpers.<br> set, range, array, class, record and interface types.
It cannot extend helpers and procedural types.<br>
Type helpers are enabled by default in <i>$mode delphi</i> and disabled in <i>$mode objfpc</i>. Type helpers are enabled by default in <i>$mode delphi</i> and disabled in <i>$mode objfpc</i>.
You can enable them with <b>{$modeswitch typehelpers}</b>. You can enable them with <b>{$modeswitch typehelpers}</b>.
</li> </li>
@ -1929,6 +1930,8 @@ function(){
<li><i>with value do ;</i> : uses a temporary variable. Delphi/FPC do not support it.</li> <li><i>with value do ;</i> : uses a temporary variable. Delphi/FPC do not support it.</li>
</ul> </ul>
</li> </li>
<li>A method with <i>external name</i> modifier is treated as an external
method of the helped type.</li>
</ul> </ul>
</div> </div>