Browse Source

pastojs: external class field with brackets

git-svn-id: trunk@38958 -
Mattias Gaertner 7 years ago
parent
commit
cc7983d781

+ 72 - 12
packages/pastojs/src/fppas2js.pp

@@ -1333,6 +1333,8 @@ type
   TDotContext = Class(TConvertContext)
   public
     LeftResolved: TPasResolverResult;
+    // created by ConvertElement if subident needs special translation:
+    JS: TJSElement;
   end;
 
   { TAssignContext - used for left side of an assign statement }
@@ -1343,7 +1345,7 @@ type
     LeftResolved: TPasResolverResult;
     RightResolved: TPasResolverResult;
     RightSide: TJSElement;
-    // created by ConvertElement:
+    // created by ConvertElement if assign needs a call:
     PropertyEl: TPasProperty;
     Setter: TPasElement;
     Call: TJSCallExpression;
@@ -1531,6 +1533,7 @@ type
     Function CreateLiteralBoolean(El: TPasElement; b: boolean): TJSLiteral; virtual;
     Function CreateLiteralNull(El: TPasElement): TJSLiteral; virtual;
     Function CreateLiteralUndefined(El: TPasElement): TJSLiteral; virtual;
+    Function CreateLiteralCustomValue(El: TPasElement; const s: TJSString): TJSLiteral; virtual;
     Function CreateSetLiteralElement(Expr: TPasExpr; AContext: TConvertContext): TJSElement; virtual;
     Procedure ConvertCharLiteralToInt(Lit: TJSLiteral; ErrorEl: TPasElement; AContext: TConvertContext); virtual;
     Function ClonePrimaryExpression(El: TJSPrimaryExpression; Src: TPasElement): TJSPrimaryExpression;
@@ -6087,6 +6090,7 @@ var
   RightRef: TResolvedReference;
   ParamsExpr: TParamsExpr;
   RightEl: TPasExpr;
+  RightRefDecl: TPasElement;
 begin
   Result:=nil;
 
@@ -6098,11 +6102,14 @@ begin
     RightEl:=ParamsExpr.Value;
     end;
 
+  RightRef:=nil;
+  RightRefDecl:=nil;
   if (RightEl.ClassType=TPrimitiveExpr)
       and (RightEl.CustomData is TResolvedReference) then
     begin
     RightRef:=TResolvedReference(RightEl.CustomData);
-    if IsExternalClassConstructor(RightRef.Declaration) then
+    RightRefDecl:=RightRef.Declaration;
+    if IsExternalClassConstructor(RightRefDecl) then
       begin
       if ParamsExpr<>nil then
         begin
@@ -6137,14 +6144,20 @@ begin
   Left:=ConvertElement(El.left,AContext);
   if Left=nil then
     RaiseInconsistency(20170201140821,El);
-
   AContext.Access:=OldAccess;
+
   // convert right side
   DotContext:=TDotContext.Create(El,Left,AContext);
   Right:=nil;
   try
     DotContext.LeftResolved:=LeftResolved;
     Right:=ConvertElement(El.right,DotContext);
+    if DotContext.JS<>nil then
+      begin
+      Left:=nil;
+      Right:=nil;
+      exit(DotContext.JS);
+      end;
   finally
     DotContext.Free;
     if Right=nil then
@@ -6358,6 +6371,7 @@ var
   FuncScope: TPas2JSProcedureScope;
   Value: TResEvalValue;
   aResolver: TPas2JSResolver;
+  BracketExpr: TJSBracketMemberExpression;
 begin
   Result:=nil;
   if not (El.CustomData is TResolvedReference) then
@@ -6467,6 +6481,7 @@ begin
     begin
     if TPasConst(Decl).IsConst and (TPasConst(Decl).Expr<>nil) then
       begin
+      // const with expression
       Value:=aResolver.Eval(TPasConst(Decl).Expr,[refConst]);
       if (Value<>nil)
           and (Value.Kind in [revkNil,revkBool,revkInt,revkUInt,revkFloat,revkEnum]) then
@@ -6478,7 +6493,7 @@ begin
         end;
       if vmExternal in TPasConst(Decl).VarModifiers then
         begin
-        // external constant are always added by value, not by reference
+        // external constant with expression is always added by value, not by reference
         Result:=ConvertElement(TPasConst(Decl).Expr,AContext);
         exit;
         end;
@@ -6550,8 +6565,28 @@ begin
     Name:=AContext.GetLocalName(Decl)
   else
     Name:=CreateReferencePath(Decl,AContext,rpkPathAndName,false,Ref);
