pastojs: external class field with brackets

git-svn-id: trunk@38958 -
This commit is contained in:
Mattias Gaertner 2018-05-09 12:47:17 +00:00
parent e283024ad7
commit cc7983d781
3 changed files with 112 additions and 38 deletions

View File

@ -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;

View File

@ -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;',

View File

@ -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">