unit TCGenerics; {$mode objfpc}{$H+} interface uses Classes, SysUtils, fpcunit, testregistry, TCModules, FPPas2Js, PScanner, PasResolveEval; type { TTestGenerics } TTestGenerics = class(TCustomTestModule) Published // generic record Procedure TestGen_RecordEmpty; Procedure TestGen_Record_ClassProc; Procedure TestGen_Record_ClassVarRecord_Program; Procedure TestGen_Record_ClassVarRecord_UnitImpl; Procedure TestGen_Record_RTTI_UnitImpl; procedure TestGen_Record_Delay_UsedByImplUses; // ToDo: type alias type as parameter, TBird = type word; // generic class Procedure TestGen_ClassEmpty; Procedure TestGen_Class_EmptyMethod; Procedure TestGen_Class_TList; Procedure TestGen_Class_TCustomList; Procedure TestGen_ClassAncestor; Procedure TestGen_Class_TypeInfo; Procedure TestGen_Class_TypeOverload; Procedure TestGen_Class_ClassProperty; Procedure TestGen_Class_ClassProc; Procedure TestGen_Class_ReferGenClass_DelphiFail; Procedure TestGen_Class_ClassConstructor; Procedure TestGen_Class_TypeCastSpecializesWarn; Procedure TestGen_Class_TypeCastSpecializesJSValueNoWarn; procedure TestGen_Class_OverloadsInUnit; procedure TestGen_ClassForward_CircleRTTI; procedure TestGen_Class_Nested_RTTI; procedure TestGen_Class_ClassVarRecord_UnitImpl; procedure TestGen_Class_Field_ArrayOfSpec; // generic external class procedure TestGen_ExtClass_VarArgsOfType; procedure TestGen_ExtClass_Array; procedure TestGen_ExtClass_GenJSValueAssign; procedure TestGen_ExtClass_AliasMemberType; Procedure TestGen_ExtClass_RTTI; procedure TestGen_ExtClass_UnitImplRec; // class interfaces procedure TestGen_ClassInterface_Corba; procedure TestGen_ClassInterface_InterfacedObject; procedure TestGen_ClassInterface_COM_RTTI; procedure TestGen_ClassInterface_Helper; procedure TestGen_ClassInterface_DelayedInitSpec; // statements Procedure TestGen_InlineSpec_Constructor; Procedure TestGen_CallUnitImplProc; Procedure TestGen_IntAssignTemplVar; Procedure TestGen_TypeCastDotField; Procedure TestGen_Except; // generic helper procedure TestGen_HelperForArray; // generic functions procedure TestGenProc_Function_ObjFPC; procedure TestGenProc_Function_Delphi; procedure TestGenProc_Overload; procedure TestGenProc_Forward; procedure TestGenProc_Infer_OverloadForward; procedure TestGenProc_TypeInfo; procedure TestGenProc_Infer_Widen; procedure TestGenProc_Infer_PassAsArg; procedure TestGenProc_AnonymousProc; // generic methods procedure TestGenMethod_ImplicitSpec_ObjFPC; procedure TestGenMethod_Delphi; procedure TestGenMethod_Overload_Delphi; procedure TestGenMethod_ParamProcVar_Forward_Issue39216; // generic array procedure TestGen_Array_OtherUnit; procedure TestGen_ArrayOfUnitImplRec; procedure TestGen_Array_TypecastJSValueResultToArg; // generic procedure type procedure TestGen_ProcType_ProcLocal; procedure TestGen_ProcType_Local_RTTI_Fail; procedure TestGen_ProcType_ParamUnitImpl; // procedure TestGen_ProcType_TemplateCountOverload_ObjFPC; ObjFPC does not support that in FPC procedure TestGen_ProcType_TemplateCountOverload_Delphi; end; implementation { TTestGenerics } procedure TTestGenerics.TestGen_RecordEmpty; begin StartProgram(false); Add([ 'type', ' generic TRecA = record', ' end;', 'var a,b: specialize TRecA;', 'begin', ' if a=b then ;']); ConvertProgram; CheckSource('TestGen_RecordEmpty', LinesToStr([ // statements 'rtl.recNewT(this, "TRecA$G1", function () {', ' this.$eq = function (b) {', ' return true;', ' };', ' this.$assign = function (s) {', ' return this;', ' };', '});', 'this.a = this.TRecA$G1.$new();', 'this.b = this.TRecA$G1.$new();', '']), LinesToStr([ // $mod.$main 'if ($mod.a.$eq($mod.b)) ;' ])); end; procedure TTestGenerics.TestGen_Record_ClassProc; begin StartProgram(false); Add([ '{$modeswitch AdvancedRecords}', 'type', ' generic TPoint = record', ' class var x: T;', ' class procedure Fly; static;', ' end;', 'class procedure Tpoint.Fly;', 'begin', ' x:=x+3;', ' tpoint.x:=tpoint.x+4;', ' Fly;', ' tpoint.Fly;', 'end;', 'var p: specialize TPoint;', 'begin', ' p.x:=p.x+10;', ' p.Fly;', ' p.Fly();', '']); ConvertProgram; CheckSource('TestGen_Record_ClassProc', LinesToStr([ // statements 'rtl.recNewT(this, "TPoint$G1", function () {', ' this.x = 0;', ' this.$eq = function (b) {', ' return true;', ' };', ' this.$assign = function (s) {', ' return this;', ' };', ' this.Fly = function () {', ' $mod.TPoint$G1.x = $mod.TPoint$G1.x + 3;', ' $mod.TPoint$G1.x = $mod.TPoint$G1.x + 4;', ' $mod.TPoint$G1.Fly();', ' $mod.TPoint$G1.Fly();', ' };', '}, true);', 'this.p = this.TPoint$G1.$new();', '']), LinesToStr([ // $mod.$main '$mod.TPoint$G1.x = $mod.p.x + 10;', '$mod.TPoint$G1.Fly();', '$mod.TPoint$G1.Fly();', ''])); end; procedure TTestGenerics.TestGen_Record_ClassVarRecord_Program; begin StartProgram(false); Add([ '{$modeswitch AdvancedRecords}', 'type', ' generic TAnt = record', ' class var x: T;', ' end;', ' TBird = record', ' b: word;', ' end;', 'var f: specialize TAnt;', 'begin', ' f.x.b:=f.x.b+10;', '']); ConvertProgram; CheckSource('TestGen_Record_ClassVarRecord_Program', LinesToStr([ // statements 'rtl.recNewT(this, "TBird", function () {', ' this.b = 0;', ' this.$eq = function (b) {', ' return this.b === b.b;', ' };', ' this.$assign = function (s) {', ' this.b = s.b;', ' return this;', ' };', '});', 'rtl.recNewT(this, "TAnt$G1", function () {', ' this.x = $mod.TBird.$new();', ' this.$eq = function (b) {', ' return true;', ' };', ' this.$assign = function (s) {', ' return this;', ' };', '}, true);', 'this.f = this.TAnt$G1.$new();', '']), LinesToStr([ // $mod.$main '$mod.f.x.b = $mod.f.x.b + 10;', ''])); end; procedure TTestGenerics.TestGen_Record_ClassVarRecord_UnitImpl; begin StartProgram(true,[supTObject]); AddModuleWithIntfImplSrc('UnitA.pas', LinesToStr([ '{$modeswitch AdvancedRecords}', 'type', ' generic TAnt = record', ' class var x: T;', ' class var a: array[1..2] of T;', ' end;', '']), LinesToStr([ 'type', ' TBird = record', ' b: word;', ' end;', 'var f: specialize TAnt;', 'begin', ' f.x.b:=f.x.b+10;', ''])); Add([ 'uses UnitA;', 'begin', 'end.']); ConvertProgram; CheckUnit('UnitA.pas', LinesToStr([ // statements 'rtl.module("UnitA", ["system"], function () {', ' var $mod = this;', ' var $impl = $mod.$impl;', ' rtl.recNewT(this, "TAnt$G1", function () {', ' this.$initSpec = function () {', ' this.x = $impl.TBird.$new();', ' this.a = rtl.arraySetLength(null, $impl.TBird, 2);', ' };', ' this.a$a$clone = function (a) {', ' var b = [];', ' b.length = 2;', ' for (var c = 0; c < 2; c++) b[c] = $impl.TBird.$clone(a[c]);', ' return b;', ' };', ' this.$eq = function (b) {', ' return true;', ' };', ' this.$assign = function (s) {', ' return this;', ' };', ' }, true);', ' $mod.$implcode = function () {', ' rtl.recNewT($impl, "TBird", function () {', ' this.b = 0;', ' this.$eq = function (b) {', ' return this.b === b.b;', ' };', ' this.$assign = function (s) {', ' this.b = s.b;', ' return this;', ' };', ' });', ' $impl.f = $mod.TAnt$G1.$new();', ' };', ' $mod.$init = function () {', ' $impl.f.x.b = $impl.f.x.b + 10;', ' };', '}, []);'])); CheckSource('TestGen_Record_ClassVarRecord_UnitImpl', LinesToStr([ // statements '$mod.$implcode = function () {', ' pas.UnitA.TAnt$G1.$initSpec();', '};', '']), LinesToStr([ // $mod.$main ''])); end; procedure TTestGenerics.TestGen_Record_RTTI_UnitImpl; begin WithTypeInfo:=true; StartUnit(true); Add([ 'interface', '{$modeswitch AdvancedRecords}', 'type', ' generic TAnt = record', ' class var x: T;', //' class var a,b: array of T;', ' end;', 'implementation', 'type', ' TBird = record', ' b: word;', ' end;', 'var f: specialize TAnt;', ' p: pointer;', 'begin', ' p:=typeinfo(f);', '']); ConvertUnit; CheckSource('TestGen_Record_RTTI_UnitImpl', LinesToStr([ // statements 'var $impl = $mod.$impl;', 'rtl.recNewT(this, "TAnt$G1", function () {', ' var $r = $mod.$rtti.$Record("TAnt", {}, this);', ' this.$initSpec = function () {', ' this.x = $impl.TBird.$new();', ' $r.addField("x", $mod.$rtti["TBird"]);', ' };', ' this.$eq = function (b) {', ' return true;', ' };', ' this.$assign = function (s) {', ' return this;', ' };', '}, true);', '']), LinesToStr([ // $mod.$init '$impl.p = $mod.$rtti["TAnt"];', '']), LinesToStr([ // statements 'rtl.recNewT($impl, "TBird", function () {', ' this.b = 0;', ' this.$eq = function (b) {', ' return this.b === b.b;', ' };', ' this.$assign = function (s) {', ' this.b = s.b;', ' return this;', ' };', ' var $r = $mod.$rtti.$Record("TBird", {}, this);', ' $r.addField("b", rtl.word);', '});', '$impl.f = $mod.TAnt$G1.$new();', '$impl.p = null;', ''])); end; procedure TTestGenerics.TestGen_Record_Delay_UsedByImplUses; begin WithTypeInfo:=true; StartProgram(true,[supTObject]); AddModuleWithIntfImplSrc('UnitA.pas', LinesToStr([ '{$modeswitch AdvancedRecords}', 'type', ' generic TBird = record', ' class var a: T;', ' end;', '']), LinesToStr([ ''])); AddModuleWithIntfImplSrc('UnitB.pas', LinesToStr([ 'procedure Fly;', '']), LinesToStr([ 'uses UnitA;', 'type', ' TFox = record', ' B: word;', ' end;', 'procedure Fly;', 'var Bird: specialize TBird;', 'begin', ' if typeinfo(Bird)<>nil then ;', ' Bird.a:=Bird.a;', 'end;', ''])); Add([ 'uses UnitB;', 'begin', ' Fly;']); ConvertProgram; CheckSource('TestGen_Record_Delay_UsedByImplUses', LinesToStr([ // statements '$mod.$implcode = function () {', ' pas.UnitA.TBird$G1.$initSpec();', '};', '']), LinesToStr([ // $mod.$main 'pas.UnitB.Fly();' ])); end; procedure TTestGenerics.TestGen_ClassEmpty; begin StartProgram(false); Add([ 'type', ' TObject = class end;', ' generic TBird = class', ' end;', 'var a,b: specialize TBird;', 'begin', ' if a=b then ;']); ConvertProgram; CheckSource('TestGen_ClassEmpty', LinesToStr([ // statements 'rtl.createClass(this, "TObject", null, function () {', ' this.$init = function () {', ' };', ' this.$final = function () {', ' };', '});', 'rtl.createClass(this, "TBird$G1", this.TObject, function () {', '}, "TBird");', 'this.a = null;', 'this.b = null;', '']), LinesToStr([ // $mod.$main 'if ($mod.a === $mod.b) ;' ])); end; procedure TTestGenerics.TestGen_Class_EmptyMethod; begin StartProgram(false); Add([ 'type', ' TObject = class end;', ' generic TBird = class', ' function Fly(w: T): T;', ' end;', 'function TBird.Fly(w: T): T;', 'begin', 'end;', 'var a: specialize TBird;', 'begin', ' if a.Fly(3)=4 then ;']); ConvertProgram; CheckSource('TestGen_Class_EmptyMethod', LinesToStr([ // statements 'rtl.createClass(this, "TObject", null, function () {', ' this.$init = function () {', ' };', ' this.$final = function () {', ' };', '});', 'rtl.createClass(this, "TBird$G1", this.TObject, function () {', ' this.Fly = function (w) {', ' var Result = 0;', ' return Result;', ' };', '}, "TBird");', 'this.a = null;', '']), LinesToStr([ // $mod.$main ' if ($mod.a.Fly(3) === 4) ;' ])); end; procedure TTestGenerics.TestGen_Class_TList; begin StartProgram(false); Add([ '{$mode objfpc}', 'type', ' TObject = class end;', ' generic TList = class', ' strict private', ' FItems: array of T;', ' function GetItems(Index: longint): T;', ' procedure SetItems(Index: longint; Value: T);', ' public', ' procedure Alter(w: T);', ' property Items[Index: longint]: T read GetItems write SetItems; default;', ' end;', ' TWordList = specialize TList;', 'function TList.GetItems(Index: longint): T;', 'begin', ' Result:=FItems[Index];', 'end;', 'procedure TList.SetItems(Index: longint; Value: T);', 'begin', ' FItems[Index]:=Value;', 'end;', 'procedure TList.Alter(w: T);', 'begin', ' SetLength(FItems,length(FItems)+1);', ' Insert(w,FItems,2);', ' Delete(FItems,2,3);', 'end;', 'var l: TWordList;', ' w: word;', 'begin', ' l[1]:=w;', ' w:=l[2];', '']); ConvertProgram; CheckSource('TestGen_Class_TList', LinesToStr([ // statements 'rtl.createClass(this, "TObject", null, function () {', ' this.$init = function () {', ' };', ' this.$final = function () {', ' };', '});', 'rtl.createClass(this, "TList$G1", this.TObject, function () {', ' this.$init = function () {', ' $mod.TObject.$init.call(this);', ' this.FItems = [];', ' };', ' this.$final = function () {', ' this.FItems = undefined;', ' $mod.TObject.$final.call(this);', ' };', ' this.GetItems = function (Index) {', ' var Result = 0;', ' Result = this.FItems[Index];', ' return Result;', ' };', ' this.SetItems = function (Index, Value) {', ' this.FItems[Index] = Value;', ' };', ' this.Alter = function (w) {', ' this.FItems = rtl.arraySetLength(this.FItems, 0, rtl.length(this.FItems) + 1);', ' this.FItems = rtl.arrayInsert(w, this.FItems, 2);', ' this.FItems.splice(2, 3);', ' };', '}, "TList");', 'this.l = null;', 'this.w = 0;', '']), LinesToStr([ // $mod.$main '$mod.l.SetItems(1, $mod.w);', '$mod.w = $mod.l.GetItems(2);', ''])); end; procedure TTestGenerics.TestGen_Class_TCustomList; begin StartProgram(false); Add([ '{$mode delphi}', 'type', ' TObject = class end;', ' TCustomList = class', ' public', ' function PrepareAddingItem: word; virtual;', ' end;', ' TList = class(TCustomList)', ' public', ' function Add: word;', ' end;', ' TWordList = TList;', 'function TCustomList.PrepareAddingItem: word;', 'begin', 'end;', 'function TList.Add: word;', 'begin', ' Result:=PrepareAddingItem;', ' Result:=Self.PrepareAddingItem;', ' with Self do Result:=PrepareAddingItem;', 'end;', 'var l: TWordList;', 'begin', '']); ConvertProgram; CheckSource('TestGen_Class_TCustomList', LinesToStr([ // statements 'rtl.createClass(this, "TObject", null, function () {', ' this.$init = function () {', ' };', ' this.$final = function () {', ' };', '});', 'rtl.createClass(this, "TCustomList$G2", this.TObject, function () {', ' this.PrepareAddingItem = function () {', ' var Result = 0;', ' return Result;', ' };', '}, "TCustomList");', 'rtl.createClass(this, "TList$G1", this.TCustomList$G2, function () {', ' this.Add = function () {', ' var Result = 0;', ' Result = this.PrepareAddingItem();', ' Result = this.PrepareAddingItem();', ' Result = this.PrepareAddingItem();', ' return Result;', ' };', '}, "TList");', 'this.l = null;', '']), LinesToStr([ // $mod.$main ''])); end; procedure TTestGenerics.TestGen_ClassAncestor; begin StartProgram(false); Add([ 'type', ' TObject = class end;', ' generic TBird = class', ' end;', ' generic TEagle = class(specialize TBird)', ' end;', 'var a: specialize TEagle;', 'begin', '']); ConvertProgram; CheckSource('TestGen_ClassAncestor', LinesToStr([ // statements 'rtl.createClass(this, "TObject", null, function () {', ' this.$init = function () {', ' };', ' this.$final = function () {', ' };', '});', 'rtl.createClass(this, "TBird$G2", this.TObject, function () {', '}, "TBird");', 'rtl.createClass(this, "TEagle$G1", this.TBird$G2, function () {', '}, "TEagle");', 'this.a = null;', '']), LinesToStr([ // $mod.$main ''])); end; procedure TTestGenerics.TestGen_Class_TypeInfo; begin WithTypeInfo:=true; StartProgram(false); Add([ 'type', ' TObject = class end;', ' generic TBird = class', ' published', ' m: T;', ' end;', ' TEagle = specialize TBird;', 'var', ' b: specialize TBird;', ' p: pointer;', 'begin', ' p:=typeinfo(TEagle);', ' p:=typeinfo(b);', '']); ConvertProgram; CheckSource('TestGen_Class_TypeInfo', LinesToStr([ // statements '$mod.$rtti.$Class("TBird");', 'rtl.createClass(this, "TObject", null, function () {', ' this.$init = function () {', ' };', ' this.$final = function () {', ' };', '});', 'rtl.createClass(this, "TBird$G1", this.TObject, function () {', ' this.$init = function () {', ' $mod.TObject.$init.call(this);', ' this.m = 0;', ' };', ' var $r = this.$rtti;', ' $r.addField("m", rtl.word, 4);', '}, "TBird");', 'this.b = null;', 'this.p = null;', '']), LinesToStr([ // $mod.$main '$mod.p = $mod.$rtti["TBird"];', '$mod.p = $mod.b.$rtti;', ''])); end; procedure TTestGenerics.TestGen_Class_TypeOverload; begin StartProgram(false); Add([ '{$mode delphi}', 'type', ' TObject = class end;', ' TBird = word;', ' TBird = class', ' m: T;', ' end;', ' TEagle = TBird;', 'var', ' b: TBird;', ' e: TEagle;', 'begin', '']); ConvertProgram; CheckSource('TestGen_Class_TypeOverload', LinesToStr([ // statements 'rtl.createClass(this, "TObject", null, function () {', ' this.$init = function () {', ' };', ' this.$final = function () {', ' };', '});', 'rtl.createClass(this, "TBird$G1", this.TObject, function () {', ' this.$init = function () {', ' $mod.TObject.$init.call(this);', ' this.m = 0;', ' };', '}, "TBird");', 'this.b = null;', 'this.e = null;', '']), LinesToStr([ // $mod.$main ''])); end; procedure TTestGenerics.TestGen_Class_ClassProperty; begin StartProgram(false); Add([ '{$mode delphi}', 'type', ' TObject = class end;', ' TBird = class', ' private', ' class var fSize: T;', ' public', ' class property Size: T read fSize write fSize;', ' end;', ' TEagle = TBird;', 'begin', ' TBird.Size:=3+TBird.Size;', '']); ConvertProgram; CheckSource('TestGen_Class_ClassProperty', LinesToStr([ // statements 'rtl.createClass(this, "TObject", null, function () {', ' this.$init = function () {', ' };', ' this.$final = function () {', ' };', '});', 'rtl.createClass(this, "TBird$G1", this.TObject, function () {', ' this.fSize = 0;', '}, "TBird");', '']), LinesToStr([ // $mod.$main '$mod.TBird$G1.fSize = 3 + $mod.TBird$G1.fSize;', ''])); end; procedure TTestGenerics.TestGen_Class_ClassProc; begin StartProgram(false); Add([ 'type', ' TObject = class end;', ' generic TPoint = class', ' class var x: T;', ' class procedure Fly; static;', ' class procedure Run;', ' end;', 'class procedure Tpoint.Fly;', 'begin', ' x:=x+3;', ' tpoint.x:=tpoint.x+4;', ' Fly;', ' tpoint.Fly;', ' Run;', ' tpoint.Run;', 'end;', 'class procedure TPoint.Run;', 'begin', ' x:=x+5;', ' tpoint.x:=tpoint.x+6;', ' Fly;', ' tpoint.Fly;', ' Run;', ' tpoint.Run;', 'end;', 'var p: specialize TPoint;', 'begin', '']); ConvertProgram; CheckSource('TestGen_Class_ClassProc', LinesToStr([ // statements 'rtl.createClass(this, "TObject", null, function () {', ' this.$init = function () {', ' };', ' this.$final = function () {', ' };', '});', 'rtl.createClass(this, "TPoint$G1", this.TObject, function () {', ' this.x = 0;', ' this.Fly = function () {', ' $mod.TPoint$G1.x = $mod.TPoint$G1.x + 3;', ' $mod.TPoint$G1.x = $mod.TPoint$G1.x + 4;', ' $mod.TPoint$G1.Fly();', ' $mod.TPoint$G1.Fly();', ' $mod.TPoint$G1.Run();', ' $mod.TPoint$G1.Run();', ' };', ' this.Run = function () {', ' $mod.TPoint$G1.x = this.x + 5;', ' $mod.TPoint$G1.x = $mod.TPoint$G1.x + 6;', ' this.Fly();', ' this.Fly();', ' this.Run();', ' $mod.TPoint$G1.Run();', ' };', '}, "TPoint");', 'this.p = null;', '']), LinesToStr([ // $mod.$main ''])); end; procedure TTestGenerics.TestGen_Class_ReferGenClass_DelphiFail; begin StartProgram(false); Add([ '{$mode delphi}', 'type', ' TObject = class end;', ' TPoint = class', ' var x: TPoint;', // alowed in objfpc, forbidden in delphi ' end;', 'var p: specialize TPoint;', 'begin', '']); SetExpectedPasResolverError('Generics without specialization cannot be used as a type for a variable', nGenericsWithoutSpecializationAsType); ConvertProgram; end; procedure TTestGenerics.TestGen_Class_ClassConstructor; begin StartProgram(false); Add([ 'type', ' TObject = class end;', ' generic TPoint = class', ' class var x: T;', ' class procedure Fly; static;', ' class constructor Init;', ' end;', 'var count: word;', 'class procedure Tpoint.Fly;', 'begin', 'end;', 'class constructor tpoint.init;', 'begin', ' count:=count+1;', ' x:=3;', ' tpoint.x:=4;', ' fly;', ' tpoint.fly;', 'end;', 'var', ' r: specialize TPoint;', ' s: specialize TPoint;', 'begin', ' r.x:=10;', '']); ConvertProgram; CheckSource('TestGen_Class_ClassConstructor', LinesToStr([ // statements 'rtl.createClass(this, "TObject", null, function () {', ' this.$init = function () {', ' };', ' this.$final = function () {', ' };', '});', 'this.count = 0;', 'rtl.createClass(this, "TPoint$G1", this.TObject, function () {', ' this.x = 0;', ' this.Fly = function () {', ' };', '}, "TPoint");', 'this.r = null;', 'rtl.createClass(this, "TPoint$G2", this.TObject, function () {', ' this.x = 0;', ' this.Fly = function () {', ' };', '}, "TPoint");', 'this.s = null;', '']), LinesToStr([ // $mod.$main '(function () {', ' $mod.count = $mod.count + 1;', ' $mod.TPoint$G1.x = 3;', ' $mod.TPoint$G1.x = 4;', ' $mod.TPoint$G1.Fly();', ' $mod.TPoint$G1.Fly();', '})();', '(function () {', ' $mod.count = $mod.count + 1;', ' $mod.TPoint$G2.x = 3;', ' $mod.TPoint$G2.x = 4;', ' $mod.TPoint$G2.Fly();', ' $mod.TPoint$G2.Fly();', '})();', '$mod.TPoint$G1.x = 10;', ''])); end; procedure TTestGenerics.TestGen_Class_TypeCastSpecializesWarn; begin StartProgram(false); Add([ '{$mode delphi}', 'type', ' TObject = class end;', ' TBird = class F: T; end;', ' TBirdWord = TBird;', ' TBirdChar = TBird;', 'var', ' w: TBirdWord;', ' c: TBirdChar;', 'begin', ' w:=TBirdWord(c);', '']); ConvertProgram; CheckSource('TestGen_Class_TypeCastSpecializesWarn', LinesToStr([ // statements 'rtl.createClass(this, "TObject", null, function () {', ' this.$init = function () {', ' };', ' this.$final = function () {', ' };', '});', 'rtl.createClass(this, "TBird$G1", this.TObject, function () {', ' this.$init = function () {', ' $mod.TObject.$init.call(this);', ' this.F = 0;', ' };', '}, "TBird");', 'rtl.createClass(this, "TBird$G2", this.TObject, function () {', ' this.$init = function () {', ' $mod.TObject.$init.call(this);', ' this.F = "\x00";', ' };', '}, "TBird");', 'this.w = null;', 'this.c = null;', '']), LinesToStr([ // $mod.$main '$mod.w = $mod.c;', ''])); CheckHint(mtWarning,nClassTypesAreNotRelatedXY,'Class types "TBird" and "TBird" are not related'); CheckResolverUnexpectedHints(); end; procedure TTestGenerics.TestGen_Class_TypeCastSpecializesJSValueNoWarn; begin StartProgram(false); Add([ '{$mode delphi}', 'type', ' TObject = class end;', ' TBird = class F: T; end;', ' TBirdWord = TBird;', ' TBirdAny = TBird;', 'var', ' w: TBirdWord;', ' a: TBirdAny;', 'begin', ' w:=TBirdWord(a);', ' a:=TBirdAny(w);', '']); ConvertProgram; CheckSource('TestGen_Class_TypeCastSpecializesJSValueNoWarn', LinesToStr([ // statements 'rtl.createClass(this, "TObject", null, function () {', ' this.$init = function () {', ' };', ' this.$final = function () {', ' };', '});', 'rtl.createClass(this, "TBird$G1", this.TObject, function () {', ' this.$init = function () {', ' $mod.TObject.$init.call(this);', ' this.F = 0;', ' };', '}, "TBird");', 'rtl.createClass(this, "TBird$G2", this.TObject, function () {', ' this.$init = function () {', ' $mod.TObject.$init.call(this);', ' this.F = undefined;', ' };', '}, "TBird");', 'this.w = null;', 'this.a = null;', '']), LinesToStr([ // $mod.$main '$mod.w = $mod.a;', '$mod.a = $mod.w;', ''])); CheckResolverUnexpectedHints(); end; procedure TTestGenerics.TestGen_Class_OverloadsInUnit; begin StartProgram(true,[supTObject]); AddModuleWithIntfImplSrc('UnitA.pas', LinesToStr([ 'type', ' generic TBird = class', ' const c = 13;', ' constructor Create(w: T);', ' constructor Create(b: boolean);', ' end;', '']), LinesToStr([ 'constructor TBird.Create(w: T);', 'const c = 14;', 'begin', 'end;', 'constructor TBird.Create(b: boolean);', 'const c = 15;', 'begin', 'end;', ''])); Add([ 'uses UnitA;', 'type', ' TWordBird = specialize TBird;', ' TDoubleBird = specialize TBird;', 'var', ' wb: TWordBird;', ' db: TDoubleBird;', 'begin', ' wb:=TWordBird.Create(3);', ' wb:=TWordBird.Create(true);', ' db:=TDoubleBird.Create(1.3);', ' db:=TDoubleBird.Create(true);', '']); ConvertProgram; CheckUnit('UnitA.pas', LinesToStr([ // statements 'rtl.module("UnitA", ["system"], function () {', ' var $mod = this;', ' rtl.createClass(this, "TBird$G1", pas.system.TObject, function () {', ' this.c = 13;', ' var c$1 = 14;', ' this.Create$1 = function (w) {', ' return this;', ' };', ' var c$2 = 15;', ' this.Create$2 = function (b) {', ' return this;', ' };', ' }, "TBird");', ' rtl.createClass(this, "TBird$G2", pas.system.TObject, function () {', ' this.c = 13;', ' var c$1 = 14;', ' this.Create$1 = function (w) {', ' return this;', ' };', ' var c$2 = 15;', ' this.Create$2 = function (b) {', ' return this;', ' };', ' }, "TBird");', '});', ''])); CheckSource('TestGen_Class_OverloadsInUnit', LinesToStr([ // statements 'this.wb = null;', 'this.db = null;', '']), LinesToStr([ // $mod.$main '$mod.wb = pas.UnitA.TBird$G1.$create("Create$1", [3]);', '$mod.wb = pas.UnitA.TBird$G1.$create("Create$2", [true]);', '$mod.db = pas.UnitA.TBird$G2.$create("Create$1", [1.3]);', '$mod.db = pas.UnitA.TBird$G2.$create("Create$2", [true]);', ''])); end; procedure TTestGenerics.TestGen_ClassForward_CircleRTTI; begin WithTypeInfo:=true; StartProgram(false); Add([ '{$mode objfpc}', 'type', ' TObject = class end;', ' {$M+}', ' TPersistent = class end;', ' {$M-}', ' generic TAnt = class;', ' generic TFish = class(TPersistent)', ' private type AliasU = U;', ' published', ' a: specialize TAnt;', ' end;', ' generic TAnt = class(TPersistent)', ' private type AliasT = T;', ' published', ' f: specialize TFish;', ' end;', 'var', ' WordFish: specialize TFish;', ' p: pointer;', 'begin', ' p:=typeinfo(specialize TAnt);', ' p:=typeinfo(specialize TFish);', '']); ConvertProgram; CheckSource('TestGen_ClassForward_CircleRTTI', LinesToStr([ // statements '$mod.$rtti.$Class("TAnt");', '$mod.$rtti.$Class("TFish");', 'rtl.createClass(this, "TObject", null, function () {', ' this.$init = function () {', ' };', ' this.$final = function () {', ' };', '});', 'rtl.createClass(this, "TPersistent", this.TObject, function () {', '});', 'rtl.createClass(this, "TAnt$G2", this.TPersistent, function () {', ' this.$init = function () {', ' $mod.TPersistent.$init.call(this);', ' this.f = null;', ' };', ' this.$final = function () {', ' this.f = undefined;', ' $mod.TPersistent.$final.call(this);', ' };', ' var $r = this.$rtti;', ' $r.addField("f", $mod.$rtti["TFish"], 4);', '}, "TAnt");', 'rtl.createClass(this, "TFish$G2", this.TPersistent, function () {', ' this.$init = function () {', ' $mod.TPersistent.$init.call(this);', ' this.a = null;', ' };', ' this.$final = function () {', ' this.a = undefined;', ' $mod.TPersistent.$final.call(this);', ' };', ' var $r = this.$rtti;', ' $r.addField("a", $mod.$rtti["TAnt"], 4);', '}, "TFish");', 'this.WordFish = null;', 'this.p = null;', '']), LinesToStr([ // $mod.$main '$mod.p = $mod.$rtti["TAnt"];', '$mod.p = $mod.$rtti["TFish"];', ''])); end; procedure TTestGenerics.TestGen_Class_Nested_RTTI; begin WithTypeInfo:=true; StartProgram(true,[supTObject]); AddModuleWithIntfImplSrc('UnitA.pas', LinesToStr([ 'type', ' generic TAnt = class', ' type', ' TLeg = class', ' published', ' Size: T;', ' end;', ' end;', ' TBoolAnt = specialize TAnt;', '']), LinesToStr([ ''])); Add([ 'uses UnitA;', 'var', ' BoolLeg: TBoolAnt.TLeg;', 'begin', ' if typeinfo(TBoolAnt.TLeg)=nil then ;', '']); ConvertProgram; CheckUnit('UnitA.pas', LinesToStr([ // statements 'rtl.module("UnitA", ["system"], function () {', ' var $mod = this;', ' $mod.$rtti.$Class("TAnt");', ' rtl.createClass(this, "TAnt$G1", pas.system.TObject, function () {', ' rtl.createClass(this, "TLeg", pas.system.TObject, function () {', ' this.$init = function () {', ' pas.system.TObject.$init.call(this);', ' this.Size = false;', ' };', ' var $r = this.$rtti;', ' $r.addField("Size", rtl.boolean, 4);', ' }, "TAnt.TLeg");', ' }, "TAnt");', '});'])); CheckSource('TestGen_Class_Nested_RTTI', LinesToStr([ // statements 'this.BoolLeg = null;', '']), LinesToStr([ // $mod.$main 'if (pas.UnitA.$rtti["TAnt.TLeg"] === null) ;', ''])); end; procedure TTestGenerics.TestGen_Class_ClassVarRecord_UnitImpl; begin StartProgram(true,[supTObject]); AddModuleWithIntfImplSrc('UnitA.pas', LinesToStr([ 'type', ' generic TAnt = class', ' public', ' class var x: T;', ' class var a: array[1..2] of T;', ' end;', '']), LinesToStr([ 'type', ' TBird = record', ' b: word;', ' end;', 'var f: specialize TAnt;', 'begin', ' f.x.b:=f.x.b+10;', ''])); Add([ 'uses UnitA;', 'begin', 'end.']); ConvertProgram; CheckUnit('UnitA.pas', LinesToStr([ // statements 'rtl.module("UnitA", ["system"], function () {', ' var $mod = this;', ' var $impl = $mod.$impl;', ' rtl.createClass(this, "TAnt$G1", pas.system.TObject, function () {', ' this.$initSpec = function () {', ' this.x = $impl.TBird.$new();', ' this.a = rtl.arraySetLength(null, $impl.TBird, 2);', ' };', ' this.a$a$clone = function (a) {', ' var b = [];', ' b.length = 2;', ' for (var c = 0; c < 2; c++) b[c] = $impl.TBird.$clone(a[c]);', ' return b;', ' };', ' }, "TAnt");', ' $mod.$implcode = function () {', ' rtl.recNewT($impl, "TBird", function () {', ' this.b = 0;', ' this.$eq = function (b) {', ' return this.b === b.b;', ' };', ' this.$assign = function (s) {', ' this.b = s.b;', ' return this;', ' };', ' });', ' $impl.f = null;', ' };', ' $mod.$init = function () {', ' $impl.f.x.b = $impl.f.x.b + 10;', ' };', '}, []);', ''])); CheckSource('TestGen_Class_ClassVarRecord_UnitImpl', LinesToStr([ // statements '$mod.$implcode = function () {', ' pas.UnitA.TAnt$G1.$initSpec();', '};', '']), LinesToStr([ // $mod.$main ''])); end; procedure TTestGenerics.TestGen_Class_Field_ArrayOfSpec; begin StartProgram(false); Add([ '{$mode delphi}', 'type', ' TObject = class', ' end;', ' TWing = class', ' end;', ' TBird = class', ' Wings: array of TWing;', ' end;', 'begin', 'end.']); ConvertProgram; CheckSource('TestGen_Class_Field_ArrayOfSpec', LinesToStr([ // statements 'rtl.createClass(this, "TObject", null, function () {', ' this.$init = function () {', ' };', ' this.$final = function () {', ' };', '});', 'rtl.createClass(this, "TWing$G1", this.TObject, function () {', '}, "TWing");', 'rtl.createClass(this, "TBird", this.TObject, function () {', ' this.$init = function () {', ' $mod.TObject.$init.call(this);', ' this.Wings = [];', ' };', ' this.$final = function () {', ' this.Wings = undefined;', ' $mod.TObject.$final.call(this);', ' };', '});', '']), LinesToStr([ // $mod.$main ''])); end; procedure TTestGenerics.TestGen_ExtClass_VarArgsOfType; begin StartProgram(false); Add([ '{$mode objfpc}', '{$modeswitch externalclass}', 'type', ' TJSObject = class external name ''Object''', ' end;', ' generic TGJSSet = class external name ''Set''', ' constructor new(aElement1: T); varargs of T; overload;', ' function bind(thisArg: TJSObject): T; varargs of T;', ' end;', ' TJSWordSet = specialize TGJSSet;', 'var', ' s: TJSWordSet;', ' w: word;', 'begin', ' s:=TJSWordSet.new(3);', ' s:=TJSWordSet.new(3,5);', ' w:=s.bind(nil);', ' w:=s.bind(nil,6);', ' w:=s.bind(nil,7,8);', '']); ConvertProgram; CheckSource('TestGen_ExtClass_VarArgsOfType', LinesToStr([ // statements 'this.s = null;', 'this.w = 0;', '']), LinesToStr([ // $mod.$main '$mod.s = new Set(3);', '$mod.s = new Set(3, 5);', '$mod.w = $mod.s.bind(null);', '$mod.w = $mod.s.bind(null, 6);', '$mod.w = $mod.s.bind(null, 7, 8);', ''])); end; procedure TTestGenerics.TestGen_ExtClass_Array; begin StartProgram(false); Add([ '{$mode delphi}', '{$ModeSwitch externalclass}', 'type', ' NativeInt = longint;', ' TJSGenArray = Class external name ''Array''', ' private', ' function GetElements(Index: NativeInt): T; external name ''[]'';', ' procedure SetElements(Index: NativeInt; const AValue: T); external name ''[]'';', ' public', ' type TSelfType = TJSGenArray;', ' public', ' FLength : NativeInt; external name ''length'';', ' constructor new; overload;', ' constructor new(aLength : NativeInt); overload;', ' class function _of() : TSelfType; varargs; external name ''of'';', ' function fill(aValue : T) : TSelfType; overload;', ' function fill(aValue : T; aStartIndex : NativeInt) : TSelfType; overload;', ' function fill(aValue : T; aStartIndex,aEndIndex : NativeInt) : TSelfType; overload;', ' property Length : NativeInt Read FLength Write FLength;', ' property Elements[Index: NativeInt]: T read GetElements write SetElements; default;', ' end;', ' TJSWordArray = TJSGenArray;', 'var', ' wa: TJSWordArray;', ' w: word;', 'begin', ' wa:=TJSWordArray.new;', ' wa:=TJSWordArray.new(3);', ' wa:=TJSWordArray._of(4,5);', ' wa:=wa.fill(7);', ' wa:=wa.fill(7,8,9);', ' w:=wa.length;', ' wa.length:=10;', ' wa[11]:=w;', ' w:=wa[12];', '']); ConvertProgram; CheckSource('TestGen_ExtClass_Array', LinesToStr([ // statements 'this.wa = null;', 'this.w = 0;', '']), LinesToStr([ // $mod.$main '$mod.wa = new Array();', '$mod.wa = new Array(3);', '$mod.wa = Array.of(4, 5);', '$mod.wa = $mod.wa.fill(7);', '$mod.wa = $mod.wa.fill(7, 8, 9);', '$mod.w = $mod.wa.length;', '$mod.wa.length = 10;', '$mod.wa[11] = $mod.w;', '$mod.w = $mod.wa[12];', ''])); end; procedure TTestGenerics.TestGen_ExtClass_GenJSValueAssign; begin StartProgram(false); Add([ '{$mode delphi}', '{$modeswitch externalclass}', 'type', ' TExt = class external name ''Ext''', ' F: T;', ' end;', ' TExtWord = TExt;', ' TExtAny = TExt;', 'procedure Run(e: TExtAny);', 'begin end;', 'var', ' w: TExtWord;', ' a: TExtAny;', 'begin', ' a:=w;', ' Run(w);', '']); ConvertProgram; CheckSource('TestGen_ExtClass_GenJSValueAssign', LinesToStr([ // statements 'this.Run = function (e) {', '};', 'this.w = null;', 'this.a = null;', '']), LinesToStr([ // $mod.$main '$mod.a = $mod.w;', '$mod.Run($mod.w);', ''])); CheckResolverUnexpectedHints(); end; procedure TTestGenerics.TestGen_ExtClass_AliasMemberType; begin StartProgram(false); Add([ '{$mode objfpc}', '{$modeswitch externalclass}', 'type', ' generic TExt = class external name ''Ext''', ' public type TRun = reference to function(a: T): T;', ' end;', ' TExtWord = specialize TExt;', ' TExtWordRun = TExtWord.TRun;', 'begin', '']); ConvertProgram; CheckSource('TestGen_ExtClass_AliasMemberType', LinesToStr([ // statements '']), LinesToStr([ // $mod.$main ''])); end; procedure TTestGenerics.TestGen_ExtClass_RTTI; begin WithTypeInfo:=true; StartProgram(false); Add([ '{$mode objfpc}', '{$modeswitch externalclass}', 'type', ' generic TGJSSET = class external name ''SET''', ' A: T;', ' end;', ' TJSSet = specialize TGJSSET;', ' TJSSetEventProc = reference to procedure(value : JSValue; key: NativeInt; set_: TJSSet);', 'var p: Pointer;', 'begin', ' p:=typeinfo(TJSSetEventProc);', '']); ConvertProgram; CheckSource('TestGen_ExtClass_RTTI', LinesToStr([ // statements 'this.$rtti.$ExtClass("TGJSSET", {', ' jsclass: "SET"', '});', 'this.$rtti.$RefToProcVar("TJSSetEventProc", {', ' procsig: rtl.newTIProcSig([["value", rtl.jsvalue], ["key", rtl.nativeint], ["set_", this.$rtti["TGJSSET"]]])', '});', 'this.p = null;', '']), LinesToStr([ // $mod.$main '$mod.p = $mod.$rtti["TJSSetEventProc"];', ''])); end; procedure TTestGenerics.TestGen_ExtClass_UnitImplRec; begin WithTypeInfo:=true; StartProgram(true,[supTObject]); AddModuleWithIntfImplSrc('UnitA.pas', LinesToStr([ '{$mode objfpc}', '{$modeswitch externalclass}', 'type', ' generic TAnt = class external name ''SET''', ' x: T;', ' end;', '']), LinesToStr([ 'type', ' TBird = record', ' b: word;', ' end;', 'var', ' f: specialize TAnt;', 'begin', ' f.x.b:=f.x.b+10;', ''])); Add([ 'uses UnitA;', 'begin', 'end.']); ConvertProgram; CheckUnit('UnitA.pas', LinesToStr([ // statements 'rtl.module("UnitA", ["system"], function () {', ' var $mod = this;', ' var $impl = $mod.$impl;', ' this.$rtti.$ExtClass("TAnt", {', ' jsclass: "SET"', ' });', ' $mod.$implcode = function () {', ' rtl.recNewT($impl, "TBird", function () {', ' this.b = 0;', ' this.$eq = function (b) {', ' return this.b === b.b;', ' };', ' this.$assign = function (s) {', ' this.b = s.b;', ' return this;', ' };', ' var $r = $mod.$rtti.$Record("TBird", {}, this);', ' $r.addField("b", rtl.word);', ' });', ' $impl.f = null;', ' };', ' $mod.$init = function () {', ' $impl.f.x.b = $impl.f.x.b + 10;', ' };', '}, []);'])); CheckSource('TestGen_Class_ClassVarRecord_UnitImpl', LinesToStr([ // statements '']), LinesToStr([ // $mod.$main ''])); end; procedure TTestGenerics.TestGen_ClassInterface_Corba; begin StartProgram(false); Add([ '{$interfaces corba}', 'type', ' IUnknown = interface;', ' IUnknown = interface', ' [''{00000000-0000-0000-C000-000000000046}'']', ' end;', ' IInterface = IUnknown;', ' generic IBird = interface(IInterface)', ' function GetSize: T;', ' procedure SetSize(i: T);', ' property Size: T read GetSize write SetSize;', ' procedure DoIt(i: T);', ' end;', ' TObject = class', ' end;', ' generic TBird = class(TObject,specialize IBird)', ' function GetSize: T; virtual; abstract;', ' procedure SetSize(i: T); virtual; abstract;', ' procedure DoIt(i: T); virtual; abstract;', ' end;', ' IWordBird = specialize IBird;', ' TWordBird = specialize TBird;', 'var', ' BirdIntf: IWordBird;', 'begin', ' BirdIntf.Size:=BirdIntf.Size;', '']); ConvertProgram; CheckSource('TestGen_ClassInterface_Corba', LinesToStr([ // statements 'rtl.createInterface(this, "IUnknown", "{00000000-0000-0000-C000-000000000046}", [], null);', 'rtl.createClass(this, "TObject", null, function () {', ' this.$init = function () {', ' };', ' this.$final = function () {', ' };', '});', 'rtl.createInterface(', ' this,', ' "IBird$G2",', ' "{33AB51C6-6240-3BDF-B4B0-D48A593EAB0A}",', ' ["GetSize", "SetSize", "DoIt"],', ' this.IUnknown,', ' "IBird"', ');', 'rtl.createClass(this, "TBird$G1", this.TObject, function () {', ' rtl.addIntf(this, $mod.IBird$G2);', '}, "TBird");', 'this.BirdIntf = null;', '']), LinesToStr([ // $mod.$main '$mod.BirdIntf.SetSize($mod.BirdIntf.GetSize());', ''])); end; procedure TTestGenerics.TestGen_ClassInterface_InterfacedObject; begin StartProgram(true,[supTInterfacedObject]); Add([ '{$mode delphi}', 'type', ' IComparer = interface [''{505778ED-F783-4456-9691-32F419CC5E18}'']', ' function Compare(const Left, Right: T): Integer; overload;', ' end;', ' TComparer = class(TInterfacedObject, IComparer)', ' function Compare(const Left, Right: T): Integer;', ' end;', 'function TComparer.Compare(const Left, Right: T): Integer; begin end;', 'var', ' aComparer : IComparer;', 'begin', ' aComparer:=TComparer.Create;', '']); ConvertProgram; CheckSource('TestGen_ClassInterface_InterfacedObject', LinesToStr([ // statements 'rtl.createInterface(', ' this,', ' "IComparer$G2",', ' "{505778ED-F783-4456-9691-32F419CC5E18}",', ' ["Compare"],', ' pas.system.IUnknown,', ' "IComparer"', ');', 'this.aComparer = null;', 'rtl.createClass(this, "TComparer$G1", pas.system.TInterfacedObject, function () {', ' this.Compare = function (Left, Right) {', ' var Result = 0;', ' return Result;', ' };', ' rtl.addIntf(this, $mod.IComparer$G2);', ' rtl.addIntf(this, pas.system.IUnknown);', '}, "TComparer");', '']), LinesToStr([ // $mod.$main 'rtl.setIntfP($mod, "aComparer", rtl.queryIntfT($mod.TComparer$G1.$create("Create"), $mod.IComparer$G2), true);', ''])); end; procedure TTestGenerics.TestGen_ClassInterface_COM_RTTI; begin StartProgram(true,[supTInterfacedObject]); Add([ '{$mode delphi}', 'type', ' TBird = class', ' function Fly: T;', ' end;', ' IAnt = interface', ' procedure InterfaceProc;', ' end;', 'function TBird.Fly: T;', 'begin', ' if TypeInfo(T)=nil then ;', 'end;', 'var Bird: TBird;', ' Ant: IAnt;', 'begin', ' Ant := Bird.Fly;', '']); ConvertProgram; CheckSource('TestGen_ClassInterface_COM_RTTI', LinesToStr([ // statements 'rtl.createClass(this, "TBird", pas.system.TObject, function () {', ' this.Fly$G1 = function () {', ' var Result = null;', ' if ($mod.$rtti["IAnt"] === null) ;', ' return Result;', ' };', '});', 'rtl.createInterface(this, "IAnt", "{B9D0FF27-A446-3A1B-AA85-F167837AA297}", ["InterfaceProc"], pas.system.IUnknown);', 'this.Bird = null;', 'this.Ant = null;', '']), LinesToStr([ // $mod.$main 'rtl.setIntfP($mod, "Ant", $mod.Bird.Fly$G1(), true);', ''])); end; procedure TTestGenerics.TestGen_ClassInterface_Helper; begin StartProgram(true,[supTInterfacedObject]); Add([ '{$mode objfpc}', '{$ModeSwitch typehelpers}', 'type', ' IAnt = interface', ' procedure InterfaceProc;', ' end;', ' TBird = type helper for IAnt', ' generic procedure Fly(a: T);', ' end;', 'generic procedure TBird.Fly(a: T);', 'begin', 'end;', 'var ', ' Ant: IAnt;', 'begin', ' Ant.specialize Fly(3);', '']); ConvertProgram; CheckSource('TestGen_ClassInterface_COM_RTTI', LinesToStr([ // statements 'rtl.createInterface(this, "IAnt", "{B9D0FF27-A446-3A1B-AA85-F167837AA297}", ["InterfaceProc"], pas.system.IUnknown);', 'rtl.createHelper(this, "TBird", null, function () {', ' this.Fly$G1 = function (a) {', ' };', '});', 'this.Ant = null;', '']), LinesToStr([ // $mod.$main '$mod.TBird.Fly$G1.call({', ' p: $mod,', ' get: function () {', ' return this.p.Ant;', ' },', ' set: function (v) {', ' rtl.setIntfP(this.p, "Ant", v);', ' }', '}, 3);', ''])); end; procedure TTestGenerics.TestGen_ClassInterface_DelayedInitSpec; begin WithTypeInfo:=true; StartProgram(true,[supTObject,supTInterfacedObject]); AddModuleWithIntfImplSrc('UnitA.pas', LinesToStr([ '{$mode delphi}', 'type', ' TAnt = interface', ' procedure Run(x: T);', ' end;', '']), LinesToStr([ ''])); Add([ '{$mode delphi}', 'uses UnitA;', 'type', ' TArrWord = array of word;', ' TMyIntf = TAnt;', ' TBird = class(TInterfacedObject,TMyIntf)', ' procedure Run(a: TArrWord); external name ''Run'';', ' end;', 'var', ' i: TMyIntf;', 'begin', ' i:=TBird.Create;', ' i.Run([3,4]);', 'end.']); ConvertProgram; CheckUnit('UnitA.pas', LinesToStr([ // statements 'rtl.module("UnitA", ["system"], function () {', ' var $mod = this;', ' $mod.$rtti.$Interface("TAnt");', ' rtl.createInterface(', ' this,', ' "TAnt$G1",', ' "{B145F21B-2696-32D5-87A5-F16C037A2D45}",', ' ["Run"],', ' pas.system.IUnknown,', ' function () {', ' this.$initSpec = function () {', ' var $r = this.$rtti;', ' $r.addMethod("Run", 0, [["x", pas.program.$rtti["TArrWord"]]]);', ' };', ' },', ' "TAnt"', ' );', '});'])); CheckSource('TestGen_ClassInterface_DelayedInitSpec', LinesToStr([ // statements 'this.$rtti.$DynArray("TArrWord", {', ' eltype: rtl.word', '});', 'rtl.createClass(this, "TBird", pas.system.TInterfacedObject, function () {', ' rtl.addIntf(this, pas.UnitA.TAnt$G1);', ' rtl.addIntf(this, pas.system.IUnknown);', '});', 'this.i = null;', '$mod.$implcode = function () {', ' pas.UnitA.TAnt$G1.$initSpec();', '};', '']), LinesToStr([ // $mod.$main 'rtl.setIntfP($mod, "i", rtl.queryIntfT($mod.TBird.$create("Create"), pas.UnitA.TAnt$G1), true);', '$mod.i.Run([3, 4]);', ''])); end; procedure TTestGenerics.TestGen_InlineSpec_Constructor; begin StartProgram(false); Add([ '{$mode objfpc}', 'type', ' TObject = class', ' public', ' constructor Create;', ' end;', ' generic TBird = class', ' end;', 'constructor TObject.Create; begin end;', 'var b: specialize TBird;', 'begin', ' b:=specialize TBird.Create;', '']); ConvertProgram; CheckSource('TestGen_InlineSpec_Constructor', LinesToStr([ // statements 'rtl.createClass(this, "TObject", null, function () {', ' this.$init = function () {', ' };', ' this.$final = function () {', ' };', ' this.Create = function () {', ' return this;', ' };', '});', 'rtl.createClass(this, "TBird$G1", this.TObject, function () {', '}, "TBird");', 'this.b = null;', '']), LinesToStr([ // $mod.$main '$mod.b = $mod.TBird$G1.$create("Create");', ''])); end; procedure TTestGenerics.TestGen_CallUnitImplProc; begin AddModuleWithIntfImplSrc('UnitA.pas', LinesToStr([ 'type', ' generic TBird = class', ' procedure Fly;', ' end;', 'var b: specialize TBird;', '']), LinesToStr([ 'procedure DoIt;', 'var b: specialize TBird;', 'begin', ' b:=specialize TBird.Create;', ' b.Fly;', 'end;', 'procedure TBird.Fly;', 'begin', ' DoIt;', 'end;', ''])); StartProgram(true,[supTObject]); Add('uses UnitA;'); Add('begin'); ConvertProgram; CheckUnit('UnitA.pas', LinesToStr([ // statements 'rtl.module("UnitA", ["system"], function () {', ' var $mod = this;', ' var $impl = $mod.$impl;', ' rtl.createClass(this, "TBird$G1", pas.system.TObject, function () {', ' this.Fly = function () {', ' $impl.DoIt();', ' };', ' }, "TBird");', ' this.b = null;', ' rtl.createClass(this, "TBird$G2", pas.system.TObject, function () {', ' this.Fly = function () {', ' $impl.DoIt();', ' };', ' }, "TBird");', ' $mod.$implcode = function () {', ' $impl.DoIt = function () {', ' var b = null;', ' b = $mod.TBird$G2.$create("Create");', ' b.Fly();', ' };', ' };', '}, []);', ''])); end; procedure TTestGenerics.TestGen_IntAssignTemplVar; begin StartProgram(false); Add([ 'type', ' TObject = class end;', ' generic TBird = class', ' m: T;', ' procedure Fly;', ' end;', 'var b: specialize TBird;', 'procedure TBird.Fly;', 'var i: nativeint;', 'begin', ' i:=m;', 'end;', 'begin', '']); ConvertProgram; CheckSource('TestGen_IntAssignTemplVar', LinesToStr([ // statements 'rtl.createClass(this, "TObject", null, function () {', ' this.$init = function () {', ' };', ' this.$final = function () {', ' };', '});', 'rtl.createClass(this, "TBird$G1", this.TObject, function () {', ' this.$init = function () {', ' $mod.TObject.$init.call(this);', ' this.m = 0;', ' };', ' this.Fly = function () {', ' var i = 0;', ' i = this.m;', ' };', '}, "TBird");', 'this.b = null;', '']), LinesToStr([ // $mod.$main ''])); end; procedure TTestGenerics.TestGen_TypeCastDotField; begin StartProgram(false); Add([ 'type', ' TObject = class end;', ' generic TBird = class', ' Field: T;', ' procedure Fly;', ' end;', 'var', ' o: TObject;', ' b: specialize TBird;', 'procedure TBird.Fly;', 'begin', ' specialize TBird(o).Field:=3;', ' if 4=specialize TBird(o).Field then ;', 'end;', 'begin', ' specialize TBird(o).Field:=5;', ' if 6=specialize TBird(o).Field then ;', '']); ConvertProgram; CheckSource('TestGen_TypeCastDotField', LinesToStr([ // statements 'rtl.createClass(this, "TObject", null, function () {', ' this.$init = function () {', ' };', ' this.$final = function () {', ' };', '});', 'this.o = null;', 'rtl.createClass(this, "TBird$G1", this.TObject, function () {', ' this.$init = function () {', ' $mod.TObject.$init.call(this);', ' this.Field = 0;', ' };', ' this.Fly = function () {', ' $mod.o.Field = 3;', ' if (4 === $mod.o.Field) ;', ' };', '}, "TBird");', 'this.b = null;', '']), LinesToStr([ // $mod.$main '$mod.o.Field = 5;', 'if (6 === $mod.o.Field) ;', ''])); end; procedure TTestGenerics.TestGen_Except; begin StartProgram(false); Add([ 'type', ' TObject = class end;', ' generic TBird = class', ' Field: T;', ' procedure Fly;', ' end;', ' Exception = class', ' end;', ' generic EBird = class(Exception)', ' Id: T;', ' end;', 'var', ' b: specialize TBird;', 'procedure TBird.Fly;', 'begin', ' try', ' except', ' on E: Exception do Fly;', ' on EBird: specialize EBird do EBird.Id:=3;', ' else', ' Fly;', ' end;', 'end;', 'begin', '']); ConvertProgram; CheckSource('TestGen_Except', LinesToStr([ // statements 'rtl.createClass(this, "TObject", null, function () {', ' this.$init = function () {', ' };', ' this.$final = function () {', ' };', '});', 'rtl.createClass(this, "Exception", this.TObject, function () {', '});', 'rtl.createClass(this, "TBird$G1", this.TObject, function () {', ' this.$init = function () {', ' $mod.TObject.$init.call(this);', ' this.Field = 0;', ' };', ' this.Fly = function () {', ' try {} catch ($e) {', ' if ($mod.Exception.isPrototypeOf($e)) {', ' var E = $e;', ' this.Fly();', ' } else if ($mod.EBird$G1.isPrototypeOf($e)) {', ' var EBird = $e;', ' EBird.Id = 3;', ' } else {', ' this.Fly();', ' }', ' };', ' };', '}, "TBird");', 'this.b = null;', 'rtl.createClass(this, "EBird$G1", this.Exception, function () {', ' this.$init = function () {', ' $mod.Exception.$init.call(this);', ' this.Id = 0;', ' };', '}, "EBird");', '']), LinesToStr([ // $mod.$main ''])); end; procedure TTestGenerics.TestGen_HelperForArray; begin StartProgram(false); Add([ '{$ModeSwitch typehelpers}', 'type', ' generic TArr = array[1..2] of T;', ' TWordArrHelper = type helper for specialize TArr', ' procedure Fly(w: word);', ' end;', 'procedure TWordArrHelper.Fly(w: word);', 'begin', 'end;', 'var', ' a: specialize TArr;', 'begin', ' a.Fly(3);', '']); ConvertProgram; CheckSource('TestGen_HelperForArray', LinesToStr([ // statements 'rtl.createHelper(this, "TWordArrHelper", null, function () {', ' this.Fly = function (w) {', ' };', '});', 'this.a = rtl.arraySetLength(null, 0, 2);', '']), LinesToStr([ // $mod.$main '$mod.TWordArrHelper.Fly.call({', ' p: $mod,', ' get: function () {', ' return this.p.a;', ' },', ' set: function (v) {', ' this.p.a = v;', ' }', '}, 3);', ''])); end; procedure TTestGenerics.TestGenProc_Function_ObjFPC; begin StartProgram(false); Add([ 'generic function Run(a: T): T;', 'var i: T;', 'begin', ' a:=i;', ' Result:=a;', 'end;', 'var w: word;', 'begin', ' w:=specialize Run(3);', '']); ConvertProgram; CheckSource('TestGenProc_Function_ObjFPC', LinesToStr([ // statements 'this.w = 0;', 'this.Run$G1 = function (a) {', ' var Result = 0;', ' var i = 0;', ' a = i;', ' Result = a;', ' return Result;', '};', '']), LinesToStr([ // $mod.$main '$mod.w = $mod.Run$G1(3);', ''])); end; procedure TTestGenerics.TestGenProc_Function_Delphi; begin StartProgram(false); Add([ '{$mode delphi}', 'function Run(a: T): T;', 'var i: T;', 'begin', ' a:=i;', ' Result:=a;', 'end;', 'var w: word;', 'begin', ' w:=Run(3);', '']); ConvertProgram; CheckSource('TestGenProc_Function_Delphi', LinesToStr([ // statements 'this.w = 0;', 'this.Run$G1 = function (a) {', ' var Result = 0;', ' var i = 0;', ' a = i;', ' Result = a;', ' return Result;', '};', '']), LinesToStr([ // $mod.$main '$mod.w = $mod.Run$G1(3);', ''])); end; procedure TTestGenerics.TestGenProc_Overload; begin StartProgram(false); Add([ 'generic procedure DoIt(a: T; w: word); overload;', 'begin', 'end;', 'generic procedure DoIt(a: T; b: boolean); overload;', 'begin', 'end;', 'begin', ' specialize DoIt(3,4);', ' specialize DoIt(false,5);', ' specialize DoIt(6,true);', ' specialize DoIt(7.3,true);', '']); ConvertProgram; CheckSource('TestGenProc_Overload', LinesToStr([ // statements 'this.DoIt$G1 = function (a, w) {', '};', 'this.DoIt$G2 = function (a, w) {', '};', 'this.DoIt$1G1 = function (a, b) {', '};', 'this.DoIt$1G2 = function (a, b) {', '};', '']), LinesToStr([ // $mod.$main '$mod.DoIt$G1(3, 4);', '$mod.DoIt$G2(false, 5);', '$mod.DoIt$1G1(6, true);', '$mod.DoIt$1G2(7.3, true);', ''])); end; procedure TTestGenerics.TestGenProc_Forward; begin StartProgram(false); Add([ '{$mode delphi}', 'procedure Run(a: S; b: boolean); forward;', 'procedure Run(a: S; b: boolean);', 'begin', ' Run(1,true);', 'end;', 'begin', ' Run(1.3,true);', '']); ConvertProgram; CheckSource('TestGenProc_infer_OverloadForward', LinesToStr([ // statements 'this.Run$G1 = function (a, b) {', ' $mod.Run$G1(1, true);', '};', 'this.Run$G2 = function (a, b) {', ' $mod.Run$G1(1, true);', '};', '']), LinesToStr([ // $mod.$main '$mod.Run$G2(1.3, true);', ''])); end; procedure TTestGenerics.TestGenProc_Infer_OverloadForward; begin StartProgram(false); Add([ '{$mode delphi}', 'procedure {#A}Run(a: S; b: boolean); forward; overload;', 'procedure {#B}Run(a: T; w: word); forward; overload;', 'procedure {#C}Run(a: U; b: U); forward; overload;', 'procedure {#A2}Run(a: S; b: boolean); overload;', 'begin', ' {@A}Run(1,true);', // non generic take precedence ' {@B}Run(2,word(3));', // non generic take precedence ' {@C}Run(''foo'',''bar'');', 'end;', 'procedure {#B2}Run(a: T; w: word); overload;', 'begin', 'end;', 'procedure {#C2}Run(a: U; b: U); overload;', 'begin', 'end;', 'begin', ' {@A}Run(1,true);', // non generic take precedence ' {@B}Run(2,word(3));', // non generic take precedence ' {@C}Run(''foo'',''bar'');', '']); ConvertProgram; CheckSource('TestGenProc_infer_OverloadForward', LinesToStr([ // statements 'this.Run$G1 = function (a, b) {', ' $mod.Run$G1(1, true);', ' $mod.Run$1G1(2, 3);', ' $mod.Run$2G1("foo", "bar");', '};', 'this.Run$1G1 = function (a, w) {', '};', 'this.Run$2G1 = function (a, b) {', '};', '']), LinesToStr([ // $mod.$main '$mod.Run$G1(1, true);', '$mod.Run$1G1(2, 3);', '$mod.Run$2G1("foo", "bar");', ''])); end; procedure TTestGenerics.TestGenProc_TypeInfo; begin WithTypeInfo:=true; StartProgram(true,[supTypeInfo]); Add([ '{$modeswitch implicitfunctionspecialization}', 'generic procedure Run(a: S);', 'var', ' p: TTypeInfo;', 'begin', ' p:=TypeInfo(S);', ' p:=TypeInfo(a);', 'end;', 'begin', ' Run(word(3));', ' Run(''foo'');', '']); ConvertProgram; CheckSource('TestGenProc_TypeInfo', LinesToStr([ // statements 'this.Run$G1 = function (a) {', ' var p = null;', ' p = rtl.word;', ' p = rtl.word;', '};', 'this.Run$G2 = function (a) {', ' var p = null;', ' p = rtl.string;', ' p = rtl.string;', '};', '']), LinesToStr([ // $mod.$main '$mod.Run$G1(3);', '$mod.Run$G2("foo");', ''])); end; procedure TTestGenerics.TestGenProc_Infer_Widen; begin StartProgram(false); Add([ '{$mode delphi}', 'procedure Run(a: S; b: S);', 'begin', 'end;', 'begin', ' Run(word(1),longint(2));', ' Run(byte(2),smallint(2));', ' Run(longword(3),longint(2));', ' Run(nativeint(4),longint(2));', ' Run(nativeint(5),nativeuint(2));', ' Run(''a'',''foo'');', ' Run(''bar'',''c'');', '']); ConvertProgram; CheckSource('TestGenProc_Infer_Widen', LinesToStr([ // statements 'this.Run$G1 = function (a, b) {', '};', 'this.Run$G2 = function (a, b) {', '};', 'this.Run$G3 = function (a, b) {', '};', '']), LinesToStr([ // $mod.$main '$mod.Run$G1(1, 2);', '$mod.Run$G1(2, 2);', '$mod.Run$G2(3, 2);', '$mod.Run$G2(4, 2);', '$mod.Run$G2(5, 2);', '$mod.Run$G3("a", "foo");', '$mod.Run$G3("bar", "c");', ''])); end; procedure TTestGenerics.TestGenProc_Infer_PassAsArg; begin StartProgram(false); Add([ '{$mode delphi}', 'function Run(a: T): T;', 'var b: T;', 'begin', ' Run(Run(3));', ' Run(Run(word(4)));', 'end;', 'begin', ' Run(Run(5));', ' Run(Run(word(6)));', '']); ConvertProgram; CheckSource('TestGenProc_Infer_PassAsArg', LinesToStr([ // statements 'this.Run$G1 = function (a) {', ' var Result = 0;', ' var b = 0;', ' $mod.Run$G1($mod.Run$G1(3));', ' $mod.Run$G1($mod.Run$G1(4));', ' return Result;', '};', '']), LinesToStr([ // $mod.$main '$mod.Run$G1($mod.Run$G1(5));', '$mod.Run$G1($mod.Run$G1(6));', ''])); end; procedure TTestGenerics.TestGenProc_AnonymousProc; begin StartProgram(false); Add([ '{$mode delphi}', 'type', ' TProc = reference to procedure;', ' TFunc = reference to function(Value: JSValue): JSValue;', 'function Run(a: T; p: TProc): T;', 'var b: T;', ' f: TFunc;', 'begin', ' Result:=Run(a,procedure()begin end);', ' f:=function(b: JSValue): JSValue begin end;', ' f:=function(b: JSValue): JSValue', ' function Sub(c: JSValue): JSValue;', ' begin', ' Result:=c;', ' end;', ' begin', ' Result:=Sub(b);', ' end;', 'end;', 'begin', ' Run(3,procedure() begin end);', '']); ConvertProgram; CheckSource('TestGenProc_AnonymousProc', LinesToStr([ // statements 'this.Run$G1 = function (a, p) {', ' var Result = 0;', ' var b = 0;', ' var f = null;', ' Result = $mod.Run$G1(a, function () {', ' });', ' f = function (b) {', ' var Result = undefined;', ' return Result;', ' };', ' f = function (b) {', ' var Result = undefined;', ' function Sub(c) {', ' var Result = undefined;', ' Result = c;', ' return Result;', ' };', ' Result = Sub(b);', ' return Result;', ' };', ' return Result;', '};', '']), LinesToStr([ // $mod.$main '$mod.Run$G1(3, function () {', '});', ''])); end; procedure TTestGenerics.TestGenMethod_ImplicitSpec_ObjFPC; begin StartProgram(false); Add([ '{$mode objfpc}', '{$ModeSwitch implicitfunctionspecialization}', 'type', ' TObject = class', ' generic procedure {#A}Run(a: S; b: boolean); overload;', ' generic procedure {#B}Run(a: T; w: word); overload;', ' generic procedure {#C}Run(a: U; b: U); overload;', ' end; ', 'generic procedure {#A2}TObject.Run(a: S; b: boolean); overload;', 'begin', ' {@A}Run(1,true);', // non generic take precedence ' {@B}Run(2,word(3));', // non generic take precedence ' {@C}Run(''foo'',''bar'');', 'end;', 'generic procedure {#B2}TObject.Run(a: T; w: word); overload;', 'begin', 'end;', 'generic procedure {#C2}TObject.Run(a: U; b: U); overload;', 'begin', 'end;', 'var o: TObject;', 'begin', ' o.{@A}Run(1,true);', // non generic take precedence ' o.{@B}Run(2,word(3));', // non generic take precedence ' o.{@C}Run(''foo'',''bar'');', '']); ConvertProgram; CheckSource('TestGenMethod_ImplicitSpec_ObjFPC', LinesToStr([ // statements 'rtl.createClass(this, "TObject", null, function () {', ' this.$init = function () {', ' };', ' this.$final = function () {', ' };', ' this.Run$G1 = function (a, b) {', ' this.Run$G1(1, true);', ' this.Run$1G1(2, 3);', ' this.Run$2G1("foo", "bar");', ' };', ' this.Run$1G1 = function (a, w) {', ' };', ' this.Run$2G1 = function (a, b) {', ' };', '});', 'this.o = null;', '']), LinesToStr([ // $mod.$main '$mod.o.Run$G1(1, true);', '$mod.o.Run$1G1(2, 3);', '$mod.o.Run$2G1("foo", "bar");', ''])); end; procedure TTestGenerics.TestGenMethod_Delphi; begin StartProgram(false); Add([ '{$mode delphi}', 'type', ' TObject = class', ' procedure Run;', ' end; ', 'procedure TObject.Run;', 'begin', 'end;', 'var o: TObject;', 'begin', ' o.Run;', ' o.Run();', ' with o do begin', ' Run;', ' Run();', ' end;', '']); ConvertProgram; CheckSource('TestGenMethod_Delphi', LinesToStr([ // statements 'rtl.createClass(this, "TObject", null, function () {', ' this.$init = function () {', ' };', ' this.$final = function () {', ' };', ' this.Run$G1 = function () {', ' };', '});', 'this.o = null;', '']), LinesToStr([ // $mod.$main '$mod.o.Run$G1();', '$mod.o.Run$G1();', 'var $with = $mod.o;', '$with.Run$G1();', '$with.Run$G1();', ''])); end; procedure TTestGenerics.TestGenMethod_Overload_Delphi; begin StartProgram(false); Add([ '{$mode delphi}', 'type', ' TObject = class', ' procedure Run; overload;', ' procedure Run(w: word); overload;', ' end; ', 'procedure TObject.Run;', 'begin', 'end;', 'procedure TObject.Run(w: word);', 'begin', 'end;', 'var o: TObject;', 'begin', ' o.Run;', ' o.Run();', ' o.Run(3);', ' with o do begin', ' Run;', ' Run();', ' Run(13);', ' end;', '']); ConvertProgram; CheckSource('TestGenMethod_Overload_Delphi', LinesToStr([ // statements 'rtl.createClass(this, "TObject", null, function () {', ' this.$init = function () {', ' };', ' this.$final = function () {', ' };', ' this.Run$G1 = function () {', ' };', ' this.Run$1G1 = function (w) {', ' };', '});', 'this.o = null;', '']), LinesToStr([ // $mod.$main '$mod.o.Run$G1();', '$mod.o.Run$G1();', '$mod.o.Run$1G1(3);', 'var $with = $mod.o;', '$with.Run$G1();', '$with.Run$G1();', '$with.Run$1G1(13);', ''])); end; procedure TTestGenerics.TestGenMethod_ParamProcVar_Forward_Issue39216; begin StartProgram(false); Add([ '{$mode delphi}', '{$modeswitch externalclass}', 'type', ' TMyProc = reference to procedure(Arg: T);', ' TMyClass = class;', ' TMyClassArray = array of TMyClass;', ' TMyClass = class external name ''MyClass''', ' public', ' procedure MyProc(MyProcVar: TMyProc);', ' end;', 'procedure Fly(w: word);', 'begin', 'end;', 'var', ' p: TMyProc;', ' e: TMyClass;', 'begin', ' e.MyProc(p);', ' e.MyProc(@Fly);', '']); ConvertProgram; CheckSource('TestGenMethod_ParamProcVar_Forward_Issue39216', LinesToStr([ // statements 'this.Fly = function (w) {', '};', 'this.p = null;', 'this.e = null;', '']), LinesToStr([ // $mod.$main '$mod.e.MyProc($mod.p);', '$mod.e.MyProc($mod.Fly);', ''])); end; procedure TTestGenerics.TestGen_Array_OtherUnit; begin WithTypeInfo:=true; StartProgram(true,[supTObject]); AddModuleWithIntfImplSrc('UnitA.pas', LinesToStr([ 'type', ' generic TDyn = array of T;', ' generic TStatic = array[1..2] of T;', '']), ''); AddModuleWithIntfImplSrc('UnitB.pas', LinesToStr([ 'uses UnitA;', 'type', ' TAnt = class end;', ' TAntArray = specialize TDyn;', 'procedure Run;', '']), LinesToStr([ 'procedure Run;', 'begin', ' if typeinfo(TAntArray)=nil then ;', 'end;', ''])); Add([ 'uses UnitB;', 'begin', ' Run;', '']); ConvertProgram; CheckUnit('UnitA.pas', LinesToStr([ // statements 'rtl.module("UnitA", ["system"], function () {', ' var $mod = this;', ' this.$rtti.$DynArray("TDyn", {});', '});'])); CheckUnit('UnitB.pas', LinesToStr([ // statements 'rtl.module("UnitB", ["system", "UnitA"], function () {', ' var $mod = this;', ' rtl.createClass(this, "TAnt", pas.system.TObject, function () {', ' });', ' this.Run = function () {', ' if (pas.UnitA.$rtti["TDyn"] === null) ;', ' };', '});'])); CheckSource('TestGen_Array_OtherUnit', LinesToStr([ // statements '$mod.$implcode = function () {', ' pas.UnitA.$rtti["TDyn"].eltype = pas.UnitB.$rtti["TAnt"];', '};', '']), LinesToStr([ // $mod.$main ' pas.UnitB.Run();', ''])); end; procedure TTestGenerics.TestGen_ArrayOfUnitImplRec; begin WithTypeInfo:=true; StartProgram(true,[supTObject]); AddModuleWithIntfImplSrc('UnitA.pas', LinesToStr([ 'type', ' generic TDyn = array of T;', ' generic TStatic = array[1..2] of T;', '']), LinesToStr([ 'type', ' TBird = record', ' b: word;', ' end;', ' TAnt = class end;', ' TAntArray = specialize TDyn;', 'var', ' d: specialize TDyn;', ' s: specialize TStatic;', ' p: pointer;', 'begin', ' d[0].b:=s[1].b;', ' s:=s;', ' p:=typeinfo(TAntArray);', ''])); Add([ 'uses UnitA;', 'begin', '']); ConvertProgram; CheckUnit('UnitA.pas', LinesToStr([ // statements 'rtl.module("UnitA", ["system"], function () {', ' var $mod = this;', ' var $impl = $mod.$impl;', ' this.$rtti.$DynArray("TDyn", {});', ' this.$rtti.$DynArray("TDyn", {});', ' this.TStatic$G1$clone = function (a) {', ' var b = [];', ' b.length = 2;', ' for (var c = 0; c < 2; c++) b[c] = $impl.TBird.$clone(a[c]);', ' return b;', ' };', ' this.$rtti.$StaticArray("TStatic", {', ' dims: [2]', ' });', ' $mod.$implcode = function () {', ' rtl.recNewT($impl, "TBird", function () {', ' this.b = 0;', ' this.$eq = function (b) {', ' return this.b === b.b;', ' };', ' this.$assign = function (s) {', ' this.b = s.b;', ' return this;', ' };', ' var $r = $mod.$rtti.$Record("TBird", {}, this);', ' $r.addField("b", rtl.word);', ' });', ' rtl.createClass($impl, "TAnt", pas.system.TObject, function () {', ' });', ' $impl.d = [];', ' $impl.s = rtl.arraySetLength(null, $impl.TBird, 2);', ' $impl.p = null;', ' };', ' $mod.$init = function () {', ' $impl.d[0].b = $impl.s[0].b;', ' $impl.s = $mod.TStatic$G1$clone($impl.s);', ' $impl.p = $mod.$rtti["TDyn"];', ' };', '}, []);'])); CheckSource('TestGen_ArrayOfUnitImplRec', LinesToStr([ // statements '$mod.$implcode = function () {', ' pas.UnitA.$rtti["TDyn"].eltype = pas.UnitA.$rtti["TAnt"];', ' pas.UnitA.$rtti["TDyn"].eltype = pas.UnitA.$rtti["TBird"];', ' pas.UnitA.$rtti["TStatic"].eltype = pas.UnitA.$rtti["TBird"];', '};', '']), LinesToStr([ // $mod.$main ''])); end; procedure TTestGenerics.TestGen_Array_TypecastJSValueResultToArg; begin StartProgram(false); Add([ '{$mode delphi}', 'type', ' TArray = array of T;', ' TFunc = function: JSValue of object;', ' TObject = class', ' f: TFunc;', ' function Run: jsvalue; virtual; abstract;', ' end;', 'procedure Sit(Arr: TArray);', 'begin', 'end;', 'procedure Fly(o: TObject);', 'begin', ' Sit(TArray(o.f()));', ' Sit(TArray(o.Run));', ' Sit(TArray(o.Run()));', 'end;', 'begin']); ConvertProgram; CheckSource('TestGen_Array_TypecastJSValueResultToArg', LinesToStr([ // statements 'rtl.createClass(this, "TObject", null, function () {', ' this.$init = function () {', ' this.f = null;', ' };', ' this.$final = function () {', ' this.f = undefined;', ' };', '});', 'this.Sit = function (Arr) {', '};', 'this.Fly = function (o) {', ' $mod.Sit(o.f());', ' $mod.Sit(o.Run());', ' $mod.Sit(o.Run());', '};', '']), LinesToStr([ // $mod.$main ''])); end; procedure TTestGenerics.TestGen_ProcType_ProcLocal; begin StartProgram(false); Add([ 'procedure Fly(w: word);', 'begin', 'end;', 'procedure Run(w: word);', 'type generic TProc = procedure(a: T);', 'var p: specialize TProc;', 'begin', ' p:=@Fly;', ' p(w);', 'end;', 'begin', 'end.']); ConvertProgram; CheckSource('TestGen_ProcType_ProcLocal', LinesToStr([ // statements 'this.Fly = function (w) {', '};', 'this.Run = function (w) {', ' var p = null;', ' p = $mod.Fly;', ' p(w);', '};', '']), LinesToStr([ // $mod.$main ''])); end; procedure TTestGenerics.TestGen_ProcType_Local_RTTI_Fail; begin WithTypeInfo:=true; StartProgram(false); Add([ 'procedure Fly(w: word);', 'begin', 'end;', 'procedure Run(w: word);', 'type generic TProc = procedure(a: T);', 'var', ' p: specialize TProc;', ' t: Pointer;', 'begin', ' p:=@Fly;', ' p(w);', ' t:=typeinfo(p);', 'end;', 'begin', 'end.']); SetExpectedPasResolverError(sSymbolCannotBePublished,nSymbolCannotBePublished); ConvertProgram; end; procedure TTestGenerics.TestGen_ProcType_ParamUnitImpl; begin WithTypeInfo:=true; StartProgram(true,[supTObject]); AddModuleWithIntfImplSrc('UnitA.pas', LinesToStr([ 'type', ' generic TAnt = function(const a: T): T;', '']), LinesToStr([ 'type', ' TBird = record', ' b: word;', ' end;', 'var', ' f: specialize TAnt;', ' b: TBird;', ' p: pointer;', 'begin', ' b:=f(b);', ' p:=typeinfo(f);', ''])); Add([ 'uses UnitA;', 'begin', 'end.']); ConvertProgram; CheckUnit('UnitA.pas', LinesToStr([ // statements 'rtl.module("UnitA", ["system"], function () {', ' var $mod = this;', ' var $impl = $mod.$impl;', ' this.$rtti.$ProcVar("TAnt", {', ' init: function () {', ' this.procsig = rtl.newTIProcSig([["a", $mod.$rtti["TBird"], 2]], $mod.$rtti["TBird"]);', ' }', ' });', ' $mod.$implcode = function () {', ' rtl.recNewT($impl, "TBird", function () {', ' this.b = 0;', ' this.$eq = function (b) {', ' return this.b === b.b;', ' };', ' this.$assign = function (s) {', ' this.b = s.b;', ' return this;', ' };', ' var $r = $mod.$rtti.$Record("TBird", {}, this);', ' $r.addField("b", rtl.word);', ' });', ' $impl.f = null;', ' $impl.b = $impl.TBird.$new();', ' $impl.p = null;', ' };', ' $mod.$init = function () {', ' $impl.b.$assign($impl.f($impl.b));', ' $impl.p = $mod.$rtti["TAnt"];', ' };', '}, []);'])); CheckSource('TestGen_Class_ClassVarRecord_UnitImpl', LinesToStr([ // statements '$mod.$implcode = function () {', ' pas.UnitA.$rtti["TAnt"].init();', '};', '']), LinesToStr([ // $mod.$main ''])); end; procedure TTestGenerics.TestGen_ProcType_TemplateCountOverload_Delphi; begin WithTypeInfo:=true; StartProgram(false); Add([ '{$mode delphi}', 'type', ' TProc = procedure(a, b: T);', ' TProc = procedure(a: S; b: T);', 'var', ' p: TProc;', ' q: TProc;', 'procedure Run(x,y: word);', 'begin', 'end;', 'procedure Fly(x: char; y: boolean);', 'begin', 'end;', 'begin', ' p:=Run;', ' q:=Fly;', 'end.']); ConvertProgram; CheckSource('TestGen_ProcType_TemplateCountOverload_Delphi', LinesToStr([ // statements 'this.$rtti.$ProcVar("TProc", {', ' procsig: rtl.newTIProcSig([["a", rtl.word], ["b", rtl.word]])', '});', 'this.p = null;', 'this.$rtti.$ProcVar("TProc", {', ' procsig: rtl.newTIProcSig([["a", rtl.char], ["b", rtl.boolean]])', '});', 'this.q = null;', 'this.Run = function (x, y) {', '};', 'this.Fly = function (x, y) {', '};', '']), LinesToStr([ // $mod.$main '$mod.p = $mod.Run;', '$mod.q = $mod.Fly;', ''])); end; Initialization RegisterTests([TTestGenerics]); end.