Browse Source

pastojs: await with one param must be async function

git-svn-id: trunk@47895 -
(cherry picked from commit 8eafcd94903fe7432fe15d90fe3533e88c155a18)
Mattias Gaertner 4 years ago
parent
commit
de6a8a1c9b

+ 3 - 1
packages/fcl-passrc/tests/tcresolver.pas

@@ -3544,7 +3544,8 @@ begin
   '  s[9+1]:=''b'';',
   '  s[10]:='''''''';',
   '  s[11]:=^g;',
-  '  s[12]:=^H;']);
+  '  s[12]:=^H;',
+  '']);
   ParseProgram;
 end;
 
@@ -3622,6 +3623,7 @@ begin
   '  m=low(char)+high(char);',
   '  n = string(''A'');',
   '  o = UnicodeString(''A'');',
+  //'  p = ^C''bird'';',
   'begin']);
   ParseProgram;
   CheckResolverUnexpectedHints;

+ 68 - 13
packages/pastojs/src/fppas2js.pp

@@ -2399,6 +2399,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
@@ -5956,12 +5957,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;
@@ -5991,6 +5990,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
@@ -6028,7 +6069,7 @@ begin
       begin
       if RaiseOnError then
         RaiseMsg(20200520090749,nWrongNumberOfParametersForCallTo,
-          sWrongNumberOfParametersForCallTo,[Signature2],Params);
+          sWrongNumberOfParametersForCallTo,[AwaitSignature2],Params);
       exit(cIncompatible);
       end;
 
@@ -6062,14 +6103,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;
 
@@ -6084,11 +6132,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

+ 114 - 34
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);

+ 2 - 2
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:
     <ul>
-    <li><i>function await(AsyncFunctionWithResultT): T;</i>  // implicit promise</li>
+    <li><i>function await(AsyncFunctionWithResultT()): T;</i>  // implicit promise, the inner () can be omitted</li>
     <li><i>function await(aType; p: TJSPromise): aType;</i>  // explicit promise requires the resolved type</li>
-    <li><i>function await(const Expr: T): T;</i>  // implicit promise</li>
+    <li><i>function await(aType; j: jsvalue): aType;</i>  // explicit promise requires the resolved type</li>
     </ul>
     The await function can only be used inside a procedure with the async modifier.<br>
     Example for the explicit promise: