mirror of
https://gitlab.com/freepascal.org/fpc/pas2js.git
synced 2025-08-29 16:20:40 +02:00
pas2js: fixed try exit(value) finally read Result end
This commit is contained in:
parent
a9af2a4169
commit
66bec81336
@ -1176,6 +1176,7 @@ type
|
|||||||
TPRResolveVarAccesses = set of TResolvedRefAccess;
|
TPRResolveVarAccesses = set of TResolvedRefAccess;
|
||||||
|
|
||||||
const
|
const
|
||||||
|
rraAllRead = [rraRead,rraReadAndAssign,rraVarParam];
|
||||||
rraAllWrite = [rraAssign,rraReadAndAssign,rraVarParam,rraOutParam];
|
rraAllWrite = [rraAssign,rraReadAndAssign,rraVarParam,rraOutParam];
|
||||||
|
|
||||||
ResolvedToPSRefAccess: array[TResolvedRefAccess] of TPSRefAccess = (
|
ResolvedToPSRefAccess: array[TResolvedRefAccess] of TPSRefAccess = (
|
||||||
|
@ -1128,6 +1128,7 @@ type
|
|||||||
{ TPasClassOperator }
|
{ TPasClassOperator }
|
||||||
|
|
||||||
TPasClassOperator = class(TPasOperator)
|
TPasClassOperator = class(TPasOperator)
|
||||||
|
public
|
||||||
function TypeName: string; override;
|
function TypeName: string; override;
|
||||||
function GetProcTypeEnum: TProcType; override;
|
function GetProcTypeEnum: TProcType; override;
|
||||||
end;
|
end;
|
||||||
@ -1576,7 +1577,7 @@ type
|
|||||||
TPasImplTryExceptElse = class(TPasImplTryHandler)
|
TPasImplTryExceptElse = class(TPasImplTryHandler)
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TPasImplExceptOn }
|
{ TPasImplExceptOn - Parent is TPasImplTryExcept }
|
||||||
|
|
||||||
TPasImplExceptOn = class(TPasImplStatement)
|
TPasImplExceptOn = class(TPasImplStatement)
|
||||||
public
|
public
|
||||||
|
@ -1295,6 +1295,14 @@ type
|
|||||||
end;
|
end;
|
||||||
PHasAnoFuncData = ^THasAnoFuncData;
|
PHasAnoFuncData = ^THasAnoFuncData;
|
||||||
procedure OnHasAnonymousEl(El: TPasElement; arg: pointer);
|
procedure OnHasAnonymousEl(El: TPasElement; arg: pointer);
|
||||||
|
protected
|
||||||
|
type
|
||||||
|
THasElReadingDeclData = record
|
||||||
|
Decl: TPasElement;
|
||||||
|
El: TPasElement;
|
||||||
|
end;
|
||||||
|
PHasElReadingDeclData = ^THasElReadingDeclData;
|
||||||
|
procedure OnHasElReadingDecl(El: TPasElement; arg: pointer);
|
||||||
protected
|
protected
|
||||||
// overloads: fix name clashes in JS
|
// overloads: fix name clashes in JS
|
||||||
FOverloadScopes: TFPList; // list of TPasIdentifierScope
|
FOverloadScopes: TFPList; // list of TPasIdentifierScope
|
||||||
@ -1432,7 +1440,8 @@ type
|
|||||||
InResolved: TPasResolverResult; out ArgResolved, LengthResolved,
|
InResolved: TPasResolverResult; out ArgResolved, LengthResolved,
|
||||||
PropResultResolved: TPasResolverResult): boolean;
|
PropResultResolved: TPasResolverResult): boolean;
|
||||||
function IsHelperMethod(El: TPasElement): boolean; override;
|
function IsHelperMethod(El: TPasElement): boolean; override;
|
||||||
function IsHelperForMember(El: TPasElement): boolean;
|
function IsHelperForMember(El: TPasElement): boolean; virtual;
|
||||||
|
function ImplBlockReadsDecl(Block: TPasImplBlock; Decl: TPasElement): boolean; virtual;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
@ -2719,6 +2728,22 @@ begin
|
|||||||
Data^.Expr:=TProcedureExpr(El);
|
Data^.Expr:=TProcedureExpr(El);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TPas2JSResolver.OnHasElReadingDecl(El: TPasElement; arg: pointer);
|
||||||
|
var
|
||||||
|
Data: PHasElReadingDeclData absolute arg;
|
||||||
|
Ref: TResolvedReference;
|
||||||
|
begin
|
||||||
|
if Data^.El<>nil then exit;
|
||||||
|
if El.CustomData is TResolvedReference then
|
||||||
|
begin
|
||||||
|
Ref:=TResolvedReference(El.CustomData);
|
||||||
|
if (Ref.Declaration=Data^.Decl) and (Ref.Access in rraAllRead) then
|
||||||
|
begin
|
||||||
|
Data^.El:=El;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
function TPas2JSResolver.HasOverloadIndex(El: TPasElement;
|
function TPas2JSResolver.HasOverloadIndex(El: TPasElement;
|
||||||
WithElevatedLocal: boolean): boolean;
|
WithElevatedLocal: boolean): boolean;
|
||||||
var
|
var
|
||||||
@ -5742,6 +5767,17 @@ begin
|
|||||||
Result:=true;
|
Result:=true;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TPas2JSResolver.ImplBlockReadsDecl(Block: TPasImplBlock;
|
||||||
|
Decl: TPasElement): boolean;
|
||||||
|
var
|
||||||
|
Data: THasElReadingDeclData;
|
||||||
|
begin
|
||||||
|
Data.Decl:=Decl;
|
||||||
|
Data.El:=nil;
|
||||||
|
Block.ForEachCall(@OnHasElReadingDecl,@Data);
|
||||||
|
Result:=Data.El<>nil;
|
||||||
|
end;
|
||||||
|
|
||||||
{ TParamContext }
|
{ TParamContext }
|
||||||
|
|
||||||
constructor TParamContext.Create(PasEl: TPasElement; JSEl: TJSElement;
|
constructor TParamContext.Create(PasEl: TPasElement; JSEl: TJSElement;
|
||||||
@ -10765,39 +10801,91 @@ function TPasToJSConverter.ConvertBuiltIn_Exit(El: TPasExpr;
|
|||||||
// convert "exit;" -> in a function: "return result;" in a procedure: "return;"
|
// convert "exit;" -> in a function: "return result;" in a procedure: "return;"
|
||||||
// convert "exit(param);" -> "return param;"
|
// convert "exit(param);" -> "return param;"
|
||||||
var
|
var
|
||||||
ProcEl: TPasElement;
|
ParentEl: TPasElement;
|
||||||
Scope: TPas2JSProcedureScope;
|
ImplProcScope: TPas2JSProcedureScope;
|
||||||
VarName: String;
|
ResultVarName: String;
|
||||||
FuncContext: TFunctionContext;
|
FuncContext: TFunctionContext;
|
||||||
AssignSt: TJSSimpleAssignStatement;
|
AssignSt: TJSSimpleAssignStatement;
|
||||||
St: TJSStatementList;
|
St: TJSStatementList;
|
||||||
Proc: TPasProcedure;
|
ImplProc, DeclProc: TPasProcedure;
|
||||||
|
ImplTry: TPasImplTry;
|
||||||
|
ResultIsRead: Boolean;
|
||||||
|
ResultEl: TPasResultElement;
|
||||||
begin
|
begin
|
||||||
{$IFDEF VerbosePas2JS}
|
{$IFDEF VerbosePas2JS}
|
||||||
writeln('TPasToJSConverter.ConvertBuiltIn_Exit ',GetObjName(El));
|
writeln('TPasToJSConverter.ConvertBuiltIn_Exit ',GetObjName(El));
|
||||||
{$ENDIF}
|
{$ENDIF}
|
||||||
ProcEl:=El.Parent;
|
ParentEl:=El.Parent;
|
||||||
while (ProcEl<>nil) and not (ProcEl is TPasProcedure) do
|
while (ParentEl<>nil) and not (ParentEl is TPasProcedure) do
|
||||||
ProcEl:=ProcEl.Parent;
|
ParentEl:=ParentEl.Parent;
|
||||||
// ProcEl can be nil, when exit is in program begin block
|
// ParentEl can be nil, when exit is in program begin block
|
||||||
Proc:=TPasProcedure(ProcEl);
|
ImplProc:=TPasProcedure(ParentEl);
|
||||||
|
ResultVarName:='';
|
||||||
|
if ImplProc<>nil then
|
||||||
|
begin
|
||||||
|
ImplProcScope:=ImplProc.CustomData as TPas2JSProcedureScope;
|
||||||
|
if ImplProc.ProcType is TPasFunctionType then
|
||||||
|
begin
|
||||||
|
ResultVarName:=ImplProcScope.ResultVarName; // ResultVarName needs ImplProc
|
||||||
|
if ResultVarName='' then
|
||||||
|
ResultVarName:=ResolverResultVar;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
Result:=TJSReturnStatement(CreateElement(TJSReturnStatement,El));
|
Result:=TJSReturnStatement(CreateElement(TJSReturnStatement,El));
|
||||||
if (El is TParamsExpr) and (length(TParamsExpr(El).Params)>0) then
|
if (El is TParamsExpr) and (length(TParamsExpr(El).Params)>0) then
|
||||||
begin
|
begin
|
||||||
// with parameter. convert "exit(param);" -> "return param;"
|
// with parameter, e.g. "exit(param);"
|
||||||
TJSReturnStatement(Result).Expr:=ConvertExpression(TParamsExpr(El).Params[0],AContext);
|
ResultIsRead:=false;
|
||||||
|
if (ResultVarName<>'') then
|
||||||
|
begin
|
||||||
|
DeclProc:=ImplProcScope.DeclarationProc;
|
||||||
|
if DeclProc=nil then
|
||||||
|
DeclProc:=ImplProc; // Note: references refer to ResultEl of DeclProc
|
||||||
|
ResultEl:=TPasFunctionType(DeclProc.ProcType).ResultEl;
|
||||||
|
ParentEl:=El.Parent;
|
||||||
|
while (ParentEl<>ImplProc) do
|
||||||
|
begin
|
||||||
|
if ParentEl is TPasImplTry then
|
||||||
|
begin
|
||||||
|
ImplTry:=TPasImplTry(ParentEl);
|
||||||
|
if ImplTry.FinallyExcept is TPasImplTryFinally then
|
||||||
|
begin
|
||||||
|
if AContext.Resolver.ImplBlockReadsDecl(ImplTry.FinallyExcept,ResultEl) then
|
||||||
|
begin
|
||||||
|
ResultIsRead:=true;
|
||||||
|
break;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
ParentEl:=ParentEl.Parent;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if ResultIsRead then
|
||||||
|
begin
|
||||||
|
// create "Result = param; return Result;"
|
||||||
|
AssignSt:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,El));
|
||||||
|
AssignSt.LHS:=CreatePrimitiveDotExpr(ResultVarName,El);
|
||||||
|
AssignSt.Expr:=ConvertExpression(TParamsExpr(El).Params[0],AContext);
|
||||||
|
TJSReturnStatement(Result).Expr:=CreatePrimitiveDotExpr(ResultVarName,El);
|
||||||
|
St:=TJSStatementList(CreateElement(TJSStatementList,El));
|
||||||
|
St.A:=AssignSt;
|
||||||
|
St.B:=Result;
|
||||||
|
Result:=St;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
// create "return param;"
|
||||||
|
TJSReturnStatement(Result).Expr:=ConvertExpression(TParamsExpr(El).Params[0],AContext);
|
||||||
|
end;
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
// without parameter
|
// without parameter
|
||||||
if (Proc<>nil) and (Proc.ProcType is TPasFunctionType) then
|
if (ResultVarName<>'') then
|
||||||
begin
|
begin
|
||||||
// in a function, "return result;"
|
// in a function, "return Result;"
|
||||||
Scope:=Proc.CustomData as TPas2JSProcedureScope;
|
TJSReturnStatement(Result).Expr:=CreatePrimitiveDotExpr(ResultVarName,El);
|
||||||
VarName:=Scope.ResultVarName;
|
|
||||||
if VarName='' then
|
|
||||||
VarName:=ResolverResultVar;
|
|
||||||
TJSReturnStatement(Result).Expr:=CreatePrimitiveDotExpr(VarName,El);
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
; // in a procedure, "return;" which means "return undefined;"
|
; // in a procedure, "return;" which means "return undefined;"
|
||||||
|
@ -317,6 +317,7 @@ type
|
|||||||
Procedure TestFunctionResultInForLoop;
|
Procedure TestFunctionResultInForLoop;
|
||||||
Procedure TestFunctionResultInTypeCast;
|
Procedure TestFunctionResultInTypeCast;
|
||||||
Procedure TestExit;
|
Procedure TestExit;
|
||||||
|
Procedure TestExit_ResultInFinally;
|
||||||
Procedure TestBreak;
|
Procedure TestBreak;
|
||||||
Procedure TestBreakAsVar;
|
Procedure TestBreakAsVar;
|
||||||
Procedure TestContinue;
|
Procedure TestContinue;
|
||||||
@ -3634,6 +3635,77 @@ begin
|
|||||||
'']));
|
'']));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TTestModule.TestExit_ResultInFinally;
|
||||||
|
begin
|
||||||
|
StartProgram(false);
|
||||||
|
Add([
|
||||||
|
'function Run: word;',
|
||||||
|
'begin',
|
||||||
|
' try',
|
||||||
|
' exit(3);', // no Result in finally -> use return 3
|
||||||
|
' finally',
|
||||||
|
' end;',
|
||||||
|
'end;',
|
||||||
|
'function Fly: word;',
|
||||||
|
'begin',
|
||||||
|
' try',
|
||||||
|
' exit(3);',
|
||||||
|
' finally',
|
||||||
|
' if Result>0 then ;',
|
||||||
|
' end;',
|
||||||
|
'end;',
|
||||||
|
'function Jump: word;',
|
||||||
|
'begin',
|
||||||
|
' try',
|
||||||
|
' try',
|
||||||
|
' exit(4);',
|
||||||
|
' finally',
|
||||||
|
' end;',
|
||||||
|
' finally',
|
||||||
|
' if Result>0 then ;',
|
||||||
|
' end;',
|
||||||
|
'end;',
|
||||||
|
'begin',
|
||||||
|
'']);
|
||||||
|
ConvertProgram;
|
||||||
|
CheckSource('TestExit_ResultInFinally',
|
||||||
|
LinesToStr([ // statements
|
||||||
|
'this.Run = function () {',
|
||||||
|
' var Result = 0;',
|
||||||
|
' try {',
|
||||||
|
' return 3;',
|
||||||
|
' } finally {',
|
||||||
|
' };',
|
||||||
|
' return Result;',
|
||||||
|
'};',
|
||||||
|
'this.Fly = function () {',
|
||||||
|
' var Result = 0;',
|
||||||
|
' try {',
|
||||||
|
' Result = 3;',
|
||||||
|
' return Result;',
|
||||||
|
' } finally {',
|
||||||
|
' if (Result > 0) ;',
|
||||||
|
' };',
|
||||||
|
' return Result;',
|
||||||
|
'};',
|
||||||
|
'this.Jump = function () {',
|
||||||
|
' var Result = 0;',
|
||||||
|
' try {',
|
||||||
|
' try {',
|
||||||
|
' Result = 4;',
|
||||||
|
' return Result;',
|
||||||
|
' } finally {',
|
||||||
|
' };',
|
||||||
|
' } finally {',
|
||||||
|
' if (Result > 0) ;',
|
||||||
|
' };',
|
||||||
|
' return Result;',
|
||||||
|
'};',
|
||||||
|
'']),
|
||||||
|
LinesToStr([
|
||||||
|
'']));
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TTestModule.TestBreak;
|
procedure TTestModule.TestBreak;
|
||||||
begin
|
begin
|
||||||
StartProgram(false);
|
StartProgram(false);
|
||||||
|
Loading…
Reference in New Issue
Block a user