From 807fddf6582a095f623c397704141963376f5bac Mon Sep 17 00:00:00 2001 From: mattias Date: Tue, 29 Dec 2020 23:27:28 +0000 Subject: [PATCH] pastojs: await with one param must be async function --- .../packages/fcl-passrc/tests/tcresolver.pas | 4 +- compiler/packages/pastojs/src/fppas2js.pp | 81 ++++++++-- compiler/packages/pastojs/tests/tcmodules.pas | 148 ++++++++++++++---- compiler/utils/pas2js/docs/translation.html | 4 +- 4 files changed, 187 insertions(+), 50 deletions(-) diff --git a/compiler/packages/fcl-passrc/tests/tcresolver.pas b/compiler/packages/fcl-passrc/tests/tcresolver.pas index 362fd74..f4b23e6 100644 --- a/compiler/packages/fcl-passrc/tests/tcresolver.pas +++ b/compiler/packages/fcl-passrc/tests/tcresolver.pas @@ -3542,7 +3542,8 @@ begin ' s[9+1]:=''b'';', ' s[10]:='''''''';', ' s[11]:=^g;', - ' s[12]:=^H;']); + ' s[12]:=^H;', + '']); ParseProgram; end; @@ -3620,6 +3621,7 @@ begin ' m=low(char)+high(char);', ' n = string(''A'');', ' o = UnicodeString(''A'');', + //' p = ^C''bird'';', 'begin']); ParseProgram; CheckResolverUnexpectedHints; diff --git a/compiler/packages/pastojs/src/fppas2js.pp b/compiler/packages/pastojs/src/fppas2js.pp index 07632be..c29e04b 100644 --- a/compiler/packages/pastojs/src/fppas2js.pp +++ b/compiler/packages/pastojs/src/fppas2js.pp @@ -2398,6 +2398,7 @@ const TempRefSetPathName = 's'; TempRefParamName = 'a'; IdentChars = ['0'..'9', 'A'..'Z', 'a'..'z','_']; + AwaitSignature2 = 'function await(aType,TJSPromise):aType'; function CodePointToJSString(u: longword): TJSString; begin @@ -5955,12 +5956,10 @@ end; function TPas2JSResolver.BI_AWait_OnGetCallCompatibility( Proc: TResElDataBuiltInProc; Expr: TPasExpr; RaiseOnError: boolean): integer; -// await(T; p: TJSPromise): T; +// await(T; p: TJSPromise): T +// await(T; jsvalue): T +// await(AsyncFuncWithResultT): T // await(AsyncProc); -// await(Proc); -// await(const Expr: T): T -const - Signature2 = 'function await(aType,TJSPromise):aType'; var Params: TParamsExpr; Param: TPasExpr; @@ -5990,6 +5989,48 @@ begin // must be the only parameter Result:=CheckBuiltInMaxParamCount(Proc,Params,1,RaiseOnError); if Result=cIncompatible then exit; + + TypeEl:=ParamResolved.LoTypeEl; + if (ParamResolved.IdentEl is TPasResultElement) then + begin + // await(AsyncFuncCall) + if not TPasFunctionType(ParamResolved.IdentEl.Parent).IsAsync then + begin + {$IFDEF VerbosePas2JS} + writeln('TPas2JSResolver.BI_AWait_OnGetCallCompatibility ',GetResolverResultDbg(ParamResolved)); + {$ENDIF} + if RaiseOnError then + RaiseMsg(20201229232446,nXExpectedButYFound,sXExpectedButYFound,['async function',GetResolverResultDescription(ParamResolved)],Expr) + else + exit(cIncompatible); + end; + end + else if (ParamResolved.BaseType=btContext) + and (TypeEl is TPasProcedureType) then + begin + // await(AsyncFuncTypeVar) + if not TPasProcedureType(TypeEl).IsAsync then + begin + {$IFDEF VerbosePas2JS} + writeln('TPas2JSResolver.BI_AWait_OnGetCallCompatibility ',GetResolverResultDbg(ParamResolved)); + {$ENDIF} + if RaiseOnError then + RaiseMsg(20201229232541,nXExpectedButYFound,sXExpectedButYFound,['async function',GetResolverResultDescription(ParamResolved)],Expr) + else + exit(cIncompatible); + end; + end + else + begin + {$IFDEF VerbosePas2JS} + writeln('TPas2JSResolver.BI_AWait_OnGetCallCompatibility ',GetResolverResultDbg(ParamResolved)); + {$ENDIF} + if RaiseOnError then + RaiseMsg(20201229224920,nXExpectedButYFound,sXExpectedButYFound,['async function',GetResolverResultDescription(ParamResolved)],Expr) + else + exit(cIncompatible); + end; + end else if ParamResolved.BaseType=btProc then begin @@ -6027,7 +6068,7 @@ begin begin if RaiseOnError then RaiseMsg(20200520090749,nWrongNumberOfParametersForCallTo, - sWrongNumberOfParametersForCallTo,[Signature2],Params); + sWrongNumberOfParametersForCallTo,[AwaitSignature2],Params); exit(cIncompatible); end; @@ -6061,14 +6102,21 @@ begin exit(CheckRaiseTypeArgNo(20200520091707,2,Param,Param2Resolved, 'instance of TJSPromise',RaiseOnError)); - if (Param2Resolved.BaseType<>btContext) - or not (Param2Resolved.LoTypeEl is TPasClassType) - or not IsExternalClass_Name(TPasClassType(Param2Resolved.LoTypeEl),'Promise') then - exit(CheckRaiseTypeArgNo(20200520091707,2,Param,Param2Resolved, + if (Param2Resolved.BaseType=btContext) + and (Param2Resolved.LoTypeEl is TPasClassType) + and IsExternalClass_Name(TPasClassType(Param2Resolved.LoTypeEl),'Promise') then + // await(T,aPromise) + else if IsJSBaseType(Param2Resolved,pbtJSValue) then + // await(T,jsvalue) + else if (Param2Resolved.IdentEl is TPasArgument) + and (Param2Resolved.LoTypeEl=nil) then + // await(T,UntypedArg) + else + exit(CheckRaiseTypeArgNo(20200520091708,2,Param,Param2Resolved, 'TJSPromise',RaiseOnError)); end; - Result:=CheckBuiltInMaxParamCount(Proc,Params,2,RaiseOnError,Signature2); + Result:=CheckBuiltInMaxParamCount(Proc,Params,2,RaiseOnError,AwaitSignature2); end; end; @@ -6083,11 +6131,18 @@ begin Param:=Params.Params[0]; if length(Params.Params)=1 then begin - // await(expr) + // await(AsyncFuncCall) if CheckCallAsyncFuncResult(Param,ResolvedEl) then + begin // await(CallAsynFuncResultT): T + if (ResolvedEl.BaseType=btContext) + and (ResolvedEl.LoTypeEl is TPasClassType) + and IsExternalClass_Name(TPasClassType(ResolvedEl.LoTypeEl),'Promise') then + // async function returns a promise, await resolve all promises -> need final type as first param + RaiseMsg(20201229235932,nWrongNumberOfParametersForCallTo, + sWrongNumberOfParametersForCallTo,[AwaitSignature2],Param); exit; - // await(expr:T):T + end; end else begin diff --git a/compiler/packages/pastojs/tests/tcmodules.pas b/compiler/packages/pastojs/tests/tcmodules.pas index 5fb832d..faef15a 100644 --- a/compiler/packages/pastojs/tests/tcmodules.pas +++ b/compiler/packages/pastojs/tests/tcmodules.pas @@ -883,8 +883,11 @@ type Procedure TestAwait_NonPromiseWithTypeFail; Procedure TestAwait_AsyncCallTypeMismatch; Procedure TestAWait_OutsideAsyncFail; - Procedure TestAWait_Result; + Procedure TestAWait_IntegerFail; Procedure TestAWait_ExternalClassPromise; + Procedure TestAWait_JSValue; + Procedure TestAWait_Result; + Procedure TestAWait_ResultPromiseMissingTypeFail; Procedure TestAsync_AnonymousProc; Procedure TestAsync_ProcType; Procedure TestAsync_ProcTypeAsyncModMismatchFail; @@ -32619,48 +32622,21 @@ begin ConvertProgram; end; -procedure TTestModule.TestAWait_Result; +procedure TTestModule.TestAWait_IntegerFail; begin StartProgram(false); Add([ - '{$modeswitch externalclass}', - 'type', - ' TJSPromise = class external name ''Promise''', - ' end;', - 'function Crawl(d: double = 1.3): word; ', + 'function Run: word;', 'begin', 'end;', - 'function Run(d: double = 1.6): word; async;', + 'procedure Fly(w: word); async;', 'begin', - ' Result:=await(1);', - ' Result:=await(Crawl);', - ' Result:=await(Crawl(4.5));', - ' Result:=await(Run);', - ' Result:=await(Run(6.7));', + ' await(Run());', 'end;', 'begin', - ' Run(1);']); + ' Fly(1);']); + SetExpectedPasResolverError('async function expected, but Result:Word found',nXExpectedButYFound); ConvertProgram; - CheckSource('TestAWait_Result', - LinesToStr([ // statements - 'this.Crawl = function (d) {', - ' var Result = 0;', - ' return Result;', - '};', - 'this.Run = async function (d) {', - ' var Result = 0;', - ' Result = await 1;', - ' Result = await $mod.Crawl(1.3);', - ' Result = await $mod.Crawl(4.5);', - ' Result = await $mod.Run(1.6);', - ' Result = await $mod.Run(6.7);', - ' return Result;', - '};', - '']), - LinesToStr([ - '$mod.Run(1);' - ])); - SetExpectedPasResolverError('Await without promise',nAwaitWithoutPromise); end; procedure TTestModule.TestAWait_ExternalClassPromise; @@ -32723,6 +32699,110 @@ begin CheckResolverUnexpectedHints(); end; +procedure TTestModule.TestAWait_JSValue; +begin + StartProgram(false); + Add([ + '{$modeswitch externalclass}', + 'type', + ' TJSPromise = class external name ''Promise''', + ' end;', + 'function Fly(w: word): jsvalue; async;', + 'begin', + 'end;', + 'function Run(d: jsvalue; var e): word; async;', + 'begin', + ' Result:=await(word,d);', // promise needs type + ' d:=await(Fly(4));', // async non promise must omit the type + ' Result:=await(word,e);', // promise needs type + 'end;', + 'begin', + '']); + ConvertProgram; + CheckSource('TestAWait_JSValue', + LinesToStr([ // statements + 'this.Fly = async function (w) {', + ' var Result = undefined;', + ' return Result;', + '};', + 'this.Run = async function (d, e) {', + ' var Result = 0;', + ' Result = await d;', + ' d = await $mod.Fly(4);', + ' Result = await e.get();', + ' return Result;', + '};', + '']), + LinesToStr([ + ])); + CheckResolverUnexpectedHints(); +end; + +procedure TTestModule.TestAWait_Result; +begin + StartProgram(false); + Add([ + '{$modeswitch externalclass}', + 'type', + ' TJSPromise = class external name ''Promise''', + ' end;', + 'function Crawl(d: double = 1.3): TJSPromise; ', + 'begin', + 'end;', + 'function Run(d: double = 1.6): word; async;', + 'begin', + ' Result:=await(word,Crawl);', + ' Result:=await(word,Crawl(4.5));', + ' Result:=await(Run);', + ' Result:=await(Run(6.7));', + 'end;', + 'begin', + ' Run(1);']); + ConvertProgram; + CheckSource('TestAWait_Result', + LinesToStr([ // statements + 'this.Crawl = function (d) {', + ' var Result = null;', + ' return Result;', + '};', + 'this.Run = async function (d) {', + ' var Result = 0;', + ' Result = await $mod.Crawl(1.3);', + ' Result = await $mod.Crawl(4.5);', + ' Result = await $mod.Run(1.6);', + ' Result = await $mod.Run(6.7);', + ' return Result;', + '};', + '']), + LinesToStr([ + '$mod.Run(1);' + ])); + CheckResolverUnexpectedHints(); +end; + +procedure TTestModule.TestAWait_ResultPromiseMissingTypeFail; +begin + StartProgram(false); + Add([ + '{$mode objfpc}', + '{$modeswitch externalclass}', + 'type', + ' TJSPromise = class external name ''Promise''', + ' end;', + 'function Run: TJSPromise; async;', + 'begin', + 'end;', + 'procedure Fly(w: word); async;', + 'begin', + ' await(Run());', + 'end;', + 'begin', + ' Fly(1);']); + SetExpectedPasResolverError('Wrong number of parameters specified for call to "function await(aType,TJSPromise):aType"', + nWrongNumberOfParametersForCallTo); + ConvertProgram; +end; + procedure TTestModule.TestAsync_AnonymousProc; begin StartProgram(false); diff --git a/compiler/utils/pas2js/docs/translation.html b/compiler/utils/pas2js/docs/translation.html index 86cd339..23baf7f 100644 --- a/compiler/utils/pas2js/docs/translation.html +++ b/compiler/utils/pas2js/docs/translation.html @@ -3060,9 +3060,9 @@ End. Pas2js supports the JS operators async and await to simplify the use of Promise. The await operator corresponds to three intrinsic Pas2js functions: The await function can only be used inside a procedure with the async modifier.
Example for the explicit promise: