pastojs: constructor of external class: funcname and {}

git-svn-id: trunk@43166 -
This commit is contained in:
Mattias Gaertner 2019-10-11 10:25:24 +00:00
parent 1bed61195e
commit 8d60f4542c
3 changed files with 187 additions and 82 deletions

View File

@ -228,6 +228,8 @@ Works:
- destructor forbidden - destructor forbidden
- constructor must not be virtual - constructor must not be virtual
- constructor 'new' -> new extclass(params) - constructor 'new' -> new extclass(params)
- constructor Name -> new extclass.name(params)
- constructor Name external name '{}' -> {}
- identifiers are renamed to avoid clashes with external names - identifiers are renamed to avoid clashes with external names
- call inherited - call inherited
- Pascal descendant can override newinstance - Pascal descendant can override newinstance
@ -508,7 +510,7 @@ const
nVirtualMethodNameMustMatchExternal = 4013; nVirtualMethodNameMustMatchExternal = 4013;
nPublishedNameMustMatchExternal = 4014; nPublishedNameMustMatchExternal = 4014;
nInvalidVariableModifier = 4015; nInvalidVariableModifier = 4015;
nExternalObjectConstructorMustBeNamedNew = 4016; // was nExternalObjectConstructorMustBeNamedNew = 4016;
nNewInstanceFunctionMustBeVirtual = 4017; nNewInstanceFunctionMustBeVirtual = 4017;
nNewInstanceFunctionMustHaveTwoParameters = 4018; nNewInstanceFunctionMustHaveTwoParameters = 4018;
nNewInstanceFunctionMustNotHaveOverloadAtX = 4019; nNewInstanceFunctionMustNotHaveOverloadAtX = 4019;
@ -540,7 +542,7 @@ resourcestring
sVirtualMethodNameMustMatchExternal = 'Virtual method name must match external'; sVirtualMethodNameMustMatchExternal = 'Virtual method name must match external';
sInvalidVariableModifier = 'Invalid variable modifier "%s"'; sInvalidVariableModifier = 'Invalid variable modifier "%s"';
sPublishedNameMustMatchExternal = 'Published name must match external'; sPublishedNameMustMatchExternal = 'Published name must match external';
sExternalObjectConstructorMustBeNamedNew = 'external object constructor must be named "new"'; // was sExternalObjectConstructorMustBeNamedNew = 'external object constructor must be named "new"';
sNewInstanceFunctionMustBeVirtual = 'NewInstance function must be virtual'; sNewInstanceFunctionMustBeVirtual = 'NewInstance function must be virtual';
sNewInstanceFunctionMustHaveTwoParameters = 'NewInstance function must have two parameters'; sNewInstanceFunctionMustHaveTwoParameters = 'NewInstance function must have two parameters';
sNewInstanceFunctionMustNotHaveOverloadAtX = 'NewInstance function must not have overload at %s'; sNewInstanceFunctionMustNotHaveOverloadAtX = 'NewInstance function must not have overload at %s';
@ -4073,16 +4075,13 @@ begin
// constructor of external class can't be overriden -> forbid virtual // constructor of external class can't be overriden -> forbid virtual
RaiseMsg(20170323100447,nInvalidXModifierY,sInvalidXModifierY, RaiseMsg(20170323100447,nInvalidXModifierY,sInvalidXModifierY,
[Proc.ElementTypeName,'virtual,external'],Proc); [Proc.ElementTypeName,'virtual,external'],Proc);
ExtName:=ComputeConstString(Proc.LibrarySymbolName,true,true);
if CompareText(Proc.Name,'new')=0 then if CompareText(Proc.Name,'new')=0 then
begin begin
ExtName:=ComputeConstString(Proc.LibrarySymbolName,true,true);
if ExtName<>Proc.Name then if ExtName<>Proc.Name then
RaiseMsg(20170323083511,nVirtualMethodNameMustMatchExternal, RaiseMsg(20170323083511,nVirtualMethodNameMustMatchExternal,
sVirtualMethodNameMustMatchExternal,[],Proc.LibrarySymbolName); sVirtualMethodNameMustMatchExternal,[],Proc.LibrarySymbolName);
end end;
else
RaiseMsg(20190116211019,nExternalObjectConstructorMustBeNamedNew,
sExternalObjectConstructorMustBeNamedNew,[],El);
end end
else else
RaiseMsg(20170322163210,nPasElementNotSupported,sPasElementNotSupported, RaiseMsg(20170322163210,nPasElementNotSupported,sPasElementNotSupported,
@ -10371,68 +10370,83 @@ var
OldAccess: TCtxAccess; OldAccess: TCtxAccess;
ExtNameEl: TJSElement; ExtNameEl: TJSElement;
WithData: TPas2JSWithExprScope; WithData: TPas2JSWithExprScope;
PosEl: TPasElement;
aResolver: TPas2JSResolver;
begin begin
Result:=nil; Result:=nil;
aResolver:=AContext.Resolver;
NewExpr:=nil; NewExpr:=nil;
ExtNameEl:=nil; ExtNameEl:=nil;
try try
Proc:=Ref.Declaration as TPasConstructor; Proc:=Ref.Declaration as TPasConstructor;
ExtNameEl:=nil; PosEl:=Ref.Element;
if Left<>nil then
begin
if AContext.Resolver<>nil then
begin
AContext.Resolver.ComputeElement(Left,LeftResolved,[]);
if LeftResolved.BaseType=btModule then
begin
// e.g. Unit.TExtA
// ExtName is global -> omit unit
Left:=nil;
end
else ;
end;
if Left<>nil then
begin
// convert left side
OldAccess:=AContext.Access;
AContext.Access:=caRead;
ExtNameEl:=ConvertExpression(Left,AContext);
AContext.Access:=OldAccess;
end;
end;
if ExtNameEl=nil then
begin
if Ref.WithExprScope<>nil then
begin
// using local WITH var
WithData:=Ref.WithExprScope as TPas2JSWithExprScope;
ExtName:=WithData.WithVarName;
if ExtName='' then
RaiseNotSupported(ParamsExpr,AContext,20190209092049);
end
else
// use external class name
ExtName:=(Proc.Parent as TPasClassType).ExternalName;
if ExtName='' then
DoError(20180511163944,nJSNewNotSupported,sJSNewNotSupported,[],ParamsExpr);
ExtNameEl:=CreatePrimitiveDotExpr(ExtName,Ref.Element);
end;
if CompareText(Proc.Name,'new')=0 then if CompareText(Proc.Name,'new')=0 then
begin begin
// create "new ExtName(params)" if Left<>nil then
NewExpr:=TJSNewMemberExpression(CreateElement(TJSNewMemberExpression,Ref.Element)); begin
NewExpr.MExpr:=ExtNameEl; if aResolver<>nil then
NewExpr.Args:=TJSArguments(CreateElement(TJSArguments,Ref.Element)); begin
ExtNameEl:=nil; aResolver.ComputeElement(Left,LeftResolved,[]);
if ParamsExpr<>nil then if LeftResolved.BaseType=btModule then
CreateProcedureCallArgs(NewExpr.Args.Elements,ParamsExpr,Proc.ProcType,AContext); begin
Result:=NewExpr; // e.g. Unit.TExtA
NewExpr:=nil; // ExtName is global -> omit unit
Left:=nil;
end
else ;
end;
if Left<>nil then
begin
// convert left side
OldAccess:=AContext.Access;
AContext.Access:=caRead;
ExtNameEl:=ConvertExpression(Left,AContext);
AContext.Access:=OldAccess;
end;
end;
if ExtNameEl=nil then
begin
if Ref.WithExprScope<>nil then
begin
// using local WITH var
WithData:=Ref.WithExprScope as TPas2JSWithExprScope;
ExtName:=WithData.WithVarName;
if ExtName='' then
RaiseNotSupported(ParamsExpr,AContext,20190209092049);
end
else
// use external class name
ExtName:=(Proc.Parent as TPasClassType).ExternalName;
if ExtName='' then
DoError(20180511163944,nJSNewNotSupported,sJSNewNotSupported,[],ParamsExpr);
ExtNameEl:=CreatePrimitiveDotExpr(ExtName,PosEl);
end;
end end
else else
RaiseNotSupported(Ref.Element,AContext,20190116210204); begin
// external constructor ProcName
ExtName:='';
if aResolver<>nil then
ExtName:=aResolver.ComputeConstString(Proc.LibrarySymbolName,true,true);
if ExtName='{}' then
begin
// external constructor {} -> "{}"
Result:=TJSObjectLiteral(CreateElement(TJSObjectLiteral,PosEl));
exit;
end;
// external constructor ProcName -> "new ExtA.ProcName()"
ExtNameEl:=CreateReferencePathExpr(Proc,AContext,true);
end;
NewExpr:=TJSNewMemberExpression(CreateElement(TJSNewMemberExpression,PosEl));
NewExpr.MExpr:=ExtNameEl;
ExtNameEl:=nil;
NewExpr.Args:=TJSArguments(CreateElement(TJSArguments,PosEl));
if ParamsExpr<>nil then
CreateProcedureCallArgs(NewExpr.Args.Elements,ParamsExpr,Proc.ProcType,AContext);
Result:=NewExpr;
NewExpr:=nil;
finally finally
ExtNameEl.Free; ExtNameEl.Free;
NewExpr.Free; NewExpr.Free;

View File

@ -588,6 +588,8 @@ type
Procedure TestExternalClass_FuncClassOf_New; Procedure TestExternalClass_FuncClassOf_New;
Procedure TestExternalClass_New_PasClassFail; Procedure TestExternalClass_New_PasClassFail;
Procedure TestExternalClass_New_PasClassBracketsFail; Procedure TestExternalClass_New_PasClassBracketsFail;
Procedure TestExternalClass_Constructor;
Procedure TestExternalClass_ConstructorBrackets;
Procedure TestExternalClass_LocalConstSameName; Procedure TestExternalClass_LocalConstSameName;
Procedure TestExternalClass_ReintroduceOverload; Procedure TestExternalClass_ReintroduceOverload;
Procedure TestExternalClass_Inherited; Procedure TestExternalClass_Inherited;
@ -16369,27 +16371,29 @@ end;
procedure TTestModule.TestExternalClass_New; procedure TTestModule.TestExternalClass_New;
begin begin
StartProgram(false); StartProgram(false);
Add('{$modeswitch externalclass}'); Add([
Add('type'); '{$modeswitch externalclass}',
Add(' TExtA = class external name ''ExtA'''); 'type',
Add(' constructor New;'); ' TExtA = class external name ''ExtA''',
Add(' constructor New(i: longint; j: longint = 2);'); ' constructor New;',
Add(' end;'); ' constructor New(i: longint; j: longint = 2);',
Add('var'); ' end;',
Add(' A: texta;'); 'var',
Add('begin'); ' A: texta;',
Add(' a:=texta.new;'); 'begin',
Add(' a:=texta(texta.new);'); ' a:=texta.new;',
Add(' a:=texta.new();'); ' a:=texta(texta.new);',
Add(' a:=texta.new(1);'); ' a:=texta.new();',
Add(' with texta do begin'); ' a:=texta.new(1);',
Add(' a:=new;'); ' with texta do begin',
Add(' a:=new();'); ' a:=new;',
Add(' a:=new(2);'); ' a:=new();',
Add(' end;'); ' a:=new(2);',
Add(' a:=test1.texta.new;'); ' end;',
Add(' a:=test1.texta.new();'); ' a:=test1.texta.new;',
Add(' a:=test1.texta.new(3);'); ' a:=test1.texta.new();',
' a:=test1.texta.new(3);',
'']);
ConvertProgram; ConvertProgram;
CheckSource('TestExternalClass_New', CheckSource('TestExternalClass_New',
LinesToStr([ // statements LinesToStr([ // statements
@ -16532,6 +16536,89 @@ begin
ConvertProgram; ConvertProgram;
end; end;
procedure TTestModule.TestExternalClass_Constructor;
begin
StartProgram(false);
Add([
'{$modeswitch externalclass}',
'type',
' TExtA = class external name ''ExtA''',
' constructor Create;',
' constructor Create(i: longint; j: longint = 2);',
' end;',
'var',
' A: texta;',
'begin',
' a:=texta.create;',
' a:=texta(texta.create);',
' a:=texta.create();',
' a:=texta.create(1);',
' with texta do begin',
' a:=create;',
' a:=create();',
' a:=create(2);',
' end;',
' a:=test1.texta.create;',
' a:=test1.texta.create();',
' a:=test1.texta.create(3);',
'']);
ConvertProgram;
CheckSource('TestExternalClass_Constructor',
LinesToStr([ // statements
'this.A = null;',
'']),
LinesToStr([ // $mod.$main
'$mod.A = new ExtA.Create();',
'$mod.A = new ExtA.Create();',
'$mod.A = new ExtA.Create();',
'$mod.A = new ExtA.Create(1,2);',
'$mod.A = new ExtA.Create();',
'$mod.A = new ExtA.Create();',
'$mod.A = new ExtA.Create(2,2);',
'$mod.A = new ExtA.Create();',
'$mod.A = new ExtA.Create();',
'$mod.A = new ExtA.Create(3,2);',
'']));
end;
procedure TTestModule.TestExternalClass_ConstructorBrackets;
begin
StartProgram(false);
Add([
'{$modeswitch externalclass}',
'type',
' TExtA = class external name ''ExtA''',
' constructor Create; external name ''{}'';',
' end;',
'var',
' A: texta;',
'begin',
' a:=texta.create;',
' a:=texta(texta.create);',
' a:=texta.create();',
' with texta do begin',
' a:=create;',
' a:=create();',
' end;',
' a:=test1.texta.create;',
' a:=test1.texta.create();',
'']);
ConvertProgram;
CheckSource('TestExternalClass_ConstructorBrackets',
LinesToStr([ // statements
'this.A = null;',
'']),
LinesToStr([ // $mod.$main
'$mod.A = {};',
'$mod.A = {};',
'$mod.A = {};',
'$mod.A = {};',
'$mod.A = {};',
'$mod.A = {};',
'$mod.A = {};',
'']));
end;
procedure TTestModule.TestExternalClass_LocalConstSameName; procedure TTestModule.TestExternalClass_LocalConstSameName;
begin begin
StartProgram(false); StartProgram(false);

View File

@ -2691,10 +2691,14 @@ function(){
An external class is not a TObject and has none of its methods.<br> An external class is not a TObject and has none of its methods.<br>
All members are external. If you omit the <i>external</i> modifier the All members are external. If you omit the <i>external</i> modifier the
external name is the member name. Keep in mind that JS is case sensitive.<br> external name is the member name. Keep in mind that JS is case sensitive.<br>
Destructors are not allowed.<br>
Constructors are only allowed with the name <i>New</i> and a call
translates to <i>new ExtClass(params)</i>.
Properties work the same as with Pascal classes, i.e. are replaced by Getter/Setter.<br> Properties work the same as with Pascal classes, i.e. are replaced by Getter/Setter.<br>
Destructors are not allowed.<br>
Constructors are supported in three ways:
<ul>
<li>With name <i>New</i> it is translated to <i>new ExtClass(params)</i>.</li>
<li>With external name <i>'{}'</i> it is translated to <i>{}</i>.</li>
<li>Otherwise it is translated to <i>new ExtClass.FuncName(params)</i>.</li>
</ul>
<table class="sample"> <table class="sample">
<tbody> <tbody>