Browse Source

pastojs: mark record fields as used when passing record to a jsvalue parameter

mattias 3 years ago
parent
commit
9387f87db9

+ 12 - 8
packages/fcl-passrc/src/pasresolver.pp

@@ -1254,8 +1254,9 @@ type
     rrfNoImplicitCallWithoutParams, // a TPrimitiveExpr is not an implicit call
     rrfNewInstance, // constructor call (without it call constructor as normal method)
     rrfFreeInstance, // destructor call (without it call destructor as normal method)
-    rrfVMT, // use VMT for call
-    rrfConstInherited // parent is const and this child is too
+    rrfVMT, // use VMT for call (e.g. calling a virtual method)
+    rrfConstInherited, // parent is const and this child is too (e.g. field of a const record argument)
+    rrfUseFields // use record fields too, flag is used by pas2js
     );
   TResolvedReferenceFlags = set of TResolvedReferenceFlag;
 
@@ -1734,7 +1735,7 @@ type
       SetReferenceFlags: boolean);
     procedure ComputeArgumentExpr(const ArgResolved: TPasResolverResult;
       Access: TArgumentAccess; Expr: TPasExpr; out ExprResolved: TPasResolverResult;
-      SetReferenceFlags: boolean);
+      SetReferenceFlags: boolean); virtual;
     procedure ComputeArrayParams(Params: TParamsExpr;
       out ResolvedEl: TPasResolverResult; Flags: TPasResolverComputeFlags;
       StartEl: TPasElement);
@@ -9311,13 +9312,15 @@ var
   ParamAccess: TResolvedRefAccess;
   i: Integer;
   ArrParams: TPasExprArray;
+  Args: TFPList;
 begin
   ArrParams:=Params.Params;
+  Args:=ProcType.Args;
   for i:=0 to length(ArrParams)-1 do
     begin
     ParamAccess:=rraRead;
-    if i<ProcType.Args.Count then
-      case TPasArgument(ProcType.Args[i]).Access of
+    if i<Args.Count then
+      case TPasArgument(Args[i]).Access of
       argVar: ParamAccess:=rraVarParam;
       argOut: ParamAccess:=rraOutParam;
       end;
@@ -13519,9 +13522,9 @@ begin
         if (LeftResolved.IdentEl is TPasType)
             or (not (rrfReadable in LeftResolved.Flags)) then
           begin
-          { $IFDEF VerbosePasResolver}
+          {$IFDEF VerbosePasResolver}
           writeln('TPasResolver.ComputeBinaryExprRes as-operator: left=',GetResolverResultDbg(LeftResolved),' right=',GetResolverResultDbg(RightResolved));
-          { $ENDIF}
+          {$ENDIF}
           RaiseIncompatibleTypeRes(20180204124711,nOperatorIsNotOverloadedAOpB,
             [OpcodeStrings[Bin.OpCode]],LeftResolved,RightResolved,Bin);
           end;
@@ -21624,9 +21627,10 @@ begin
   //    ' FindData.Found=',GetObjName(FindData.Found));
   if OnlyTypeMembers then
     begin
+    // only class vars/procs allowed
+
     //writeln('TPasResolver.CheckFoundElOnStartScope ',GetObjName(FindData.Found),' ',(FindData.Found is TPasVariable)
     //    and (vmClass in TPasVariable(FindData.Found).VarModifiers));
-    // only class vars/procs allowed
     if FindData.Found.ClassType=TPasConstructor then
       // constructor: ok
     else if IsClassMethod(FindData.Found)

+ 53 - 0
packages/fcl-passrc/src/pasuseanalyzer.pas

@@ -285,6 +285,7 @@ type
     procedure UseExportSymbol(El: TPasExportSymbol); virtual;
     procedure UseArgument(El: TPasArgument; Access: TResolvedRefAccess); virtual;
     procedure UseResultElement(El: TPasResultElement; Access: TResolvedRefAccess); virtual;
+    procedure UseRecordFields(El: TPasExpr); virtual;
     // create hints for a unit, program or library
     procedure EmitElementHints(El: TPasElement); virtual;
     procedure EmitSectionHints(Section: TPasSection); virtual;
@@ -1760,6 +1761,8 @@ begin
           end;
         end;
       end;
+    if rrfUseFields in Ref.Flags then
+      UseRecordFields(El);
 
     if Decl is TPasUnresolvedSymbolRef then
       begin
@@ -2711,6 +2714,56 @@ begin
   UpdateAccess(IsWrite, IsRead, Usage);
 end;
 
+procedure TPasAnalyzer.UseRecordFields(El: TPasExpr);
+
+  procedure UseRec(Rec: TPasRecordType); forward;
+
+  procedure UseVar(V: TPasVariable);
+  var
+    ResolvedEl: TPasResolverResult;
+  begin
+    UseElement(V,rraNone,false);
+
+    // check nested record
+    Resolver.ComputeElement(V.VarType,ResolvedEl,[],V);
+    if ResolvedEl.LoTypeEl is TPasRecordType then
+      UseRec(TPasRecordType(ResolvedEl.LoTypeEl));
+  end;
+
+  procedure UseRec(Rec: TPasRecordType);
+  var
+    Members: TFPList;
+    i: Integer;
+    Member: TPasElement;
+    C: TClass;
+    Variant: TPasVariant;
+  begin
+    Members:=Rec.Members;
+    for i:=0 to Members.Count-1 do
+      begin
+      Member:=TPasElement(Members[i]);
+      C:=Member.ClassType;
+      if C=TPasVariable then
+        UseVar(TPasVariable(Member));
+      end;
+    if Rec.VariantEl is TPasVariable then
+      UseVar(TPasVariable(Rec.VariantEl));
+    if Rec.Variants<>nil then
+      for i:=0 to Rec.Variants.Count-1 do
+        begin
+        Variant:=TPasVariant(Rec.Variants[i]);
+        UseRec(Variant.Members);
+        end;
+  end;
+
+var
+  ResolvedEl: TPasResolverResult;
+begin
+  Resolver.ComputeElement(El,ResolvedEl,[]);
+  if ResolvedEl.LoTypeEl is TPasRecordType then
+    UseRec(TPasRecordType(ResolvedEl.LoTypeEl));
+end;
+
 procedure TPasAnalyzer.EmitElementHints(El: TPasElement);
 var
   C: TClass;

+ 29 - 0
packages/pastojs/src/fppas2js.pp

@@ -1554,6 +1554,9 @@ type
     procedure FinishPropertyParamAccess(Params: TParamsExpr; Prop: TPasProperty
       ); override;
     procedure FinishExportSymbol(El: TPasExportSymbol); override;
+    procedure ComputeArgumentExpr(const ArgResolved: TPasResolverResult;
+      Access: TArgumentAccess; Expr: TPasExpr; out
+      ExprResolved: TPasResolverResult; SetReferenceFlags: boolean); override;
     procedure FindCreatorArrayOfConst(Args: TFPList; ErrorEl: TPasElement);
     function FindProc_ArrLitToArrayOfConst(ErrorEl: TPasElement): TPasFunction; virtual;
     function FindSystemExternalClassType(const aClassName, JSName: string;
@@ -4979,6 +4982,32 @@ begin
     end;
 end;
 
+procedure TPas2JSResolver.ComputeArgumentExpr(
+  const ArgResolved: TPasResolverResult; Access: TArgumentAccess;
+  Expr: TPasExpr; out ExprResolved: TPasResolverResult;
+  SetReferenceFlags: boolean);
+var
+  RightEl: TPasExpr;
+  Ref: TResolvedReference;
+begin
+  inherited ComputeArgumentExpr(ArgResolved, Access, Expr, ExprResolved,
+    SetReferenceFlags);
+  if SetReferenceFlags
+      and (Access in [argDefault, argConst])
+      and ((ArgResolved.BaseType=btUntyped)
+         or IsJSBaseType(ArgResolved,pbtJSValue,true{must have rrfReadable}))
+      and (ExprResolved.LoTypeEl is TPasRecordType) then
+    begin
+    // passing a record to an untyped or jsvalue parameter -> mark fields as "read" too
+    RightEl:=GetRightMostExpr(Expr);
+    if RightEl.CustomData is TResolvedReference then
+      begin
+      Ref:=TResolvedReference(RightEl.CustomData);
+      Include(Ref.Flags,rrfUseFields);
+      end;
+    end;
+end;
+
 procedure TPas2JSResolver.FindCreatorArrayOfConst(Args: TFPList;
   ErrorEl: TPasElement);
 var

+ 2 - 1
packages/pastojs/src/pas2jsfiler.pp

@@ -565,7 +565,8 @@ const
     'NewInst',
     'FreeInst',
     'VMT',
-    'ConstInh'
+    'ConstInh',
+    'UseFields'
     );
 
   PCUResolverWithExprScopeFlagNames: array[TPasWithExprScopeFlag] of string = (

+ 5 - 5
packages/pastojs/tests/tcconverter.pas

@@ -394,7 +394,7 @@ begin
 
   // "var $l=1, $end=100"
   VS:=TJSVariableStatement(AssertElement('For init is var '+LoopEndVar,TJSVariableStatement,ForSt.Init));
-  VDL:=TJSVariableDeclarationList(AssertElement('For init var has comma',TJSVariableDeclarationList,VS.A));
+  VDL:=TJSVariableDeclarationList(AssertElement('For init var has comma',TJSVariableDeclarationList,VS.VarDecl));
   VD:=TJSVarDeclaration(AssertElement('var '+LoopVar,TJSVarDeclaration,VDL.A));
   AssertEquals('Correct name for '+LoopVar,LoopVar,VD.Name);
   AssertLiteral('Correct start value',VD.Init,1);
@@ -455,7 +455,7 @@ begin
 
   // "var $l=100, $end=1"
   VS:=TJSVariableStatement(AssertElement('For init is var '+LoopEndVar,TJSVariableStatement,ForSt.Init));
-  VDL:=TJSVariableDeclarationList(AssertElement('For init var has comma',TJSVariableDeclarationList,VS.A));
+  VDL:=TJSVariableDeclarationList(AssertElement('For init var has comma',TJSVariableDeclarationList,VS.VarDecl));
   VD:=TJSVarDeclaration(AssertElement('var '+LoopVar,TJSVarDeclaration,VDL.A));
   AssertEquals('Correct name for '+LoopVar,LoopVar,VD.Name);
   AssertLiteral('Correct start value',VD.Init,100);
@@ -704,7 +704,7 @@ begin
   L:=AssertListStatement('On block is always a list',I.BTrue);
   writeln('TTestStatementConverter.TestTryExceptStatementOnE ',L.A.ClassName);
   VS:=TJSVariableStatement(AssertElement('First statement in list is a var statement',TJSVariableStatement,L.A));
-  V:=TJSVarDeclaration(AssertElement('var declaration e=ExceptObject',TJSVarDeclaration,VS.A));
+  V:=TJSVarDeclaration(AssertElement('var declaration e=ExceptObject',TJSVarDeclaration,VS.VarDecl));
   AssertEquals('Variable name is identifier in On A : Ex do','e',V.Name);
   Assertidentifier('Variable init is exception object',V.Init,ExceptObjName);
   // check "b = c;"
@@ -765,7 +765,7 @@ begin
   L:=AssertListStatement('On block is always a list',I.BTrue);
   writeln('TTestStatementConverter.TestTryExceptStatementOnE ',L.A.ClassName);
   VS:=TJSVariableStatement(AssertElement('First statement in list is a var statement',TJSVariableStatement,L.A));
-  V:=TJSVarDeclaration(AssertElement('var declaration e=ExceptObject',TJSVarDeclaration,VS.A));
+  V:=TJSVarDeclaration(AssertElement('var declaration e=ExceptObject',TJSVarDeclaration,VS.VarDecl));
   AssertEquals('Variable name is identifier in On A : Ex do','e',V.Name);
   Assertidentifier('Variable init is exception object',V.Init,ExceptObjName);
   R:=TJSThrowStatement(AssertElement('On block is throw statement',TJSThrowStatement,L.B));
@@ -787,7 +787,7 @@ begin
   S.Variables.Add(V);
   L:=TJSStatementList(Convert(S,TJSStatementList));
   JV:=TJSVariableStatement(AssertElement('Variable statement',TJSVariableStatement,L.A));
-  JVD:=TJSVarDeclaration(AssertElement('Variable declaration',TJSVarDeclaration,JV.A));
+  JVD:=TJSVarDeclaration(AssertElement('Variable declaration',TJSVarDeclaration,JV.VarDecl));
   AssertEquals('Correct variable name','a',JVD.Name);
 end;
 

+ 68 - 66
packages/pastojs/tests/tcmodules.pas

@@ -103,8 +103,13 @@ type
     FStreamResolver: TStreamResolver;
     FScanner: TPas2jsPasScanner;
     FSource: string;
+    procedure SetModule(const AValue: TPasModule);
   public
     destructor Destroy; override;
+    function CreateElement(AClass: TPTreeElement; const AName: String;
+      AParent: TPasElement; AVisibility: TPasMemberVisibility;
+      const ASrcPos: TPasSourcePos; TypeParams: TFPList = nil): TPasElement;
+      overload; override;
     function FindUnit(const AName, InFilename: String; NameExpr,
       InFileExpr: TPasExpr): TPasModule; override;
     procedure UsedInterfacesFinished(Section: TPasSection); override;
@@ -114,52 +119,50 @@ type
     property Scanner: TPas2jsPasScanner read FScanner write FScanner;
     property Parser: TTestPasParser read FParser write FParser;
     property Source: string read FSource write FSource;
-    property Module: TPasModule read FModule;
+    property Module: TPasModule read FModule write SetModule;
   end;
 
   { TCustomTestModule }
 
   TCustomTestModule = Class(TTestCase)
   private
-    FConverter: TPasToJSConverter;
-    FEngine: TTestEnginePasResolver;
-    FExpectedErrorClass: ExceptClass;
-    FExpectedErrorMsg: string;
-    FExpectedErrorNumber: integer;
-    FFilename: string;
-    FFileResolver: TStreamResolver;
-    FHub: TPas2JSResolverHub;
-    FJSImplementationUses: TJSArrayLiteral;
-    FJSInitBody: TJSFunctionBody;
-    FJSImplentationUses: TJSArrayLiteral;
-    FJSInterfaceUses: TJSArrayLiteral;
-    FJSModule: TJSSourceElements;
-    FJSModuleSrc: TJSSourceElements;
-    FJSSource: TStringList;
-    FModule: TPasModule;
-    FJSModuleCallArgs: TJSArguments;
-    FModules: TObjectList;// list of TTestEnginePasResolver
-    FParser: TTestPasParser;
+    FWithTypeInfo: boolean;
+    FSource: TStringList;
+    FSkipTests: boolean;
+    FScanner: TPas2jsPasScanner;
+    FResolvers: TObjectList;// list of TTestEnginePasResolver
     FPasProgram: TPasProgram;
     FPasLibrary: TPasLibrary;
-    FHintMsgs: TObjectList; // list of TTestHintMessage
-    FHintMsgsGood: TFPList; // list of TTestHintMessage marked as expected
+    FParser: TTestPasParser;
+    FModule: TPasModule;
+    FJSSource: TStringList;
     FJSRegModuleCall: TJSCallExpression;
-    FScanner: TPas2jsPasScanner;
-    FSkipTests: boolean;
-    FSource: TStringList;
+    FJSModuleSrc: TJSSourceElements;
+    FJSModuleCallArgs: TJSArguments;
+    FJSModule: TJSSourceElements;
+    FJSInterfaceUses: TJSArrayLiteral;
+    FJSInitBody: TJSFunctionBody;
+    FJSImplentationUses: TJSArrayLiteral;
+    FJSImplementationUses: TJSArrayLiteral;
+    FHub: TPas2JSResolverHub;
+    FHintMsgsGood: TFPList; // list of TTestHintMessage marked as expected
+    FHintMsgs: TObjectList; // list of TTestHintMessage
     FFirstPasStatement: TPasImplBlock;
-    FWithTypeInfo: boolean;
+    FFileResolver: TStreamResolver;
+    FFilename: string;
+    FExpectedErrorNumber: integer;
+    FExpectedErrorMsg: string;
+    FExpectedErrorClass: ExceptClass;
+    FEngine: TTestEnginePasResolver;
+    FConverter: TPasToJSConverter;
     {$IFDEF EnablePasTreeGlobalRefCount}
     FElementRefCountAtSetup: int64;
     {$ENDIF}
     procedure FreeSrcMarkers;
-    function GetModuleCount: integer;
-    function GetModules(Index: integer): TTestEnginePasResolver;
-    function GetMsgCount: integer;
-    function GetMsgs(Index: integer): TTestHintMessage;
     function GetResolverCount: integer;
     function GetResolvers(Index: integer): TTestEnginePasResolver;
+    function GetMsgCount: integer;
+    function GetMsgs(Index: integer): TTestHintMessage;
     function OnPasResolverFindUnit(const aUnitName: String): TPasModule;
     procedure OnParserLog(Sender: TObject; const Msg: String);
     procedure OnPasResolverLog(Sender: TObject; const Msg: String);
@@ -232,8 +235,6 @@ type
     function GetDefaultNamespace: string;
     property PasProgram: TPasProgram Read FPasProgram;
     property PasLibrary: TPasLibrary Read FPasLibrary;
-    property Resolvers[Index: integer]: TTestEnginePasResolver read GetResolvers;
-    property ResolverCount: integer read GetResolverCount;
     property ResolverEngine: TTestEnginePasResolver read FEngine;
     property Filename: string read FFilename;
     Property Module: TPasModule Read FModule;
@@ -259,8 +260,8 @@ type
     property FileResolver: TStreamResolver read FFileResolver;
     property Scanner: TPas2jsPasScanner read FScanner;
     property Parser: TTestPasParser read FParser;
-    property Modules[Index: integer]: TTestEnginePasResolver read GetModules;
-    property ModuleCount: integer read GetModuleCount;
+    property Resolvers[Index: integer]: TTestEnginePasResolver read GetResolvers;
+    property ResolverCount: integer read GetResolverCount;
     property MsgCount: integer read GetMsgCount;
     property Msgs[Index: integer]: TTestHintMessage read GetMsgs;
     property WithTypeInfo: boolean read FWithTypeInfo write SetWithTypeInfo;
@@ -1216,20 +1217,32 @@ end;
 
 { TTestEnginePasResolver }
 
+procedure TTestEnginePasResolver.SetModule(const AValue: TPasModule);
+begin
+  if FModule=AValue then Exit;
+  FModule:=AValue;
+end;
+
 destructor TTestEnginePasResolver.Destroy;
 begin
   FreeAndNil(FStreamResolver);
   FreeAndNil(FParser);
   FreeAndNil(FScanner);
   FreeAndNil(FStreamResolver);
-  if Module<>nil then
-    begin
-    Module.Release{$IFDEF CheckPasTreeRefCount}('CreateElement'){$ENDIF};
-    FModule:=nil;
-    end;
+  Module:=nil;
   inherited Destroy;
 end;
 
+function TTestEnginePasResolver.CreateElement(AClass: TPTreeElement;
+  const AName: String; AParent: TPasElement; AVisibility: TPasMemberVisibility;
+  const ASrcPos: TPasSourcePos; TypeParams: TFPList): TPasElement;
+begin
+  Result:=inherited CreateElement(AClass, AName, AParent, AVisibility, ASrcPos,
+    TypeParams);
+  if (FModule=nil) and AClass.InheritsFrom(TPasModule) then
+    Module:=TPasModule(Result);
+end;
+
 function TTestEnginePasResolver.FindUnit(const AName, InFilename: String;
   NameExpr, InFileExpr: TPasExpr): TPasModule;
 begin
@@ -1265,14 +1278,14 @@ begin
   LastSrcMarker:=nil;
 end;
 
-function TCustomTestModule.GetModuleCount: integer;
+function TCustomTestModule.GetResolverCount: integer;
 begin
-  Result:=FModules.Count;
+  Result:=FResolvers.Count;
 end;
 
-function TCustomTestModule.GetModules(Index: integer): TTestEnginePasResolver;
+function TCustomTestModule.GetResolvers(Index: integer): TTestEnginePasResolver;
 begin
-  Result:=TTestEnginePasResolver(FModules[Index]);
+  Result:=TTestEnginePasResolver(FResolvers[Index]);
 end;
 
 function TCustomTestModule.GetMsgCount: integer;
@@ -1285,17 +1298,6 @@ begin
   Result:=TTestHintMessage(FHintMsgs[Index]);
 end;
 
-function TCustomTestModule.GetResolverCount: integer;
-begin
-  Result:=FModules.Count;
-end;
-
-function TCustomTestModule.GetResolvers(Index: integer
-  ): TTestEnginePasResolver;
-begin
-  Result:=TTestEnginePasResolver(FModules[Index]);
-end;
-
 function TCustomTestModule.OnPasResolverFindUnit(const aUnitName: String
   ): TPasModule;
 var
@@ -1534,7 +1536,7 @@ begin
   FElementRefCountAtSetup:=TPasElement.GlobalRefCount;
   {$ENDIF}
 
-  if FModules<>nil then
+  if FResolvers<>nil then
     begin
     writeln('TCustomTestModule.SetUp FModules<>nil');
     Halt;
@@ -1546,7 +1548,7 @@ begin
   FSource:=TStringList.Create;
 
   FHub:=TPas2JSResolverHub.Create(Self);
-  FModules:=TObjectList.Create(true);
+  FResolvers:=TObjectList.Create(true);
 
   FFilename:='test1.pp';
   FFileResolver:=TStreamResolver.Create;
@@ -1628,24 +1630,24 @@ begin
   ResolverEngine.Clear;
   FreeAndNil(FSource);
   FreeAndNil(FFileResolver);
-  if FModules<>nil then
+  if FResolvers<>nil then
     begin
-    for i:=0 to FModules.Count-1 do
+    for i:=0 to FResolvers.Count-1 do
       begin
-      CurModule:=TTestEnginePasResolver(FModules[i]).Module;
+      CurModule:=TTestEnginePasResolver(FResolvers[i]).Module;
       if CurModule=nil then continue;
       //writeln('TCustomTestModule.TearDown ReleaseUsedUnits ',CurModule.Name,' ',CurModule.RefCount,' ',CurModule.RefIds.Text);
       CurModule.ReleaseUsedUnits;
       end;
     if FModule<>nil then
       FModule.ReleaseUsedUnits;
-    for i:=0 to FModules.Count-1 do
+    for i:=0 to FResolvers.Count-1 do
       begin
-      CurModule:=TTestEnginePasResolver(FModules[i]).Module;
+      CurModule:=TTestEnginePasResolver(FResolvers[i]).Module;
       if CurModule=nil then continue;
       //writeln('TCustomTestModule.TearDown UsesReleased ',CurModule.Name,' ',CurModule.RefCount,' ',CurModule.RefIds.Text);
       end;
-    FreeAndNil(FModules);
+    FreeAndNil(FResolvers);
     ReleaseAndNil(TPasElement(FModule){$IFDEF CheckPasTreeRefCount},'CreateElement'{$ENDIF});
     FEngine:=nil;
     end;
@@ -1704,7 +1706,7 @@ var
   Found: Boolean;
   Section: TPasSection;
 begin
-  // parse til exception or all modules finished
+  // parse til exception or all Resolvers finished
   while not SkipTests do
     begin
     Found:=false;
@@ -1821,7 +1823,7 @@ begin
   Result.OnFindUnit:=@OnPasResolverFindUnit;
   Result.OnLog:=@OnPasResolverLog;
   Result.Hub:=Hub;
-  FModules.Add(Result);
+  FResolvers.Add(Result);
 end;
 
 function TCustomTestModule.AddModuleWithSrc(aFilename, Src: string
@@ -3083,9 +3085,9 @@ begin
   try
     // find all markers
     Module.ForEachCall(@OnFindReference,@FoundRefs);
-    for i:=0 to ModuleCount-1 do
+    for i:=0 to ResolverCount-1 do
       begin
-      CurResolver:=Modules[i];
+      CurResolver:=Resolvers[i];
       if CurResolver.Module=Module then continue;
       //writeln('TCustomTestResolver.FindElementsAt ',CurResolver.Filename);
       CurResolver.Module.ForEachCall(@OnFindReference,@FoundRefs);

+ 36 - 0
packages/pastojs/tests/tcpas2jsanalyzer.pas

@@ -23,6 +23,7 @@ type
   protected
     procedure SetUp; override;
     procedure TearDown; override;
+    procedure ParseModule; override;
     procedure AnalyzeModule; virtual;
     procedure AnalyzeProgram; virtual;
     procedure AnalyzeUnit; virtual;
@@ -46,6 +47,7 @@ type
   TTestPas2jsAnalyzer = class(TCustomTestPas2jsAnalyzer)
   Published
     procedure TestM_ProgramLocalVar;
+    procedure TestM_PassRecordToJSValue;
   end;
 
 
@@ -67,6 +69,33 @@ begin
   AnalyzeProgram;
 end;
 
+procedure TTestPas2jsAnalyzer.TestM_PassRecordToJSValue;
+begin
+  StartProgram(false);
+  Add([
+  'type',
+  '  {#trec_used}TRec = record',
+  '    {#x_used}x: word;',
+  '  end;',
+  '  {#tbig_used}TBig = record',
+  '    {#r_used}r: TRec;',
+  '  end;',
+  '  {#tnope_used}TNope = record',
+  '    {#a_notused}a: word;',
+  '    {#b_used}b: word;',
+  '  end;',
+  'procedure DoIt(v: JSValue);',
+  'begin',
+  'end;',
+  'var big: TBig;',
+  '  n: TNope;',
+  'begin',
+  '  DoIt(big);',
+  '  DoIt(n.b);',
+  'end.']);
+  AnalyzeProgram;
+end;
+
 { TCustomTestPas2jsAnalyzer }
 
 function TCustomTestPas2jsAnalyzer.GetPAMessages(Index: integer): TPAMessage;
@@ -104,6 +133,13 @@ begin
   inherited TearDown;
 end;
 
+procedure TCustomTestPas2jsAnalyzer.ParseModule;
+begin
+  inherited ParseModule;
+  if SkipTests then exit;
+  CheckReferenceDirectives;
+end;
+
 procedure TCustomTestPas2jsAnalyzer.AnalyzeModule;
 begin
   Analyzer.AnalyzeModule(Module);