Selaa lähdekoodia

pastojs: type helper pass property

git-svn-id: trunk@41293 -
Mattias Gaertner 6 vuotta sitten
vanhempi
commit
ff59f3b570
2 muutettua tiedostoa jossa 1293 lisäystä ja 73 poistoa
  1. 150 54
      packages/pastojs/src/fppas2js.pp
  2. 1143 19
      packages/pastojs/tests/tcmodules.pas

+ 150 - 54
packages/pastojs/src/fppas2js.pp

@@ -2010,6 +2010,9 @@ const
   TempRefObjGetterName = 'get';
   TempRefObjSetterName = 'set';
   TempRefObjSetterArgName = 'v';
+  TempRefGetPathName = 'p';
+  TempRefSetPathName = 's';
+  TempRefParamName = 'a';
   IdentChars = ['0'..'9', 'A'..'Z', 'a'..'z','_'];
 
 function CodePointToJSString(u: longword): TJSString;
@@ -17043,21 +17046,56 @@ var
       end;
   end;
 
+  function CreateRefObj(PosEl: TPasElement;
+    GetExpr, SetExpr: TJSElement; SetterArgName: string;
+    PathExpr: TJSElement = nil): TJSObjectLiteral;
+  var
+    Obj: TJSObjectLiteral;
+    ObjLit: TJSObjectLiteralElement;
+    FuncSt: TJSFunctionDeclarationStatement;
+    RetSt: TJSReturnStatement;
+  begin
+    Obj:=TJSObjectLiteral(CreateElement(TJSObjectLiteral,PosEl));
+    Result:=Obj;
+
+    if PathExpr<>nil then
+      begin
+      // add "p:path"
+      ObjLit:=Obj.Elements.AddElement;
+      ObjLit.Name:=TJSString(TempRefGetPathName);
+      ObjLit.Expr:=PathExpr;
+      end;
+
+    // add "get: function(){return Left}"
+    ObjLit:=Obj.Elements.AddElement;
+    ObjLit.Name:=TempRefObjGetterName;
+    FuncSt:=CreateFunctionSt(PosEl);
+    ObjLit.Expr:=FuncSt;
+    RetSt:=TJSReturnStatement(CreateElement(TJSReturnStatement,PosEl));
+    FuncSt.AFunction.Body.A:=RetSt;
+    RetSt.Expr:=GetExpr;
+
+    // add "set: function(v){Left=v}"
+    ObjLit:=Obj.Elements.AddElement;
+    ObjLit.Name:=TempRefObjSetterName;
+    FuncSt:=CreateFunctionSt(PosEl);
+    ObjLit.Expr:=FuncSt;
+    if SetterArgName<>'' then
+      FuncSt.AFunction.Params.Add(SetterArgName);
+    FuncSt.AFunction.Body.A:=SetExpr;
+  end;
+
   function ConvertImplicitLeftIdentifier(PosEl: TPasElement;
     const LeftResolved: TPasResolverResult): TJSElement;
   var
     GetExpr, SetExpr: TJSElement;
     SetterArgName: string;
     AssignSt: TJSSimpleAssignStatement;
-    Obj: TJSObjectLiteral;
-    FuncSt: TJSFunctionDeclarationStatement;
-    RetSt: TJSReturnStatement;
-    ObjLit: TJSObjectLiteralElement;
     Arg: TPasArgument;
   begin
     // implicit Left (e.g. with Left do proc, or Self.proc)
 
-    if (LeftResolved.IdentEl is TPasArgument) then
+    if LeftResolved.IdentEl is TPasArgument then
       begin
       Arg:=TPasArgument(LeftResolved.IdentEl);
       if Arg.Access in [argVar,argOut] then
@@ -17087,30 +17125,82 @@ var
     else
       begin
       // SetExpr  rtl.raiseE("EPropReadOnly")
+      SetterArgName:='';
       SetExpr:=CreateRaisePropReadOnly(PosEl);
       end;
 
-    Obj:=TJSObjectLiteral(CreateElement(TJSObjectLiteral,PosEl));
-    Result:=Obj;
+    Result:=CreateRefObj(PosEl,GetExpr,SetExpr,SetterArgName);
+  end;
 
-    // add "get: function(){return Left}"
-    ObjLit:=Obj.Elements.AddElement;
-    ObjLit.Name:=TempRefObjGetterName;
-    FuncSt:=CreateFunctionSt(PosEl);
-    ObjLit.Expr:=FuncSt;
-    RetSt:=TJSReturnStatement(CreateElement(TJSReturnStatement,PosEl));
-    FuncSt.AFunction.Body.A:=RetSt;
-    RetSt.Expr:=GetExpr;
-    GetExpr:=nil;
+  function CreatePropertyReference(PosEl: TPasElement;
+    const LeftResolved: TPasResolverResult): TJSElement;
+  var
+    Prop: TPasProperty;
+    OldAccess: TCtxAccess;
+    GetExpr, SetExpr, LeftJS, PathExpr: TJSElement;
+    DotExpr: TJSDotMemberExpression;
+    AssignSt: TJSSimpleAssignStatement;
+    SetterArgName, aName: String;
+  begin
+    // explicit Left is property
+    // path.Prop.Proc or Prop.Proc
+    Prop:=TPasProperty(LeftResolved.IdentEl);
 
-    // add "set: function(v){Left=v}"
-    ObjLit:=Obj.Elements.AddElement;
-    ObjLit.Name:=TempRefObjSetterName;
-    FuncSt:=CreateFunctionSt(PosEl);
-    ObjLit.Expr:=FuncSt;
-    FuncSt.AFunction.Params.Add(SetterArgName);
-    FuncSt.AFunction.Body.A:=SetExpr;
-    SetExpr:=nil;
+    OldAccess:=AContext.Access;
+    AContext.Access:=caRead;
+    LeftJS:=ConvertExpression(Left,AContext);
+    AContext.Access:=OldAccess;
+    {$IFDEF VerbosePas2JS}
+    writeln('CreatePropertyReference LeftJS=',GetObjName(LeftJS));
+    {$ENDIF}
+
+    PathExpr:=nil;
+    SetterArgName:='';
+    if LeftJS.ClassType=TJSLiteral then
+      begin
+      // getter is a const
+      // convert to {get:function(){return value},set:function(v){ error }}
+      SetExpr:=CreateRaisePropReadOnly(PosEl);
+      GetExpr:=LeftJS;
+      end
+    else if LeftJS.ClassType=TJSDotMemberExpression then
+      begin
+      // getter is a field
+      // convert to {p:path,get:function(){return this.p.field},set:function(v){ this.p.field=v }}
+      DotExpr:=TJSDotMemberExpression(LeftJS);
+      PathExpr:=DotExpr.MExpr;
+      DotExpr.MExpr:=nil;
+      aName:=String(DotExpr.Name);
+      DotExpr.Free;
+      GetExpr:=CreateMemberExpression(['this',TempRefGetPathName,aName]);
+      AssignSt:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,PosEl));
+      if vmClass in Prop.VarModifiers then
+        // assign class field -> always use class path
+        AssignSt.LHS:=CreateDotExpression(PosEl,
+           CreateReferencePathExpr(Prop.Parent,AContext),
+           CreatePrimitiveDotExpr(aName,PosEl))
+      else
+        AssignSt.LHS:=CreateMemberExpression(['this',TempRefGetPathName,aName]);
+      SetExpr:=AssignSt;
+      SetterArgName:=TempRefObjSetterArgName;
+      AssignSt.Expr:=CreatePrimitiveDotExpr(SetterArgName,PosEl);
+      end
+    else if LeftJS.ClassType=TJSCallExpression then
+      begin
+      // getter is a function
+      // convert to {p:FuncResult(),get:function(){return this.p},set:function(v){ this.p=v }}
+      PathExpr:=TJSCallExpression(LeftJS);
+      GetExpr:=CreateMemberExpression(['this',TempRefGetPathName]);
+      AssignSt:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,PosEl));
+      AssignSt.LHS:=CreateMemberExpression(['this',TempRefGetPathName]);
+      SetExpr:=AssignSt;
+      SetterArgName:=TempRefObjSetterArgName;
+      AssignSt.Expr:=CreatePrimitiveDotExpr(SetterArgName,PosEl);
+      end
+    else
+      RaiseNotSupported(PosEl,AContext,20190210193605,GetObjName(LeftJS));
+
+    Result:=CreateRefObj(PosEl,GetExpr,SetExpr,SetterArgName,PathExpr);
   end;
 
   function CreateReference(PosEl: TPasElement;
@@ -17118,15 +17208,19 @@ var
   var
     ProcScope: TPas2JSProcedureScope;
   begin
-    ProcScope:=Proc.CustomData as TPas2JSProcedureScope;
-    if ProcScope.ImplProc<>nil then
-      ProcScope:=ProcScope.ImplProc.CustomData as TPas2JSProcedureScope;
-    if ProcScope.SelfArg=nil then
-      RaiseNotSupported(PosEl,AContext,20190209214906,GetObjName(Proc));
     if Left=nil then
       Result:=ConvertImplicitLeftIdentifier(PosEl,LeftResolved)
+    else if LeftResolved.IdentEl is TPasProperty then
+      Result:=CreatePropertyReference(PosEl,LeftResolved)
     else
+      begin
+      ProcScope:=Proc.CustomData as TPas2JSProcedureScope;
+      if ProcScope.ImplProc<>nil then
+        ProcScope:=ProcScope.ImplProc.CustomData as TPas2JSProcedureScope;
+      if ProcScope.SelfArg=nil then
+        RaiseNotSupported(PosEl,AContext,20190209214906,GetObjName(Proc));
       Result:=CreateProcCallArgRef(Left,LeftResolved,ProcScope.SelfArg,AContext);
+      end;
   end;
 
 var
@@ -17244,7 +17338,7 @@ begin
         Prop:=TPasProperty(Ref.Declaration);
       end;
     {$IFDEF VerbosePas2JS}
-    writeln('TPasToJSConverter.CreateCallHelperMethod IsConstructorNormalCall=',IsConstructorNormalCall,' Ref=',GetObjName(Ref),' Left=',GetObjName(Left),' ',GetResolverResultDbg(LeftResolved));
+    writeln('TPasToJSConverter.CreateCallHelperMethod IsStatic=',IsStatic,' IsConstructorNormalCall=',IsConstructorNormalCall,' Ref=',GetObjName(Ref),' Left=',GetObjName(Left),' ',GetResolverResultDbg(LeftResolved));
     {$ENDIF}
 
     if IsStatic then
@@ -17309,6 +17403,7 @@ begin
         if (C=TPasArgument)
             or (C=TPasVariable)
             or (C=TPasConst)
+            or (C=TPasProperty)
             or (C=TPasResultElement)
             or (C=TPasEnumValue) then
           begin
@@ -19198,7 +19293,6 @@ var
   MExpr, LastMExpr: TJSMemberExpression;
   k: integer;
   CurName: String;
-  Lit: TJSLiteral;
 begin
   if Length(Members) < 1 then
     DoError(20161024192715,'internal error: member expression needs at least one element');
@@ -19215,9 +19309,9 @@ begin
       else
         CurName:=copy(CurName,2,length(CurName)-1);
       MExpr := TJSBracketMemberExpression.Create(0,0,'');
-      Lit:=TJSLiteral.Create(0,0,'');
-      Lit.Value.CustomValue:=TJSString(CurName);
-      TJSBracketMemberExpression(MExpr).Name := Lit;
+      Prim := TJSPrimaryExpressionIdent.Create(0, 0, '');
+      Prim.Name:=TJSString(CurName);
+      TJSBracketMemberExpression(MExpr).Name := Prim;
       end
     else
       begin
@@ -20188,6 +20282,12 @@ begin
     if WithData.WithVarName='' then
       RaiseNotSupported(WithData.Expr,AContext,20190209092506,GetObjName(El));
     Prepend(Result,WithData.WithVarName);
+    if not (wesfOnlyTypeMembers in WithData.Flags)
+        and IsClassFunction(El) and (not TPasProcedure(El).IsStatic) then
+      begin
+      // with Obj do NonStaticClassMethod -> append .$class
+      Append_GetClass(El);
+      end;
     end
   else
     begin
@@ -20625,10 +20725,6 @@ end;
 function TPasToJSConverter.CreateProcCallArgRef(El: TPasExpr;
   ResolvedEl: TPasResolverResult; TargetArg: TPasArgument;
   AContext: TConvertContext): TJSElement;
-const
-  GetPathName = 'p';
-  SetPathName = 's';
-  ParamName = 'a';
 var
   Obj: TJSObjectLiteral;
 
@@ -20723,9 +20819,9 @@ begin
         // Will create "{p:GetPathExpr, get:function(){return GetExpr;},
         //                              set:function(v){SetExpr = v;}}"
         GetPathExpr:=CreatePrimitiveDotExpr(LeftStr(GetPath,GetDotPos-1),El);
-        GetExpr:=CreatePrimitiveDotExpr('this.'+GetPathName+'.'+copy(GetPath,GetDotPos+1),El);
+        GetExpr:=CreatePrimitiveDotExpr('this.'+TempRefGetPathName+'.'+copy(GetPath,GetDotPos+1),El);
         if SetExpr=nil then
-          SetExpr:=CreatePrimitiveDotExpr('this.'+GetPathName+'.'+copy(GetPath,GetDotPos+1),El);
+          SetExpr:=CreatePrimitiveDotExpr('this.'+TempRefGetPathName+'.'+copy(GetPath,GetDotPos+1),El);
         end
       else
         begin
@@ -20748,13 +20844,13 @@ begin
           if LeftStr(GetPath,GetDotPos)=LeftStr(SetPath,SetDotPos) then
             begin
             // use GetPathExpr for setter
-            SetExpr:=CreatePrimitiveDotExpr('this.'+GetPathName+'.'+copy(SetPath,GetDotPos+1),El);
+            SetExpr:=CreatePrimitiveDotExpr('this.'+TempRefGetPathName+'.'+copy(SetPath,GetDotPos+1),El);
             end
           else
             begin
             // setter needs its own SetPathExpr
             SetPathExpr:=CreatePrimitiveDotExpr(LeftStr(SetPath,SetDotPos-1),El);
-            SetExpr:=CreatePrimitiveDotExpr('this.'+SetPathName+'.'+copy(SetPath,GetDotPos+1),El);
+            SetExpr:=CreatePrimitiveDotExpr('this.'+TempRefSetPathName+'.'+copy(SetPath,GetDotPos+1),El);
             end;
           end;
         end;
@@ -20773,11 +20869,11 @@ begin
       // SetExpr:  this.p.i
       DotExpr:=TJSDotMemberExpression(FullGetter);
       GetPathExpr:=DotExpr.MExpr;
-      DotExpr.MExpr:=CreatePrimitiveDotExpr('this.'+GetPathName,El);
+      DotExpr.MExpr:=CreatePrimitiveDotExpr('this.'+TempRefGetPathName,El);
       GetExpr:=DotExpr;
       FullGetter:=nil;
       if (rrfWritable in ResolvedEl.Flags) then
-        SetExpr:=CreatePrimitiveDotExpr('this.'+GetPathName+'.'+String(DotExpr.Name),El)
+        SetExpr:=CreatePrimitiveDotExpr('this.'+TempRefGetPathName+'.'+String(DotExpr.Name),El)
       else
         SetExpr:=IfReadOnlyCreateRaiseE(ParamContext);
       end
@@ -20795,22 +20891,22 @@ begin
       ParamExpr:=BracketExpr.Name;
 
       // create "a:ParamExpr"
-      AddVar(ParamName,ParamExpr);
+      AddVar(TempRefParamName,ParamExpr);
 
       // create GetPathExpr "this.arr"
       GetPathExpr:=BracketExpr.MExpr;
 
       // GetExpr  "this.p[this.a]"
-      BracketExpr.MExpr:=CreatePrimitiveDotExpr('this.'+GetPathName,El);
-      BracketExpr.Name:=CreatePrimitiveDotExpr('this.'+ParamName,El);
+      BracketExpr.MExpr:=CreatePrimitiveDotExpr('this.'+TempRefGetPathName,El);
+      BracketExpr.Name:=CreatePrimitiveDotExpr('this.'+TempRefParamName,El);
       GetExpr:=BracketExpr;
       FullGetter:=nil;
 
       // SetExpr  "this.p[this.a]"
       BracketExpr:=TJSBracketMemberExpression(CreateElement(TJSBracketMemberExpression,El));
       SetExpr:=BracketExpr;
-      BracketExpr.MExpr:=CreatePrimitiveDotExpr('this.'+GetPathName,El);
-      BracketExpr.Name:=CreatePrimitiveDotExpr('this.'+ParamName,El);
+      BracketExpr.MExpr:=CreatePrimitiveDotExpr('this.'+TempRefGetPathName,El);
+      BracketExpr.Name:=CreatePrimitiveDotExpr('this.'+TempRefParamName,El);
       end
     else if FullGetter.ClassType=TJSCallExpression then
       begin
@@ -20823,14 +20919,14 @@ begin
       // }
 
       // create "p:FullGetter"
-      AddVar(ParamName,FullGetter);
+      AddVar(TempRefParamName,FullGetter);
       FullGetter:=nil;
 
       // GetExpr  "this.a"
-      GetExpr:=CreatePrimitiveDotExpr('this.'+ParamName,El);
+      GetExpr:=CreatePrimitiveDotExpr('this.'+TempRefParamName,El);
 
       // SetExpr  "this.a"
-      SetExpr:=CreatePrimitiveDotExpr('this.'+ParamName,El);
+      SetExpr:=CreatePrimitiveDotExpr('this.'+TempRefParamName,El);
       end
     else if FullGetter.ClassType=TJSLiteral then
       begin
@@ -20900,7 +20996,7 @@ begin
       RaiseInconsistency(20170213225940,El);
 
     // add   p:GetPathExpr
-    AddVar(GetPathName,GetPathExpr);
+    AddVar(TempRefGetPathName,GetPathExpr);
 
     // add   get:function(){ return GetExpr; }
     ObjLit:=Obj.Elements.AddElement;
@@ -20913,7 +21009,7 @@ begin
     GetExpr:=nil;
 
     // add   s:SetPathExpr
-    AddVar(SetPathName,SetPathExpr);
+    AddVar(TempRefSetPathName,SetPathExpr);
 
     // add   set:function(v){ SetExpr }
     ObjLit:=Obj.Elements.AddElement;

+ 1143 - 19
packages/pastojs/tests/tcmodules.pas

@@ -645,6 +645,7 @@ type
     Procedure TestClassHelper_ClassPropertyStatic;
     Procedure TestClassHelper_ClassProperty_Array;
     Procedure TestClassHelper_ForIn;
+    Procedure TestClassHelper_PassProperty;
     // ToDo: RTTI, class property static/nonstatic
     Procedure TestExtClassHelper_ClassVar;
     Procedure TestExtClassHelper_Method_Call;
@@ -652,14 +653,19 @@ type
     Procedure TestRecordHelper_Method_Call;
     Procedure TestRecordHelper_Constructor;
     Procedure TestTypeHelper_ClassVar;
-    Procedure TestTypeHelper_ResultElement;
-    Procedure TestTypeHelper_Args;
-    Procedure TestTypeHelper_VarConst;
-    Procedure TestTypeHelper_FuncResult;
-    // todo: TestTypeHelper_Property
-    // todo: TestTypeHelper_Property_Array
-    // todo: TestTypeHelper_ClassProperty
-    // todo: TestTypeHelper_ClassProperty_Array
+    Procedure TestTypeHelper_PassResultElement;
+    Procedure TestTypeHelper_PassArgs;
+    Procedure TestTypeHelper_PassVarConst;
+    Procedure TestTypeHelper_PassFuncResult;
+    Procedure TestTypeHelper_PassPropertyField;
+    Procedure TestTypeHelper_PassPropertyGetter;
+    Procedure TestTypeHelper_PassClassPropertyField;
+    Procedure TestTypeHelper_PassClassPropertyGetterStatic;
+    Procedure TestTypeHelper_PassClassPropertyGetterNonStatic;
+    Procedure TestTypeHelper_Property;
+    Procedure TestTypeHelper_Property_Array;
+    Procedure TestTypeHelper_ClassProperty;
+    Procedure TestTypeHelper_ClassProperty_Array;
     Procedure TestTypeHelper_ClassMethod;
     Procedure TestTypeHelper_Constructor;
     Procedure TestTypeHelper_Word;
@@ -11902,7 +11908,7 @@ begin
     'var $with2 = $mod.Obj;',
     '$mod.TObject.Fx = $with2.Fy + 1;',
     '$mod.TObject.Fy = $with2.Fx + 2;',
-    '$with2.SetInt($with2.GetInt() + 3);',
+    '$with2.$class.SetInt($with2.$class.GetInt() + 3);',
     '']));
 end;
 
@@ -19938,7 +19944,7 @@ begin
     '$mod.TObjHelper.SetLeft.apply($mod.b.$class, $mod.TObjHelper.GetLeft.apply($mod.b.$class) + 14);',
     'var $with1 = $mod.b;',
     '$mod.TObject.FSize = $with1.FSize + 31;',
-    '$with1.SetSpeed($with1.GetSpeed() + 32);',
+    '$with1.$class.SetSpeed($with1.$class.GetSpeed() + 32);',
     '$mod.TObjHelper.SetLeft.apply($with1.$class, $mod.TObjHelper.GetLeft.apply($with1.$class) + 33);',
     '$mod.TObjHelper.SetLeft.apply($with1.$class, $mod.TObjHelper.GetLeft.apply($with1.$class) + 34);',
     '$mod.TObject.FSize = $mod.c.FSize + 11;',
@@ -20246,7 +20252,7 @@ begin
     '$mod.TObjHelper.SetSize.apply($mod.b.$class, true, $mod.TObjHelper.GetSize.apply($mod.b.$class, false) + 13);',
     'var $with1 = $mod.b;',
     '$mod.TObjHelper.SetSize.apply($with1.$class, true, $mod.TObjHelper.GetSize.apply($with1.$class, false) + 21);',
-    '$with1.SetSpeed(true, $with1.GetSpeed(false) + 22);',
+    '$with1.$class.SetSpeed(true, $with1.$class.GetSpeed(false) + 22);',
     '$mod.TObjHelper.SetSize.apply($with1.$class, true, $mod.TObjHelper.GetSize.apply($with1.$class, false) + 23);',
     '$mod.TObjHelper.SetSize.apply($mod.c, true, $mod.TObjHelper.GetSize.apply($mod.c, false) + 11);',
     '$mod.c.SetSpeed(true, $mod.c.GetSpeed(false) + 12);',
@@ -20341,6 +20347,97 @@ begin
     '']));
 end;
 
