Forráskód Böngészése

fcl-passrc: specialize external class

git-svn-id: trunk@42644 -
Mattias Gaertner 6 éve
szülő
commit
7173349689

+ 2 - 0
packages/fcl-passrc/src/pasresolver.pp

@@ -10890,6 +10890,8 @@ begin
         exit;
         exit;
       if TPasClassType(aClassOrRec).IsForward then
       if TPasClassType(aClassOrRec).IsForward then
         exit;
         exit;
+      if TPasClassType(aClassOrRec).IsExternal then
+        exit;
       end;
       end;
     ClassOrRecScope:=aClassOrRec.CustomData as TPasClassOrRecordScope;
     ClassOrRecScope:=aClassOrRec.CustomData as TPasClassOrRecordScope;
     if ClassOrRecScope.SpecializedFrom<>nil then
     if ClassOrRecScope.SpecializedFrom<>nil then

+ 52 - 30
packages/fcl-passrc/src/pparser.pp

@@ -369,6 +369,8 @@ type
     function ParseExprOperand(AParent : TPasElement): TPasExpr;
     function ParseExprOperand(AParent : TPasElement): TPasExpr;
     function ParseExpIdent(AParent : TPasElement): TPasExpr; deprecated 'use ParseExprOperand instead'; // since fpc 3.3.1
     function ParseExpIdent(AParent : TPasElement): TPasExpr; deprecated 'use ParseExprOperand instead'; // since fpc 3.3.1
     procedure DoParseClassType(AType: TPasClassType);
     procedure DoParseClassType(AType: TPasClassType);
+    procedure DoParseClassExternalHeader(AObjKind: TPasObjKind;
+      out AExternalNameSpace, AExternalName: string);
     procedure DoParseArrayType(ArrType: TPasArrayType);
     procedure DoParseArrayType(ArrType: TPasArrayType);
     function DoParseExpression(AParent: TPaselement;InitExpr: TPasExpr=nil; AllowEqual : Boolean = True): TPasExpr;
     function DoParseExpression(AParent: TPaselement;InitExpr: TPasExpr=nil; AllowEqual : Boolean = True): TPasExpr;
     function DoParseConstValueExpression(AParent: TPasElement): TPasExpr;
     function DoParseConstValueExpression(AParent: TPasElement): TPasExpr;
@@ -1702,8 +1704,12 @@ begin
     ParseExcTokenError('[20190801112729]');
     ParseExcTokenError('[20190801112729]');
   ST:=TPasSpecializeType(CreateElement(TPasSpecializeType,TypeName,Parent));
   ST:=TPasSpecializeType(CreateElement(TPasSpecializeType,TypeName,Parent));
   try
   try
-    ST.Expr:=GenNameExpr;
-    GenNameExpr:=nil; // ownership transferred to ST
+    if GenNameExpr<>nil then
+      begin
+      ST.Expr:=GenNameExpr;
+      GenNameExpr.Parent:=ST;
+      GenNameExpr:=nil; // ownership transferred to ST
+      end;
     // read nested specialize arguments
     // read nested specialize arguments
     ReadSpecializeArguments(ST);
     ReadSpecializeArguments(ST);
     // Important: resolve type reference AFTER args, because arg count is needed
     // Important: resolve type reference AFTER args, because arg count is needed
@@ -4265,7 +4271,7 @@ function TPasParser.ParseGenericTypeDecl(Parent: TPasElement;
   end;
   end;
 
 
 var
 var
-  TypeName: String;
+  TypeName, AExternalNameSpace, AExternalName: String;
   NamePos: TPasSourcePos;
   NamePos: TPasSourcePos;
   TypeParams: TFPList;
   TypeParams: TFPList;
   ClassEl: TPasClassType;
   ClassEl: TPasClassType;
@@ -4274,6 +4280,7 @@ var
   ProcTypeEl: TPasProcedureType;
   ProcTypeEl: TPasProcedureType;
   ProcType: TProcType;
   ProcType: TProcType;
   i: Integer;
   i: Integer;
+  AObjKind: TPasObjKind;
 begin
 begin
   Result:=nil;
   Result:=nil;
   TypeName := CurTokenString;
   TypeName := CurTokenString;
@@ -4287,16 +4294,25 @@ begin
       tkObject,
       tkObject,
       tkClass :
       tkClass :
         begin
         begin
-        ClassEl := TPasClassType(CreateElement(TPasClassType,
-          TypeName, Parent, visDefault, NamePos, TypeParams));
         if CurToken=tkobject then
         if CurToken=tkobject then
-          ClassEl.ObjKind:=okObject
+          AObjKind:=okObject
         else
         else
-          ClassEl.ObjKind:=okClass;
+          AObjKind:=okClass;
+        NextToken;
+        if (AObjKind = okClass) and (CurToken = tkOf) then
+          ParseExcExpectedIdentifier;
+        DoParseClassExternalHeader(AObjKind,AExternalNameSpace,AExternalName);
+        ClassEl := TPasClassType(CreateElement(TPasClassType,
+          TypeName, Parent, visDefault, NamePos, TypeParams));
+        ClassEl.ObjKind:=AObjKind;
         if AddToParent and (Parent is TPasDeclarations) then
         if AddToParent and (Parent is TPasDeclarations) then
           TPasDeclarations(Parent).Classes.Add(ClassEl);
           TPasDeclarations(Parent).Classes.Add(ClassEl);
+        ClassEl.IsExternal:=(AExternalName<>'');
+        if AExternalName<>'' then
+          ClassEl.ExternalName:={$ifdef pas2js}DeQuoteString{$else}AnsiDequotedStr{$endif}(AExternalName,'''');
+        if AExternalNameSpace<>'' then
+          ClassEl.ExternalNameSpace:={$ifdef pas2js}DeQuoteString{$else}AnsiDequotedStr{$endif}(AExternalNameSpace,'''');
         InitGenericType(ClassEl,TypeParams);
         InitGenericType(ClassEl,TypeParams);
-        NextToken;
         DoParseClassType(ClassEl);
         DoParseClassType(ClassEl);
         CheckHint(ClassEl,True);
         CheckHint(ClassEl,True);
         Engine.FinishScope(stTypeDef,ClassEl);
         Engine.FinishScope(stTypeDef,ClassEl);
@@ -7125,6 +7141,33 @@ begin
     end;
     end;
 end;
 end;
 
 
+procedure TPasParser.DoParseClassExternalHeader(AObjKind: TPasObjKind; out
+  AExternalNameSpace, AExternalName: string);
+begin
+  if ((AObjKind in [okClass,okInterface]) and (msExternalClass in CurrentModeswitches)
+      and CurTokenIsIdentifier('external')) then
+    begin
+    NextToken;
+    if CurToken<>tkString then
+      UnGetToken
+    else
+      AExternalNameSpace:=CurTokenString;
+    ExpectIdentifier;
+    If Not CurTokenIsIdentifier('Name')  then
+      ParseExc(nParserExpectedExternalClassName,SParserExpectedExternalClassName);
+    NextToken;
+    if not (CurToken in [tkChar,tkString]) then
+      CheckToken(tkString);
+    AExternalName:=CurTokenString;
+    NextToken;
+    end
+  else
+    begin
+    AExternalNameSpace:='';
+    AExternalName:='';
+    end;
+end;
+
 procedure TPasParser.DoParseArrayType(ArrType: TPasArrayType);
 procedure TPasParser.DoParseArrayType(ArrType: TPasArrayType);
 var
 var
   S: String;
   S: String;
@@ -7211,28 +7254,7 @@ begin
     end;
     end;
     exit;
     exit;
     end;
     end;
-  if ((AObjKind in [okClass,okInterface]) and (msExternalClass in CurrentModeswitches)
-      and CurTokenIsIdentifier('external')) then
-    begin
-    NextToken;
-    if CurToken<>tkString then
-      UnGetToken
-    else
-      AExternalNameSpace:=CurTokenString;
-    ExpectIdentifier;
-    If Not CurTokenIsIdentifier('Name')  then
-      ParseExc(nParserExpectedExternalClassName,SParserExpectedExternalClassName);
-    NextToken;
-    if not (CurToken in [tkChar,tkString]) then
-      CheckToken(tkString);
-    AExternalName:=CurTokenString;
-    NextToken;
-    end
-  else
-    begin
-    AExternalNameSpace:='';
-    AExternalName:='';
-    end;
+  DoParseClassExternalHeader(AObjKind,AExternalNameSpace,AExternalName);
   if AObjKind in okAllHelpers then
   if AObjKind in okAllHelpers then
     begin
     begin
     if not CurTokenIsIdentifier('Helper') then
     if not CurTokenIsIdentifier('Helper') then

+ 46 - 0
packages/fcl-passrc/tests/tcresolvegenerics.pas

@@ -58,6 +58,9 @@ type
     // ToDo: class-of
     // ToDo: class-of
     // ToDo: UnitA.impl uses UnitB.intf uses UnitA.intf, UnitB has specialize of UnitA
     // ToDo: UnitA.impl uses UnitB.intf uses UnitA.intf, UnitB has specialize of UnitA
 
 
+    // generic external class
+    procedure TestGen_ExtClass_Array;
+
     // ToDo: generic interface
     // ToDo: generic interface
 
 
     // ToDo: generic array
     // ToDo: generic array
@@ -499,6 +502,49 @@ begin
   CheckResolverException('type "TBird" is not yet completely defined',nTypeXIsNotYetCompletelyDefined);
   CheckResolverException('type "TBird" is not yet completely defined',nTypeXIsNotYetCompletelyDefined);
 end;
 end;
 
 
+procedure TTestResolveGenerics.TestGen_ExtClass_Array;
+begin
+  StartProgram(false);
+  Add([
+  '{$mode delphi}',
+  '{$ModeSwitch externalclass}',
+  'type',
+  '  NativeInt = longint;',
+  '  TJSGenArray<T> = 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<T>;',
+  '  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<word>;',
+  '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];',
+  '']);
+  ParseProgram;
+end;
+
 procedure TTestResolveGenerics.TestGen_GenericFunction;
 procedure TTestResolveGenerics.TestGen_GenericFunction;
 begin
 begin
   StartProgram(false);
   StartProgram(false);