diff options
author | marco <marco@3ad0048d-3df7-0310-abae-a5850022a9f2> | 2017-04-27 20:59:57 +0000 |
---|---|---|
committer | marco <marco@3ad0048d-3df7-0310-abae-a5850022a9f2> | 2017-04-27 20:59:57 +0000 |
commit | 87f9b1c605f844e48d17c09fc9757cb569de3c70 (patch) | |
tree | a5afa0156ae948faace4ae68cebc422aa0cdaedb | |
parent | 2b2ff872745dd40adc046e428e1887f40584a13d (diff) | |
download | fpc-87f9b1c605f844e48d17c09fc9757cb569de3c70.tar.gz |
merged revs: 35782,35790,35791,35793,35794,35795,35798,35799,35800,35801,35802,35803,35804,35805,35806,35808,35809,35810,35811
git-svn-id: http://svn.freepascal.org/svn/fpc/branches/fixes_3_0@35987 3ad0048d-3df7-0310-abae-a5850022a9f2
-rw-r--r-- | packages/fcl-passrc/src/pasresolver.pp | 605 | ||||
-rw-r--r-- | packages/fcl-passrc/src/pastree.pp | 49 | ||||
-rw-r--r-- | packages/fcl-passrc/src/pasuseanalyzer.pas | 145 | ||||
-rw-r--r-- | packages/fcl-passrc/src/pparser.pp | 120 | ||||
-rw-r--r-- | packages/fcl-passrc/tests/tcbaseparser.pas | 22 | ||||
-rw-r--r-- | packages/fcl-passrc/tests/tcprocfunc.pas | 241 | ||||
-rw-r--r-- | packages/fcl-passrc/tests/tcresolver.pas | 529 | ||||
-rw-r--r-- | packages/fcl-passrc/tests/tcuseanalyzer.pas | 304 | ||||
-rw-r--r-- | packages/pastojs/fpmake.pp | 2 | ||||
-rw-r--r-- | packages/pastojs/src/fppas2js.pp | 2702 | ||||
-rw-r--r-- | packages/pastojs/tests/tcmodules.pas | 2130 | ||||
-rw-r--r-- | packages/pastojs/tests/tcoptimizations.pas | 104 | ||||
-rw-r--r-- | utils/fpdoc/dw_xml.pp | 2 |
13 files changed, 5661 insertions, 1294 deletions
diff --git a/packages/fcl-passrc/src/pasresolver.pp b/packages/fcl-passrc/src/pasresolver.pp index f9c24ca0cd..47b810f0fa 100644 --- a/packages/fcl-passrc/src/pasresolver.pp +++ b/packages/fcl-passrc/src/pasresolver.pp @@ -131,6 +131,8 @@ Works: - built-in functions pred, succ for range type and enums - untyped parameters - built-in procedure str(const boolean|integer|enumvalue|classinstance,var s: string) +- pointer TPasPointerType + - nil, assigned(), typecast, class, classref, dynarray, procvar ToDo: - fix slow lookup declaration proc in PParser @@ -141,7 +143,6 @@ ToDo: - nested types - check if constant is longint or int64 - for..in..do -- pointer TPasPointerType - records - TPasRecordType, - const TRecordValues - function default(record type): record @@ -229,7 +230,7 @@ const nTypesAreNotRelated = 3029; nAbstractMethodsCannotBeCalledDirectly = 3030; nMissingParameterX = 3031; - nCannotAccessThisMemberFromAClassReference = 3032; + nCannotAccessThisMemberFromAX = 3032; nInOperatorExpectsSetElementButGot = 3033; nWrongNumberOfParametersForTypeCast = 3034; nIllegalTypeConversionTo = 3035; @@ -252,6 +253,8 @@ const nXModifierMismatchY = 3052; nSymbolCannotBePublished = 3053; nCannotTypecastAType = 3054; + nTypeIdentifierExpected = 3055; + nCannotNestAnonymousX = 3056; // resourcestring patterns of messages resourcestring @@ -286,7 +289,7 @@ resourcestring sTypesAreNotRelated = 'Types are not related'; sAbstractMethodsCannotBeCalledDirectly = 'Abstract methods cannot be called directly'; sMissingParameterX = 'Missing parameter %s'; - sCannotAccessThisMemberFromAClassReference = 'Cannot access this member from a class reference'; + sCannotAccessThisMemberFromAX = 'Cannot access this member from a %s'; sInOperatorExpectsSetElementButGot = 'the in-operator expects a set element, but got %s'; sWrongNumberOfParametersForTypeCast = 'wrong number of parameters for type cast to %s'; sIllegalTypeConversionTo = 'Illegal type conversion: "%s" to "%s"'; @@ -307,8 +310,10 @@ resourcestring sVirtualMethodXHasLowerVisibility = 'Virtual method "%s" has a lower visibility (%s) than parent class %s (%s)'; sExternalClassInstanceCannotAccessStaticX = 'External class instance cannot access static %s'; sXModifierMismatchY = '%s modifier "%s" mismatch'; - sSymbolCannotBePublished = 'Symbol cannot be published. Only methods and properties.'; + sSymbolCannotBePublished = 'Symbol cannot be published'; sCannotTypecastAType = 'Cannot type cast a type'; + sTypeIdentifierExpected = 'Type identifier expected'; + sCannotNestAnonymousX = 'Cannot nest anonymous %s'; type TResolverBaseType = ( @@ -443,7 +448,7 @@ const 'Nil', 'Procedure/Function', 'BuiltInProc', - 'set literal', + 'set', 'range..', 'array literal' ); @@ -472,7 +477,8 @@ type bfConcatArray, bfCopyArray, bfInsertArray, - bfDeleteArray + bfDeleteArray, + bfTypeInfo ); TResolverBuiltInProcs = set of TResolverBuiltInProc; const @@ -499,7 +505,8 @@ const 'Concat', 'Copy', 'Insert', - 'Delete' + 'Delete', + 'TypeInfo' ); bfAllStandardProcs = [Succ(bfCustom)..high(TResolverBuiltInProc)]; @@ -960,7 +967,8 @@ type proClassOfIs, // class-of supports is and as operator proExtClassInstanceNoTypeMembers, // class members of external class cannot be accessed by instance proOpenAsDynArrays, // open arrays work like dynamic arrays - proProcTypeWithoutIsNested // proc types can use nested procs without 'is nested' + proProcTypeWithoutIsNested, // proc types can use nested procs without 'is nested' + proMethodAddrAsPointer // can assign @method to a pointer ); TPasResolverOptions = set of TPasResolverOption; @@ -972,6 +980,7 @@ type TResolveDataListKind = (lkBuiltIn,lkModule); procedure ClearResolveDataList(Kind: TResolveDataListKind); private + FAnonymousElTypePostfix: String; FBaseTypes: array[TResolverBaseType] of TPasUnresolvedSymbolRef; FBaseTypeStringIndex: TResolverBaseType; FDefaultScope: TPasDefaultScope; @@ -1085,6 +1094,7 @@ type procedure FinishTypeDef(El: TPasType); virtual; procedure FinishEnumType(El: TPasEnumType); virtual; procedure FinishSetType(El: TPasSetType); virtual; + procedure FinishSubElementType(Parent, El: TPasElement); virtual; procedure FinishRangeType(El: TPasRangeType); virtual; procedure FinishRecordType(El: TPasRecordType); virtual; procedure FinishClassType(El: TPasClassType); virtual; @@ -1218,6 +1228,10 @@ type Expr: TPasExpr; RaiseOnError: boolean): integer; virtual; procedure BI_DeleteArray_OnFinishParamsExpr(Proc: TResElDataBuiltInProc; Params: TParamsExpr); virtual; + function BI_TypeInfo_OnGetCallCompatibility(Proc: TResElDataBuiltInProc; + Expr: TPasExpr; RaiseOnError: boolean): integer; virtual; + procedure BI_TypeInfo_OnGetCallResult({%H-}Proc: TResElDataBuiltInProc; + {%H-}Params: TParamsExpr; out ResolvedEl: TPasResolverResult); virtual; public constructor Create; destructor Destroy; override; @@ -1359,10 +1373,12 @@ type function GetPasPropertyAncestor(El: TPasProperty): TPasProperty; function GetPasPropertyGetter(El: TPasProperty): TPasElement; function GetPasPropertySetter(El: TPasProperty): TPasElement; + function GetPasPropertyStored(El: TPasProperty): TPasElement; function GetPasClassAncestor(ClassEl: TPasClassType; SkipAlias: boolean): TPasType; function GetLoop(El: TPasElement): TPasImplElement; function ResolveAliasType(aType: TPasType): TPasType; function ExprIsAddrTarget(El: TPasExpr): boolean; + function IsNameExpr(El: TPasExpr): boolean; inline; // TSelfExpr or TPrimitiveExpr with Kind=pekIdent function GetLastExprIdentifier(El: TPasExpr): TPasExpr; function ParentNeedsExprResult(El: TPasExpr): boolean; function GetReference_NewInstanceClass(Ref: TResolvedReference): TPasClassType; @@ -1376,6 +1392,7 @@ type function IsTypeCast(Params: TParamsExpr): boolean; function ProcNeedsParams(El: TPasProcedureType): boolean; function GetRangeLength(RangeResolved: TPasResolverResult): integer; + function HasTypeInfo(El: TPasType): boolean; virtual; public property BaseTypes[bt: TResolverBaseType]: TPasUnresolvedSymbolRef read GetBaseTypes; property BaseTypeStringIndex: TResolverBaseType read FBaseTypeStringIndex write FBaseTypeStringIndex; @@ -1399,6 +1416,8 @@ type property Options: TPasResolverOptions read FOptions write FOptions; property ScopeClass_Class: TPasClassScopeClass read FScopeClass_Class write FScopeClass_Class; property ScopeClass_WithExpr: TPasWithExprScopeClass read FScopeClass_WithExpr write FScopeClass_WithExpr; + property AnonymousElTypePostfix: String read FAnonymousElTypePostfix + write FAnonymousElTypePostfix; // default empty, if set, anonymous element types are named ArrayName+Postfix and added to declarations end; function GetObjName(o: TObject): string; @@ -1407,6 +1426,7 @@ function GetTypeDesc(aType: TPasType; AddPath: boolean = false): string; function GetTreeDesc(El: TPasElement; Indent: integer = 0): string; function GetResolverResultDesc(const T: TPasResolverResult): string; function GetResolverResultDescription(const T: TPasResolverResult; OnlyType: boolean = false): string; +function GetResolverResultDbg(const T: TPasResolverResult): string; function ResolverResultFlagsToStr(const Flags: TPasResolverResultFlags): string; procedure SetResolverIdentifier(out ResolvedType: TPasResolverResult; BaseType: TResolverBaseType; IdentEl: TPasElement; @@ -1468,9 +1488,9 @@ begin Result:=Result+')'; end; if ProcType.IsOfObject then - Result:=Result+' of object'; + Result:=Result+' '+ProcTypeModifiers[ptmOfObject]; if ProcType.IsNested then - Result:=Result+' is nested'; + Result:=Result+' '+ProcTypeModifiers[ptmIsNested]; if cCallingConventions[ProcType.CallingConvention]<>'' then Result:=Result+';'+cCallingConventions[ProcType.CallingConvention]; end; @@ -1624,9 +1644,9 @@ begin if El is TPasFunction then Result:=Result+':'+GetTreeDesc(TPasFunctionType(TPasFunction(El).ProcType).ResultEl,Indent); if TPasProcedureType(El).IsOfObject then - Result:=Result+' of object'; + Result:=Result+' '+ProcTypeModifiers[ptmOfObject]; if TPasProcedureType(El).IsNested then - Result:=Result+' is nested'; + Result:=Result+' '+ProcTypeModifiers[ptmIsNested]; if cCallingConventions[TPasProcedureType(El).CallingConvention]<>'' then Result:=Result+'; '+cCallingConventions[TPasProcedureType(El).CallingConvention]; end @@ -1742,6 +1762,18 @@ begin Result:=T.IdentEl.Name+':'+Result; end; +function GetResolverResultDbg(const T: TPasResolverResult): string; +begin + Result:='bt='+BaseTypeNames[T.BaseType]; + if T.SubType<>btNone then + Result:=Result+' Sub='+BaseTypeNames[T.SubType]; + Result:=Result + +' Ident='+GetObjName(T.IdentEl) + +' Type='+GetObjName(T.TypeEl) + +' Expr='+GetObjName(T.ExprEl) + +' Flags='+ResolverResultFlagsToStr(T.Flags); +end; + function ResolverResultFlagsToStr(const Flags: TPasResolverResultFlags): string; var f: TPasResolverResultFlag; @@ -2551,6 +2583,13 @@ begin Result:=FScopes[Index]; end; +// inline +function TPasResolver.IsNameExpr(El: TPasExpr): boolean; +begin + if El.ClassType=TSelfExpr then exit(true); + Result:=(El.ClassType=TPrimitiveExpr) and (TPrimitiveExpr(El).Kind=pekIdent); +end; + procedure TPasResolver.ClearResolveDataList(Kind: TResolveDataListKind); var El: TPasElement; @@ -2696,9 +2735,11 @@ begin else if (C=TPasClassType) or (C=TPasClassOfType) or (C=TPasEnumType) + or (C=TPasProcedureType) + or (C=TPasFunctionType) or (C=TPasArrayType) then begin - // type cast to a class, class-of, enum, or array + // type cast to user type Abort:=true; // can't be overloaded if Data^.Found<>nil then exit; Distance:=CheckTypeCast(TPasType(El),Data^.Params,false); @@ -2864,14 +2905,16 @@ var ClassScope: TPasClassScope; OlderEl: TPasElement; IsClassScope: Boolean; + C: TClass; begin IsClassScope:=(Scope is TPasClassScope); if (El.Visibility=visPublished) then begin - if El.ClassType=TPasProperty then + C:=El.ClassType; + if (C=TPasProperty) or (C=TPasVariable) then // Note: VarModifiers are not yet set - else if (El.ClassType=TPasProcedure) or (El.ClassType=TPasFunction) then + else if (C=TPasProcedure) or (C=TPasFunction) then // ok else RaiseMsg(20170403223024,nSymbolCannotBePublished,sSymbolCannotBePublished,[],El); @@ -3130,12 +3173,16 @@ begin EnumType:=El.EnumType; C:=EnumType.ClassType; if C=TPasEnumType then - exit + begin + FinishSubElementType(El,EnumType); + exit; + end else if C=TPasRangeType then begin RangeExpr:=TPasRangeType(EnumType).RangeExpr; if RangeExpr.Parent=El then CheckRangeExpr(RangeExpr.left,RangeExpr.right,StartResolved,EndResolved); + FinishSubElementType(El,EnumType); exit; end else if C=TPasUnresolvedSymbolRef then @@ -3151,6 +3198,37 @@ begin RaiseXExpectedButYFound(20170216151557,'enum type',EnumType.ElementTypeName,EnumType); end; +procedure TPasResolver.FinishSubElementType(Parent, El: TPasElement); +var + Decl: TPasDeclarations; + EnumScope: TPasEnumTypeScope; +begin + if (El.Name<>'') or (AnonymousElTypePostfix='') then exit; + if Parent.Name='' then + RaiseMsg(20170415165455,nCannotNestAnonymousX,sCannotNestAnonymousX,[El.ElementTypeName],El); + if not (Parent.Parent is TPasDeclarations) then + RaiseMsg(20170416094735,nCannotNestAnonymousX,sCannotNestAnonymousX,[El.ElementTypeName],El); + // give anonymous sub type a name + El.Name:=Parent.Name+AnonymousElTypePostfix; + {$IFDEF VerbosePasResolver} + writeln('TPasResolver.FinishSubElementType parent="',GetObjName(Parent),'" named anonymous type "',GetObjName(El),'"'); + {$ENDIF} + Decl:=TPasDeclarations(Parent.Parent); + Decl.Declarations.Add(El); + El.AddRef; + El.Parent:=Decl; + Decl.Types.Add(El); + if (El.ClassType=TPasEnumType) and (Parent.ClassType=TPasSetType) then + begin + EnumScope:=TPasEnumTypeScope(El.CustomData); + if EnumScope.CanonicalSet<>Parent then + begin + ReleaseAndNil(TPasElement(EnumScope.CanonicalSet)); + EnumScope.CanonicalSet:=TPasSetType(Parent); + end; + end; +end; + procedure TPasResolver.FinishRangeType(El: TPasRangeType); var StartResolved, EndResolved: TPasResolverResult; @@ -3202,6 +3280,7 @@ begin else RaiseXExpectedButYFound(20170216151609,'range',RangeResolved.IdentEl.ElementTypeName,Expr); end; + FinishSubElementType(El,El.ElType); end; procedure TPasResolver.FinishConstDef(El: TPasConst); @@ -3254,6 +3333,7 @@ var DeclProcScope, ProcScope: TPasProcedureScope; ParentScope: TPasScope; pm: TProcedureModifier; + ptm: TProcTypeModifier; begin if (El.Parent is TPasProcedure) and (TPasProcedure(El.Parent).ProcType=El) then begin @@ -3281,15 +3361,21 @@ begin end; if Proc.IsExternal then + begin for pm in TProcedureModifier do if (pm in Proc.Modifiers) and not (pm in [pmVirtual, pmDynamic, pmOverride, pmOverload, pmMessage, pmReintroduce, - pmStatic, pmVarargs, pmExternal, pmDispId, pmfar]) then RaiseMsg(20170216151616,nInvalidXModifierY, sInvalidXModifierY,[Proc.ElementTypeName,'external, '+ModifierNames[pm]],Proc); + for ptm in TProcTypeModifier do + if (ptm in Proc.ProcType.Modifiers) + and not (ptm in [ptmOfObject,ptmIsNested,ptmStatic,ptmVarargs]) then + RaiseMsg(20170411171224,nInvalidXModifierY, + sInvalidXModifierY,[Proc.ElementTypeName,'external, '+ProcTypeModifiers[ptm]],Proc); + end; if Proc.Parent is TPasClassType then begin @@ -3597,19 +3683,25 @@ begin end; procedure TPasResolver.FinishDeclaration(El: TPasElement); +var + C: TClass; begin - if El.ClassType=TPasVariable then + C:=El.ClassType; + if C=TPasVariable then FinishVariable(TPasVariable(El)) - else if El.ClassType=TPasProperty then + else if C=TPasProperty then FinishPropertyOfClass(TPasProperty(El)) - else if El.ClassType=TPasArgument then + else if C=TPasArgument then FinishArgument(TPasArgument(El)); end; procedure TPasResolver.FinishVariable(El: TPasVariable); begin if (El.Visibility=visPublished) then - RaiseMsg(20170403223837,nSymbolCannotBePublished,sSymbolCannotBePublished,[],El); + begin + if [vmClass,vmStatic,vmCVar]*El.VarModifiers<>[] then + RaiseMsg(20170403223837,nSymbolCannotBePublished,sSymbolCannotBePublished,[],El); + end; if El.Expr<>nil then begin ResolveExpr(El.Expr,rraRead); @@ -3757,7 +3849,7 @@ var end; var - ResultType: TPasType; + ResultType, TypeEl: TPasType; CurClassType: TPasClassType; AccEl: TPasElement; Proc: TPasProcedure; @@ -3788,7 +3880,7 @@ begin begin // check compatibility AccEl:=GetAccessor(PropEl.ReadAccessor); - if AccEl is TPasVariable then + if (AccEl.ClassType=TPasVariable) or (AccEl.ClassType=TPasConst) then begin if PropEl.Args.Count>0 then RaiseXExpectedButYFound(20170216151823,'function',AccEl.ElementTypeName,PropEl.ReadAccessor); @@ -3838,7 +3930,7 @@ begin begin // check compatibility AccEl:=GetAccessor(PropEl.WriteAccessor); - if AccEl is TPasVariable then + if AccEl.ClassType=TPasVariable then begin if PropEl.Args.Count>0 then RaiseXExpectedButYFound(20170216151852,'procedure',AccEl.ElementTypeName,PropEl.WriteAccessor); @@ -3892,13 +3984,27 @@ begin begin ResolveExpr(PropEl.ImplementsFunc,rraRead); // ToDo: check compatibility - + RaiseNotYetImplemented(20170409213850,PropEl.ImplementsFunc); end; if PropEl.StoredAccessor<>nil then begin // check compatibility AccEl:=GetAccessor(PropEl.StoredAccessor); - if AccEl is TPasProcedure then + if (AccEl.ClassType=TPasVariable) or (AccEl.ClassType=TPasConst) then + begin + if PropEl.IndexExpr<>nil then + RaiseNotYetImplemented(20170409214006,PropEl.StoredAccessor,'stored with index'); + TypeEl:=ResolveAliasType(TPasVariable(AccEl).VarType); + if not IsBaseType(TypeEl,btBoolean) then + RaiseIncompatibleType(20170409214300,nIncompatibleTypesGotExpected, + [],TypeEl,BaseTypes[btBoolean],PropEl.StoredAccessor); + if (vmClass in PropEl.VarModifiers)<>(vmClass in TPasVariable(AccEl).VarModifiers) then + if vmClass in PropEl.VarModifiers then + RaiseXExpectedButYFound(20170409214351,'class var','var',PropEl.StoredAccessor) + else + RaiseXExpectedButYFound(20170409214359,'var','class var',PropEl.StoredAccessor); + end + else if AccEl is TPasProcedure then begin // check function Proc:=TPasProcedure(AccEl); @@ -4913,9 +5019,7 @@ var C: TClass; begin Value:=Params.Value; - if (Value.ClassType=TSelfExpr) - or ((Value.ClassType=TPrimitiveExpr) - and (TPrimitiveExpr(Value).Kind=pekIdent)) then + if IsNameExpr(Value) then begin // e.g. Name() -> find compatible if Value.ClassType=TPrimitiveExpr then @@ -4932,12 +5036,12 @@ begin begin // FoundEl one element, but it was incompatible => raise error {$IFDEF VerbosePasResolver} - writeln('TPasResolver.ResolveParamsExpr found one element, but it was incompatible => check again to raise error'); + writeln('TPasResolver.ResolveFuncParamsExpr found one element, but it was incompatible => check again to raise error. Found=',GetObjName(FindCallData.Found)); {$ENDIF} if FindCallData.Found is TPasProcedure then CheckCallProcCompatibility(TPasProcedure(FindCallData.Found).ProcType,Params,true) else if FindCallData.Found is TPasProcedureType then - CheckCallProcCompatibility(TPasProcedureType(FindCallData.Found),Params,true) + CheckTypeCast(TPasProcedureType(FindCallData.Found),Params,true) else if FindCallData.Found.ClassType=TPasUnresolvedSymbolRef then begin if FindCallData.Found.CustomData is TResElDataBuiltInProc then @@ -4978,7 +5082,7 @@ begin // ToDo: create a hint for each candidate El:=TPasElement(FindCallData.List[i]); {$IFDEF VerbosePasResolver} - writeln('TPasResolver.ResolveParamsExpr Overload Candidate: ',GetElementSourcePosStr(El),' ',GetTreeDesc(El)); + writeln('TPasResolver.ResolveFuncParamsExpr Overload Candidate: ',GetElementSourcePosStr(El),' ',GetTreeDesc(El)); {$ENDIF} Msg:=Msg+', '; Msg:=Msg+GetElementSourcePosStr(El); @@ -5013,6 +5117,10 @@ begin if (C=TPasClassType) or (C=TPasClassOfType) or (C=TPasEnumType) + or (C=TPasSetType) + or (C=TPasPointerType) + or (C=TPasProcedureType) + or (C=TPasFunctionType) or (C=TPasArrayType) then begin // type cast @@ -5050,11 +5158,12 @@ begin {$IFDEF VerbosePasResolver} writeln('TPasResolver.ResolveFuncParamsExpr FoundEl=',GetObjName(FoundEl),' CustomData=',GetObjName(FoundEl.CustomData)); {$ENDIF} - RaiseNotYetImplemented(20170306121908,Params); + RaiseMsg(20170306121908,nIllegalExpression,sIllegalExpression,[],Params); end; end else begin + // FoundEl is not a type, maybe a var ComputeElement(FoundEl,ResolvedEl,[rcNoImplicitProc]); if ResolvedEl.TypeEl is TPasProcedureType then begin @@ -5064,7 +5173,7 @@ begin {$IFDEF VerbosePasResolver} writeln('TPasResolver.ResolveFuncParamsExpr FoundEl=',GetObjName(FoundEl),' CustomData=',GetObjName(FoundEl.CustomData),' Resolvedel=',GetResolverResultDesc(ResolvedEl)); {$ENDIF} - RaiseNotYetImplemented(20170306104301,Params); + RaiseMsg(20170306104301,nIllegalExpression,sIllegalExpression,[],Params); end; end else if Value.ClassType=TParamsExpr then @@ -5078,7 +5187,7 @@ begin if IsProcedureType(ResolvedEl,true) then begin CheckCallProcCompatibility(TPasProcedureType(ResolvedEl.TypeEl),Params,true); - CreateReference(ResolvedEl.TypeEl,Value,Access); + CreateReference(TPasProcedureType(ResolvedEl.TypeEl),Value,Access); exit; end end; @@ -5273,7 +5382,7 @@ end; procedure TPasResolver.AccessExpr(Expr: TPasExpr; Access: TResolvedRefAccess); -// called after a call overload was found for each element +// called after a call target was found, called for each element // to set the rraParamToUnknownProc to Access var Ref: TResolvedReference; @@ -6336,16 +6445,39 @@ begin end else if ResolvedEl.TypeEl is TPasProcedureType then begin - if rcConstant in Flags then - RaiseConstantExprExp(20170216152639,Params); - if ResolvedEl.TypeEl is TPasFunctionType then - // function call => return result - ComputeElement(TPasFunctionType(ResolvedEl.TypeEl).ResultEl, - ResolvedEl,Flags+[rcNoImplicitProc],StartEl) + if Params.Value is TParamsExpr then + begin + // e.g. Name()() or Name[]() + Include(ResolvedEl.Flags,rrfReadable); + end; + if rrfReadable in ResolvedEl.Flags then + begin + // call procvar + if rcConstant in Flags then + RaiseConstantExprExp(20170216152639,Params); + if ResolvedEl.TypeEl is TPasFunctionType then + // function call => return result + ComputeElement(TPasFunctionType(ResolvedEl.TypeEl).ResultEl, + ResolvedEl,Flags+[rcNoImplicitProc],StartEl) + else + // procedure call, result is neither readable nor writable + SetResolverTypeExpr(ResolvedEl,btProc,TPasProcedureType(ResolvedEl.TypeEl),[]); + Include(ResolvedEl.Flags,rrfCanBeStatement); + end else - // procedure call, result is neither readable nor writable - SetResolverTypeExpr(ResolvedEl,btProc,TPasProcedureType(ResolvedEl.TypeEl),[]); - Include(ResolvedEl.Flags,rrfCanBeStatement); + begin + // typecast proctype + if length(Params.Params)<>1 then + begin + {$IFDEF VerbosePasResolver} + writeln('TPasResolver.ComputeFuncParams DeclEl=',GetObjName(DeclEl),' ',GetResolverResultDbg(ResolvedEl)); + {$ENDIF} + RaiseMsg(20170416185211,nWrongNumberOfParametersForTypeCast, + sWrongNumberOfParametersForTypeCast,[ResolvedEl.TypeEl.Name],Params); + end; + SetResolverValueExpr(ResolvedEl,btContext,TPasProcedureType(ResolvedEl.TypeEl), + Params.Params[0],[rrfReadable]); + end; end else if (DeclEl is TPasType) then begin @@ -7494,6 +7626,51 @@ begin AccessExpr(P[2],rraRead); end; +function TPasResolver.BI_TypeInfo_OnGetCallCompatibility( + Proc: TResElDataBuiltInProc; Expr: TPasExpr; RaiseOnError: boolean): integer; +var + Params: TParamsExpr; + Param: TPasExpr; + Decl: TPasElement; + ParamResolved: TPasResolverResult; + aType: TPasType; +begin + Result:=cIncompatible; + if not CheckBuiltInMinParamCount(Proc,Expr,1,RaiseOnError) then + exit; + Params:=TParamsExpr(Expr); + + // check type or var + Param:=Params.Params[0]; + ComputeElement(Param,ParamResolved,[rcNoImplicitProc]); + Decl:=ParamResolved.IdentEl; + aType:=nil; + if (Decl<>nil) then + begin + if Decl is TPasType then + aType:=TPasType(Decl) + else if Decl is TPasVariable then + aType:=TPasVariable(Decl).VarType + else if Decl is TPasArgument then + aType:=TPasArgument(Decl).ArgType; + end; + if aType=nil then + RaiseMsg(20170411100259,nTypeIdentifierExpected,sTypeIdentifierExpected,[],Param); + aType:=ResolveAliasType(aType); + if not HasTypeInfo(aType) then + RaiseMsg(20170413200118,nSymbolCannotBePublished,sSymbolCannotBePublished,[],Param); + + Result:=CheckBuiltInMaxParamCount(Proc,Params,1,RaiseOnError); +end; + +procedure TPasResolver.BI_TypeInfo_OnGetCallResult(Proc: TResElDataBuiltInProc; + Params: TParamsExpr; out ResolvedEl: TPasResolverResult); +begin + if Proc=nil then; + if Params=nil then ; + SetResolverTypeExpr(ResolvedEl,btPointer,FBaseTypes[btPointer],[rrfReadable]); +end; + constructor TPasResolver.Create; begin inherited Create; @@ -7807,8 +7984,10 @@ begin and (vmClass in TPasVariable(FindData.Found).VarModifiers) then // class var/const/property: ok else - RaiseMsg(20170216152348,nCannotAccessThisMemberFromAClassReference, - sCannotAccessThisMemberFromAClassReference,[],FindData.ErrorPosEl); + begin + RaiseMsg(20170216152348,nCannotAccessThisMemberFromAX, + sCannotAccessThisMemberFromAX,[FindData.Found.Parent.ElementTypeName],FindData.ErrorPosEl); + end; end else if (proExtClassInstanceNoTypeMembers in Options) and (StartScope.ClassType=TPasDotClassScope) @@ -8126,6 +8305,10 @@ begin AddBuiltInProc('Delete','procedure Delete(var Array; Start, Count: integer)', @BI_DeleteArray_OnGetCallCompatibility,nil, @BI_DeleteArray_OnFinishParamsExpr,bfDeleteArray,[bipfCanBeStatement]); + if bfTypeInfo in TheBaseProcs then + AddBuiltInProc('TypeInfo','function TypeInfo(type or var identifier): Pointer', + @BI_TypeInfo_OnGetCallCompatibility,@BI_TypeInfo_OnGetCallResult, + nil,bfTypeInfo); end; function TPasResolver.AddBaseType(const aName: string; Typ: TResolverBaseType @@ -8613,7 +8796,10 @@ begin if GotType.BaseType<>ExpType.BaseType then begin GotDesc:=GetBaseDecs(GotType); - ExpDesc:=GetBaseDecs(ExpType); + if ExpType.BaseType=btNil then + ExpDesc:=BaseTypeNames[btPointer] + else + ExpDesc:=GetBaseDecs(ExpType); if GotDesc=ExpDesc then begin GotDesc:=GetBaseDecs(GotType,true); @@ -8683,8 +8869,7 @@ begin end else begin - IsVarArgs:=IsVarArgs or ((ProcType.Parent is TPasProcedure) - and (pmVarargs in TPasProcedure(ProcType.Parent).Modifiers)); + IsVarArgs:=IsVarArgs or (ptmVarargs in ProcType.Modifiers); if IsVarArgs then begin ComputeElement(Param,ParamResolved,[],Param); @@ -8884,15 +9069,15 @@ begin exit; end; if Proc1.IsNested<>Proc2.IsNested then - exit(ModifierError('is nested')); + exit(ModifierError(ProcTypeModifiers[ptmIsNested])); if Proc1.IsOfObject<>Proc2.IsOfObject then begin if (proProcTypeWithoutIsNested in Options) then - exit(ModifierError('of object')) + exit(ModifierError(ProcTypeModifiers[ptmOfObject])) else if Proc1.IsNested then // "is nested" can handle both, proc and method. else - exit(ModifierError('of object')) + exit(ModifierError(ProcTypeModifiers[ptmOfObject])) end; if Proc1.CallingConvention<>Proc2.CallingConvention then begin @@ -9049,6 +9234,7 @@ function TPasResolver.CheckAssignResCompatibility(const LHS, var TypeEl: TPasType; Handled: Boolean; + C: TClass; begin // check if the RHS can be converted to LHS {$IFDEF VerbosePasResolver} @@ -9099,7 +9285,7 @@ begin [],ErrorEl); exit(cIncompatible); end - else if LHS.BaseType in [btRange,btSet,btModule,btArray] then + else if LHS.BaseType in [btRange,btSet,btModule,btArray,btProc] then begin if RaiseOnIncompatible then RaiseMsg(20170216152432,nIllegalExpression,sIllegalExpression,[],ErrorEl); @@ -9118,10 +9304,11 @@ begin else if LHS.BaseType=btContext then begin TypeEl:=LHS.TypeEl; - if (TypeEl.ClassType=TPasClassType) - or (TypeEl.ClassType=TPasClassOfType) - or (TypeEl.ClassType=TPasPointerType) - or (TypeEl is TPasProcedureType) + C:=TypeEl.ClassType; + if (C=TPasClassType) + or (C=TPasClassOfType) + or (C=TPasPointerType) + or C.InheritsFrom(TPasProcedureType) or IsDynArray(TypeEl) then Result:=cExact; end; @@ -9154,6 +9341,40 @@ begin Result:=cExact; end; end + else if LHS.BaseType=btPointer then + begin + if RHS.BaseType=btPointer then + begin + if IsBaseType(LHS.TypeEl,btPointer) then + Result:=cExact // btPointer can take any pointer + else if IsBaseType(RHS.TypeEl,btPointer) then + Result:=cExact+1 // any pointer can take a btPointer + else if IsSameType(LHS.TypeEl,RHS.TypeEl) then + Result:=cExact // pointer of same type + else if (LHS.TypeEl.ClassType=TPasPointerType) + and (RHS.TypeEl.ClassType=TPasPointerType) then + Result:=CheckAssignCompatibility(TPasPointerType(LHS.TypeEl).DestType, + TPasPointerType(RHS.TypeEl).DestType,RaiseOnIncompatible); + end + else if IsBaseType(LHS.TypeEl,btPointer) then + begin + if RHS.BaseType=btContext then + begin + C:=RHS.TypeEl.ClassType; + if C=TPasClassType then + exit(cExact) // class type or class instance + else if C=TPasClassOfType then + Result:=cExact + else if C=TPasArrayType then + begin + if IsDynArray(RHS.TypeEl) then + Result:=cExact; + end + else if (C=TPasProcedureType) or (C=TPasFunctionType) then + Result:=cExact+1; + end; + end; + end else if (LHS.BaseType=btContext) and (LHS.TypeEl is TPasArrayType) then Result:=CheckAssignCompatibilityArrayType(LHS,RHS,ErrorEl,RaiseOnIncompatible); end; @@ -9309,10 +9530,10 @@ begin or (TypeEl is TPasProcedureType) or IsDynArray(TypeEl) then exit(cExact); - end - else if RaiseOnIncompatible then - RaiseMsg(20170216152442,nIncompatibleTypesGotExpected,sIncompatibleTypesGotExpected, - [BaseTypeNames[RHS.BaseType],BaseTypeNames[LHS.BaseType]],LErrorEl) + end; + if RaiseOnIncompatible then + RaiseIncompatibleTypeRes(20170216152442,nIncompatibleTypesGotExpected, + [],RHS,LHS,RErrorEl) else exit(cIncompatible); end @@ -9329,10 +9550,10 @@ begin or (TypeEl is TPasProcedureType) or IsDynArray(TypeEl) then exit(cExact); - end - else if RaiseOnIncompatible then - RaiseMsg(20170216152444,nIncompatibleTypesGotExpected,sIncompatibleTypesGotExpected, - [BaseTypeNames[LHS.BaseType],BaseTypeNames[RHS.BaseType]],LErrorEl) + end; + if RaiseOnIncompatible then + RaiseIncompatibleTypeRes(20170216152444,nIncompatibleTypesGotExpected, + [],LHS,RHS,LErrorEl) else exit(cIncompatible); end @@ -9475,6 +9696,24 @@ begin end; end; +function TPasResolver.GetPasPropertyStored(El: TPasProperty): TPasElement; +// search the member variable or setter procedure of a property +var + DeclEl: TPasElement; +begin + Result:=nil; + while El<>nil do + begin + if El.StoredAccessor<>nil then + begin + DeclEl:=(El.StoredAccessor.CustomData as TResolvedReference).Declaration; + Result:=DeclEl; + exit; + end; + El:=GetPasPropertyAncestor(El); + end; +end; + function TPasResolver.CheckParamCompatibility(Expr: TPasExpr; Param: TPasArgument; ParamNo: integer; RaiseOnError: boolean): integer; var @@ -9529,7 +9768,7 @@ begin if not ResolvedElCanBeVarParam(ExprResolved) then begin {$IFDEF VerbosePasResolver} - writeln('TPasResolver.CheckParamCompatibility NeedWritable: Identifier=',GetObjName(ExprResolved.IdentEl),' Type=',GetObjName(ExprResolved.TypeEl),' Expr=',GetObjName(ExprResolved.ExprEl),' Flags=',ResolverResultFlagsToStr(ExprResolved.Flags)); + writeln('TPasResolver.CheckParamCompatibility NeedWritable: ',GetResolverResultDbg(ExprResolved)); {$ENDIF} if RaiseOnError then RaiseMsg(20170216152450,nVariableIdentifierExpected,sVariableIdentifierExpected,[],Expr); @@ -9968,8 +10207,8 @@ begin exit(cIncompatible); end; Param:=Params.Params[0]; - ComputeElement(Param,ParamResolved,[]); - ComputeElement(El,ResolvedEl,[]); + ComputeElement(Param,ParamResolved,[rcNoImplicitProcType]); + ComputeElement(El,ResolvedEl,[rcType]); Result:=CheckTypeCastRes(ParamResolved,ResolvedEl,Param,RaiseOnError); end; @@ -9980,9 +10219,10 @@ var ToTypeEl, ToClassType, FromClassType: TPasType; ToTypeBaseType: TResolverBaseType; C: TClass; + ToProcType, FromProcType: TPasProcedureType; begin Result:=cIncompatible; - ToTypeEl:=ToResolved.TypeEl; + ToTypeEl:=ResolveAliasType(ToResolved.TypeEl); if (ToTypeEl<>nil) and (rrfReadable in FromResolved.Flags) then begin @@ -10021,33 +10261,75 @@ begin begin if FromResolved.BaseType in btAllStringAndChars then Result:=cExact+1; + end + else if ToTypeBaseType=btPointer then + begin + if FromResolved.BaseType=btPointer then + Result:=cExact + else if FromResolved.BaseType=btContext then + begin + C:=FromResolved.TypeEl.ClassType; + if (C=TPasClassType) + or (C=TPasClassOfType) + or (C=TPasPointerType) + or ((C=TPasArrayType) and IsDynArray(FromResolved.TypeEl)) then + Result:=cExact + else if (C=TPasProcedureType) or (C=TPasFunctionType) then + begin + // from procvar to pointer + FromProcType:=TPasProcedureType(FromResolved.TypeEl); + if FromProcType.IsOfObject then + begin + if proMethodAddrAsPointer in Options then + Result:=cExact+1 + else if RaiseOnError then + RaiseMsg(20170416183615,nIllegalTypeConversionTo,sIllegalTypeConversionTo, + [FromProcType.ElementTypeName+' '+ProcTypeModifiers[ptmOfObject], + BaseTypeNames[btPointer]],ErrorEl); + end + else if FromProcType.IsNested then + begin + if RaiseOnError then + RaiseMsg(20170416183800,nIllegalTypeConversionTo,sIllegalTypeConversionTo, + [FromProcType.ElementTypeName+' '+ProcTypeModifiers[ptmIsNested], + BaseTypeNames[btPointer]],ErrorEl); + end + else + Result:=cExact+1; + end; + end; end; end; end else if C=TPasClassType then begin // to class - if FromResolved.BaseType=btNil then - Result:=cExact - else if (FromResolved.BaseType=btContext) - and (FromResolved.TypeEl.ClassType=TPasClassType) then + if FromResolved.BaseType=btContext then begin - if (FromResolved.IdentEl is TPasType) then - RaiseMsg(20170404162606,nCannotTypecastAType,sCannotTypecastAType,[],ErrorEl); - // type cast upwards or downwards - Result:=CheckSrcIsADstType(FromResolved,ToResolved,ErrorEl); - if Result=cIncompatible then - Result:=CheckSrcIsADstType(ToResolved,FromResolved,ErrorEl); - if Result=cIncompatible then - Result:=CheckTypeCastClassInstanceToClass(FromResolved,ToResolved,ErrorEl); + if FromResolved.TypeEl.ClassType=TPasClassType then + begin + if FromResolved.IdentEl is TPasType then + RaiseMsg(20170404162606,nCannotTypecastAType,sCannotTypecastAType,[],ErrorEl); + // type cast upwards or downwards + Result:=CheckSrcIsADstType(FromResolved,ToResolved,ErrorEl); + if Result=cIncompatible then + Result:=CheckSrcIsADstType(ToResolved,FromResolved,ErrorEl); + if Result=cIncompatible then + Result:=CheckTypeCastClassInstanceToClass(FromResolved,ToResolved,ErrorEl); + end + end + else if FromResolved.BaseType=btPointer then + begin + if IsBaseType(FromResolved.TypeEl,btPointer) then + Result:=cExact; // untyped pointer to class instance end; end else if C=TPasClassOfType then begin //writeln('TPasResolver.CheckTypeCast class-of FromRes.TypeEl=',GetObjName(FromResolved.TypeEl),' FromRes.IdentEl=',GetObjName(FromResolved.IdentEl)); - if (FromResolved.BaseType=btContext) then + if FromResolved.BaseType=btContext then begin - if (FromResolved.TypeEl.ClassType=TPasClassOfType) then + if FromResolved.TypeEl.ClassType=TPasClassOfType then begin if (FromResolved.IdentEl is TPasType) then RaiseMsg(20170404162604,nCannotTypecastAType,sCannotTypecastAType,[],ErrorEl); @@ -10056,6 +10338,11 @@ begin FromClassType:=TPasClassOfType(FromResolved.TypeEl).DestType; Result:=CheckClassesAreRelated(ToClassType,FromClassType,ErrorEl); end; + end + else if FromResolved.BaseType=btPointer then + begin + if IsBaseType(FromResolved.TypeEl,btPointer) then + Result:=cExact; // untyped pointer to class-of end; end else if C=TPasEnumType then @@ -10065,29 +10352,89 @@ begin end else if C=TPasArrayType then begin - if (FromResolved.BaseType=btContext) - and (FromResolved.TypeEl.ClassType=TPasArrayType) then - Result:=CheckTypeCastArray(TPasArrayType(FromResolved.TypeEl), - TPasArrayType(ToTypeEl),ErrorEl,RaiseOnError); + if FromResolved.BaseType=btContext then + begin + if FromResolved.TypeEl.ClassType=TPasArrayType then + Result:=CheckTypeCastArray(TPasArrayType(FromResolved.TypeEl), + TPasArrayType(ToTypeEl),ErrorEl,RaiseOnError); + end + else if FromResolved.BaseType=btPointer then + begin + if IsDynArray(ToResolved.TypeEl) + and IsBaseType(FromResolved.TypeEl,btPointer) then + Result:=cExact; // untyped pointer to dynnamic array + end; + end + else if (C=TPasProcedureType) or (C=TPasFunctionType) then + begin + ToProcType:=TPasProcedureType(ToTypeEl); + if IsBaseType(FromResolved.TypeEl,btPointer) then + begin + // type cast untyped pointer value to proctype + if ToProcType.IsOfObject then + begin + if proMethodAddrAsPointer in Options then + Result:=cExact+1 + else if RaiseOnError then + RaiseMsg(20170416183940,nIllegalTypeConversionTo,sIllegalTypeConversionTo, + [BaseTypeNames[btPointer], + ToProcType.ElementTypeName+' '+ProcTypeModifiers[ptmOfObject]],ErrorEl); + end + else if ToProcType.IsNested then + begin + if RaiseOnError then + RaiseMsg(20170416184149,nIllegalTypeConversionTo,sIllegalTypeConversionTo, + [BaseTypeNames[btPointer], + ToProcType.ElementTypeName+' '+ProcTypeModifiers[ptmIsNested]],ErrorEl); + end + else + Result:=cExact+1; + end + else if FromResolved.BaseType=btContext then + begin + if FromResolved.TypeEl is TPasProcedureType then + begin + // type cast procvar to proctype + FromProcType:=TPasProcedureType(FromResolved.TypeEl); + if (FromProcType.IsOfObject<>ToProcType.IsOfObject) + and not (proMethodAddrAsPointer in Options) then + begin + if RaiseOnError then + RaiseMsg(20170416183109,nIllegalTypeConversionTo,sIllegalTypeConversionTo, + [FromProcType.ElementTypeName+BoolToStr(FromProcType.IsOfObject,' '+ProcTypeModifiers[ptmOfObject],''), + ToProcType.ElementTypeName+BoolToStr(ToProcType.IsOfObject,' '+ProcTypeModifiers[ptmOfObject],'')],ErrorEl); + end + else if FromProcType.IsNested<>ToProcType.IsNested then + begin + if RaiseOnError then + RaiseMsg(20170416183305,nIllegalTypeConversionTo,sIllegalTypeConversionTo, + [FromProcType.ElementTypeName+BoolToStr(FromProcType.IsNested,' '+ProcTypeModifiers[ptmIsNested],''), + ToProcType.ElementTypeName+BoolToStr(ToProcType.IsNested,' '+ProcTypeModifiers[ptmIsNested],'')],ErrorEl); + end + else + Result:=cExact+1; + end; + end; end; end else if ToTypeEl<>nil then begin // FromResolved is not readable - if (FromResolved.BaseType=btContext) - and (FromResolved.TypeEl.ClassType=TPasClassType) - and (FromResolved.TypeEl=FromResolved.IdentEl) - and (ToResolved.BaseType=btContext) - and (ToResolved.TypeEl.ClassType=TPasClassOfType) - and (ToResolved.TypeEl=ToResolved.IdentEl) then - begin - // for example class-of(Self) in a class function - ToClassType:=TPasClassOfType(ToTypeEl).DestType; - FromClassType:=TPasClassType(FromResolved.TypeEl); - Result:=CheckClassesAreRelated(ToClassType,FromClassType,ErrorEl); - if Result<cIncompatible then exit; + if FromResolved.BaseType=btContext then + begin + if (FromResolved.TypeEl.ClassType=TPasClassType) + and (FromResolved.TypeEl=FromResolved.IdentEl) + and (ToResolved.BaseType=btContext) + and (ToResolved.TypeEl.ClassType=TPasClassOfType) + and (ToResolved.TypeEl=ToResolved.IdentEl) then + begin + // for example class-of(Self) in a class function + ToClassType:=TPasClassOfType(ToTypeEl).DestType; + FromClassType:=TPasClassType(FromResolved.TypeEl); + Result:=CheckClassesAreRelated(ToClassType,FromClassType,ErrorEl); + end; end; - if RaiseOnError then + if (Result=cIncompatible) and RaiseOnError then begin if FromResolved.IdentEl is TPasType then RaiseMsg(20170404162610,nCannotTypecastAType,sCannotTypecastAType,[],ErrorEl); @@ -10097,7 +10444,7 @@ begin if Result=cIncompatible then begin {$IFDEF VerbosePasResolver} - writeln('TPasResolver.CheckTypeCastRes From=',GetResolverResultDesc(FromResolved),' To=',GetResolverResultDesc(ToResolved)); + writeln('TPasResolver.CheckTypeCastRes From={',GetResolverResultDbg(FromResolved),'} To={',GetResolverResultDbg(ToResolved),'}'); {$ENDIF} if RaiseOnError then RaiseIncompatibleTypeRes(20170216152528,nIllegalTypeConversionTo, @@ -10595,11 +10942,21 @@ begin end; function TPasResolver.ResolveAliasType(aType: TPasType): TPasType; +var + C: TClass; begin Result:=aType; - while (Result<>nil) - and ((Result.ClassType=TPasAliasType) or (Result.ClassType=TPasTypeAliasType)) do - Result:=TPasAliasType(Result).DestType; + while Result<>nil do + begin + C:=Result.ClassType; + if (C=TPasAliasType) or (C=TPasTypeAliasType) then + Result:=TPasAliasType(Result).DestType + else if (C=TPasClassType) and TPasClassType(Result).IsForward + and (Result.CustomData is TResolvedReference) then + Result:=TResolvedReference(Result.CustomData).Declaration as TPasType + else + exit; + end; end; function TPasResolver.ExprIsAddrTarget(El: TPasExpr): boolean; @@ -10615,8 +10972,7 @@ var begin Result:=false; if El=nil then exit; - if not ((El.ClassType=TParamsExpr) or (El.ClassType=TPrimitiveExpr) - or (El.ClassType=TSelfExpr)) then + if not IsNameExpr(El) then exit; repeat Parent:=El.Parent; @@ -10789,23 +11145,30 @@ var Value: TPasExpr; Ref: TResolvedReference; Decl: TPasElement; + C: TClass; begin Result:=false; if (Params=nil) or (Params.Kind<>pekFuncParams) then exit; Value:=Params.Value; - if (Value.ClassType<>TSelfExpr) - and ((Value.ClassType<>TPrimitiveExpr) or (TPrimitiveExpr(Value).Kind<>pekIdent)) then + if not IsNameExpr(Value) then exit; if not (Value.CustomData is TResolvedReference) then exit; Ref:=TResolvedReference(Value.CustomData); Decl:=Ref.Declaration; - if (Decl.ClassType=TPasAliasType) or (Decl.ClassType=TPasTypeAliasType) then + C:=Decl.ClassType; + if (C=TPasAliasType) or (C=TPasTypeAliasType) then + begin Decl:=ResolveAliasType(TPasAliasType(Decl)); - if (Decl.ClassType=TPasClassType) - or (Decl.ClassType=TPasClassOfType) - or (Decl.ClassType=TPasEnumType) then - exit(true); - if (Decl.ClassType=TPasUnresolvedSymbolRef) + C:=Decl.ClassType; + end; + if (C=TPasProcedureType) + or (C=TPasFunctionType) then + exit(true) + else if (C=TPasClassType) + or (C=TPasClassOfType) + or (C=TPasEnumType) then + exit(true) + else if (C=TPasUnresolvedSymbolRef) and (Decl.CustomData is TResElDataBaseType) then exit(true); end; @@ -10828,6 +11191,18 @@ begin Result:=2; end; +function TPasResolver.HasTypeInfo(El: TPasType): boolean; +begin + Result:=false; + if El=nil then exit; + if El.CustomData is TResElDataBaseType then + exit(true); // base type + if El.Parent=nil then exit; + if (El.Parent is TPasType) and not HasTypeInfo(TPasType(El.Parent)) then + exit; + Result:=true; +end; + function TPasResolver.CheckSrcIsADstType(const ResolvedSrcType, ResolvedDestType: TPasResolverResult; ErrorEl: TPasElement): integer; // finds distance between classes SrcType and DestType diff --git a/packages/fcl-passrc/src/pastree.pp b/packages/fcl-passrc/src/pastree.pp index 82c701ff24..a6746e497f 100644 --- a/packages/fcl-passrc/src/pastree.pp +++ b/packages/fcl-passrc/src/pastree.pp @@ -101,7 +101,10 @@ type visPublished, visAutomated, visStrictPrivate, visStrictProtected); - TCallingConvention = (ccDefault,ccRegister,ccPascal,ccCDecl,ccStdCall,ccOldFPCCall,ccSafeCall,ccSysCall); + TCallingConvention = (ccDefault,ccRegister,ccPascal,ccCDecl,ccStdCall, + ccOldFPCCall,ccSafeCall,ccSysCall); + TProcTypeModifier = (ptmOfObject,ptmIsNested,ptmStatic,ptmVarargs); + TProcTypeModifiers = set of TProcTypeModifier; TPackMode = (pmNone,pmPacked,pmBitPacked); TPasMemberVisibilities = set of TPasMemberVisibility; @@ -648,6 +651,11 @@ type { TPasProcedureType } TPasProcedureType = class(TPasType) + private + function GetIsNested: Boolean; + function GetIsOfObject: Boolean; + procedure SetIsNested(const AValue: Boolean); + procedure SetIsOfObject(const AValue: Boolean); public constructor Create(const AName: string; AParent: TPasElement); override; destructor Destroy; override; @@ -659,10 +667,11 @@ type procedure ForEachCall(const aMethodCall: TOnForEachPasElement; const Arg: Pointer); override; public - IsOfObject: Boolean; - IsNested : Boolean; Args: TFPList; // List of TPasArgument objects CallingConvention: TCallingConvention; + Modifiers: TProcTypeModifiers; + property IsOfObject: Boolean read GetIsOfObject write SetIsOfObject; + property IsNested : Boolean read GetIsNested write SetIsNested; end; { TPasResultElement } @@ -828,7 +837,7 @@ type TProcedureModifier = (pmVirtual, pmDynamic, pmAbstract, pmOverride, pmExport, pmOverload, pmMessage, pmReintroduce, - pmStatic,pmInline,pmAssembler,pmVarargs, pmPublic, + pmInline,pmAssembler, pmPublic, pmCompilerProc,pmExternal,pmForward, pmDispId, pmNoReturn, pmfar); TProcedureModifiers = Set of TProcedureModifier; @@ -1410,11 +1419,13 @@ const ( 'deprecated', 'library', 'platform', 'experimental', 'unimplemented' ); cCallingConventions : Array[TCallingConvention] of string = ( '', 'Register','Pascal','CDecl','StdCall','OldFPCCall','SafeCall','SysCall'); + ProcTypeModifiers : Array[TProcTypeModifier] of string = + ('of Object', 'is nested','static','varargs'); ModifierNames : Array[TProcedureModifier] of string = ('virtual', 'dynamic','abstract', 'override', 'export', 'overload', 'message', 'reintroduce', - 'static','inline','assembler','varargs', 'public', + 'inline','assembler','public', 'compilerproc','external','forward','dispid', 'noreturn','far'); @@ -2447,6 +2458,32 @@ end; { TPasProcedureType } +function TPasProcedureType.GetIsNested: Boolean; +begin + Result:=ptmIsNested in Modifiers; +end; + +function TPasProcedureType.GetIsOfObject: Boolean; +begin + Result:=ptmOfObject in Modifiers; +end; + +procedure TPasProcedureType.SetIsNested(const AValue: Boolean); +begin + if AValue then + Include(Modifiers,ptmIsNested) + else + Exclude(Modifiers,ptmIsNested); +end; + +procedure TPasProcedureType.SetIsOfObject(const AValue: Boolean); +begin + if AValue then + Include(Modifiers,ptmOfObject) + else + Exclude(Modifiers,ptmOfObject); +end; + constructor TPasProcedureType.Create(const AName: string; AParent: TPasElement); begin inherited Create(AName, AParent); @@ -3690,7 +3727,7 @@ end; function TPasProcedure.IsStatic: Boolean; begin - Result:=pmStatic in FModifiers; + Result:=ptmStatic in ProcType.Modifiers; end; function TPasProcedure.IsForward: Boolean; diff --git a/packages/fcl-passrc/src/pasuseanalyzer.pas b/packages/fcl-passrc/src/pasuseanalyzer.pas index 9cebe564fc..b7fe8cd305 100644 --- a/packages/fcl-passrc/src/pasuseanalyzer.pas +++ b/packages/fcl-passrc/src/pasuseanalyzer.pas @@ -147,15 +147,15 @@ type end; TPasAnalyzerOption = ( - paoKeepPublished, // when a class is used, all its published members are used as well paoOnlyExports // default: use all class members accessible from outside (protected, but not private) ); TPasAnalyzerOptions = set of TPasAnalyzerOption; TPAUseMode = ( - paumElement, // mark element - paumAllPublic, // mark element and descend into children and mark public identifiers - paumAllExports // do not mark element and descend into children and mark exports + paumElement, // Mark element. Do not descend into children. + paumAllPublic, // Mark element and descend into children and mark public identifiers + paumAllExports, // Do not mark element. Descend into children and mark exports. + paumPublished // Mark element and its type and descend into children and mark published identifiers ); TPAUseModes = set of TPAUseMode; @@ -188,6 +188,7 @@ type function ElementVisited(El: TPasElement; Mode: TPAUseMode): boolean; procedure UseElement(El: TPasElement; Access: TResolvedRefAccess; UseFull: boolean); virtual; + procedure UsePublished(El: TPasElement); virtual; procedure UseModule(aModule: TPasModule; Mode: TPAUseMode); virtual; procedure UseSection(Section: TPasSection; Mode: TPAUseMode); virtual; procedure UseImplBlock(Block: TPasImplBlock; Mark: boolean); virtual; @@ -221,6 +222,7 @@ type function FindElement(El: TPasElement): TPAElement; // utility function IsUsed(El: TPasElement): boolean; // valid after calling Analyze* + function IsTypeInfoUsed(El: TPasElement): boolean; // valid after calling Analyze* function IsModuleInternal(El: TPasElement): boolean; function IsExport(El: TPasElement): boolean; function IsIdentifier(El: TPasElement): boolean; @@ -615,6 +617,82 @@ begin RaiseNotSupported(20170307090947,El); end; +procedure TPasAnalyzer.UsePublished(El: TPasElement); +// mark typeinfo, do not +var + C: TClass; + Members: TFPList; + i: Integer; + Member: TPasElement; + MemberResolved: TPasResolverResult; + Prop: TPasProperty; + ProcType: TPasProcedureType; +begin + {$IFDEF VerbosePasAnalyzer} + writeln('TPasAnalyzer.UsePublished START ',GetObjName(El)); + {$ENDIF} + if ElementVisited(El,paumPublished) then exit; + C:=El.ClassType; + if C=TPasUnresolvedSymbolRef then + else if (C=TPasVariable) or (C=TPasConst) then + UsePublished(TPasVariable(El).VarType) + else if C=TPasProperty then + begin + // published property + Prop:=TPasProperty(El); + for i:=0 to Prop.Args.Count-1 do + UsePublished(TPasArgument(Prop.Args[i]).ArgType); + UsePublished(Prop.VarType); + // Note: read, write and index don't need extra typeinfo + + // stored and defaultvalue are only used when published -> mark as used + UseElement(Prop.StoredAccessor,rraRead,false); + UseElement(Prop.DefaultExpr,rraRead,false); + end + else if (C=TPasAliasType) or (C=TPasTypeAliasType) then + UsePublished(TPasAliasType(El).DestType) + else if C=TPasEnumType then + else if C=TPasSetType then + UsePublished(TPasSetType(El).EnumType) + else if C=TPasArrayType then + begin + UsePublished(TPasArrayType(El).ElType); + for i:=0 to length(TPasArrayType(El).Ranges)-1 do + begin + Member:=TPasArrayType(El).Ranges[i]; + Resolver.ComputeElement(Member,MemberResolved,[rcConstant]); + UsePublished(MemberResolved.TypeEl); + end; + end + else if C=TPasPointerType then + UsePublished(TPasPointerType(El).DestType) + else if C=TPasClassType then + else if C=TPasClassOfType then + else if C=TPasRecordType then + begin + // published record: use all members + Members:=TPasRecordType(El).Members; + for i:=0 to Members.Count-1 do + begin + Member:=TPasElement(Members[i]); + UsePublished(Member); + UseElement(Member,rraNone,true); + end; + end + else if C.InheritsFrom(TPasProcedure) then + UsePublished(TPasProcedure(El).ProcType) + else if C.InheritsFrom(TPasProcedureType) then + begin + ProcType:=TPasProcedureType(El); + for i:=0 to ProcType.Args.Count-1 do + UsePublished(TPasArgument(ProcType.Args[i]).ArgType); + if El is TPasFunctionType then + UsePublished(TPasFunctionType(El).ResultEl.ResultType); + end + else + RaiseNotSupported(20170414153904,El); +end; + procedure TPasAnalyzer.UseModule(aModule: TPasModule; Mode: TPAUseMode); procedure UseInitFinal(aSection: TPasImplBlock); @@ -877,18 +955,22 @@ var C: TClass; Params: TPasExprArray; i: Integer; + BuiltInProc: TResElDataBuiltInProc; + ParamResolved: TPasResolverResult; + Decl: TPasElement; begin if El=nil then exit; // expressions are not marked + Ref:=nil; if El.CustomData is TResolvedReference then begin // this is a reference -> mark target Ref:=TResolvedReference(El.CustomData); - UseElement(Ref.Declaration,Ref.Access,false); + Decl:=Ref.Declaration; + UseElement(Decl,Ref.Access,false); - if (El.ClassType=TSelfExpr) - or ((El.ClassType=TPrimitiveExpr) and (TPrimitiveExpr(El).Kind=pekIdent)) then + if Resolver.IsNameExpr(El) then begin if Ref.WithExprScope<>nil then begin @@ -899,12 +981,12 @@ begin exit; end; end; - if (Ref.Declaration is TPasVariable) + if (Decl is TPasVariable) and (El.Parent is TBinaryExpr) and (TBinaryExpr(El.Parent).right=El) then begin - if ((Ref.Declaration.Parent is TPasRecordType) - or (Ref.Declaration.Parent is TPasVariant)) then + if ((Decl.Parent is TPasRecordType) + or (Decl.Parent is TPasVariant)) then begin // a record member was accessed -> access the record too UseExprRef(TBinaryExpr(El.Parent).left,Ref.Access,false); @@ -912,6 +994,20 @@ begin end; end; + if Decl is TPasUnresolvedSymbolRef then + begin + if Decl.CustomData is TResElDataBuiltInProc then + begin + BuiltInProc:=TResElDataBuiltInProc(Decl.CustomData); + if BuiltInProc.BuiltIn=bfTypeInfo then + begin + Params:=(El.Parent as TParamsExpr).Params; + Resolver.ComputeElement(Params[0],ParamResolved,[]); + UsePublished(ParamResolved.IdentEl); + end; + end; + end; + end; UseExpr(El.format1); UseExpr(El.format2); @@ -1039,7 +1135,7 @@ begin if ImplProc.Body<>nil then UseImplBlock(ImplProc.Body.Body,false); - if ProcScope.OverriddenProc<>nil then + if Proc.IsOverride and (ProcScope.OverriddenProc<>nil) then AddOverride(ProcScope.OverriddenProc,Proc); // mark overrides @@ -1158,7 +1254,7 @@ procedure TPasAnalyzer.UseClassType(El: TPasClassType; Mode: TPAUseMode); var i: Integer; Member: TPasElement; - UsePublished, FirstTime: Boolean; + AllPublished, FirstTime: Boolean; ProcScope: TPasProcedureScope; ClassScope: TPasClassScope; Ref: TResolvedReference; @@ -1178,6 +1274,8 @@ begin end; paumElement: if not MarkElementAsUsed(El) then exit; + else + RaiseInconsistency(20170414152143,IntToStr(ord(Mode))); end; {$IFDEF VerbosePasAnalyzer} writeln('TPasAnalyzer.UseClassType ',GetElModName(El),' ',Mode,' First=',FirstTime); @@ -1199,20 +1297,30 @@ begin UseType(TPasType(El.Interfaces[i]),paumElement); end; // members - UsePublished:=(Mode<>paumAllExports) and (paoKeepPublished in Options); + AllPublished:=(Mode<>paumAllExports); for i:=0 to El.Members.Count-1 do begin Member:=TPasElement(El.Members[i]); if FirstTime and (Member is TPasProcedure) then begin ProcScope:=Member.CustomData as TPasProcedureScope; - if ProcScope.OverriddenProc<>nil then + if TPasProcedure(Member).IsOverride and (ProcScope.OverriddenProc<>nil) then + begin + // this is an override AddOverride(ProcScope.OverriddenProc,Member); + if ScopeModule<>nil then + begin + // when analyzingf a single module, all overrides are assumed to be called + UseElement(Member,rraNone,true); + continue; + end; + end; end; - if UsePublished and (Member.Visibility=visPublished) then + if AllPublished and (Member.Visibility=visPublished) then begin // include published if not FirstTime then continue; + UsePublished(Member); end else if Mode=paumElement then continue @@ -1763,6 +1871,11 @@ begin Result:=FindElement(El)<>nil; end; +function TPasAnalyzer.IsTypeInfoUsed(El: TPasElement): boolean; +begin + Result:=FChecked[paumPublished].Find(El)<>nil; +end; + function TPasAnalyzer.IsModuleInternal(El: TPasElement): boolean; begin if El=nil then @@ -1772,7 +1885,7 @@ begin if IsExport(El) then exit(false); case El.Visibility of visPrivate,visStrictPrivate: exit(true); - visPublished: if paoKeepPublished in Options then exit(false); + visPublished: exit(false); end; Result:=IsModuleInternal(El.Parent); end; diff --git a/packages/fcl-passrc/src/pparser.pp b/packages/fcl-passrc/src/pparser.pp index 170575011d..a8aa7e113c 100644 --- a/packages/fcl-passrc/src/pparser.pp +++ b/packages/fcl-passrc/src/pparser.pp @@ -245,6 +245,7 @@ type function GetVariableModifiers(Parent: TPasElement; Out VarMods: TVariableModifiers; Out LibName, ExportName: TPasExpr; ExternalClass : Boolean): string; function GetVariableValueAndLocation(Parent : TPasElement; Out Value : TPasExpr; Out Location: String): Boolean; procedure HandleProcedureModifier(Parent: TPasElement; pm : TProcedureModifier); + procedure HandleProcedureTypeModifier(ProcType: TPasProcedureType; ptm : TProcTypeModifier); procedure ParseClassLocalConsts(AType: TPasClassType; AVisibility: TPasMemberVisibility); procedure ParseClassLocalTypes(AType: TPasClassType; AVisibility: TPasMemberVisibility); procedure ParseVarList(Parent: TPasElement; VarList: TFPList; AVisibility: TPasMemberVisibility; Full: Boolean); @@ -299,8 +300,9 @@ type function CreateRecordValues(AParent : TPasElement): TRecordValues; Function IsCurTokenHint(out AHint : TPasMemberHint) : Boolean; overload; Function IsCurTokenHint: Boolean; overload; - Function TokenIsCallingConvention(S : String; out CC : TCallingConvention) : Boolean; virtual; - Function TokenIsProcedureModifier(Parent : TPasElement; S : String; Out Pm : TProcedureModifier) : Boolean; virtual; + Function TokenIsCallingConvention(const S : String; out CC : TCallingConvention) : Boolean; virtual; + Function TokenIsProcedureModifier(Parent : TPasElement; const S : String; Out PM : TProcedureModifier) : Boolean; virtual; + Function TokenIsProcedureTypeModifier(Parent : TPasElement; const S : String; Out PTM : TProcTypeModifier) : Boolean; virtual; Function CheckHint(Element : TPasElement; ExpectSemiColon : Boolean) : TPasMemberHints; function ParseParams(AParent : TPasElement;paramskind: TPasExprKind; AllowFormatting : Boolean = False): TParamsExpr; function ParseExpIdent(AParent : TPasElement): TPasExpr; @@ -401,7 +403,7 @@ function ParseSource(AEngine: TPasTreeContainer; const FPCCommandLine, OSTarget, CPUTarget: String; UseStreams : Boolean = False): TPasModule; Function IsHintToken(T : String; Out AHint : TPasMemberHint) : boolean; -Function IsModifier(S : String; Out Pm : TProcedureModifier) : Boolean; +Function IsProcModifier(S : String; Out PM : TProcedureModifier) : Boolean; Function IsCallingConvention(S : String; out CC : TCallingConvention) : Boolean; Function TokenToAssignKind( tk : TToken) : TAssignKind; @@ -459,9 +461,7 @@ begin end; end; - -Function IsModifier(S : String; Out Pm : TProcedureModifier) : Boolean; - +Function IsProcModifier(S : String; Out PM : TProcedureModifier) : Boolean; Var P : TProcedureModifier; @@ -924,17 +924,17 @@ begin Result:=IsCurTokenHint(dummy); end; -function TPasParser.TokenIsCallingConvention(S: String; out +function TPasParser.TokenIsCallingConvention(const S: String; out CC: TCallingConvention): Boolean; begin Result:=IsCallingConvention(S,CC); end; -function TPasParser.TokenIsProcedureModifier(Parent: TPasElement; S: String; - out Pm: TProcedureModifier): Boolean; +function TPasParser.TokenIsProcedureModifier(Parent: TPasElement; + const S: String; out PM: TProcedureModifier): Boolean; begin - Result:=IsModifier(S,PM); - if result and (pm in [pmPublic,pmForward]) then + Result:=IsProcModifier(S,PM); + if Result and (PM in [pmPublic,pmForward]) then begin While (Parent<>Nil) and Not ((Parent is TPasClassType) or (Parent is TPasRecordType)) do Parent:=Parent.Parent; @@ -942,6 +942,23 @@ begin end; end; +function TPasParser.TokenIsProcedureTypeModifier(Parent: TPasElement; + const S: String; out PTM: TProcTypeModifier): Boolean; +begin + if CompareText(S,ProcTypeModifiers[ptmVarargs])=0 then + begin + Result:=true; + PTM:=ptmVarargs; + end + else if CompareText(S,ProcTypeModifiers[ptmStatic])=0 then + begin + Result:=true; + PTM:=ptmStatic; + end + else + Result:=false; + if Parent=nil then; +end; function TPasParser.CheckHint(Element: TPasElement; ExpectSemiColon: Boolean ): TPasMemberHints; @@ -2601,6 +2618,7 @@ begin for i := 0 to List.Count - 1 do begin VarEl := TPasVariable(List[i]); + Engine.FinishScope(stDeclaration,VarEl); Declarations.Declarations.Add(VarEl); Declarations.Variables.Add(VarEl); end; @@ -3083,7 +3101,8 @@ end; // Full means that a full variable declaration is being parsed. -procedure TPasParser.ParseVarList(Parent: TPasElement; VarList: TFPList; AVisibility: TPasMemberVisibility; Full : Boolean); +procedure TPasParser.ParseVarList(Parent: TPasElement; VarList: TFPList; + AVisibility: TPasMemberVisibility; Full : Boolean); // on Exception the VarList is restored, no need to Release the new elements var @@ -3186,8 +3205,6 @@ begin aExpName.AddRef; end; end; - for i := OldListCount to VarList.Count - 1 do - Engine.FinishScope(stDeclaration,TPasVariable(VarList[i])); ok:=true; finally if aLibName<>nil then aLibName.Release; @@ -3441,8 +3458,6 @@ Var end; begin - if not (Parent is TPasProcedure) then - exit; P:=TPasProcedure(Parent); if pm<>pmPublic then AddModifier; @@ -3541,6 +3556,14 @@ begin end; // Case end; +procedure TPasParser.HandleProcedureTypeModifier(ProcType: TPasProcedureType; + ptm: TProcTypeModifier); +begin + if ptm in ProcType.Modifiers then + ParseExcSyntaxError; + Include(ProcType.Modifiers,ptm); +end; + // Next token is expected to be a "(", ";" or for a function ":". The caller // will get the token after the final ";" as next token. procedure TPasParser.ParseProcedureOrFunctionHeader(Parent: TPasElement; @@ -3608,12 +3631,16 @@ Var PM : TProcedureModifier; Done: Boolean; ResultEl: TPasResultElement; - OK : Boolean; + OK,IsProc : Boolean; + PTM: TProcTypeModifier; + ModCount: Integer; + LastToken: TToken; begin // Element must be non-nil. Removed all checks for not-nil. // If it is nil, the following fails anyway. CheckProcedureArgs(Element,Element.Args,ProcType in [ptOperator,ptClassOperator]); + IsProc:=Parent is TPasProcedure; case ProcType of ptFunction,ptClassFunction: begin @@ -3667,12 +3694,12 @@ begin if OfObjectPossible then begin NextToken; - if (curToken =tkOf) then + if (CurToken = tkOf) then begin ExpectToken(tkObject); Element.IsOfObject := True; end - else if (curToken = tkIs) then + else if (CurToken = tkIs) then begin expectToken(tkIdentifier); if (lowerCase(CurTokenString)<>'nested') then @@ -3682,18 +3709,23 @@ begin else UnGetToken; end; - NextToken; - if CurToken = tkEqual then - begin - // for example: const p: procedure = nil; - UngetToken; - exit; - end - else - UngetToken; + ModCount:=0; Repeat + inc(ModCount); + LastToken:=CurToken; NextToken; - If TokenIsCallingConvention(CurTokenString,cc) then + if (ModCount=1) and (CurToken = tkEqual) then + begin + // for example: const p: procedure = nil; + UngetToken; + exit; + end; + If CurToken=tkSemicolon then + begin + if LastToken=tkSemicolon then + ParseExcSyntaxError; + end + else if TokenIsCallingConvention(CurTokenString,cc) then begin Element.CallingConvention:=Cc; if cc = ccSysCall then @@ -3712,8 +3744,10 @@ begin end; ExpectToken(tkSemicolon); end - else if TokenIsProcedureModifier(Parent,CurTokenString,pm) then - HandleProcedureModifier(Parent,Pm) + else if IsProc and TokenIsProcedureModifier(Parent,CurTokenString,PM) then + HandleProcedureModifier(Parent,PM) + else if TokenIsProcedureTypeModifier(Parent,CurTokenString,PTM) then + HandleProcedureTypeModifier(Element,PTM) else if (CurToken=tklibrary) then // library is a token and a directive. begin Tok:=UpperCase(CurTokenString); @@ -3743,16 +3777,17 @@ begin until CurToken = tkSquaredBraceClose; ExpectToken(tkSemicolon); end - else if CurToken<>tkSemicolon then + else CheckToken(tkSemicolon); Done:=(CurToken=tkSemiColon); if Done then begin NextToken; Done:=Not ((Curtoken=tkSquaredBraceOpen) or - TokenIsProcedureModifier(Parent,CurtokenString,Pm) or - IscurtokenHint() or - TokenisCallingConvention(CurTokenString,cc) or + TokenIsProcedureModifier(Parent,CurtokenString,PM) or + TokenIsProcedureTypeModifier(Parent,CurtokenString,PTM) or + IsCurTokenHint() or + TokenIsCallingConvention(CurTokenString,cc) or (CurToken=tkIdentifier) and (CompareText(CurTokenText,'alias')=0)); // DumpCurToken('Done '+IntToStr(Ord(Done))); UngetToken; @@ -3852,7 +3887,7 @@ var begin Result:=TPasProperty(CreateElement(TPasProperty,AName,Parent,AVisibility)); if IsClassField then - Result.VarModifiers:=Result.VarModifiers+[vmClass]; + Include(Result.VarModifiers,vmClass); ok:=false; try NextToken; @@ -4103,7 +4138,7 @@ begin while True do begin NextToken; - WriteLn({$IFDEF VerbosePasParser}i,{$ENDIF}' Token=',CurTokenText); + // WriteLn({$IFDEF VerbosePasParser}i,{$ENDIF}' Token=',CurTokenText); case CurToken of tkasm: begin @@ -4760,6 +4795,7 @@ Var Cons : TPasConst; isClass : Boolean; NamePos: TPasSourcePos; + OldCount, i: Integer; begin v:=visDefault; isClass:=False; @@ -4818,7 +4854,10 @@ begin NextToken; Continue; end; + OldCount:=ARec.Members.Count; ParseInlineVarDecl(ARec, ARec.Members, v, AEndToken=tkBraceClose); + for i:=OldCount to ARec.Members.Count-1 do + Engine.FinishScope(stDeclaration,TPasVariable(ARec.Members[i])); end; tkCase : begin @@ -4946,6 +4985,7 @@ Var Element: TPasElement; I : Integer; isStatic : Boolean; + VarEl: TPasVariable; begin VarList := TFPList.Create; @@ -4966,10 +5006,12 @@ begin Element.Visibility := AVisibility; if (Element is TPasVariable) then begin + VarEl:=TPasVariable(Element); if IsClassField then - TPasVariable(Element).VarModifiers:=TPasVariable(Element).VarModifiers+[vmClass]; + Include(VarEl.VarModifiers,vmClass); if isStatic then - TPasVariable(Element).VarModifiers:=TPasVariable(Element).VarModifiers+[vmStatic]; + Include(VarEl.VarModifiers,vmStatic); + Engine.FinishScope(stDeclaration,VarEl); end; AType.Members.Add(Element); end; diff --git a/packages/fcl-passrc/tests/tcbaseparser.pas b/packages/fcl-passrc/tests/tcbaseparser.pas index e3e0569b93..733173f6f8 100644 --- a/packages/fcl-passrc/tests/tcbaseparser.pas +++ b/packages/fcl-passrc/tests/tcbaseparser.pas @@ -79,6 +79,7 @@ Type Procedure AssertEquals(Const Msg : String; AExpected, AActual: TPasMemberVisibility); overload; Procedure AssertEquals(Const Msg : String; AExpected, AActual: TProcedureModifier); overload; Procedure AssertEquals(Const Msg : String; AExpected, AActual: TProcedureModifiers); overload; + Procedure AssertEquals(Const Msg : String; AExpected, AActual: TProcTypeModifiers); overload; Procedure AssertEquals(Const Msg : String; AExpected, AActual: TAssignKind); overload; Procedure AssertEquals(Const Msg : String; AExpected, AActual: TProcedureMessageType); overload; Procedure AssertEquals(Const Msg : String; AExpected, AActual: TOperatorType); overload; @@ -850,6 +851,27 @@ begin end; procedure TTestParser.AssertEquals(const Msg: String; AExpected, + AActual: TProcTypeModifiers); + + Function Sn (S : TProcTypeModifiers) : String; + + Var + m : TProcTypeModifier; + begin + Result:=''; + For M:=Low(TProcTypeModifier) to High(TProcTypeModifier) do + If (m in S) then + begin + If (Result<>'') then + Result:=Result+','; + Result:=Result+GetEnumName(TypeInfo(TProcTypeModifier),Ord(m)) + end; + end; +begin + AssertEquals(Msg,Sn(AExpected),SN(AActual)); +end; + +procedure TTestParser.AssertEquals(const Msg: String; AExpected, AActual: TAssignKind); begin AssertEquals(Msg,GetEnumName(TypeInfo(TAssignKind),Ord(AExpected)), diff --git a/packages/fcl-passrc/tests/tcprocfunc.pas b/packages/fcl-passrc/tests/tcprocfunc.pas index db37a12ce4..1fb053c159 100644 --- a/packages/fcl-passrc/tests/tcprocfunc.pas +++ b/packages/fcl-passrc/tests/tcprocfunc.pas @@ -24,8 +24,8 @@ type AValue: String=''); procedure AssertArrayArg(ProcType: TPasProcedureType; AIndex: Integer; AName: String; AAccess: TArgumentAccess; const ElementTypeName: String); - procedure AssertFunc(Mods: TProcedureModifiers; CC: TCallingConvention; ArgCount: Integer; P: TPasFunction=nil); - procedure AssertProc(Mods: TProcedureModifiers; CC: TCallingConvention; ArgCount: Integer; P: TPasProcedure=nil); + procedure AssertFunc(const Mods: TProcedureModifiers; const TypeMods: TProcTypeModifiers; CC: TCallingConvention; ArgCount: Integer; P: TPasFunction=nil); + procedure AssertProc(const Mods: TProcedureModifiers; const TypeMods: TProcTypeModifiers; CC: TCallingConvention; ArgCount: Integer; P: TPasProcedure=nil); function BaseAssertArg(ProcType: TPasProcedureType; AIndex: Integer; AName: String; AAccess: TArgumentAccess; AValue: String=''): TPasArgument; procedure CreateForwardTest; @@ -269,13 +269,16 @@ begin CheckHint(TPasMemberHint(Getenumvalue(typeinfo(TPasMemberHint),'h'+Hint))); end; -procedure TTestProcedureFunction.AssertProc(Mods : TProcedureModifiers; CC : TCallingConvention; ArgCount : Integer; P : TPasProcedure = Nil); +procedure TTestProcedureFunction.AssertProc(const Mods: TProcedureModifiers; + const TypeMods: TProcTypeModifiers; CC: TCallingConvention; ArgCount: Integer; + P: TPasProcedure); begin If P=Nil then P:=Proc; AssertNotNull('No proc to assert',P); AssertEquals('Procedure modifiers',Mods,P.Modifiers); + AssertEquals('Procedure type modifiers',TypeMods,P.ProcType.Modifiers); AssertEquals('Procedue calling convention',CC,P.CallingConvention); AssertEquals('No message name','',p.MessageName); AssertEquals('No message type',pmtNone,P.MessageType); @@ -285,13 +288,16 @@ begin AssertEquals('Not is nested',False,P.ProcType.IsNested); end; -procedure TTestProcedureFunction.AssertFunc(Mods : TProcedureModifiers; CC : TCallingConvention; ArgCount : Integer; P : TPasFunction = Nil); +procedure TTestProcedureFunction.AssertFunc(const Mods: TProcedureModifiers; + const TypeMods: TProcTypeModifiers; CC: TCallingConvention; ArgCount: Integer; + P: TPasFunction); begin If P=Nil then P:=Func; AssertNotNull('No func to assert',P); AssertEquals('Procedure modifiers',Mods,P.Modifiers); + AssertEquals('Procedure type modifiers',TypeMods,P.ProcType.Modifiers); AssertEquals('Procedue calling convention',CC,P.CallingConvention); AssertEquals('No message name','',p.MessageName); AssertEquals('No message type',pmtNone,P.MessageType); @@ -384,7 +390,7 @@ end; procedure TTestProcedureFunction.TestEmptyProcedure; begin ParseProcedure(''); - AssertProc([],ccDefault,0); + AssertProc([],[],ccDefault,0); end; procedure TTestProcedureFunction.TestEmptyProcedureComment; @@ -396,7 +402,7 @@ end; procedure TTestProcedureFunction.TestEmptyFunction; begin ParseFunction(''); - AssertFunc([],ccDefault,0); + AssertFunc([],[],ccDefault,0); end; procedure TTestProcedureFunction.TestEmptyFunctionComment; @@ -408,50 +414,49 @@ end; procedure TTestProcedureFunction.TestEmptyProcedureDeprecated; begin ParseProcedure('','deprecated'); - AssertProc([],ccDefault,0); + AssertProc([],[],ccDefault,0); end; procedure TTestProcedureFunction.TestEmptyFunctionDeprecated; begin ParseFunction('','deprecated'); - AssertFunc([],ccDefault,0); + AssertFunc([],[],ccDefault,0); end; procedure TTestProcedureFunction.TestEmptyProcedurePlatform; begin ParseProcedure('','platform'); - AssertProc([],ccDefault,0); + AssertProc([],[],ccDefault,0); end; procedure TTestProcedureFunction.TestEmptyFunctionPlatform; begin ParseFunction('','platform'); - AssertFunc([],ccDefault,0); + AssertFunc([],[],ccDefault,0); end; procedure TTestProcedureFunction.TestEmptyProcedureExperimental; begin ParseProcedure('','experimental'); - AssertProc([],ccDefault,0); + AssertProc([],[],ccDefault,0); end; procedure TTestProcedureFunction.TestEmptyFunctionExperimental; begin ParseFunction('','experimental'); - AssertFunc([],ccDefault,0); + AssertFunc([],[],ccDefault,0); end; procedure TTestProcedureFunction.TestEmptyProcedureUnimplemented; begin ParseProcedure('','unimplemented'); - AssertProc([],ccDefault,0); + AssertProc([],[],ccDefault,0); end; procedure TTestProcedureFunction.TestEmptyFunctionUnimplemented; begin ParseFunction('','unimplemented'); - AssertFunc([],ccDefault,0); - + AssertFunc([],[],ccDefault,0); end; @@ -459,77 +464,77 @@ end; procedure TTestProcedureFunction.TestProcedureOneArg; begin ParseProcedure('(B : Integer)'); - AssertProc([],ccDefault,1); + AssertProc([],[],ccDefault,1); AssertArg(ProcType,0,'B',argDefault,'Integer',''); end; procedure TTestProcedureFunction.TestFunctionOneArg; begin ParseFunction('(B : Integer)'); - AssertFunc([],ccDefault,1); + AssertFunc([],[],ccDefault,1); AssertArg(FuncType,0,'B',argDefault,'Integer',''); end; procedure TTestProcedureFunction.TestProcedureOneVarArg; begin ParseProcedure('(Var B : Integer)'); - AssertProc([],ccDefault,1); + AssertProc([],[],ccDefault,1); AssertArg(ProcType,0,'B',argVar,'Integer',''); end; procedure TTestProcedureFunction.TestFunctionOneVarArg; begin ParseFunction('(Var B : Integer)'); - AssertFunc([],ccDefault,1); + AssertFunc([],[],ccDefault,1); AssertArg(FuncType,0,'B',argVar,'Integer',''); end; procedure TTestProcedureFunction.TestProcedureOneConstArg; begin ParseProcedure('(Const B : Integer)'); - AssertProc([],ccDefault,1); + AssertProc([],[],ccDefault,1); AssertArg(ProcType,0,'B',argConst,'Integer',''); end; procedure TTestProcedureFunction.TestFunctionOneConstArg; begin ParseFunction('(Const B : Integer)'); - AssertFunc([],ccDefault,1); + AssertFunc([],[],ccDefault,1); AssertArg(FuncType,0,'B',argConst,'Integer',''); end; procedure TTestProcedureFunction.TestProcedureOneOutArg; begin ParseProcedure('(Out B : Integer)'); - AssertProc([],ccDefault,1); + AssertProc([],[],ccDefault,1); AssertArg(ProcType,0,'B',argOut,'Integer',''); end; procedure TTestProcedureFunction.TestFunctionOneOutArg; begin ParseFunction('(Out B : Integer)'); - AssertFunc([],ccDefault,1); + AssertFunc([],[],ccDefault,1); AssertArg(FuncType,0,'B',argOut,'Integer',''); end; procedure TTestProcedureFunction.TestProcedureOneConstRefArg; begin ParseProcedure('(Constref B : Integer)'); - AssertProc([],ccDefault,1); + AssertProc([],[],ccDefault,1); AssertArg(ProcType,0,'B',argConstRef,'Integer',''); end; procedure TTestProcedureFunction.TestFunctionOneConstRefArg; begin ParseFunction('(ConstRef B : Integer)'); - AssertFunc([],ccDefault,1); + AssertFunc([],[],ccDefault,1); AssertArg(FuncType,0,'B',argConstref,'Integer',''); end; procedure TTestProcedureFunction.TestProcedureTwoArgs; begin ParseProcedure('(B,C : Integer)'); - AssertProc([],ccDefault,2); + AssertProc([],[],ccDefault,2); AssertArg(ProcType,0,'B',argDefault,'Integer',''); AssertArg(ProcType,1,'C',argDefault,'Integer',''); end; @@ -537,7 +542,7 @@ end; procedure TTestProcedureFunction.TestFunctionTwoArgs; begin ParseFunction('(B,C : Integer)'); - AssertFunc([],ccDefault,2); + AssertFunc([],[],ccDefault,2); AssertArg(FuncType,0,'B',argDefault,'Integer',''); AssertArg(FuncType,1,'C',argDefault,'Integer',''); end; @@ -545,7 +550,7 @@ end; procedure TTestProcedureFunction.TestProcedureTwoArgsSeparate; begin ParseProcedure('(B : Integer; C : Integer)'); - AssertProc([],ccDefault,2); + AssertProc([],[],ccDefault,2); AssertArg(ProcType,0,'B',argDefault,'Integer',''); AssertArg(ProcType,1,'C',argDefault,'Integer',''); end; @@ -553,7 +558,7 @@ end; procedure TTestProcedureFunction.TestFunctionTwoArgsSeparate; begin ParseFunction('(B : Integer;C : Integer)'); - AssertFunc([],ccDefault,2); + AssertFunc([],[],ccDefault,2); AssertArg(FuncType,0,'B',argDefault,'Integer',''); AssertArg(FuncType,1,'C',argDefault,'Integer',''); end; @@ -561,56 +566,56 @@ end; procedure TTestProcedureFunction.TestProcedureOneArgDefault; begin ParseProcedure('(B : Integer = 1)'); - AssertProc([],ccDefault,1); + AssertProc([],[],ccDefault,1); AssertArg(ProcType,0,'B',argDefault,'Integer','1'); end; procedure TTestProcedureFunction.TestFunctionOneArgDefault; begin ParseFunction('(B : Integer = 1)'); - AssertFunc([],ccDefault,1); + AssertFunc([],[],ccDefault,1); AssertArg(FuncType,0,'B',argDefault,'Integer','1'); end; procedure TTestProcedureFunction.TestFunctionOneArgEnumeratedExplicit; begin ParseFunction('(B : TSomeEnum = TSomeEnum.False)'); - AssertFunc([],ccDefault,1); + AssertFunc([],[],ccDefault,1); AssertArg(FuncType,0,'B',argDefault,'TSomeEnum','TSomeEnum.False'); end; procedure TTestProcedureFunction.TestProcedureOneArgDefaultSet; begin ParseProcedure('(B : MySet = [1,2])'); - AssertProc([],ccDefault,1); + AssertProc([],[],ccDefault,1); AssertArg(ProcType,0,'B',argDefault,'MySet','[1, 2]'); end; procedure TTestProcedureFunction.TestFunctionOneArgDefaultSet; begin ParseFunction('(B : MySet = [1,2])'); - AssertFunc([],ccDefault,1); + AssertFunc([],[],ccDefault,1); AssertArg(FuncType,0,'B',argDefault,'MySet','[1, 2]'); end; procedure TTestProcedureFunction.TestProcedureOneArgDefaultExpr; begin ParseProcedure('(B : Integer = 1 + 2)'); - AssertProc([],ccDefault,1); + AssertProc([],[],ccDefault,1); AssertArg(ProcType,0,'B',argDefault,'Integer','1 + 2'); end; procedure TTestProcedureFunction.TestFunctionOneArgDefaultExpr; begin ParseFunction('(B : Integer = 1 + 2)'); - AssertFunc([],ccDefault,1); + AssertFunc([],[],ccDefault,1); AssertArg(FuncType,0,'B',argDefault,'Integer','1 + 2'); end; procedure TTestProcedureFunction.TestProcedureTwoArgsDefault; begin ParseProcedure('(B : Integer = 1; C : Integer = 2)'); - AssertProc([],ccDefault,2); + AssertProc([],[],ccDefault,2); AssertArg(ProcType,0,'B',argDefault,'Integer','1'); AssertArg(ProcType,1,'C',argDefault,'Integer','2'); end; @@ -618,7 +623,7 @@ end; procedure TTestProcedureFunction.TestFunctionTwoArgsDefault; begin ParseFunction('(B : Integer = 1; C : Integer = 2)'); - AssertFunc([],ccDefault,2); + AssertFunc([],[],ccDefault,2); AssertArg(FuncType,0,'B',argDefault,'Integer','1'); AssertArg(FuncType,1,'C',argDefault,'Integer','2'); end; @@ -626,21 +631,21 @@ end; procedure TTestProcedureFunction.TestProcedureOneUntypedVarArg; begin ParseProcedure('(Var B)'); - AssertProc([],ccDefault,1); + AssertProc([],[],ccDefault,1); AssertArg(ProcType,0,'B',argVar,'',''); end; procedure TTestProcedureFunction.TestFunctionOneUntypedVarArg; begin ParseFunction('(Var B)'); - AssertFunc([],ccDefault,1); + AssertFunc([],[],ccDefault,1); AssertArg(FuncType,0,'B',argVar,'',''); end; procedure TTestProcedureFunction.TestProcedureTwoUntypedVarArgs; begin ParseProcedure('(Var B; Var C)'); - AssertProc([],ccDefault,2); + AssertProc([],[],ccDefault,2); AssertArg(ProcType,0,'B',argVar,'',''); AssertArg(ProcType,1,'C',argVar,'',''); end; @@ -648,7 +653,7 @@ end; procedure TTestProcedureFunction.TestFunctionTwoUntypedVarArgs; begin ParseFunction('(Var B; Var C)'); - AssertFunc([],ccDefault,2); + AssertFunc([],[],ccDefault,2); AssertArg(FuncType,0,'B',argVar,'',''); AssertArg(FuncType,1,'C',argVar,'',''); end; @@ -656,21 +661,21 @@ end; procedure TTestProcedureFunction.TestProcedureOneUntypedConstArg; begin ParseProcedure('(Const B)'); - AssertProc([],ccDefault,1); + AssertProc([],[],ccDefault,1); AssertArg(ProcType,0,'B',argConst,'',''); end; procedure TTestProcedureFunction.TestFunctionOneUntypedConstArg; begin ParseFunction('(Const B)'); - AssertFunc([],ccDefault,1); + AssertFunc([],[],ccDefault,1); AssertArg(FuncType,0,'B',argConst,'',''); end; procedure TTestProcedureFunction.TestProcedureTwoUntypedConstArgs; begin ParseProcedure('(Const B; Const C)'); - AssertProc([],ccDefault,2); + AssertProc([],[],ccDefault,2); AssertArg(ProcType,0,'B',argConst,'',''); AssertArg(ProcType,1,'C',argConst,'',''); end; @@ -678,7 +683,7 @@ end; procedure TTestProcedureFunction.TestFunctionTwoUntypedConstArgs; begin ParseFunction('(Const B; Const C)'); - AssertFunc([],ccDefault,2); + AssertFunc([],[],ccDefault,2); AssertArg(FuncType,0,'B',argConst,'',''); AssertArg(FuncType,1,'C',argConst,'',''); end; @@ -686,21 +691,21 @@ end; procedure TTestProcedureFunction.TestProcedureOpenArrayArg; begin ParseProcedure('(B : Array of Integer)'); - AssertProc([],ccDefault,1); + AssertProc([],[],ccDefault,1); AssertArrayArg(ProcType,0,'B',argDefault,'Integer'); end; procedure TTestProcedureFunction.TestFunctionOpenArrayArg; begin ParseFunction('(B : Array of Integer)'); - AssertFunc([],ccDefault,1); + AssertFunc([],[],ccDefault,1); AssertArrayArg(FuncType,0,'B',argDefault,'Integer'); end; procedure TTestProcedureFunction.TestProcedureTwoOpenArrayArgs; begin ParseProcedure('(B : Array of Integer;C : Array of Integer)'); - AssertProc([],ccDefault,2); + AssertProc([],[],ccDefault,2); AssertArrayArg(ProcType,0,'B',argDefault,'Integer'); AssertArrayArg(ProcType,1,'C',argDefault,'Integer'); end; @@ -708,7 +713,7 @@ end; procedure TTestProcedureFunction.TestFunctionTwoOpenArrayArgs; begin ParseFunction('(B : Array of Integer;C : Array of Integer)'); - AssertFunc([],ccDefault,2); + AssertFunc([],[],ccDefault,2); AssertArrayArg(FuncType,0,'B',argDefault,'Integer'); AssertArrayArg(FuncType,1,'C',argDefault,'Integer'); end; @@ -716,142 +721,142 @@ end; procedure TTestProcedureFunction.TestProcedureConstOpenArrayArg; begin ParseProcedure('(Const B : Array of Integer)'); - AssertProc([],ccDefault,1); + AssertProc([],[],ccDefault,1); AssertArrayArg(ProcType,0,'B',argConst,'Integer'); end; procedure TTestProcedureFunction.TestFunctionConstOpenArrayArg; begin ParseFunction('(Const B : Array of Integer)'); - AssertFunc([],ccDefault,1); + AssertFunc([],[],ccDefault,1); AssertArrayArg(FuncType,0,'B',argConst,'Integer'); end; procedure TTestProcedureFunction.TestProcedureVarOpenArrayArg; begin ParseProcedure('(Var B : Array of Integer)'); - AssertProc([],ccDefault,1); + AssertProc([],[],ccDefault,1); AssertArrayArg(ProcType,0,'B',argVar,'Integer'); end; procedure TTestProcedureFunction.TestFunctionVarOpenArrayArg; begin ParseFunction('(Var B : Array of Integer)'); - AssertFunc([],ccDefault,1); + AssertFunc([],[],ccDefault,1); AssertArrayArg(FuncType,0,'B',argVar,'Integer'); end; procedure TTestProcedureFunction.TestProcedureArrayOfConstArg; begin ParseProcedure('(B : Array of Const)'); - AssertProc([],ccDefault,1); + AssertProc([],[],ccDefault,1); AssertArrayArg(ProcType,0,'B',argDefault,''); end; procedure TTestProcedureFunction.TestFunctionArrayOfConstArg; begin ParseFunction('(B : Array of Const)'); - AssertFunc([],ccDefault,1); + AssertFunc([],[],ccDefault,1); AssertArrayArg(FuncType,0,'B',argDefault,''); end; procedure TTestProcedureFunction.TestProcedureConstArrayOfConstArg; begin ParseProcedure('(Const B : Array of Const)'); - AssertProc([],ccDefault,1); + AssertProc([],[],ccDefault,1); AssertArrayArg(ProcType,0,'B',argConst,''); end; procedure TTestProcedureFunction.TestFunctionConstArrayOfConstArg; begin ParseFunction('(Const B : Array of Const)'); - AssertFunc([],ccDefault,1); + AssertFunc([],[],ccDefault,1); AssertArrayArg(FuncType,0,'B',argConst,''); end; procedure TTestProcedureFunction.TestProcedureCdecl; begin ParseProcedure('; cdecl'); - AssertProc([],ccCdecl,0); + AssertProc([],[],ccCdecl,0); end; procedure TTestProcedureFunction.TestFunctionCdecl; begin ParseFunction('','','',ccCdecl); - AssertFunc([],ccCdecl,0); + AssertFunc([],[],ccCdecl,0); end; procedure TTestProcedureFunction.TestProcedureCdeclDeprecated; begin ParseProcedure('; cdecl;','deprecated'); - AssertProc([],ccCdecl,0); + AssertProc([],[],ccCdecl,0); end; procedure TTestProcedureFunction.TestFunctionCdeclDeprecated; begin ParseFunction('','','deprecated',ccCdecl); - AssertFunc([],ccCdecl,0); + AssertFunc([],[],ccCdecl,0); end; procedure TTestProcedureFunction.TestProcedureSafeCall; begin ParseProcedure('; safecall;',''); - AssertProc([],ccSafeCall,0); + AssertProc([],[],ccSafeCall,0); end; procedure TTestProcedureFunction.TestFunctionSafeCall; begin ParseFunction('','','',ccSafecall); - AssertFunc([],ccSafecall,0); + AssertFunc([],[],ccSafecall,0); end; procedure TTestProcedureFunction.TestProcedurePascal; begin ParseProcedure('; pascal;',''); - AssertProc([],ccPascal,0); + AssertProc([],[],ccPascal,0); end; procedure TTestProcedureFunction.TestFunctionPascal; begin ParseFunction('','','',ccPascal); - AssertFunc([],ccPascal,0); + AssertFunc([],[],ccPascal,0); end; procedure TTestProcedureFunction.TestProcedureStdCall; begin ParseProcedure('; stdcall;',''); - AssertProc([],ccstdcall,0); + AssertProc([],[],ccstdcall,0); end; procedure TTestProcedureFunction.TestFunctionStdCall; begin ParseFunction('','','',ccStdCall); - AssertFunc([],ccStdCall,0); + AssertFunc([],[],ccStdCall,0); end; procedure TTestProcedureFunction.TestProcedureOldFPCCall; begin ParseProcedure('; oldfpccall;',''); - AssertProc([],ccoldfpccall,0); + AssertProc([],[],ccoldfpccall,0); end; procedure TTestProcedureFunction.TestFunctionOldFPCCall; begin ParseFunction('','','',ccOldFPCCall); - AssertFunc([],ccOldFPCCall,0); + AssertFunc([],[],ccOldFPCCall,0); end; procedure TTestProcedureFunction.TestProcedurePublic; begin ParseProcedure('; public name ''myfunc'';',''); - AssertProc([pmPublic],ccDefault,0); + AssertProc([pmPublic],[],ccDefault,0); AssertExpression('Public name',Proc.PublicName,pekString,'''myfunc'''); end; procedure TTestProcedureFunction.TestProcedurePublicIdent; begin ParseProcedure('; public name exportname;',''); - AssertProc([pmPublic],ccDefault,0); + AssertProc([pmPublic],[],ccDefault,0); AssertExpression('Public name',Proc.PublicName,pekIdent,'exportname'); end; @@ -859,14 +864,14 @@ procedure TTestProcedureFunction.TestFunctionPublic; begin AddDeclaration('function A : Integer; public name exportname'); ParseFunction; - AssertFunc([pmPublic],ccDefault,0); + AssertFunc([pmPublic],[],ccDefault,0); AssertExpression('Public name',Func.PublicName,pekIdent,'exportname'); end; procedure TTestProcedureFunction.TestProcedureCdeclPublic; begin ParseProcedure('; cdecl; public name exportname;',''); - AssertProc([pmPublic],ccCDecl,0); + AssertProc([pmPublic],[],ccCDecl,0); AssertExpression('Public name',Proc.PublicName,pekIdent,'exportname'); end; @@ -874,47 +879,47 @@ procedure TTestProcedureFunction.TestFunctionCdeclPublic; begin AddDeclaration('function A : Integer; cdecl; public name exportname'); ParseFunction; - AssertFunc([pmPublic],ccCDecl,0); + AssertFunc([pmPublic],[],ccCDecl,0); AssertExpression('Public name',Func.PublicName,pekIdent,'exportname'); end; procedure TTestProcedureFunction.TestProcedureOverload; begin ParseProcedure('; overload;',''); - AssertProc([pmOverload],ccDefault,0); + AssertProc([pmOverload],[],ccDefault,0); end; procedure TTestProcedureFunction.TestFunctionOverload; begin AddDeclaration('function A : Integer; overload'); ParseFunction; - AssertFunc([pmOverload],ccDefault,0); + AssertFunc([pmOverload],[],ccDefault,0); end; procedure TTestProcedureFunction.TestProcedureVarargs; begin ParseProcedure('; varargs;',''); - AssertProc([pmVarArgs],ccDefault,0); + AssertProc([],[ptmVarArgs],ccDefault,0); end; procedure TTestProcedureFunction.TestFunctionVarArgs; begin AddDeclaration('function A : Integer; varargs'); ParseFunction; - AssertFunc([pmVarArgs],ccDefault,0); + AssertFunc([],[ptmVarArgs],ccDefault,0); end; procedure TTestProcedureFunction.TestProcedureCDeclVarargs; begin ParseProcedure(';cdecl; varargs;',''); - AssertProc([pmVarArgs],ccCDecl,0); + AssertProc([],[ptmVarArgs],ccCDecl,0); end; procedure TTestProcedureFunction.TestFunctionCDeclVarArgs; begin AddDeclaration('function A : Integer; cdecl; varargs'); ParseFunction; - AssertFunc([pmVarArgs],ccCdecl,0); + AssertFunc([],[ptmVarArgs],ccCdecl,0); end; procedure TTestProcedureFunction.TestProcedureForwardInterface; @@ -934,7 +939,7 @@ begin UseImplementation:=True; AddDeclaration('procedure A; forward;'); ParseProcedure; - AssertProc([pmforward],ccDefault,0); + AssertProc([pmforward],[],ccDefault,0); end; procedure TTestProcedureFunction.TestFunctionForward; @@ -942,21 +947,21 @@ begin UseImplementation:=True; AddDeclaration('function A : integer; forward;'); ParseFunction; - AssertFunc([pmforward],ccDefault,0); + AssertFunc([pmforward],[],ccDefault,0); end; procedure TTestProcedureFunction.TestProcedureFar; begin AddDeclaration('procedure A; far;'); ParseProcedure; - AssertProc([pmfar],ccDefault,0); + AssertProc([pmfar],[],ccDefault,0); end; procedure TTestProcedureFunction.TestFunctionFar; begin AddDeclaration('function A : integer; far;'); ParseFunction; - AssertFunc([pmfar],ccDefault,0); + AssertFunc([pmfar],[],ccDefault,0); end; procedure TTestProcedureFunction.TestProcedureCdeclForward; @@ -964,7 +969,7 @@ begin UseImplementation:=True; AddDeclaration('procedure A; cdecl; forward;'); ParseProcedure; - AssertProc([pmforward],ccCDecl,0); + AssertProc([pmforward],[],ccCDecl,0); end; procedure TTestProcedureFunction.TestFunctionCDeclForward; @@ -972,97 +977,97 @@ begin UseImplementation:=True; AddDeclaration('function A : integer; cdecl; forward;'); ParseFunction; - AssertFunc([pmforward],ccCDecl,0); + AssertFunc([pmforward],[],ccCDecl,0); end; procedure TTestProcedureFunction.TestProcedureCompilerProc; begin ParseProcedure(';compilerproc;',''); - AssertProc([pmCompilerProc],ccDefault,0); + AssertProc([pmCompilerProc],[],ccDefault,0); end; procedure TTestProcedureFunction.TestProcedureNoReturn; begin ParseProcedure(';noreturn;',''); - AssertProc([pmnoreturn],ccDefault,0); + AssertProc([pmnoreturn],[],ccDefault,0); end; procedure TTestProcedureFunction.TestFunctionCompilerProc; begin AddDeclaration('function A : Integer; compilerproc'); ParseFunction; - AssertFunc([pmCompilerProc],ccDefault,0); + AssertFunc([pmCompilerProc],[],ccDefault,0); end; procedure TTestProcedureFunction.TestProcedureCDeclCompilerProc; begin ParseProcedure(';cdecl;compilerproc;',''); - AssertProc([pmCompilerProc],ccCDecl,0); + AssertProc([pmCompilerProc],[],ccCDecl,0); end; procedure TTestProcedureFunction.TestFunctionCDeclCompilerProc; begin AddDeclaration('function A : Integer; cdecl; compilerproc'); ParseFunction; - AssertFunc([pmCompilerProc],ccCDecl,0); + AssertFunc([pmCompilerProc],[],ccCDecl,0); end; procedure TTestProcedureFunction.TestProcedureAssembler; begin ParseProcedure(';assembler;',''); - AssertProc([pmAssembler],ccDefault,0); + AssertProc([pmAssembler],[],ccDefault,0); end; procedure TTestProcedureFunction.TestFunctionAssembler; begin AddDeclaration('function A : Integer; assembler'); ParseFunction; - AssertFunc([pmAssembler],ccDefault,0); + AssertFunc([pmAssembler],[],ccDefault,0); end; procedure TTestProcedureFunction.TestProcedureCDeclAssembler; begin ParseProcedure(';cdecl;assembler;',''); - AssertProc([pmAssembler],ccCDecl,0); + AssertProc([pmAssembler],[],ccCDecl,0); end; procedure TTestProcedureFunction.TestFunctionCDeclAssembler; begin AddDeclaration('function A : Integer; cdecl; assembler'); ParseFunction; - AssertFunc([pmAssembler],ccCDecl,0); + AssertFunc([pmAssembler],[],ccCDecl,0); end; procedure TTestProcedureFunction.TestProcedureExport; begin ParseProcedure(';export;',''); - AssertProc([pmExport],ccDefault,0); + AssertProc([pmExport],[],ccDefault,0); end; procedure TTestProcedureFunction.TestFunctionExport; begin AddDeclaration('function A : Integer; export'); ParseFunction; - AssertFunc([pmExport],ccDefault,0); + AssertFunc([pmExport],[],ccDefault,0); end; procedure TTestProcedureFunction.TestProcedureCDeclExport; begin ParseProcedure('cdecl;export;',''); - AssertProc([pmExport],ccCDecl,0); + AssertProc([pmExport],[],ccCDecl,0); end; procedure TTestProcedureFunction.TestFunctionCDeclExport; begin AddDeclaration('function A : Integer; cdecl; export'); ParseFunction; - AssertFunc([pmExport],ccCDecl,0); + AssertFunc([pmExport],[],ccCDecl,0); end; procedure TTestProcedureFunction.TestProcedureExternal; begin ParseProcedure(';external',''); - AssertProc([pmExternal],ccDefault,0); + AssertProc([pmExternal],[],ccDefault,0); AssertNull('No Library name expression',Proc.LibraryExpr); end; @@ -1070,7 +1075,7 @@ procedure TTestProcedureFunction.TestFunctionExternal; begin AddDeclaration('function A : Integer; external'); ParseFunction; - AssertFunc([pmExternal],ccDefault,0); + AssertFunc([pmExternal],[],ccDefault,0); AssertNull('No Library name expression',Func.LibraryExpr); end; @@ -1110,7 +1115,7 @@ end; procedure TTestProcedureFunction.TestProcedureExternalLibName; begin ParseProcedure(';external ''libname''',''); - AssertProc([pmExternal],ccDefault,0); + AssertProc([pmExternal],[],ccDefault,0); AssertExpression('Library name expression',Proc.LibraryExpr,pekString,'''libname'''); end; @@ -1118,14 +1123,14 @@ procedure TTestProcedureFunction.TestFunctionExternalLibName; begin AddDeclaration('function A : Integer; external ''libname'''); ParseFunction; - AssertFunc([pmExternal],ccDefault,0); + AssertFunc([pmExternal],[],ccDefault,0); AssertExpression('Library name expression',Func.LibraryExpr,pekString,'''libname'''); end; procedure TTestProcedureFunction.TestProcedureExternalLibNameName; begin ParseProcedure(';external ''libname'' name ''symbolname''',''); - AssertProc([pmExternal],ccDefault,0); + AssertProc([pmExternal],[],ccDefault,0); AssertExpression('Library name expression',Proc.LibraryExpr,pekString,'''libname'''); AssertExpression('Library symbol expression',Proc.LibrarySymbolName,pekString,'''symbolname'''); end; @@ -1134,7 +1139,7 @@ procedure TTestProcedureFunction.TestFunctionExternalLibNameName; begin AddDeclaration('function A : Integer; external ''libname'' name ''symbolname'''); ParseFunction; - AssertFunc([pmExternal],ccDefault,0); + AssertFunc([pmExternal],[],ccDefault,0); AssertExpression('Library name expression',Func.LibraryExpr,pekString,'''libname'''); AssertExpression('Library symbol expression',Func.LibrarySymbolName,pekString,'''symbolname'''); end; @@ -1142,7 +1147,7 @@ end; procedure TTestProcedureFunction.TestProcedureExternalName; begin ParseProcedure(';external name ''symbolname''',''); - AssertProc([pmExternal],ccDefault,0); + AssertProc([pmExternal],[],ccDefault,0); AssertNull('No Library name expression',Proc.LibraryExpr); AssertExpression('Library symbol expression',Proc.LibrarySymbolName,pekString,'''symbolname'''); end; @@ -1151,7 +1156,7 @@ procedure TTestProcedureFunction.TestFunctionExternalName; begin AddDeclaration('function A : Integer; external name ''symbolname'''); ParseFunction; - AssertFunc([pmExternal],ccDefault,0); + AssertFunc([pmExternal],[],ccDefault,0); AssertNull('No Library name expression',Func.LibraryExpr); AssertExpression('Library symbol expression',Func.LibrarySymbolName,pekString,'''symbolname'''); end; @@ -1159,7 +1164,7 @@ end; procedure TTestProcedureFunction.TestProcedureCdeclExternal; begin ParseProcedure('; cdecl; external',''); - AssertProc([pmExternal],ccCdecl,0); + AssertProc([pmExternal],[],ccCdecl,0); AssertNull('No Library name expression',Proc.LibraryExpr); end; @@ -1167,14 +1172,14 @@ procedure TTestProcedureFunction.TestFunctionCdeclExternal; begin AddDeclaration('function A : Integer; cdecl; external'); ParseFunction; - AssertFunc([pmExternal],ccCdecl,0); + AssertFunc([pmExternal],[],ccCdecl,0); AssertNull('No Library name expression',Func.LibraryExpr); end; procedure TTestProcedureFunction.TestProcedureCdeclExternalLibName; begin ParseProcedure('; cdecl; external ''libname''',''); - AssertProc([pmExternal],ccCdecl,0); + AssertProc([pmExternal],[],ccCdecl,0); AssertExpression('Library name expression',Proc.LibraryExpr,pekString,'''libname'''); end; @@ -1182,14 +1187,14 @@ procedure TTestProcedureFunction.TestFunctionCdeclExternalLibName; begin AddDeclaration('function A : Integer; cdecl; external ''libname'''); ParseFunction; - AssertFunc([pmExternal],ccCdecl,0); + AssertFunc([pmExternal],[],ccCdecl,0); AssertExpression('Library name expression',Func.LibraryExpr,pekString,'''libname'''); end; procedure TTestProcedureFunction.TestProcedureCdeclExternalLibNameName; begin ParseProcedure('; cdecl; external ''libname'' name ''symbolname''',''); - AssertProc([pmExternal],ccCdecl,0); + AssertProc([pmExternal],[],ccCdecl,0); AssertExpression('Library name expression',Proc.LibraryExpr,pekString,'''libname'''); AssertExpression('Library symbol expression',Proc.LibrarySymbolName,pekString,'''symbolname'''); end; @@ -1198,7 +1203,7 @@ procedure TTestProcedureFunction.TestFunctionCdeclExternalLibNameName; begin AddDeclaration('function A : Integer; cdecl; external ''libname'' name ''symbolname'''); ParseFunction; - AssertFunc([pmExternal],ccCdecl,0); + AssertFunc([pmExternal],[],ccCdecl,0); AssertExpression('Library name expression',Func.LibraryExpr,pekString,'''libname'''); AssertExpression('Library symbol expression',Func.LibrarySymbolName,pekString,'''symbolname'''); end; @@ -1206,7 +1211,7 @@ end; procedure TTestProcedureFunction.TestProcedureCdeclExternalName; begin ParseProcedure('; cdecl; external name ''symbolname''',''); - AssertProc([pmExternal],ccCdecl,0); + AssertProc([pmExternal],[],ccCdecl,0); AssertNull('No Library name expression',Proc.LibraryExpr); AssertExpression('Library symbol expression',Proc.LibrarySymbolName,pekString,'''symbolname'''); end; @@ -1215,7 +1220,7 @@ procedure TTestProcedureFunction.TestFunctionCdeclExternalName; begin AddDeclaration('function A : Integer; cdecl; external name ''symbolname'''); ParseFunction; - AssertFunc([pmExternal],ccCdecl,0); + AssertFunc([pmExternal],[],ccCdecl,0); AssertNull('No Library name expression',Func.LibraryExpr); AssertExpression('Library symbol expression',Func.LibrarySymbolName,pekString,'''symbolname'''); end; @@ -1224,7 +1229,7 @@ procedure TTestProcedureFunction.TestFunctionAlias; begin AddDeclaration('function A : Integer; alias: ''myalias'''); ParseFunction; - AssertFunc([],ccDefault,0); + AssertFunc([],[],ccDefault,0); AssertEquals('Alias name','''myalias''',Func.AliasName); end; @@ -1232,7 +1237,7 @@ procedure TTestProcedureFunction.TestProcedureAlias; begin AddDeclaration('Procedure A; Alias : ''myalias'''); ParseProcedure; - AssertProc([],ccDefault,0); + AssertProc([],[],ccDefault,0); AssertEquals('Alias name','''myalias''',Proc.AliasName); end; diff --git a/packages/fcl-passrc/tests/tcresolver.pas b/packages/fcl-passrc/tests/tcresolver.pas index a68b8605ce..9e591e1a57 100644 --- a/packages/fcl-passrc/tests/tcresolver.pas +++ b/packages/fcl-passrc/tests/tcresolver.pas @@ -179,12 +179,7 @@ type Procedure TestTypedConstWrongExprFail; Procedure TestVarWrongExprFail; Procedure TestArgWrongExprFail; - Procedure TestIncDec; - Procedure TestIncStringFail; Procedure TestVarExternal; - Procedure TestStr_BaseTypes; - Procedure TestStr_StringFail; - Procedure TestStr_CharFail; Procedure TestVarNoSemicolonBeginFail; // strings @@ -208,8 +203,11 @@ type Procedure TestEnumHighLow; Procedure TestEnumOrd; Procedure TestEnumPredSucc; + Procedure TestEnum_EqualNilFail; Procedure TestEnum_CastIntegerToEnum; Procedure TestEnum_Str; + Procedure TestSet_AnonymousEnumtype; + Procedure TestSet_AnonymousEnumtypeName; // operators Procedure TestPrgAssignment; @@ -233,10 +231,18 @@ type Procedure TestTypeCastDoubleToIntFail; Procedure TestTypeCastDoubleToBoolFail; Procedure TestTypeCastBooleanToDoubleFail; - Procedure TestHighLow; Procedure TestAssign_Access; Procedure TestAssignedIntFail; + // misc built-in functions + Procedure TestHighLow; + Procedure TestStr_BaseTypes; + Procedure TestStr_StringFail; + Procedure TestStr_CharFail; + Procedure TestIncDec; + Procedure TestIncStringFail; + Procedure TestTypeInfo; + // statements Procedure TestForLoop; Procedure TestStatements; @@ -305,6 +311,7 @@ type Procedure TestProc_Varargs; Procedure TestProc_ParameterExprAccess; Procedure TestProc_FunctionResult_DeclProc; + Procedure TestProc_TypeCastFunctionResult; // ToDo: fail builtin functions in constant with non const param // record @@ -391,7 +398,7 @@ type // Todo: Fail to use class.method in constant or type, e.g. const p = @o.doit; // published - Procedure TestClass_PublishedVarFail; + Procedure TestClass_PublishedClassVarFail; Procedure TestClass_PublishedClassPropertyFail; Procedure TestClass_PublishedClassFunctionFail; Procedure TestClass_PublishedOverloadFail; @@ -439,6 +446,8 @@ type Procedure TestPropertyWriteAccessorProc; Procedure TestPropertyTypeless; Procedure TestPropertyTypelessNoAncestorFail; + Procedure TestPropertyStoredAccessor; + Procedure TestPropertyStoredAccessorVarWrongType; Procedure TestPropertyStoredAccessorProcNotFunc; Procedure TestPropertyStoredAccessorFuncWrongResult; Procedure TestPropertyStoredAccessorFuncWrongArgCount; @@ -465,6 +474,7 @@ type Procedure TestDynArrayOfLongint; Procedure TestStaticArray; Procedure TestArrayOfArray; + Procedure TestArrayOfArray_NameAnonymous; Procedure TestFunctionReturningArray; Procedure TestArray_LowHigh; Procedure TestArray_AssignSameSignatureFail; @@ -520,6 +530,14 @@ type Procedure TestProcType_AsArgOtherUnit; Procedure TestProcType_Property; Procedure TestProcType_PropertyCallWrongArgFail; + Procedure TestProcType_Typecast; + + // pointer + Procedure TestPointer; + Procedure TestPointer_AssignPointerToClassFail; + Procedure TestPointer_TypecastToMethodTypeFail; + Procedure TestPointer_TypecastFromMethodTypeFail; + Procedure TestPointer_TypecastMethod_proMethodAddrAsPointer; end; function LinesToStr(Args: array of const): string; @@ -2002,30 +2020,6 @@ begin PasResolver.nIncompatibleTypesGotExpected); end; -procedure TTestResolver.TestIncDec; -begin - StartProgram(false); - Add('var'); - Add(' i: longint;'); - Add('begin'); - Add(' inc({#a_var}i);'); - Add(' inc({#b_var}i,2);'); - Add(' dec({#c_var}i);'); - Add(' dec({#d_var}i,3);'); - ParseProgram; - CheckAccessMarkers; -end; - -procedure TTestResolver.TestIncStringFail; -begin - StartProgram(false); - Add('var'); - Add(' i: string;'); - Add('begin'); - Add(' inc(i);'); - CheckResolverException('Incompatible type arg no. 1: Got "String", expected "integer"',PasResolver.nIncompatibleTypeArgNo); -end; - procedure TTestResolver.TestVarExternal; begin StartProgram(false); @@ -2035,74 +2029,6 @@ begin ParseProgram; end; -procedure TTestResolver.TestStr_BaseTypes; -begin - StartProgram(false); - Add('var'); - Add(' b: boolean;'); - Add(' i: longint;'); - Add(' i64: int64;'); - Add(' s: single;'); - Add(' d: double;'); - Add(' aString: string;'); - Add(' r: record end;'); - Add('begin'); - Add(' Str(b,{#a_var}aString);'); - Add(' Str(b:1,aString);'); - Add(' Str(b:i,aString);'); - Add(' Str(i,aString);'); - Add(' Str(i:2,aString);'); - Add(' Str(i:i64,aString);'); - Add(' Str(i64,aString);'); - Add(' Str(i64:3,aString);'); - Add(' Str(i64:i,aString);'); - Add(' Str(s,aString);'); - Add(' Str(d,aString);'); - Add(' Str(d:4,aString);'); - Add(' Str(d:4:5,aString);'); - Add(' Str(d:4:i,aString);'); - Add(' aString:=Str(b);'); - Add(' aString:=Str(i:3);'); - Add(' aString:=Str(d:3:4);'); - Add(' aString:=Str(b,i,d);'); - Add(' aString:=Str(s,''foo'');'); - Add(' aString:=Str(i,{#assign_read}aString);'); - Add(' while true do Str(i,{#whiledo_var}aString);'); - Add(' repeat Str(i,{#repeat_var}aString); until true;'); - Add(' if true then Str(i,{#ifthen_var}aString) else Str(i,{#ifelse_var}aString);'); - Add(' for i:=0 to 0 do Str(i,{#fordo_var}aString);'); - Add(' with r do Str(i,{#withdo_var}aString);'); - Add(' case Str(s,''caseexpr'') of'); - Add(' ''bar'': Str(i,{#casest_var}aString);'); - Add(' else Str(i,{#caseelse_var}aString);'); - Add(' end;'); - ParseProgram; - CheckAccessMarkers; -end; - -procedure TTestResolver.TestStr_StringFail; -begin - StartProgram(false); - Add('var'); - Add(' aString: string;'); - Add('begin'); - Add(' Str(aString,aString);'); - CheckResolverException('Incompatible type arg no. 1: Got "String", expected "boolean, integer, enum value"', - nIncompatibleTypeArgNo); -end; - -procedure TTestResolver.TestStr_CharFail; -begin - StartProgram(false); - Add('var'); - Add(' c: char;'); - Add(' aString: string;'); - Add('begin'); - Add(' Str(c,aString);'); - CheckResolverException('Incompatible type arg no. 1: Got "Char", expected "boolean, integer, enum value"', - nIncompatibleTypeArgNo); -end; - procedure TTestResolver.TestVarNoSemicolonBeginFail; begin StartProgram(false); @@ -2447,6 +2373,19 @@ begin ParseProgram; end; +procedure TTestResolver.TestEnum_EqualNilFail; +begin + StartProgram(false); + Add('type'); + Add(' TFlag = (red, green);'); + Add('var'); + Add(' f: TFlag;'); + Add('begin'); + Add(' if f=nil then ;'); + CheckResolverException('Incompatible types: got "TFlag" expected "Pointer"', + nIncompatibleTypesGotExpected); +end; + procedure TTestResolver.TestEnum_CastIntegerToEnum; begin StartProgram(false); @@ -2479,6 +2418,57 @@ begin ParseProgram; end; +procedure TTestResolver.TestSet_AnonymousEnumtype; +begin + StartProgram(false); + Add('type'); + Add(' TFlags = set of (red, green);'); + Add('const'); + Add(' favorite = red;'); + Add('var'); + Add(' f: TFlags;'); + Add(' i: longint;'); + Add('begin'); + Add(' Include(f,red);'); + Add(' Include(f,favorite);'); + Add(' i:=ord(red);'); + Add(' i:=ord(favorite);'); + Add(' i:=ord(low(TFlags));'); + Add(' i:=ord(low(f));'); + Add(' i:=ord(low(favorite));'); + Add(' i:=ord(high(TFlags));'); + Add(' i:=ord(high(f));'); + Add(' i:=ord(high(favorite));'); + Add(' f:=[green,favorite];'); + ParseProgram; +end; + +procedure TTestResolver.TestSet_AnonymousEnumtypeName; +begin + ResolverEngine.AnonymousElTypePostfix:='$enum'; + StartProgram(false); + Add('type'); + Add(' TFlags = set of (red, green);'); + Add('const'); + Add(' favorite = red;'); + Add('var'); + Add(' f: TFlags;'); + Add(' i: longint;'); + Add('begin'); + Add(' Include(f,red);'); + Add(' Include(f,favorite);'); + Add(' i:=ord(red);'); + Add(' i:=ord(favorite);'); + Add(' i:=ord(low(TFlags));'); + Add(' i:=ord(low(f));'); + Add(' i:=ord(low(favorite));'); + Add(' i:=ord(high(TFlags));'); + Add(' i:=ord(high(f));'); + Add(' i:=ord(high(favorite));'); + Add(' f:=[green,favorite];'); + ParseProgram; +end; + procedure TTestResolver.TestPrgAssignment; var El: TPasElement; @@ -2947,6 +2937,31 @@ begin CheckResolverException(sIllegalTypeConversionTo,PasResolver.nIllegalTypeConversionTo); end; +procedure TTestResolver.TestAssign_Access; +begin + StartProgram(false); + Parser.Options:=Parser.Options+[po_cassignments]; + Scanner.Options:=Scanner.Options+[po_cassignments]; + Add('var i: longint;'); + Add('begin'); + Add(' {#a1_assign}i:={#a2_read}i;'); + Add(' {#b1_readandassign}i+={#b2_read}i;'); + Add(' {#c1_readandassign}i-={#c2_read}i;'); + Add(' {#d1_readandassign}i*={#d2_read}i;'); + ParseProgram; + CheckAccessMarkers; +end; + +procedure TTestResolver.TestAssignedIntFail; +begin + StartProgram(false); + Add('var i: longint;'); + Add('begin'); + Add(' if Assigned(i) then ;'); + CheckResolverException('Incompatible type arg no. 1: Got "Longint", expected "class or array"', + nIncompatibleTypeArgNo); +end; + procedure TTestResolver.TestHighLow; begin StartProgram(false); @@ -2961,31 +2976,121 @@ begin ParseProgram; end; -procedure TTestResolver.TestAssign_Access; +procedure TTestResolver.TestStr_BaseTypes; begin StartProgram(false); - Parser.Options:=Parser.Options+[po_cassignments]; - Scanner.Options:=Scanner.Options+[po_cassignments]; - Add('var i: longint;'); + Add('var'); + Add(' b: boolean;'); + Add(' i: longint;'); + Add(' i64: int64;'); + Add(' s: single;'); + Add(' d: double;'); + Add(' aString: string;'); + Add(' r: record end;'); Add('begin'); - Add(' {#a1_assign}i:={#a2_read}i;'); - Add(' {#b1_readandassign}i+={#b2_read}i;'); - Add(' {#c1_readandassign}i-={#c2_read}i;'); - Add(' {#d1_readandassign}i*={#d2_read}i;'); + Add(' Str(b,{#a_var}aString);'); + Add(' Str(b:1,aString);'); + Add(' Str(b:i,aString);'); + Add(' Str(i,aString);'); + Add(' Str(i:2,aString);'); + Add(' Str(i:i64,aString);'); + Add(' Str(i64,aString);'); + Add(' Str(i64:3,aString);'); + Add(' Str(i64:i,aString);'); + Add(' Str(s,aString);'); + Add(' Str(d,aString);'); + Add(' Str(d:4,aString);'); + Add(' Str(d:4:5,aString);'); + Add(' Str(d:4:i,aString);'); + Add(' aString:=Str(b);'); + Add(' aString:=Str(i:3);'); + Add(' aString:=Str(d:3:4);'); + Add(' aString:=Str(b,i,d);'); + Add(' aString:=Str(s,''foo'');'); + Add(' aString:=Str(i,{#assign_read}aString);'); + Add(' while true do Str(i,{#whiledo_var}aString);'); + Add(' repeat Str(i,{#repeat_var}aString); until true;'); + Add(' if true then Str(i,{#ifthen_var}aString) else Str(i,{#ifelse_var}aString);'); + Add(' for i:=0 to 0 do Str(i,{#fordo_var}aString);'); + Add(' with r do Str(i,{#withdo_var}aString);'); + Add(' case Str(s,''caseexpr'') of'); + Add(' ''bar'': Str(i,{#casest_var}aString);'); + Add(' else Str(i,{#caseelse_var}aString);'); + Add(' end;'); ParseProgram; CheckAccessMarkers; end; -procedure TTestResolver.TestAssignedIntFail; +procedure TTestResolver.TestStr_StringFail; begin StartProgram(false); - Add('var i: longint;'); + Add('var'); + Add(' aString: string;'); Add('begin'); - Add(' if Assigned(i) then ;'); - CheckResolverException('Incompatible type arg no. 1: Got "Longint", expected "class or array"', + Add(' Str(aString,aString);'); + CheckResolverException('Incompatible type arg no. 1: Got "String", expected "boolean, integer, enum value"', + nIncompatibleTypeArgNo); +end; + +procedure TTestResolver.TestStr_CharFail; +begin + StartProgram(false); + Add('var'); + Add(' c: char;'); + Add(' aString: string;'); + Add('begin'); + Add(' Str(c,aString);'); + CheckResolverException('Incompatible type arg no. 1: Got "Char", expected "boolean, integer, enum value"', nIncompatibleTypeArgNo); end; +procedure TTestResolver.TestIncDec; +begin + StartProgram(false); + Add('var'); + Add(' i: longint;'); + Add('begin'); + Add(' inc({#a_var}i);'); + Add(' inc({#b_var}i,2);'); + Add(' dec({#c_var}i);'); + Add(' dec({#d_var}i,3);'); + ParseProgram; + CheckAccessMarkers; +end; + +procedure TTestResolver.TestIncStringFail; +begin + StartProgram(false); + Add('var'); + Add(' i: string;'); + Add('begin'); + Add(' inc(i);'); + CheckResolverException('Incompatible type arg no. 1: Got "String", expected "integer"',PasResolver.nIncompatibleTypeArgNo); +end; + +procedure TTestResolver.TestTypeInfo; +begin + StartProgram(false); + Add('type'); + Add(' integer = longint;'); + Add(' TRec = record'); + Add(' v: integer;'); + Add(' end;'); + Add('var'); + Add(' i: integer;'); + Add(' s: string;'); + Add(' p: pointer;'); + Add(' r: TRec;'); + Add('begin'); + Add(' p:=typeinfo(integer);'); + Add(' p:=typeinfo(longint);'); + Add(' p:=typeinfo(i);'); + Add(' p:=typeinfo(s);'); + Add(' p:=typeinfo(p);'); + Add(' p:=typeinfo(r.v);'); + ParseProgram; +end; + procedure TTestResolver.TestForLoop; begin StartProgram(false); @@ -4162,6 +4267,16 @@ begin end; end; +procedure TTestResolver.TestProc_TypeCastFunctionResult; +begin + StartProgram(false); + Add('function GetIt: longint; begin end;'); + Add('var s: smallint;'); + Add('begin'); + Add(' s:=smallint(GetIt);'); + ParseProgram; +end; + procedure TTestResolver.TestRecord; begin StartProgram(false); @@ -5211,8 +5326,8 @@ begin Add(' end;'); Add('begin'); Add(' if TObject.i=7 then ;'); - CheckResolverException(sCannotAccessThisMemberFromAClassReference, - PasResolver.nCannotAccessThisMemberFromAClassReference); + CheckResolverException(sCannotAccessThisMemberFromAX, + PasResolver.nCannotAccessThisMemberFromAX); end; procedure TTestResolver.TestClass_FuncReturningObjectMember; @@ -5958,13 +6073,13 @@ begin ParseProgram; end; -procedure TTestResolver.TestClass_PublishedVarFail; +procedure TTestResolver.TestClass_PublishedClassVarFail; begin StartProgram(false); Add('type'); Add(' TObject = class'); Add(' published'); - Add(' Id: longint;'); + Add(' class var Id: longint;'); Add(' end;'); Add('begin'); CheckResolverException(sSymbolCannotBePublished,nSymbolCannotBePublished); @@ -6204,8 +6319,8 @@ begin Add(' oc: TObjectClass;'); Add('begin'); Add(' oc.Id:=3;'); - CheckResolverException(sCannotAccessThisMemberFromAClassReference, - PasResolver.nCannotAccessThisMemberFromAClassReference); + CheckResolverException(sCannotAccessThisMemberFromAX, + PasResolver.nCannotAccessThisMemberFromAX); end; procedure TTestResolver.TestClassOfDotClassProc; @@ -6264,8 +6379,8 @@ begin Add(' oc: TObjectClass;'); Add('begin'); Add(' oc.ProcA;'); - CheckResolverException(sCannotAccessThisMemberFromAClassReference, - PasResolver.nCannotAccessThisMemberFromAClassReference); + CheckResolverException(sCannotAccessThisMemberFromAX, + PasResolver.nCannotAccessThisMemberFromAX); end; procedure TTestResolver.TestClassOfDotClassProperty; @@ -6311,8 +6426,8 @@ begin Add(' oc: TObjectClass;'); Add('begin'); Add(' if oc.A=3 then ;'); - CheckResolverException(sCannotAccessThisMemberFromAClassReference, - PasResolver.nCannotAccessThisMemberFromAClassReference); + CheckResolverException(sCannotAccessThisMemberFromAX, + PasResolver.nCannotAccessThisMemberFromAX); end; procedure TTestResolver.TestClass_ClassProcSelf; @@ -6775,6 +6890,35 @@ begin PasResolver.nNoPropertyFoundToOverride); end; +procedure TTestResolver.TestPropertyStoredAccessor; +begin + StartProgram(false); + Add('type'); + Add(' TObject = class'); + Add(' FBird: longint;'); + Add(' VStored: boolean;'); + Add(' function IsBirdStored: boolean; virtual; abstract;'); + Add(' property Bird: longint read FBird stored VStored;'); + Add(' property B: longint read FBird stored IsBirdStored;'); + Add(' end;'); + Add('begin'); + ParseProgram; +end; + +procedure TTestResolver.TestPropertyStoredAccessorVarWrongType; +begin + StartProgram(false); + Add('type'); + Add(' TObject = class'); + Add(' FB: longint;'); + Add(' BStored: longint;'); + Add(' property B: longint read FB stored BStored;'); + Add(' end;'); + Add('begin'); + CheckResolverException('Incompatible types: got "Longint" expected "Boolean"', + PasResolver.nIncompatibleTypesGotExpected); +end; + procedure TTestResolver.TestPropertyStoredAccessorProcNotFunc; begin StartProgram(false); @@ -7218,12 +7362,31 @@ begin Add('type'); Add(' TArrA = array[byte] of longint;'); Add(' TArrB = array[smallint] of TArrA;'); + Add(' TArrC = array of array of longint;'); Add('var'); Add(' b: TArrB;'); + Add(' c: TArrC;'); Add('begin'); Add(' b[1][2]:=5;'); Add(' b[1,2]:=5;'); Add(' if b[2,1]=b[0,1] then ;'); + Add(' c[3][4]:=c[5,6];'); + ParseProgram; +end; + +procedure TTestResolver.TestArrayOfArray_NameAnonymous; +begin + ResolverEngine.AnonymousElTypePostfix:='$array'; + StartProgram(false); + Add('type'); + Add(' TArrA = array of array of longint;'); + Add('var'); + Add(' a: TArrA;'); + Add('begin'); + Add(' a[1][2]:=5;'); + Add(' a[1,2]:=5;'); + Add(' if a[2,1]=a[0,1] then ;'); + Add(' a[3][4]:=a[5,6];'); ParseProgram; end; @@ -7979,7 +8142,7 @@ begin Add('var n: TNotifyEvent;'); Add('begin'); Add(' n:=@ProcA;'); - CheckResolverException('procedure type modifier "of object" mismatch', + CheckResolverException('procedure type modifier "of Object" mismatch', PasResolver.nXModifierMismatchY); end; @@ -7998,7 +8161,7 @@ begin Add(' o: TObject;'); Add('begin'); Add(' n:=@o.ProcA;'); - CheckResolverException('procedure type modifier "of object" mismatch', + CheckResolverException('procedure type modifier "of Object" mismatch', PasResolver.nXModifierMismatchY); end; @@ -8173,7 +8336,7 @@ begin Add('begin'); Add(' Button1.OnClick := App.BtnClickHandler();'); CheckResolverException( - 'Wrong number of parameters specified for call to "procedure BtnClickHandler(TObject) of object"', + 'Wrong number of parameters specified for call to "procedure BtnClickHandler(TObject) of Object"', nWrongNumberOfParametersForCallTo); end; @@ -8197,7 +8360,7 @@ begin Add('begin'); Add(' Button1.OnClick := @App.BtnClickHandler();'); CheckResolverException( - 'Wrong number of parameters specified for call to "procedure BtnClickHandler(TObject) of object"', + 'Wrong number of parameters specified for call to "procedure BtnClickHandler(TObject) of Object"', nWrongNumberOfParametersForCallTo); end; @@ -8407,6 +8570,122 @@ begin nIncompatibleTypeArgNo); end; +procedure TTestResolver.TestProcType_Typecast; +begin + StartProgram(false); + Add('type'); + Add(' TNotifyEvent = procedure(Sender: Pointer) of object;'); + Add(' TEvent = procedure of object;'); + Add(' TProcA = procedure(i: longint);'); + Add(' TFuncB = function(i, j: longint): longint;'); + Add('var'); + Add(' Notify: TNotifyEvent;'); + Add(' Event: TEvent;'); + Add(' ProcA: TProcA;'); + Add(' FuncB: TFuncB;'); + Add(' p: pointer;'); + Add('begin'); + Add(' Notify:=TNotifyEvent(Event);'); + Add(' Event:=TEvent(Event);'); + Add(' Event:=TEvent(Notify);'); + Add(' ProcA:=TProcA(FuncB);'); + Add(' FuncB:=TFuncB(FuncB);'); + Add(' FuncB:=TFuncB(ProcA);'); + Add(' ProcA:=TProcA(p);'); + Add(' FuncB:=TFuncB(p);'); + ParseProgram; +end; + +procedure TTestResolver.TestPointer; +begin + StartProgram(false); + Add('type'); + Add(' TObject = class end;'); + Add(' TClass = class of TObject;'); + Add(' TMyPtr = pointer;'); + Add(' TArrInt = array of longint;'); + Add(' TFunc = function: longint;'); + Add('procedure DoIt; begin end;'); + Add('var'); + Add(' p: TMyPtr;'); + Add(' Obj: TObject;'); + Add(' Cl: TClass;'); + Add(' a: tarrint;'); + Add(' f: TFunc;'); + Add('begin'); + Add(' p:=nil;'); + Add(' if p=nil then;'); + Add(' if nil=p then;'); + Add(' if Assigned(p) then;'); + Add(' p:=obj;'); + Add(' p:=cl;'); + Add(' p:=a;'); + Add(' p:=Pointer(f);'); + Add(' p:=@DoIt;'); + Add(' p:=Pointer(@DoIt)'); + Add(' obj:=TObject(p);'); + Add(' cl:=TClass(p);'); + Add(' a:=TArrInt(p);'); + ParseProgram; +end; + +procedure TTestResolver.TestPointer_AssignPointerToClassFail; +begin + StartProgram(false); + Add('type'); + Add(' TObject = class end;'); + Add('var'); + Add(' Obj: TObject;'); + Add(' p: pointer;'); + Add('begin'); + Add(' obj:=p;'); + CheckResolverException('Incompatible types: got "Pointer" expected "TObject"', + nIncompatibleTypesGotExpected); +end; + +procedure TTestResolver.TestPointer_TypecastToMethodTypeFail; +begin + StartProgram(false); + Add('type'); + Add(' TEvent = procedure of object;'); + Add('var'); + Add(' p: pointer;'); + Add(' e: TEvent;'); + Add('begin'); + Add(' e:=TEvent(p);'); + CheckResolverException('Illegal type conversion: "Pointer" to "procedure type of Object"', + nIllegalTypeConversionTo); +end; + +procedure TTestResolver.TestPointer_TypecastFromMethodTypeFail; +begin + StartProgram(false); + Add('type'); + Add(' TEvent = procedure of object;'); + Add('var'); + Add(' p: pointer;'); + Add(' e: TEvent;'); + Add('begin'); + Add(' p:=Pointer(e);'); + CheckResolverException('Illegal type conversion: "procedure type of Object" to "Pointer"', + nIllegalTypeConversionTo); +end; + +procedure TTestResolver.TestPointer_TypecastMethod_proMethodAddrAsPointer; +begin + ResolverEngine.Options:=ResolverEngine.Options+[proMethodAddrAsPointer]; + StartProgram(false); + Add('type'); + Add(' TEvent = procedure of object;'); + Add('var'); + Add(' p: pointer;'); + Add(' e: TEvent;'); + Add('begin'); + Add(' e:=TEvent(p);'); + Add(' p:=Pointer(e);'); + ParseProgram; +end; + initialization RegisterTests([TTestResolver]); diff --git a/packages/fcl-passrc/tests/tcuseanalyzer.pas b/packages/fcl-passrc/tests/tcuseanalyzer.pas index 7919996660..3c4cad5249 100644 --- a/packages/fcl-passrc/tests/tcuseanalyzer.pas +++ b/packages/fcl-passrc/tests/tcuseanalyzer.pas @@ -21,6 +21,7 @@ type private FAnalyzer: TPasAnalyzer; FPAMessages: TFPList; // list of TPAMessage + FPAGoodMessages: TFPList; function GetPAMessages(Index: integer): TPAMessage; procedure OnAnalyzerMessage(Sender: TObject; Msg: TPAMessage); protected @@ -32,8 +33,9 @@ type procedure AnalyzeWholeProgram; virtual; procedure CheckUsedMarkers; virtual; procedure CheckHasHint(MsgType: TMessageType; MsgNumber: integer; - const MsgText: string; Has: boolean = true); virtual; - procedure CheckUnitUsed(const aFilename: string; Used: boolean); + const MsgText: string); virtual; + procedure CheckUnexpectedMessages; virtual; + procedure CheckUnitUsed(const aFilename: string; Used: boolean); virtual; public property Analyzer: TPasAnalyzer read FAnalyzer; function PAMessageCount: integer; @@ -77,6 +79,7 @@ type procedure TestM_Hint_UnitNotUsed_No_OnlyExternal; procedure TestM_Hint_ParameterNotUsed; procedure TestM_Hint_ParameterNotUsed_Abstract; + procedure TestM_Hint_ParameterNotUsedTypecast; procedure TestM_Hint_LocalVariableNotUsed; procedure TestM_Hint_InterfaceUnitVariableUsed; procedure TestM_Hint_ValueParameterIsAssignedButNeverUsed; @@ -85,6 +88,7 @@ type procedure TestM_Hint_PrivateFieldIsNeverUsed; procedure TestM_Hint_PrivateFieldIsAssignedButNeverUsed; procedure TestM_Hint_PrivateMethodIsNeverUsed; + procedure TestM_Hint_LocalDestructor_No_IsNeverUsed; procedure TestM_Hint_PrivateTypeNeverUsed; procedure TestM_Hint_PrivateConstNeverUsed; procedure TestM_Hint_PrivatePropertyNeverUsed; @@ -106,6 +110,13 @@ type procedure TestWP_CallInherited; procedure TestWP_ProgramPublicDeclarations; procedure TestWP_ClassDefaultProperty; + procedure TestWP_Published; + procedure TestWP_PublishedSetType; + procedure TestWP_PublishedArrayType; + procedure TestWP_PublishedClassOfType; + procedure TestWP_PublishedRecordType; + procedure TestWP_PublishedProcType; + procedure TestWP_PublishedProperty; end; implementation @@ -128,6 +139,7 @@ procedure TCustomTestUseAnalyzer.SetUp; begin inherited SetUp; FPAMessages:=TFPList.Create; + FPAGoodMessages:=TFPList.Create; FAnalyzer:=TPasAnalyzer.Create; FAnalyzer.Resolver:=ResolverEngine; Analyzer.OnMessage:=@OnAnalyzerMessage; @@ -137,6 +149,7 @@ procedure TCustomTestUseAnalyzer.TearDown; var i: Integer; begin + FreeAndNil(FPAGoodMessages); for i:=0 to FPAMessages.Count-1 do TPAMessage(FPAMessages[i]).Release; FreeAndNil(FPAMessages); @@ -227,7 +240,7 @@ begin end; procedure TCustomTestUseAnalyzer.CheckHasHint(MsgType: TMessageType; - MsgNumber: integer; const MsgText: string; Has: boolean); + MsgNumber: integer; const MsgText: string); var i: Integer; Msg: TPAMessage; @@ -239,22 +252,14 @@ begin Msg:=PAMessages[i]; if (Msg.MsgNumber=MsgNumber) then begin - if Has then + if (Msg.MsgType=MsgType) and (Msg.MsgText=MsgText) then begin - // must have -> message type and text must match exactly - if (Msg.MsgType=MsgType) and (Msg.MsgText=MsgText) then - exit; - end - else - begin - // must not have -> matching number is enough - break; + FPAGoodMessages.Add(Msg); + exit; end; end; dec(i); end; - if (not Has) and (i<0) then exit; - // mismatch writeln('TCustomTestUseAnalyzer.CheckHasHint: '); for i:=0 to PAMessageCount-1 do @@ -264,7 +269,23 @@ begin end; s:=''; str(MsgType,s); - Fail('Analyzer Message '+BoolToStr(Has,'not ','')+'found: '+s+': ('+IntToStr(MsgNumber)+') {'+MsgText+'}'); + Fail('Analyzer Message not found: '+s+': ('+IntToStr(MsgNumber)+') {'+MsgText+'}'); +end; + +procedure TCustomTestUseAnalyzer.CheckUnexpectedMessages; +var + i: Integer; + Msg: TPAMessage; + s: String; +begin + for i:=0 to PAMessageCount-1 do + begin + Msg:=PAMessages[i]; + if FPAGoodMessages.IndexOf(Msg)>=0 then continue; + s:=''; + str(Msg.MsgType,s); + Fail('Analyzer Message found ['+IntToStr(Msg.Id)+'] '+s+': ('+IntToStr(Msg.MsgNumber)+') {'+Msg.MsgText+'}'); + end; end; procedure TCustomTestUseAnalyzer.CheckUnitUsed(const aFilename: string; @@ -749,7 +770,7 @@ begin Add(' {tmobile_used}TMobile = class(TObject)'); Add(' constructor {#mob_create_used}Create;'); Add(' procedure {#mob_doa_used}DoA; override;'); - Add(' procedure {#mob_dob_notused}DoB; override;'); + Add(' procedure {#mob_dob_used}DoB; override;'); Add(' end;'); Add('constructor TMobile.Create; begin end;'); Add('procedure TMobile.DoA; begin end;'); @@ -831,6 +852,7 @@ begin Add('begin'); AnalyzeProgram; CheckHasHint(mtHint,nPAUnitNotUsed,'Unit "unit2" not used in afile'); + CheckUnexpectedMessages; end; procedure TTestUseAnalyzer.TestM_Hint_UnitNotUsed_No_OnlyExternal; @@ -851,7 +873,7 @@ begin AnalyzeProgram; // unit hints: no hint, even though no code is actually used - CheckHasHint(mtHint,nPAUnitNotUsed,'Unit "unit2" not used in afile',false); + CheckUnexpectedMessages; end; procedure TTestUseAnalyzer.TestM_Hint_ParameterNotUsed; @@ -863,6 +885,7 @@ begin Add(' DoIt(1);'); AnalyzeProgram; CheckHasHint(mtHint,nPAParameterNotUsed,'Parameter "i" not used'); + CheckUnexpectedMessages; end; procedure TTestUseAnalyzer.TestM_Hint_ParameterNotUsed_Abstract; @@ -875,8 +898,28 @@ begin Add('begin'); Add(' TObject.DoIt(3);'); AnalyzeProgram; - CheckHasHint(mtHint,nPAParameterNotUsed, - sPAParameterNotUsed,false); + CheckUnexpectedMessages; +end; + +procedure TTestUseAnalyzer.TestM_Hint_ParameterNotUsedTypecast; +begin + StartProgram(true); + Add('type'); + Add(' TObject = class end;'); + Add(' TSortCompare = function(a,b: Pointer): integer;'); + Add(' TObjCompare = function(a,b: TObject): integer;'); + Add('procedure Sort(const Compare: TSortCompare);'); + Add('begin'); + Add(' Compare(nil,nil);'); + Add('end;'); + Add('procedure DoIt(const Compare: TObjCompare);'); + Add('begin'); + Add(' Sort(TSortCompare(Compare));'); + Add('end;'); + Add('begin'); + Add(' DoIt(nil);'); + AnalyzeProgram; + CheckUnexpectedMessages; end; procedure TTestUseAnalyzer.TestM_Hint_LocalVariableNotUsed; @@ -897,6 +940,7 @@ begin CheckHasHint(mtHint,nPALocalXYNotUsed,'Local constant "b" not used'); CheckHasHint(mtHint,nPALocalVariableNotUsed,'Local variable "c" not used'); CheckHasHint(mtHint,nPALocalVariableNotUsed,'Local variable "d" not used'); + CheckUnexpectedMessages; end; procedure TTestUseAnalyzer.TestM_Hint_InterfaceUnitVariableUsed; @@ -921,8 +965,14 @@ begin Add(' {#ImpTFlags_notused}ImpTFlags = set of TFlag;'); Add(' {#ImpTArrInt_notused}ImpTArrInt = array of integer;'); AnalyzeUnit; - CheckHasHint(mtHint,nPALocalVariableIsAssignedButNeverUsed, - 'Local variable "a" is assigned but never used',false); + CheckHasHint(mtHint,nPALocalXYNotUsed,'Local constant "d" not used'); + CheckHasHint(mtHint,nPALocalXYNotUsed,'Local constant "e" not used'); + CheckHasHint(mtHint,nPALocalVariableNotUsed,'Local variable "f" not used'); + CheckHasHint(mtHint,nPALocalXYNotUsed,'Local alias type "ImpTColor" not used'); + CheckHasHint(mtHint,nPALocalXYNotUsed,'Local enumeration type "ImpTFlag" not used'); + CheckHasHint(mtHint,nPALocalXYNotUsed,'Local set type "ImpTFlags" not used'); + CheckHasHint(mtHint,nPALocalXYNotUsed,'Local array type "ImpTArrInt" not used'); + CheckUnexpectedMessages; end; procedure TTestUseAnalyzer.TestM_Hint_ValueParameterIsAssignedButNeverUsed; @@ -937,6 +987,7 @@ begin AnalyzeProgram; CheckHasHint(mtHint,nPAValueParameterIsAssignedButNeverUsed, 'Value parameter "i" is assigned but never used'); + CheckUnexpectedMessages; end; procedure TTestUseAnalyzer.TestM_Hint_LocalVariableIsAssignedButNeverUsed; @@ -962,6 +1013,7 @@ begin 'Local variable "b" is assigned but never used'); CheckHasHint(mtHint,nPALocalVariableIsAssignedButNeverUsed, 'Local variable "c" is assigned but never used'); + CheckUnexpectedMessages; end; procedure TTestUseAnalyzer.TestM_Hint_LocalXYNotUsed; @@ -984,6 +1036,7 @@ begin CheckHasHint(mtHint,nPALocalXYNotUsed,'Local set type "TFlags" not used'); CheckHasHint(mtHint,nPALocalXYNotUsed,'Local array type "TArrInt" not used'); CheckHasHint(mtHint,nPALocalXYNotUsed,'Local procedure "Sub" not used'); + CheckUnexpectedMessages; end; procedure TTestUseAnalyzer.TestM_Hint_PrivateFieldIsNeverUsed; @@ -998,7 +1051,11 @@ begin Add('begin'); Add(' m:=nil;'); AnalyzeProgram; - CheckHasHint(mtHint,nPAPrivateFieldIsNeverUsed,'Private field "TMobile.a" is never used'); + CheckHasHint(mtHint,nPAPrivateFieldIsNeverUsed, + 'Private field "TMobile.a" is never used'); + CheckHasHint(mtHint,nPALocalVariableIsAssignedButNeverUsed, + 'Local variable "m" is assigned but never used'); + CheckUnexpectedMessages; end; procedure TTestUseAnalyzer.TestM_Hint_PrivateFieldIsAssignedButNeverUsed; @@ -1020,6 +1077,7 @@ begin AnalyzeProgram; CheckHasHint(mtHint,nPAPrivateFieldIsAssignedButNeverUsed, 'Private field "TMobile.a" is assigned but never used'); + CheckUnexpectedMessages; end; procedure TTestUseAnalyzer.TestM_Hint_PrivateMethodIsNeverUsed; @@ -1040,6 +1098,34 @@ begin AnalyzeProgram; CheckHasHint(mtHint,nPAPrivateMethodIsNeverUsed, 'Private method "TMobile.DoSome" is never used'); + CheckUnexpectedMessages; +end; + +procedure TTestUseAnalyzer.TestM_Hint_LocalDestructor_No_IsNeverUsed; +begin + StartProgram(true,[supTObject]); + Add('type'); + Add(' TMobile = class'); + Add(' private'); + Add(' public'); + Add(' constructor Create;'); + Add(' destructor Destroy; override;'); + Add(' end;'); + Add('var DestroyCount: longint = 0;'); + Add('constructor TMobile.Create;'); + Add('begin'); + Add('end;'); + Add('destructor TMobile.Destroy;'); + Add('begin'); + Add(' inc(DestroyCount);'); + Add(' inherited;'); + Add('end;'); + Add('var o: TObject;'); + Add('begin'); + Add(' o:=TMobile.Create;'); + Add(' o.Destroy;'); + AnalyzeProgram; + CheckUnexpectedMessages; end; procedure TTestUseAnalyzer.TestM_Hint_PrivateTypeNeverUsed; @@ -1060,6 +1146,7 @@ begin AnalyzeProgram; CheckHasHint(mtHint,nPAPrivateTypeXNeverUsed, 'Private type "TMobile.t" never used'); + CheckUnexpectedMessages; end; procedure TTestUseAnalyzer.TestM_Hint_PrivateConstNeverUsed; @@ -1080,6 +1167,7 @@ begin AnalyzeProgram; CheckHasHint(mtHint,nPAPrivateConstXNeverUsed, 'Private const "TMobile.c" never used'); + CheckUnexpectedMessages; end; procedure TTestUseAnalyzer.TestM_Hint_PrivatePropertyNeverUsed; @@ -1101,6 +1189,9 @@ begin AnalyzeProgram; CheckHasHint(mtHint,nPAPrivatePropertyXNeverUsed, 'Private property "TMobile.A" never used'); + CheckHasHint(mtHint,nPAPrivateFieldIsNeverUsed, + 'Private field "TMobile.FA" is never used'); + CheckUnexpectedMessages; end; procedure TTestUseAnalyzer.TestM_Hint_LocalClassInProgramNotUsed; @@ -1120,6 +1211,7 @@ begin AnalyzeProgram; CheckHasHint(mtHint,nPALocalXYNotUsed,'Local class "TMobile" not used'); CheckHasHint(mtHint,nPALocalVariableNotUsed,'Local variable "m" not used'); + CheckUnexpectedMessages; end; procedure TTestUseAnalyzer.TestM_Hint_LocalMethodInProgramNotUsed; @@ -1139,6 +1231,7 @@ begin Add(' if m=nil then ;'); AnalyzeProgram; CheckHasHint(mtHint,nPALocalXYNotUsed,'Local constructor "Create" not used'); + CheckUnexpectedMessages; end; procedure TTestUseAnalyzer.TestM_Hint_AssemblerParameterIgnored; @@ -1161,8 +1254,7 @@ begin Add('begin'); Add(' DoIt(1);'); AnalyzeProgram; - CheckHasHint(mtHint,nPAParameterNotUsed,'Parameter "i" not used',false); - AssertEquals('no hints for assembler proc',0,PAMessageCount); + CheckUnexpectedMessages; end; procedure TTestUseAnalyzer.TestM_Hint_FunctionResultDoesNotSeemToBeSet; @@ -1175,6 +1267,7 @@ begin AnalyzeProgram; CheckHasHint(mtHint,nPAFunctionResultDoesNotSeemToBeSet, sPAFunctionResultDoesNotSeemToBeSet); + CheckUnexpectedMessages; end; procedure TTestUseAnalyzer.TestM_Hint_FunctionResultDoesNotSeemToBeSet_Abstract; @@ -1187,8 +1280,7 @@ begin Add('begin'); Add(' TObject.DoIt;'); AnalyzeProgram; - CheckHasHint(mtHint,nPAFunctionResultDoesNotSeemToBeSet, - sPAFunctionResultDoesNotSeemToBeSet,false); + CheckUnexpectedMessages; end; procedure TTestUseAnalyzer.TestM_Hint_FunctionResultRecord; @@ -1196,15 +1288,17 @@ begin StartProgram(true); Add('type'); Add(' TPoint = record X,Y:longint; end;'); - Add('function Point(Left,Top: longint): TPoint;'); + Add('function Point(Left: longint): TPoint;'); Add('begin'); Add(' Result.X:=Left;'); Add('end;'); Add('begin'); - Add(' Point(1,2);'); + Add(' Point(1);'); AnalyzeProgram; - CheckHasHint(mtHint,nPAFunctionResultDoesNotSeemToBeSet, - sPAFunctionResultDoesNotSeemToBeSet,false); + CheckHasHint(mtHint,nPALocalVariableIsAssignedButNeverUsed, + 'Local variable "X" is assigned but never used'); + CheckHasHint(mtHint,nPALocalVariableNotUsed,'Local variable "Y" not used'); + CheckUnexpectedMessages; end; procedure TTestUseAnalyzer.TestM_Hint_FunctionResultPassRecordElement; @@ -1216,15 +1310,15 @@ begin Add('begin'); Add(' x:=3;'); Add('end;'); - Add('function Point(Left,Top: longint): TPoint;'); + Add('function Point(): TPoint;'); Add('begin'); Add(' Three(Result.X)'); Add('end;'); Add('begin'); - Add(' Point(1,2);'); + Add(' Point();'); AnalyzeProgram; - CheckHasHint(mtHint,nPAFunctionResultDoesNotSeemToBeSet, - sPAFunctionResultDoesNotSeemToBeSet,false); + CheckHasHint(mtHint,nPALocalVariableNotUsed,'Local variable "Y" not used'); + CheckUnexpectedMessages; end; procedure TTestUseAnalyzer.TestM_Hint_OutParam_No_AssignedButNeverUsed; @@ -1238,8 +1332,7 @@ begin Add('begin'); Add(' DoIt(i);'); AnalyzeProgram; - CheckHasHint(mtHint,nPAValueParameterIsAssignedButNeverUsed, - sPAValueParameterIsAssignedButNeverUsed,false); + CheckUnexpectedMessages; end; procedure TTestUseAnalyzer.TestWP_LocalVar; @@ -1402,6 +1495,145 @@ begin AnalyzeWholeProgram; end; +procedure TTestUseAnalyzer.TestWP_Published; +begin + StartProgram(false); + Add('type'); + Add(' {#tobject_used}TObject = class'); + Add(' private'); + Add(' {#fcol_used}FCol: string;'); + Add(' {#fbird_notused}FBird: string;'); + Add(' published'); + Add(' {#fielda_used}FieldA: longint;'); + Add(' procedure {#doit_used}ProcA; virtual; abstract;'); + Add(' property {#col_used}Col: string read FCol;'); + Add(' end;'); + Add('var'); + Add(' {#o_used}o: TObject;'); + Add('begin'); + Add(' o:=nil;'); + AnalyzeWholeProgram; +end; + +procedure TTestUseAnalyzer.TestWP_PublishedSetType; +begin + StartProgram(false); + Add('type'); + Add(' {#tflag_used}TFlag = (red, green);'); + Add(' {#tflags_used}TFlags = set of TFlag;'); + Add(' {#tobject_used}TObject = class'); + Add(' published'); + Add(' {#fielda_used}FieldA: TFlag;'); + Add(' {#fieldb_used}FieldB: TFlags;'); + Add(' end;'); + Add('var'); + Add(' {#o_used}o: TObject;'); + Add('begin'); + Add(' o:=nil;'); + AnalyzeWholeProgram; +end; + +procedure TTestUseAnalyzer.TestWP_PublishedArrayType; +begin + StartProgram(false); + Add('type'); + Add(' {#tdynarr_used}TDynArr = array of longint;'); + Add(' {#tstatarr_used}TStatArr = array[boolean] of longint;'); + Add(' {#tobject_used}TObject = class'); + Add(' published'); + Add(' {#fielda_used}FieldA: TDynArr;'); + Add(' {#fieldb_used}FieldB: TStatArr;'); + Add(' end;'); + Add('var'); + Add(' {#o_used}o: TObject;'); + Add('begin'); + Add(' o:=nil;'); + AnalyzeWholeProgram; +end; + +procedure TTestUseAnalyzer.TestWP_PublishedClassOfType; +begin + StartProgram(false); + Add('type'); + Add(' {#tobjectclass_used}TObjectClass = class of TObject;'); + Add(' {#tobject_used}TObject = class'); + Add(' published'); + Add(' {#fielda_used}FieldA: TObjectClass;'); + Add(' end;'); + Add(' {#tclass_used}TClass = class of TObject;'); + Add('var'); + Add(' {#c_used}c: TClass;'); + Add('begin'); + Add(' c:=nil;'); + AnalyzeWholeProgram; +end; + +procedure TTestUseAnalyzer.TestWP_PublishedRecordType; +begin + StartProgram(false); + Add('type'); + Add(' {#trec_used}TRec = record'); + Add(' {treci_used}i: longint;'); + Add(' end;'); + Add(' {#tobject_used}TObject = class'); + Add(' published'); + Add(' {#fielda_used}FieldA: TRec;'); + Add(' end;'); + Add('var'); + Add(' {#o_used}o: TObject;'); + Add('begin'); + Add(' o:=nil;'); + AnalyzeWholeProgram; +end; + +procedure TTestUseAnalyzer.TestWP_PublishedProcType; +begin + StartProgram(false); + Add('type'); + Add(' {#ta_used}ta = array of longint;'); + Add(' {#tb_used}tb = array of longint;'); + Add(' {#tproca_used}TProcA = procedure;'); + Add(' {#tfunca_used}TFuncA = function: ta;'); + Add(' {#tprocb_used}TProcB = procedure(a: tb);'); + Add(' {#tobject_used}TObject = class'); + Add(' published'); + Add(' {#fielda_used}FieldA: TProcA;'); + Add(' {#fieldb_used}FieldB: TFuncA;'); + Add(' {#fieldc_used}FieldC: TProcB;'); + Add(' end;'); + Add('var'); + Add(' {#o_used}o: TObject;'); + Add('begin'); + Add(' o:=nil;'); + AnalyzeWholeProgram; +end; + +procedure TTestUseAnalyzer.TestWP_PublishedProperty; +begin + StartProgram(false); + Add('const'); + Add(' {#defcol_used}DefCol = 3;'); + Add(' {#defsize_notused}DefSize = 43;'); + Add('type'); + Add(' {#tobject_used}TObject = class'); + Add(' private'); + Add(' {#fcol_used}FCol: longint;'); + Add(' {#fsize_used}FSize: longint;'); + Add(' {#fbird_notused}FBird: string;'); + Add(' {#fcolstored_used}FColStored: boolean;'); + Add(' {#fsizestored_notused}FSizeStored: boolean;'); + Add(' public'); + Add(' property {#size_used}Size: longint read FSize stored FSizeStored default DefSize;'); + Add(' published'); + Add(' property {#col_used}Col: longint read FCol stored FColStored default DefCol;'); + Add(' end;'); + Add('var'); + Add(' {#o_used}o: TObject;'); + Add('begin'); + Add(' if o.Size=13 then ;'); + AnalyzeWholeProgram; +end; + initialization RegisterTests([TTestUseAnalyzer]); diff --git a/packages/pastojs/fpmake.pp b/packages/pastojs/fpmake.pp index 935778586a..7400adf5e3 100644 --- a/packages/pastojs/fpmake.pp +++ b/packages/pastojs/fpmake.pp @@ -19,7 +19,7 @@ begin {$endif ALLPACKAGES} P.Version:='3.0.3'; - P.OSes := AllOses-[embedded,msdos,win16]; + P.OSes := AllOses-[embedded,msdos]; P.Dependencies.Add('fcl-js'); P.Dependencies.Add('fcl-passrc'); diff --git a/packages/pastojs/src/fppas2js.pp b/packages/pastojs/src/fppas2js.pp index c9fff09c3b..cc5920c29d 100644 --- a/packages/pastojs/src/fppas2js.pp +++ b/packages/pastojs/src/fppas2js.pp @@ -103,6 +103,7 @@ Works: - reintroduced variables - external vars and methods - const + - bracket accessor, getter/setter has external name '[]' - dynamic arrays - arrays can be null - init as "arr = []" so typeof works @@ -133,6 +134,7 @@ Works: - ord(), low(), high(), pred(), succ() - type cast alias to enumtype - type cast number to enumtype + - const aliasname = enumvalue - sets - set of enum - include, exclude, clone when referenced @@ -142,6 +144,7 @@ Works: - in-operator - low(), high() - when passing as argument set state referenced + - set of (enum,enum2) - anonymous enumtype - with-do using local var - with record do i:=v; - with classinstance do begin create; i:=v; f(); i:=a[]; end; @@ -188,7 +191,6 @@ Works: - Pascal descendant can override newinstance - any class can be typecasted to any root class - class instances cannot access external class members (e.g. static class functions) - - external class bracket accessor, getter/setter has external name '[]' - external class 'Array' bracket operator [integer] type jsvalue - external class 'Object' bracket operator [string] type jsvalue - jsvalue @@ -210,29 +212,60 @@ Works: - parameter, result type, assign from/to untyped - operators equal, not equal - callback: assign to jsvalue, equal, not equal +- RTTI + - base types + - unit $rtti + - enum type tkEnumeration + - set type tkSet + - procedure type tkProcVar, tkMethod + - class type tkClass + - fields, + - methods, + - properties no params, no index, no defaultvalue + - class forward + - class-of type tkClassRef + - dyn array type tkDynArray + - static array type tkArray + - record type tkRecord + - no typeinfo for local types + - built-in function typeinfo(): Pointer/TTypeInfo/...; + - typeinfo(class) -> class.$rtti + - WPO skip not used typeinfo +- pointer + - compare with and assign nil - ECMAScript6: - use 0b for binary literals - use 0o for octal literals ToDos: -- -Jirtl.js- +- typecast proctype +- RTTI + - open array param + - codetools function typeinfo + - jsinteger (pasresolver: btIntDouble) + - class property + - defaultvalue + - type alias type + - typinfo.pp functions to get/setprop + - documentation +- warn int64 +- move local types to unit scope +- local var absolute - make -Jirtl.js default for -Jc and -Tnodejs, needs #IFDEF in cfg -- remove 'Object' array workaround - FuncName:= (instead of Result:=) -- ord(s[i]) -> s.charCodeAt(i) - $modeswitch -> define <modeswitch> - $modeswitch- -> turn off +- check memleaks - integer range - @@ compare method in delphi mode - make records more lightweight - dotted unit names, namespaces -- type alias type -- RTTI - enumeration for..in..do - pointer of record - nested types in class - asm: pas() - useful for overloads and protect an identifier from optimization - source maps +- ifthen Not in Version 1.0: - write, writeln @@ -243,9 +276,9 @@ Not in Version 1.0: - array of const - sets - set of char, boolean, integer range, char range, enum range - - set of (enum,enum2) - anonymous enumtype - call array of proc element without () - record const +- class: property modifier index - enums with custom values - library - option typecast checking @@ -300,12 +333,15 @@ const nInvalidFunctionReference = 4011; nMissingExternalName = 4012; nVirtualMethodNameMustMatchExternal = 4013; - nInvalidVariableModifier = 4014; - nNoArgumentsAllowedForExternalObjectConstructor = 4015; - nNewInstanceFunctionMustBeVirtual = 4016; - nNewInstanceFunctionMustHaveTwoParameters = 4017; - nNewInstanceFunctionMustNotHaveOverloads = 4018; - nBracketAccessorOfExternalClassMustHaveOneParameter = 4019; + nPublishedNameMustMatchExternal = 4014; + nInvalidVariableModifier = 4015; + nNoArgumentsAllowedForExternalObjectConstructor = 4016; + nNewInstanceFunctionMustBeVirtual = 4017; + nNewInstanceFunctionMustHaveTwoParameters = 4018; + nNewInstanceFunctionMustNotHaveOverloadAtX = 4019; + nBracketAccessorOfExternalClassMustHaveOneParameter = 4020; + nTypeXCannotBePublished = 4021; + nNotSupportedX = 4022; // resourcestring patterns of messages resourcestring sPasElementNotSupported = 'Pascal element not supported: %s'; @@ -322,11 +358,14 @@ resourcestring sMissingExternalName = 'Missing external name'; sVirtualMethodNameMustMatchExternal = 'Virtual method name must match external'; sInvalidVariableModifier = 'Invalid variable modifier "%s"'; + sPublishedNameMustMatchExternal = 'Published name must match external'; sNoArgumentsAllowedForExternalObjectConstructor = 'no arguments allowed for external object constructor'; sNewInstanceFunctionMustBeVirtual = 'NewInstance function must be virtual'; sNewInstanceFunctionMustHaveTwoParameters = 'NewInstance function must have two parameters'; - sNewInstanceFunctionMustNotHaveOverloads = 'NewInstance function must not have overloads'; + sNewInstanceFunctionMustNotHaveOverloadAtX = 'NewInstance function must not have overload at %s'; sBracketAccessorOfExternalClassMustHaveOneParameter = 'Bracket accessor of external class must have one parameter'; + sTypeXCannotBePublished = 'Type "%s" cannot be published'; + sNotSupportedX = 'Not supported: %s'; const ExtClassBracketAccessor = '[]'; // external name '[]' marks the array param getter/setter @@ -353,6 +392,21 @@ type pbifnProcType_Equal, pbifnProgramMain, pbifnRecordEqual, + pbifnRTTIAddField, // typeinfos of tkclass and tkrecord have addField + pbifnRTTIAddFields, // typeinfos of tkclass and tkrecord have addFields + pbifnRTTIAddMethod,// " " + pbifnRTTIAddProperty,// " " + pbifnRTTINewClass,// typeinfo creator of tkClass $Class + pbifnRTTINewClassRef,// typeinfo of tkClassRef $ClassRef + pbifnRTTINewEnum,// typeinfo of tkEnumeration $Enum + pbifnRTTINewDynArray,// typeinfo of tkDynArray $DynArray + pbifnRTTINewMethodVar,// typeinfo of tkMethod $MethodVar + pbifnRTTINewPointer,// typeinfo of tkPointer $Pointer + pbifnRTTINewProcSig,// rtl.newTIProcSig + pbifnRTTINewProcVar,// typeinfo of tkProcVar $ProcVar + pbifnRTTINewRecord,// typeinfo creator of tkRecord $Record + pbifnRTTINewSet,// typeinfo of tkSet $Set + pbifnRTTINewStaticArray,// typeinfo of tkArray $StaticArray pbifnSetCharAt, pbifnSet_Clone, pbifnSet_Create, @@ -375,7 +429,35 @@ type pbivnModules, pbivnPtrClass, pbivnRTL, - pbivnWith + pbivnRTTI, // $rtti + pbivnRTTIArray_Dims, + pbivnRTTIArray_ElType, + pbivnRTTIClassRef_InstanceType, + pbivnRTTIEnum_EnumType, + pbivnRTTIInt_MaxValue, + pbivnRTTIInt_MinValue, + pbivnRTTILocal, // $r + pbivnRTTIMethodKind, // tTypeInfoMethodVar has methodkind + pbivnRTTIPointer_RefType, + pbivnRTTIProcFlags, + pbivnRTTIProcVar_ProcSig, + pbivnRTTIPropDefault, + pbivnRTTIPropStored, + pbivnRTTISet_CompType, + pbivnWith, + pbitnAnonymousPostfix, + pbitnTI, + pbitnTIClass, + pbitnTIClassRef, + pbitnTIDynArray, + pbitnTIEnum, + pbitnTIInteger, + pbitnTIMethodVar, + pbitnTIPointer, + pbitnTIProcVar, + pbitnTIRecord, + pbitnTISet, + pbitnTIStaticArray ); const @@ -400,6 +482,21 @@ const 'eqCallback', // rtl.eqCallback '$main', '$equal', + 'addField', + 'addFields', + 'addMethod', + 'addProperty', + '$Class', + '$ClassRef', + '$Enum', + '$DynArray', + '$MethodVar', + '$Pointer', + 'newTIProcSig', + '$ProcVar', + '$Record', + '$Set', + '$StaticArray', 'setCharAt', // rtl.setCharAt 'cloneSet', // rtl.cloneSet 'createSet', // rtl.createSet [...] @@ -422,10 +519,38 @@ const 'pas', '$class', 'rtl', - '$with' + '$rtti', + 'dims', + 'eltype', + 'instancetype', + 'enumtype', + 'maxvalue', + 'minvalue', + '$r', + 'methodkind', + 'reftype', + 'flags', + 'procsig', + 'defaultvalue', + 'stored', + 'comptype', + '$with', + '$a', + 'tTypeInfo', + 'tTypeInfoClass', + 'tTypeInfoClassRef', + 'tTypeInfoDynArray', + 'tTypeInfoEnum', + 'tTypeInfoInteger', + 'tTypeInfoMethodVar', + 'tTypeInfoPointer', + 'tTypeInfoProcVar', + 'tTypeInfoRecord', + 'tTypeInfoSet', + 'tTypeInfoStaticArray' ); - JSReservedWords: array[0..106] of string = ( + JSReservedWords: array[0..108] of string = ( // keep sorted, first uppercase, then lowercase ! 'Array', 'ArrayBuffer', @@ -479,6 +604,8 @@ const 'call', 'case', 'catch', + 'charAt', + 'charCodeAt', 'class', 'constructor', 'continue', @@ -542,7 +669,6 @@ const HighJSInteger = $fffffffffffff; LowJSBoolean = false; HighJSBoolean = true; - Type { EPas2JS } @@ -570,10 +696,10 @@ const 'None', 'JSValue' ); - btAllJSValueSrcTypes = [btNil,btUntyped]+btAllInteger + btAllJSValueSrcTypes = [btNil,btUntyped,btPointer]+btAllInteger +btAllStringAndChars+btAllFloats+btAllBooleans; btAllJSValueTypeCastTo = btAllInteger - +btAllStringAndChars+btAllFloats+btAllBooleans; + +btAllStringAndChars+btAllFloats+btAllBooleans+[btPointer]; //------------------------------------------------------------------------------ // Element CustomData @@ -625,6 +751,30 @@ type //------------------------------------------------------------------------------ // TPas2JSResolver const + btAllPas2jsBaseTypes = [ + btChar, + btString, + btDouble, + btBoolean, + //btByteBool, + //btWordBool, + //btLongBool, + //btQWordBool, + btByte, + btShortInt, + btWord, + btSmallInt, + btLongWord, + btCardinal, + btLongint, + //btQWord, + btInt64, + btPointer + //btFile, + //btText, + //btVariant + ]; + bfAllPas2jsBaseProcs = bfAllStandardProcs; DefaultPasResolverOptions = [ proFixCaseOfOverrides, proClassPropertyNonStatic, @@ -632,7 +782,8 @@ const proClassOfIs, proExtClassInstanceNoTypeMembers, proOpenAsDynArrays, - proProcTypeWithoutIsNested + proProcTypeWithoutIsNested, + proMethodAddrAsPointer ]; type TPas2JSResolver = class(TPasResolver) @@ -645,9 +796,12 @@ type procedure OnClearHashItem(Item, Dummy: pointer); protected FOverloadScopes: TFPList; // list of TPasIdentifierScope + function HasOverloadIndex(El: TPasElement): boolean; virtual; function GetOverloadIndex(Identifier: TPasIdentifier; StopAt: TPasElement): integer; + function GetOverloadAt(Identifier: TPasIdentifier; var Index: integer): TPasIdentifier; function GetOverloadIndex(El: TPasElement): integer; + function GetOverloadAt(const aName: String; Index: integer): TPasIdentifier; function RenameOverload(El: TPasElement): boolean; procedure RenameOverloadsInSection(aSection: TPasSection); procedure RenameOverloads(DeclEl: TPasElement; Declarations: TFPList); @@ -656,6 +810,7 @@ type procedure PopOverloadScope; procedure ResolveImplAsm(El: TPasImplAsmStatement); override; procedure FinishModule(CurModule: TPasModule); override; + procedure FinishSetType(El: TPasSetType); override; procedure FinishClassType(El: TPasClassType); override; procedure FinishVariable(El: TPasVariable); override; procedure FinishProcedureType(El: TPasProcedureType); override; @@ -679,19 +834,15 @@ type function CheckEqualCompatibilityCustomType(const LHS, RHS: TPasResolverResult; ErrorEl: TPasElement; RaiseOnIncompatible: boolean): integer; override; - function ResolveBracketOperatorClass(Params: TParamsExpr; - const ResolvedValue: TPasResolverResult; ClassScope: TPasClassScope; - Access: TResolvedRefAccess): boolean; override; - procedure ComputeArrayParams_Class(Params: TParamsExpr; var - ResolvedEl: TPasResolverResult; ClassScope: TPasClassScope; - Flags: TPasResolverComputeFlags; StartEl: TPasElement); override; + procedure BI_TypeInfo_OnGetCallResult(Proc: TResElDataBuiltInProc; + Params: TParamsExpr; out ResolvedEl: TPasResolverResult); override; public constructor Create; destructor Destroy; override; // base types procedure AddObjFPCBuiltInIdentifiers( - const TheBaseTypes: TResolveBaseTypes=btAllStandardTypes; - const TheBaseProcs: TResolverBuiltInProcs=bfAllStandardProcs); override; + const TheBaseTypes: TResolveBaseTypes; + const TheBaseProcs: TResolverBuiltInProcs); override; function CheckTypeCastRes(const FromResolved, ToResolved: TPasResolverResult; ErrorEl: TPasElement; RaiseOnError: boolean): integer; override; @@ -707,6 +858,8 @@ type procedure AddElementData(Data: TPas2JsElementData); virtual; function CreateElementData(DataClass: TPas2JsElementDataClass; El: TPasElement): TPas2JsElementData; virtual; + // utility + function HasTypeInfo(El: TPasType): boolean; override; end; //------------------------------------------------------------------------------ @@ -773,9 +926,9 @@ type constructor Create(PasEl: TPasElement; JSEl: TJSElement; aParent: TConvertContext); override; end; - { TInterfaceContext } + { TSectionContext - interface/implementation/program/library } - TInterfaceContext = Class(TFunctionContext) + TSectionContext = Class(TFunctionContext) public constructor Create(PasEl: TPasElement; JSEl: TJSElement; aParent: TConvertContext); override; end; @@ -825,7 +978,8 @@ type coLowerCase, // lowercase all identifiers, except conflicts with JS reserved words coSwitchStatement, // convert case-of into switch instead of if-then-else coEnumNumbers, // use enum numbers instead of names - coUseStrict // insert 'use strict' + coUseStrict, // insert 'use strict' + coNoTypeInfo // do not generate RTTI ); TPasToJsConverterOptions = set of TPasToJsConverterOption; @@ -891,6 +1045,7 @@ type private FBuiltInNames: array[TPas2JSBuiltInName] of string; FOnIsElementUsed: TPas2JSIsElementUsedEvent; + FOnIsTypeInfoUsed: TPas2JSIsElementUsedEvent; FOptions: TPasToJsConverterOptions; FPreservedWords: TJSReservedWordList; // sorted with CompareStr FTargetPlatform: TPasToJsPlatform; @@ -924,17 +1079,22 @@ type Function GetPasIdentValueType(AName: String; AContext: TConvertContext): TJSType; virtual; Function ComputeConstString(Expr: TPasExpr; AContext: TConvertContext; NotEmpty: boolean): String; virtual; Function IsExternalClassConstructor(El: TPasElement): boolean; + Procedure ComputeRange(const RangeResolved: TPasResolverResult; + out MinValue, MaxValue: int64; ErrorEl: TPasElement); virtual; // Name mangling Function TransformVariableName(El: TPasElement; Const AName: String; AContext : TConvertContext): String; virtual; Function TransformVariableName(El: TPasElement; AContext : TConvertContext) : String; virtual; - Function TransformModuleName(El: TPasModule; AContext : TConvertContext) : String; virtual; + Function TransformModuleName(El: TPasModule; AddModulesPrefix: boolean; AContext : TConvertContext) : String; virtual; Function IsPreservedWord(const aName: string): boolean; virtual; // Never create an element manually, always use the below functions Function IsElementUsed(El: TPasElement): boolean; virtual; + Function HasTypeInfo(El: TPasType; AContext: TConvertContext): boolean; virtual; + Function IsClassRTTICreatedBefore(aClass: TPasClassType; Before: TPasElement): boolean; Function CreateElement(C: TJSElementClass; Src: TPasElement): TJSElement; virtual; Function CreateFreeOrNewInstanceExpr(Ref: TResolvedReference; AContext : TConvertContext): TJSCallExpression; virtual; - Function CreateFunction(El: TPasElement; WithBody: boolean = true): TJSFunctionDeclarationStatement; + Function CreateFunction(El: TPasElement; WithBody: boolean = true; + WithSrc: boolean = false): TJSFunctionDeclarationStatement; Procedure CreateProcedureCall(var Call: TJSCallExpression; Args: TParamsExpr; TargetProc: TPasProcedureType; AContext: TConvertContext); virtual; Procedure CreateProcedureCallArgs(Elements: TJSArrayLiteralElements; @@ -949,23 +1109,30 @@ type Function CreateUsesList(UsesSection: TPasSection; AContext : TConvertContext): TJSArrayLiteral; Procedure AddToStatementList(var First, Last: TJSStatementList; Add: TJSElement; Src: TPasElement); - Function CreateValInit(PasType: TPasType; Expr: TPasElement; El: TPasElement; AContext: TConvertContext): TJSElement; virtual; + Function CreateValInit(PasType: TPasType; Expr: TPasElement; El: TPasElement; + AContext: TConvertContext): TJSElement; virtual; Function CreateVarInit(El: TPasVariable; AContext: TConvertContext): TJSElement; virtual; + Function CreateVarStatement(const aName: String; Init: TJSElement; + El: TPasElement): TJSVariableStatement; virtual; + Function CreateVarDecl(const aName: String; Init: TJSElement; El: TPasElement): TJSVarDeclaration; virtual; Function CreateLiteralNumber(El: TPasElement; const n: TJSNumber): TJSLiteral; virtual; Function CreateLiteralString(El: TPasElement; const s: string): TJSLiteral; virtual; Function CreateLiteralJSString(El: TPasElement; const s: TJSString): TJSLiteral; virtual; Function CreateLiteralBoolean(El: TPasElement; b: boolean): TJSLiteral; virtual; Function CreateLiteralNull(El: TPasElement): TJSLiteral; virtual; Function CreateLiteralUndefined(El: TPasElement): TJSLiteral; virtual; + Function CreateSetLiteralElement(Expr: TPasExpr; AContext: TConvertContext): TJSElement; virtual; Function CreateRecordInit(aRecord: TPasRecordType; Expr: TPasElement; El: TPasElement; AContext: TConvertContext): TJSElement; virtual; Function CreateArrayInit(ArrayType: TPasArrayType; Expr: TPasElement; El: TPasElement; AContext: TConvertContext): TJSElement; virtual; - Function CreateCmpArrayWithNil(El: TPasElement; JSArray: TJSElement; OpCode: TExprOpCode): TJSElement; virtual; + Function CreateCmpArrayWithNil(El: TPasElement; JSArray: TJSElement; + OpCode: TExprOpCode): TJSElement; virtual; Function CreateReferencePath(El: TPasElement; AContext : TConvertContext; Kind: TRefPathKind; Full: boolean = false; Ref: TResolvedReference = nil): string; virtual; - Function CreateReferencePathExpr(El: TPasElement; AContext : TConvertContext; Full: boolean = false; Ref: TResolvedReference = nil): TJSPrimaryExpressionIdent; virtual; - Function CreateImplementationSection(El: TPasModule; Src: TJSSourceElements; AContext: TConvertContext): TJSElement; + Function CreateReferencePathExpr(El: TPasElement; AContext : TConvertContext; + Full: boolean = false; Ref: TResolvedReference = nil): TJSPrimaryExpressionIdent; virtual; + Function CreateImplementationSection(El: TPasModule; AContext: TConvertContext): TJSFunctionDeclarationStatement; Procedure CreateInitSection(El: TPasModule; Src: TJSSourceElements; AContext: TConvertContext); Function CreateDotExpression(aParent: TPasElement; Left, Right: TJSElement): TJSElement; virtual; Function CreateReferencedSet(El: TPasElement; SetExpr: TJSElement): TJSElement; virtual; @@ -974,6 +1141,17 @@ type Function CreateCallback(El: TPasElement; ResolvedEl: TPasResolverResult; AContext: TConvertContext): TJSElement; virtual; Function CreateAssignStatement(LeftEl: TPasElement; AssignContext: TAssignContext): TJSElement; virtual; + Function CreateTypeInfoRef(El: TPasType; AContext: TConvertContext; + ErrorEl: TPasElement): TJSElement; virtual; + Function CreateRTTIArgList(Parent: TPasElement; Args: TFPList; + AContext: TConvertContext): TJSElement; virtual; + Procedure AddRTTIArgument(Arg: TPasArgument; TargetParams: TJSArrayLiteral; + AContext: TConvertContext); virtual; + Function CreateRTTINewType(El: TPasType; const CallFuncName: string; + IsForward: boolean; AContext: TConvertContext; out ObjLit: TJSObjectLiteral): TJSCallExpression; virtual; + Function CreateRTTIClassField(V: TPasVariable; AContext: TConvertContext): TJSElement; virtual; + Function CreateRTTIClassMethod(Proc: TPasProcedure; AContext: TConvertContext): TJSElement; virtual; + Function CreateRTTIClassProperty(Prop: TPasProperty; AContext: TConvertContext): TJSElement; virtual; // Statements Function ConvertImplBlockElements(El: TPasImplBlock; AContext: TConvertContext; NilIfEmpty: boolean): TJSElement; virtual; Function ConvertBeginEndStatement(El: TPasImplBeginBlock; AContext: TConvertContext; NilIfEmpty: boolean): TJSElement; virtual; @@ -1002,30 +1180,31 @@ type Function ConvertExternalConstructor(Left: TPasElement; Ref: TResolvedReference; ParamsExpr: TParamsExpr; AContext : TConvertContext): TJSElement; virtual; - Function ConvertTypeCastToBaseType(El: TParamsExpr; AContext: TConvertContext; BaseTypeData: TResElDataBaseType): TJSElement; virtual; + Function ConvertTypeCastToBaseType(El: TParamsExpr; AContext: TConvertContext; ToBaseTypeData: TResElDataBaseType): TJSElement; virtual; Function ConvertSetLiteral(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual; Function ConvertOpenArrayParam(ElType: TPasType; El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual; - Function ConvertBuiltInLength(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual; - Function ConvertBuiltInSetLength(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual; - Function ConvertBuiltInExcludeInclude(El: TParamsExpr; AContext: TConvertContext; IsInclude: boolean): TJSElement; virtual; + Function ConvertBuiltIn_Length(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual; + Function ConvertBuiltIn_SetLength(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual; + Function ConvertBuiltIn_ExcludeInclude(El: TParamsExpr; AContext: TConvertContext; IsInclude: boolean): TJSElement; virtual; Function ConvertBuiltInContinue(El: TPasExpr; AContext: TConvertContext): TJSElement; virtual; Function ConvertBuiltInBreak(El: TPasExpr; AContext: TConvertContext): TJSElement; virtual; - Function ConvertBuiltInExit(El: TPasExpr; AContext: TConvertContext): TJSElement; virtual; - Function ConvertBuiltInIncDec(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual; - Function ConvertBuiltInAssigned(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual; - Function ConvertBuiltInChr(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual; - Function ConvertBuiltInOrd(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual; - Function ConvertBuiltInLow(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual; - Function ConvertBuiltInHigh(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual; - Function ConvertBuiltInPred(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual; - Function ConvertBuiltInSucc(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual; - Function ConvertBuiltInStrProc(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual; - Function ConvertBuiltInStrFunc(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual; + Function ConvertBuiltIn_Exit(El: TPasExpr; AContext: TConvertContext): TJSElement; virtual; + Function ConvertBuiltIn_IncDec(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual; + Function ConvertBuiltIn_Assigned(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual; + Function ConvertBuiltIn_Chr(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual; + Function ConvertBuiltIn_Ord(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual; + Function ConvertBuiltIn_Low(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual; + Function ConvertBuiltIn_High(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual; + Function ConvertBuiltIn_Pred(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual; + Function ConvertBuiltIn_Succ(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual; + Function ConvertBuiltIn_StrProc(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual; + Function ConvertBuiltIn_StrFunc(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual; Function ConvertBuiltInStrParam(El: TPasExpr; AContext: TConvertContext; IsStrFunc, IsFirst: boolean): TJSElement; virtual; - Function ConvertBuiltInConcatArray(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual; - Function ConvertBuiltInCopyArray(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual; - Function ConvertBuiltInInsertArray(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual; - Function ConvertBuiltInDeleteArray(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual; + Function ConvertBuiltIn_ConcatArray(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual; + Function ConvertBuiltIn_CopyArray(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual; + Function ConvertBuiltIn_InsertArray(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual; + Function ConvertBuiltIn_DeleteArray(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual; + Function ConvertBuiltIn_TypeInfo(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual; Function ConvertRecordValues(El: TRecordValues; AContext: TConvertContext): TJSElement; virtual; Function ConvertSelfExpression(El: TSelfExpr; AContext: TConvertContext): TJSElement; virtual; Function ConvertBinaryExpression(El: TBinaryExpr; AContext: TConvertContext): TJSElement; virtual; @@ -1055,8 +1234,38 @@ type Function ConvertVariable(El: TPasVariable; AContext: TConvertContext): TJSElement; virtual; Function ConvertRecordType(El: TPasRecordType; AContext: TConvertContext): TJSElement; virtual; Function ConvertClassType(El: TPasClassType; AContext: TConvertContext): TJSElement; virtual; + Function ConvertClassForwardType(El: TPasClassType; AContext: TConvertContext): TJSElement; virtual; Function ConvertClassExternalType(El: TPasClassType; AContext: TConvertContext): TJSElement; virtual; + Function ConvertClassOfType(El: TPasClassOfType; AContext: TConvertContext): TJSElement; virtual; Function ConvertEnumType(El: TPasEnumType; AContext: TConvertContext): TJSElement; virtual; + Function ConvertSetType(El: TPasSetType; AContext: TConvertContext): TJSElement; virtual; + Function ConvertPointerType(El: TPasPointerType; AContext: TConvertContext): TJSElement; virtual; + Function ConvertProcedureType(El: TPasProcedureType; AContext: TConvertContext): TJSElement; virtual; + Function ConvertArrayType(El: TPasArrayType; AContext: TConvertContext): TJSElement; virtual; + Public + // RTTI, TypeInfo constants + const + // TParamFlag + pfVar = 1; + pfConst = 2; + pfOut = 4; + // TProcedureFlag + pfStatic = 1; + pfVarargs = 2; + pfExternal = 4; + // TPropertyFlag + pfGetFunction = 1; + pfSetProcedure = 2; + pfStoredFunction = 4; + type + TMethodKind = ( + mkProcedure, // 0 default + mkFunction, // 1 + mkConstructor, // 2 + mkDestructor, // 3 + mkClassProcedure, // 4 + mkClassFunction // 5 + ); Public Constructor Create; destructor Destroy; override; @@ -1069,6 +1278,7 @@ type Property UseSwitchStatement: boolean read GetUseSwitchStatement write SetUseSwitchStatement;// default false, because slower than "if" in many engines Property UseEnumNumbers: boolean read GetUseEnumNumbers write SetUseEnumNumbers; // default false Property OnIsElementUsed: TPas2JSIsElementUsedEvent read FOnIsElementUsed write FOnIsElementUsed; + Property OnIsTypeInfoUsed: TPas2JSIsElementUsedEvent read FOnIsTypeInfoUsed write FOnIsTypeInfoUsed; Property PreservedWords: TJSReservedWordList read FPreservedWords write SetPreservedWords; // names Property BuildInNames[bin: TPas2JSBuiltInName]: string read GetBuildInNames write SetBuildInNames; @@ -1170,17 +1380,42 @@ begin end; end; +function TPas2JSResolver.HasOverloadIndex(El: TPasElement): boolean; +var + C: TClass; + ProcScope: TPasProcedureScope; +begin + C:=El.ClassType; + if C=TPasProperty then + exit(false) + else if C=TPasClassType then + begin + if TPasClassType(El).IsForward then + exit(false); + end + else if C.InheritsFrom(TPasProcedure) then + begin + if TPasProcedure(El).IsOverride then + exit(true); + // Note: external proc pollutes the name space + ProcScope:=TPasProcedureScope(El.CustomData); + if ProcScope.DeclarationProc<>nil then + // implementation proc -> only count the header -> skip + exit(false); + end; + Result:=true; +end; + function TPas2JSResolver.GetOverloadIndex(Identifier: TPasIdentifier; StopAt: TPasElement): integer; // if not found return number of overloads // if found return index in overloads var El: TPasElement; - ProcScope: TPasProcedureScope; - C: TClass; begin Result:=0; // iterate from last added to first added + // Note: the first added has Index=0 while Identifier<>nil do begin El:=Identifier.Element; @@ -1190,23 +1425,53 @@ begin Result:=0; continue; end; - C:=El.ClassType; - if C=TPasClassType then - begin - if TPasClassType(El).IsForward then - continue; - end - else if C.InheritsFrom(TPasProcedure) then + if HasOverloadIndex(El) then + inc(Result); + end; +end; + +function TPas2JSResolver.GetOverloadAt(Identifier: TPasIdentifier; + var Index: integer): TPasIdentifier; +// if found Result<>nil and Index=0 +// if not found Result=nil and Index is reduced by number of overloads +var + El: TPasElement; + CurIdent: TPasIdentifier; + Count: Integer; +begin + if Identifier=nil then exit(nil); + // Note: the Identifier chain is from last added to first added + // -> get length of chain + Count:=0; + CurIdent:=Identifier; + while CurIdent<>nil do + begin + El:=CurIdent.Element; + CurIdent:=CurIdent.NextSameIdentifier; + if HasOverloadIndex(El) then + inc(Count); + end; + if Count<=Index then + begin + // Index is not in this scope + dec(Index); + exit(nil); + end; + // Index is in this scope -> find it + CurIdent:=Identifier; + while CurIdent<>nil do + begin + if HasOverloadIndex(CurIdent.Element) then begin - if TPasProcedure(El).IsOverride then - continue; - // Note: external proc pollute the name space - ProcScope:=TPasProcedureScope(El.CustomData); - if ProcScope.DeclarationProc<>nil then - // implementation proc -> only count the header -> skip - continue; + dec(Count); + if (Index=Count) then + begin + Index:=0; + Result:=CurIdent; + exit; + end; end; - inc(Result); + CurIdent:=CurIdent.NextSameIdentifier; end; end; @@ -1229,19 +1494,60 @@ begin inc(Result,GetOverloadIndex(Identifier,El)); end; +function TPas2JSResolver.GetOverloadAt(const aName: String; Index: integer + ): TPasIdentifier; +var + i: Integer; +begin + Result:=nil; + for i:=FOverloadScopes.Count-1 downto 0 do + begin + // find last added + Result:=TPasIdentifierScope(FOverloadScopes[i]).FindLocalIdentifier(aName); + Result:=GetOverloadAt(Result,Index); + if Result<>nil then + exit; + end; + // find in external names + Result:=FindExternalName(aName); + Result:=GetOverloadAt(Result,Index); +end; + function TPas2JSResolver.RenameOverload(El: TPasElement): boolean; var OverloadIndex: Integer; + + function GetDuplicate: TPasElement; + var + Duplicate: TPasIdentifier; + begin + Duplicate:=GetOverloadAt(El.Name,0); + Result:=Duplicate.Element; + end; + +var NewName: String; + Duplicate: TPasElement; begin // => count overloads in this section OverloadIndex:=GetOverloadIndex(El); if OverloadIndex=0 then exit(false); // there is no overload + if (El.ClassType=TPasClassFunction) and (TPas2JSClassScope(TPasClassType(El.Parent).CustomData).NewInstanceFunction=El) then - RaiseMsg(20170324234324,nNewInstanceFunctionMustNotHaveOverloads, - sNewInstanceFunctionMustNotHaveOverloads,[],El); + begin + Duplicate:=GetDuplicate; + RaiseMsg(20170324234324,nNewInstanceFunctionMustNotHaveOverloadAtX, + sNewInstanceFunctionMustNotHaveOverloadAtX,[GetElementSourcePosStr(Duplicate)],El); + end; + if El.Visibility=visPublished then + begin + Duplicate:=GetDuplicate; + RaiseMsg(20170413220924,nDuplicateIdentifier,sDuplicateIdentifier, + [Duplicate.Name,GetElementSourcePosStr(Duplicate)],El); + end; + NewName:=El.Name+'$'+IntToStr(OverloadIndex); {$IFDEF VerbosePas2JS} writeln('TPas2JSResolver.RenameOverload "',El.Name,'" has overload. NewName="',NewName,'"'); @@ -1456,6 +1762,18 @@ begin end; end; +procedure TPas2JSResolver.FinishSetType(El: TPasSetType); +var + TypeEl: TPasType; +begin + inherited FinishSetType(El); + TypeEl:=ResolveAliasType(El.EnumType); + if TypeEl.ClassType=TPasEnumType then + // ok + else + RaiseMsg(20170415182320,nNotSupportedX,sNotSupportedX,['set of '+TypeEl.Name],El); +end; + procedure TPas2JSResolver.FinishClassType(El: TPasClassType); begin inherited FinishClassType(El); @@ -1514,6 +1832,10 @@ begin end; Include(El.VarModifiers,vmExternal); end; + if El.Visibility=visPublished then + // Note: an external class has no typeinfo + RaiseMsg(20170413221516,nSymbolCannotBePublished,sSymbolCannotBePublished, + [],El); end; end else if ParentC=TPasRecordType then @@ -1547,6 +1869,9 @@ begin if El.ExportName=nil then RaiseMsg(20170227100750,nMissingExternalName,sMissingExternalName,[],El); ExtName:=ComputeConstString(El.ExportName,true,true); + if (El.Visibility=visPublished) and (ExtName<>El.Name) then + RaiseMsg(20170407002940,nPublishedNameMustMatchExternal, + sPublishedNameMustMatchExternal,[],El.ExportName); // add external name to FExternalNames if (El.Parent is TPasSection) or ((El.ClassType=TPasConst) and (El.Parent is TPasProcedure)) then @@ -1562,6 +1887,7 @@ var C: TClass; AClass: TPasClassType; ClassScope: TPas2JSClassScope; + ptm: TProcTypeModifier; begin inherited FinishProcedureType(El); if El.Parent is TPasProcedure then @@ -1577,9 +1903,13 @@ begin if (pm in Proc.Modifiers) and (not (pm in [pmVirtual, pmAbstract, pmOverride, pmOverload, pmReintroduce, - pmAssembler, pmVarargs, pmPublic, + pmAssembler, pmPublic, pmExternal, pmForward])) then RaiseNotYetImplemented(20170208142159,El,'modifier '+ModifierNames[pm]); + for ptm in TProcTypeModifiers do + if (ptm in Proc.ProcType.Modifiers) + and (not (ptm in [ptmOfObject,ptmVarargs])) then + RaiseNotYetImplemented(20170411171454,El,'modifier '+ProcTypeModifiers[ptm]); // check pmPublic if [pmPublic,pmExternal]<=Proc.Modifiers then @@ -1607,6 +1937,11 @@ begin Proc.LibrarySymbolName:=TPrimitiveExpr.Create(El,pekString,''''+Proc.Name+''''); end; + if Proc.Visibility=visPublished then + // Note: an external class has no typeinfo + RaiseMsg(20170413221327,nSymbolCannotBePublished,sSymbolCannotBePublished, + [],Proc); + C:=Proc.ClassType; if (C=TPasProcedure) or (C=TPasFunction) or (C=TPasClassProcedure) or (C=TPasClassFunction) then @@ -1667,7 +2002,7 @@ begin if Proc.LibraryExpr<>nil then RaiseMsg(20170211220712,nPasElementNotSupported,sPasElementNotSupported, - ['library'],Proc.LibraryExpr); + ['external library name'],Proc.LibraryExpr); if Proc.LibrarySymbolName=nil then RaiseMsg(20170227095454,nMissingExternalName,sMissingExternalName, ['missing external name'],Proc); @@ -1685,6 +2020,17 @@ begin RaiseMsg(20170321090049,nVirtualMethodNameMustMatchExternal, sVirtualMethodNameMustMatchExternal,[],Proc.LibrarySymbolName); + // a published must have the external name, so that streaming works + if (Proc.Visibility=visPublished) then + begin + if (Proc.Name<>ExtName) then + RaiseMsg(20170407002940,nPublishedNameMustMatchExternal, + sPublishedNameMustMatchExternal,[],Proc.LibrarySymbolName); + if ExtName=ExtClassBracketAccessor then + RaiseMsg(20170409211805,nSymbolCannotBePublished, + sSymbolCannotBePublished,[],Proc.LibrarySymbolName); + end; + if Proc.Parent is TPasSection then AddExternalPath(ExtName,Proc.LibrarySymbolName); @@ -1699,8 +2045,24 @@ var GetterIsBracketAccessor, SetterIsBracketAccessor: Boolean; Arg: TPasArgument; ArgResolved: TPasResolverResult; + ParentC: TClass; begin inherited FinishPropertyOfClass(PropEl); + + ParentC:=PropEl.Parent.ClassType; + if (ParentC=TPasClassType) then + begin + // class member + if TPasClassType(PropEl.Parent).IsExternal then + begin + // external class + if PropEl.Visibility=visPublished then + // Note: an external class has no typeinfo + RaiseMsg(20170413221703,nSymbolCannotBePublished,sSymbolCannotBePublished, + [],PropEl); + end; + end; + Getter:=GetPasPropertyGetter(PropEl); GetterIsBracketAccessor:=IsExternalBracketAccessor(Getter); Setter:=GetPasPropertySetter(PropEl); @@ -1912,7 +2274,7 @@ begin if RHS.IdentEl<>nil then begin if RHS.IdentEl.ClassType=TPasClassType then - Result:=cExact+1; // RHS is a class + Result:=cExact+1; // RHS is a class type end; end; end; @@ -2008,75 +2370,123 @@ begin RaiseInternalError(20170330005725); end; -function TPas2JSResolver.ResolveBracketOperatorClass(Params: TParamsExpr; - const ResolvedValue: TPasResolverResult; ClassScope: TPasClassScope; - Access: TResolvedRefAccess): boolean; +procedure TPas2JSResolver.BI_TypeInfo_OnGetCallResult( + Proc: TResElDataBuiltInProc; Params: TParamsExpr; out + ResolvedEl: TPasResolverResult); +// if an external type with the right name and external name is in scope return +// that, otherwise btPointer var - ParamResolved: TPasResolverResult; Param: TPasExpr; - aClass: TPasClassType; -begin - if ClassScope.DefaultProperty=nil then - begin - aClass:=TPasClassType(ClassScope.Element); - if IsExternalClassName(aClass,'Array') then - begin - if ResolvedValue.IdentEl is TPasType then - RaiseMsg(20170402194000,nIllegalQualifier,sIllegalQualifier,['['],Params); - if length(Params.Params)<>1 then - RaiseMsg(20170402194059,nWrongNumberOfParametersForArray, - sWrongNumberOfParametersForArray,[],Params); - // check first param is an integer value - Param:=Params.Params[0]; - ComputeElement(Param,ParamResolved,[]); - if (not (rrfReadable in ParamResolved.Flags)) - or not (ParamResolved.BaseType in btAllInteger) then - CheckRaiseTypeArgNo(20170402194221,1,Param,ParamResolved,'integer',true); - AccessExpr(Param,rraRead); - exit(true); + ParamResolved: TPasResolverResult; + C: TClass; + TIName: String; + FindData: TPRFindData; + Abort: boolean; + bt: TResolverBaseType; + jbt: TPas2jsBaseType; + TypeEl: TPasType; + FoundClass: TPasClassType; +begin + Param:=Params.Params[0]; + ComputeElement(Param,ParamResolved,[rcNoImplicitProc]); + if ParamResolved.TypeEl=nil then + RaiseInternalError(20170413090726); + TypeEl:=ResolveAliasType(ParamResolved.TypeEl); + C:=TypeEl.ClassType; + TIName:=''; + //writeln('TPas2JSResolver.BI_TypeInfo_OnGetCallResult TypeEl=',GetObjName(TypeEl)); + if C=TPasUnresolvedSymbolRef then + begin + if TypeEl.CustomData is TResElDataPas2JSBaseType then + begin + jbt:=TResElDataPas2JSBaseType(TypeEl.CustomData).JSBaseType; + if jbt=pbtJSValue then + TIName:=Pas2JSBuiltInNames[pbitnTI]; end - else if IsExternalClassName(aClass,'Object') then - begin - if ResolvedValue.IdentEl is TPasType then - RaiseMsg(20170402194453,nIllegalQualifier,sIllegalQualifier,['['],Params); - if length(Params.Params)<>1 then - RaiseMsg(20170402194456,nWrongNumberOfParametersForArray, - sWrongNumberOfParametersForArray,[],Params); - // check first param is a string value - Param:=Params.Params[0]; - ComputeElement(Param,ParamResolved,[]); - if (not (rrfReadable in ParamResolved.Flags)) - or not (ParamResolved.BaseType in btAllStringAndChars) then - CheckRaiseTypeArgNo(20170402194511,1,Param,ParamResolved,'string',true); - AccessExpr(Param,rraRead); - exit(true); + else if TypeEl.CustomData is TResElDataBaseType then + begin + bt:=TResElDataBaseType(TypeEl.CustomData).BaseType; + if bt in btAllInteger then + TIName:=Pas2JSBuiltInNames[pbitnTIInteger] + else if bt in [btString,btChar,btDouble,btBoolean] then + TIName:=Pas2JSBuiltInNames[pbitnTI] + else if bt=btPointer then + TIName:=Pas2JSBuiltInNames[pbitnTIPointer]; end; + end + else if ParamResolved.BaseType=btContext then + begin + if C=TPasEnumType then + TIName:=Pas2JSBuiltInNames[pbitnTIEnum] + else if C=TPasSetType then + TIName:=Pas2JSBuiltInNames[pbitnTISet] + else if C.InheritsFrom(TPasProcedureType) then + begin + if TPasProcedureType(TypeEl).IsOfObject then + TIName:=Pas2JSBuiltInNames[pbitnTIMethodVar] + else + TIName:=Pas2JSBuiltInNames[pbitnTIProcVar]; + end + else if C=TPasRecordType then + TIName:=Pas2JSBuiltInNames[pbitnTIRecord] + else if C=TPasClassType then + TIName:=Pas2JSBuiltInNames[pbitnTIClass] + else if C=TPasClassOfType then + TIName:=Pas2JSBuiltInNames[pbitnTIClassRef] + else if C=TPasArrayType then + begin + if length(TPasArrayType(TypeEl).Ranges)>0 then + TIName:=Pas2JSBuiltInNames[pbitnTIStaticArray] + else + TIName:=Pas2JSBuiltInNames[pbitnTIDynArray]; + end + else if C=TPasPointerType then + TIName:=Pas2JSBuiltInNames[pbitnTIPointer] + end + else if ParamResolved.BaseType=btSet then + begin + if ParamResolved.IdentEl is TPasSetType then + TIName:=Pas2JSBuiltInNames[pbitnTISet]; + end + else if ParamResolved.BaseType=btCustom then + begin + + end; + if TIName='' then + begin + {$IFDEF VerbosePas2JS} + writeln('TPas2JSResolver.BI_TypeInfo_OnGetCallResult ',GetResolverResultDesc(ParamResolved)); + {$ENDIF} + RaiseMsg(20170413091852,nTypeIdentifierExpected,sTypeIdentifierExpected,[],Param); end; - Result:=inherited ResolveBracketOperatorClass(Params, ResolvedValue, ClassScope, Access); -end; -procedure TPas2JSResolver.ComputeArrayParams_Class(Params: TParamsExpr; - var ResolvedEl: TPasResolverResult; ClassScope: TPasClassScope; - Flags: TPasResolverComputeFlags; StartEl: TPasElement); -var - aClass: TPasClassType; - OrigResolved: TPasResolverResult; -begin - aClass:=TPasClassType(ClassScope.Element); - if IsExternalClassName(aClass,'Array') or IsExternalClassName(aClass,'Object') then - begin - if [rcConstant,rcType]*Flags<>[] then - RaiseConstantExprExp(20170402202137,Params); - OrigResolved:=ResolvedEl; - SetResolverTypeExpr(ResolvedEl,btCustom,JSBaseTypes[pbtJSValue],[rrfReadable,rrfWritable]); - // identifier and value is the array/object itself - ResolvedEl.IdentEl:=OrigResolved.IdentEl; - ResolvedEl.ExprEl:=OrigResolved.ExprEl; - ResolvedEl.Flags:=OrigResolved.Flags+[rrfReadable,rrfWritable]; - exit; + // search for TIName + FindData:=Default(TPRFindData); + FindData.ErrorPosEl:=Params; + Abort:=false; + IterateElements(TIName,@OnFindFirstElement,@FindData,Abort); + {$IFDEF VerbosePas2JS} + writeln('TPas2JSResolver.BI_TypeInfo_OnGetCallResult TIName="',TIName,'" FindData.Found="',GetObjName(FindData.Found),'"'); + {$ENDIF} + if (FindData.Found<>nil) and (FindData.Found.ClassType=TPasClassType) then + begin + FoundClass:=TPasClassType(FindData.Found); + if FoundClass.IsExternal + and (FoundClass.ExternalName=Pas2JSBuiltInNames[pbivnRTL]+'.'+TIName) then + begin + // use external class definition + {$IFDEF VerbosePas2JS} + writeln('TPas2JSResolver.BI_TypeInfo_OnGetCallResult FindData.Found="',FindData.Found.FullName,'"'); + {$ENDIF} + SetResolverTypeExpr(ResolvedEl,btContext,TPasClassType(FindData.Found),[rrfReadable]); + exit; + end; end; - inherited ComputeArrayParams_Class(Params, ResolvedEl, ClassScope, Flags, - StartEl); + + // default: btPointer + SetResolverTypeExpr(ResolvedEl,btPointer,BaseTypes[btPointer],[rrfReadable]); + + if Proc=nil then ; end; constructor TPas2JSResolver.Create; @@ -2091,6 +2501,7 @@ begin ScopeClass_WithExpr:=TPas2JSWithExprScope; for bt in [pbtJSValue] do AddJSBaseType(Pas2jsBaseTypeNames[bt],bt); + AnonymousElTypePostfix:=Pas2JSBuiltInNames[pbitnAnonymousPostfix]; end; destructor TPas2JSResolver.Destroy; @@ -2103,12 +2514,21 @@ end; procedure TPas2JSResolver.AddObjFPCBuiltInIdentifiers( const TheBaseTypes: TResolveBaseTypes; const TheBaseProcs: TResolverBuiltInProcs); +var + InvalidTypes: TResolveBaseTypes; + bt: TResolverBaseType; + InvalidProcs: TResolverBuiltInProcs; + bf: TResolverBuiltInProc; begin - inherited AddObjFPCBuiltInIdentifiers( - TheBaseTypes - -btAllStrings+[btString] // allow only String - -btAllFloats+[btDouble] // allow only Double - ,TheBaseProcs); + InvalidTypes:=TheBaseTypes-btAllPas2jsBaseTypes; + if InvalidTypes<>[] then + for bt in InvalidTypes do + RaiseInternalError(20170409180202,BaseTypeNames[bt]); + InvalidProcs:=TheBaseProcs-bfAllPas2jsBaseProcs; + if InvalidProcs<>[] then + for bf in InvalidProcs do + RaiseInternalError(20170409180246,ResolverBuiltInProcNames[bf]); + inherited AddObjFPCBuiltInIdentifiers(TheBaseTypes,TheBaseProcs); end; function TPas2JSResolver.CheckTypeCastRes(const FromResolved, @@ -2167,11 +2587,8 @@ begin Result:=cExact+1 // type cast JSValue to simple base type else if ToResolved.BaseType=btContext then begin - C:=ToResolved.TypeEl.ClassType; - if (C=TPasClassType) - or (C=TPasClassOfType) - or (C=TPasEnumType) then - Result:=cExact+1; + // typecast JSValue to user type + Result:=cExact+1; end; end; exit; @@ -2440,6 +2857,16 @@ begin AddElementData(Result); end; +function TPas2JSResolver.HasTypeInfo(El: TPasType): boolean; +begin + Result:=inherited HasTypeInfo(El); + if not Result then exit; + if (El.ClassType=TPasClassType) and TPasClassType(El).IsExternal then + exit(false); + if El.Parent is TProcedureBody then + Result:=false; +end; + { TP2JConstExprData } destructor TP2JConstExprData.Destroy; @@ -2533,9 +2960,9 @@ begin Kind:=cjkDot; end; -{ TInterfaceContext } +{ TSectionContext } -constructor TInterfaceContext.Create(PasEl: TPasElement; JSEl: TJSElement; +constructor TSectionContext.Create(PasEl: TPasElement; JSEl: TJSElement; aParent: TConvertContext); begin inherited; @@ -2742,33 +3169,45 @@ end; function TPasToJSConverter.ConvertModule(El: TPasModule; AContext: TConvertContext): TJSElement; -(* Format: - rtl.module('<unitname>', - [<interface uses1>,<uses2>, ...], - function(){ - <interface> - <implementation> - this.$init=function(){ - <initialization> - }; - }, - [<implementation uses1>,<uses2>, ...]); +(* +Program: + rtl.module('program', + [<uses1>,<uses2>, ...], + function(){ + <programsection> + this.$main=function(){ + <initialization> + }; + }); + +Unit: + rtl.module('<unitname>', + [<interface uses1>,<uses2>, ...], + function(){ + var $impl = {}; + this.$impl = $impl; + <interface> + this.$init=function(){ + <initialization> + }; + }, + [<implementation uses1>,<uses2>, ...], + function(){ + var $impl = this.$impl; + <implementation> + }); *) Var OuterSrc , Src: TJSSourceElements; RegModuleCall: TJSCallExpression; ArgArray: TJSArguments; UsesList: TFPList; - FunDef: TJSFuncDef; - FunBody: TJSFunctionBody; - FunDecl: TJSFunctionDeclarationStatement; + FunDecl, ImplFunc: TJSFunctionDeclarationStatement; UsesSection: TPasSection; ModuleName: String; - IntfContext: TInterfaceContext; + IntfContext: TSectionContext; ImplVarSt: TJSVariableStatement; - VarDecl: TJSVarDeclaration; - ImplAssignSt: TJSSimpleAssignStatement; - ImplDecl: TJSElement; + HasImplUsesList: Boolean; begin Result:=Nil; OuterSrc:=TJSSourceElements(CreateElement(TJSSourceElements, El)); @@ -2782,7 +3221,7 @@ begin RegModuleCall.Args:=ArgArray; // add unitname parameter: unitname - ModuleName:=TransformModuleName(El,AContext); + ModuleName:=TransformModuleName(El,false,AContext); ArgArray.Elements.AddElement.Expr:=CreateLiteralString(El,ModuleName); // add interface-uses-section parameter: [<interface uses1>,<uses2>, ...] @@ -2796,20 +3235,17 @@ begin ArgArray.Elements.AddElement.Expr:=CreateUsesList(UsesSection,AContext); // add interface parameter: function(){} - FunDecl:=TJSFunctionDeclarationStatement.Create(0,0); + FunDecl:=CreateFunction(El,true,true); ArgArray.Elements.AddElement.Expr:=FunDecl; - FunDef:=TJSFuncDef.Create; - FunDecl.AFunction:=FunDef; - FunDef.Name:=''; - FunBody:=TJSFunctionBody.Create(0,0); - FunDef.Body:=FunBody; - Src:=TJSSourceElements(CreateElement(TJSSourceElements, El)); - FunBody.A:=Src; + Src:=FunDecl.AFunction.Body.A as TJSSourceElements; if coUseStrict in Options then AddToSourceElements(Src,CreateLiteralString(El,'use strict')); - IntfContext:=TInterfaceContext.Create(El,Src,AContext); + ImplVarSt:=nil; + HasImplUsesList:=false; + + IntfContext:=TSectionContext.Create(El,Src,AContext); try IntfContext.This:=El; if (El is TPasProgram) then @@ -2827,37 +3263,15 @@ begin else begin // unit // add implementation object at top, so the interface elemwnts can add stuff - if (FBuiltInNames[pbivnImplementation]<>'') and Assigned(El.ImplementationSection) then + if Assigned(El.ImplementationSection) then begin - // add 'var $impl = {};' - ImplVarSt:=TJSVariableStatement(CreateElement(TJSVariableStatement,El)); + // add var $impl = this.$impl + ImplVarSt:=CreateVarStatement(FBuiltInNames[pbivnImplementation], + CreateMemberExpression(['this',FBuiltInNames[pbivnImplementation]]),El); AddToSourceElements(Src,ImplVarSt); - VarDecl:=TJSVarDeclaration(CreateElement(TJSVarDeclaration,El)); - ImplVarSt.A:=VarDecl; - VarDecl.Name:=FBuiltInNames[pbivnImplementation]; - VarDecl.Init:=TJSEmptyBlockStatement(CreateElement(TJSEmptyBlockStatement,El.ImplementationSection)); - // add 'this.$impl = $impl;' - ImplAssignSt:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,El)); - AddToSourceElements(Src,ImplAssignSt); - ImplAssignSt.LHS:=CreateBuiltInIdentifierExpr('this.'+FBuiltInNames[pbivnImplementation]); - ImplAssignSt.Expr:=CreateBuiltInIdentifierExpr(FBuiltInNames[pbivnImplementation]); - end - else - begin - ImplVarSt:=nil; - ImplAssignSt:=nil; end; if Assigned(El.InterfaceSection) then AddToSourceElements(Src,ConvertDeclarations(El.InterfaceSection,IntfContext)); - if ImplVarSt<>nil then - begin - ImplDecl:=CreateImplementationSection(El,Src,IntfContext); - if ImplDecl=nil then - begin - RemoveFromSourceElements(Src,ImplVarSt); - RemoveFromSourceElements(Src,ImplAssignSt); - end; - end; CreateInitSection(El,Src,IntfContext); // add optional implementation uses list: [<implementation uses1>,<uses2>, ...] @@ -2865,12 +3279,34 @@ begin begin UsesList:=El.ImplementationSection.UsesList; if (UsesList<>nil) and (UsesList.Count>0) then + begin ArgArray.Elements.AddElement.Expr:=CreateUsesList(El.ImplementationSection,AContext); + HasImplUsesList:=true; + end; end; + end; finally IntfContext.Free; end; + + // add implementation function + if ImplVarSt<>nil then + begin + ImplFunc:=CreateImplementationSection(El,AContext); + if ImplFunc=nil then + begin + // remove unneeded $impl from interface + RemoveFromSourceElements(Src,ImplVarSt); + end + else + begin + // add param + if not HasImplUsesList then + ArgArray.Elements.AddElement.Expr:=CreateLiteralNull(El); + ArgArray.Elements.AddElement.Expr:=ImplFunc; + end; + end; end; function TPasToJSConverter.CreateElement(C: TJSElementClass; Src: TPasElement @@ -2937,18 +3373,26 @@ begin Result:=C; end; -function TPasToJSConverter.CreateFunction(El: TPasElement; WithBody: boolean - ): TJSFunctionDeclarationStatement; +function TPasToJSConverter.CreateFunction(El: TPasElement; WithBody: boolean; + WithSrc: boolean): TJSFunctionDeclarationStatement; var FuncDef: TJSFuncDef; FuncSt: TJSFunctionDeclarationStatement; + Src: TJSSourceElements; begin FuncSt:=TJSFunctionDeclarationStatement(CreateElement(TJSFunctionDeclarationStatement,El)); Result:=FuncSt; FuncDef:=TJSFuncDef.Create; FuncSt.AFunction:=FuncDef; if WithBody then + begin FuncDef.Body:=TJSFunctionBody(CreateElement(TJSFunctionBody,El)); + if WithSrc then + begin + Src:=TJSSourceElements(CreateElement(TJSSourceElements, El)); + FuncDef.Body.A:=Src; + end; + end; end; function TPasToJSConverter.ConvertUnaryExpression(El: TUnaryExpr; @@ -3117,6 +3561,66 @@ begin Result:=false; end; +procedure TPasToJSConverter.ComputeRange( + const RangeResolved: TPasResolverResult; out MinValue, MaxValue: int64; + ErrorEl: TPasElement); +var + EnumType: TPasEnumType; +begin + if RangeResolved.BaseType in btAllBooleans then + begin + MinValue:=0; + MaxValue:=1; + end + else if RangeResolved.BaseType=btShortInt then + begin + MinValue:=-$80; + MaxValue:=-$7f; + end + else if RangeResolved.BaseType=btByte then + begin + MinValue:=0; + MaxValue:=$ff; + end + else if RangeResolved.BaseType=btSmallInt then + begin + MinValue:=-$8000; + MaxValue:=$7fff; + end + else if RangeResolved.BaseType=btWord then + begin + MinValue:=0; + MaxValue:=$ffff; + end + else if RangeResolved.BaseType=btLongint then + begin + MinValue:=-$80000000; + MaxValue:=$7fffffff; + end + else if RangeResolved.BaseType=btCardinal then + begin + MinValue:=0; + MaxValue:=$ffffffff; + end + else if RangeResolved.BaseType in [btChar,btWideChar] then + begin + MinValue:=0; + MaxValue:=$ffff; + end + else if RangeResolved.BaseType=btContext then + begin + if RangeResolved.TypeEl.ClassType=TPasEnumType then + begin + EnumType:=TPasEnumType(RangeResolved.TypeEl); + MinValue:=0; + MaxValue:=EnumType.Values.Count-1; + end; + end + else + DoError(20170411224022,nPasElementNotSupported,sPasElementNotSupported, + [BaseTypeNames[RangeResolved.BaseType]],ErrorEl); +end; + function TPasToJSConverter.ConvertBinaryExpression(El: TBinaryExpr; AContext: TConvertContext): TJSElement; Const @@ -3229,8 +3733,8 @@ begin else // otherwise -> "rtl.as(A,B)" Call.Expr:=CreateBuiltInIdentifierExpr(FBuiltInNames[pbivnRTL]+'.'+FBuiltInNames[pbifnAs]); - Call.Args.Elements.AddElement.Expr:=A; - Call.Args.Elements.AddElement.Expr:=B; + Call.AddArg(A); + Call.AddArg(B); Result:=Call; exit; end; @@ -3273,7 +3777,7 @@ begin begin // convert "a div b" to "Math.floor(a/b)" Call:=CreateCallExpression(El); - Call.Args.Elements.AddElement.Expr:=R; + Call.AddArg(R); Call.Expr:=CreateBuiltInIdentifierExpr('Math.floor'); Result:=Call; end; @@ -3299,9 +3803,9 @@ function TPasToJSConverter.ConvertBinaryExpressionRes(El: TBinaryExpr; // convert "proctypeA = proctypeB" to "rtl.eqCallback(proctypeA,proctypeB)" Call:=CreateCallExpression(El); Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnProcType_Equal]]); - Call.Args.Elements.AddElement.Expr:=A; + Call.AddArg(A); A:=nil; - Call.Args.Elements.AddElement.Expr:=B; + Call.AddArg(B); B:=nil; if El.OpCode=eopNotEqual then begin @@ -3317,9 +3821,9 @@ function TPasToJSConverter.ConvertBinaryExpressionRes(El: TBinaryExpr; var FunName: String; Call: TJSCallExpression; - Bracket: TJSBracketMemberExpression; DotExpr: TJSDotMemberExpression; NotEl: TJSUnaryNotExpression; + InOp: TJSRelationalExpressionIn; begin {$IFDEF VerbosePas2JS} writeln('TPasToJSConverter.ConvertBinaryExpressionRes OpCode="',OpcodeStrings[El.OpCode],'" Left=',GetResolverResultDesc(LeftResolved),' Right=',GetResolverResultDesc(RightResolved)); @@ -3342,22 +3846,27 @@ begin end; Call:=CreateCallExpression(El); Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FunName]); - Call.Args.Elements.AddElement.Expr:=A; + Call.AddArg(A); A:=nil; - Call.Args.Elements.AddElement.Expr:=B; + Call.AddArg(B); B:=nil; Result:=Call; exit; end else if (RightResolved.BaseType=btSet) and (El.OpCode=eopIn) then begin - // a in b -> b[a] - Bracket:=TJSBracketMemberExpression(CreateElement(TJSBracketMemberExpression,El)); - Bracket.MExpr:=B; - B:=nil; - Bracket.Name:=A; + // a in b -> a in b + if not (A is TJSLiteral) or (TJSLiteral(A).Value.ValueType<>jstNumber) then + begin + FreeAndNil(A); + A:=CreateSetLiteralElement(El.left,AContext); + end; + InOp:=TJSRelationalExpressionIn(CreateElement(TJSRelationalExpressionIn,El)); + InOp.A:=A; A:=nil; - Result:=Bracket; + InOp.B:=B; + B:=nil; + Result:=InOp; exit; end else if (El.OpCode=eopIs) then @@ -3365,7 +3874,7 @@ begin // "A is B" Call:=CreateCallExpression(El); Result:=Call; - Call.Args.Elements.AddElement.Expr:=A; A:=nil; + Call.AddArg(A); A:=nil; if RightResolved.IdentEl is TPasClassOfType then begin // "A is class-of-type" -> "A is class" @@ -3376,13 +3885,13 @@ begin begin // B is an external class -> "rtl.isExt(A,B)" Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnIsExt]]); - Call.Args.Elements.AddElement.Expr:=B; B:=nil; + Call.AddArg(B); B:=nil; end else if LeftResolved.TypeEl is TPasClassOfType then begin // A is a TPasClassOfType -> "rtl.is(A,B)" Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnIs]]); - Call.Args.Elements.AddElement.Expr:=B; B:=nil; + Call.AddArg(B); B:=nil; end else begin @@ -3415,7 +3924,7 @@ begin Call:=CreateCallExpression(El); Call.Expr:=CreateDotExpression(El,A,CreateBuiltInIdentifierExpr(FBuiltInNames[pbifnRecordEqual])); A:=nil; - Call.Args.Elements.AddElement.Expr:=B; + Call.AddArg(B); B:=nil; if El.OpCode=eopNotEqual then begin @@ -3544,7 +4053,7 @@ var CurName: String; begin CurName:=TransformVariableName(El,Name,AContext); - if (FBuiltInNames[pbivnImplementation]<>'') and (El.Parent.ClassType=TImplementationSection) then + if El.Parent.ClassType=TImplementationSection then CurName:=FBuiltInNames[pbivnImplementation]+'.'+CurName else CurName:='this.'+CurName; @@ -3690,7 +4199,7 @@ begin Call:=CreateCallExpression(El); AssignContext.Call:=Call; Call.Expr:=CreateReferencePathExpr(Decl,AContext,false,Ref); - Call.Args.Elements.AddElement.Expr:=AssignContext.RightSide; + Call.AddArg(AssignContext.RightSide); AssignContext.RightSide:=nil; Result:=Call; exit; @@ -3734,7 +4243,7 @@ begin Call.Expr:=CreateDotExpression(El, CreateIdentifierExpr(Arg.Name,Arg,AContext), CreateBuiltInIdentifierExpr(TempRefObjSetterName)); - Call.Args.Elements.AddElement.Expr:=AssignContext.RightSide; + Call.AddArg(AssignContext.RightSide); AssignContext.RightSide:=nil; Result:=Call; exit; @@ -3763,7 +4272,7 @@ begin case BuiltInProc.BuiltIn of bfBreak: Result:=ConvertBuiltInBreak(El,AContext); bfContinue: Result:=ConvertBuiltInContinue(El,AContext); - bfExit: Result:=ConvertBuiltInExit(El,AContext); + bfExit: Result:=ConvertBuiltIn_Exit(El,AContext); else RaiseNotSupported(El,AContext,20161130164955,'built in proc '+ResolverBuiltInProcNames[BuiltInProc.BuiltIn]); end; @@ -3776,7 +4285,7 @@ begin writeln('TPasToJSConverter.ConvertIdentifierExpr ',GetObjName(El),' Decl=',GetObjName(Decl),' Decl.Parent=',GetObjName(Decl.Parent)); {$ENDIF} if Decl is TPasModule then - Name:=FBuiltInNames[pbivnModules]+'.'+TransformModuleName(TPasModule(Decl),AContext) + Name:=TransformModuleName(TPasModule(Decl),true,AContext) else if (Decl is TPasFunctionType) and (CompareText(ResolverResultVar,El.Value)=0) then Name:=ResolverResultVar else if Decl.ClassType=TPasEnumValue then @@ -3891,9 +4400,9 @@ function TPasToJSConverter.ConvertInheritedExpression(El: TInheritedExpr; try Call:=CreateCallExpression(ParentEl); Call.Expr:=CreateBuiltInIdentifierExpr(FunName); - Call.Args.Elements.AddElement.Expr:=CreateBuiltInIdentifierExpr('this'); + Call.AddArg(CreateBuiltInIdentifierExpr('this')); if Apply then - Call.Args.Elements.AddElement.Expr:=CreateBuiltInIdentifierExpr('arguments') + Call.AddArg(CreateBuiltInIdentifierExpr('arguments')) else CreateProcedureCall(Call,ParamsExpr,AncestorProc.ProcType,AContext); Result:=Call; @@ -4394,7 +4903,7 @@ begin if AContext.Resolver=nil then begin // without Resolver - if Length(El.Params)<>1 then + if Length(El.Params)>1 then RaiseNotSupported(El,AContext,20170207151325,'Cannot convert 2-dim arrays'); B:=TJSBracketMemberExpression(CreateElement(TJSBracketMemberExpression,El)); try @@ -4432,12 +4941,9 @@ begin if TypeEl.ClassType=TPasClassType then begin aClass:=TPasClassType(TypeEl); - ClassScope:=TypeEl.CustomData as TPas2JSClassScope; + ClassScope:=aClass.CustomData as TPas2JSClassScope; if ClassScope.DefaultProperty<>nil then ConvertDefaultProperty(ResolvedEl,ClassScope.DefaultProperty) - else if AContext.Resolver.IsExternalClassName(aClass,'Array') - or AContext.Resolver.IsExternalClassName(aClass,'Object') then - ConvertJSObject else RaiseInconsistency(20170206180448); end @@ -4468,7 +4974,7 @@ var Elements: TJSArrayLiteralElements; E: TJSArrayLiteral; OldAccess: TCtxAccess; - DeclResolved, ParamResolved: TPasResolverResult; + DeclResolved, ParamResolved, ValueResolved: TPasResolverResult; Param: TPasExpr; JSBaseType: TPas2jsBaseType; C: TClass; @@ -4498,26 +5004,27 @@ begin writeln('TPasToJSConverter.ConvertFuncParams ',Decl.Name,' ',ResolverBuiltInProcNames[BuiltInProc.BuiltIn]); {$ENDIF} case BuiltInProc.BuiltIn of - bfLength: Result:=ConvertBuiltInLength(El,AContext); - bfSetLength: Result:=ConvertBuiltInSetLength(El,AContext); - bfInclude: Result:=ConvertBuiltInExcludeInclude(El,AContext,true); - bfExclude: Result:=ConvertBuiltInExcludeInclude(El,AContext,false); - bfExit: Result:=ConvertBuiltInExit(El,AContext); + bfLength: Result:=ConvertBuiltIn_Length(El,AContext); + bfSetLength: Result:=ConvertBuiltIn_SetLength(El,AContext); + bfInclude: Result:=ConvertBuiltIn_ExcludeInclude(El,AContext,true); + bfExclude: Result:=ConvertBuiltIn_ExcludeInclude(El,AContext,false); + bfExit: Result:=ConvertBuiltIn_Exit(El,AContext); bfInc, - bfDec: Result:=ConvertBuiltInIncDec(El,AContext); - bfAssigned: Result:=ConvertBuiltInAssigned(El,AContext); - bfChr: Result:=ConvertBuiltInChr(El,AContext); - bfOrd: Result:=ConvertBuiltInOrd(El,AContext); - bfLow: Result:=ConvertBuiltInLow(El,AContext); - bfHigh: Result:=ConvertBuiltInHigh(El,AContext); - bfPred: Result:=ConvertBuiltInPred(El,AContext); - bfSucc: Result:=ConvertBuiltInSucc(El,AContext); - bfStrProc: Result:=ConvertBuiltInStrProc(El,AContext); - bfStrFunc: Result:=ConvertBuiltInStrFunc(El,AContext); - bfConcatArray: Result:=ConvertBuiltInConcatArray(El,AContext); - bfCopyArray: Result:=ConvertBuiltInCopyArray(El,AContext); - bfInsertArray: Result:=ConvertBuiltInInsertArray(El,AContext); - bfDeleteArray: Result:=ConvertBuiltInDeleteArray(El,AContext); + bfDec: Result:=ConvertBuiltIn_IncDec(El,AContext); + bfAssigned: Result:=ConvertBuiltIn_Assigned(El,AContext); + bfChr: Result:=ConvertBuiltIn_Chr(El,AContext); + bfOrd: Result:=ConvertBuiltIn_Ord(El,AContext); + bfLow: Result:=ConvertBuiltIn_Low(El,AContext); + bfHigh: Result:=ConvertBuiltIn_High(El,AContext); + bfPred: Result:=ConvertBuiltIn_Pred(El,AContext); + bfSucc: Result:=ConvertBuiltIn_Succ(El,AContext); + bfStrProc: Result:=ConvertBuiltIn_StrProc(El,AContext); + bfStrFunc: Result:=ConvertBuiltIn_StrFunc(El,AContext); + bfConcatArray: Result:=ConvertBuiltIn_ConcatArray(El,AContext); + bfCopyArray: Result:=ConvertBuiltIn_CopyArray(El,AContext); + bfInsertArray: Result:=ConvertBuiltIn_InsertArray(El,AContext); + bfDeleteArray: Result:=ConvertBuiltIn_DeleteArray(El,AContext); + bfTypeInfo: Result:=ConvertBuiltIn_TypeInfo(El,AContext); else RaiseNotSupported(El,AContext,20161130164955,'built in proc '+ResolverBuiltInProcNames[BuiltInProc.BuiltIn]); end; @@ -4572,7 +5079,7 @@ begin // TObject(jsvalue) -> rtl.getObject(jsvalue) Call:=CreateCallExpression(El); Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnGetObject]]); - Call.Args.Elements.AddElement.Expr:=Result; + Call.AddArg(Result); Result:=Call; end; end; @@ -4598,7 +5105,19 @@ begin else if (C=TPasProcedureType) or (C=TPasFunctionType) then begin - TargetProcType:=TPasProcedureType(Decl); + AContext.Resolver.ComputeElement(El.Value,ValueResolved,[rcNoImplicitProc]); + if ValueResolved.IdentEl is TPasProcedureType then + begin + // type cast to proc type + Param:=El.Params[0]; + Result:=ConvertElement(Param,AContext); + exit; + end + else + begin + // calling proc var + TargetProcType:=TPasProcedureType(Decl); + end; end else begin @@ -4728,9 +5247,9 @@ begin end; function TPasToJSConverter.ConvertTypeCastToBaseType(El: TParamsExpr; - AContext: TConvertContext; BaseTypeData: TResElDataBaseType): TJSElement; + AContext: TConvertContext; ToBaseTypeData: TResElDataBaseType): TJSElement; var - bt: TResolverBaseType; + to_bt: TResolverBaseType; Param: TPasExpr; ParamResolved: TPasResolverResult; NotEqual: TJSEqualityExpressionNE; @@ -4763,8 +5282,8 @@ begin JSBaseTypeData:=nil; JSBaseType:=pbtNone; - bt:=BaseTypeData.BaseType; - if bt in btAllInteger then + to_bt:=ToBaseTypeData.BaseType; + if to_bt in btAllInteger then begin if ParamResolved.BaseType in btAllInteger then begin @@ -4793,13 +5312,13 @@ begin // Note: convert value first in case it raises an exception Call:=CreateCallExpression(El); Call.Expr:=CreateMemberExpression(['Math','floor']); - Call.Args.Elements.AddElement.Expr:=Result; + Call.AddArg(Result); Result:=Call; exit; end; end; end - else if bt in btAllBooleans then + else if to_bt in btAllBooleans then begin if ParamResolved.BaseType in btAllBooleans then begin @@ -4834,7 +5353,7 @@ begin end; end; end - else if bt in btAllFloats then + else if to_bt in btAllFloats then begin if ParamResolved.BaseType in (btAllFloats+btAllInteger) then begin @@ -4851,13 +5370,13 @@ begin // Note: convert value first in case it raises an exception Call:=CreateCallExpression(El); Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnGetNumber]]); - Call.Args.Elements.AddElement.Expr:=Result; + Call.AddArg(Result); Result:=Call; exit; end; end; end - else if bt in btAllStrings then + else if to_bt in btAllStrings then begin if ParamResolved.BaseType in btAllStringAndChars then begin @@ -4880,7 +5399,7 @@ begin end; end; end - else if bt=btChar then + else if to_bt=btChar then begin if ParamResolved.BaseType=btChar then begin @@ -4897,15 +5416,33 @@ begin // Note: convert value first in case it raises an exception Call:=CreateCallExpression(El); Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnGetChar]]); - Call.Args.Elements.AddElement.Expr:=Result; + Call.AddArg(Result); Result:=Call; exit; end; end; end - else if (bt=btCustom) and (BaseTypeData is TResElDataPas2JSBaseType) then + else if to_bt=btPointer then + begin + if IsParamPas2JSBaseType then + begin + if JSBaseType=pbtJSValue then + begin + // convert jsvalue to pointer -> pass through + Result:=ConvertElement(Param,AContext); + exit; + end; + end + else if ParamResolved.BaseType=btContext then + begin + // convert user type/value to pointer -> pass through + Result:=ConvertElement(Param,AContext); + exit; + end; + end + else if (to_bt=btCustom) and (ToBaseTypeData is TResElDataPas2JSBaseType) then begin - JSBaseType:=TResElDataPas2JSBaseType(BaseTypeData).JSBaseType; + JSBaseType:=TResElDataPas2JSBaseType(ToBaseTypeData).JSBaseType; if JSBaseType=pbtJSValue then begin // type cast to jsvalue @@ -4920,7 +5457,7 @@ begin // TObject(vsvalue) -> rtl.getObject(vsvalue) Call:=CreateCallExpression(El); Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnGetObject]]); - Call.Args.Elements.AddElement.Expr:=Result; + Call.AddArg(Result); Result:=Call; end; end; @@ -4928,7 +5465,7 @@ begin end; end; {$IFDEF VerbosePas2JS} - writeln('TPasToJSConverter.ConvertTypeCastToBaseType BaseTypeData=',BaseTypeNames[bt],' ParamResolved=',GetResolverResultDesc(ParamResolved)); + writeln('TPasToJSConverter.ConvertTypeCastToBaseType BaseTypeData=',BaseTypeNames[to_bt],' ParamResolved=',GetResolverResultDesc(ParamResolved)); {$ENDIF} RaiseNotSupported(El,AContext,20170325161150); end; @@ -4938,8 +5475,15 @@ function TPasToJSConverter.ConvertSetLiteral(El: TParamsExpr; var Call: TJSCallExpression; ArgContext: TConvertContext; + + procedure AddArg(Expr: TPasExpr); + begin + Result:=CreateSetLiteralElement(Expr,ArgContext); + Call.AddArg(Result); + end; + +var i: Integer; - Arg: TJSElement; ArgEl: TPasExpr; begin if El.Kind<>pekSet then @@ -4966,21 +5510,12 @@ begin if (ArgEl.ClassType=TBinaryExpr) and (TBinaryExpr(ArgEl).Kind=pekRange) then begin // range -> add three parameters: null,left,right - // ToDo: error if left>right - // add null - Call.Args.Elements.AddElement.Expr:=CreateLiteralNull(ArgEl); - // add left - Arg:=ConvertElement(TBinaryExpr(ArgEl).left,ArgContext); - Call.Args.Elements.AddElement.Expr:=Arg; - // add right - Arg:=ConvertElement(TBinaryExpr(ArgEl).right,ArgContext); - Call.Args.Elements.AddElement.Expr:=Arg; + Call.AddArg(CreateLiteralNull(ArgEl)); + AddArg(TBinaryExpr(ArgEl).left); + AddArg(TBinaryExpr(ArgEl).right); end else - begin - Arg:=ConvertElement(ArgEl,ArgContext); - Call.Args.Elements.AddElement.Expr:=Arg; - end; + AddArg(ArgEl); end; Result:=Call; finally @@ -5025,14 +5560,15 @@ begin end; end; -function TPasToJSConverter.ConvertBuiltInLength(El: TParamsExpr; +function TPasToJSConverter.ConvertBuiltIn_Length(El: TParamsExpr; AContext: TConvertContext): TJSElement; var Arg: TJSElement; - Param: TPasExpr; + Param, RangeEl: TPasExpr; ParamResolved, RangeResolved: TPasResolverResult; Ranges: TPasExprArray; Call: TJSCallExpression; + aMinValue, aMaxValue: int64; begin Result:=nil; Param:=El.Params[0]; @@ -5047,20 +5583,11 @@ begin // static array -> number literal if length(Ranges)>1 then RaiseNotSupported(El,AContext,20170223131042); - AContext.Resolver.ComputeElement(Ranges[0],RangeResolved,[rcConstant]); - if RangeResolved.BaseType=btContext then - begin - if RangeResolved.IdentEl is TPasEnumType then - begin - Result:=CreateLiteralNumber(El,TPasEnumType(RangeResolved.IdentEl).Values.Count); - exit; - end; - end - else if RangeResolved.BaseType=btBoolean then - begin - Result:=CreateLiteralNumber(El,2); - exit; - end; + RangeEl:=Ranges[0]; + AContext.Resolver.ComputeElement(RangeEl,RangeResolved,[rcType]); + ComputeRange(RangeResolved,aMinValue,aMaxValue,RangeEl); + Result:=CreateLiteralNumber(El,aMaxValue-aMinValue+1); + exit; end else begin @@ -5069,7 +5596,7 @@ begin // Note: convert param first, it may raise an exception Call:=CreateCallExpression(El); Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnArray_Length]]); - Call.Args.Elements.AddElement.Expr:=Result; + Call.AddArg(Result); Result:=Call; exit; end; @@ -5081,7 +5608,7 @@ begin Result:=CreateDotExpression(El,Arg,CreateBuiltInIdentifierExpr('length')); end; -function TPasToJSConverter.ConvertBuiltInSetLength(El: TParamsExpr; +function TPasToJSConverter.ConvertBuiltIn_SetLength(El: TParamsExpr; AContext: TConvertContext): TJSElement; // convert "SetLength(a,Len)" to "a = rtl.arraySetLength(a,Len)" var @@ -5122,16 +5649,16 @@ begin AssignContext.RightSide:=Call; Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnArray_SetLength]]); // 1st param: AnArray - Call.Args.Elements.AddElement.Expr:=ConvertElement(Param0,AContext); + Call.AddArg(ConvertElement(Param0,AContext)); // 2nd param: newlength - Call.Args.Elements.AddElement.Expr:=ConvertElement(El.Params[1],AContext); + Call.AddArg(ConvertElement(El.Params[1],AContext)); // 3rd param: default value ElType:=AContext.Resolver.ResolveAliasType(ArrayType.ElType); if ElType.ClassType=TPasRecordType then ValInit:=CreateReferencePathExpr(ElType,AContext) else ValInit:=CreateValInit(ElType,nil,Param0,AContext); - Call.Args.Elements.AddElement.Expr:=ValInit; + Call.AddArg(ValInit); // create left side: array = Result:=CreateAssignStatement(Param0,AssignContext); @@ -5163,7 +5690,7 @@ begin RaiseNotSupported(El.Value,AContext,20170130141026,'setlength '+GetResolverResultDesc(ResolvedParam0)); end; -function TPasToJSConverter.ConvertBuiltInExcludeInclude(El: TParamsExpr; +function TPasToJSConverter.ConvertBuiltIn_ExcludeInclude(El: TParamsExpr; AContext: TConvertContext; IsInclude: boolean): TJSElement; // convert "Include(aSet,Enum)" to "aSet=rtl.includeSet(aSet,Enum)" var @@ -5187,8 +5714,8 @@ begin else FunName:=FBuiltInNames[pbifnSet_Exclude]; Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FunName]); - Call.Args.Elements.AddElement.Expr:=ConvertElement(Param0,AContext); - Call.Args.Elements.AddElement.Expr:=ConvertElement(El.Params[1],AContext); + Call.AddArg(ConvertElement(Param0,AContext)); + Call.AddArg(ConvertElement(El.Params[1],AContext)); Result:=CreateAssignStatement(Param0,AssignContext); finally @@ -5211,7 +5738,7 @@ begin Result:=TJSBreakStatement(CreateElement(TJSBreakStatement,El)); end; -function TPasToJSConverter.ConvertBuiltInExit(El: TPasExpr; +function TPasToJSConverter.ConvertBuiltIn_Exit(El: TPasExpr; AContext: TConvertContext): TJSElement; // convert "exit;" -> in a function: "return result;" in a procedure: "return;" // convert "exit(param);" -> "return param;" @@ -5237,7 +5764,7 @@ begin end; end; -function TPasToJSConverter.ConvertBuiltInIncDec(El: TParamsExpr; +function TPasToJSConverter.ConvertBuiltIn_IncDec(El: TParamsExpr; AContext: TConvertContext): TJSElement; // convert inc(a,b) to a+=b // convert dec(a,b) to a-=b @@ -5256,7 +5783,7 @@ begin AssignSt.Expr:=ConvertExpression(El.Params[1],AContext); end; -function TPasToJSConverter.ConvertBuiltInAssigned(El: TParamsExpr; +function TPasToJSConverter.ConvertBuiltIn_Assigned(El: TParamsExpr; AContext: TConvertContext): TJSElement; var NE: TJSEqualityExpressionNE; @@ -5274,7 +5801,17 @@ begin {$IFDEF VerbosePas2JS} writeln('TPasToJSConverter.ConvertBuiltInAssigned ParamResolved=',GetResolverResultDesc(ParamResolved)); {$ENDIF} - if ParamResolved.BaseType=btContext then + if ParamResolved.BaseType=btPointer then + begin + // convert Assigned(value) -> value!=null + Result:=ConvertElement(Param,AContext); + // Note: convert Param first, it may raise an exception + NE:=TJSEqualityExpressionNE(CreateElement(TJSEqualityExpressionNE,El)); + NE.A:=Result; + NE.B:=CreateLiteralNull(El); + Result:=NE; + end + else if ParamResolved.BaseType=btContext then begin C:=ParamResolved.TypeEl.ClassType; if (C=TPasClassType) @@ -5297,7 +5834,7 @@ begin GT:=TJSRelationalExpressionGT(CreateElement(TJSRelationalExpressionGT,El)); Call:=CreateCallExpression(El); Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnArray_Length]]); - Call.Args.Elements.AddElement.Expr:=Result; + Call.AddArg(Result); GT.A:=Call; GT.B:=CreateLiteralNumber(El,0); Result:=GT; @@ -5307,7 +5844,7 @@ begin end; end; -function TPasToJSConverter.ConvertBuiltInChr(El: TParamsExpr; +function TPasToJSConverter.ConvertBuiltIn_Chr(El: TParamsExpr; AContext: TConvertContext): TJSElement; var ParamResolved: TPasResolverResult; @@ -5326,19 +5863,22 @@ begin // Note: convert Param first, as it might raise an exception Call:=CreateCallExpression(El); Call.Expr:=CreateMemberExpression(['String','fromCharCode']); - Call.Args.Elements.AddElement.Expr:=Result; + Call.AddArg(Result); Result:=Call; exit; end; DoError(20170325185906,nExpectedXButFoundY,sExpectedXButFoundY,['integer',GetResolverResultDescription(ParamResolved)],Param); end; -function TPasToJSConverter.ConvertBuiltInOrd(El: TParamsExpr; +function TPasToJSConverter.ConvertBuiltIn_Ord(El: TParamsExpr; AContext: TConvertContext): TJSElement; var - ParamResolved: TPasResolverResult; - Param: TPasExpr; + ParamResolved, SubParamResolved: TPasResolverResult; + Param, SubParam: TPasExpr; Call: TJSCallExpression; + SubParams: TParamsExpr; + SubParamJS: TJSElement; + Minus: TJSAdditiveExpressionMinus; begin Result:=nil; if AContext.Resolver=nil then @@ -5347,6 +5887,38 @@ begin AContext.Resolver.ComputeElement(Param,ParamResolved,[]); if ParamResolved.BaseType=btChar then begin + if Param is TParamsExpr then + begin + SubParams:=TParamsExpr(Param); + if SubParams.Kind=pekArrayParams then + begin + // e.g. ord(something[index]) + SubParam:=SubParams.Value; + AContext.Resolver.ComputeElement(SubParam,SubParamResolved,[]); + if SubParamResolved.BaseType in btAllStrings then + begin + // e.g. ord(aString[index]) -> aString.charCodeAt(index-1) + SubParamJS:=ConvertElement(SubParam,AContext); + // Note: convert SubParam first, as it might raise an exception + Call:=nil; + try + Call:=CreateCallExpression(El); + Call.Expr:=CreateDotExpression(El,SubParamJS,CreateBuiltInIdentifierExpr('charCodeAt')); + Minus:=TJSAdditiveExpressionMinus(CreateElement(TJSAdditiveExpressionMinus,Param)); + Call.AddArg(Minus); + if length(SubParams.Params)<>1 then + RaiseInconsistency(20170405231706); + Minus.A:=ConvertElement(SubParams.Params[0],AContext); + Minus.B:=CreateLiteralNumber(Param,1); + Result:=Call; + finally + if Result=nil then + Call.Free; + end; + exit; + end; + end; + end; // ord(aChar) -> aChar.charCodeAt() Result:=ConvertElement(Param,AContext); // Note: convert Param first, as it might raise an exception @@ -5367,7 +5939,7 @@ begin DoError(20170210105339,nExpectedXButFoundY,sExpectedXButFoundY,['enum',GetResolverResultDescription(ParamResolved)],Param); end; -function TPasToJSConverter.ConvertBuiltInLow(El: TParamsExpr; +function TPasToJSConverter.ConvertBuiltIn_Low(El: TParamsExpr; AContext: TConvertContext): TJSElement; // low(enumtype) -> first enumvalue // low(set var) -> first enumvalue @@ -5463,7 +6035,7 @@ begin DoError(20170210110717,nExpectedXButFoundY,sExpectedXButFoundY,['enum or array',GetResolverResultDescription(ResolvedEl)],Param); end; -function TPasToJSConverter.ConvertBuiltInHigh(El: TParamsExpr; +function TPasToJSConverter.ConvertBuiltIn_High(El: TParamsExpr; AContext: TConvertContext): TJSElement; // high(enumtype) -> last enumvalue // high(set var) -> last enumvalue @@ -5485,6 +6057,7 @@ var TypeEl: TPasType; MinusExpr: TJSAdditiveExpressionMinus; Call: TJSCallExpression; + aMinValue, aMaxValue: int64; begin Result:=nil; if AContext.Resolver=nil then @@ -5518,7 +6091,7 @@ begin // Note: convert Param first, it may raise an exception Call:=CreateCallExpression(El); Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnArray_Length]]); - Call.Args.Elements.AddElement.Expr:=Result; + Call.AddArg(Result); MinusExpr:=TJSAdditiveExpressionMinus(CreateElement(TJSAdditiveExpressionMinus,El)); MinusExpr.A:=Call; MinusExpr.B:=CreateLiteralNumber(El,1); @@ -5542,6 +6115,12 @@ begin begin Result:=CreateLiteralBoolean(Param,HighJSBoolean); exit; + end + else if RangeResolved.BaseType in btAllInteger then + begin + ComputeRange(RangeResolved,aMinValue,aMaxValue,Range); + Result:=CreateLiteralNumber(Param,aMaxValue); + exit; end; end; RaiseNotSupported(El,AContext,20170222231101); @@ -5565,7 +6144,7 @@ begin DoError(20170210114139,nExpectedXButFoundY,sExpectedXButFoundY,['enum or array',GetResolverResultDescription(ResolvedEl)],Param); end; -function TPasToJSConverter.ConvertBuiltInPred(El: TParamsExpr; +function TPasToJSConverter.ConvertBuiltIn_Pred(El: TParamsExpr; AContext: TConvertContext): TJSElement; // pred(enumvalue) -> enumvalue-1 var @@ -5592,7 +6171,7 @@ begin DoError(20170210120039,nExpectedXButFoundY,sExpectedXButFoundY,['enum',GetResolverResultDescription(ResolvedEl)],Param); end; -function TPasToJSConverter.ConvertBuiltInSucc(El: TParamsExpr; +function TPasToJSConverter.ConvertBuiltIn_Succ(El: TParamsExpr; AContext: TConvertContext): TJSElement; // succ(enumvalue) -> enumvalue+1 var @@ -5619,7 +6198,7 @@ begin DoError(20170210120626,nExpectedXButFoundY,sExpectedXButFoundY,['enum',GetResolverResultDescription(ResolvedEl)],Param); end; -function TPasToJSConverter.ConvertBuiltInStrProc(El: TParamsExpr; +function TPasToJSConverter.ConvertBuiltIn_StrProc(El: TParamsExpr; AContext: TConvertContext): TJSElement; // convert 'str(value,aString)' to 'aString = <string>' // for the conversion see ConvertBuiltInStrFunc @@ -5646,7 +6225,7 @@ begin end; end; -function TPasToJSConverter.ConvertBuiltInStrFunc(El: TParamsExpr; +function TPasToJSConverter.ConvertBuiltIn_StrFunc(El: TParamsExpr; AContext: TConvertContext): TJSElement; // convert 'str(boolean)' to '""+boolean' // convert 'str(integer)' to '""+integer' @@ -5729,7 +6308,7 @@ begin NeedStrLit:=false; Call:=CreateCallExpression(El); Call.Expr:=CreateDotExpression(El,Add,CreateBuiltInIdentifierExpr('toFixed')); - Call.Args.Elements.AddElement.Expr:=ConvertElement(El.format2,AContext); + Call.AddArg(ConvertElement(El.format2,AContext)); Add:=Call; Call:=nil; end; @@ -5761,9 +6340,9 @@ begin // create 'rtl.spaceLeft(add,width)' Call:=CreateCallExpression(El); Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnSpaceLeft]]); - Call.Args.Elements.AddElement.Expr:=Add; + Call.AddArg(Add); Add:=nil; - Call.Args.Elements.AddElement.Expr:=ConvertElement(El.format1,AContext); + Call.AddArg(ConvertElement(El.format1,AContext)); Add:=Call; Call:=nil; end @@ -5778,7 +6357,7 @@ begin end; end; -function TPasToJSConverter.ConvertBuiltInConcatArray(El: TParamsExpr; +function TPasToJSConverter.ConvertBuiltIn_ConcatArray(El: TParamsExpr; AContext: TConvertContext): TJSElement; // concat(array1, array2) var @@ -5822,22 +6401,21 @@ begin begin // record: rtl.arrayConcat(RecordType,array1,array2,...) Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnArray_Concat]]); - Call.Args.Elements.AddElement.Expr:=CreateReferencePathExpr( - ElTypeResolved.TypeEl,AContext); + Call.AddArg(CreateReferencePathExpr(ElTypeResolved.TypeEl,AContext)); end; end else if ElTypeResolved.BaseType=btSet then begin // set: rtl.arrayConcat("refSet",array1,array2,...) Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnArray_Concat]]); - Call.Args.Elements.AddElement.Expr:=CreateLiteralString(El,FBuiltInNames[pbifnSet_Reference]); + Call.AddArg(CreateLiteralString(El,FBuiltInNames[pbifnSet_Reference])); end; if Call.Expr=nil then // default: array1.concat(array2,...) Call.Expr:=CreateDotExpression(El,ConvertElement(Param0,AContext), CreateBuiltInIdentifierExpr('concat')); for i:=1 to length(El.Params)-1 do - Call.Args.Elements.AddElement.Expr:=ConvertElement(El.Params[i],AContext); + Call.AddArg(ConvertElement(El.Params[i],AContext)); Result:=Call; finally if Result=nil then @@ -5846,7 +6424,7 @@ begin end; end; -function TPasToJSConverter.ConvertBuiltInCopyArray(El: TParamsExpr; +function TPasToJSConverter.ConvertBuiltIn_CopyArray(El: TParamsExpr; AContext: TConvertContext): TJSElement; var Param: TPasExpr; @@ -5883,17 +6461,17 @@ begin // rtl.arrayCopy Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnArray_Copy]]); // param: type - Call.Args.Elements.AddElement.Expr:=TypeParam; + Call.AddArg(TypeParam); // param: src - Call.Args.Elements.AddElement.Expr:=ConvertElement(Param,AContext); + Call.AddArg(ConvertElement(Param,AContext)); // param: start if length(El.Params)=1 then - Call.Args.Elements.AddElement.Expr:=CreateLiteralNumber(El,0) + Call.AddArg(CreateLiteralNumber(El,0)) else - Call.Args.Elements.AddElement.Expr:=ConvertElement(El.Params[1],AContext); + Call.AddArg(ConvertElement(El.Params[1],AContext)); // param: count if length(El.Params)>=3 then - Call.Args.Elements.AddElement.Expr:=ConvertElement(El.Params[2],AContext); + Call.AddArg(ConvertElement(El.Params[2],AContext)); Result:=Call; finally if Result=nil then @@ -5904,7 +6482,7 @@ begin if AContext=nil then; end; -function TPasToJSConverter.ConvertBuiltInInsertArray(El: TParamsExpr; +function TPasToJSConverter.ConvertBuiltIn_InsertArray(El: TParamsExpr; AContext: TConvertContext): TJSElement; // procedure insert(item,var array,const position) // -> array.splice(position,1,item); @@ -5918,9 +6496,9 @@ begin Call:=CreateCallExpression(El); ArrEl:=ConvertElement(El.Params[1],AContext); Call.Expr:=CreateDotExpression(El,ArrEl,CreateBuiltInIdentifierExpr('splice')); - Call.Args.Elements.AddElement.Expr:=ConvertElement(El.Params[2],AContext); - Call.Args.Elements.AddElement.Expr:=CreateLiteralNumber(El,1); - Call.Args.Elements.AddElement.Expr:=ConvertElement(El.Params[0],AContext); + Call.AddArg(ConvertElement(El.Params[2],AContext)); + Call.AddArg(CreateLiteralNumber(El,1)); + Call.AddArg(ConvertElement(El.Params[0],AContext)); Result:=Call; finally if Result=nil then @@ -5928,7 +6506,7 @@ begin end; end; -function TPasToJSConverter.ConvertBuiltInDeleteArray(El: TParamsExpr; +function TPasToJSConverter.ConvertBuiltIn_DeleteArray(El: TParamsExpr; AContext: TConvertContext): TJSElement; // proc delete(var array,const start,count) // -> array.splice(start,count) @@ -5942,8 +6520,8 @@ begin Call:=CreateCallExpression(El); ArrEl:=ConvertElement(El.Params[0],AContext); Call.Expr:=CreateDotExpression(El,ArrEl,CreateBuiltInIdentifierExpr('splice')); - Call.Args.Elements.AddElement.Expr:=ConvertElement(El.Params[1],AContext); - Call.Args.Elements.AddElement.Expr:=ConvertElement(El.Params[2],AContext); + Call.AddArg(ConvertElement(El.Params[1],AContext)); + Call.AddArg(ConvertElement(El.Params[2],AContext)); Result:=Call; finally if Result=nil then @@ -5951,6 +6529,35 @@ begin end; end; +function TPasToJSConverter.ConvertBuiltIn_TypeInfo(El: TParamsExpr; + AContext: TConvertContext): TJSElement; +var + ParamResolved: TPasResolverResult; + Param: TPasExpr; +begin + Result:=nil; + Param:=El.Params[0]; + AContext.Resolver.ComputeElement(Param,ParamResolved,[rcNoImplicitProc]); + if ParamResolved.IdentEl is TPasType then + Result:=CreateTypeInfoRef(TPasType(ParamResolved.IdentEl),AContext,Param) + else if ParamResolved.TypeEl<>nil then + begin + if (ParamResolved.TypeEl.ClassType=TPasClassType) + and (rrfReadable in ParamResolved.Flags) + and ((ParamResolved.IdentEl is TPasVariable) + or (ParamResolved.IdentEl.ClassType=TPasArgument)) then + begin + // typeinfo(classinstance) -> classinstance.$rtti + Result:=ConvertElement(Param,AContext); + Result:=CreateDotExpression(El,Result,CreateBuiltInIdentifierExpr(FBuiltInNames[pbivnRTTI])); + end + else + Result:=CreateTypeInfoRef(ParamResolved.TypeEl,AContext,Param); + end + else + RaiseNotSupported(El,AContext,20170413001544); +end; + function TPasToJSConverter.ConvertRecordValues(El: TRecordValues; AContext: TConvertContext): TJSElement; @@ -6044,34 +6651,22 @@ begin ElClass:=El.ClassType; if ElClass=TPasClassType then Result := ConvertClassType(TPasClassType(El), AContext) + else if (ElClass=TPasClassOfType) then + Result := ConvertClassOfType(TPasClassOfType(El), AContext) else if ElClass=TPasRecordType then Result := ConvertRecordType(TPasRecordType(El), AContext) else if ElClass=TPasEnumType then Result := ConvertEnumType(TPasEnumType(El), AContext) else if (ElClass=TPasSetType) then - begin - if TPasSetType(El).IsPacked then - DoError(20170222231613,nPasElementNotSupported,sPasElementNotSupported, - ['packed'],El); - end - else if (ElClass=TPasAliasType) - or (ElClass=TPasClassOfType) then + Result := ConvertSetType(TPasSetType(El), AContext) + else if (ElClass=TPasAliasType) then + else if (ElClass=TPasPointerType) then + Result:=ConvertPointerType(TPasPointerType(El),AContext) else if (ElClass=TPasProcedureType) or (ElClass=TPasFunctionType) then - begin - if TPasProcedureType(El).IsNested then - DoError(20170222231636,nPasElementNotSupported,sPasElementNotSupported, - ['is nested'],El); - if TPasProcedureType(El).CallingConvention<>ccDefault then - DoError(20170222231532,nPasElementNotSupported,sPasElementNotSupported, - [cCallingConventions[TPasProcedureType(El).CallingConvention]],El); - end + Result:=ConvertProcedureType(TPasProcedureType(El),AContext) else if (ElClass=TPasArrayType) then - begin - if TPasArrayType(El).PackMode<>pmNone then - DoError(20170222231648,nPasElementNotSupported,sPasElementNotSupported, - ['packed'],El); - end + Result:=ConvertArrayType(TPasArrayType(El),AContext) else begin {$IFDEF VerbosePas2JS} @@ -6225,7 +6820,6 @@ Var Procedure AddFunctionResultInit; var VarSt: TJSVariableStatement; - AssignSt: TJSSimpleAssignStatement; PasFun: TPasFunction; FunType: TPasFunctionType; ResultEl: TPasResultElement; @@ -6235,13 +6829,9 @@ Var ResultEl:=FunType.ResultEl; // add 'var result=initvalue' - VarSt:=TJSVariableStatement(CreateElement(TJSVariableStatement,El)); + VarSt:=CreateVarStatement(ResolverResultVar,CreateValInit(ResultEl.ResultType,nil,El,aContext),El); Add(VarSt); Result:=SLFirst; - AssignSt:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,El)); - VarSt.A:=AssignSt; - AssignSt.LHS:=CreateBuiltInIdentifierExpr(ResolverResultVar); - AssignSt.Expr:=CreateValInit(ResultEl.ResultType,nil,El,aContext); end; Procedure AddFunctionResultReturn; @@ -6277,7 +6867,9 @@ begin For I:=0 to El.Declarations.Count-1 do begin P:=TPasElement(El.Declarations[i]); + {$IFDEF VerbosePas2JS} //writeln('TPasToJSConverter.ConvertDeclarations El[',i,']=',GetObjName(P)); + {$ENDIF} if not IsElementUsed(P) then continue; E:=Nil; @@ -6366,7 +6958,7 @@ var Call:=CreateCallExpression(El); AncestorPath:=CreateReferencePath(Ancestor,ClassContext,rpkPathAndName); Call.Expr:=CreateBuiltInIdentifierExpr(AncestorPath+'.'+MemberFuncName[Kind]+'.call'); - Call.Args.Elements.AddElement.Expr:=CreateBuiltInIdentifierExpr('this'); + Call.AddArg(CreateBuiltInIdentifierExpr('this')); AddToSourceElements(Src,Call); end; @@ -6458,10 +7050,50 @@ var end; end; + procedure AddRTTI(Src: TJSSourceElements; FuncContext: TConvertContext); + var + HasRTTIMembers: Boolean; + i: Integer; + P: TPasElement; + NewEl: TJSElement; + VarSt: TJSVariableStatement; + begin + HasRTTIMembers:=false; + For i:=0 to El.Members.Count-1 do + begin + P:=TPasElement(El.Members[i]); + //writeln('TPasToJSConverter.ConvertClassType RTTI El[',i,']=',GetObjName(P)); + if P.Visibility<>visPublished then continue; + if not IsMemberNeeded(P) then continue; + NewEl:=nil; + if P.ClassType=TPasVariable then + NewEl:=CreateRTTIClassField(TPasVariable(P),FuncContext) + else if P.InheritsFrom(TPasProcedure) then + NewEl:=CreateRTTIClassMethod(TPasProcedure(P),FuncContext) + else if P.ClassType=TPasProperty then + NewEl:=CreateRTTIClassProperty(TPasProperty(P),FuncContext) + else if P.InheritsFrom(TPasType) then + continue + else + DoError(20170409202315,nSymbolCannotBePublished,sSymbolCannotBePublished,[],P); + if NewEl=nil then + continue; // e.g. abstract or external proc + // add RTTI element + if not HasRTTIMembers then + begin + // add "var $r = this.$rtti" + VarSt:=CreateVarStatement(FBuiltInNames[pbivnRTTILocal], + CreateMemberExpression(['this',FBuiltInNames[pbivnRTTI]]),El); + AddToSourceElements(Src,VarSt); + HasRTTIMembers:=true; + end; + AddToSourceElements(Src,NewEl); + end; + end; + var Call: TJSCallExpression; FunDecl: TJSFunctionDeclarationStatement; - FunDef: TJSFuncDef; Src: TJSSourceElements; ArgEx: TJSLiteral; FuncContext: TFunctionContext; @@ -6475,7 +7107,10 @@ var begin Result:=nil; if El.IsForward then - exit(nil); + begin + Result:=ConvertClassForwardType(El,AContext); + exit; + end; if El.IsExternal then exit; @@ -6492,6 +7127,7 @@ begin Ancestor:=El.AncestorType; // create call 'rtl.createClass(' + FuncContext:=nil; Call:=CreateCallExpression(El); try AncestorIsExternal:=(Ancestor is TPasClassType) and TPasClassType(Ancestor).IsExternal; @@ -6501,11 +7137,11 @@ begin Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnCreateClass]]); // add parameter: owner. 'this' for top level class. - Call.Args.Elements.AddElement.Expr:=CreateBuiltInIdentifierExpr('this'); + Call.AddArg(CreateBuiltInIdentifierExpr('this')); // add parameter: string constant '"classname"' ArgEx := CreateLiteralString(El,TransformVariableName(El,AContext)); - Call.Args.Elements.AddElement.Expr:=ArgEx; + Call.AddArg(ArgEx); // add parameter: ancestor if Ancestor=nil then @@ -6514,100 +7150,115 @@ begin AncestorPath:=TPasClassType(Ancestor).ExternalName else AncestorPath:=CreateReferencePath(Ancestor,AContext,rpkPathAndName); - Call.Args.Elements.AddElement.Expr:=CreateBuiltInIdentifierExpr(AncestorPath); + Call.AddArg(CreateBuiltInIdentifierExpr(AncestorPath)); if AncestorIsExternal then begin // add the name of the NewInstance function if Scope.NewInstanceFunction<>nil then - Call.Args.Elements.AddElement.Expr:=CreateLiteralString( - Scope.NewInstanceFunction,Scope.NewInstanceFunction.Name) + Call.AddArg(CreateLiteralString( + Scope.NewInstanceFunction,Scope.NewInstanceFunction.Name)) else - Call.Args.Elements.AddElement.Expr:=CreateLiteralString(El,''); + Call.AddArg(CreateLiteralString(El,'')); end; // add parameter: class initialize function 'function(){...}' - FunDecl:=TJSFunctionDeclarationStatement.Create(0,0); - Call.Args.Elements.AddElement.Expr:=FunDecl; - FunDef:=TJSFuncDef.Create; - FunDecl.AFunction:=FunDef; - FunDef.Name:=''; - FunDef.Body:=TJSFunctionBody.Create(0,0); - Src:=TJSSourceElements(CreateElement(TJSSourceElements, El)); - FunDef.Body.A:=Src; + FunDecl:=CreateFunction(El,true,true); + Call.AddArg(FunDecl); + Src:=TJSSourceElements(FunDecl.AFunction.Body.A); // add members FuncContext:=TFunctionContext.Create(El,Src,AContext); - try - FuncContext.IsSingleton:=true; - FuncContext.This:=El; - // add class members: types and class vars - For i:=0 to El.Members.Count-1 do + FuncContext.IsSingleton:=true; + FuncContext.This:=El; + // add class members: types and class vars + For i:=0 to El.Members.Count-1 do + begin + P:=TPasElement(El.Members[i]); + //writeln('TPasToJSConverter.ConvertClassType class vars El[',i,']=',GetObjName(P)); + if not IsMemberNeeded(P) then continue; + C:=P.ClassType; + NewEl:=nil; + if C=TPasVariable then begin - P:=TPasElement(El.Members[i]); - //writeln('TPasToJSConverter.ConvertClassType class El[',i,']=',GetObjName(P)); - if not IsMemberNeeded(P) then continue; - C:=P.ClassType; - NewEl:=nil; - if C=TPasVariable then - begin - if ClassVarModifiersType*TPasVariable(P).VarModifiers<>[] then - begin - NewEl:=CreateVarDecl(TPasVariable(P),FuncContext); // can be nil - if NewEl=nil then continue; - end - else - continue; - end - else if C=TPasConst then - NewEl:=ConvertConst(TPasConst(P),aContext) - else if C=TPasProperty then + if ClassVarModifiersType*TPasVariable(P).VarModifiers<>[] then begin - NewEl:=ConvertProperty(TPasProperty(P),AContext); + NewEl:=CreateVarDecl(TPasVariable(P),FuncContext); // can be nil if NewEl=nil then continue; end - else if C.InheritsFrom(TPasType) then - NewEl:=CreateTypeDecl(TPasType(P),aContext) - else if C.InheritsFrom(TPasProcedure) then - continue else - RaiseNotSupported(P,FuncContext,20161221233338); - if NewEl=nil then - RaiseNotSupported(P,FuncContext,20170204223922); - AddToSourceElements(Src,NewEl); - end; + continue; + end + else if C=TPasConst then + NewEl:=ConvertConst(TPasConst(P),aContext) + else if C=TPasProperty then + begin + NewEl:=ConvertProperty(TPasProperty(P),AContext); + if NewEl=nil then continue; + end + else if C.InheritsFrom(TPasType) then + NewEl:=CreateTypeDecl(TPasType(P),aContext) + else if C.InheritsFrom(TPasProcedure) then + continue + else + RaiseNotSupported(P,FuncContext,20161221233338); + if NewEl=nil then + RaiseNotSupported(P,FuncContext,20170204223922); + AddToSourceElements(Src,NewEl); + end; - // instance initialization function - AddInstanceMemberFunction(Src,FuncContext,Ancestor,mfInit); - // instance finalization function - AddInstanceMemberFunction(Src,FuncContext,Ancestor,mfFinalize); + // instance initialization function + AddInstanceMemberFunction(Src,FuncContext,Ancestor,mfInit); + // instance finalization function + AddInstanceMemberFunction(Src,FuncContext,Ancestor,mfFinalize); - // add methods - For i:=0 to El.Members.Count-1 do - begin - P:=TPasElement(El.Members[i]); - //writeln('TPasToJSConverter.ConvertClassType class El[',i,']=',GetObjName(P)); - if not IsMemberNeeded(P) then continue; - if P is TPasProcedure then - NewEl:=ConvertProcedure(TPasProcedure(P),aContext) - else - continue; - if NewEl=nil then - continue; // e.g. abstract or external proc - AddToSourceElements(Src,NewEl); - end; + // add methods + For i:=0 to El.Members.Count-1 do + begin + P:=TPasElement(El.Members[i]); + //writeln('TPasToJSConverter.ConvertClassType methods El[',i,']=',GetObjName(P)); + if not IsMemberNeeded(P) then continue; + if P is TPasProcedure then + NewEl:=ConvertProcedure(TPasProcedure(P),FuncContext) + else + continue; + if NewEl=nil then + continue; // e.g. abstract or external proc + AddToSourceElements(Src,NewEl); + end; - finally - FuncContext.Free; - end; + // add RTTI init function + if AContext.Resolver<>nil then + AddRTTI(Src,FuncContext); Result:=Call; finally + FuncContext.Free; if Result<>Call then Call.Free; end; end; +function TPasToJSConverter.ConvertClassForwardType(El: TPasClassType; + AContext: TConvertContext): TJSElement; +// module.$rtti.$Class("classname"); +var + Ref: TResolvedReference; + aClass: TPasClassType; + ObjLit: TJSObjectLiteral; +begin + Result:=nil; + if (AContext.Resolver=nil) or not (El.CustomData is TResolvedReference) then exit; + Ref:=TResolvedReference(El.CustomData); + aClass:=Ref.Declaration as TPasClassType; + if not HasTypeInfo(aClass,AContext) then exit; + if IsClassRTTICreatedBefore(aClass,El) then exit; + // module.$rtti.$Class("classname"); + Result:=CreateRTTINewType(aClass,FBuiltInNames[pbifnRTTINewClass],true,AContext,ObjLit); + if ObjLit<>nil then + RaiseInconsistency(20170412093427); +end; + function TPasToJSConverter.ConvertClassExternalType(El: TPasClassType; AContext: TConvertContext): TJSElement; @@ -6625,7 +7276,6 @@ begin Result:=nil; if El.IsForward then exit; - // add class members: types and class vars For i:=0 to El.Members.Count-1 do begin @@ -6653,24 +7303,78 @@ begin end; end; +function TPasToJSConverter.ConvertClassOfType(El: TPasClassOfType; + AContext: TConvertContext): TJSElement; +// create +// module.$rtti.$ClassRef("typename",{ +// instancetype: module.$rtti["classname"]) +// } +// if class is defined later add a forward define for the class +var + ObjLit: TJSObjectLiteral; + Prop: TJSObjectLiteralElement; + Call: TJSCallExpression; + ok: Boolean; + List: TJSStatementList; +begin + Result:=nil; + if not HasTypeInfo(El,AContext) then exit; + + ok:=false; + Call:=CreateRTTINewType(El,FBuiltInNames[pbifnRTTINewClassRef],false,AContext,ObjLit); + Result:=Call; + try + Prop:=ObjLit.Elements.AddElement; + Prop.Name:=TJSString(FBuiltInNames[pbivnRTTIClassRef_InstanceType]); + Prop.Expr:=CreateTypeInfoRef(El.DestType,AContext,El); + + if not IsClassRTTICreatedBefore(El.DestType as TPasClassType,El) then + begin + // class rtti must be forward registered + if not (AContext is TFunctionContext) then + RaiseNotSupported(El,AContext,20170412102916); + // prepend module.$rtti.$Class("classname"); + Call:=CreateRTTINewType(El.DestType,FBuiltInNames[pbifnRTTINewClass],true,AContext,ObjLit); + if ObjLit<>nil then + RaiseInconsistency(20170412102654); + List:=TJSStatementList(CreateElement(TJSStatementList,El)); + List.A:=Call; + List.B:=Result; + Result:=List; + end; + ok:=true; + finally + if not ok then + FreeAndNil(Result); + end; +end; + function TPasToJSConverter.ConvertEnumType(El: TPasEnumType; AContext: TConvertContext): TJSElement; // TMyEnum = (red, green) // convert to -// this.TMyEnum = { -// "0":"red", -// "red":0, -// "0":"green", -// "green":0, -// } +// this.TMyEnum = { +// "0":"red", +// "red":0, +// "0":"green", +// "green":0, +// }; +// module.$rtti.$TIEnum("TMyEnum",{ +// enumtype: this.TMyEnum, +// minvalue: 0, +// maxvalue: 1 +// }); var ObjectContect: TObjectContext; i: Integer; EnumValue: TPasEnumValue; - ParentObj, Obj: TJSObjectLiteral; - ObjLit: TJSObjectLiteralElement; + ParentObj, Obj, TIObj: TJSObjectLiteral; + ObjLit, TIProp: TJSObjectLiteralElement; AssignSt: TJSSimpleAssignStatement; JSName: TJSString; + Call: TJSCallExpression; + List: TJSStatementList; + ok: Boolean; begin Result:=nil; for i:=0 to El.Values.Count-1 do @@ -6680,6 +7384,7 @@ begin RaiseNotSupported(EnumValue.Value,AContext,20170208145221,'enum constant'); end; + ok:=false; ObjectContect:=nil; try Obj:=TJSObjectLiteral(CreateElement(TJSObjectLiteral,El)); @@ -6716,8 +7421,254 @@ begin ObjLit.Expr:=CreateLiteralNumber(El,i); end; + if HasTypeInfo(El,AContext) then + begin + // create typeinfo + if not (AContext is TFunctionContext) then + RaiseNotSupported(El,AContext,20170411210045,'typeinfo'); + // create statement list + List:=TJSStatementList(CreateElement(TJSStatementList,El)); + List.A:=Result; + Result:=List; + // module.$rtti.$TIEnum("TMyEnum",{...}); + Call:=CreateRTTINewType(El,FBuiltInNames[pbifnRTTINewEnum],false,AContext,TIObj); + List.B:=Call; + // add minvalue: number + TIProp:=TIObj.Elements.AddElement; + TIProp.Name:=TJSString(FBuiltInNames[pbivnRTTIInt_MinValue]); + TIProp.Expr:=CreateLiteralNumber(El,0); + // add maxvalue: number + TIProp:=TIObj.Elements.AddElement; + TIProp.Name:=TJSString(FBuiltInNames[pbivnRTTIInt_MaxValue]); + TIProp.Expr:=CreateLiteralNumber(El,El.Values.Count-1); + // add enumtype: this.TypeName + TIProp:=TIObj.Elements.AddElement; + TIProp.Name:=TJSString(FBuiltInNames[pbivnRTTIEnum_EnumType]); + TIProp.Expr:=CreateDeclNameExpression(El,El.Name,AContext); + end; + + ok:=true; finally ObjectContect.Free; + if not ok then + FreeAndNil(Result); + end; +end; + +function TPasToJSConverter.ConvertSetType(El: TPasSetType; + AContext: TConvertContext): TJSElement; +// create +// module.$rtti.$Set("name",{ +// comptype: module.$rtti["enumtype"] +// }) +var + Obj: TJSObjectLiteral; + Call: TJSCallExpression; + Prop: TJSObjectLiteralElement; +begin + Result:=nil; + if El.IsPacked then + DoError(20170222231613,nPasElementNotSupported,sPasElementNotSupported, + ['packed'],El); + if not HasTypeInfo(El,AContext) then exit; + + // module.$rtti.$Set("name",{...}) + Call:=CreateRTTINewType(El,FBuiltInNames[pbifnRTTINewSet],false,AContext,Obj); + try + // "comptype: ref" + Prop:=Obj.Elements.AddElement; + Prop.Name:=TJSString(FBuiltInNames[pbivnRTTISet_CompType]); + Prop.Expr:=CreateTypeInfoRef(El.EnumType,AContext,El); + Result:=Call; + finally + if Result=nil then + Call.Free; + end; +end; + +function TPasToJSConverter.ConvertPointerType(El: TPasPointerType; + AContext: TConvertContext): TJSElement; +// create +// module.$rtti.$Set("name",{ +// reftype: module.$rtti["reftype"] +// }) +var + Obj: TJSObjectLiteral; + Call: TJSCallExpression; + Prop: TJSObjectLiteralElement; +begin + Result:=nil; + if not HasTypeInfo(El,AContext) then exit; + + // module.$rtti.$Pointer("name",{...}) + Call:=CreateRTTINewType(El,FBuiltInNames[pbifnRTTINewPointer],false,AContext,Obj); + try + // "reftype: ref" + Prop:=Obj.Elements.AddElement; + Prop.Name:=TJSString(FBuiltInNames[pbivnRTTISet_CompType]); + Prop.Expr:=CreateTypeInfoRef(El.DestType,AContext,El); + Result:=Call; + finally + if Result=nil then + Call.Free; + end; +end; + +function TPasToJSConverter.ConvertProcedureType(El: TPasProcedureType; + AContext: TConvertContext): TJSElement; +// create +// module.$rtti.$ProcVar("name",{ +// procsig: rtl.newTIProcSignature([[arg1name,arg1type,arg1flags],[arg2name...],...],resulttype,flags) +// }) +// module.$rtti.$MethodVar("name",{ +// procsig: rtl.newTIProcSignature([[arg1name,arg1type,arg1flags],[arg2name...],...],resulttype,flags), +// methodkind: 1 +// }) +var + Call, InnerCall: TJSCallExpression; + FunName: String; + ResultEl: TPasResultElement; + ResultTypeInfo: TJSElement; + Flags: Integer; + MethodKind: TMethodKind; + Obj: TJSObjectLiteral; + Prop: TJSObjectLiteralElement; +begin + Result:=nil; + if El.IsNested then + DoError(20170222231636,nPasElementNotSupported,sPasElementNotSupported, + ['is nested'],El); + if El.CallingConvention<>ccDefault then + DoError(20170222231532,nPasElementNotSupported,sPasElementNotSupported, + ['calling convention '+cCallingConventions[El.CallingConvention]],El); + if not HasTypeInfo(El,AContext) then exit; + + // module.$rtti.$ProcVar("name",function(){}) + if El.IsOfObject then + FunName:=FBuiltInNames[pbifnRTTINewMethodVar] + else + FunName:=FBuiltInNames[pbifnRTTINewProcVar]; + Call:=CreateRTTINewType(El,FunName,false,AContext,Obj); + try + // add "procsig: rtl.newTIProcSignature()" + Prop:=Obj.Elements.AddElement; + Prop.Name:=TJSString(FBuiltInNames[pbivnRTTIProcVar_ProcSig]); + InnerCall:=CreateCallExpression(El); + Prop.Expr:=InnerCall; + InnerCall.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnRTTINewProcSig]]); + // add array of arguments + InnerCall.AddArg(CreateRTTIArgList(El,El.Args,AContext)); + // add resulttype as typeinfo reference + if El is TPasFunctionType then + begin + ResultEl:=TPasFunctionType(El).ResultEl; + ResultTypeInfo:=CreateTypeInfoRef(ResultEl.ResultType,AContext,ResultEl); + if ResultTypeInfo<>nil then + InnerCall.AddArg(ResultTypeInfo); + end; + // add param flags + Flags:=0; + if ptmVarargs in El.Modifiers then + inc(Flags,pfVarargs); + if Flags>0 then + InnerCall.AddArg(CreateLiteralNumber(El,Flags)); + + if El.IsOfObject then + begin + // add "methodkind: number;" + Prop:=Obj.Elements.AddElement; + Prop.Name:=TJSString(FBuiltInNames[pbivnRTTIMethodKind]); + if El.ClassType=TPasProcedureType then + MethodKind:=mkProcedure + else if El.ClassType=TPasFunctionType then + MethodKind:=mkFunction + else + RaiseNotSupported(El,AContext,20170411180848); + Prop.Expr:=CreateLiteralNumber(El,ord(MethodKind)); + end; + + Result:=Call; + finally + if Result=nil then + Call.Free; + end; +end; + +function TPasToJSConverter.ConvertArrayType(El: TPasArrayType; + AContext: TConvertContext): TJSElement; +// Create +// module.$rtti.$StaticArray("name",{ +// dims: [dimsize1,dimsize2,...], +// eltype: module.$rtti["ElTypeName"] +// }; +// module.$rtti.$DynArray("name",{ +// eltype: module.$rtti["ElTypeName"] +// }; +var + CallName: String; + Obj: TJSObjectLiteral; + Prop: TJSObjectLiteralElement; + ArrLit: TJSArrayLiteral; + Arr: TPasArrayType; + Index: Integer; + RangeResolved: TPasResolverResult; + ElType: TPasType; + RangeEl: TPasExpr; + aMinValue, aMaxValue: int64; + Call: TJSCallExpression; +begin + Result:=nil; + if El.PackMode<>pmNone then + DoError(20170222231648,nPasElementNotSupported,sPasElementNotSupported, + ['packed'],El); + {$IFDEF VerbosePas2JS} + writeln('TPasToJSConverter.ConvertArrayType ',GetObjName(El)); + {$ENDIF} + if not HasTypeInfo(El,AContext) then exit; + + // module.$rtti.$DynArray("name",{...}) + if length(El.Ranges)>0 then + CallName:=FBuiltInNames[pbifnRTTINewStaticArray] + else + CallName:=FBuiltInNames[pbifnRTTINewDynArray]; + Call:=CreateRTTINewType(El,CallName,false,AContext,Obj); + try + ElType:=El.ElType; + if length(El.Ranges)>0 then + begin + // dims: [dimsize1,dimsize2,...] + Prop:=Obj.Elements.AddElement; + Prop.Name:=TJSString(FBuiltInNames[pbivnRTTIArray_Dims]); + ArrLit:=TJSArrayLiteral(CreateElement(TJSArrayLiteral,El)); + Prop.Expr:=ArrLit; + Arr:=El; + Index:=0; + repeat + RangeEl:=Arr.Ranges[Index]; + AContext.Resolver.ComputeElement(RangeEl,RangeResolved,[rcType]); + ComputeRange(RangeResolved,aMinValue,aMaxValue,RangeEl); + ArrLit.AddElement(CreateLiteralNumber(RangeEl,aMaxValue-aMinValue+1)); + inc(Index); + if Index=length(Arr.Ranges) then + begin + if ElType.ClassType<>TPasArrayType then + break; + Arr:=TPasArrayType(ElType); + if length(Arr.Ranges)=0 then + RaiseNotSupported(Arr,AContext,20170411222315,'static array of anonymous array'); + ElType:=Arr.ElType; + Index:=0; + end; + until false; + end; + // eltype: ref + Prop:=Obj.Elements.AddElement; + Prop.Name:=TJSString(FBuiltInNames[pbivnRTTIArray_ElType]); + Prop.Expr:=CreateTypeInfoRef(ElType,AContext,El); + Result:=Call; + finally + if Result=nil then + Call.Free; end; end; @@ -7201,18 +8152,44 @@ begin end; function TPasToJSConverter.CreateImplementationSection(El: TPasModule; - Src: TJSSourceElements; AContext: TConvertContext): TJSElement; + AContext: TConvertContext + ): TJSFunctionDeclarationStatement; var - Section: TImplementationSection; + Src: TJSSourceElements; + ImplContext: TSectionContext; + ImplDecl: TJSElement; + ImplVarSt: TJSVariableStatement; + FunDecl: TJSFunctionDeclarationStatement; begin Result:=nil; - if not Assigned(El.ImplementationSection) then - exit; - Section:=El.ImplementationSection; - // add implementation section - // merge interface and implementation - Result:=ConvertDeclarations(Section,AContext); - AddToSourceElements(Src,Result); + // create function(){} + FunDecl:=CreateFunction(El,true,true); + Src:=TJSSourceElements(FunDecl.AFunction.Body.A); + + // create section context (a function) + ImplContext:=TSectionContext.Create(El,Src,AContext); + try + if coUseStrict in Options then + AddToSourceElements(Src,CreateLiteralString(El,'use strict')); + + // add var $impl = this.$impl + ImplVarSt:=CreateVarStatement(FBuiltInNames[pbivnImplementation], + CreateMemberExpression(['this',FBuiltInNames[pbivnImplementation]]),El); + AddToSourceElements(Src,ImplVarSt); + + ImplContext.This:=El; + // create implementation declarations + ImplDecl:=ConvertDeclarations(El.ImplementationSection,ImplContext); + if ImplDecl=nil then + exit; + // add impl declarations + AddToSourceElements(Src,ImplDecl); + Result:=FunDecl; + finally + ImplContext.Free; + if Result=nil then + FunDecl.Free; + end; end; procedure TPasToJSConverter.CreateInitSection(El: TPasModule; @@ -7333,7 +8310,7 @@ var begin Call:=CreateCallExpression(El); Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnSet_Reference]]); - Call.Args.Elements.AddElement.Expr:=SetExpr; + Call.AddArg(SetExpr); Result:=Call; end; @@ -7349,7 +8326,7 @@ begin NewExpr:=TJSNewMemberExpression(CreateElement(TJSNewMemberExpression,El)); NewExpr.MExpr:=CreateReferencePathExpr(ResolvedEl.TypeEl,AContext); NewExpr.Args:=TJSArguments(CreateElement(TJSArguments,El)); - NewExpr.Args.Elements.AddElement.Expr:=RecordExpr; + NewExpr.AddArg(RecordExpr); Result:=NewExpr; end; @@ -7416,9 +8393,9 @@ begin {$ENDIF} DoError(20170215161802,nInvalidFunctionReference,sInvalidFunctionReference,[],El); end; - Call.Args.Elements.AddElement.Expr:=Scope; + Call.AddArg(Scope); // add function name as parameter - Call.Args.Elements.AddElement.Expr:=CreateLiteralString(El,FunName); + Call.AddArg(CreateLiteralString(El,FunName)); end else if Scope.ClassType=TJSPrimaryExpressionIdent then begin @@ -7430,18 +8407,18 @@ begin // chomp dotted identifier -> rtl.createCallback(scope,"FunName") FunName:=copy(aName,DotPos+1); Prim.Name:=TJSString(LeftStr(aName,DotPos-1)); - Call.Args.Elements.AddElement.Expr:=Prim; + Call.AddArg(Prim); // add function name as parameter - Call.Args.Elements.AddElement.Expr:=CreateLiteralString(El,FunName); + Call.AddArg(CreateLiteralString(El,FunName)); end else begin // nested proc -> rtl.createCallback(this,FunName) FunName:=aName; Prim.Name:='this'; - Call.Args.Elements.AddElement.Expr:=Prim; + Call.AddArg(Prim); // add function as parameter - Call.Args.Elements.AddElement.Expr:=CreateBuiltInIdentifierExpr(FunName); + Call.AddArg(CreateBuiltInIdentifierExpr(FunName)); end; end else @@ -7490,6 +8467,449 @@ begin end; end; +function TPasToJSConverter.CreateTypeInfoRef(El: TPasType; + AContext: TConvertContext; ErrorEl: TPasElement): TJSElement; +var + C: TClass; + aName, aModName: String; + bt: TResolverBaseType; + jbt: TPas2jsBaseType; + Parent: TPasElement; + aModule: TPasModule; + Bracket: TJSBracketMemberExpression; +begin + El:=AContext.Resolver.ResolveAliasType(El); + if El=nil then + RaiseInconsistency(20170409172756); + if El=AContext.PasElement then + begin + // refering itself + if El is TPasClassType then + begin + // use this + Result:=CreateBuiltInIdentifierExpr(FBuiltInNames[pbivnRTTILocal]); + exit; + end + else + RaiseNotSupported(ErrorEl,AContext,20170409195518,'cannot typeinfo itself'); + end; + if El.Name='' then + RaiseNotSupported(El,AContext,20170412125911,'typeinfo of anonymous '+El.ElementTypeName); + + C:=El.ClassType; + if C=TPasUnresolvedSymbolRef then + begin + if El.CustomData is TResElDataBaseType then + begin + bt:=TResElDataBaseType(El.CustomData).BaseType; + case bt of + btLongint,btCardinal,btSmallInt,btWord,btShortInt,btByte, + btString,btChar, + btDouble, + btBoolean, + btPointer: + begin + // create rtl.basename + Result:=CreateMemberExpression([FBuiltInNames[pbivnRTL],lowercase(BaseTypeNames[bt])]); + exit; + end; + btCustom: + if El.CustomData is TResElDataPas2JSBaseType then + begin + jbt:=TResElDataPas2JSBaseType(El.CustomData).JSBaseType; + case jbt of + pbtJSValue: + begin + // create rtl.basename + Result:=CreateMemberExpression([FBuiltInNames[pbivnRTL],lowercase(Pas2jsBaseTypeNames[jbt])]); + exit; + end; + else + {$IFDEF VerbosePas2JS} + writeln('TPasToJSConverter.CreateTypeInfoRef [20170409174539] El=',GetObjName(El),' El.CustomData=',GetObjName(El.CustomData),' jbt=',Pas2jsBaseTypeNames[jbt]); + {$ENDIF} + end; + end + else + begin + {$IFDEF VerbosePas2JS} + writeln('TPasToJSConverter.CreateTypeInfoRef [20170409174645] El=',GetObjName(El),' El.CustomData=',GetObjName(El.CustomData),' bt=',BaseTypeNames[bt]); + {$ENDIF} + end + else + {$IFDEF VerbosePas2JS} + writeln('TPasToJSConverter.CreateTypeInfoRef [20170409173746] El=',GetObjName(El),' El.CustomData=',GetObjName(El.CustomData),' bt=',BaseTypeNames[bt]); + {$ENDIF} + end; + end + else + begin + {$IFDEF VerbosePas2JS} + writeln('TPasToJSConverter.CreateTypeInfoRef [20170409173729] El=',GetObjName(El),' El.CustomData=',GetObjName(El.CustomData)); + {$ENDIF} + end; + end + else if (C=TPasEnumType) + or (C=TPasSetType) + or (C=TPasClassType) + or (C=TPasClassOfType) + or (C=TPasArrayType) + or (C=TPasProcedureType) + or (C=TPasFunctionType) + or (C=TPasPointerType) + // ToDo or (C=TPasTypeAliasType) + or (C=TPasRecordType) + // ToDo or (C=TPasRangeType) + then + begin + // user type -> module.$rtti[typename] + aName:=TransformVariableName(El,AContext); + if aName='' then + DoError(20170411230435,nPasElementNotSupported,sPasElementNotSupported, + ['typeinfo of anonymous '+El.ElementTypeName+' not supported'],ErrorEl); + Parent:=El.Parent; + while Parent.ClassType=TPasClassType do + begin + aName:=TransformVariableName(Parent,AContext)+'.'+aName; + Parent:=Parent.Parent; + end; + if Parent is TPasSection then + begin + aModule:=Parent.Parent as TPasModule; + if AContext.GetThis=aModule then + aModName:='this' + else + aModName:=TransformModuleName(aModule,true,AContext); + Bracket:=TJSBracketMemberExpression(CreateElement(TJSBracketMemberExpression,El)); + Bracket.MExpr:=CreateMemberExpression([aModName,FBuiltInNames[pbivnRTTI]]); + Bracket.Name:=CreateLiteralString(El,aName); + Result:=Bracket; + exit; + end; + end; + aName:=El.Name; + if aName='' then aName:=El.ClassName; + DoError(20170409173329,nTypeXCannotBePublished,sTypeXCannotBePublished, + [aName],ErrorEl); +end; + +function TPasToJSConverter.CreateRTTIArgList(Parent: TPasElement; + Args: TFPList; AContext: TConvertContext): TJSElement; +var + Params: TJSArrayLiteral; + i: Integer; +begin + Result:=nil; + if Args.Count=0 then + Result:=CreateLiteralNull(Parent) + else + begin + try + Params:=TJSArrayLiteral(CreateElement(TJSArrayLiteral,Parent)); + for i:=0 to Args.Count-1 do + AddRTTIArgument(TPasArgument(Args[i]),Params,AContext); + Result:=Params; + finally + if Result=nil then + Params.Free; + end; + end; +end; + +procedure TPasToJSConverter.AddRTTIArgument(Arg: TPasArgument; + TargetParams: TJSArrayLiteral; AContext: TConvertContext); +var + Param: TJSArrayLiteral; + ArgName: String; + Flags: Integer; +begin + // for each param add "["argname",argtype,flags]" Note: flags only if >0 + Param:=TJSArrayLiteral(CreateElement(TJSArrayLiteral,Arg)); + TargetParams.Elements.AddElement.Expr:=Param; + // add "argname" + ArgName:=TransformVariableName(Arg,Arg.Name,AContext); + Param.Elements.AddElement.Expr:=CreateLiteralString(Arg,ArgName); + // add "argtype" + if Arg.ArgType=nil then + // untyped + Param.Elements.AddElement.Expr:=CreateLiteralNull(Arg) + else + Param.Elements.AddElement.Expr:=CreateTypeInfoRef(Arg.ArgType,AContext,Arg); + // add flags + Flags:=0; + case Arg.Access of + argDefault: ; + argConst: inc(Flags,pfConst); + argVar: inc(Flags,pfVar); + argOut: inc(Flags,pfOut); + else + RaiseNotSupported(Arg,AContext,20170409192127,AccessNames[Arg.Access]); + end; + if Flags>0 then + Param.Elements.AddElement.Expr:=CreateLiteralNumber(Arg,Flags); +end; + +function TPasToJSConverter.CreateRTTINewType(El: TPasType; + const CallFuncName: string; IsForward: boolean; AContext: TConvertContext; + out ObjLit: TJSObjectLiteral): TJSCallExpression; +// module.$rtti.$TiSomething("name",{}) +var + ThisContext: TFunctionContext; + RttiPath, TypeName: String; + Call: TJSCallExpression; +begin + Result:=nil; + ObjLit:=nil; + // get module path + ThisContext:=AContext.GetThisContext; + if ThisContext=nil then + RaiseInconsistency(20170411151517); + if ThisContext.This is TPasModule then + RttiPath:='this' + else + begin + RttiPath:=TransformModuleName(ThisContext.GetRootModule,true,AContext); + end; + Call:=CreateCallExpression(El); + try + // module.$rtti.$ProcVar + Call.Expr:=CreateMemberExpression([RttiPath,FBuiltInNames[pbivnRTTI],CallFuncName]); + // add param "typename" + TypeName:=TransformVariableName(El,AContext); + Call.AddArg(CreateLiteralString(El,TypeName)); + if not IsForward then + begin + // add {} + ObjLit:=TJSObjectLiteral(CreateElement(TJSObjectLiteral,El)); + Call.AddArg(ObjLit); + end; + Result:=Call; + finally + if Result=nil then + Call.Free; + end; +end; + +function TPasToJSConverter.CreateRTTIClassField(V: TPasVariable; + AContext: TConvertContext): TJSElement; +// create $r.addField("varname",typeinfo); +var + Call: TJSCallExpression; +var + JSTypeInfo: TJSElement; + aName: String; +begin + Result:=nil; + JSTypeInfo:=CreateTypeInfoRef(V.VarType,AContext,V); + // Note: create JSTypeInfo first, it may raise an exception + Call:=CreateCallExpression(V); + // $r.addField + Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTTILocal],FBuiltInNames[pbifnRTTIAddField]]); + // param "varname" + aName:=TransformVariableName(V,AContext); + Call.AddArg(CreateLiteralString(V,aName)); + // param typeinfo + Call.AddArg(JSTypeInfo); + Result:=Call; +end; + +function TPasToJSConverter.CreateRTTIClassMethod(Proc: TPasProcedure; + AContext: TConvertContext): TJSElement; +// create $r.addMethod("funcname",methodkind,params,resulttype,options) +var + OptionsEl: TJSObjectLiteral; + ResultTypeInfo: TJSElement; + Call: TJSCallExpression; + + procedure AddOption(const aName: String; JS: TJSElement); + var + ObjLit: TJSObjectLiteralElement; + begin + if OptionsEl=nil then + begin + OptionsEl:=TJSObjectLiteral(CreateElement(TJSObjectLiteral,Proc)); + if ResultTypeInfo=nil then + Call.AddArg(CreateLiteralNull(Proc)); + Call.AddArg(OptionsEl); + end; + ObjLit:=OptionsEl.Elements.AddElement; + ObjLit.Name:=TJSString(aName); + ObjLit.Expr:=JS; + end; + +var + FunName: String; + C: TClass; + MethodKind, Flags: Integer; + ResultEl: TPasResultElement; + ProcScope, OverriddenProcScope: TPasProcedureScope; + OverriddenClass: TPasClassType; +begin + Result:=nil; + if Proc.IsOverride then + begin + ProcScope:=Proc.CustomData as TPasProcedureScope; + if ProcScope.OverriddenProc.Visibility=visPublished then + begin + // overridden proc is published as well + OverriddenProcScope:=ProcScope.OverriddenProc.CustomData as TPasProcedureScope; + OverriddenClass:=OverriddenProcScope.ClassScope.Element as TPasClassType; + if HasTypeInfo(OverriddenClass,AContext) then + exit; // overridden proc was already published in ancestor + end; + end; + OptionsEl:=nil; + ResultTypeInfo:=nil; + try + // $r.addMethod + Call:=CreateCallExpression(Proc); + Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTTILocal],FBuiltInNames[pbifnRTTIAddMethod]]); + + // param "funname" + FunName:=TransformVariableName(Proc,AContext); + Call.AddArg(CreateLiteralString(Proc,FunName)); + + // param methodkind as number + C:=Proc.ClassType; + if C=TPasProcedure then + MethodKind:=ord(mkProcedure) + else if C=TPasFunction then + MethodKind:=ord(mkFunction) + else if C=TPasConstructor then + MethodKind:=ord(mkConstructor) + else if C=TPasDestructor then + MethodKind:=ord(mkDestructor) + else if C=TPasClassProcedure then + MethodKind:=ord(mkClassProcedure) + else if C=TPasClassFunction then + MethodKind:=ord(mkClassFunction) + else + RaiseNotSupported(Proc,AContext,20170409190242); + Call.AddArg(CreateLiteralNumber(Proc,MethodKind)); + + // param params as [] + Call.AddArg(CreateRTTIArgList(Proc,Proc.ProcType.Args,AContext)); + + // param resulttype as typeinfo reference + if C.InheritsFrom(TPasFunction) then + begin + ResultEl:=TPasFunction(Proc).FuncType.ResultEl; + ResultTypeInfo:=CreateTypeInfoRef(ResultEl.ResultType,AContext,ResultEl); + if ResultTypeInfo<>nil then + Call.AddArg(ResultTypeInfo); + end; + + // param options if needed as {} + Flags:=0; + if Proc.IsStatic then + inc(Flags,pfStatic); + if ptmVarargs in Proc.ProcType.Modifiers then + inc(Flags,pfVarargs); + if Proc.IsExternal then + inc(Flags,pfExternal); + if Flags>0 then + AddOption(FBuiltInNames[pbivnRTTIProcFlags],CreateLiteralNumber(Proc,Flags)); + + Result:=Call; + finally + if Result=nil then + Call.Free; + end; +end; + +function TPasToJSConverter.CreateRTTIClassProperty(Prop: TPasProperty; + AContext: TConvertContext): TJSElement; +// create $r.addProperty("propname",flags,result,"getter","setter",{options}) +var + Call: TJSCallExpression; + OptionsEl: TJSObjectLiteral; + + function GetAccessorName(Decl: TPasElement): String; + begin + Result:=TransformVariableName(Decl,AContext); + end; + + procedure AddOption(const aName: String; JS: TJSElement); + var + ObjLit: TJSObjectLiteralElement; + begin + if OptionsEl=nil then + begin + OptionsEl:=TJSObjectLiteral(CreateElement(TJSObjectLiteral,Prop)); + Call.AddArg(OptionsEl); + end; + ObjLit:=OptionsEl.Elements.AddElement; + ObjLit.Name:=TJSString(aName); + ObjLit.Expr:=JS; + end; + +var + PropName: String; + Flags: Integer; + GetterPas, StoredPas, SetterPas: TPasElement; + ResultTypeInfo: TJSElement; +begin + Result:=nil; + OptionsEl:=nil; + try + // $r.addProperty + Call:=CreateCallExpression(Prop); + Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTTILocal],FBuiltInNames[pbifnRTTIAddProperty]]); + + // param "propname" + PropName:=TransformVariableName(Prop,Prop.Name,AContext); + Call.AddArg(CreateLiteralString(Prop,PropName)); + + // add flags + Flags:=0; + GetterPas:=AContext.Resolver.GetPasPropertyGetter(Prop); + if GetterPas is TPasProcedure then + inc(Flags,pfGetFunction); + SetterPas:=AContext.Resolver.GetPasPropertySetter(Prop); + if SetterPas is TPasProcedure then + inc(Flags,pfSetProcedure); + StoredPas:=AContext.Resolver.GetPasPropertyStored(Prop); + if StoredPas is TPasProcedure then + inc(Flags,pfStoredFunction); + Call.AddArg(CreateLiteralNumber(Prop,Flags)); + + // add resulttype + ResultTypeInfo:=CreateTypeInfoRef(Prop.VarType,AContext,Prop); + if ResultTypeInfo<>nil then + Call.AddArg(ResultTypeInfo) + else + Call.AddArg(CreateLiteralNull(Prop)); + + // add "getter" + if GetterPas=nil then + Call.AddArg(CreateLiteralString(Prop,'')) + else + Call.AddArg(CreateLiteralString(GetterPas,GetAccessorName(GetterPas))); + + // add "setter" + if SetterPas=nil then + Call.AddArg(CreateLiteralString(Prop,'')) + else + Call.AddArg(CreateLiteralString(SetterPas,GetAccessorName(SetterPas))); + + // add option "stored" + if StoredPas<>nil then + AddOption(FBuiltInNames[pbivnRTTIPropStored], + CreateLiteralString(StoredPas,GetAccessorName(StoredPas))); + + // add option defaultvalue + // ToDo + + // add option Index + // ToDo + + Result:=Call; + finally + if Result=nil then + Call.Free; + end; +end; + function TPasToJSConverter.ConvertImplBlock(El: TPasImplBlock; AContext: TConvertContext): TJSElement; @@ -7579,10 +8999,6 @@ begin RaiseNotSupported(El.DispIDExpr,AContext,20170215103029,'property dispid expression'); if El.DefaultExpr<>nil then RaiseNotSupported(El.DefaultExpr,AContext,20170215103129,'property default modifier'); - if El.StoredAccessor<>nil then - RaiseNotSupported(El.StoredAccessor,AContext,20170215121145,'property stored accessor'); - if El.StoredAccessorName<>'' then - RaiseNotSupported(El,AContext,20170215121248,'property stored accessor'); // does not need any declaration. Access is redirected to getter/setter. end; @@ -7850,7 +9266,6 @@ Var ForSt : TJSForStatement; List, ListEnd: TJSStatementList; SimpleAss : TJSSimpleAssignStatement; - VarDecl : TJSVarDeclaration; Incr, Decr : TJSUNaryExpression; BinExp : TJSBinaryExpression; VarStat: TJSVariableStatement; @@ -7916,12 +9331,9 @@ begin ListEnd:=List; try // add "var $loopend=<EndExpr>" - VarStat:=TJSVariableStatement(CreateElement(TJSVariableStatement,El)); + VarStat:=CreateVarStatement(CurLoopEndVarName, + ConvertElement(El.EndExpr,AContext),El); List.A:=VarStat; - VarDecl:=TJSVarDeclaration(CreateElement(TJSVarDeclaration,El)); - VarStat.A:=VarDecl; - VarDecl.Name:=CurLoopEndVarName; - VarDecl.Init:=ConvertElement(El.EndExpr,AContext); // add "for()" ForSt:=TJSForStatement(CreateElement(TJSForStatement,El)); List.B:=ForSt; @@ -8007,7 +9419,6 @@ Var ok: Boolean; PasExpr: TPasElement; V: TJSVariableStatement; - VarDecl: TJSVarDeclaration; FuncContext: TFunctionContext; FirstSt, LastSt: TJSStatementList; WithScope: TPasWithScope; @@ -8039,11 +9450,7 @@ begin WithExprScope:=WithScope.ExpressionScopes[i] as TPas2JSWithExprScope; WithExprScope.WithVarName:=FuncContext.CreateLocalIdentifier(FBuiltInNames[pbivnWith]); // create local "var $with1 = expr;" - V:=TJSVariableStatement(CreateElement(TJSVariableStatement,PasExpr)); - VarDecl:=TJSVarDeclaration(CreateElement(TJSVarDeclaration,PasExpr)); - V.A:=VarDecl; - VarDecl.Name:=WithExprScope.WithVarName; - VarDecl.Init:=Expr; + V:=CreateVarStatement(WithExprScope.WithVarName,Expr,PasExpr); AddToStatementList(FirstSt,LastSt,V,PasExpr); end; if Assigned(El.Body) then @@ -8098,6 +9505,51 @@ begin Result:=true; end; +function TPasToJSConverter.HasTypeInfo(El: TPasType; AContext: TConvertContext + ): boolean; +begin + Result:=false; + if coNoTypeInfo in Options then exit; + if AContext.Resolver=nil then exit; + if not AContext.Resolver.HasTypeInfo(El) then exit; + if Assigned(OnIsTypeInfoUsed) and not OnIsTypeInfoUsed(Self,El) then exit; + Result:=true; +end; + +function TPasToJSConverter.IsClassRTTICreatedBefore(aClass: TPasClassType; + Before: TPasElement): boolean; +var + Decls: TPasDeclarations; + i: Integer; + Types: TFPList; + T: TPasType; + C: TClass; +begin + Result:=false; + if aClass.Parent=nil then exit; + if not aClass.Parent.InheritsFrom(TPasDeclarations) then + RaiseInconsistency(20170412101457); + Decls:=TPasDeclarations(aClass.Parent); + Types:=Decls.Types; + for i:=0 to Types.Count-1 do + begin + T:=TPasType(Types[i]); + if T=Before then exit; + if T=aClass then exit(true); + C:=T.ClassType; + if C=TPasClassType then + begin + if TPasClassType(T).IsForward and (T.CustomData is TResolvedReference) + and (TResolvedReference(T.CustomData).Declaration=aClass) then + exit(true); + end + else if C=TPasClassOfType then + begin + if TPasClassOfType(T).DestType=aClass then exit(true); + end; + end; +end; + procedure TPasToJSConverter.RaiseInconsistency(Id: int64); begin raise Exception.Create('TPasToJSConverter.RaiseInconsistency['+IntToStr(Id)+']: you found a bug'); @@ -8179,7 +9631,7 @@ procedure TPasToJSConverter.AddToStatementList(var First, var SL2: TJSStatementList; begin - if not Assigned(Add) then exit; + if Add=nil then exit; if Add is TJSStatementList then begin // add list @@ -8246,37 +9698,58 @@ var Lit: TJSLiteral; bt: TResolverBaseType; JSBaseType: TPas2jsBaseType; + C: TClass; begin T:=PasType; if AContext.Resolver<>nil then T:=AContext.Resolver.ResolveAliasType(T); - if (T is TPasArrayType) then + //writeln('START TPasToJSConverter.CreateValInit PasType=',GetObjName(PasType),' El=',GetObjName(El),' T=',GetObjName(T),' Expr=',GetObjName(Expr)); + if T=nil then + begin + // untyped var/const + if Expr=nil then + begin + if AContext.Resolver=nil then + exit(CreateLiteralUndefined(El)); + RaiseInconsistency(20170415185745); + end; + Result:=ConvertElement(Expr,AContext); + if Result=nil then + begin + {$IFDEF VerbosePas2JS} + writeln('TPasToJSConverter.CreateValInit PasType=',GetObjName(PasType),' El=',GetObjName(El),' T=',GetObjName(T),' Expr=',GetObjName(Expr)); + {$ENDIF} + RaiseNotSupported(Expr,AContext,20170415185927); + end; + exit; + end; + + C:=T.ClassType; + if C=TPasArrayType then Result:=CreateArrayInit(TPasArrayType(T),Expr,El,AContext) - else if T is TPasRecordType then + else if C=TPasRecordType then Result:=CreateRecordInit(TPasRecordType(T),Expr,El,AContext) else if Assigned(Expr) then Result:=ConvertElement(Expr,AContext) - else if T is TPasSetType then + else if C=TPasSetType then Result:=TJSObjectLiteral(CreateElement(TJSObjectLiteral,El)) else begin // always init with a default value to create a typed variable (faster and more readable) Lit:=TJSLiteral(CreateElement(TJSLiteral,El)); Result:=Lit; - if T=nil then - Lit.Value.IsUndefined:=true - else if (T.ClassType=TPasPointerType) - or (T.ClassType=TPasClassType) - or (T.ClassType=TPasClassOfType) - or (T.ClassType=TPasProcedureType) - or (T.ClassType=TPasFunctionType) then + if (C=TPasPointerType) + or (C=TPasClassType) + or (C=TPasClassOfType) + or (C=TPasProcedureType) + or (C=TPasFunctionType) then Lit.Value.IsNull:=true - else if T.ClassType=TPasStringType then + else if C=TPasStringType then Lit.Value.AsString:='' - else if T.ClassType=TPasEnumType then + else if C=TPasEnumType then Lit.Value.AsNumber:=0 - else if T.ClassType=TPasUnresolvedSymbolRef then + else if C=TPasUnresolvedSymbolRef then begin if T.CustomData is TResElDataBaseType then begin @@ -8305,6 +9778,13 @@ begin RaiseNotSupported(PasType,AContext,20170208162121); end; end + else if AContext.Resolver<>nil then + begin + {$IFDEF VerbosePas2JS} + writeln('TPasToJSConverter.CreateValInit PasType=',GetObjName(PasType),' El=',GetObjName(El),' T=',GetObjName(T),' Expr=',GetObjName(Expr)); + {$ENDIF} + RaiseNotSupported(El,AContext,20170415190259); + end else if (CompareText(T.Name,'longint')=0) or (CompareText(T.Name,'int64')=0) or (CompareText(T.Name,'real')=0) @@ -8333,6 +9813,13 @@ begin RaiseNotSupported(PasType,AContext,20170208161506); end; end; + if Result=nil then + begin + {$IFDEF VerbosePas2JS} + writeln('TPasToJSConverter.CreateValInit PasType=',GetObjName(PasType),' El=',GetObjName(El),' T=',GetObjName(T),' Expr=',GetObjName(Expr)); + {$ENDIF} + RaiseNotSupported(El,AContext,20170415190103); + end; end; function TPasToJSConverter.CreateVarInit(El: TPasVariable; @@ -8341,6 +9828,21 @@ begin Result:=CreateValInit(El.VarType,El.Expr,El,AContext); end; +function TPasToJSConverter.CreateVarStatement(const aName: String; + Init: TJSElement; El: TPasElement): TJSVariableStatement; +begin + Result:=TJSVariableStatement(CreateElement(TJSVariableStatement,El)); + Result.A:=CreateVarDecl(aName,Init,El); +end; + +function TPasToJSConverter.CreateVarDecl(const aName: String; Init: TJSElement; + El: TPasElement): TJSVarDeclaration; +begin + Result:=TJSVarDeclaration(CreateElement(TJSVarDeclaration,El)); + Result.Name:=aName; + Result.Init:=Init; +end; + function TPasToJSConverter.CreateLiteralNumber(El: TPasElement; const n: TJSNumber): TJSLiteral; begin @@ -8381,6 +9883,86 @@ begin Result.Value.IsUndefined:=true; end; +function TPasToJSConverter.CreateSetLiteralElement(Expr: TPasExpr; + AContext: TConvertContext): TJSElement; +var + LitVal: TJSValue; + NewEl: TJSElement; + WS: TJSString; + ExprResolved: TPasResolverResult; + Call: TJSCallExpression; + DotExpr: TJSDotMemberExpression; +begin + Result:=ConvertElement(Expr,AContext); + if Result=nil then + RaiseNotSupported(Expr,AContext,20170415192209); + if Result.ClassType=TJSLiteral then + begin + // argument is a literal -> convert to number + LitVal:=TJSLiteral(Result).Value; + case LitVal.ValueType of + jstBoolean: + begin + if LitVal.AsBoolean=LowJSBoolean then + NewEl:=CreateLiteralNumber(Expr,0) + else + NewEl:=CreateLiteralNumber(Expr,1); + Result.Free; + exit(NewEl); + end; + jstNumber: + exit; + jstString: + begin + WS:=LitVal.AsString; + Result.Free; + if length(WS)<>1 then + DoError(20170415193254,nXExpectedButYFound,sXExpectedButYFound,['char','string'],Expr); + Result:=CreateLiteralNumber(Expr,ord(WS[1])); + exit; + end; + else + RaiseNotSupported(Expr,AContext,20170415205955); + end; + end + else if Result.ClassType=TJSCallExpression then + begin + Call:=TJSCallExpression(Result); + if (Call.Expr is TJSDotMemberExpression) then + begin + DotExpr:=TJSDotMemberExpression(Call.Expr); + if DotExpr.Name='charCodeAt' then + exit; + if DotExpr.Name='charAt' then + begin + DotExpr.Name:='charCodeAt'; + exit; + end; + end; + end; + + if AContext.Resolver<>nil then + begin + AContext.Resolver.ComputeElement(Expr,ExprResolved,[]); + if ExprResolved.BaseType in btAllStringAndChars then + begin + // aChar -> aChar.charCodeAt() + Call:=TJSCallExpression(CreateElement(TJSCallExpression,Expr)); + Call.Expr:=CreateDotExpression(Expr,Result,CreateBuiltInIdentifierExpr('charCodeAt')); + Result:=Call; + end + else if ExprResolved.BaseType=btContext then + begin + if ExprResolved.TypeEl.ClassType=TPasEnumType then + // ok + else + RaiseNotSupported(Expr,AContext,20170415191933); + end + else + RaiseNotSupported(Expr,AContext,20170415191822); + end; +end; + function TPasToJSConverter.CreateRecordInit(aRecord: TPasRecordType; Expr: TPasElement; El: TPasElement; AContext: TConvertContext): TJSElement; // new recordtype() @@ -8447,7 +10029,7 @@ begin Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnArray_NewMultiDim]]); // add parameter [dim1,dim2,...] DimArray:=TJSArrayLiteral(CreateElement(TJSArrayLiteral,El)); - Call.Args.Elements.AddElement.Expr:=DimArray; + Call.AddArg(DimArray); CurArrayType:=ArrayType; while true do begin @@ -8477,7 +10059,7 @@ begin // add parameter defaultvalue DefaultValue:=CreateValInit(ElTypeResolved.TypeEl,nil,El,AContext); - Call.Args.Elements.AddElement.Expr:=DefaultValue; + Call.AddArg(DefaultValue); Result:=Call; finally @@ -8497,7 +10079,7 @@ begin RaiseInconsistency(20170401184819); Call:=CreateCallExpression(El); Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnArray_Length]]); - Call.Args.Elements.AddElement.Expr:=JSArray; + Call.AddArg(JSArray); if OpCode=eopEqual then BinExpr:=TJSEqualityExpressionEQ(CreateElement(TJSEqualityExpressionEQ,El)) else @@ -8640,7 +10222,7 @@ begin Result:=''; exit; end - else if (El is TPasClassType) and TPasClassType(El).IsExternal then + else if (El.ClassType=TPasClassType) and TPasClassType(El).IsExternal then begin Result:=TPasClassType(El).ExternalName; exit; @@ -8658,7 +10240,7 @@ begin end; ThisContext:=AContext.GetThisContext; if ThisContext<>nil then - This:=ThisContext.GetThis + This:=ThisContext.This else This:=nil; ParentEl:=El.Parent; @@ -8685,8 +10267,7 @@ begin Prepend(Result,FBuiltInNames[pbivnImplementation]) else // in other unit -> use pas.unitname.$impl - Prepend(Result,FBuiltInNames[pbivnModules] - +'.'+TransformModuleName(FoundModule,AContext) + Prepend(Result,TransformModuleName(FoundModule,true,AContext) +'.'+FBuiltInNames[pbivnImplementation]); end; break; @@ -8697,8 +10278,7 @@ begin if ParentEl=This then Prepend(Result,'this') else - Prepend(Result,FBuiltInNames[pbivnModules] - +'.'+TransformModuleName(TPasModule(ParentEl),AContext)); + Prepend(Result,TransformModuleName(TPasModule(ParentEl),true,AContext)); break; end else if (ParentEl.ClassType=TPasClassType) @@ -9167,8 +10747,6 @@ Var DotExpr: TJSDotMemberExpression; Call: TJSCallExpression; V: TJSVariableStatement; - VarDecl: TJSVarDeclaration; - begin Result:=nil; // create "if()" @@ -9181,7 +10759,7 @@ begin // create "T.isPrototypeOf(exceptObject)" Call:=CreateCallExpression(El); Call.Expr:=DotExpr; - Call.Args.Elements.AddElement.Expr:=CreateBuiltInIdentifierExpr(FBuiltInNames[pbivnExceptObject]); + Call.AddArg(CreateBuiltInIdentifierExpr(FBuiltInNames[pbivnExceptObject])); IfSt.Cond:=Call; if El.VarEl<>nil then @@ -9190,12 +10768,9 @@ begin ListFirst:=TJSStatementList(CreateElement(TJSStatementList,El.Body)); ListLast:=ListFirst; IfSt.BTrue:=ListFirst; - V:=TJSVariableStatement(CreateElement(TJSVariableStatement,El)); + V:=CreateVarStatement(TransformVariableName(El,El.VariableName,AContext), + CreateBuiltInIdentifierExpr(FBuiltInNames[pbivnExceptObject]),El); ListFirst.A:=V; - VarDecl:=TJSVarDeclaration(CreateElement(TJSVarDeclaration,El)); - V.A:=VarDecl; - VarDecl.Name:=TransformVariableName(El,El.VariableName,AContext); - VarDecl.Init:=CreateBuiltInIdentifierExpr(FBuiltInNames[pbivnExceptObject]); // add statements AddToStatementList(ListFirst,ListLast,ConvertElement(El.Body,AContext),El); end @@ -9250,7 +10825,6 @@ end; function TPasToJSConverter.ConvertConst(El: TPasConst; AContext: TConvertContext ): TJSElement; // Important: returns nil if const was added to higher context - Var AssignSt: TJSSimpleAssignStatement; Obj: TJSObjectLiteral; @@ -9535,7 +11109,7 @@ const Call:=CreateCallExpression(PasVar); Add_AndExpr_ToReturnSt(RetSt,PasVar,LastAndExpr,Call); Call.Expr:=CreateMemberExpression(['this',VarName,FBuiltInNames[pbifnRecordEqual]]); - Call.Args.Elements.AddElement.Expr:=CreateMemberExpression([EqualParamName,VarName]); + Call.AddArg(CreateMemberExpression([EqualParamName,VarName])); end else if VarType.ClassType=TPasSetType then begin @@ -9544,8 +11118,8 @@ const Call:=CreateCallExpression(PasVar); Add_AndExpr_ToReturnSt(RetSt,PasVar,LastAndExpr,Call); Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnSet_Equal]]); - Call.Args.Elements.AddElement.Expr:=CreateMemberExpression(['this',VarName]); - Call.Args.Elements.AddElement.Expr:=CreateMemberExpression([EqualParamName,VarName]); + Call.AddArg(CreateMemberExpression(['this',VarName])); + Call.AddArg(CreateMemberExpression([EqualParamName,VarName])); end else if VarType is TPasProcedureType then begin @@ -9554,8 +11128,8 @@ const Call:=CreateCallExpression(PasVar); Add_AndExpr_ToReturnSt(RetSt,PasVar,LastAndExpr,Call); Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnProcType_Equal]]); - Call.Args.Elements.AddElement.Expr:=CreateMemberExpression(['this',VarName]); - Call.Args.Elements.AddElement.Expr:=CreateMemberExpression([EqualParamName,VarName]); + Call.AddArg(CreateMemberExpression(['this',VarName])); + Call.AddArg(CreateMemberExpression([EqualParamName,VarName])); end else begin @@ -9568,33 +11142,53 @@ const end; end; + procedure AddRTTIFields(Args: TJSArguments); + var + i: Integer; + PasVar: TPasVariable; + begin + for i:=0 to El.Members.Count-1 do + begin + PasVar:=TPasVariable(El.Members[i]); + if not IsElementUsed(PasVar) then continue; + // add quoted "fieldname" + Args.AddElement(CreateLiteralString(PasVar,TransformVariableName(PasVar,AContext))); + // add typeinfo ref + Args.AddElement(CreateTypeInfoRef(PasVar.VarType,AContext,PasVar)); + end; + end; + var AssignSt: TJSSimpleAssignStatement; FDS: TJSFunctionDeclarationStatement; FD: TJSFuncDef; - BodyFirst, BodyLast: TJSStatementList; + BodyFirst, BodyLast, List: TJSStatementList; FuncContext: TFunctionContext; - Obj: TJSObjectLiteral; - ObjLit: TJSObjectLiteralElement; + ObjLit: TJSObjectLiteral; + ObjEl: TJSObjectLiteralElement; IfSt: TJSIfStatement; + Call: TJSCallExpression; + ok: Boolean; begin Result:=nil; FuncContext:=nil; - AssignSt:=nil; + ok:=false; try FDS:=CreateFunction(El); if AContext is TObjectContext then begin // add 'TypeName: function(){}' - Obj:=TObjectContext(AContext).JSElement as TJSObjectLiteral; - ObjLit:=Obj.Elements.AddElement; - ObjLit.Name:=TJSString(TransformVariableName(El,AContext)); - ObjLit.Expr:=FDS; + ObjLit:=TObjectContext(AContext).JSElement as TJSObjectLiteral; + Result:=ObjLit; + ObjEl:=ObjLit.Elements.AddElement; + ObjEl.Name:=TJSString(TransformVariableName(El,AContext)); + ObjEl.Expr:=FDS; end else begin // add 'this.TypeName = function(){}' AssignSt:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,El)); + Result:=AssignSt; AssignSt.LHS:=CreateDeclNameExpression(El,El.Name,AContext); AssignSt.Expr:=FDS; end; @@ -9624,10 +11218,38 @@ begin AddEqualFunction(BodyFirst,BodyLast,FuncContext); end; - Result:=AssignSt; + + if HasTypeInfo(El,AContext) then + begin + // add $rtti as second statement + if not (AContext is TFunctionContext) then + RaiseNotSupported(El,AContext,20170412120012); + + List:=TJSStatementList(CreateElement(TJSStatementList,El)); + List.A:=Result; + Result:=List; + // module.$rtti.$Record("typename",{}); + Call:=CreateRTTINewType(El,FBuiltInNames[pbifnRTTINewRecord],false,AContext,ObjLit); + List.B:=Call; + if ObjLit=nil then + RaiseInconsistency(20170412124804); + if El.Members.Count>0 then + begin + // module.$rtti.$Record("typename",{}).addFields( + // "fieldname1",type1,"fieldname2",type2,... + // ); + Call:=CreateCallExpression(El); + Call.Expr:=CreateDotExpression(El,List.B, + CreateBuiltInIdentifierExpr(FBuiltInNames[pbifnRTTIAddFields])); + List.B:=Call; + AddRTTIFields(Call.Args); + end; + end; + ok:=true;; finally FuncContext.Free; - if Result=nil then AssignSt.Free; + if not ok then + FreeAndNil(Result); end; end; @@ -9747,12 +11369,14 @@ begin end; function TPasToJSConverter.TransformModuleName(El: TPasModule; - AContext: TConvertContext): String; + AddModulesPrefix: boolean; AContext: TConvertContext): String; begin if El is TPasProgram then Result:='program' else Result:=TransformVariableName(El,AContext); + if AddModulesPrefix then + Result:=FBuiltInNames[pbivnModules]+'.'+Result; end; function TPasToJSConverter.IsPreservedWord(const aName: string): boolean; diff --git a/packages/pastojs/tests/tcmodules.pas b/packages/pastojs/tests/tcmodules.pas index 784dfcb780..7c29627770 100644 --- a/packages/pastojs/tests/tcmodules.pas +++ b/packages/pastojs/tests/tcmodules.pas @@ -28,7 +28,9 @@ uses pastree, PScanner, PasResolver, PParser, jstree, jswriter, jsbase; const + // default parser+scanner options po_pas2js = [po_asmwhole,po_resolvestandardtypes]; + co_tcmodules = [coNoTypeInfo]; type { TTestPasParser } @@ -73,7 +75,10 @@ type FExpectedErrorNumber: integer; FFilename: string; FFileResolver: TStreamResolver; + FJSImplementationSrc: TJSSourceElements; + FJSImplementationUses: TJSArrayLiteral; FJSInitBody: TJSFunctionBody; + FJSImplentationUses: TJSArrayLiteral; FJSInterfaceUses: TJSArrayLiteral; FJSModule: TJSSourceElements; FJSModuleSrc: TJSSourceElements; @@ -113,7 +118,8 @@ type Procedure ConvertUnit; virtual; procedure CheckDottedIdentifier(Msg: string; El: TJSElement; DottedName: string); function GetDottedIdentifier(El: TJSElement): string; - procedure CheckSource(Msg,Statements, InitStatements: string); virtual; + procedure CheckSource(Msg,Statements: String; InitStatements: string = ''; + ImplStatements: string = ''); virtual; procedure CheckDiff(Msg, Expected, Actual: string); virtual; procedure SetExpectedPasResolverError(Msg: string; MsgNumber: integer); procedure SetExpectedConverterError(Msg: string; MsgNumber: integer); @@ -137,9 +143,11 @@ type property JSModule: TJSSourceElements read FJSModule; property JSRegModuleCall: TJSCallExpression read FJSRegModuleCall; property JSModuleCallArgs: TJSArguments read FJSModuleCallArgs; + property JSImplementationUses: TJSArrayLiteral read FJSImplementationUses; property JSInterfaceUses: TJSArrayLiteral read FJSInterfaceUses; property JSModuleSrc: TJSSourceElements read FJSModuleSrc; property JSInitBody: TJSFunctionBody read FJSInitBody; + property JSImplementationSrc: TJSSourceElements read FJSImplementationSrc; property ExpectedErrorClass: ExceptClass read FExpectedErrorClass write FExpectedErrorClass; property ExpectedErrorMsg: string read FExpectedErrorMsg write FExpectedErrorMsg; property ExpectedErrorNumber: integer read FExpectedErrorNumber write FExpectedErrorNumber; @@ -215,28 +223,34 @@ type Procedure TestExit; Procedure TestBreak; Procedure TestContinue; - Procedure TestProcedureExternal; - Procedure TestProcedureExternalOtherUnit; - Procedure TestProcedure_Asm; - Procedure TestProcedureAssembler; - Procedure TestProcedure_VarParam; - Procedure TestProcedureOverload; - Procedure TestProcedureOverloadForward; - Procedure TestProcedureOverloadUnit; - Procedure TestProcedureOverloadNested; + Procedure TestProc_External; + Procedure TestProc_ExternalOtherUnit; + Procedure TestProc_Asm; + Procedure TestProc_Assembler; + Procedure TestProc_VarParam; + Procedure TestProc_Overload; + Procedure TestProc_OverloadForward; + Procedure TestProc_OverloadUnit; + Procedure TestProc_OverloadNested; Procedure TestProc_Varargs; // enums, sets - Procedure TestEnumName; - Procedure TestEnumNumber; - Procedure TestEnumFunctions; + Procedure TestEnum_Name; + Procedure TestEnum_Number; + Procedure TestEnum_Functions; + Procedure TestEnum_AsParams; Procedure TestSet; - Procedure TestSetOperators; - Procedure TestSetFunctions; + Procedure TestSet_Operators; + Procedure TestSet_Operator_In; + Procedure TestSet_Functions; Procedure TestSet_PassAsArgClone; - Procedure TestEnum_AsParams; Procedure TestSet_AsParams; Procedure TestSet_Property; + Procedure TestSet_EnumConst; + Procedure TestSet_AnonymousEnumType; + Procedure TestSet_CharFail; + Procedure TestSet_BooleanFail; + Procedure TestSet_ConstChar; // statements Procedure TestNestBegin; @@ -279,8 +293,9 @@ type Procedure TestArray_InsertDelete; Procedure TestExternalClass_TypeCastArrayToExternalArray; Procedure TestExternalClass_TypeCastArrayFromExternalArray; - // ToDo: const array + // ToDo: array const // ToDo: SetLength(array of static array) + // ToDo: SetLength(dim1,dim2) // record Procedure TestRecord_Var; @@ -371,7 +386,6 @@ type Procedure TestExternalClass_TypeCastToRootClass; Procedure TestExternalClass_TypeCastStringToExternalString; Procedure TestExternalClass_CallClassFunctionOfInstanceFail; - Procedure TestExternalClass_BracketOperatorOld; Procedure TestExternalClass_BracketAccessor; Procedure TestExternalClass_BracketAccessor_2ParamsFail; Procedure TestExternalClass_BracketAccessor_ReadOnly; @@ -389,6 +403,14 @@ type Procedure TestProcType_PropertyDelphi; Procedure TestProcType_WithClassInstDoPropertyFPC; Procedure TestProcType_Nested; + Procedure TestProcType_Typecast; + + // pointer + Procedure TestPointer; + Procedure TestPointer_Proc; + Procedure TestPointer_AssignRecordFail; + Procedure TestPointer_AssignStaticArrayFail; + Procedure TestPointer_ArrayParamsFail; // jsvalue Procedure TestJSValue_AssignToJSValue; @@ -403,6 +425,38 @@ type Procedure TestJSValue_FuncResultType; Procedure TestJSValue_ProcType_Assign; Procedure TestJSValue_ProcType_Equal; + + // RTTI + Procedure TestRTTI_ProcType; + Procedure TestRTTI_ProcType_ArgFromOtherUnit; + Procedure TestRTTI_EnumAndSetType; + Procedure TestRTTI_AnonymousEnumType; + Procedure TestRTTI_StaticArray; + Procedure TestRTTI_DynArray; + Procedure TestRTTI_ArrayNestedAnonymous; + // ToDo: Procedure TestRTTI_Pointer; + Procedure TestRTTI_PublishedMethodOverloadFail; + Procedure TestRTTI_PublishedMethodExternalFail; + Procedure TestRTTI_PublishedClassPropertyFail; + Procedure TestRTTI_PublishedClassFieldFail; + Procedure TestRTTI_PublishedFieldExternalFail; + Procedure TestRTTI_Class_Field; + Procedure TestRTTI_Class_Method; + Procedure TestRTTI_Class_Property; + Procedure TestRTTI_Class_PropertyParams; + // ToDo: property default value + Procedure TestRTTI_OverrideMethod; + Procedure TestRTTI_OverloadProperty; + // ToDo: array argument + Procedure TestRTTI_ClassForward; + Procedure TestRTTI_ClassOf; + Procedure TestRTTI_Record; + Procedure TestRTTI_LocalTypes; + Procedure TestRTTI_TypeInfo_BaseTypes; + Procedure TestRTTI_TypeInfo_LocalFail; + Procedure TestRTTI_TypeInfo_ExtTypeInfoClasses1; + Procedure TestRTTI_TypeInfo_ExtTypeInfoClasses2; + Procedure TestRTTI_TypeInfo_ExtTypeInfoClasses3; end; function LinesToStr(Args: array of const): string; @@ -573,7 +627,7 @@ begin Parser.Options:=Parser.Options+po_pas2js; FModule:=Nil; FConverter:=TPasToJSConverter.Create; - FConverter.UseLowerCase:=false; + FConverter.Options:=co_tcmodules; FExpectedErrorClass:=nil; end; @@ -584,6 +638,7 @@ begin FJSModule:=nil; FJSRegModuleCall:=nil; FJSModuleCallArgs:=nil; + FJSImplentationUses:=nil; FJSInterfaceUses:=nil; FJSModuleSrc:=nil; FJSInitBody:=nil; @@ -697,7 +752,7 @@ begin Fail('TTestModuleConverter.AddModule: file "'+aFilename+'" already exists'); Result:=TTestEnginePasResolver.Create; Result.Filename:=aFilename; - Result.AddObjFPCBuiltInIdentifiers([btChar,btString,btLongint,btInt64,btBoolean,btDouble]); + Result.AddObjFPCBuiltInIdentifiers(btAllPas2jsBaseTypes,bfAllPas2jsBaseProcs); Result.OnFindUnit:=@OnPasResolverFindUnit; FModules.Add(Result); end; @@ -763,14 +818,61 @@ begin end; procedure TCustomTestModule.ConvertModule; + + procedure CheckUsesList(UsesName: String; Arg: TJSArrayLiteralElement; + out UsesLit: TJSArrayLiteral); + var + i: Integer; + Item: TJSElement; + Lit: TJSLiteral; + begin + UsesLit:=nil; + AssertNotNull(UsesName+' uses section',Arg.Expr); + if (Arg.Expr.ClassType=TJSLiteral) and TJSLiteral(Arg.Expr).Value.IsNull then + exit; // null is ok + AssertEquals(UsesName+' uses section param is array',TJSArrayLiteral,Arg.Expr.ClassType); + FJSInterfaceUses:=TJSArrayLiteral(Arg.Expr); + for i:=0 to FJSInterfaceUses.Elements.Count-1 do + begin + Item:=FJSInterfaceUses.Elements.Elements[i].Expr; + AssertNotNull(UsesName+' uses section item['+IntToStr(i)+'].Expr',Item); + AssertEquals(UsesName+' uses section item['+IntToStr(i)+'] is lit',TJSLiteral,Item.ClassType); + Lit:=TJSLiteral(Item); + AssertEquals(UsesName+' uses section item['+IntToStr(i)+'] is string lit', + ord(jsbase.jstString),ord(Lit.Value.ValueType)); + end; + end; + + procedure CheckFunctionParam(ParamName: string; Arg: TJSArrayLiteralElement; + out Src: TJSSourceElements); + var + FunDecl: TJSFunctionDeclarationStatement; + FunDef: TJSFuncDef; + FunBody: TJSFunctionBody; + begin + Src:=nil; + AssertNotNull(ParamName,Arg.Expr); + AssertEquals(ParamName+' Arg.Expr type',TJSFunctionDeclarationStatement,Arg.Expr.ClassType); + FunDecl:=Arg.Expr as TJSFunctionDeclarationStatement; + AssertNotNull(ParamName+' FunDecl.AFunction',FunDecl.AFunction); + AssertEquals(ParamName+' FunDecl.AFunction type',TJSFuncDef,FunDecl.AFunction.ClassType); + FunDef:=FunDecl.AFunction as TJSFuncDef; + AssertEquals(ParamName+' name empty','',String(FunDef.Name)); + AssertNotNull(ParamName+' body',FunDef.Body); + AssertEquals(ParamName+' body type',TJSFunctionBody,FunDef.Body.ClassType); + FunBody:=FunDef.Body as TJSFunctionBody; + AssertNotNull(ParamName+' body.A',FunBody.A); + AssertEquals(ParamName+' body.A type',TJSSourceElements,FunBody.A.ClassType); + Src:=FunBody.A as TJSSourceElements; + end; + var ModuleNameExpr: TJSLiteral; - FunDecl, InitFunction: TJSFunctionDeclarationStatement; - FunDef: TJSFuncDef; + InitFunction: TJSFunctionDeclarationStatement; InitAssign: TJSSimpleAssignStatement; - FunBody: TJSFunctionBody; InitName: String; LastNode: TJSElement; + Arg: TJSArrayLiteralElement; begin if SkipTests then exit; try @@ -807,11 +909,13 @@ begin AssertNotNull('register module rtl.module args',JSRegModuleCall.Args); AssertEquals('rtl.module args',TJSArguments,JSRegModuleCall.Args.ClassType); FJSModuleCallArgs:=JSRegModuleCall.Args as TJSArguments; - AssertEquals('rtl.module args.count',3,JSModuleCallArgs.Elements.Count); // parameter 'unitname' - AssertNotNull('module name param',JSModuleCallArgs.Elements.Elements[0].Expr); - ModuleNameExpr:=JSModuleCallArgs.Elements.Elements[0].Expr as TJSLiteral; + if JSModuleCallArgs.Elements.Count<1 then + Fail('rtl.module first param unit missing'); + Arg:=JSModuleCallArgs.Elements.Elements[0]; + AssertNotNull('module name param',Arg.Expr); + ModuleNameExpr:=Arg.Expr as TJSLiteral; AssertEquals('module name param is string',ord(jstString),ord(ModuleNameExpr.Value.ValueType)); if Module is TPasProgram then AssertEquals('module name','program',String(ModuleNameExpr.Value.AsString)) @@ -819,22 +923,18 @@ begin AssertEquals('module name',Module.Name,String(ModuleNameExpr.Value.AsString)); // main uses section - AssertNotNull('interface uses section',JSModuleCallArgs.Elements.Elements[1].Expr); - AssertEquals('interface uses section type',TJSArrayLiteral,JSModuleCallArgs.Elements.Elements[1].Expr.ClassType); - FJSInterfaceUses:=JSModuleCallArgs.Elements.Elements[1].Expr as TJSArrayLiteral; - - // function() - AssertNotNull('module function',JSModuleCallArgs.Elements.Elements[2].Expr); - AssertEquals('module function type',TJSFunctionDeclarationStatement,JSModuleCallArgs.Elements.Elements[2].Expr.ClassType); - FunDecl:=JSModuleCallArgs.Elements.Elements[2].Expr as TJSFunctionDeclarationStatement; - AssertNotNull('module function def',FunDecl.AFunction); - FunDef:=FunDecl.AFunction as TJSFuncDef; - AssertEquals('module function name','',String(FunDef.Name)); - AssertNotNull('module function body',FunDef.Body); - FunBody:=FunDef.Body as TJSFunctionBody; - FJSModuleSrc:=FunBody.A as TJSSourceElements; - - // init this.$main - the last statement + if JSModuleCallArgs.Elements.Count<2 then + Fail('rtl.module second param main uses missing'); + Arg:=JSModuleCallArgs.Elements.Elements[1]; + CheckUsesList('interface',Arg,FJSInterfaceUses); + + // program/library/interface function() + if JSModuleCallArgs.Elements.Count<3 then + Fail('rtl.module third param intf-function missing'); + Arg:=JSModuleCallArgs.Elements.Elements[2]; + CheckFunctionParam('module intf-function',Arg,FJSModuleSrc); + + // search for this.$init or this.$main - the last statement if Module is TPasProgram then begin InitName:='$main'; @@ -858,6 +958,18 @@ begin CheckDottedIdentifier('init function',InitAssign.LHS,'this.'+InitName); end; end; + + // optional: implementation uses section + if JSModuleCallArgs.Elements.Count<4 then + exit; + Arg:=JSModuleCallArgs.Elements.Elements[3]; + CheckUsesList('implementation',Arg,FJSImplentationUses); + + // optional: implementation function() + if JSModuleCallArgs.Elements.Count<5 then + exit; + Arg:=JSModuleCallArgs.Elements.Elements[4]; + CheckFunctionParam('module impl-function',Arg,FJSImplementationSrc); end; procedure TCustomTestModule.ConvertProgram; @@ -900,7 +1012,8 @@ begin AssertEquals('GetDottedIdentifier',TJSPrimaryExpressionIdent,El.ClassType); end; -procedure TCustomTestModule.CheckSource(Msg, Statements, InitStatements: string); +procedure TCustomTestModule.CheckSource(Msg, Statements: String; + InitStatements: string; ImplStatements: string); var ActualSrc, ExpectedSrc, InitName: String; begin @@ -910,13 +1023,26 @@ begin InitName:='$main' else InitName:='$init'; - if (Module is TPasProgram) or (InitStatements<>'') then + if (Module is TPasProgram) or (Trim(InitStatements)<>'') then ExpectedSrc:=ExpectedSrc+LineEnding +'this.'+InitName+' = function () {'+LineEnding +InitStatements +'};'+LineEnding; //writeln('TTestModule.CheckSource InitStatements="',InitStatements,'"'); CheckDiff(Msg,ExpectedSrc,ActualSrc); + + if (JSImplementationSrc<>nil) then + begin + ActualSrc:=JSToStr(JSImplementationSrc); + ExpectedSrc:='var $impl = this.$impl;'+LineEnding+ImplStatements; + end + else + begin + ActualSrc:=''; + ExpectedSrc:=ImplStatements; + end; + //writeln('TTestModule.CheckSource InitStatements="',InitStatements,'"'); + CheckDiff(Msg,ExpectedSrc,ActualSrc); end; procedure TCustomTestModule.CheckDiff(Msg, Expected, Actual: string); @@ -975,7 +1101,7 @@ var break; end; until p^=#0; - raise Exception.Create('diff found, but lines are the same, internal error'); + Fail('diff found, but lines are the same, internal error'); end; var @@ -1072,7 +1198,7 @@ begin WriteSources(E.Filename,E.Row,E.Column); writeln('ERROR: TCustomTestModule.HandleParserError '+E.ClassName+':'+E.Message +' '+E.Filename+'('+IntToStr(E.Row)+','+IntToStr(E.Column)+')' - +' Line="'+Scanner.CurLine+'"' + +' MainModuleScannerLine="'+Scanner.CurLine+'"' ); RaiseException(E); end; @@ -1105,8 +1231,11 @@ end; procedure TCustomTestModule.HandleException(E: Exception); begin if IsErrorExpected(E) then exit; - WriteSources('',0,0); - writeln('ERROR: TCustomTestModule.HandleException '+E.ClassName+':'+E.Message); + if not (E is EAssertionFailedError) then + begin + WriteSources('',0,0); + writeln('ERROR: TCustomTestModule.HandleException '+E.ClassName+':'+E.Message); + end; RaiseException(E); end; @@ -1141,6 +1270,7 @@ var Line: string; aModule: TTestEnginePasResolver; begin + writeln('TCustomTestModule.WriteSources File="',aFilename,'" Row=',aRow,' Col=',aCol); for i:=0 to ModuleCount-1 do begin aModule:=Modules[i]; @@ -1679,16 +1809,17 @@ begin ConvertUnit; CheckSource('TestUnitProcVar', LinesToStr([ // statements - 'var $impl = {', - '};', - 'this.$impl = $impl;', + 'var $impl = this.$impl;', 'this.Proc1 = function () {', ' var v1 = 0;', '};', - '$impl.v2 = "";' - ]), - '' // this.$init - ); + '']), + // this.$init + '', + // implementation + LinesToStr([ + '$impl.v2 = "";', + ''])); end; procedure TTestModule.TestImplProc; @@ -1708,18 +1839,19 @@ begin ConvertUnit; CheckSource('TestImplProc', LinesToStr([ // statements - 'var $impl = {', - '};', - 'this.$impl = $impl;', + 'var $impl = this.$impl;', 'this.Proc1 = function () {', '};', - '$impl.Proc2 = function () {', - '};', '']), LinesToStr([ // this.$init 'this.Proc1();', '$impl.Proc2();', - ''])); + '']), + LinesToStr([ // implementation + '$impl.Proc2 = function () {', + '};', + '']) + ); end; procedure TTestModule.TestFunctionResult; @@ -2004,7 +2136,7 @@ begin ])); end; -procedure TTestModule.TestProcedureExternal; +procedure TTestModule.TestProc_External; begin StartProgram(false); Add('procedure Foo; external name ''console.log'';'); @@ -2028,7 +2160,7 @@ begin ])); end; -procedure TTestModule.TestProcedureExternalOtherUnit; +procedure TTestModule.TestProc_ExternalOtherUnit; begin AddModuleWithIntfImplSrc('unit2.pas', LinesToStr([ @@ -2068,7 +2200,7 @@ begin ])); end; -procedure TTestModule.TestProcedure_Asm; +procedure TTestModule.TestProc_Asm; begin StartProgram(false); Add('function DoIt: longint;'); @@ -2092,7 +2224,7 @@ begin ])); end; -procedure TTestModule.TestProcedureAssembler; +procedure TTestModule.TestProc_Assembler; begin StartProgram(false); Add('function DoIt: longint; assembler;'); @@ -2112,7 +2244,7 @@ begin ])); end; -procedure TTestModule.TestProcedure_VarParam; +procedure TTestModule.TestProc_VarParam; begin StartProgram(false); Add('type integer = longint;'); @@ -2179,7 +2311,7 @@ begin ])); end; -procedure TTestModule.TestProcedureOverload; +procedure TTestModule.TestProc_Overload; begin StartProgram(false); Add('procedure DoIt(vI: longint); begin end;'); @@ -2206,7 +2338,7 @@ begin ''])); end; -procedure TTestModule.TestProcedureOverloadForward; +procedure TTestModule.TestProc_OverloadForward; begin StartProgram(false); Add('procedure DoIt(vI: longint); forward;'); @@ -2229,7 +2361,7 @@ begin ''])); end; -procedure TTestModule.TestProcedureOverloadUnit; +procedure TTestModule.TestProc_OverloadUnit; begin StartUnit(false); Add('interface'); @@ -2251,30 +2383,30 @@ begin ConvertUnit; CheckSource('TestProcedureOverloadUnit', LinesToStr([ // statements - 'var $impl = {', - '};', - 'this.$impl = $impl;', + 'var $impl = this.$impl;', 'this.DoIt = function (vI) {', '};', 'this.DoIt$1 = function (vI, vJ) {', '};', - '$impl.DoIt$3 = function (vI, vJ, vK) {', - '};', - '$impl.DoIt$4 = function (vI, vJ, vK, vL) {', - '};', - '$impl.DoIt$2 = function (vI, vJ, vK, vL, vM) {', - '};', '']), - LinesToStr([ + LinesToStr([ // this.$init 'this.DoIt(1);', 'this.DoIt$1(2, 3);', '$impl.DoIt$3(4,5,6);', '$impl.DoIt$4(7,8,9,10);', '$impl.DoIt$2(11,12,13,14,15);', + '']), + LinesToStr([ // implementation + '$impl.DoIt$3 = function (vI, vJ, vK) {', + '};', + '$impl.DoIt$4 = function (vI, vJ, vK, vL) {', + '};', + '$impl.DoIt$2 = function (vI, vJ, vK, vL, vM) {', + '};', ''])); end; -procedure TTestModule.TestProcedureOverloadNested; +procedure TTestModule.TestProc_OverloadNested; begin StartProgram(false); Add('procedure DoIt(vA: longint); forward;'); @@ -2435,7 +2567,7 @@ begin ''])); end; -procedure TTestModule.TestEnumName; +procedure TTestModule.TestEnum_Name; begin StartProgram(false); Add('type TMyEnum = (Red, Green, Blue);'); @@ -2462,7 +2594,7 @@ begin ])); end; -procedure TTestModule.TestEnumNumber; +procedure TTestModule.TestEnum_Number; begin Converter.Options:=Converter.Options+[coEnumNumbers]; StartProgram(false); @@ -2489,7 +2621,7 @@ begin ])); end; -procedure TTestModule.TestEnumFunctions; +procedure TTestModule.TestEnum_Functions; begin StartProgram(false); Add('type TMyEnum = (Red, Green);'); @@ -2547,6 +2679,79 @@ begin ''])); end; +procedure TTestModule.TestEnum_AsParams; +begin + StartProgram(false); + Add('type TEnum = (Red,Blue);'); + Add('procedure DoIt(vG: TEnum; const vH: TEnum; var vI: TEnum);'); + Add('var vJ: TEnum;'); + Add('begin'); + Add(' vg:=vg;'); + Add(' vj:=vh;'); + Add(' vi:=vi;'); + Add(' doit(vg,vg,vg);'); + Add(' doit(vh,vh,vj);'); + Add(' doit(vi,vi,vi);'); + Add(' doit(vj,vj,vj);'); + Add('end;'); + Add('var i: TEnum;'); + Add('begin'); + Add(' doit(i,i,i);'); + ConvertProgram; + CheckSource('TestEnum_AsParams', + LinesToStr([ // statements + 'this.TEnum = {', + ' "0": "Red",', + ' Red: 0,', + ' "1": "Blue",', + ' Blue: 1', + '};', + 'this.DoIt = function (vG,vH,vI) {', + ' var vJ = 0;', + ' vG = vG;', + ' vJ = vH;', + ' vI.set(vI.get());', + ' this.DoIt(vG, vG, {', + ' get: function () {', + ' return vG;', + ' },', + ' set: function (v) {', + ' vG = v;', + ' }', + ' });', + ' this.DoIt(vH, vH, {', + ' get: function () {', + ' return vJ;', + ' },', + ' set: function (v) {', + ' vJ = v;', + ' }', + ' });', + ' this.DoIt(vI.get(), vI.get(), vI);', + ' this.DoIt(vJ, vJ, {', + ' get: function () {', + ' return vJ;', + ' },', + ' set: function (v) {', + ' vJ = v;', + ' }', + ' });', + '};', + 'this.i = 0;' + ]), + LinesToStr([ + 'this.DoIt(this.i,this.i,{', + ' p: this,', + ' get: function () {', + ' return this.p.i;', + ' },', + ' set: function (v) {', + ' this.p.i = v;', + ' }', + '});' + ])); +end; + procedure TTestModule.TestSet; begin StartProgram(false); @@ -2593,7 +2798,7 @@ begin ''])); end; -procedure TTestModule.TestSetOperators; +procedure TTestModule.TestSet_Operators; begin StartProgram(false); Add('type'); @@ -2640,12 +2845,8 @@ begin Add(' b:=vt>=[red];'); Add(' b:=[red]>=vt;'); Add(' b:=[red]>=[green];'); - Add(' b:=Red in vt;'); - Add(' b:=vc in vt;'); - Add(' b:=Green in [Red..Blue];'); - Add(' b:=vc in [Red..Blue];'); ConvertProgram; - CheckSource('TestEnumName', + CheckSource('TestSet_Operators', LinesToStr([ // statements 'this.TColor = {', ' "0":"Red",', @@ -2696,14 +2897,58 @@ begin 'this.B = rtl.geSet(this.vT, rtl.createSet(this.TColor.Red));', 'this.B = rtl.geSet(rtl.createSet(this.TColor.Red), this.vT);', 'this.B = rtl.geSet(rtl.createSet(this.TColor.Red), rtl.createSet(this.TColor.Green));', - 'this.B = this.vT[this.TColor.Red];', - 'this.B = this.vT[this.vC];', - 'this.B = rtl.createSet(null, this.TColor.Red, this.TColor.Blue)[this.TColor.Green];', - 'this.B = rtl.createSet(null, this.TColor.Red, this.TColor.Blue)[this.vC];', ''])); end; -procedure TTestModule.TestSetFunctions; +procedure TTestModule.TestSet_Operator_In; +begin + StartProgram(false); + Add('type'); + Add(' TColor = (Red, Green, Blue);'); + Add(' TColors = set of tcolor;'); + Add('var'); + Add(' vC: tcolor;'); + Add(' vT: tcolors;'); + Add(' B: boolean;'); + Add('begin'); + Add(' b:=red in vt;'); + Add(' b:=vc in vt;'); + Add(' b:=green in [red..blue];'); + Add(' b:=vc in [red..blue];'); + Add(' '); + Add(' if red in vt then ;'); + Add(' while vC in vt do ;'); + Add(' repeat'); + Add(' until vC in vt;'); + ConvertProgram; + CheckSource('TestSet_Operator_In', + LinesToStr([ // statements + 'this.TColor = {', + ' "0":"Red",', + ' Red:0,', + ' "1":"Green",', + ' Green:1,', + ' "2":"Blue",', + ' Blue:2', + ' };', + 'this.vC = 0;', + 'this.vT = {};', + 'this.B = false;' + ]), + LinesToStr([ + 'this.B = this.TColor.Red in this.vT;', + 'this.B = this.vC in this.vT;', + 'this.B = this.TColor.Green in rtl.createSet(null, this.TColor.Red, this.TColor.Blue);', + 'this.B = this.vC in rtl.createSet(null, this.TColor.Red, this.TColor.Blue);', + 'if (this.TColor.Red in this.vT) ;', + 'while (this.vC in this.vT) {', + '};', + 'do {', + '} while (!(this.vC in this.vT));', + ''])); +end; + +procedure TTestModule.TestSet_Functions; begin StartProgram(false); Add('type'); @@ -2771,79 +3016,6 @@ begin ''])); end; -procedure TTestModule.TestEnum_AsParams; -begin - StartProgram(false); - Add('type TEnum = (Red,Blue);'); - Add('procedure DoIt(vG: TEnum; const vH: TEnum; var vI: TEnum);'); - Add('var vJ: TEnum;'); - Add('begin'); - Add(' vg:=vg;'); - Add(' vj:=vh;'); - Add(' vi:=vi;'); - Add(' doit(vg,vg,vg);'); - Add(' doit(vh,vh,vj);'); - Add(' doit(vi,vi,vi);'); - Add(' doit(vj,vj,vj);'); - Add('end;'); - Add('var i: TEnum;'); - Add('begin'); - Add(' doit(i,i,i);'); - ConvertProgram; - CheckSource('TestEnum_AsParams', - LinesToStr([ // statements - 'this.TEnum = {', - ' "0": "Red",', - ' Red: 0,', - ' "1": "Blue",', - ' Blue: 1', - '};', - 'this.DoIt = function (vG,vH,vI) {', - ' var vJ = 0;', - ' vG = vG;', - ' vJ = vH;', - ' vI.set(vI.get());', - ' this.DoIt(vG, vG, {', - ' get: function () {', - ' return vG;', - ' },', - ' set: function (v) {', - ' vG = v;', - ' }', - ' });', - ' this.DoIt(vH, vH, {', - ' get: function () {', - ' return vJ;', - ' },', - ' set: function (v) {', - ' vJ = v;', - ' }', - ' });', - ' this.DoIt(vI.get(), vI.get(), vI);', - ' this.DoIt(vJ, vJ, {', - ' get: function () {', - ' return vJ;', - ' },', - ' set: function (v) {', - ' vJ = v;', - ' }', - ' });', - '};', - 'this.i = 0;' - ]), - LinesToStr([ - 'this.DoIt(this.i,this.i,{', - ' p: this,', - ' get: function () {', - ' return this.p.i;', - ' },', - ' set: function (v) {', - ' this.p.i = v;', - ' }', - '});' - ])); -end; - procedure TTestModule.TestSet_AsParams; begin StartProgram(false); @@ -2961,6 +3133,147 @@ begin ''])); end; +procedure TTestModule.TestSet_EnumConst; +begin + StartProgram(false); + Add('type'); + Add(' TEnum = (Red,Blue);'); + Add(' TEnums = set of TEnum;'); + Add('const'); + Add(' Orange = red;'); + Add('var'); + Add(' Enum: tenum;'); + Add(' Enums: tenums;'); + Add('begin'); + Add(' Include(enums,orange);'); + Add(' Exclude(enums,orange);'); + Add(' if orange in enums then;'); + Add(' if orange in [orange,red] then;'); + ConvertProgram; + CheckSource('TestEnumConst', + LinesToStr([ // statements + 'this.TEnum = {', + ' "0": "Red",', + ' Red: 0,', + ' "1": "Blue",', + ' Blue: 1', + '};', + 'this.Orange = this.TEnum.Red;', + 'this.Enum = 0;', + 'this.Enums = {};', + '']), + LinesToStr([ + 'this.Enums = rtl.includeSet(this.Enums, this.Orange);', + 'this.Enums = rtl.excludeSet(this.Enums, this.Orange);', + 'if (this.Orange in this.Enums) ;', + 'if (this.Orange in rtl.createSet(this.Orange, this.TEnum.Red)) ;', + ''])); +end; + +procedure TTestModule.TestSet_AnonymousEnumType; +begin + StartProgram(false); + Add('type'); + Add(' TFlags = set of (red, green);'); + Add('const'); + Add(' favorite = red;'); + Add('var'); + Add(' f: TFlags;'); + Add(' i: longint;'); + Add('begin'); + Add(' Include(f,red);'); + Add(' Include(f,favorite);'); + Add(' i:=ord(red);'); + Add(' i:=ord(favorite);'); + Add(' i:=ord(low(TFlags));'); + Add(' i:=ord(low(f));'); + Add(' i:=ord(low(favorite));'); + Add(' i:=ord(high(TFlags));'); + Add(' i:=ord(high(f));'); + Add(' i:=ord(high(favorite));'); + Add(' f:=[green,favorite];'); + ConvertProgram; + CheckSource('TestSet_AnonymousEnumType', + LinesToStr([ // statements + 'this.TFlags$a = {', + ' "0": "red",', + ' red: 0,', + ' "1": "green",', + ' green: 1', + '};', + 'this.favorite = this.TFlags$a.red;', + 'this.f = {};', + 'this.i = 0;', + '']), + LinesToStr([ + 'this.f = rtl.includeSet(this.f, this.TFlags$a.red);', + 'this.f = rtl.includeSet(this.f, this.favorite);', + 'this.i = this.TFlags$a.red;', + 'this.i = this.favorite;', + 'this.i = this.TFlags$a.red;', + 'this.i = this.TFlags$a.red;', + 'this.i = this.TFlags$a.red;', + 'this.i = this.TFlags$a.green;', + 'this.i = this.TFlags$a.green;', + 'this.i = this.TFlags$a.green;', + 'this.f = rtl.createSet(this.TFlags$a.green, this.favorite);', + ''])); +end; + +procedure TTestModule.TestSet_CharFail; +begin + StartProgram(false); + Add('type'); + Add(' TChars = set of char;'); + Add('begin'); + SetExpectedPasResolverError('Not supported: set of Char',nNotSupportedX); + ConvertProgram; +end; + +procedure TTestModule.TestSet_BooleanFail; +begin + StartProgram(false); + Add('type'); + Add(' TBools = set of boolean;'); + Add('begin'); + SetExpectedPasResolverError('Not supported: set of Boolean',nNotSupportedX); + ConvertProgram; +end; + +procedure TTestModule.TestSet_ConstChar; +begin + StartProgram(false); + Add('const'); + Add(' LowChars = [''a''..''z''];'); + Add(' Chars = LowChars+[''A''..''Z''];'); + Add('var'); + Add(' c: char;'); + Add(' s: string;'); + Add('begin'); + Add(' if c in lowchars then ;'); + Add(' if ''a'' in lowchars then ;'); + Add(' if s[1] in lowchars then ;'); + Add(' if c in chars then ;'); + Add(' if c in [''a''..''z'',''_''] then ;'); + Add(' if ''b'' in [''a''..''z'',''_''] then ;'); + ConvertProgram; + CheckSource('TestSet_ConstChar', + LinesToStr([ // statements + 'this.LowChars = rtl.createSet(null, 97, 122);', + 'this.Chars = rtl.unionSet(this.LowChars, rtl.createSet(null, 65, 90));', + 'this.c = "";', + 'this.s = "";', + '']), + LinesToStr([ + 'if (this.c.charCodeAt() in this.LowChars) ;', + 'if (97 in this.LowChars) ;', + 'if (this.s.charCodeAt(1 - 1) in this.LowChars) ;', + 'if (this.c.charCodeAt() in this.Chars) ;', + 'if (this.c.charCodeAt() in rtl.createSet(null, 97, 122, 95)) ;', + 'if (98 in rtl.createSet(null, 97, 122, 95)) ;', + ''])); +end; + procedure TTestModule.TestNestBegin; begin StartProgram(false); @@ -2990,14 +3303,14 @@ begin ConvertUnit; CheckSource('TestUnitImplVars', LinesToStr([ // statements - 'var $impl = {', - '};', - 'this.$impl = $impl;', + 'var $impl = this.$impl;', + '']), + '', // this.$init + LinesToStr([ // implementation '$impl.V1 = 0;', '$impl.V2 = 3;', - '$impl.V3 = "abc";' - ]), - ''); + '$impl.V3 = "abc";', + '']) ); end; procedure TTestModule.TestUnitImplConsts; @@ -3012,14 +3325,14 @@ begin ConvertUnit; CheckSource('TestUnitImplConsts', LinesToStr([ // statements - 'var $impl = {', - '};', - 'this.$impl = $impl;', + 'var $impl = this.$impl;', + '']), + '', // this.$init + LinesToStr([ // implementation '$impl.v1 = 3;', '$impl.v2 = 4;', - '$impl.v3 = "abc";' - ]), - ''); + '$impl.v3 = "abc";', + '']) ); end; procedure TTestModule.TestUnitImplRecord; @@ -3037,9 +3350,11 @@ begin ConvertUnit; CheckSource('TestUnitImplRecord', LinesToStr([ // statements - 'var $impl = {', - '};', - 'this.$impl = $impl;', + 'var $impl = this.$impl;', + '']), + // this.$init + '$impl.aRec.i = 3;', + LinesToStr([ // implementation '$impl.TMyRecord = function (s) {', ' if (s) {', ' this.i = s.i;', @@ -3050,10 +3365,8 @@ begin ' return this.i == b.i;', ' };', '};', - '$impl.aRec = new $impl.TMyRecord();' - ]), - '$impl.aRec.i = 3;' - ); + '$impl.aRec = new $impl.TMyRecord();', + '']) ); end; procedure TTestModule.TestRenameJSNameConflict; @@ -3157,20 +3470,20 @@ begin ConvertUnit; CheckSource('TestVarExternalOtherUnit', LinesToStr([ - 'var $impl = {', - '};', - 'this.$impl = $impl;', - '$impl.d = 0.0;', - '$impl.i = 0;', + 'var $impl = this.$impl;', '']), - LinesToStr([ + LinesToStr([ // this.$init '$impl.d = Global.NaN;', '$impl.d = Global.NaN;', '$impl.d = Global.NaN;', '$impl.i = pas.unit2.iV;', '$impl.i = pas.unit2.iV;', '$impl.i = pas.unit2.iV;', - ''])); + '']), + LinesToStr([ // implementation + '$impl.d = 0.0;', + '$impl.i = 0;', + '']) ); end; procedure TTestModule.TestCharConst; @@ -3263,16 +3576,20 @@ begin Add('var'); Add(' c: char;'); Add(' i: longint;'); + Add(' s: string;'); Add('begin'); Add(' i:=ord(c);'); + Add(' i:=ord(s[i]);'); ConvertProgram; CheckSource('TestChar_Ord', LinesToStr([ 'this.c = "";', - 'this.i = 0;' + 'this.i = 0;', + 'this.s = "";' ]), LinesToStr([ 'this.i = this.c.charCodeAt();', + 'this.i = this.s.charCodeAt(this.i-1);', ''])); end; @@ -3834,19 +4151,19 @@ begin // ToDo: check use analyzer CheckSource('TestAsmPas_Impl', LinesToStr([ - 'var $impl = {', - '};', - 'this.$impl = $impl;', + 'var $impl = this.$impl;', 'this.cIntf = 1;', 'this.vIntf = 0;', + '']), + '', // this.$init + LinesToStr([ // implementation 'var cLoc = 3;', '$impl.cImpl = 2;', '$impl.vImpl = 0;', '$impl.DoIt = function () {', ' var vLoc = 0;', '};', - '']), - ''); + '']) ); end; procedure TTestModule.TestTryFinally; @@ -7020,9 +7337,7 @@ begin ConvertUnit; CheckSource('TestClass_ExternalMethod', LinesToStr([ - 'var $impl = {', - '};', - 'this.$impl = $impl;', + 'var $impl = this.$impl;', 'rtl.createClass(this, "TCar", pas.unit2.TObject, function () {', ' this.DoIt = function () {', ' this.$DoIntern();', @@ -7031,9 +7346,8 @@ begin ' this.$DoIntern2();', ' };', ' });', - '$impl.Obj = null;', '']), - LinesToStr([ + LinesToStr([ // this.$init '$impl.Obj.$DoIntern();', '$impl.Obj.$DoIntern();', '$impl.Obj.$DoIntern2();', @@ -7045,7 +7359,10 @@ begin '$with1.$DoIntern();', '$with1.$DoIntern2();', '$with1.$DoIntern2();', - ''])); + '']), + LinesToStr([ // implementation + '$impl.Obj = null;', + '']) ); end; procedure TTestModule.TestClass_ExternalVirtualNameMismatchFail; @@ -7118,16 +7435,13 @@ begin ConvertUnit; CheckSource('TestClass_ExternalVar', LinesToStr([ - 'var $impl = {', - '};', - 'this.$impl = $impl;', + 'var $impl = this.$impl;', 'rtl.createClass(this, "TCar", pas.unit2.TObject, function () {', ' this.DoIt = function () {', ' this.$Intern = this.$Intern + 1;', ' this.$Intern2 = this.$Intern2 + 2;', ' };', ' });', - '$impl.Obj = null;', '']), LinesToStr([ '$impl.Obj.$Intern = $impl.Obj.$Intern + 1;', @@ -7135,6 +7449,9 @@ begin 'var $with1 = $impl.Obj;', '$with1.$Intern = $with1.$Intern + 1;', '$with1.$Intern2 = $with1.$Intern2 + 2;', + '']), + LinesToStr([ // implementation + '$impl.Obj = null;', ''])); end; @@ -8674,93 +8991,6 @@ begin ConvertProgram; end; -procedure TTestModule.TestExternalClass_BracketOperatorOld; -begin - StartProgram(false); - Add('{$modeswitch externalclass}'); - Add('type'); - Add(' TJSArray = class external name ''Array'''); - Add(' end;'); - Add(' TJSObject = class external name ''Object'''); - Add(' end;'); - Add('procedure DoIt(vI: JSValue; const vJ: jsvalue; var vK: jsvalue; out vL: jsvalue);'); - Add('begin end;'); - Add('var'); - Add(' Obj: tjsobject;'); - Add(' Arr: tjsarray;'); - Add(' s: string;'); - Add(' i: longint;'); - Add(' v: jsvalue;'); - Add('begin'); - Add(' arr[1]:=s;'); - Add(' arr[2]:=i;'); - Add(' arr[3]:=arr[4];'); - Add(' v:=arr[5];'); - Add(' v:=obj[''one''];'); - Add(' obj[''two'']:=i;'); - Add(' obj[''three'']:=v;'); - Add(' doit(arr[6],arr[7],arr[8],arr[9]);'); - Add(' doit(obj[''10''],obj[''11''],obj[''12''],obj[''13'']);'); - ConvertProgram; - CheckSource('TestExternalClass_BracketOperator', - LinesToStr([ // statements - 'this.DoIt = function (vI, vJ, vK, vL) {', - '};', - 'this.Obj = null;', - 'this.Arr = null;', - 'this.s = "";', - 'this.i = 0;', - 'this.v = undefined;', - '']), - LinesToStr([ // this.$main - 'this.Arr[1] = this.s;', - 'this.Arr[2] = this.i;', - 'this.Arr[3] = this.Arr[4];', - 'this.v = this.Arr[5];', - 'this.v = this.Obj["one"];', - 'this.Obj["two"] = this.i;', - 'this.Obj["three"] = this.v;', - 'this.DoIt(this.Arr[6], this.Arr[7], {', - ' a: 8,', - ' p: this.Arr,', - ' get: function () {', - ' return this.p[this.a];', - ' },', - ' set: function (v) {', - ' this.p[this.a] = v;', - ' }', - '}, {', - ' a: 9,', - ' p: this.Arr,', - ' get: function () {', - ' return this.p[this.a];', - ' },', - ' set: function (v) {', - ' this.p[this.a] = v;', - ' }', - '});', - ' this.DoIt(this.Obj["10"], this.Obj["11"], {', - ' a: "12",', - ' p: this.Obj,', - ' get: function () {', - ' return this.p[this.a];', - ' },', - ' set: function (v) {', - ' this.p[this.a] = v;', - ' }', - '}, {', - ' a: "13",', - ' p: this.Obj,', - ' get: function () {', - ' return this.p[this.a];', - ' },', - ' set: function (v) {', - ' this.p[this.a] = v;', - ' }', - '});', - ''])); -end; - procedure TTestModule.TestExternalClass_BracketAccessor; begin StartProgram(false); @@ -9798,6 +10028,196 @@ begin ''])); end; +procedure TTestModule.TestProcType_Typecast; +begin + StartProgram(false); + Add('type'); + Add(' TNotifyEvent = procedure(Sender: Pointer) of object;'); + Add(' TEvent = procedure of object;'); + Add(' TProcA = procedure(i: longint);'); + Add(' TFuncB = function(i, j: longint): longint;'); + Add('var'); + Add(' Notify: TNotifyEvent;'); + Add(' Event: TEvent;'); + Add(' ProcA: TProcA;'); + Add(' FuncB: TFuncB;'); + Add(' p: pointer;'); + Add('begin'); + Add(' Notify:=TNotifyEvent(Event);'); + Add(' Event:=TEvent(Event);'); + Add(' Event:=TEvent(Notify);'); + Add(' ProcA:=TProcA(FuncB);'); + Add(' FuncB:=TFuncB(FuncB);'); + Add(' FuncB:=TFuncB(ProcA);'); + Add(' ProcA:=TProcA(p);'); + Add(' FuncB:=TFuncB(p);'); + Add(' p:=Pointer(Notify);'); + Add(' p:=Notify;'); + Add(' p:=Pointer(ProcA);'); + Add(' p:=ProcA;'); + Add(' p:=Pointer(FuncB);'); + Add(' p:=FuncB;'); + ConvertProgram; + CheckSource('TestProcType_Typecast', + LinesToStr([ // statements + 'this.Notify = null;', + 'this.Event = null;', + 'this.ProcA = null;', + 'this.FuncB = null;', + 'this.p = null;', + '']), + LinesToStr([ // this.$main + 'this.Notify = this.Event;', + 'this.Event = this.Event;', + 'this.Event = this.Notify;', + 'this.ProcA = this.FuncB;', + 'this.FuncB = this.FuncB;', + 'this.FuncB = this.ProcA;', + 'this.ProcA = this.p;', + 'this.FuncB = this.p;', + 'this.p = this.Notify;', + 'this.p = this.Notify;', + 'this.p = this.ProcA;', + 'this.p = this.ProcA;', + 'this.p = this.FuncB;', + 'this.p = this.FuncB;', + ''])); +end; + +procedure TTestModule.TestPointer; +begin + StartProgram(false); + Add('type'); + Add(' TObject = class end;'); + Add(' TClass = class of TObject;'); + Add(' TArrInt = array of longint;'); + Add('var'); + Add(' v: jsvalue;'); + Add(' Obj: tobject;'); + Add(' C: tclass;'); + Add(' a: tarrint;'); + Add(' p: Pointer;'); + Add('begin'); + Add(' p:=p;'); + Add(' p:=nil;'); + Add(' if p=nil then;'); + Add(' if nil=p then;'); + Add(' if Assigned(p) then;'); + Add(' p:=Pointer(v);'); + Add(' p:=obj;'); + Add(' p:=c;'); + Add(' p:=a;'); + Add(' p:=tobject;'); + Add(' obj:=TObject(p);'); + Add(' c:=TClass(p);'); + Add(' a:=TArrInt(p);'); + ConvertProgram; + CheckSource('TestPointer', + LinesToStr([ // statements + 'rtl.createClass(this, "TObject", null, function () {', + ' this.$init = function () {', + ' };', + ' this.$final = function () {', + ' };', + '});', + 'this.v = undefined;', + 'this.Obj = null;', + 'this.C = null;', + 'this.a = [];', + 'this.p = null;', + '']), + LinesToStr([ // this.$main + 'this.p = this.p;', + 'this.p = null;', + 'if (this.p == null) ;', + 'if (null == this.p) ;', + 'if (this.p != null) ;', + 'this.p = this.v;', + 'this.p = this.Obj;', + 'this.p = this.C;', + 'this.p = this.a;', + 'this.p = this.TObject;', + 'this.Obj = this.p;', + 'this.C = this.p;', + 'this.a = this.p;', + ''])); +end; + +procedure TTestModule.TestPointer_Proc; +begin + StartProgram(false); + Add('type'); + Add(' TObject = class'); + Add(' procedure DoIt; virtual; abstract;'); + Add(' end;'); + Add('procedure DoSome; begin end;'); + Add('var'); + Add(' o: TObject;'); + Add(' p: Pointer;'); + Add('begin'); + Add(' p:=@DoSome;'); + Add(' p:=@o.DoIt;'); + ConvertProgram; + CheckSource('TestPointer_Proc', + LinesToStr([ // statements + 'rtl.createClass(this, "TObject", null, function () {', + ' this.$init = function () {', + ' };', + ' this.$final = function () {', + ' };', + '});', + 'this.DoSome = function () {', + '};', + 'this.o = null;', + 'this.p = null;', + '']), + LinesToStr([ // this.$main + 'this.p = rtl.createCallback(this, "DoSome");', + 'this.p = rtl.createCallback(this.o, "DoIt");', + ''])); +end; + +procedure TTestModule.TestPointer_AssignRecordFail; +begin + StartProgram(false); + Add('type'); + Add(' TRec = record end;'); + Add('var'); + Add(' p: Pointer;'); + Add(' r: TRec;'); + Add('begin'); + Add(' p:=r;'); + SetExpectedPasResolverError('Incompatible types: got "TRec" expected "Pointer"', + nIncompatibleTypesGotExpected); + ConvertProgram; +end; + +procedure TTestModule.TestPointer_AssignStaticArrayFail; +begin + StartProgram(false); + Add('type'); + Add(' TArr = array[boolean] of longint;'); + Add('var'); + Add(' p: Pointer;'); + Add(' a: TArr;'); + Add('begin'); + Add(' p:=a;'); + SetExpectedPasResolverError('Incompatible types: got "TArr" expected "Pointer"', + nIncompatibleTypesGotExpected); + ConvertProgram; +end; + +procedure TTestModule.TestPointer_ArrayParamsFail; +begin + StartProgram(false); + Add('var'); + Add(' p: Pointer;'); + Add('begin'); + Add(' p:=p[1];'); + SetExpectedPasResolverError('illegal qualifier "["',nIllegalQualifier); + ConvertProgram; +end; + procedure TTestModule.TestJSValue_AssignToJSValue; begin StartProgram(false); @@ -9807,6 +10227,7 @@ begin Add(' s: string;'); Add(' b: boolean;'); Add(' d: double;'); + Add(' p: pointer;'); Add('begin'); Add(' v:=v;'); Add(' v:=1;'); @@ -9821,6 +10242,7 @@ begin Add(' v:=0.1;'); Add(' v:=d;'); Add(' v:=nil;'); + Add(' v:=p;'); ConvertProgram; CheckSource('TestJSValue_AssignToJSValue', LinesToStr([ // statements @@ -9829,6 +10251,7 @@ begin 'this.s = "";', 'this.b = false;', 'this.d = 0.0;', + 'this.p = null;', '']), LinesToStr([ // this.$main 'this.v = this.v;', @@ -9844,6 +10267,7 @@ begin 'this.v = 0.1;', 'this.v = this.d;', 'this.v = null;', + 'this.v = this.p;', ''])); end; @@ -10490,6 +10914,1120 @@ begin ''])); end; +procedure TTestModule.TestRTTI_ProcType; +begin + Converter.Options:=Converter.Options-[coNoTypeInfo]; + StartProgram(false); + Add('type'); + Add(' TProcA = procedure;'); + Add(' TMethodB = procedure of object;'); + Add(' TProcC = procedure; varargs;'); + Add(' TProcD = procedure(i: longint; const j: string; var c: char; out d: double);'); + Add(' TProcE = function: longint;'); + Add(' TProcF = function(const p: TProcA): longint;'); + Add('var p: pointer;'); + Add('begin'); + Add(' p:=typeinfo(tproca);'); + ConvertProgram; + CheckSource('TestRTTI_ProcType', + LinesToStr([ // statements + 'this.$rtti.$ProcVar("TProcA", {', + ' procsig: rtl.newTIProcSig(null)', + '});', + 'this.$rtti.$MethodVar("TMethodB", {', + ' procsig: rtl.newTIProcSig(null),', + ' methodkind: 0', + '});', + 'this.$rtti.$ProcVar("TProcC", {', + ' procsig: rtl.newTIProcSig(null, 2)', + '});', + 'this.$rtti.$ProcVar("TProcD", {', + ' procsig: rtl.newTIProcSig([["i", rtl.longint], ["j", rtl.string, 2], ["c", rtl.char, 1], ["d", rtl.double, 4]])', + '});', + 'this.$rtti.$ProcVar("TProcE", {', + ' procsig: rtl.newTIProcSig(null, rtl.longint)', + '});', + 'this.$rtti.$ProcVar("TProcF", {', + ' procsig: rtl.newTIProcSig([["p", this.$rtti["TProcA"], 2]], rtl.longint)', + '});', + 'this.p = null;', + '']), + LinesToStr([ // this.$main + 'this.p = this.$rtti["TProcA"];', + ''])); +end; + +procedure TTestModule.TestRTTI_ProcType_ArgFromOtherUnit; +begin + Converter.Options:=Converter.Options-[coNoTypeInfo]; + + AddModuleWithIntfImplSrc('unit2.pas', + LinesToStr([ + 'type', + ' TObject = class end;' + ]), + ''); + StartUnit(true); + Add('interface'); + Add('uses unit2;'); + Add('type'); + Add(' TProcA = function(o: tobject): tobject;'); + Add('implementation'); + Add('type'); + Add(' TProcB = function(o: tobject): tobject;'); + Add('var p: Pointer;'); + Add('initialization'); + Add(' p:=typeinfo(tproca);'); + Add(' p:=typeinfo(tprocb);'); + ConvertUnit; + CheckSource('TestRTTI_ProcType_ArgFromOtherUnit', + LinesToStr([ // statements + 'var $impl = this.$impl;', + 'this.$rtti.$ProcVar("TProcA", {', + ' procsig: rtl.newTIProcSig([["o", pas.unit2.$rtti["TObject"]]], pas.unit2.$rtti["TObject"])', + '});', + '']), + LinesToStr([ // this.$init + '$impl.p = this.$rtti["TProcA"];', + '$impl.p = this.$rtti["TProcB"];', + '']), + LinesToStr([ // implementation + 'this.$rtti.$ProcVar("TProcB", {', + ' procsig: rtl.newTIProcSig([["o", pas.unit2.$rtti["TObject"]]], pas.unit2.$rtti["TObject"])', + '});', + '$impl.p = null;', + '']) ); +end; + +procedure TTestModule.TestRTTI_EnumAndSetType; +begin + Converter.Options:=Converter.Options-[coNoTypeInfo]; + StartProgram(false); + Add('type'); + Add(' TFlag = (light,dark);'); + Add(' TFlags = set of TFlag;'); + Add(' TProc = function(f: TFlags): TFlag;'); + Add('var p: pointer;'); + Add('begin'); + Add(' p:=typeinfo(tflag);'); + Add(' p:=typeinfo(tflags);'); + ConvertProgram; + CheckSource('TestRTTI_EnumAndType', + LinesToStr([ // statements + 'this.TFlag = {', + ' "0": "light",', + ' light: 0,', + ' "1": "dark",', + ' dark: 1', + '};', + 'this.$rtti.$Enum("TFlag", {', + ' minvalue: 0,', + ' maxvalue: 1,', + ' enumtype: this.TFlag', + '});', + 'this.$rtti.$Set("TFlags", {', + ' comptype: this.$rtti["TFlag"]', + '});', + 'this.$rtti.$ProcVar("TProc", {', + ' procsig: rtl.newTIProcSig([["f", this.$rtti["TFlags"]]], this.$rtti["TFlag"])', + '});', + 'this.p = null;', + '']), + LinesToStr([ // this.$main + 'this.p = this.$rtti["TFlag"];', + 'this.p = this.$rtti["TFlags"];', + ''])); +end; + +procedure TTestModule.TestRTTI_AnonymousEnumType; +begin + Converter.Options:=Converter.Options-[coNoTypeInfo]; + StartProgram(false); + Add('type'); + Add(' TFlags = set of (red, green);'); + Add('var'); + Add(' f: TFlags;'); + Add('begin'); + Add(' Include(f,red);'); + ConvertProgram; + CheckSource('TestRTTI_AnonymousEnumType', + LinesToStr([ // statements + 'this.TFlags$a = {', + ' "0": "red",', + ' red: 0,', + ' "1": "green",', + ' green: 1', + '};', + 'this.$rtti.$Enum("TFlags$a", {', + ' minvalue: 0,', + ' maxvalue: 1,', + ' enumtype: this.TFlags$a', + '});', + 'this.$rtti.$Set("TFlags", {', + ' comptype: this.$rtti["TFlags$a"]', + '});', + 'this.f = {};', + '']), + LinesToStr([ + 'this.f = rtl.includeSet(this.f, this.TFlags$a.red);', + ''])); +end; + +procedure TTestModule.TestRTTI_StaticArray; +begin + Converter.Options:=Converter.Options-[coNoTypeInfo]; + StartProgram(false); + Add('type'); + Add(' TFlag = (light,dark);'); + Add(' TFlagNames = array[TFlag] of string;'); + Add(' TBoolNames = array[boolean] of string;'); + Add(' TProc = function(f: TBoolNames): TFlagNames;'); + Add('var p: pointer;'); + Add('begin'); + Add(' p:=typeinfo(TFlagNames);'); + Add(' p:=typeinfo(TBoolNames);'); + ConvertProgram; + CheckSource('TestRTTI_StaticArray', + LinesToStr([ // statements + 'this.TFlag = {', + ' "0": "light",', + ' light: 0,', + ' "1": "dark",', + ' dark: 1', + '};', + 'this.$rtti.$Enum("TFlag", {', + ' minvalue: 0,', + ' maxvalue: 1,', + ' enumtype: this.TFlag', + '});', + 'this.$rtti.$StaticArray("TFlagNames", {', + ' dims: [2],', + ' eltype: rtl.string', + '});', + 'this.$rtti.$StaticArray("TBoolNames", {', + ' dims: [2],', + ' eltype: rtl.string', + '});', + 'this.$rtti.$ProcVar("TProc", {', + ' procsig: rtl.newTIProcSig([["f", this.$rtti["TBoolNames"]]], this.$rtti["TFlagNames"])', + '});', + 'this.p = null;', + '']), + LinesToStr([ // this.$main + 'this.p = this.$rtti["TFlagNames"];', + 'this.p = this.$rtti["TBoolNames"];', + ''])); +end; + +procedure TTestModule.TestRTTI_DynArray; +begin + Converter.Options:=Converter.Options-[coNoTypeInfo]; + StartProgram(false); + Add('type'); + Add(' TArrStr = array of string;'); + Add(' TArr2Dim = array of tarrstr;'); + Add(' TProc = function(f: TArrStr): TArr2Dim;'); + Add('var p: pointer;'); + Add('begin'); + Add(' p:=typeinfo(tarrstr);'); + Add(' p:=typeinfo(tarr2dim);'); + ConvertProgram; + CheckSource('TestRTTI_DynArray', + LinesToStr([ // statements + 'this.$rtti.$DynArray("TArrStr", {', + ' eltype: rtl.string', + '});', + 'this.$rtti.$DynArray("TArr2Dim", {', + ' eltype: this.$rtti["TArrStr"]', + '});', + 'this.$rtti.$ProcVar("TProc", {', + ' procsig: rtl.newTIProcSig([["f", this.$rtti["TArrStr"]]], this.$rtti["TArr2Dim"])', + '});', + 'this.p = null;', + '']), + LinesToStr([ // this.$main + 'this.p = this.$rtti["TArrStr"];', + 'this.p = this.$rtti["TArr2Dim"];', + ''])); +end; + +procedure TTestModule.TestRTTI_ArrayNestedAnonymous; +begin + Converter.Options:=Converter.Options-[coNoTypeInfo]; + StartProgram(false); + Add('type'); + Add(' TArr = array of array of longint;'); + Add('var a: TArr;'); + Add('begin'); + ConvertProgram; + CheckSource('TestRTTI_ArrayNestedAnonymous', + LinesToStr([ // statements + 'this.$rtti.$DynArray("TArr$a", {', + ' eltype: rtl.longint', + '});', + 'this.$rtti.$DynArray("TArr", {', + ' eltype: this.$rtti["TArr$a"]', + '});', + 'this.a = [];', + '']), + LinesToStr([ // this.$main + ])); +end; + +procedure TTestModule.TestRTTI_PublishedMethodOverloadFail; +begin + Converter.Options:=Converter.Options-[coNoTypeInfo]; + StartProgram(false); + Add('type'); + Add(' TObject = class'); + Add(' published'); + Add(' procedure Proc; virtual; abstract;'); + Add(' procedure Proc(Sender: tobject); virtual; abstract;'); + Add(' end;'); + Add('begin'); + SetExpectedPasResolverError('Duplicate identifier "Proc" at test1.pp(6,18)', + nDuplicateIdentifier); + ConvertProgram; +end; + +procedure TTestModule.TestRTTI_PublishedMethodExternalFail; +begin + Converter.Options:=Converter.Options-[coNoTypeInfo]; + StartProgram(false); + Add('type'); + Add(' TObject = class'); + Add(' published'); + Add(' procedure Proc; external name ''foo'';'); + Add(' end;'); + Add('begin'); + SetExpectedPasResolverError(sPublishedNameMustMatchExternal, + nPublishedNameMustMatchExternal); + ConvertProgram; +end; + +procedure TTestModule.TestRTTI_PublishedClassPropertyFail; +begin + Converter.Options:=Converter.Options-[coNoTypeInfo]; + StartProgram(false); + Add('type'); + Add(' TObject = class'); + Add(' class var FA: longint;'); + Add(' published'); + Add(' class property A: longint read FA;'); + Add(' end;'); + Add('begin'); + SetExpectedPasResolverError('Invalid published property modifier "class"', + nInvalidXModifierY); + ConvertProgram; +end; + +procedure TTestModule.TestRTTI_PublishedClassFieldFail; +begin + Converter.Options:=Converter.Options-[coNoTypeInfo]; + StartProgram(false); + Add('type'); + Add(' TObject = class'); + Add(' published'); + Add(' class var FA: longint;'); + Add(' end;'); + Add('begin'); + SetExpectedPasResolverError(sSymbolCannotBePublished, + nSymbolCannotBePublished); + ConvertProgram; +end; + +procedure TTestModule.TestRTTI_PublishedFieldExternalFail; +begin + Converter.Options:=Converter.Options-[coNoTypeInfo]; + StartProgram(false); + Add('{$modeswitch externalclass}'); + Add('type'); + Add(' TObject = class'); + Add(' published'); + Add(' V: longint; external name ''foo'';'); + Add(' end;'); + Add('begin'); + SetExpectedPasResolverError(sPublishedNameMustMatchExternal, + nPublishedNameMustMatchExternal); + ConvertProgram; +end; + +procedure TTestModule.TestRTTI_Class_Field; +begin + Converter.Options:=Converter.Options-[coNoTypeInfo]; + StartProgram(false); + Add('{$modeswitch externalclass}'); + Add('type'); + Add(' TObject = class'); + Add(' private'); + Add(' FPropA: string;'); + Add(' published'); + Add(' VarLI: longint;'); + Add(' VarC: char;'); + Add(' VarS: string;'); + Add(' VarD: double;'); + Add(' VarB: boolean;'); + Add(' VarCa: cardinal;'); + Add(' VarSmI: smallint;'); + Add(' VarW: word;'); + Add(' VarShI: shortint;'); + Add(' VarBy: byte;'); + Add(' VarExt: longint external name ''VarExt'';'); + Add(' end;'); + Add('var p: pointer;'); + Add(' Obj: tobject;'); + Add('begin'); + Add(' p:=typeinfo(tobject);'); + Add(' p:=typeinfo(p);'); + Add(' p:=typeinfo(obj);'); + ConvertProgram; + CheckSource('TestRTTI_Class_Field', + LinesToStr([ // statements + 'rtl.createClass(this, "TObject", null, function () {', + ' this.$init = function () {', + ' this.FPropA = "";', + ' this.VarLI = 0;', + ' this.VarC = "";', + ' this.VarS = "";', + ' this.VarD = 0.0;', + ' this.VarB = false;', + ' this.VarCa = 0;', + ' this.VarSmI = 0;', + ' this.VarW = 0;', + ' this.VarShI = 0;', + ' this.VarBy = 0;', + ' };', + ' this.$final = function () {', + ' };', + ' var $r = this.$rtti;', + ' $r.addField("VarLI", rtl.longint);', + ' $r.addField("VarC", rtl.char);', + ' $r.addField("VarS", rtl.string);', + ' $r.addField("VarD", rtl.double);', + ' $r.addField("VarB", rtl.boolean);', + ' $r.addField("VarCa", rtl.cardinal);', + ' $r.addField("VarSmI", rtl.smallint);', + ' $r.addField("VarW", rtl.word);', + ' $r.addField("VarShI", rtl.shortint);', + ' $r.addField("VarBy", rtl.byte);', + ' $r.addField("VarExt", rtl.longint);', + '});', + 'this.p = null;', + 'this.Obj = null;', + '']), + LinesToStr([ // this.$main + 'this.p = this.$rtti["TObject"];', + 'this.p = rtl.pointer;', + 'this.p = this.Obj.$rtti;', + ''])); +end; + +procedure TTestModule.TestRTTI_Class_Method; +begin + Converter.Options:=Converter.Options-[coNoTypeInfo]; + StartProgram(false); + Add('type'); + Add(' TObject = class'); + Add(' private'); + Add(' procedure Internal; external name ''$intern'';'); + Add(' published'); + Add(' procedure Click; virtual; abstract;'); + Add(' procedure Notify(Sender: TObject); virtual; abstract;'); + Add(' function GetNotify: boolean; external name ''GetNotify'';'); + Add(' procedure Println(a,b: longint); varargs; virtual; abstract;'); + Add(' end;'); + Add('begin'); + ConvertProgram; + CheckSource('TestRTTI_Class_Method', + LinesToStr([ // statements + 'rtl.createClass(this, "TObject", null, function () {', + ' this.$init = function () {', + ' };', + ' this.$final = function () {', + ' };', + ' var $r = this.$rtti;', + ' $r.addMethod("Click", 0, null);', + ' $r.addMethod("Notify", 0, [["Sender", $r]]);', + ' $r.addMethod("GetNotify", 1, null, rtl.boolean,{flags: 4});', + ' $r.addMethod("Println", 0, [["a", rtl.longint], ["b", rtl.longint]], null, {', + ' flags: 2', + ' });', + '});', + '']), + LinesToStr([ // this.$main + ''])); +end; + +procedure TTestModule.TestRTTI_Class_Property; +begin + Converter.Options:=Converter.Options-[coNoTypeInfo]; + StartProgram(false); + Add('{$modeswitch externalclass}'); + Add('type'); + Add(' TObject = class'); + Add(' private'); + Add(' FColor: longint;'); + Add(' FColorStored: boolean;'); + Add(' procedure SetColor(Value: longint); virtual; abstract;'); + Add(' function GetColor: longint; virtual; abstract;'); + Add(' function GetColorStored: boolean; virtual; abstract;'); + Add(' FExtSize: longint external name ''$extSize'';'); + Add(' FExtSizeStored: boolean external name ''$extSizeStored'';'); + Add(' procedure SetExtSize(Value: longint); external name ''$setSize'';'); + Add(' function GetExtSize: longint; external name ''$getSize'';'); + Add(' function GetExtSizeStored: boolean; external name ''$getExtSizeStored'';'); + Add(' published'); + Add(' property ColorA: longint read FColor;'); + Add(' property ColorB: longint write FColor;'); + Add(' property ColorC: longint read GetColor write SetColor;'); + Add(' property ColorD: longint read FColor write FColor stored FColorStored;'); + Add(' property ExtSizeA: longint read FExtSize write FExtSize;'); + Add(' property ExtSizeB: longint read GetExtSize write SetExtSize stored FExtSizeStored;'); + Add(' property ExtSizeC: longint read FExtSize write FExtSize stored GetExtSizeStored;'); + Add(' end;'); + Add('begin'); + ConvertProgram; + CheckSource('TestRTTI_Class_Property', + LinesToStr([ // statements + 'rtl.createClass(this, "TObject", null, function () {', + ' this.$init = function () {', + ' this.FColor = 0;', + ' this.FColorStored = false;', + ' };', + ' this.$final = function () {', + ' };', + ' var $r = this.$rtti;', + ' $r.addProperty("ColorA", 0, rtl.longint, "FColor", "");', + ' $r.addProperty("ColorB", 0, rtl.longint, "", "FColor");', + ' $r.addProperty("ColorC", 3, rtl.longint, "GetColor", "SetColor");', + ' $r.addProperty("ColorD", 0, rtl.longint, "FColor", "FColor",{', + ' stored: "FColorStored"', + ' }', + ' );', + ' $r.addProperty("ExtSizeA", 0, rtl.longint, "$extSize", "$extSize");', + ' $r.addProperty("ExtSizeB", 3, rtl.longint, "$getSize", "$setSize",{', + ' stored: "$extSizeStored"', + ' }', + ' );', + ' $r.addProperty("ExtSizeC", 4, rtl.longint, "$extSize", "$extSize",{', + ' stored: "$getExtSizeStored"', + ' }', + ' );', + '});', + '']), + LinesToStr([ // this.$main + ''])); +end; + +procedure TTestModule.TestRTTI_Class_PropertyParams; +begin + Converter.Options:=Converter.Options-[coNoTypeInfo]; + StartProgram(false); + Add('{$modeswitch externalclass}'); + Add('type'); + Add(' integer = longint;'); + Add(' TObject = class'); + Add(' private'); + Add(' function GetItems(i: integer): tobject; virtual; abstract;'); + Add(' procedure SetItems(i: integer; value: tobject); virtual; abstract;'); + Add(' function GetValues(const i: integer; var b: boolean): char; virtual; abstract;'); + Add(' procedure SetValues(const i: integer; var b: boolean; value: char); virtual; abstract;'); + Add(' published'); + Add(' property Items[Index: integer]: tobject read getitems write setitems;'); + Add(' property Values[const keya: integer; var keyb: boolean]: char read getvalues write setvalues;'); + Add(' end;'); + Add('begin'); + ConvertProgram; + CheckSource('TestRTTI_Class_PropertyParams', + LinesToStr([ // statements + 'rtl.createClass(this, "TObject", null, function () {', + ' this.$init = function () {', + ' };', + ' this.$final = function () {', + ' };', + ' var $r = this.$rtti;', + ' $r.addProperty("Items", 3, $r, "GetItems", "SetItems");', + ' $r.addProperty("Values", 3, rtl.char, "GetValues", "SetValues");', + '});', + '']), + LinesToStr([ // this.$main + ''])); +end; + +procedure TTestModule.TestRTTI_OverrideMethod; +begin + Converter.Options:=Converter.Options-[coNoTypeInfo]; + StartProgram(false); + Add('type'); + Add(' TObject = class'); + Add(' published'); + Add(' procedure DoIt; virtual; abstract;'); + Add(' end;'); + Add(' TSky = class'); + Add(' published'); + Add(' procedure DoIt; override;'); + Add(' end;'); + Add('procedure TSky.DoIt; begin end;'); + Add('begin'); + ConvertProgram; + CheckSource('TestRTTI_OverrideMethod', + LinesToStr([ // statements + 'rtl.createClass(this, "TObject", null, function () {', + ' this.$init = function () {', + ' };', + ' this.$final = function () {', + ' };', + ' var $r = this.$rtti;', + ' $r.addMethod("DoIt", 0, null);', + '});', + 'rtl.createClass(this, "TSky", this.TObject, function () {', + ' this.DoIt = function () {', + ' };', + '});', + '']), + LinesToStr([ // this.$main + ''])); +end; + +procedure TTestModule.TestRTTI_OverloadProperty; +begin + Converter.Options:=Converter.Options-[coNoTypeInfo]; + StartProgram(false); + Add('type'); + Add(' TObject = class'); + Add(' protected'); + Add(' FFlag: longint;'); + Add(' published'); + Add(' property Flag: longint read FFlag;'); + Add(' end;'); + Add(' TSky = class'); + Add(' published'); + Add(' property Flag: longint write FFlag;'); + Add(' end;'); + Add('begin'); + ConvertProgram; + CheckSource('TestRTTI_OverrideMethod', + LinesToStr([ // statements + 'rtl.createClass(this, "TObject", null, function () {', + ' this.$init = function () {', + ' this.FFlag = 0;', + ' };', + ' this.$final = function () {', + ' };', + ' var $r = this.$rtti;', + ' $r.addProperty("Flag", 0, rtl.longint, "FFlag", "");', + '});', + 'rtl.createClass(this, "TSky", this.TObject, function () {', + ' var $r = this.$rtti;', + ' $r.addProperty("Flag", 0, rtl.longint, "", "FFlag");', + '});', + '']), + LinesToStr([ // this.$main + ''])); +end; + +procedure TTestModule.TestRTTI_ClassForward; +begin + Converter.Options:=Converter.Options-[coNoTypeInfo]; + StartProgram(false); + Add('type'); + Add(' TObject = class end;'); + Add(' tbridge = class;'); + Add(' TProc = function: tbridge;'); + Add(' TOger = class'); + Add(' published'); + Add(' FBridge: tbridge;'); + Add(' procedure SetBridge(Value: tbridge); virtual; abstract;'); + Add(' property Bridge: tbridge read fbridge write setbridge;'); + Add(' end;'); + Add(' TBridge = class'); + Add(' FOger: toger;'); + Add(' end;'); + Add('var p: Pointer;'); + Add(' b: tbridge;'); + Add('begin'); + Add(' p:=typeinfo(tbridge);'); + Add(' p:=typeinfo(b);'); + ConvertProgram; + CheckSource('TestRTTI_ClassForward', + LinesToStr([ // statements + 'rtl.createClass(this, "TObject", null, function () {', + ' this.$init = function () {', + ' };', + ' this.$final = function () {', + ' };', + '});', + 'this.$rtti.$Class("TBridge");', + 'this.$rtti.$ProcVar("TProc", {', + ' procsig: rtl.newTIProcSig(null, this.$rtti["TBridge"])', + '});', + 'rtl.createClass(this, "TOger", this.TObject, function () {', + ' this.$init = function () {', + ' pas.program.TObject.$init.call(this);', + ' this.FBridge = null;', + ' };', + ' this.$final = function () {', + ' this.FBridge = undefined;', + ' pas.program.TObject.$final.call(this);', + ' };', + ' var $r = this.$rtti;', + ' $r.addField("FBridge", pas.program.$rtti["TBridge"]);', + ' $r.addMethod("SetBridge", 0, [["Value", pas.program.$rtti["TBridge"]]]);', + ' $r.addProperty("Bridge", 2, pas.program.$rtti["TBridge"], "FBridge", "SetBridge");', + '});', + 'rtl.createClass(this, "TBridge", this.TObject, function () {', + ' this.$init = function () {', + ' pas.program.TObject.$init.call(this);', + ' this.FOger = null;', + ' };', + ' this.$final = function () {', + ' this.FOger = undefined;', + ' pas.program.TObject.$final.call(this);', + ' };', + '});', + 'this.p = null;', + 'this.b = null;', + '']), + LinesToStr([ // this.$main + 'this.p = this.$rtti["TBridge"];', + 'this.p = this.b.$rtti;', + ''])); +end; + +procedure TTestModule.TestRTTI_ClassOf; +begin + Converter.Options:=Converter.Options-[coNoTypeInfo]; + StartProgram(false); + Add('type'); + Add(' TClass = class of tobject;'); + Add(' TProcA = function: TClass;'); + Add(' TObject = class'); + Add(' published'); + Add(' C: tclass;'); + Add(' end;'); + Add(' tfox = class;'); + Add(' TBird = class end;'); + Add(' TBirds = class of tbird;'); + Add(' TFox = class end;'); + Add(' TFoxes = class of tfox;'); + Add(' TCows = class of TCow;'); + Add(' TCow = class;'); + Add(' TCow = class end;'); + Add('begin'); + ConvertProgram; + CheckSource('TestRTTI_ClassOf', + LinesToStr([ // statements + 'this.$rtti.$Class("TObject");', + 'this.$rtti.$ClassRef("TClass", {', + ' instancetype: this.$rtti["TObject"]', + '});', + 'this.$rtti.$ProcVar("TProcA", {', + ' procsig: rtl.newTIProcSig(null, this.$rtti["TClass"])', + '});', + 'rtl.createClass(this, "TObject", null, function () {', + ' this.$init = function () {', + ' this.C = null;', + ' };', + ' this.$final = function () {', + ' this.C = undefined;', + ' };', + ' var $r = this.$rtti;', + ' $r.addField("C", pas.program.$rtti["TClass"]);', + '});', + 'this.$rtti.$Class("TFox");', + 'rtl.createClass(this, "TBird", this.TObject, function () {', + '});', + 'this.$rtti.$ClassRef("TBirds", {', + ' instancetype: this.$rtti["TBird"]', + '});', + 'rtl.createClass(this, "TFox", this.TObject, function () {', + '});', + 'this.$rtti.$ClassRef("TFoxes", {', + ' instancetype: this.$rtti["TFox"]', + '});', + 'this.$rtti.$Class("TCow");', + 'this.$rtti.$ClassRef("TCows", {', + ' instancetype: this.$rtti["TCow"]', + '});', + 'rtl.createClass(this, "TCow", this.TObject, function () {', + '});', + '']), + LinesToStr([ // this.$main + ''])); +end; + +procedure TTestModule.TestRTTI_Record; +begin + Converter.Options:=Converter.Options-[coNoTypeInfo]; + StartProgram(false); + Add('type'); + Add(' integer = longint;'); + Add(' TPoint = record'); + Add(' x,y: integer;'); + Add(' end;'); + Add('var p: pointer;'); + Add(' r: tpoint;'); + Add('begin'); + Add(' p:=typeinfo(tpoint);'); + Add(' p:=typeinfo(r);'); + Add(' p:=typeinfo(r.x);'); + ConvertProgram; + CheckSource('TestRTTI_Record', + LinesToStr([ // statements + 'this.TPoint = function (s) {', + ' if (s) {', + ' this.x = s.x;', + ' this.y = s.y;', + ' } else {', + ' this.x = 0;', + ' this.y = 0;', + ' };', + ' this.$equal = function (b) {', + ' return (this.x == b.x) && (this.y == b.y);', + ' };', + '};', + 'this.$rtti.$Record("TPoint", {}).addFields("x", rtl.longint, "y", rtl.longint);', + 'this.p = null;', + 'this.r = new this.TPoint();', + '']), + LinesToStr([ // this.$main + 'this.p = this.$rtti["TPoint"];', + 'this.p = this.$rtti["TPoint"];', + 'this.p = rtl.longint;', + ''])); +end; + +procedure TTestModule.TestRTTI_LocalTypes; +begin + Converter.Options:=Converter.Options-[coNoTypeInfo]; + StartProgram(false); + Add('procedure DoIt;'); + Add('type'); + Add(' integer = longint;'); + Add(' TPoint = record'); + Add(' x,y: integer;'); + Add(' end;'); + Add('begin'); + Add('end;'); + Add('begin'); + ConvertProgram; + CheckSource('TestRTTI_LocalTypes', + LinesToStr([ // statements + 'this.DoIt = function () {', + ' this.TPoint = function (s) {', + ' if (s) {', + ' this.x = s.x;', + ' this.y = s.y;', + ' } else {', + ' this.x = 0;', + ' this.y = 0;', + ' };', + ' this.$equal = function (b) {', + ' return (this.x == b.x) && (this.y == b.y);', + ' };', + ' };', + '};', + '']), + LinesToStr([ // this.$main + ''])); +end; + +procedure TTestModule.TestRTTI_TypeInfo_BaseTypes; +begin + Converter.Options:=Converter.Options-[coNoTypeInfo]; + StartProgram(false); + Add('type'); + Add(' TCaption = string;'); + Add(' TYesNo = boolean;'); + Add(' TLetter = char;'); + Add(' TFloat = double;'); + Add(' TPtr = pointer;'); + Add(' TShortInt = shortint;'); + Add(' TByte = byte;'); + Add(' TSmallInt = smallint;'); + Add(' TWord = word;'); + Add(' TInt32 = longint;'); + Add(' TDWord = cardinal;'); + Add(' TValue = jsvalue;'); + Add('var p: TPtr;'); + Add('begin'); + Add(' p:=typeinfo(string);'); + Add(' p:=typeinfo(tcaption);'); + Add(' p:=typeinfo(boolean);'); + Add(' p:=typeinfo(tyesno);'); + Add(' p:=typeinfo(char);'); + Add(' p:=typeinfo(tletter);'); + Add(' p:=typeinfo(double);'); + Add(' p:=typeinfo(tfloat);'); + Add(' p:=typeinfo(pointer);'); + Add(' p:=typeinfo(tptr);'); + Add(' p:=typeinfo(shortint);'); + Add(' p:=typeinfo(tshortint);'); + Add(' p:=typeinfo(byte);'); + Add(' p:=typeinfo(tbyte);'); + Add(' p:=typeinfo(smallint);'); + Add(' p:=typeinfo(tsmallint);'); + Add(' p:=typeinfo(word);'); + Add(' p:=typeinfo(tword);'); + Add(' p:=typeinfo(cardinal);'); + Add(' p:=typeinfo(tdword);'); + Add(' p:=typeinfo(jsvalue);'); + Add(' p:=typeinfo(tvalue);'); + ConvertProgram; + CheckSource('TestRTTI_TypeInfo_BaseTypes', + LinesToStr([ // statements + 'this.p = null;', + '']), + LinesToStr([ // this.$main + 'this.p = rtl.string;', + 'this.p = rtl.string;', + 'this.p = rtl.boolean;', + 'this.p = rtl.boolean;', + 'this.p = rtl.char;', + 'this.p = rtl.char;', + 'this.p = rtl.double;', + 'this.p = rtl.double;', + 'this.p = rtl.pointer;', + 'this.p = rtl.pointer;', + 'this.p = rtl.shortint;', + 'this.p = rtl.shortint;', + 'this.p = rtl.byte;', + 'this.p = rtl.byte;', + 'this.p = rtl.smallint;', + 'this.p = rtl.smallint;', + 'this.p = rtl.word;', + 'this.p = rtl.word;', + 'this.p = rtl.cardinal;', + 'this.p = rtl.cardinal;', + 'this.p = rtl.jsvalue;', + 'this.p = rtl.jsvalue;', + ''])); +end; + +procedure TTestModule.TestRTTI_TypeInfo_LocalFail; +begin + Converter.Options:=Converter.Options-[coNoTypeInfo]; + StartProgram(false); + Add('procedure DoIt;'); + Add('type'); + Add(' integer = longint;'); + Add(' TPoint = record'); + Add(' x,y: integer;'); + Add(' end;'); + Add('var p: pointer;'); + Add('begin'); + Add(' p:=typeinfo(tpoint);'); + Add('end;'); + Add('begin'); + SetExpectedPasResolverError(sSymbolCannotBePublished,nSymbolCannotBePublished); + ConvertProgram; +end; + +procedure TTestModule.TestRTTI_TypeInfo_ExtTypeInfoClasses1; +begin + Converter.Options:=Converter.Options-[coNoTypeInfo]; + StartProgram(false); + Add('{$modeswitch externalclass}'); + Add('type'); + Add(' TTypeInfo = class external name ''rtl.tTypeInfo'' end;'); + Add(' TTypeInfoInteger = class external name ''rtl.tTypeInfoInteger''(TTypeInfo) end;'); + Add(' TFlag = (up,down);'); + Add(' TTypeInfoEnum = class external name ''rtl.tTypeInfoEnum''(TTypeInfoInteger) end;'); + Add(' TFlags = set of TFlag;'); + Add(' TTypeInfoSet = class external name ''rtl.tTypeInfoSet''(TTypeInfo) end;'); + Add('var'); + Add(' ti: TTypeInfo;'); + Add(' tiInt: TTypeInfoInteger;'); + Add(' tiEnum: TTypeInfoEnum;'); + Add(' tiSet: TTypeInfoSet;'); + Add('begin'); + Add(' ti:=typeinfo(string);'); + Add(' ti:=typeinfo(boolean);'); + Add(' ti:=typeinfo(char);'); + Add(' ti:=typeinfo(double);'); + Add(' tiInt:=typeinfo(shortint);'); + Add(' tiInt:=typeinfo(byte);'); + Add(' tiInt:=typeinfo(smallint);'); + Add(' tiInt:=typeinfo(word);'); + Add(' tiInt:=typeinfo(longint);'); + Add(' tiInt:=typeinfo(cardinal);'); + Add(' ti:=typeinfo(jsvalue);'); + Add(' tiEnum:=typeinfo(tflag);'); + Add(' tiSet:=typeinfo(tflags);'); + ConvertProgram; + CheckSource('TestRTTI_TypeInfo_ExtTypeInfoClasses1', + LinesToStr([ // statements + 'this.TFlag = {', + ' "0": "up",', + ' up: 0,', + ' "1": "down",', + ' down: 1', + '};', + 'this.$rtti.$Enum("TFlag", {', + ' minvalue: 0,', + ' maxvalue: 1,', + ' enumtype: this.TFlag', + '});', + 'this.$rtti.$Set("TFlags", {', + ' comptype: this.$rtti["TFlag"]', + '});', + 'this.ti = null;', + 'this.tiInt = null;', + 'this.tiEnum = null;', + 'this.tiSet = null;', + '']), + LinesToStr([ // this.$main + 'this.ti = rtl.string;', + 'this.ti = rtl.boolean;', + 'this.ti = rtl.char;', + 'this.ti = rtl.double;', + 'this.tiInt = rtl.shortint;', + 'this.tiInt = rtl.byte;', + 'this.tiInt = rtl.smallint;', + 'this.tiInt = rtl.word;', + 'this.tiInt = rtl.longint;', + 'this.tiInt = rtl.cardinal;', + 'this.ti = rtl.jsvalue;', + 'this.tiEnum = this.$rtti["TFlag"];', + 'this.tiSet = this.$rtti["TFlags"];', + ''])); +end; + +procedure TTestModule.TestRTTI_TypeInfo_ExtTypeInfoClasses2; +begin + Converter.Options:=Converter.Options-[coNoTypeInfo]; + StartProgram(false); + Add('{$modeswitch externalclass}'); + Add('type'); + Add(' TTypeInfo = class external name ''rtl.tTypeInfo'' end;'); + Add(' TStaticArr = array[boolean] of string;'); + Add(' TTypeInfoStaticArray = class external name ''rtl.tTypeInfoStaticArray''(TTypeInfo) end;'); + Add(' TDynArr = array of string;'); + Add(' TTypeInfoDynArray = class external name ''rtl.tTypeInfoDynArray''(TTypeInfo) end;'); + Add(' TProc = procedure;'); + Add(' TTypeInfoProcVar = class external name ''rtl.tTypeInfoProcVar''(TTypeInfo) end;'); + Add(' TMethod = procedure of object;'); + Add(' TTypeInfoMethodVar = class external name ''rtl.tTypeInfoMethodVar''(TTypeInfoProcVar) end;'); + Add('var'); + Add(' StaticArray: TStaticArr;'); + Add(' tiStaticArray: TTypeInfoStaticArray;'); + Add(' DynArray: TDynArr;'); + Add(' tiDynArray: TTypeInfoDynArray;'); + Add(' ProcVar: TProc;'); + Add(' tiProcVar: TTypeInfoProcVar;'); + Add(' MethodVar: TMethod;'); + Add(' tiMethodVar: TTypeInfoMethodVar;'); + Add('begin'); + Add(' tiStaticArray:=typeinfo(StaticArray);'); + Add(' tiStaticArray:=typeinfo(TStaticArr);'); + Add(' tiDynArray:=typeinfo(DynArray);'); + Add(' tiDynArray:=typeinfo(TDynArr);'); + Add(' tiProcVar:=typeinfo(ProcVar);'); + Add(' tiProcVar:=typeinfo(TProc);'); + Add(' tiMethodVar:=typeinfo(MethodVar);'); + Add(' tiMethodVar:=typeinfo(TMethod);'); + ConvertProgram; + CheckSource('TestRTTI_TypeInfo_ExtTypeInfoClasses2', + LinesToStr([ // statements + ' this.$rtti.$StaticArray("TStaticArr", {', + ' dims: [2],', + ' eltype: rtl.string', + '});', + 'this.$rtti.$DynArray("TDynArr", {', + ' eltype: rtl.string', + '});', + 'this.$rtti.$ProcVar("TProc", {', + ' procsig: rtl.newTIProcSig(null)', + '});', + 'this.$rtti.$MethodVar("TMethod", {', + ' procsig: rtl.newTIProcSig(null),', + ' methodkind: 0', + '});', + 'this.StaticArray = rtl.arrayNewMultiDim([2], "");', + 'this.tiStaticArray = null;', + 'this.DynArray = [];', + 'this.tiDynArray = null;', + 'this.ProcVar = null;', + 'this.tiProcVar = null;', + 'this.MethodVar = null;', + 'this.tiMethodVar = null;', + '']), + LinesToStr([ // this.$main + 'this.tiStaticArray = this.$rtti["TStaticArr"];', + 'this.tiStaticArray = this.$rtti["TStaticArr"];', + 'this.tiDynArray = this.$rtti["TDynArr"];', + 'this.tiDynArray = this.$rtti["TDynArr"];', + 'this.tiProcVar = this.$rtti["TProc"];', + 'this.tiProcVar = this.$rtti["TProc"];', + 'this.tiMethodVar = this.$rtti["TMethod"];', + 'this.tiMethodVar = this.$rtti["TMethod"];', + ''])); +end; + +procedure TTestModule.TestRTTI_TypeInfo_ExtTypeInfoClasses3; +begin + Converter.Options:=Converter.Options-[coNoTypeInfo]; + StartProgram(false); + Add('{$modeswitch externalclass}'); + Add('type'); + Add(' TTypeInfo = class external name ''rtl.tTypeInfo'' end;'); + Add(' TRec = record end;'); + Add(' TTypeInfoRecord = class external name ''rtl.tTypeInfoRecord''(TTypeInfo) end;'); + // ToDo: ^PRec + Add(' TObject = class end;'); + Add(' TTypeInfoClass = class external name ''rtl.tTypeInfoClass''(TTypeInfo) end;'); + Add(' TClass = class of tobject;'); + Add(' TTypeInfoClassRef = class external name ''rtl.tTypeInfoClassRef''(TTypeInfo) end;'); + Add(' TTypeInfoPointer = class external name ''rtl.tTypeInfoPointer''(TTypeInfo) end;'); + Add('var'); + Add(' Rec: trec;'); + Add(' tiRecord: ttypeinforecord;'); + Add(' Obj: tobject;'); + Add(' tiClass: ttypeinfoclass;'); + Add(' aClass: tclass;'); + Add(' tiClassRef: ttypeinfoclassref;'); + // ToDo: ^PRec + Add(' tiPointer: ttypeinfopointer;'); + Add('begin'); + Add(' tirecord:=typeinfo(trec);'); + Add(' tirecord:=typeinfo(trec);'); + Add(' ticlass:=typeinfo(obj);'); + Add(' ticlass:=typeinfo(tobject);'); + Add(' ticlassref:=typeinfo(aclass);'); + Add(' ticlassref:=typeinfo(tclass);'); + ConvertProgram; + CheckSource('TestRTTI_TypeInfo_ExtTypeInfoClasses3', + LinesToStr([ // statements + 'this.TRec = function (s) {', + '};', + 'this.$rtti.$Record("TRec", {});', + 'rtl.createClass(this, "TObject", null, function () {', + ' this.$init = function () {', + ' };', + ' this.$final = function () {', + ' };', + '});', + 'this.$rtti.$ClassRef("TClass", {', + ' instancetype: this.$rtti["TObject"]', + '});', + 'this.Rec = new this.TRec();', + 'this.tiRecord = null;', + 'this.Obj = null;', + 'this.tiClass = null;', + 'this.aClass = null;', + 'this.tiClassRef = null;', + 'this.tiPointer = null;', + '']), + LinesToStr([ // this.$main + 'this.tiRecord = this.$rtti["TRec"];', + 'this.tiRecord = this.$rtti["TRec"];', + 'this.tiClass = this.Obj.$rtti;', + 'this.tiClass = this.$rtti["TObject"];', + 'this.tiClassRef = this.$rtti["TClass"];', + 'this.tiClassRef = this.$rtti["TClass"];', + ''])); +end; + Initialization RegisterTests([TTestModule]); end. diff --git a/packages/pastojs/tests/tcoptimizations.pas b/packages/pastojs/tests/tcoptimizations.pas index 0591127b92..73329a2f5f 100644 --- a/packages/pastojs/tests/tcoptimizations.pas +++ b/packages/pastojs/tests/tcoptimizations.pas @@ -38,8 +38,8 @@ type FAnalyzerModule: TPasAnalyzer; FAnalyzerProgram: TPasAnalyzer; FWholeProgramOptimization: boolean; - function OnConverterIsElementUsed(Sender: TObject; El: TPasElement - ): boolean; + function OnConverterIsElementUsed(Sender: TObject; El: TPasElement): boolean; + function OnConverterIsTypeInfoUsed(Sender: TObject; El: TPasElement): boolean; protected procedure SetUp; override; procedure TearDown; override; @@ -78,6 +78,8 @@ type procedure TestWPO_CallInherited; procedure TestWPO_UseUnit; procedure TestWPO_ProgramPublicDeclaration; + procedure TestWPO_RTTI_PublishedField; + procedure TestWPO_RTTI_TypeInfo; end; implementation @@ -99,6 +101,21 @@ begin {$ENDIF} end; +function TCustomTestOptimizations.OnConverterIsTypeInfoUsed(Sender: TObject; + El: TPasElement): boolean; +var + A: TPasAnalyzer; +begin + if WholeProgramOptimization then + A:=AnalyzerProgram + else + A:=AnalyzerModule; + Result:=A.IsTypeInfoUsed(El); + {$IF defined(VerbosePas2JS) or defined(VerbosePasAnalyzer)} + writeln('TCustomTestOptimizations.OnConverterIsTypeInfoUsed El=',GetObjName(El),' WPO=',WholeProgramOptimization,' Result=',Result); + {$ENDIF} +end; + procedure TCustomTestOptimizations.SetUp; begin inherited SetUp; @@ -108,6 +125,7 @@ begin FAnalyzerProgram:=TPasAnalyzer.Create; FAnalyzerProgram.Resolver:=Engine; Converter.OnIsElementUsed:=@OnConverterIsElementUsed; + Converter.OnIsTypeInfoUsed:=@OnConverterIsTypeInfoUsed; end; procedure TCustomTestOptimizations.TearDown; @@ -756,6 +774,88 @@ begin CheckDiff('TestWPO_ProgramPublicDeclaration',ExpectedSrc,ActualSrc); end; +procedure TTestOptimizations.TestWPO_RTTI_PublishedField; +var + ActualSrc, ExpectedSrc: String; +begin + Converter.Options:=Converter.Options-[coNoTypeInfo]; + StartProgram(true); + Add('type'); + Add(' TArrA = array of char;'); + Add(' TArrB = array of string;'); + Add(' TObject = class'); + Add(' public'); + Add(' PublicA: TArrA;'); + Add(' published'); + Add(' PublishedB: TArrB;'); + Add(' end;'); + Add('var'); + Add(' C: TObject;'); + Add('begin'); + Add(' C.PublicA:=nil;'); + ConvertProgram; + ActualSrc:=JSToStr(JSModule); + ExpectedSrc:=LinesToStr([ + 'rtl.module("program", ["system"], function () {', + 'this.$rtti.$DynArray("TArrB", {', + ' eltype: rtl.string', + '});', + ' rtl.createClass(this, "TObject", null, function () {', + ' this.$init = function () {', + ' this.PublicA = [];', + ' this.PublishedB = [];', + ' };', + ' this.$final = function () {', + ' this.PublicA = undefined;', + ' this.PublishedB = undefined;', + ' };', + ' var $r = this.$rtti;', + ' $r.addField("PublishedB", pas.program.$rtti["TArrB"]);', + ' });', + ' this.C = null;', + ' this.$main = function () {', + ' this.C.PublicA = [];', + ' };', + '});', + '']); + CheckDiff('TestWPO_RTTI_PublishedField',ExpectedSrc,ActualSrc); +end; + +procedure TTestOptimizations.TestWPO_RTTI_TypeInfo; +var + ActualSrc, ExpectedSrc: String; +begin + Converter.Options:=Converter.Options-[coNoTypeInfo]; + StartProgram(true); + Add('type'); + Add(' TArrA = array of char;'); + Add(' TArrB = array of string;'); + Add('var'); + Add(' A: TArrA;'); + Add(' B: TArrB;'); + Add(' p: pointer;'); + Add('begin'); + Add(' A:=nil;'); + Add(' p:=typeinfo(B);'); + ConvertProgram; + ActualSrc:=JSToStr(JSModule); + ExpectedSrc:=LinesToStr([ + 'rtl.module("program", ["system"], function () {', + 'this.$rtti.$DynArray("TArrB", {', + ' eltype: rtl.string', + '});', + ' this.A = [];', + ' this.B = [];', + ' this.p = null;', + ' this.$main = function () {', + ' this.A = [];', + ' this.p = this.$rtti["TArrB"];', + ' };', + '});', + '']); + CheckDiff('TestWPO_RTTI_TypeInfo',ExpectedSrc,ActualSrc); +end; + Initialization RegisterTests([TTestOptimizations]); end. diff --git a/utils/fpdoc/dw_xml.pp b/utils/fpdoc/dw_xml.pp index b9e435e834..6925c45ac0 100644 --- a/utils/fpdoc/dw_xml.pp +++ b/utils/fpdoc/dw_xml.pp @@ -100,7 +100,7 @@ var Node['virtual'] := 'true'; if pmAbstract in ADecl.Modifiers then Node['abstract'] := 'true'; - if pmStatic in ADecl.Modifiers then + if assigned(ADecl.ProcType) and (ptmStatic in ADecl.ProcType.Modifiers) then Node['static'] := 'true'; if pmReintroduce in ADecl.Modifiers then Node['reintroduce'] := 'true'; |