pastojs: helpers: access helper fields from method

git-svn-id: trunk@41189 -
This commit is contained in:
Mattias Gaertner 2019-02-03 16:29:24 +00:00
parent 07d6c5b688
commit c2671bdbb6
3 changed files with 423 additions and 127 deletions

View File

@ -5,7 +5,7 @@ unit fpjsonrtti;
interface interface
uses uses
Classes, SysUtils, contnrs, jsonscanner, typinfo, fpjson, rttiutils, jsonparser; Classes, SysUtils, contnrs, typinfo, fpjson, rttiutils, jsonparser;
Const Const
RFC3339DateTimeFormat = 'yyyy"-"mm"-"dd"T"hh":"nn":"ss'; RFC3339DateTimeFormat = 'yyyy"-"mm"-"dd"T"hh":"nn":"ss';
@ -68,7 +68,6 @@ Type
// If AObject is of type TStrings or TCollection, special treatment occurs: // If AObject is of type TStrings or TCollection, special treatment occurs:
// TStrings results in { Strings: [S,S,S] } or { Strings: { "S1" : O1, "S2" : O2 }} depending on Options. // TStrings results in { Strings: [S,S,S] } or { Strings: { "S1" : O1, "S2" : O2 }} depending on Options.
// Collection results in { Items: [I,I,I] } // Collection results in { Items: [I,I,I] }
// Tlist/TObjectlist results in { "Objects": [O1,O2,O3] }
Function ObjectToJSON(Const AObject : TObject) : TJSONObject; Function ObjectToJSON(Const AObject : TObject) : TJSONObject;
// Stream a collection - always returns an array // Stream a collection - always returns an array
function StreamCollection(Const ACollection: TCollection): TJSONArray; function StreamCollection(Const ACollection: TCollection): TJSONArray;
@ -218,7 +217,7 @@ Type
function TJSONDeStreamer.ObjectFromString(const JSON: TJSONStringType): TJSONData; function TJSONDeStreamer.ObjectFromString(const JSON: TJSONStringType): TJSONData;
begin begin
With TJSONParser.Create(JSON,[joUTF8]) do With TJSONParser.Create(JSON) do
try try
Result:=Parse; Result:=Parse;
finally finally
@ -778,7 +777,7 @@ begin
else If AObject is TObjectList then else If AObject is TObjectList then
Result.Add('Objects',StreamObjectList(TObjectList(AObject))) Result.Add('Objects',StreamObjectList(TObjectList(AObject)))
else if (jsoStreamTlist in Options) and (AObject is TList) then else if (jsoStreamTlist in Options) and (AObject is TList) then
Result.Add('Objects', StreamTList(TList(AObject))) Result := TJSONObject(StreamTList(TList(AObject)))
else else
begin begin
PIL:=TPropInfoList.Create(AObject,tkProperties); PIL:=TPropInfoList.Create(AObject,tkProperties);

View File