+procedure TTestModule.TestClassHelper_PassProperty;
+begin
+  StartProgram(false);
+  Add([
+  'type',
+  '  TObject = class',
+  '    FField: TObject;',
+  '    property Field: TObject read FField write FField;',
+  '  end;',
+  '  THelper = class helper for TObject',
+  '    procedure Fly;',
+  '    class procedure Run;',
+  '    class procedure Jump; static;',
+  '  end;',
+  'procedure THelper.Fly;',
+  'begin',
+  '  Field.Fly;',
+  '  Field.Run;',
+  '  Field.Jump;',
+  '  with Field do begin',
+  '    Fly;',
+  '    Run;',
+  '    Jump;',
+  '  end;',
+  'end;',
+  'class procedure THelper.Run;',
+  'begin',
+  'end;',
+  'class procedure THelper.Jump;',
+  'begin',
+  'end;',
+  'var',
+  '  b: TObject;',
+  'begin',
+  '  b.Field.Fly;',
+  '  b.Field.Run;',
+  '  b.Field.Jump;',
+  '  with b do begin',
+  '    Field.Run;',
+  '    Field.Fly;',
+  '    Field.Jump;',
+  '  end;',
+  '  with b.Field do begin',
+  '    Run;',
+  '    Fly;',
+  '    Jump;',
+  '  end;',
+  '']);
+  ConvertProgram;
+  CheckSource('TestClassHelper_PassProperty',
+    LinesToStr([ // statements
+    'rtl.createClass($mod, "TObject", null, function () {',
+    '  this.$init = function () {',
+    '    this.FField = null;',
+    '  };',
+    '  this.$final = function () {',
+    '    this.FField = undefined;',
+    '  };',
+    '});',
+    'rtl.createHelper($mod, "THelper", null, function () {',
+    '  this.Fly = function () {',
+    '    $mod.THelper.Fly.apply(this.FField);',
+    '    $mod.THelper.Run.apply(this.FField.$class);',
+    '    $mod.THelper.Jump();',
+    '    var $with1 = this.FField;',
+    '    $mod.THelper.Fly.apply($with1);',
+    '    $mod.THelper.Run.apply($with1.$class);',
+    '    $mod.THelper.Jump();',
+    '  };',
+    '  this.Run = function () {',
+    '  };',
+    '  this.Jump = function () {',
+    '  };',
+    '});',
+    'this.b = null;',
+    '']),
+    LinesToStr([ // $mod.$main
+    '$mod.THelper.Fly.apply($mod.b.FField);',
+    '$mod.THelper.Run.apply($mod.b.FField.$class);',
+    '$mod.THelper.Jump();',
+    'var $with1 = $mod.b;',
+    '$mod.THelper.Run.apply($with1.FField.$class);',
+    '$mod.THelper.Fly.apply($with1.FField);',
+    '$mod.THelper.Jump();',
+    'var $with2 = $mod.b.FField;',
+    '$mod.THelper.Run.apply($with2.$class);',
+    '$mod.THelper.Fly.apply($with2);',
+    '$mod.THelper.Jump();',
+    '']));
+end;
+
 procedure TTestModule.TestExtClassHelper_ClassVar;
 begin
   StartProgram(false);
@@ -20864,7 +20961,7 @@ begin
     '']));
 end;
 
-procedure TTestModule.TestTypeHelper_ResultElement;
+procedure TTestModule.TestTypeHelper_PassResultElement;
 begin
   StartProgram(false);
   Add([
@@ -20896,7 +20993,7 @@ begin
   'begin',
   '']);
   ConvertProgram;
-  CheckSource('TestTypeHelper_ResultElement',
+  CheckSource('TestTypeHelper_PassResultElement',
     LinesToStr([ // statements
     'rtl.createHelper($mod, "THelper", null, function () {',
     '  this.DoIt = function (e) {',
@@ -20949,7 +21046,7 @@ begin
     '']));
 end;
 
-procedure TTestModule.TestTypeHelper_Args;
+procedure TTestModule.TestTypeHelper_PassArgs;
 begin
   StartProgram(false);
   Add([
@@ -20979,7 +21076,7 @@ begin
   'begin',
   '']);
   ConvertProgram;
-  CheckSource('TestTypeHelper_Args',
+  CheckSource('TestTypeHelper_PassArgs',
     LinesToStr([ // statements
     'rtl.createHelper($mod, "THelper", null, function () {',
     '  this.DoIt = function (e) {',
@@ -21031,7 +21128,7 @@ begin
     '']));
 end;
 
-procedure TTestModule.TestTypeHelper_VarConst;
+procedure TTestModule.TestTypeHelper_PassVarConst;
 begin
   StartProgram(false);
   Add([
@@ -21056,7 +21153,7 @@ begin
   '  with r do DoIt;',
   '']);
   ConvertProgram;
-  CheckSource('TestTypeHelper_VarConst',
+  CheckSource('TestTypeHelper_PassVarConst',
     LinesToStr([ // statements
     'rtl.createHelper($mod, "THelper", null, function () {',
     '  this.DoIt = function (e) {',
@@ -21123,7 +21220,7 @@ begin
     '']));
 end;
 
-procedure TTestModule.TestTypeHelper_FuncResult;
+procedure TTestModule.TestTypeHelper_PassFuncResult;
 begin
   StartProgram(false);
   Add([
@@ -21145,7 +21242,7 @@ begin
   '  with Foo() do DoIt;',
   '']);
   ConvertProgram;
-  CheckSource('TestTypeHelper_FuncResult',
+  CheckSource('TestTypeHelper_PassFuncResult',
     LinesToStr([ // statements
     'rtl.createHelper($mod, "THelper", null, function () {',
     '  this.DoIt = function (e) {',
@@ -21196,6 +21293,1033 @@ begin
     '']));
 end;
 
+procedure TTestModule.TestTypeHelper_PassPropertyField;
+begin
+  StartProgram(false);
+  Add([
+  '{$modeswitch typehelpers}',
+  'type',
+  '  TObject = class',
+  '    FField: word;',
+  '    procedure SetField(Value: word);',
+  '    property Field: word read FField write SetField;',
+  '  end;',
+  '  THelper = type helper for word',
+  '    procedure Fly;',
+  '    class procedure Run; static;',
+  '  end;',
+  'procedure TObject.SetField(Value: word);',
+  'begin',
+  '  Field.Fly;',
+  '  Field.Run;',
+  '  Self.Field.Fly;',
+  '  Self.Field.Run;',
+  '  with Self do begin',
+  '    Field.Fly;',
+  '    Field.Run;',
+  '  end;',
+  '  with Self.Field do begin',
+  '    Fly;',
+  '    Run;',
+  '  end;',
+  'end;',
+  'procedure THelper.Fly;',
+  'begin',
+  'end;',
+  'class procedure THelper.Run;',
+  'begin',
+  'end;',
+  'var',
+  '  o: TObject;',
+  'begin',
+  '  o.Field.Fly;',
+  '  o.Field.Run;',
+  '  with o do begin',
+  '    Field.Fly;',
+  '    Field.Run;',
+  '  end;',
+  '  with o.Field do begin',
+  '    Fly;',
+  '    Run;',
+  '  end;',
+  '']);
+  ConvertProgram;
+  CheckSource('TestTypeHelper_PassPropertyField',
+    LinesToStr([ // statements
+    'rtl.createClass($mod, "TObject", null, function () {',
+    '  this.$init = function () {',
+    '    this.FField = 0;',
+    '  };',
+    '  this.$final = function () {',
+    '  };',
+    '  this.SetField = function (Value) {',
+    '    $mod.THelper.Fly.apply({',
+    '      p: this,',
+    '      get: function () {',
+    '          return this.p.FField;',
+    '        },',
+    '      set: function (v) {',
+    '          this.p.FField = v;',
+    '        }',
+    '    });',
+    '    $mod.THelper.Run();',
+    '    $mod.THelper.Fly.apply({',
+    '      p: this,',
+    '      get: function () {',
+    '          return this.p.FField;',
+    '        },',
+    '      set: function (v) {',
+    '          this.p.FField = v;',
+    '        }',
+    '    });',
+    '    $mod.THelper.Run();',
+    '    $mod.THelper.Fly.apply({',
+    '      p: this,',
+    '      get: function () {',
+    '          return this.p.FField;',
+    '        },',
+    '      set: function (v) {',
+    '          this.p.FField = v;',
+    '        }',
+    '    });',
+    '    $mod.THelper.Run();',
+    '    var $with1 = this.FField;',
+    '    $mod.THelper.Fly.apply({',
+    '      get: function () {',
+    '          return $with1;',
+    '        },',
+    '      set: function (v) {',
+    '          $with1 = v;',
+    '        }',
+    '    });',
+    '    $mod.THelper.Run();',
+    '  };',
+    '});',
+    'rtl.createHelper($mod, "THelper", null, function () {',
+    '  this.Fly = function () {',
+    '  };',
+    '  this.Run = function () {',
+    '  };',
+    '});',
+    'this.o = null;',
+    '']),
+    LinesToStr([ // $mod.$main
+    '$mod.THelper.Fly.apply({',
+    '  p: $mod.o,',
+    '  get: function () {',
+    '      return this.p.FField;',
+    '    },',
+    '  set: function (v) {',
+    '      this.p.FField = v;',
+    '    }',
+    '});',
+    '$mod.THelper.Run();',
+    'var $with1 = $mod.o;',
+    '$mod.THelper.Fly.apply({',
+    '  p: $with1,',
+    '  get: function () {',
+    '      return this.p.FField;',
+    '    },',
+    '  set: function (v) {',
+    '      this.p.FField = v;',
+    '    }',
+    '});',
+    '$mod.THelper.Run();',
+    'var $with2 = $mod.o.FField;',
+    '$mod.THelper.Fly.apply({',
+    '  get: function () {',
+    '      return $with2;',
+    '    },',
+    '  set: function (v) {',
+    '      $with2 = v;',
+    '    }',
+    '});',
+    '$mod.THelper.Run();',
+    '']));
+end;
+
+procedure TTestModule.TestTypeHelper_PassPropertyGetter;
+begin
+  StartProgram(false);
+  Add([
+  '{$modeswitch typehelpers}',
+  'type',
+  '  TObject = class',
+  '    FField: word;',
+  '    function GetField: word;',
+  '    property Field: word read GetField write FField;',
+  '  end;',
+  '  THelper = type helper for word',
+  '    procedure Fly;',
+  '    class procedure Run; static;',
+  '  end;',
+  'function TObject.GetField: word;',
+  'begin',
+  '  Field.Fly;',
+  '  Field.Run;',
+  '  Self.Field.Fly;',
+  '  Self.Field.Run;',
+  '  with Self do begin',
+  '    Field.Fly;',
+  '    Field.Run;',
+  '  end;',
+  '  with Self.Field do begin',
+  '    Fly;',
+  '    Run;',
+  '  end;',
+  'end;',
+  'procedure THelper.Fly;',
+  'begin',
+  'end;',
+  'class procedure THelper.Run;',
+  'begin',
+  'end;',
+  'var',
+  '  o: TObject;',
+  'begin',
+  '  o.Field.Fly;',
+  '  o.Field.Run;',
+  '  with o do begin',
+  '    Field.Fly;',
+  '    Field.Run;',
+  '  end;',
+  '  with o.Field do begin',
+  '    Fly;',
+  '    Run;',
+  '  end;',
+  '']);
+  ConvertProgram;
+  CheckSource('TestTypeHelper_PassPropertyGetter',
+    LinesToStr([ // statements
+    'rtl.createClass($mod, "TObject", null, function () {',
+    '  this.$init = function () {',
+    '    this.FField = 0;',
+    '  };',
+    '  this.$final = function () {',
+    '  };',
+    '  this.GetField = function () {',
+    '    var Result = 0;',
+    '    $mod.THelper.Fly.apply({',
+    '      p: this.GetField(),',
+    '      get: function () {',
+    '          return this.p;',
+    '        },',
+    '      set: function (v) {',
+    '          this.p = v;',
+    '        }',
+    '    });',
+    '    $mod.THelper.Run();',
+    '    $mod.THelper.Fly.apply({',
+    '      p: this.GetField(),',
+    '      get: function () {',
+    '          return this.p;',
+    '        },',
+    '      set: function (v) {',
+    '          this.p = v;',
+    '        }',
+    '    });',
+    '    $mod.THelper.Run();',
+    '    $mod.THelper.Fly.apply({',
+    '      p: this.GetField(),',
+    '      get: function () {',
+    '          return this.p;',
+    '        },',
+    '      set: function (v) {',
+    '          this.p = v;',
+    '        }',
+    '    });',
+    '    $mod.THelper.Run();',
+    '    var $with1 = this.GetField();',
+    '    $mod.THelper.Fly.apply({',
+    '      get: function () {',
+    '          return $with1;',
+    '        },',
+    '      set: function (v) {',
+    '          $with1 = v;',
+    '        }',
+    '    });',
+    '    $mod.THelper.Run();',
+    '    return Result;',
+    '  };',
+    '});',
+    'rtl.createHelper($mod, "THelper", null, function () {',
+    '  this.Fly = function () {',
+    '  };',
+    '  this.Run = function () {',
+    '  };',
+    '});',
+    'this.o = null;',
+    '']),
+    LinesToStr([ // $mod.$main
+    '$mod.THelper.Fly.apply({',
+    '  p: $mod.o.GetField(),',
+    '  get: function () {',
+    '      return this.p;',
+    '    },',
+    '  set: function (v) {',
+    '      this.p = v;',
+    '    }',
+    '});',
+    '$mod.THelper.Run();',
+    'var $with1 = $mod.o;',
+    '$mod.THelper.Fly.apply({',
+    '  p: $with1.GetField(),',
+    '  get: function () {',
+    '      return this.p;',
+    '    },',
+    '  set: function (v) {',
+    '      this.p = v;',
+    '    }',
+    '});',
+    '$mod.THelper.Run();',
+    'var $with2 = $mod.o.GetField();',
+    '$mod.THelper.Fly.apply({',
+    '  get: function () {',
+    '      return $with2;',
+    '    },',
+    '  set: function (v) {',
+    '      $with2 = v;',
+    '    }',
+    '});',
+    '$mod.THelper.Run();',
+    '']));
+end;
+
+procedure TTestModule.TestTypeHelper_PassClassPropertyField;
+begin
+  StartProgram(false);
+  Add([
+  '{$modeswitch typehelpers}',
+  'type',
+  '  TObject = class',
+  '    class var FField: word;',
+  '    class procedure SetField(Value: word);',
+  '    class property Field: word read FField write SetField;',
+  '  end;',
+  '  THelper = type helper for word',
+  '    procedure Fly(n: byte);',
+  '  end;',
+  'class procedure TObject.SetField(Value: word);',
+  'begin',
+  '  Field.Fly(1);',
+  '  Self.Field.Fly(2);',
+  '  with Self do Field.Fly(3);',
+  '  with Self.Field do Fly(4);',
+  '  TObject.Field.Fly(5);',
+  '  with TObject do Field.Fly(6);',
+  '  with TObject.Field do Fly(7);',
+  'end;',
+  'procedure THelper.Fly(n: byte);',
+  'begin',
+  'end;',
+  'var',
+  '  o: TObject;',
+  'begin',
+  '  o.Field.Fly(11);',
+  '  with o do Field.Fly(12);',
+  '  with o.Field do Fly(13);',
+  '  TObject.Field.Fly(14);',
+  '  with TObject do Field.Fly(15);',
+  '  with TObject.Field do Fly(16);',
+  '']);
+  ConvertProgram;
+  CheckSource('TestTypeHelper_PassClassPropertyField',
+    LinesToStr([ // statements
+    'rtl.createClass($mod, "TObject", null, function () {',
+    '  this.FField = 0;',
+    '  this.$init = function () {',
+    '  };',
+    '  this.$final = function () {',
+    '  };',
+    '  this.SetField = function (Value) {',
+    '    $mod.THelper.Fly.apply({',
+    '      p: this,',
+    '      get: function () {',
+    '          return this.p.FField;',
+    '        },',
+    '      set: function (v) {',
+    '          $mod.TObject.FField = v;',
+    '        }',
+    '    }, 1);',
+    '    $mod.THelper.Fly.apply({',
+    '      p: this,',
+    '      get: function () {',
+    '          return this.p.FField;',
+    '        },',
+    '      set: function (v) {',
+    '          $mod.TObject.FField = v;',
+    '        }',
+    '    }, 2);',
+    '    $mod.THelper.Fly.apply({',
+    '      p: this,',
+    '      get: function () {',
+    '          return this.p.FField;',
+    '        },',
+    '      set: function (v) {',
+    '          $mod.TObject.FField = v;',
+    '        }',
+    '    }, 3);',
+    '    var $with1 = this.FField;',
+    '    $mod.THelper.Fly.apply({',
+    '      get: function () {',
+    '          return $with1;',
+    '        },',
+    '      set: function (v) {',
+    '          $with1 = v;',
+    '        }',
+    '    }, 4);',
+    '    $mod.THelper.Fly.apply({',
+    '      p: $mod.TObject,',
+    '      get: function () {',
+    '          return this.p.FField;',
+    '        },',
+    '      set: function (v) {',
+    '          $mod.TObject.FField = v;',
+    '        }',
+    '    }, 5);',
+    '    var $with2 = $mod.TObject;',
+    '    $mod.THelper.Fly.apply({',
+    '      p: $with2,',
+    '      get: function () {',
+    '          return this.p.FField;',
+    '        },',
+    '      set: function (v) {',
+    '          $mod.TObject.FField = v;',
+    '        }',
+    '    }, 6);',
+    '    var $with3 = $mod.TObject.FField;',
+    '    $mod.THelper.Fly.apply({',
+    '      get: function () {',
+    '          return $with3;',
+    '        },',
+    '      set: function (v) {',
+    '          $with3 = v;',
+    '        }',
+    '    }, 7);',
+    '  };',
+    '});',
+    'rtl.createHelper($mod, "THelper", null, function () {',
+    '  this.Fly = function (n) {',
+    '  };',
+    '});',
+    'this.o = null;',
+    '']),
+    LinesToStr([ // $mod.$main
+    '$mod.THelper.Fly.apply({',
+    '  p: $mod.o,',
+    '  get: function () {',
+    '      return this.p.FField;',
+    '    },',
+    '  set: function (v) {',
+    '      $mod.TObject.FField = v;',
+    '    }',
+    '}, 11);',
+    'var $with1 = $mod.o;',
+    '$mod.THelper.Fly.apply({',
+    '  p: $with1,',
+    '  get: function () {',
+    '      return this.p.FField;',
+    '    },',
+    '  set: function (v) {',
+    '      $mod.TObject.FField = v;',
+    '    }',
+    '}, 12);',
+    'var $with2 = $mod.o.FField;',
+    '$mod.THelper.Fly.apply({',
+    '  get: function () {',
+    '      return $with2;',
+    '    },',
+    '  set: function (v) {',
+    '      $with2 = v;',
+    '    }',
+    '}, 13);',
+    '$mod.THelper.Fly.apply({',
+    '  p: $mod.TObject,',
+    '  get: function () {',
+    '      return this.p.FField;',
+    '    },',
+    '  set: function (v) {',
+    '      $mod.TObject.FField = v;',
+    '    }',
+    '}, 14);',
+    'var $with3 = $mod.TObject;',
+    '$mod.THelper.Fly.apply({',
+    '  p: $with3,',
+    '  get: function () {',
+    '      return this.p.FField;',
+    '    },',
+    '  set: function (v) {',
+    '      $mod.TObject.FField = v;',
+    '    }',
+    '}, 15);',
+    'var $with4 = $mod.TObject.FField;',
+    '$mod.THelper.Fly.apply({',
+    '  get: function () {',
+    '      return $with4;',
+    '    },',
+    '  set: function (v) {',
+    '      $with4 = v;',
+    '    }',
+    '}, 16);',
+    '']));
+end;
+
+procedure TTestModule.TestTypeHelper_PassClassPropertyGetterStatic;
+begin
+  StartProgram(false);
+  Add([
+  '{$modeswitch typehelpers}',
+  'type',
+  '  TObject = class',
+  '    class var FField: word;',
+  '    class function GetField: word; static;',
+  '    class property Field: word read GetField write FField;',
+  '  end;',
+  '  THelper = type helper for word',
+  '    procedure Fly(n: byte);',
+  '  end;',
+  'class function TObject.GetField: word;',
+  'begin',
+  '  Field.Fly(1);',
+  '  TObject.Field.Fly(5);',
+  '  with TObject do Field.Fly(6);',
+  '  with TObject.Field do Fly(7);',
+  'end;',
+  'procedure THelper.Fly(n: byte);',
+  'begin',
+  'end;',
+  'var',
+  '  o: TObject;',
+  'begin',
+  '  o.Field.Fly(11);',
+  '  with o do Field.Fly(12);',
+  '  with o.Field do Fly(13);',
+  '']);
+  ConvertProgram;
+  CheckSource('TestTypeHelper_PassClassPropertyGetterStatic',
+    LinesToStr([ // statements
+    'rtl.createClass($mod, "TObject", null, function () {',
+    '  this.FField = 0;',
+    '  this.$init = function () {',
+    '  };',
+    '  this.$final = function () {',
+    '  };',
+    '  this.GetField = function () {',
+    '    var Result = 0;',
+    '    $mod.THelper.Fly.apply({',
+    '      p: this.GetField(),',
+    '      get: function () {',
+    '          return this.p;',
+    '        },',
+    '      set: function (v) {',
+    '          this.p = v;',
+    '        }',
+    '    }, 1);',
+    '    $mod.THelper.Fly.apply({',
+    '      p: $mod.TObject.GetField(),',
+    '      get: function () {',
+    '          return this.p;',
+    '        },',
+    '      set: function (v) {',
+    '          this.p = v;',
+    '        }',
+    '    }, 5);',
+    '    var $with1 = $mod.TObject;',
+    '    $mod.THelper.Fly.apply({',
+    '      p: $with1.GetField(),',
+    '      get: function () {',
+    '          return this.p;',
+    '        },',
+    '      set: function (v) {',
+    '          this.p = v;',
+    '        }',
+    '    }, 6);',
+    '    var $with2 = $mod.TObject.GetField();',
+    '    $mod.THelper.Fly.apply({',
+    '      get: function () {',
+    '          return $with2;',
+    '        },',
+    '      set: function (v) {',
+    '          $with2 = v;',
+    '        }',
+    '    }, 7);',
+    '    return Result;',
+    '  };',
+    '});',
+    'rtl.createHelper($mod, "THelper", null, function () {',
+    '  this.Fly = function (n) {',
+    '  };',
+    '});',
+    'this.o = null;',
+    '']),
+    LinesToStr([ // $mod.$main
+    '$mod.THelper.Fly.apply({',
+    '  p: $mod.o.GetField(),',
+    '  get: function () {',
+    '      return this.p;',
+    '    },',
+    '  set: function (v) {',
+    '      this.p = v;',
+    '    }',
+    '}, 11);',
+    'var $with1 = $mod.o;',
+    '$mod.THelper.Fly.apply({',
+    '  p: $with1.GetField(),',
+    '  get: function () {',
+    '      return this.p;',
+    '    },',
+    '  set: function (v) {',
+    '      this.p = v;',
+    '    }',
+    '}, 12);',
+    'var $with2 = $mod.o.GetField();',
+    '$mod.THelper.Fly.apply({',
+    '  get: function () {',
+    '      return $with2;',
+    '    },',
+    '  set: function (v) {',
+    '      $with2 = v;',
+    '    }',
+    '}, 13);',
+    '']));
+end;
+
+procedure TTestModule.TestTypeHelper_PassClassPropertyGetterNonStatic;
+begin
+  StartProgram(false);
+  Add([
+  '{$modeswitch typehelpers}',
+  'type',
+  '  TObject = class',
+  '    class var FField: word;',
+  '    class function GetField: word;',
+  '    class property Field: word read GetField write FField;',
+  '  end;',
+  '  TClass = class of TObject;',
+  '  THelper = type helper for word',
+  '    procedure Fly(n: byte);',
+  '  end;',
+  'class function TObject.GetField: word;',
+  'begin',
+  '  Field.Fly(1);',
+  '  Self.Field.Fly(5);',
+  '  with Self do Field.Fly(6);',
+  '  with Self.Field do Fly(7);',
+  'end;',
+  'procedure THelper.Fly(n: byte);',
+  'begin',
+  'end;',
+  'var',
+  '  o: TObject;',
+  '  c: TClass;',
+  'begin',
+  '  o.Field.Fly(11);',
+  '  with o do Field.Fly(12);',
+  '  with o.Field do Fly(13);',
+  '  c.Field.Fly(14);',
+  '  with c do Field.Fly(15);',
+  '  with c.Field do Fly(16);',
+  '']);
+  ConvertProgram;
+  CheckSource('TestTypeHelper_PassClassPropertyGetterNonStatic',
+    LinesToStr([ // statements
+    'rtl.createClass($mod, "TObject", null, function () {',
+    '  this.FField = 0;',
+    '  this.$init = function () {',
+    '  };',
+    '  this.$final = function () {',
+    '  };',
+    '  this.GetField = function () {',
+    '    var Result = 0;',
+    '    $mod.THelper.Fly.apply({',
+    '      p: this.GetField(),',
+    '      get: function () {',
+    '          return this.p;',
+    '        },',
+    '      set: function (v) {',
+    '          this.p = v;',
+    '        }',
+    '    }, 1);',
+    '    $mod.THelper.Fly.apply({',
+    '      p: this.GetField(),',
+    '      get: function () {',
+    '          return this.p;',
+    '        },',
+    '      set: function (v) {',
+    '          this.p = v;',
+    '        }',
+    '    }, 5);',
+    '    $mod.THelper.Fly.apply({',
+    '      p: this.GetField(),',
+    '      get: function () {',
+    '          return this.p;',
+    '        },',
+    '      set: function (v) {',
+    '          this.p = v;',
+    '        }',
+    '    }, 6);',
+    '    var $with1 = this.GetField();',
+    '    $mod.THelper.Fly.apply({',
+    '      get: function () {',
+    '          return $with1;',
+    '        },',
+    '      set: function (v) {',
+    '          $with1 = v;',
+    '        }',
+    '    }, 7);',
+    '    return Result;',
+    '  };',
+    '});',
+    'rtl.createHelper($mod, "THelper", null, function () {',
+    '  this.Fly = function (n) {',
+    '  };',
+    '});',
+    'this.o = null;',
+    'this.c = null;',
+    '']),
+    LinesToStr([ // $mod.$main
+    '$mod.THelper.Fly.apply({',
+    '  p: $mod.o.$class.GetField(),',
+    '  get: function () {',
+    '      return this.p;',
+    '    },',
+    '  set: function (v) {',
+    '      this.p = v;',
+    '    }',
+    '}, 11);',
+    'var $with1 = $mod.o;',
+    '$mod.THelper.Fly.apply({',
+    '  p: $with1.$class.GetField(),',
+    '  get: function () {',
+    '      return this.p;',
+    '    },',
+    '  set: function (v) {',
+    '      this.p = v;',
+    '    }',
+    '}, 12);',
+    'var $with2 = $mod.o.$class.GetField();',
+    '$mod.THelper.Fly.apply({',
+    '  get: function () {',
+    '      return $with2;',
+    '    },',
+    '  set: function (v) {',
+    '      $with2 = v;',
+    '    }',
+    '}, 13);',
+    '$mod.THelper.Fly.apply({',
+    '  p: $mod.c.GetField(),',
+    '  get: function () {',
+    '      return this.p;',
+    '    },',
+    '  set: function (v) {',
+    '      this.p = v;',
+    '    }',
+    '}, 14);',
+    'var $with3 = $mod.c;',
+    '$mod.THelper.Fly.apply({',
+    '  p: $with3.GetField(),',
+    '  get: function () {',
+    '      return this.p;',
+    '    },',
+    '  set: function (v) {',
+    '      this.p = v;',
+    '    }',
+    '}, 15);',
+    'var $with4 = $mod.c.GetField();',
+    '$mod.THelper.Fly.apply({',
+    '  get: function () {',
+    '      return $with4;',
+    '    },',
+    '  set: function (v) {',
+    '      $with4 = v;',
+    '    }',
+    '}, 16);',
+    '']));
+end;
+
+procedure TTestModule.TestTypeHelper_Property;
+begin
+  StartProgram(false);
+  Add([
+  '{$modeswitch typehelpers}',
+  'type',
+  '  THelper = type helper for word',
+  '    function GetSize: longint;',
+  '    procedure SetSize(Value: longint);',
+  '    property Size: longint read GetSize write SetSize;',
+  '  end;',
+  'function THelper.GetSize: longint;',
+  'begin',
+  '  Result:=Size+1;',
+  '  Size:=2;',
+  '  Result:=Self.Size+3;',
+  '  Self.Size:=4;',
+  '  with Self do begin',
+  '    Result:=Size+5;',
+  '    Size:=6;',
+  '  end;',
+  'end;',
+  'procedure THelper.SetSize(Value: longint);',
+  'begin',
+  'end;',
+  'var w: word;',
+  'begin',
+  '  w:=w.Size+7;',
+  '  w.Size:=w+8;',
+  '  with w do begin',
+  '    w:=Size+9;',
+  '    Size:=w+10;',
+  '  end;',
+  '']);
+  ConvertProgram;
+  CheckSource('TestTypeHelper_Property',
+    LinesToStr([ // statements
+    'rtl.createHelper($mod, "THelper", null, function () {',
+    '  this.GetSize = function () {',
+    '    var Result = 0;',
+    '    Result = $mod.THelper.GetSize.apply(this) + 1;',
+    '    $mod.THelper.SetSize.apply(this, 2);',
+    '    Result = $mod.THelper.GetSize.apply(this) + 3;',
+    '    $mod.THelper.SetSize.apply(this, 4);',
+    '    var $with1 = this.get();',
+    '    Result = $mod.THelper.GetSize.apply(this) + 5;',
+    '    $mod.THelper.SetSize.apply(this, 6);',
+    '    return Result;',
+    '  };',
+    '  this.SetSize = function (Value) {',
+    '  };',
+    '});',
+    'this.w = 0;',
+    '']),
+    LinesToStr([ // $mod.$main
+    '$mod.w = $mod.THelper.GetSize.apply({',
+    '  p: $mod,',
+    '  get: function () {',
+    '      return this.p.w;',
+    '    },',
+    '  set: function (v) {',
+    '      this.p.w = v;',
+    '    }',
+    '}) + 7;',
+    '$mod.THelper.SetSize.apply({',
+    '  p: $mod,',
+    '  get: function () {',
+    '      return this.p.w;',
+    '    },',
+    '  set: function (v) {',
+    '      this.p.w = v;',
+    '    }',
+    '}, $mod.w + 8);',
+    'var $with1 = $mod.w;',
+    '$mod.w = $mod.THelper.GetSize.apply({',
+    '  get: function () {',
+    '      return $with1;',
+    '    },',
+    '  set: function (v) {',
+    '      $with1 = v;',
+    '    }',
+    '}) + 9;',
+    '$mod.THelper.SetSize.apply({',
+    '  get: function () {',
+    '      return $with1;',
+    '    },',
+    '  set: function (v) {',
+    '      $with1 = v;',
+    '    }',
+    '}, $mod.w + 10);',
+    '']));
+end;
+
+procedure TTestModule.TestTypeHelper_Property_Array;
+begin
+  StartProgram(false);
+  Add([
+  '{$modeswitch typehelpers}',
+  'type',
+  '  THelper = type helper for word',
+  '    function GetItems(Index: byte): boolean;',
+  '    procedure SetItems(Index: byte; Value: boolean);',
+  '    property Items[Index: byte]: boolean read GetItems write SetItems;',
+  '  end;',
+  'function THelper.GetItems(Index: byte): boolean;',
+  'begin',
+  '  Result:=Items[1];',
+  '  Items[2]:=false;',
+  '  Result:=Self.Items[3];',
+  '  Self.Items[4]:=true;',
+  '  with Self do begin',
+  '    Result:=Items[5];',
+  '    Items[6]:=false;',
+  '  end;',
+  'end;',
+  'procedure THelper.SetItems(Index: byte; Value: boolean);',
+  'begin',
+  'end;',
+  'var',
+  '  w: word;',
+  '  b: boolean;',
+  'begin',
+  '  b:=w.Items[1];',
+  '  w.Items[2]:=b;',
+  '  with w do begin',
+  '    b:=Items[3];',
+  '    Items[4]:=b;',
+  '  end;',
+  '']);
+  ConvertProgram;
+  CheckSource('TestTypeHelper_Property_Array',
+    LinesToStr([ // statements
+    'rtl.createHelper($mod, "THelper", null, function () {',
+    '  this.GetItems = function (Index) {',
+    '    var Result = false;',
+    '    Result = $mod.THelper.GetItems.apply(this, 1);',
+    '    $mod.THelper.SetItems.apply(this, 2, false);',
+    '    Result = $mod.THelper.GetItems.apply(this, 3);',
+    '    $mod.THelper.SetItems.apply(this, 4, true);',
+    '    var $with1 = this.get();',
+    '    Result = $mod.THelper.GetItems.apply(this, 5);',
+    '    $mod.THelper.SetItems.apply(this, 6, false);',
+    '    return Result;',
+    '  };',
+    '  this.SetItems = function (Index, Value) {',
+    '  };',
+    '});',
+    'this.w = 0;',
+    'this.b = false;',
+    '']),
+    LinesToStr([ // $mod.$main
+    '$mod.b = $mod.THelper.GetItems.apply({',
+    '  p: $mod,',
+    '  get: function () {',
+    '      return this.p.w;',
+    '    },',
+    '  set: function (v) {',
+    '      this.p.w = v;',
+    '    }',
+    '}, 1);',
+    '$mod.THelper.SetItems.apply({',
+    '  p: $mod,',
+    '  get: function () {',
+    '      return this.p.w;',
+    '    },',
+    '  set: function (v) {',
+    '      this.p.w = v;',
+    '    }',
+    '}, 2, $mod.b);',
+    'var $with1 = $mod.w;',
+    '$mod.b = $mod.THelper.GetItems.apply({',
+    '  get: function () {',
+    '      return $with1;',
+    '    },',
+    '  set: function (v) {',
+    '      $with1 = v;',
+    '    }',
+    '}, 3);',
+    '$mod.THelper.SetItems.apply({',
+    '  get: function () {',
+    '      return $with1;',
+    '    },',
+    '  set: function (v) {',
+    '      $with1 = v;',
+    '    }',
+    '}, 4, $mod.b);',
+    '']));
+end;
+
+procedure TTestModule.TestTypeHelper_ClassProperty;
+begin
+  StartProgram(false);
+  Add([
+  '{$modeswitch typehelpers}',
+  'type',
+  '  THelper = type helper for word',
+  '    class function GetSize: longint; static;',
+  '    class procedure SetSize(Value: longint); static;',
+  '    class property Size: longint read GetSize write SetSize;',
+  '  end;',
+  'class function THelper.GetSize: longint;',
+  'begin',
+  '  Result:=Size+1;',
+  '  Size:=2;',
+  'end;',
+  'class procedure THelper.SetSize(Value: longint);',
+  'begin',
+  'end;',
+  'begin',
+  '']);
+  ConvertProgram;
+  CheckSource('TestTypeHelper_ClassProperty',
+    LinesToStr([ // statements
+    'rtl.createHelper($mod, "THelper", null, function () {',
+    '  this.GetSize = function () {',
+    '    var Result = 0;',
+    '    Result = $mod.THelper.GetSize() + 1;',
+    '    $mod.THelper.SetSize(2);',
+    '    return Result;',
+    '  };',
+    '  this.SetSize = function (Value) {',
+    '  };',
+    '});',
+    '']),
+    LinesToStr([ // $mod.$main
+    '']));
+end;
+
+procedure TTestModule.TestTypeHelper_ClassProperty_Array;
+begin
+  StartProgram(false);
+  Add([
+  '{$modeswitch typehelpers}',
+  'type',
+  '  THelper = type helper for word',
+  '    class function GetItems(Index: byte): boolean; static;',
+  '    class procedure SetItems(Index: byte; Value: boolean); static;',
+  '    class property Items[Index: byte]: boolean read GetItems write SetItems;',
+  '  end;',
+  'class function THelper.GetItems(Index: byte): boolean;',
+  'begin',
+  '  Result:=Items[1];',
+  '  Items[2]:=false;',
+  'end;',
+  'class procedure THelper.SetItems(Index: byte; Value: boolean);',
+  'begin',
+  'end;',
+  'var',
+  '  w: word;',
+  '  b: boolean;',
+  'begin',
+  '  b:=w.Items[1];',
+  '  w.Items[2]:=b;',
+  '  with w do begin',
+  '    b:=Items[3];',
+  '    Items[4]:=b;',
+  '  end;',
+  '']);
+  ConvertProgram;
+  CheckSource('TestTypeHelper_ClassProperty_Array',
+    LinesToStr([ // statements
+    'rtl.createHelper($mod, "THelper", null, function () {',
+    '  this.GetItems = function (Index) {',
+    '    var Result = false;',
+    '    Result = $mod.THelper.GetItems(1);',
+    '    $mod.THelper.SetItems(2, false);',
+    '    return Result;',
+    '  };',
+    '  this.SetItems = function (Index, Value) {',
+    '  };',
+    '});',
+    'this.w = 0;',
+    'this.b = false;',
+    '']),
+    LinesToStr([ // $mod.$main
+    '$mod.b = $mod.THelper.GetItems(1);',
+    '$mod.THelper.SetItems(2, $mod.b);',
+    'var $with1 = $mod.w;',
+    '$mod.b = $mod.THelper.GetItems(3);',
+    '$mod.THelper.SetItems(4, $mod.b);',
+    '']));
+end;
+
 procedure TTestModule.TestTypeHelper_ClassMethod;
 begin
   StartProgram(false);