+  if Name='' then
+    RaiseNotSupported(El,AContext,20180509134804,GetObjName(Decl));
+
   if Result=nil then
+    begin
+    if (Name[1]='[') and (Name[length(Name)]=']')
+        and (AContext is TDotContext)
+        and (AContext.JSElement<>nil) then
+      begin
+      // e.g. Obj.A  and A is defined as: A: t external name '["name"]';
+      // -> Obj["name"]
+      if IsImplicitCall then
+        RaiseNotSupported(El,AContext,20180509134951,Name);
+      BracketExpr:=TJSBracketMemberExpression(CreateElement(TJSBracketMemberExpression,El));
+      TDotContext(AContext).JS:=BracketExpr;
+      BracketExpr.MExpr:=AContext.JSElement;
+      Result:=CreateLiteralCustomValue(El,TJSString(copy(Name,2,length(Name)-2)));
+      BracketExpr.Name:=Result;
+      exit;
+      end;
     Result:=CreatePrimitiveDotExpr(Name,El);
+    end;
 
   if IsImplicitCall then
     CallImplicit(Decl);
@@ -7272,7 +7307,7 @@ var
         begin
         AccessEl:=aResolver.GetPasPropertySetter(Prop);
         if IsJSBracketAccessorAndConvert(Prop,AccessEl,AContext,true) then
-            exit;
+          exit;
         AssignContext:=AContext.AccessContext as TAssignContext;
         AssignContext.PropertyEl:=Prop;
         AssignContext.Setter:=AccessEl;
@@ -7410,6 +7445,8 @@ var
       DotContext:=TDotContext.Create(El.Value,Left,AContext);
       DotContext.LeftResolved:=ResolvedEl;
       ConvertIndexedProperty(Prop,DotContext);
+      if DotContext.JS<>nil then
+        RaiseNotSupported(El,AContext,20180509134226,GetObjName(DotContext.JS));
       Right:=Result;
       Result:=nil;
     finally
@@ -13057,8 +13094,13 @@ begin
     AssignSt.LHS:=ConvertElement(El.VariableName,AContext); // beware: might fail
 
     DotContext:=TDotContext.Create(El.StartExpr,nil,AContext);
-    GetCurrent:=CreatePropertyGet(CurrentProp,nil,DotContext,PosEl); // beware: might fail
-    FreeAndNil(DotContext);
+    try
+      GetCurrent:=CreatePropertyGet(CurrentProp,nil,DotContext,PosEl); // beware: might fail
+      if DotContext.JS<>nil then
+        RaiseNotSupported(El,AContext,20180509134302,GetObjName(DotContext.JS));
+    finally
+      FreeAndNil(DotContext);
+    end;
     AssignSt.Expr:=CreateDotExpression(PosEl,CreateInName,GetCurrent,true);
 
     // add body
@@ -16213,6 +16255,13 @@ begin
   Result.Value.IsUndefined:=true;
 end;
 
+function TPasToJSConverter.CreateLiteralCustomValue(El: TPasElement;
+  const s: TJSString): TJSLiteral;
+begin
+  Result:=TJSLiteral(CreateElement(TJSLiteral,El));
+  Result.Value.CustomValue:=s;
+end;
+
 function TPasToJSConverter.CreateSetLiteralElement(Expr: TPasExpr;
   AContext: TConvertContext): TJSElement;
 var
@@ -16533,7 +16582,7 @@ function TPasToJSConverter.CreateReferencePath(El: TPasElement;
 
   procedure Prepend(var aPath: string; Prefix: string);
   begin
-    if aPath<>'' then
+    if (aPath<>'') and (aPath[1]<>'[') then
       aPath:='.'+aPath;
     aPath:=Prefix+aPath;
   end;
@@ -16789,10 +16838,21 @@ begin
       ParentEl:=ParentEl.Parent;
       end;
     end;
-  if (Result<>'') and (Kind in [rpkPathWithDot,rpkPathAndName]) then
-    Result:=Result+'.';
-  if Kind=rpkPathAndName then
-    Result:=Result+TransformVariableName(El,AContext);
+
+  case Kind of
+  rpkPathWithDot:
+    if Result<>'' then Result:=Result+'.';
+  rpkPathAndName:
+    begin
+    ShortName:=TransformVariableName(El,AContext);
+    if Result='' then
+      Result:=ShortName
+    else if (ShortName<>'') and (ShortName[1] in ['[','(']) then
+      Result:=Result+ShortName
+    else
+      Result:=Result+'.'+ShortName;
+    end;
+  end;
 end;
 
 function TPasToJSConverter.CreateReferencePathExpr(El: TPasElement;

+ 31 - 23
packages/pastojs/tests/tcmodules.pas

@@ -10381,35 +10381,40 @@ begin
     '  TObject = class',
     '  public',
     '    Intern: longint external name ''$Intern'';',
+    '    Bracket: longint external name ''["A B"]'';',
     '  end;',
     '']),
     LinesToStr([
     '']));
 
   StartUnit(true);