@ -498,6 +498,7 @@ const
nDuplicateGUIDXInYZ = 4024; nDuplicateGUIDXInYZ = 4024;
nCantCallExtBracketAccessor = 4025; nCantCallExtBracketAccessor = 4025;
nJSNewNotSupported = 4026; nJSNewNotSupported = 4026;
nHelperClassMethodForExtClassMustBeStatic = 4027;
// resourcestring patterns of messages // resourcestring patterns of messages
resourcestring resourcestring
sPasElementNotSupported = 'Pascal element not supported: %s'; sPasElementNotSupported = 'Pascal element not supported: %s';
@ -526,6 +527,7 @@ resourcestring
sDuplicateGUIDXInYZ = 'Duplicate GUID %s in %s and %s'; sDuplicateGUIDXInYZ = 'Duplicate GUID %s in %s and %s';
sCantCallExtBracketAccessor = 'cannot call external bracket accessor, use a property instead'; sCantCallExtBracketAccessor = 'cannot call external bracket accessor, use a property instead';
sJSNewNotSupported = 'Pascal class does not support the "new" constructor'; sJSNewNotSupported = 'Pascal class does not support the "new" constructor';
sHelperClassMethodForExtClassMustBeStatic = 'Helper class method for external class must be static';
const const
ExtClassBracketAccessor = '[]'; // external name '[]' marks the array param getter/setter ExtClassBracketAccessor = '[]'; // external name '[]' marks the array param getter/setter
@ -1784,6 +1786,9 @@ type
FuncContext: TFunctionContext); FuncContext: TFunctionContext);
Procedure AddInterfaceReleases(FuncContext: TFunctionContext; PosEl: TPasElement); Procedure AddInterfaceReleases(FuncContext: TFunctionContext; PosEl: TPasElement);
Procedure AddRTLVersionCheck(FuncContext: TFunctionContext; PosEl: TPasElement); Procedure AddRTLVersionCheck(FuncContext: TFunctionContext; PosEl: TPasElement);
// create elements for helpers
Function CreateCallNonStaticHelperMethod(Proc: TPasProcedure; Expr: TPasExpr;
AContext: TConvertContext): TJSElement; virtual;
// Statements // Statements
Function ConvertImplBlockElements(El: TPasImplBlock; AContext: TConvertContext; NilIfEmpty: boolean): TJSElement; virtual; Function ConvertImplBlockElements(El: TPasImplBlock; AContext: TConvertContext; NilIfEmpty: boolean): TJSElement; virtual;
Function ConvertBeginEndStatement(El: TPasImplBeginBlock; AContext: TConvertContext; NilIfEmpty: boolean): TJSElement; virtual; Function ConvertBeginEndStatement(El: TPasImplBeginBlock; AContext: TConvertContext; NilIfEmpty: boolean): TJSElement; virtual;
@ -3644,7 +3649,7 @@ var
AClass: TPasClassType; AClass: TPasClassType;
ClassScope: TPas2JSClassScope; ClassScope: TPas2JSClassScope;
ptm: TProcTypeModifier; ptm: TProcTypeModifier;
TypeEl, ElTypeEl: TPasType; TypeEl, ElTypeEl, HelperForType: TPasType;
begin begin
inherited FinishProcedureType(El); inherited FinishProcedureType(El);
@ -3697,84 +3702,96 @@ begin
begin begin
AClass:=TPasClassType(AClassOrRec); AClass:=TPasClassType(AClassOrRec);
ClassScope:=TPas2JSClassScope(ClassOrRecScope); ClassScope:=TPas2JSClassScope(ClassOrRecScope);
if AClass.IsExternal then
begin
// external class -> make method external
if not (pmExternal in Proc.Modifiers) then
begin
if Proc.LibrarySymbolName<>nil then
RaiseMsg(20170322142158,nInvalidXModifierY,
sInvalidXModifierY,[Proc.ElementTypeName,'symbol name'],Proc.LibrarySymbolName);
Proc.Modifiers:=Proc.Modifiers+[pmExternal];
Proc.LibrarySymbolName:=TPrimitiveExpr.Create(Proc,pekString,''''+Proc.Name+'''');
end;
if Proc.Visibility=visPublished then
// Note: an external class has no typeinfo
RaiseMsg(20170413221327,nSymbolCannotBePublished,sSymbolCannotBePublished,
[],Proc);
C:=Proc.ClassType;
if (C=TPasProcedure) or (C=TPasFunction) then
// ok
else if (C=TPasClassProcedure) or (C=TPasClassFunction) then
// ok
else if C=TPasConstructor then
begin
if Proc.IsVirtual then
// constructor of external class can't be overriden -> forbid virtual
RaiseMsg(20170323100447,nInvalidXModifierY,sInvalidXModifierY,
[Proc.ElementTypeName,'virtual,external'],Proc);
if CompareText(Proc.Name,'new')=0 then
begin
ExtName:=ComputeConstString(Proc.LibrarySymbolName,true,true);
if ExtName<>Proc.Name then
RaiseMsg(20170323083511,nVirtualMethodNameMustMatchExternal,
sVirtualMethodNameMustMatchExternal,[],Proc.LibrarySymbolName);
end
else
RaiseMsg(20190116211019,nExternalObjectConstructorMustBeNamedNew,
sExternalObjectConstructorMustBeNamedNew,[],El);
end
else
RaiseMsg(20170322163210,nPasElementNotSupported,sPasElementNotSupported,
[Proc.ElementTypeName],Proc);
end
else
// Pascal class, not external
case AClass.ObjKind of
okClass:
begin
if (ClassScope.NewInstanceFunction=nil)
and (ClassScope.AncestorScope<>nil)
and (TPasClassType(ClassScope.AncestorScope.Element).IsExternal)
and (Proc.ClassType=TPasClassFunction)
and (Proc.Visibility in [visProtected,visPublic,visPublished])
and (TPasClassFunction(Proc).FuncType.ResultEl.ResultType=AClassOrRec)
and ([pmOverride,pmExternal]*Proc.Modifiers=[]) then
begin
// The first non private class function in a Pascal class descending
// from an external class
// -> this is the NewInstance function
ClassScope.NewInstanceFunction:=TPasClassFunction(Proc);
CheckNewInstanceFunction(ClassScope);
end;
end;
okInterface:
begin
for pm in Proc.Modifiers do
if not (pm in [pmOverload, pmReintroduce]) then
RaiseMsg(20180329141108,nInvalidXModifierY,
sInvalidXModifierY,[Proc.ElementTypeName,ModifierNames[pm]],Proc);
end;
okClassHelper:
begin
HelperForType:=ResolveAliasType(AClass.HelperForType);
if HelperForType.ClassType<>TPasClassType then
RaiseNotYetImplemented(20190201165157,El);
if TPasClassType(HelperForType).IsExternal then
begin
if not (ptmStatic in El.Modifiers) then
RaiseMsg(20190201165259,nHelperClassMethodForExtClassMustBeStatic,
sHelperClassMethodForExtClassMustBeStatic,[],El);
end;
end;
end;
end end
else else
begin begin
AClass:=nil; AClass:=nil;
ClassScope:=nil; ClassScope:=nil;
end; end;
if (AClass<>nil) and AClass.IsExternal then
begin
// external class -> make method external
if not (pmExternal in Proc.Modifiers) then
begin
if Proc.LibrarySymbolName<>nil then
RaiseMsg(20170322142158,nInvalidXModifierY,
sInvalidXModifierY,[Proc.ElementTypeName,'symbol name'],Proc.LibrarySymbolName);
Proc.Modifiers:=Proc.Modifiers+[pmExternal];
Proc.LibrarySymbolName:=TPrimitiveExpr.Create(Proc,pekString,''''+Proc.Name+'''');
end;
if Proc.Visibility=visPublished then
// Note: an external class has no typeinfo
RaiseMsg(20170413221327,nSymbolCannotBePublished,sSymbolCannotBePublished,
[],Proc);
C:=Proc.ClassType;
if (C=TPasProcedure) or (C=TPasFunction)
or (C=TPasClassProcedure) or (C=TPasClassFunction) then
// ok
else if C=TPasConstructor then
begin
if Proc.IsVirtual then
// constructor of external class can't be overriden -> forbid virtual
RaiseMsg(20170323100447,nInvalidXModifierY,sInvalidXModifierY,
[Proc.ElementTypeName,'virtual,external'],Proc);
if CompareText(Proc.Name,'new')=0 then
begin
ExtName:=ComputeConstString(Proc.LibrarySymbolName,true,true);
if ExtName<>Proc.Name then
RaiseMsg(20170323083511,nVirtualMethodNameMustMatchExternal,
sVirtualMethodNameMustMatchExternal,[],Proc.LibrarySymbolName);
end
else
RaiseMsg(20190116211019,nExternalObjectConstructorMustBeNamedNew,
sExternalObjectConstructorMustBeNamedNew,[],El);
end
else
RaiseMsg(20170322163210,nPasElementNotSupported,sPasElementNotSupported,
[Proc.ElementTypeName],Proc);
end
else if aClass<>nil then
// Pascal class, not external
case AClass.ObjKind of
okClass:
begin
if (ClassScope.NewInstanceFunction=nil)
and (ClassScope.AncestorScope<>nil)
and (TPasClassType(ClassScope.AncestorScope.Element).IsExternal)
and (Proc.ClassType=TPasClassFunction)
and (Proc.Visibility in [visProtected,visPublic,visPublished])
and (TPasClassFunction(Proc).FuncType.ResultEl.ResultType=AClassOrRec)
and ([pmOverride,pmExternal]*Proc.Modifiers=[]) then
begin
// The first non private class function in a Pascal class descending
// from an external class
// -> this is the NewInstance function
ClassScope.NewInstanceFunction:=TPasClassFunction(Proc);
CheckNewInstanceFunction(ClassScope);
end;
end;
okInterface:
begin
for pm in Proc.Modifiers do
if not (pm in [pmOverload, pmReintroduce]) then
RaiseMsg(20180329141108,nInvalidXModifierY,
sInvalidXModifierY,[Proc.ElementTypeName,ModifierNames[pm]],Proc);
end;
end;
end; end;
if pmExternal in Proc.Modifiers then if pmExternal in Proc.Modifiers then
@ -7207,14 +7224,14 @@ function TPasToJSConverter.ConvertSubIdentExprCustom(El: TBinaryExpr;
Data: Pointer): TJSElement; Data: Pointer): TJSElement;
var var
OldAccess: TCtxAccess; OldAccess: TCtxAccess;
Left: TJSElement; LeftJS, RightJS: TJSElement;
DotContext: TDotContext; DotContext: TDotContext;
Right: TJSElement;
aResolver: TPas2JSResolver; aResolver: TPas2JSResolver;
LeftResolved: TPasResolverResult; LeftResolved: TPasResolverResult;
RightEl: TPasExpr; RightEl: TPasExpr;
RightRef: TResolvedReference; RightRef: TResolvedReference;
RightRefDecl: TPasElement; RightRefDecl: TPasElement;
Proc: TPasProcedure;
begin begin
aResolver:=AContext.Resolver; aResolver:=AContext.Resolver;
@ -7256,81 +7273,107 @@ begin
and aResolver.IsClassField(RightRefDecl) then and aResolver.IsClassField(RightRefDecl) then
begin begin
// e.g. "Something.aClassVar:=" -> "aClass.aClassVar:=" // e.g. "Something.aClassVar:=" -> "aClass.aClassVar:="
Left:=CreateReferencePathExpr(RightRefDecl.Parent,AContext); LeftJS:=CreateReferencePathExpr(RightRefDecl.Parent,AContext);
Result:=TJSDotMemberExpression(CreateElement(TJSDotMemberExpression,El)); Result:=TJSDotMemberExpression(CreateElement(TJSDotMemberExpression,El));
TJSDotMemberExpression(Result).MExpr:=Left; TJSDotMemberExpression(Result).MExpr:=LeftJS;
TJSDotMemberExpression(Result).Name:=TJSString(TransformVariableName(RightRefDecl,AContext)); TJSDotMemberExpression(Result).Name:=TJSString(TransformVariableName(RightRefDecl,AContext));
exit; exit;
end; end;
LeftJS:=nil;
if (RightRefDecl.Parent.ClassType=TPasClassType) if (RightRefDecl.Parent.ClassType=TPasClassType)
and (TPasClassType(RightRefDecl.Parent).HelperForType<>nil) then and (TPasClassType(RightRefDecl.Parent).HelperForType<>nil) then
begin begin
// Left.HelperMember // LeftJS.HelperMember
if RightRefDecl is TPasVariable then if RightRefDecl is TPasVariable then
begin begin
// Left.HelperField // LeftJS.HelperField -> HelperType.HelperField
if Assigned(OnConvertRight) then if Assigned(OnConvertRight) then
Result:=OnConvertRight(RightEl,AContext,Data) Result:=OnConvertRight(RightEl,AContext,Data)
else else
Result:=ConvertIdentifierExpr(RightEl,TPrimitiveExpr(RightEl).Value,AContext); Result:=ConvertIdentifierExpr(RightEl,TPrimitiveExpr(RightEl).Value,AContext);
exit; exit;
end end
else else if RightRefDecl is TPasProcedure then
begin begin
RaiseNotSupported(El,AContext,20190131170119); // LeftJS.HelperCall
end; Proc:=TPasProcedure(RightRefDecl);
end; if ptmStatic in Proc.ProcType.Modifiers then
begin
if aResolver<>nil then // call static helper method -> HelperType.Call
aResolver.ComputeElement(El.left,LeftResolved,[]) OldAccess:=AContext.Access;
else AContext.Access:=caRead;
LeftResolved:=Default(TPasResolverResult); LeftJS:=CreateReferencePathExpr(Proc.Parent,AContext);
if LeftResolved.BaseType=btModule then if LeftJS=nil then
begin RaiseNotSupported(El,AContext,20190131212553);
// e.g. system.inttostr() AContext.Access:=OldAccess;
// module path is created automatically end
if Assigned(OnConvertRight) then else
Result:=OnConvertRight(RightEl,AContext,Data) begin
// call non static helper method
Result:=CreateCallNonStaticHelperMethod(Proc,El,AContext);
exit;
end;
end
else else
Result:=ConvertIdentifierExpr(RightEl,TPrimitiveExpr(RightEl).Value,AContext); RaiseNotSupported(El,AContext,20190131170119,GetObjName(RightRefDecl));
exit;
end; end;
// convert left side if LeftJS=nil then
OldAccess:=AContext.Access; begin
AContext.Access:=caRead; // check Left
Left:=ConvertExpression(El.left,AContext);
if Left=nil then
RaiseNotSupported(El,AContext,20190116110446);
AContext.Access:=OldAccess;
// convert right side if aResolver<>nil then
DotContext:=TDotContext.Create(El,Left,AContext); aResolver.ComputeElement(El.left,LeftResolved,[])
Right:=nil; else
LeftResolved:=Default(TPasResolverResult);
if LeftResolved.BaseType=btModule then
begin
// e.g. system.inttostr()
// module path is created automatically
if Assigned(OnConvertRight) then
Result:=OnConvertRight(RightEl,AContext,Data)
else
Result:=ConvertIdentifierExpr(RightEl,TPrimitiveExpr(RightEl).Value,AContext);
exit;
end;
// convert LeftJS side
OldAccess:=AContext.Access;
AContext.Access:=caRead;
LeftJS:=ConvertExpression(El.left,AContext);
if LeftJS=nil then
RaiseNotSupported(El,AContext,20190116110446);
AContext.Access:=OldAccess;
end;
// convert RightJS side
DotContext:=TDotContext.Create(El,LeftJS,AContext);
RightJS:=nil;
try try
DotContext.LeftResolved:=LeftResolved; DotContext.LeftResolved:=LeftResolved;
if Assigned(OnConvertRight) then if Assigned(OnConvertRight) then
Right:=OnConvertRight(RightEl,DotContext,Data) RightJS:=OnConvertRight(RightEl,DotContext,Data)
else else
Right:=ConvertIdentifierExpr(RightEl,TPrimitiveExpr(RightEl).Value,DotContext); RightJS:=ConvertIdentifierExpr(RightEl,TPrimitiveExpr(RightEl).Value,DotContext);
if DotContext.JS<>nil then if DotContext.JS<>nil then
begin begin
Left:=nil; LeftJS:=nil;
Right:=nil; RightJS:=nil;
exit(DotContext.JS); exit(DotContext.JS);
end; end;
finally finally
DotContext.Free; DotContext.Free;
if Right=nil then if RightJS=nil then
Left.Free; LeftJS.Free;
end; end;
if Right is TJSLiteral then if RightJS is TJSLiteral then
begin begin
Left.Free; LeftJS.Free;
exit(Right); exit(RightJS);
end; end;
// connect via dot // connect via dot
Result:=CreateDotExpression(El,Left,Right,true); Result:=CreateDotExpression(El,LeftJS,RightJS,true);
end; end;
function TPasToJSConverter.CreateIdentifierExpr(El: TPasElement; function TPasToJSConverter.CreateIdentifierExpr(El: TPasElement;
@ -16772,6 +16815,111 @@ begin
end; end;
end; end;
function TPasToJSConverter.CreateCallNonStaticHelperMethod(Proc: TPasProcedure;
Expr: TPasExpr; AContext: TConvertContext): TJSElement;
var
Helper: TPasClassType;
aResolver: TPas2JSResolver;
HelperForType, LoTypeEl: TPasType;
Bin: TBinaryExpr;
LeftResolved: TPasResolverResult;
SelfJS: TJSElement;
PosEl, Left: TPasExpr;
LeftArg: TPasArgument;
Path: String;
begin
Result:=nil;
aResolver:=AContext.Resolver;
Helper:=Proc.Parent as TPasClassType;
HelperForType:=aResolver.ResolveAliasType(Helper.HelperForType);
PosEl:=Expr;
if Expr is TBinaryExpr then
begin
Bin:=TBinaryExpr(Expr);
if Bin.OpCode<>eopSubIdent then
RaiseNotSupported(Expr,AContext,20190201163152);
Left:=Bin.left;
aResolver.ComputeElement(Left,LeftResolved,[]);
PosEl:=Bin.right;
end
else
RaiseNotSupported(Expr,AContext,20190201163210);
LoTypeEl:=LeftResolved.LoTypeEl;
if (Proc.ClassType=TPasClassFunction) or (Proc.ClassType=TPasClassProcedure) then
begin
// call non static helper class method
if LoTypeEl=nil then
RaiseNotSupported(PosEl,AContext,20190201163453,GetResolverResultDbg(LeftResolved));
if (LeftResolved.IdentEl.ClassType=TPasClassType) then
begin
// ClassType.HelperCall -> HelperType.HelperCall.apply(ClassType?,args?)
if TPasClassType(LeftResolved.IdentEl).IsExternal then
RaiseNotSupported(PosEl,AContext,20190201165636);
SelfJS:=CreateReferencePathExpr(LeftResolved.IdentEl,AContext);
end
else if (LoTypeEl.ClassType=TPasClassType) then
begin
// ClassInstance.HelperCall -> HelperType.HelperCall.apply(ClassInstance.$class?,args?)
if TPasClassType(LeftResolved.LoTypeEl).IsExternal then
RaiseNotSupported(PosEl,AContext,20190201165656);
Path:=CreateReferencePath(LeftResolved.IdentEl,AContext,rpkPathAndName)+'.'+GetBIName(pbivnPtrClass);
SelfJS:=CreatePrimitiveDotExpr(Path,Expr);
end
else if (LoTypeEl.ClassType=TPasClassOfType) then
begin
// ClassOfVar.HelperCall -> HelperType.HelperCall.apply(ClassOfVar?,args?)
SelfJS:=ConvertExpression(Left,AContext);
end
else
// forbidden in record and type helpers
RaiseNotSupported(PosEl,AContext,20190201162601);
end
else if (Proc.ClassType=TPasFunction) or (Proc.ClassType=TPasProcedure) then
begin
// method, neither static nor class method
if LeftResolved.IdentEl is TPasType then
RaiseNotSupported(PosEl,AContext,20190201170843);
if LoTypeEl is TPasClassType then
begin
// ClassInstance.HelperCall -> HelperType.HelperCall.apply(ClassInstance?,args?)
SelfJS:=ConvertExpression(Left,AContext);
end
else if HelperForType.ClassType=TPasClassType then
RaiseNotSupported(PosEl,AContext,20190203171241)
else if LeftResolved.IdentEl is TPasArgument then
begin
LeftArg:=TPasArgument(LeftResolved.IdentEl);
case LeftArg.Access of
argVar,argOut:
begin
// VarArg.HelperCall -> HelperType.HelperCall.apply(VarArg?,args?)
Path:=TransformVariableName(LeftArg,AContext);
SelfJS:=CreatePrimitiveDotExpr(Path,Expr);
end;
argConst:
begin
// ConstArg.HelperCall -> HelperType.HelperCall.apply({p: ConstArg,get,set-error}?,args?)
RaiseNotSupported(PosEl,AContext,20190201172006);
end;
end;
RaiseNotSupported(PosEl,AContext,20190201171117);
end;
// Var.HelperCall -> HelperType.HelperCall.apply({p: RecordVar,get,set}?,args?)
// FuncResult.HelperCall -> HelperType.HelperCall.apply({p: RecordFuncResult,get,set}?,args?)
// Literal.HelperCall -> HelperType.HelperCall.apply({p: Literal,get,set}?,args?)
RaiseNotSupported(PosEl,AContext,20190131211753);
end
else
RaiseNotSupported(PosEl,AContext,20190201162609);
// ToDo
if SelfJS=nil then
RaiseNotSupported(PosEl,AContext,20190203171010);
RaiseNotSupported(PosEl,AContext,20190201170016);
end;
function TPasToJSConverter.ConvertImplBlock(El: TPasImplBlock; function TPasToJSConverter.ConvertImplBlock(El: TPasImplBlock;
AContext: TConvertContext): TJSElement; AContext: TConvertContext): TJSElement;
begin begin

View File

@ -628,8 +628,17 @@ type
Procedure TestClassInterface_GUIDProperty; Procedure TestClassInterface_GUIDProperty;
// helpers // helpers
Procedure TestClassHelper_ClassVar; // ToDo Procedure TestClassHelper_ClassVar;
Procedure TestClassHelper_Method_AccessInstanceFields;
Procedure TestClassHelper_Method_Call;
//Procedure TestClassHelper_Constructor;
//Procedure TestClassHelper_InheritedObjFPC;
//Procedure TestClassHelper_InheritedDelphi;
// todo: TestClassHelper_Property
// todo: TestClassHelper_ClassProperty
// todo: TestClassHelper_Overload // todo: TestClassHelper_Overload
// todo: TestRecordHelper
// todo: TestTypeHelper
// proc types // proc types
Procedure TestProcType; Procedure TestProcType;
@ -18552,12 +18561,13 @@ begin
' TObject = class', ' TObject = class',
' end;', ' end;',
' THelper = class helper for TObject', ' THelper = class helper for TObject',
' const', ' const',
' One = 1;', ' One = 1;',
' Two: word = 2;', ' Two: word = 2;',
' class var Glob: word;', ' class var',
' function Foo(w: word): word;', ' Glob: word;',
' class function Bar(w: word): word;', ' function Foo(w: word): word;',
' class function Bar(w: word): word;',
' end;', ' end;',
'function THelper.foo(w: word): word;', 'function THelper.foo(w: word): word;',
'begin', 'begin',
@ -18640,6 +18650,145 @@ begin
''])); '']));
end; end;
procedure TTestModule.TestClassHelper_Method_AccessInstanceFields;
begin
StartProgram(false);
Add([
'type',
' TObject = class',
' FSize: word;',
' property Size: word read FSize write FSize;',
' end;',
' THelper = class helper for TObject',
' function Foo(w: word = 1): word;',
' end;',
'function THelper.foo(w: word): word;',
'begin',
' Result:=Size;',
' Size:=Size+2;',
' Self.Size:=Self.Size+3;',
' FSize:=FSize+4;',
' Self.FSize:=Self.FSize+5;',
' with Self do begin',
' Size:=Size+6;',
' FSize:=FSize+7;',
' FSize:=FSize+8;',
' end;',
'end;',
'begin',
'']);
ConvertProgram;
CheckSource('TestClassHelper_Method_AccessInstanceFields',
LinesToStr([ // statements
'rtl.createClass($mod, "TObject", null, function () {',
' this.$init = function () {',
' this.FSize = 0;',
' };',
' this.$final = function () {',
' };',
'});',
'rtl.createHelper($mod, "THelper", null, function () {',
' this.Foo = function (w) {',
' var Result = 0;',
' Result = this.FSize;',
' this.FSize = this.FSize + 2;',
' this.FSize = this.FSize + 3;',
' this.FSize = this.FSize + 4;',
' this.FSize = this.FSize + 5;',
' this.FSize = this.FSize + 6;',
' this.FSize = this.FSize + 7;',
' this.FSize = this.FSize + 8;',
' return Result;',
' };',
'});',
'']),
LinesToStr([ // $mod.$main
'']));
end;
procedure TTestModule.TestClassHelper_Method_Call;
begin
exit;
StartProgram(false);
Add([
'type',
' TObject = class',
' procedure Run(w: word = 10);',
' end;',
' THelper = class helper for TObject',
' function Foo(w: word = 1): word;',
' end;',
'procedure TObject.Run(w: word);',
'begin',
' Foo;',
' Foo();',
' Foo(2);',
' Self.Foo;',
' Self.Foo();',
' Self.Foo(3);',
' with Self do begin',
' Foo;',
' Foo();',
' Foo(4);',
' end;',
'end;',
'function THelper.foo(w: word): word;',
'begin',
' Run;',
' Run();',
' Run(11);',
' Foo;',
' Foo();',
' Foo(12);',
' Self.Foo;',
' Self.Foo();',
' Self.Foo(13);',
' with Self do begin',
' Foo;',
' Foo();',
' Foo(14);',
' end;',
'end;',
'var Obj: TObject;',
'begin',
' obj.Foo;',
' obj.Foo();',
' obj.Foo(21);',
' with obj do begin',
' Foo;',
' Foo();',
' Foo(22);',
' end;',
'']);
ConvertProgram;
CheckSource('TestClassHelper_Method_Call',
LinesToStr([ // statements
'rtl.createClass($mod, "TObject", null, function () {',
' this.$init = function () {',
' this.FSize = 0;',
' };',
' this.$final = function () {',
' };',
'});',
'rtl.createHelper($mod, "THelper", null, function () {',
' this.Foo = function (w) {',
' var Result = 0;',
' Result = this.FSize;',
' this.FSize = this.FSize + 2;',
' this.FSize = this.FSize + 3;',
' this.FSize = this.FSize + 4;',
' this.FSize = this.FSize + 5;',
' this.FSize = this.FSize + 6;',
' this.FSize = this.FSize + 7;',
' this.FSize = this.FSize + 8;',
' return Result;',
' };',
'});',
'']),
LinesToStr([ // $mod.$main
'']));
end;
procedure TTestModule.TestProcType; procedure TTestModule.TestProcType;
begin begin
StartProgram(false); StartProgram(false);