-  Add('interface');
-  Add('uses unit2;');
-  Add('{$modeswitch externalclass}');
-  Add('type');
-  Add('  TCar = class(tobject)');
-  Add('  public');
-  Add('    Intern2: longint external name ''$Intern2'';');
-  Add('    procedure DoIt;');
-  Add('  end;');
-  Add('implementation');
-  Add('procedure tcar.doit;');
-  Add('begin');
-  Add('  Intern:=Intern+1;');
-  Add('  Intern2:=Intern2+2;');
-  Add('end;');
-  Add('var Obj: TCar;');
-  Add('begin');
-  Add('  obj.intern:=obj.intern+1;');
-  Add('  obj.intern2:=obj.intern2+2;');
-  Add('  with obj do begin');
-  Add('    intern:=intern+1;');
-  Add('    intern2:=intern2+2;');
-  Add('  end;');
+  Add([
+  'interface',
+  'uses unit2;',
+  '{$modeswitch externalclass}',
+  'type',
+  '  TCar = class(tobject)',
+  '  public',
+  '    Intern2: longint external name ''$Intern2'';',
+  '    procedure DoIt;',
+  '  end;',
+  'implementation',
+  'procedure tcar.doit;',
+  'begin',
+  '  Intern:=Intern+1;',
+  '  Intern2:=Intern2+2;',
+  '  Bracket:=Bracket+3;',
+  'end;',
+  'var Obj: TCar;',
+  'begin',
+  '  obj.intern:=obj.intern+1;',
+  '  obj.intern2:=obj.intern2+2;',
+  '  obj.Bracket:=obj.Bracket+3;',
+  '  with obj do begin',
+  '    intern:=intern+1;',
+  '    intern2:=intern2+2;',
+  '    Bracket:=Bracket+3;',
+  '  end;']);
   ConvertUnit;
   CheckSource('TestClass_ExternalVar',
     LinesToStr([
@@ -10418,15 +10423,18 @@ begin
     '    this.DoIt = function () {',
     '      this.$Intern = this.$Intern + 1;',
     '      this.$Intern2 = this.$Intern2 + 2;',
+    '      this["A B"] = this["A B"] + 3;',
     '    };',
     '  });',
     '']),
     LinesToStr([
     '$impl.Obj.$Intern = $impl.Obj.$Intern + 1;',
     '$impl.Obj.$Intern2 = $impl.Obj.$Intern2 + 2;',
+    '$impl.Obj["A B"] = $impl.Obj["A B"] + 3;',
     'var $with1 = $impl.Obj;',
     '$with1.$Intern = $with1.$Intern + 1;',
     '$with1.$Intern2 = $with1.$Intern2 + 2;',
+    '$with1["A B"] = $with1["A B"] + 3;',
     '']),
     LinesToStr([ // implementation
     '$impl.Obj = null;',

+ 9 - 3
utils/pas2js/docs/translation.html

@@ -2399,9 +2399,10 @@ function(){
 type
   TWrapper = class
   private
-    // let's assume this object has a $Handle and an $id
+    // let's assume this object has the properties "$Handle", "$id", and "0"
   public
     Id: NativeInt; external name '$Id';
+    x: NativeInt; external name '[0]';
     function GetState(typ: longint): NativeInt; external name '$Handle.GetState';
     procedure DoIt;
   end;
@@ -2413,7 +2414,8 @@ var
   W: TWrapper;
 Begin
   W.Id := 2;
-  W.GetState(3);
+  W.x := 3;
+  W.GetState(4);
 End.
 </pre>
           </td>
@@ -2430,7 +2432,8 @@ function(){
   this.W = null;
   $mod.$main = function(){
     $mod.W.$Id = 2;
-    $mod.W.$Handle.GetState(3);
+    $mod.W[0] = 3;
+    $mod.W.$Handle.GetState(4);
   };
 },
 []);
@@ -2439,6 +2442,9 @@ function(){
         </tr>
       </tbody>
     </table>
+    <ul>
+      <li>Non identifiers like "0" or "A B" must be enclosed in brackets.</li>
+    </ul>
     </div>
 
     <div class="section">