diff options
author | marco <marco@3ad0048d-3df7-0310-abae-a5850022a9f2> | 2017-06-19 11:46:56 +0000 |
---|---|---|
committer | marco <marco@3ad0048d-3df7-0310-abae-a5850022a9f2> | 2017-06-19 11:46:56 +0000 |
commit | 35944ee1860ae65f8cd8d3dc0f56c1ba208bcc3e (patch) | |
tree | 4e3e5ba77ba5b98e798973aca3036811dac7bf4a | |
parent | 916559cb1fd5bb6ece7cfe9400e58a221b51bb3f (diff) | |
download | fpc-35944ee1860ae65f8cd8d3dc0f56c1ba208bcc3e.tar.gz |
--- Merging r36085 into '.':
U packages/pastojs/src/fppas2js.pp
--- Recording mergeinfo for merge of r36085 into '.':
U .
--- Merging r36118 into '.':
U packages/pastojs/tests/tcmodules.pas
G packages/pastojs/src/fppas2js.pp
--- Recording mergeinfo for merge of r36118 into '.':
G .
--- Merging r36156 into '.':
G packages/pastojs/src/fppas2js.pp
--- Recording mergeinfo for merge of r36156 into '.':
G .
--- Merging r36172 into '.':
G packages/pastojs/tests/tcmodules.pas
G packages/pastojs/src/fppas2js.pp
--- Recording mergeinfo for merge of r36172 into '.':
G .
--- Merging r36236 into '.':
G packages/pastojs/tests/tcmodules.pas
G packages/pastojs/src/fppas2js.pp
--- Recording mergeinfo for merge of r36236 into '.':
G .
--- Merging r36242 into '.':
G packages/pastojs/src/fppas2js.pp
G packages/pastojs/tests/tcmodules.pas
--- Recording mergeinfo for merge of r36242 into '.':
G .
--- Merging r36247 into '.':
G packages/pastojs/src/fppas2js.pp
--- Recording mergeinfo for merge of r36247 into '.':
G .
--- Merging r36319 into '.':
G packages/pastojs/tests/tcmodules.pas
U packages/pastojs/tests/tcoptimizations.pas
G packages/pastojs/src/fppas2js.pp
--- Recording mergeinfo for merge of r36319 into '.':
G .
--- Merging r36459 into '.':
G packages/pastojs/src/fppas2js.pp
G packages/pastojs/tests/tcmodules.pas
--- Recording mergeinfo for merge of r36459 into '.':
G .
--- Merging r36460 into '.':
G packages/pastojs/tests/tcmodules.pas
G packages/pastojs/src/fppas2js.pp
--- Recording mergeinfo for merge of r36460 into '.':
G .
# revisions: 36085,36118,36156,36172,36236,36242,36247,36319,36459,36460
git-svn-id: https://svn.freepascal.org/svn/fpc/branches/fixes_3_0@36538 3ad0048d-3df7-0310-abae-a5850022a9f2
-rw-r--r-- | packages/pastojs/src/fppas2js.pp | 647 | ||||
-rw-r--r-- | packages/pastojs/tests/tcmodules.pas | 458 | ||||
-rw-r--r-- | packages/pastojs/tests/tcoptimizations.pas | 2 |
3 files changed, 852 insertions, 255 deletions
diff --git a/packages/pastojs/src/fppas2js.pp b/packages/pastojs/src/fppas2js.pp index 2a59133fe7..4f2117c7e8 100644 --- a/packages/pastojs/src/fppas2js.pp +++ b/packages/pastojs/src/fppas2js.pp @@ -109,6 +109,7 @@ Works: - external vars and methods - const - bracket accessor, getter/setter has external name '[]' + - TObject.Free sets variable to nil - dynamic arrays - arrays can be null - init as "arr = []" so typeof works @@ -244,15 +245,11 @@ Works: - ECMAScript6: - use 0b for binary literals - use 0o for octal literals +- dotted unit names, namespaces ToDos: -- $modeswitch -> define/undefine <modeswitch> -- scanner: bark on unknown modeswitch -- scanner: bark on disabling fixed modeswitch -- $ifopt, $if option - +- change some == into === - constant evaluation -- integer ranges - static arrays - property index specifier - RTTI @@ -261,14 +258,13 @@ ToDos: - defaultvalue - type alias type - documentation +- sourcemaps - move local types to unit scope - local var absolute -- make -Jirtl.js default for -Jc and -Tnodejs, needs #IFDEF in cfg - FuncName:= (instead of Result:=) - check memleaks - @@ compare method in delphi mode - make records more lightweight -- dotted unit names, namespaces - enumeration for..in..do - pointer of record - nested types in class @@ -301,6 +297,7 @@ Not in Version 1.0: - add Self only if needed - set operators on literals without temporary arrays, a in [b], [a]*b<>[] - shortcut for test set is empty a=[] a<>[] + - put set literals into constants - use a number for small sets - nested procs without var, instead as "function name(){}" -O1 insert local/unit vars for global type references: @@ -331,7 +328,7 @@ interface uses Classes, SysUtils, math, contnrs, jsbase, jstree, PasTree, PScanner, - PasResolver; + PasResolver, PasResolveEval; // message numbers const @@ -358,6 +355,7 @@ const nTypeXCannotBePublished = 4021; nNotSupportedX = 4022; nNestedInheritedNeedsParameters = 4023; + nFreeNeedsVar = 4024; // resourcestring patterns of messages resourcestring sPasElementNotSupported = 'Pascal element not supported: %s'; @@ -383,6 +381,7 @@ resourcestring sTypeXCannotBePublished = 'Type "%s" cannot be published'; sNotSupportedX = 'Not supported: %s'; sNestedInheritedNeedsParameters = 'nested inherited needs parameters'; + sFreeNeedsVar = 'Free needs a variable'; const ExtClassBracketAccessor = '[]'; // external name '[]' marks the array param getter/setter @@ -405,6 +404,8 @@ type pbifnGetObject, pbifnIs, pbifnIsExt, + pbifnFreeLocalVar, + pbifnFreeVar, pbifnProcType_Create, pbifnProcType_Equal, pbifnProgramMain, @@ -465,6 +466,7 @@ type pbivnRTTIPropStored, pbivnRTTISet_CompType, pbivnSelf, + pbivnTObjectDestroy, pbivnWith, pbitnAnonymousPostfix, pbitnIntDouble, @@ -502,6 +504,8 @@ const 'getObject', // rtl.getObject 'is', // rtl.is 'isExt', // rtl.isExt + 'freeLoc', // rtl.freeLoc + 'free', // rtl.free 'createCallback', // rtl.createCallback 'eqCallback', // rtl.eqCallback '$main', @@ -537,7 +541,7 @@ const 'symDiffSet', // rtl.symDiffSet >< (symmetrical difference) 'unionSet', // rtl.unionSet + 'spaceLeft', // rtl.spaceLeft - 'strSetLength', + 'strSetLength', // rtl. '$init', '$e', '$impl', @@ -562,22 +566,23 @@ const 'stored', 'comptype', 'Self', + 'tObjectDestroy', // rtl.tObjectDestroy '$with', '$a', 'NativeInt', - 'tTypeInfo', - 'tTypeInfoClass', - 'tTypeInfoClassRef', - 'tTypeInfoDynArray', - 'tTypeInfoEnum', - 'tTypeInfoInteger', - 'tTypeInfoMethodVar', - 'tTypeInfoPointer', - 'tTypeInfoProcVar', - 'tTypeInfoRecord', - 'tTypeInfoRefToProcVar', - 'tTypeInfoSet', - 'tTypeInfoStaticArray', + 'tTypeInfo', // rtl. + 'tTypeInfoClass', // rtl. + 'tTypeInfoClassRef', // rtl. + 'tTypeInfoDynArray', // rtl. + 'tTypeInfoEnum', // rtl. + 'tTypeInfoInteger', // rtl. + 'tTypeInfoMethodVar', // rtl. + 'tTypeInfoPointer', // rtl. + 'tTypeInfoProcVar', // rtl. + 'tTypeInfoRecord', // rtl. + 'tTypeInfoRefToProcVar', // rtl. + 'tTypeInfoSet', // rtl. + 'tTypeInfoStaticArray', // rtl. 'NativeUInt' ); @@ -872,7 +877,10 @@ type procedure RenameSubOverloads(Declarations: TFPList); procedure PushOverloadScope(Scope: TPasIdentifierScope); procedure PopOverloadScope; + procedure AddType(El: TPasType); override; procedure ResolveImplAsm(El: TPasImplAsmStatement); override; + procedure ResolveNameExpr(El: TPasExpr; const aName: string; + Access: TResolvedRefAccess); override; procedure FinishModule(CurModule: TPasModule); override; procedure FinishSetType(El: TPasSetType); override; procedure FinishClassType(El: TPasClassType); override; @@ -919,7 +927,6 @@ type function ExtractPasStringLiteral(El: TPasElement; const S: String): TJSString; virtual; function ComputeConst(Expr: TPasExpr; StoreCustomData: boolean): TJSValue; virtual; function ComputeConstString(Expr: TPasExpr; StoreCustomData, NotEmpty: boolean): String; virtual; - function IsExternalBracketAccessor(El: TPasElement): boolean; // CustomData function GetElementData(El: TPasElementBase; DataClass: TPas2JsElementDataClass): TPas2JsElementData; virtual; @@ -930,6 +937,8 @@ type function GetBaseDescription(const R: TPasResolverResult; AddPath: boolean= false): string; override; function HasTypeInfo(El: TPasType): boolean; override; + function IsTObjectFreeMethod(El: TPasExpr): boolean; virtual; + function IsExternalBracketAccessor(El: TPasElement): boolean; end; //------------------------------------------------------------------------------ @@ -963,7 +972,7 @@ type function GetSelfContext: TFunctionContext; function GetContextOfType(aType: TConvertContextClass): TConvertContext; function CreateLocalIdentifier(const Prefix: string): string; - function CurrentModeswitches: TModeSwitches; + function CurrentModeSwitches: TModeSwitches; function GetGlobalFunc: TFunctionContext; procedure WriteStack; procedure DoWriteStack(Index: integer); virtual; @@ -1127,11 +1136,11 @@ type FPreservedWords: TJSReservedWordList; // sorted with CompareStr FTargetPlatform: TPasToJsPlatform; FTargetProcessor: TPasToJsProcessor; - Function CreateBuiltInIdentifierExpr(AName: string): TJSPrimaryExpressionIdent; + Function CreatePrimitiveDotExpr(AName: string; Src: TPasElement = nil): TJSElement; Function CreateSubDeclNameExpr(El: TPasElement; const Name: string; - AContext: TConvertContext): TJSPrimaryExpressionIdent; - Function CreateIdentifierExpr(El: TPasElement; AContext: TConvertContext): TJSPrimaryExpressionIdent; - Function CreateIdentifierExpr(AName: string; El: TPasElement; AContext: TConvertContext): TJSPrimaryExpressionIdent; + AContext: TConvertContext): TJSElement; + Function CreateIdentifierExpr(El: TPasElement; AContext: TConvertContext): TJSElement; + Function CreateIdentifierExpr(AName: string; El: TPasElement; AContext: TConvertContext): TJSElement; Function CreateSwitchStatement(El: TPasImplCaseOf; AContext: TConvertContext): TJSElement; Function CreateTypeDecl(El: TPasType; AContext: TConvertContext): TJSElement; Function CreateVarDecl(El: TPasVariable; AContext: TConvertContext): TJSElement; @@ -1167,6 +1176,7 @@ type Function IsPreservedWord(const aName: string): boolean; virtual; // Never create an element manually, always use the below functions Function IsElementUsed(El: TPasElement): boolean; virtual; + Function IsSystemUnit(aModule: TPasModule): 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; @@ -1201,6 +1211,7 @@ type Function CreateLiteralNull(El: TPasElement): TJSLiteral; virtual; Function CreateLiteralUndefined(El: TPasElement): TJSLiteral; virtual; Function CreateSetLiteralElement(Expr: TPasExpr; AContext: TConvertContext): TJSElement; virtual; + Function ClonePrimaryExpression(El: TJSPrimaryExpression; Src: TPasElement): TJSPrimaryExpression; Function CreateRecordInit(aRecord: TPasRecordType; Expr: TPasElement; El: TPasElement; AContext: TConvertContext): TJSElement; virtual; Function CreateArrayInit(ArrayType: TPasArrayType; Expr: TPasElement; @@ -1210,7 +1221,7 @@ type 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; + Full: boolean = false; Ref: TResolvedReference = nil): TJSElement; 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; @@ -1256,9 +1267,9 @@ type Function ConvertParamsExpression(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual; Function ConvertArrayParams(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual; Function ConvertFuncParams(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual; - Function ConvertExternalConstructor(Left: TPasElement; - Ref: TResolvedReference; ParamsExpr: TParamsExpr; - AContext : TConvertContext): TJSElement; virtual; + Function ConvertExternalConstructor(Left: TPasElement; Ref: TResolvedReference; + ParamsExpr: TParamsExpr; AContext: TConvertContext): TJSElement; virtual; + Function ConvertTObjectFree(Bin: TBinaryExpr; NameExpr: TPasExpr; AContext: TConvertContext): 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; @@ -1804,6 +1815,13 @@ begin FOverloadScopes.Delete(FOverloadScopes.Count-1); end; +procedure TPas2JSResolver.AddType(El: TPasType); +begin + inherited AddType(El); + if TopScope is TPasClassScope then + RaiseNotYetImplemented(20170608232534,El,'nested types'); +end; + procedure TPas2JSResolver.ResolveImplAsm(El: TPasImplAsmStatement); {type TAsmToken = ( @@ -1826,6 +1844,78 @@ begin if Lines=nil then exit; end; +procedure TPas2JSResolver.ResolveNameExpr(El: TPasExpr; const aName: string; + Access: TResolvedRefAccess); + + procedure CheckTObjectFree(Ref: TResolvedReference); + var + Bin: TBinaryExpr; + Left: TPasExpr; + LeftResolved: TPasResolverResult; + IdentEl: TPasElement; + begin + if not IsTObjectFreeMethod(El) then exit; + if Ref.WithExprScope<>nil then + begin + // with expr do free + if GetNewInstanceExpr(Ref.WithExprScope.Expr)<>nil then + exit; // with TSomeClass.Free do Free -> ok + RaiseMsg(20170517092407,nFreeNeedsVar,sFreeNeedsVar,[],El); + end; + if (El.Parent.ClassType<>TBinaryExpr) then + RaiseMsg(20170516151916,nFreeNeedsVar,sFreeNeedsVar,[],El); + Bin:=TBinaryExpr(El.Parent); + if (Bin.right<>El) or (Bin.OpCode<>eopSubIdent) then + RaiseMsg(20170516151950,nFreeNeedsVar,sFreeNeedsVar,[],El); + if rrfImplicitCallWithoutParams in Ref.Flags then + // ".Free;" -> ok + else if Bin.Parent is TParamsExpr then + begin + if Bin.Parent.Parent is TPasExpr then + RaiseMsg(20170516161345,nFreeNeedsVar,sFreeNeedsVar,[],El); + // ".Free();" -> ok + end + else if Bin.Parent is TPasImplElement then + // ok + else + begin + {$IFDEF VerbosePas2JS} + writeln('TPas2JSResolver.ResolveNameExpr.CheckTObjectFree Bin.Parent=',GetObjName(Bin.Parent)); + {$ENDIF} + RaiseMsg(20170516160347,nFreeNeedsVar,sFreeNeedsVar,[],El); + end; + + Left:=Bin.left; + ComputeElement(Left,LeftResolved,[]); + if not (rrfReadable in LeftResolved.Flags) then + RaiseMsg(20170516152300,nFreeNeedsVar,sFreeNeedsVar,[],El); + if not (rrfWritable in LeftResolved.Flags) then + RaiseMsg(20170516152307,nFreeNeedsVar,sFreeNeedsVar,[],El); + IdentEl:=LeftResolved.IdentEl; + if IdentEl=nil then + RaiseMsg(20170516152401,nFreeNeedsVar,sFreeNeedsVar,[],El); + if IdentEl.ClassType=TPasArgument then + exit; // readable and writable argument -> ok + if (IdentEl.ClassType=TPasVariable) + or (IdentEl.ClassType=TPasConst) then + exit; // readable and writable variable -> ok + if IdentEl.ClassType=TPasResultElement then + exit; // readable and writable function result -> ok + RaiseMsg(20170516152455,nFreeNeedsVar,sFreeNeedsVar,[],El); + end; + +var + Ref: TResolvedReference; +begin + inherited ResolveNameExpr(El, aName, Access); + if El.CustomData is TResolvedReference then + begin + Ref:=TResolvedReference(El.CustomData); + if (CompareText(aName,'free')=0) then + CheckTObjectFree(Ref); + end; +end; + procedure TPas2JSResolver.FinishModule(CurModule: TPasModule); var ModuleClass: TClass; @@ -2928,16 +3018,6 @@ begin Result:=String(V.AsString); end; -function TPas2JSResolver.IsExternalBracketAccessor(El: TPasElement): boolean; -var - ExtName: String; -begin - if (not (El is TPasProcedure)) or (TPasProcedure(El).LibrarySymbolName=nil) then - exit(false); - ExtName:=ComputeConstString(TPasProcedure(El).LibrarySymbolName,false,false); - Result:=ExtName=ExtClassBracketAccessor; -end; - function TPas2JSResolver.GetElementData(El: TPasElementBase; DataClass: TPas2JsElementDataClass): TPas2JsElementData; begin @@ -2992,6 +3072,37 @@ begin Result:=false; end; +function TPas2JSResolver.IsTObjectFreeMethod(El: TPasExpr): boolean; +var + Ref: TResolvedReference; + Decl: TPasElement; +begin + Result:=false; + if El=nil then exit; + if El.ClassType<>TPrimitiveExpr then exit; + if not (El.CustomData is TResolvedReference) then exit; + Ref:=TResolvedReference(El.CustomData); + if CompareText(TPrimitiveExpr(El).Value,'free')<>0 then exit; + Decl:=Ref.Declaration; + if not (Decl.ClassType=TPasProcedure) + or (Decl.Parent.ClassType<>TPasClassType) + or (CompareText(Decl.Parent.Name,'tobject')<>0) + or (pmExternal in TPasProcedure(Decl).Modifiers) + or (TPasProcedure(Decl).ProcType.Args.Count>0) then + exit; + Result:=true; +end; + +function TPas2JSResolver.IsExternalBracketAccessor(El: TPasElement): boolean; +var + ExtName: String; +begin + if (not (El is TPasProcedure)) or (TPasProcedure(El).LibrarySymbolName=nil) then + exit(false); + ExtName:=ComputeConstString(TPasProcedure(El).LibrarySymbolName,false,false); + Result:=ExtName=ExtClassBracketAccessor; +end; + { TP2JConstExprData } destructor TP2JConstExprData.Destroy; @@ -3260,7 +3371,7 @@ begin Result:=Prefix+IntToStr(TmpVarCount); end; -function TConvertContext.CurrentModeswitches: TModeSwitches; +function TConvertContext.CurrentModeSwitches: TModeSwitches; begin if Resolver=nil then Result:=OBJFPCModeSwitches @@ -3432,8 +3543,8 @@ Var ModuleName, ModVarName: String; IntfContext: TSectionContext; ImplVarSt: TJSVariableStatement; - HasImplUsesList: Boolean; - UsesList: TFPList; + HasImplUsesClause: Boolean; + UsesClause: TPasUsesClause; begin Result:=Nil; OuterSrc:=TJSSourceElements(CreateElement(TJSSourceElements, El)); @@ -3469,7 +3580,7 @@ begin AddToSourceElements(Src,CreateLiteralString(El,'use strict')); ImplVarSt:=nil; - HasImplUsesList:=false; + HasImplUsesClause:=false; IntfContext:=TSectionContext.Create(El,Src,AContext); try @@ -3478,7 +3589,7 @@ begin ModVarName:=FBuiltInNames[pbivnModule]; IntfContext.AddLocalVar(ModVarName,El); AddToSourceElements(Src,CreateVarStatement(ModVarName, - CreateBuiltInIdentifierExpr('this'),El)); + CreatePrimitiveDotExpr('this'),El)); if (El is TPasProgram) then begin // program @@ -3510,11 +3621,11 @@ begin // add optional implementation uses list: [<implementation uses1>,<uses2>, ...] if Assigned(El.ImplementationSection) then begin - UsesList:=El.ImplementationSection.UsesList; - if (UsesList<>nil) and (UsesList.Count>0) then + UsesClause:=El.ImplementationSection.UsesClause; + if length(UsesClause)>0 then begin ArgArray.Elements.AddElement.Expr:=CreateUsesList(El.ImplementationSection,AContext); - HasImplUsesList:=true; + HasImplUsesClause:=true; end; end; @@ -3535,7 +3646,7 @@ begin else begin // add param - if not HasImplUsesList then + if not HasImplUsesClause then ArgArray.Elements.AddElement.Expr:=CreateLiteralNull(El); ArgArray.Elements.AddElement.Expr:=ImplFunc; end; @@ -3593,7 +3704,7 @@ begin else FunName:=FBuiltInNames[pbifnClassInstanceFree]; FunName:=CreateReferencePath(Proc,AContext,rpkPathWithDot,false,Ref)+FunName; - C.Expr:=CreateBuiltInIdentifierExpr(FunName); + C.Expr:=CreatePrimitiveDotExpr(FunName); ArgElems:=C.Args.Elements; // parameter: "funcname" ArgEx := CreateLiteralString(Ref.Element,TransformVariableName(Proc,AContext)); @@ -3934,7 +4045,7 @@ begin if AContext.Resolver<>nil then begin - ModeSwitches:=AContext.CurrentModeswitches; + ModeSwitches:=AContext.CurrentModeSwitches; // compute left Flags:=[]; if El.OpCode in [eopEqual,eopNotEqual] then @@ -3972,10 +4083,10 @@ begin Call:=CreateCallExpression(El); if (RightResolved.TypeEl is TPasClassType) and TPasClassType(RightResolved.TypeEl).IsExternal then // B is external class -> "rtl.asExt(A,B)" - Call.Expr:=CreateBuiltInIdentifierExpr(FBuiltInNames[pbivnRTL]+'.'+FBuiltInNames[pbifnAsExt]) + Call.Expr:=CreatePrimitiveDotExpr(FBuiltInNames[pbivnRTL]+'.'+FBuiltInNames[pbifnAsExt]) else // otherwise -> "rtl.as(A,B)" - Call.Expr:=CreateBuiltInIdentifierExpr(FBuiltInNames[pbivnRTL]+'.'+FBuiltInNames[pbifnAs]); + Call.Expr:=CreatePrimitiveDotExpr(FBuiltInNames[pbivnRTL]+'.'+FBuiltInNames[pbifnAs]); Call.AddArg(A); Call.AddArg(B); Result:=Call; @@ -4008,7 +4119,7 @@ begin eopPower: begin Call:=CreateCallExpression(El); - Call.Expr:=CreateBuiltInIdentifierExpr('Math.pow'); + Call.Expr:=CreatePrimitiveDotExpr('Math.pow'); Call.AddArg(A); Call.AddArg(B); Result:=Call; @@ -4029,7 +4140,7 @@ begin // convert "a div b" to "Math.floor(a/b)" Call:=CreateCallExpression(El); Call.AddArg(R); - Call.Expr:=CreateBuiltInIdentifierExpr('Math.floor'); + Call.Expr:=CreatePrimitiveDotExpr('Math.floor'); Result:=Call; end; end; @@ -4173,7 +4284,7 @@ begin begin // convert "recordA = recordB" to "recordA.$equal(recordB)" Call:=CreateCallExpression(El); - Call.Expr:=CreateDotExpression(El,A,CreateBuiltInIdentifierExpr(FBuiltInNames[pbifnRecordEqual])); + Call.Expr:=CreateDotExpression(El,A,CreatePrimitiveDotExpr(FBuiltInNames[pbifnRecordEqual])); A:=nil; Call.AddArg(B); B:=nil; @@ -4227,7 +4338,7 @@ var begin Result:=nil; - ParamsExpr:=nil;; + ParamsExpr:=nil; RightEl:=El.right; while RightEl.ClassType=TParamsExpr do begin @@ -4249,6 +4360,11 @@ begin else Result:=ConvertExternalConstructor(El.left,RightRef,nil,AContext); exit; + end + else if AContext.Resolver.IsTObjectFreeMethod(RightEl) then + begin + Result:=ConvertTObjectFree(El,RightEl,AContext); + exit; end; end; @@ -4286,28 +4402,19 @@ begin end; function TPasToJSConverter.CreateIdentifierExpr(El: TPasElement; - AContext: TConvertContext): TJSPrimaryExpressionIdent; -var - I: TJSPrimaryExpressionIdent; + AContext: TConvertContext): TJSElement; begin - I:=TJSPrimaryExpressionIdent(CreateElement(TJSPrimaryExpressionIdent,El)); - I.Name:=TJSString(TransformVariableName(El,AContext)); - Result:=I; + Result:=CreatePrimitiveDotExpr(TransformVariableName(El,AContext),El); end; function TPasToJSConverter.CreateIdentifierExpr(AName: string; El: TPasElement; - AContext: TConvertContext): TJSPrimaryExpressionIdent; -Var - I : TJSPrimaryExpressionIdent; + AContext: TConvertContext): TJSElement; begin - I:=TJSPrimaryExpressionIdent(CreateElement(TJSPrimaryExpressionIdent,El)); - AName:=TransformVariableName(El,AName,AContext); - I.Name:=TJSString(AName); - Result:=I; + Result:=CreatePrimitiveDotExpr(TransformVariableName(El,AName,AContext),El); end; function TPasToJSConverter.CreateSubDeclNameExpr(El: TPasElement; - const Name: string; AContext: TConvertContext): TJSPrimaryExpressionIdent; + const Name: string; AContext: TConvertContext): TJSElement; var CurName, ParentName: String; begin @@ -4316,8 +4423,7 @@ begin if ParentName='' then ParentName:='this'; CurName:=ParentName+'.'+CurName; - Result:=TJSPrimaryExpressionIdent(CreateElement(TJSPrimaryExpressionIdent,El)); - Result.Name:=TJSString(CurName); + Result:=CreatePrimitiveDotExpr(CurName,El); end; function TPasToJSConverter.ConvertPrimitiveExpression(El: TPrimitiveExpr; @@ -4357,8 +4463,7 @@ begin if ConversionError<>0 then DoError(20161024191248,nInvalidNumber,sInvalidNumber,[El.Value],El); L:=CreateLiteralNumber(El,Number); - if El.Value[1] in ['0'..'9'] then - L.Value.CustomValue:=TJSString(El.Value); + L.Value.CustomValue:=TJSString(El.Value); end; '$','&','%': begin @@ -4448,6 +4553,12 @@ begin exit; end; + if (Ref.WithExprScope<>nil) and AContext.Resolver.IsTObjectFreeMethod(El) then + begin + Result:=ConvertTObjectFree(nil,El,AContext); + exit; + end; + Prop:=nil; AssignContext:=nil; ImplicitCall:=rrfImplicitCallWithoutParams in Ref.Flags; @@ -4500,7 +4611,7 @@ begin Call:=CreateCallExpression(El); Call.Expr:=CreateDotExpression(El, CreateIdentifierExpr(Arg.Name,Arg,AContext), - CreateBuiltInIdentifierExpr(TempRefObjGetterName)); + CreatePrimitiveDotExpr(TempRefObjGetterName)); Result:=Call; exit; end; @@ -4514,7 +4625,7 @@ begin AssignContext.Call:=Call; Call.Expr:=CreateDotExpression(El, CreateIdentifierExpr(Arg.Name,Arg,AContext), - CreateBuiltInIdentifierExpr(TempRefObjSetterName)); + CreatePrimitiveDotExpr(TempRefObjSetterName)); Call.AddArg(AssignContext.RightSide); AssignContext.RightSide:=nil; Result:=Call; @@ -4584,7 +4695,7 @@ begin else Name:=CreateReferencePath(Decl,AContext,rpkPathAndName,false,Ref); if Result=nil then - Result:=CreateBuiltInIdentifierExpr(Name); + Result:=CreatePrimitiveDotExpr(Name); if ImplicitCall then begin @@ -4678,11 +4789,11 @@ function TPasToJSConverter.ConvertInheritedExpression(El: TInheritedExpr; Call:=nil; try Call:=CreateCallExpression(ParentEl); - Call.Expr:=CreateBuiltInIdentifierExpr(FunName); - Call.AddArg(CreateBuiltInIdentifierExpr(SelfName)); + Call.Expr:=CreatePrimitiveDotExpr(FunName); + Call.AddArg(CreatePrimitiveDotExpr(SelfName)); if Apply then // "inherited;" -> pass the arguments - Call.AddArg(CreateBuiltInIdentifierExpr('arguments')) + Call.AddArg(CreatePrimitiveDotExpr('arguments')) else // "inherited Name(...)" -> pass the user arguments CreateProcedureCall(Call,ParamsExpr,AncestorProc.ProcType,AContext); @@ -4996,7 +5107,7 @@ var Ref:=TResolvedReference(PathEl.CustomData); Path:=CreateReferencePath(Prop,AContext,rpkPath,false,Ref); if Path<>'' then - Bracket.MExpr:=CreateBuiltInIdentifierExpr(Path); + Bracket.MExpr:=CreatePrimitiveDotExpr(Path); PathEl:=nil; end else if (PathEl is TBinaryExpr) @@ -5348,6 +5459,7 @@ begin TargetProcType:=TPasProcedure(Decl).ProcType else if (C=TPasClassType) or (C=TPasClassOfType) + or (C=TPasRecordType) or (C=TPasEnumType) or (C=TPasArrayType) then begin @@ -5364,7 +5476,8 @@ begin if JSBaseType=pbtJSValue then begin if (C=TPasClassType) - or (C=TPasClassOfType) then + or (C=TPasClassOfType) + or (C=TPasRecordType) then begin // TObject(jsvalue) -> rtl.getObject(jsvalue) Call:=CreateCallExpression(El); @@ -5512,7 +5625,7 @@ begin else // use external class name ExtName:=(Proc.Parent as TPasClassType).ExternalName; - ExtNameEl:=CreateBuiltInIdentifierExpr(ExtName); + ExtNameEl:=CreatePrimitiveDotExpr(ExtName); end; if CompareText(Proc.Name,'new')=0 then @@ -5536,6 +5649,112 @@ begin end; end; +function TPasToJSConverter.ConvertTObjectFree(Bin: TBinaryExpr; + NameExpr: TPasExpr; AContext: TConvertContext): TJSElement; + + function CreateCallRTLFree(Obj, Prop: TJSElement): TJSElement; + // create "rtl.free(obj,prop)" + var + Call: TJSCallExpression; + begin + Call:=CreateCallExpression(Bin.right); + Call.Expr:=CreateMemberExpression([GetBuildInNames(pbivnRTL),GetBuildInNames(pbifnFreeVar)]); + Call.Args.AddElement(Obj); + Call.Args.AddElement(Prop); + Result:=Call; + end; + + function CreateCallRTLFreeLoc(Setter, Getter: TJSElement; Src: TPasElement): TJSElement; + // create "Setter=rtl.freeLoc(Getter)" + var + Call: TJSCallExpression; + AssignSt: TJSSimpleAssignStatement; + begin + Call:=CreateCallExpression(Src); + Call.Expr:=CreateMemberExpression([GetBuildInNames(pbivnRTL),GetBuildInNames(pbifnFreeLocalVar)]); + Call.Args.AddElement(Getter); + AssignSt:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,Src)); + AssignSt.LHS:=Setter; + AssignSt.Expr:=Call; + Result:=AssignSt; + end; + +var + LeftJS, Obj, Prop, Getter, Setter: TJSElement; + DotExpr: TJSDotMemberExpression; + BracketJS: TJSBracketMemberExpression; + aName: TJSString; + WithExprScope: TPas2JSWithExprScope; +begin + Result:=nil; + + LeftJS:=nil; + try + WithExprScope:=TResolvedReference(NameExpr.CustomData).WithExprScope as TPas2JSWithExprScope; + if WithExprScope<>nil then + begin + if AContext.Resolver.GetNewInstanceExpr(WithExprScope.Expr)<>nil then + begin + // "with TSomeClass.Create do Free" + // -> "$with1=rtl.freeLoc($with1); + Getter:=CreatePrimitiveDotExpr(WithExprScope.WithVarName,WithExprScope.Expr); + Setter:=CreatePrimitiveDotExpr(WithExprScope.WithVarName,WithExprScope.Expr); + Result:=CreateCallRTLFreeLoc(Setter,Getter,NameExpr); + exit; + end; + {$IFDEF VerbosePas2JS} + writeln('TPasToJSConverter.ConvertTObjectFree With=',GetObjName(WithExprScope.Expr)); + {$ENDIF} + RaiseInconsistency(20170517092248); + end; + + LeftJS:=ConvertElement(Bin.left,AContext); + {$IFDEF VerbosePas2JS} + writeln('TPasToJSConverter.ConvertTObjectFree ',GetObjName(LeftJS)); + {$ENDIF} + + if LeftJS is TJSPrimaryExpressionIdent then + begin + aName:=TJSPrimaryExpressionIdent(LeftJS).Name; + if Pos('.',aName)>0 then + RaiseInconsistency(20170516173832); + // v.free + // -> v=rtl.freeLoc(v); + Getter:=LeftJS; + Setter:=ClonePrimaryExpression(TJSPrimaryExpressionIdent(LeftJS),Bin.left); + Result:=CreateCallRTLFreeLoc(Setter,Getter,NameExpr); + end + else if LeftJS is TJSDotMemberExpression then + begin + // obj.prop.free + // -> rtl.free(obj,"prop"); + DotExpr:=TJSDotMemberExpression(LeftJS); + Obj:=DotExpr.MExpr; + DotExpr.MExpr:=nil; + Prop:=CreateLiteralJSString(Bin.right,DotExpr.Name); + FreeAndNil(LeftJS); + Result:=CreateCallRTLFree(Obj,Prop); + end + else if LeftJS is TJSBracketMemberExpression then + begin + // obj[prop].free + // -> rtl.free(obj,prop); + BracketJS:=TJSBracketMemberExpression(LeftJS); + Obj:=BracketJS.MExpr; + BracketJS.MExpr:=nil; + Prop:=BracketJS.Name; + BracketJS.Name:=nil; + FreeAndNil(LeftJS); + Result:=CreateCallRTLFree(Obj,Prop); + end + else + RaiseNotSupported(Bin.left,AContext,20170516164659,'invalid scope for Free'); + finally + if Result=nil then + LeftJS.Free; + end; +end; + function TPasToJSConverter.ConvertTypeCastToBaseType(El: TParamsExpr; AContext: TConvertContext; ToBaseTypeData: TResElDataBaseType): TJSElement; var @@ -5895,7 +6114,7 @@ begin // default: Param.length Arg:=ConvertElement(Param,AContext); - Result:=CreateDotExpression(El,Arg,CreateBuiltInIdentifierExpr('length')); + Result:=CreateDotExpression(El,Arg,CreatePrimitiveDotExpr('length')); end; function TPasToJSConverter.ConvertBuiltIn_SetLength(El: TParamsExpr; @@ -6053,7 +6272,7 @@ begin ProcEl:=ProcEl.Parent; if ProcEl is TPasFunction then // in a function, "return result;" - TJSReturnStatement(Result).Expr:=CreateBuiltInIdentifierExpr(ResolverResultVar) + TJSReturnStatement(Result).Expr:=CreatePrimitiveDotExpr(ResolverResultVar) else ; // in a procedure, "return;" which means "return undefined;" end; @@ -6109,7 +6328,7 @@ begin // create "ref.set" Call.Expr:=CreateDotExpression(El, CreateIdentifierExpr(ExprResolved.IdentEl,AContext), - CreateBuiltInIdentifierExpr(TempRefObjSetterName)); + CreatePrimitiveDotExpr(TempRefObjSetterName)); // create "+" if IsInc then AddJS:=TJSAdditiveExpressionPlus(CreateElement(TJSAdditiveExpressionPlus,El)) @@ -6120,7 +6339,7 @@ begin AddJS.A:=TJSCallExpression(CreateElement(TJSCallExpression,El)); TJSCallExpression(AddJS.A).Expr:=CreateDotExpression(El, CreateIdentifierExpr(ExprResolved.IdentEl,AContext), - CreateBuiltInIdentifierExpr(TempRefObjGetterName)); + CreatePrimitiveDotExpr(TempRefObjGetterName)); // add "b" AddJS.B:=ValueJS; ValueJS:=nil; @@ -6274,7 +6493,7 @@ begin Call:=nil; try Call:=CreateCallExpression(El); - Call.Expr:=CreateDotExpression(El,SubParamJS,CreateBuiltInIdentifierExpr('charCodeAt')); + Call.Expr:=CreateDotExpression(El,SubParamJS,CreatePrimitiveDotExpr('charCodeAt')); Minus:=TJSAdditiveExpressionMinus(CreateElement(TJSAdditiveExpressionMinus,Param)); Call.AddArg(Minus); if length(SubParams.Params)<>1 then @@ -6294,7 +6513,7 @@ begin Result:=ConvertElement(Param,AContext); // Note: convert Param first, as it might raise an exception Call:=CreateCallExpression(El); - Call.Expr:=CreateDotExpression(El,Result,CreateBuiltInIdentifierExpr('charCodeAt')); + Call.Expr:=CreateDotExpression(El,Result,CreatePrimitiveDotExpr('charCodeAt')); Result:=Call; exit; end @@ -6684,7 +6903,7 @@ begin // precision -> rtl El.toFixed(precision); NeedStrLit:=false; Call:=CreateCallExpression(El); - Call.Expr:=CreateDotExpression(El,Add,CreateBuiltInIdentifierExpr('toFixed')); + Call.Expr:=CreateDotExpression(El,Add,CreatePrimitiveDotExpr('toFixed')); Call.AddArg(ConvertElement(El.format2,AContext)); Add:=Call; Call:=nil; @@ -6790,7 +7009,7 @@ begin if Call.Expr=nil then // default: array1.concat(array2,...) Call.Expr:=CreateDotExpression(El,ConvertElement(Param0,AContext), - CreateBuiltInIdentifierExpr('concat')); + CreatePrimitiveDotExpr('concat')); for i:=1 to length(El.Params)-1 do Call.AddArg(ConvertElement(El.Params[i],AContext)); Result:=Call; @@ -6872,7 +7091,7 @@ begin try Call:=CreateCallExpression(El); ArrEl:=ConvertElement(El.Params[1],AContext); - Call.Expr:=CreateDotExpression(El,ArrEl,CreateBuiltInIdentifierExpr('splice')); + Call.Expr:=CreateDotExpression(El,ArrEl,CreatePrimitiveDotExpr('splice')); Call.AddArg(ConvertElement(El.Params[2],AContext)); Call.AddArg(CreateLiteralNumber(El,1)); Call.AddArg(ConvertElement(El.Params[0],AContext)); @@ -6896,7 +7115,7 @@ begin try Call:=CreateCallExpression(El); ArrEl:=ConvertElement(El.Params[0],AContext); - Call.Expr:=CreateDotExpression(El,ArrEl,CreateBuiltInIdentifierExpr('splice')); + Call.Expr:=CreateDotExpression(El,ArrEl,CreatePrimitiveDotExpr('splice')); Call.AddArg(ConvertElement(El.Params[1],AContext)); Call.AddArg(ConvertElement(El.Params[2],AContext)); Result:=Call; @@ -6946,7 +7165,7 @@ begin // typeinfo(classinstance) -> classinstance.$rtti // typeinfo(classof) -> classof.$rtti Result:=ConvertElement(Param,AContext); - Result:=CreateDotExpression(El,Result,CreateBuiltInIdentifierExpr(FBuiltInNames[pbivnRTTI])); + Result:=CreateDotExpression(El,Result,CreatePrimitiveDotExpr(FBuiltInNames[pbivnRTTI])); end else Result:=CreateTypeInfoRef(TypeEl,AContext,Param); @@ -7022,17 +7241,35 @@ begin RaiseNotSupported(El,AContext,20161024191314); end; -function TPasToJSConverter.CreateBuiltInIdentifierExpr(AName: string - ): TJSPrimaryExpressionIdent; +function TPasToJSConverter.CreatePrimitiveDotExpr(AName: string; + Src: TPasElement): TJSElement; var + p: Integer; + DotExpr: TJSDotMemberExpression; Ident: TJSPrimaryExpressionIdent; begin if AName='' then RaiseInconsistency(20170402230134); - Ident:=TJSPrimaryExpressionIdent.Create(0,0); - // do not lowercase - Ident.Name:=TJSString(AName); - Result:=Ident; + p:=PosLast('.',AName); + if p>0 then + begin + if Src<>nil then + DotExpr:=TJSDotMemberExpression(CreateElement(TJSDotMemberExpression,Src)) + else + DotExpr:=TJSDotMemberExpression.Create(0,0); + DotExpr.Name:=TJSString(copy(AName,p+1,length(AName))); // do not lowercase + DotExpr.MExpr:=CreatePrimitiveDotExpr(LeftStr(AName,p-1)); + Result:=DotExpr; + end + else + begin + if Src<>nil then + Ident:=TJSPrimaryExpressionIdent(CreateElement(TJSPrimaryExpressionIdent,Src)) + else + Ident:=TJSPrimaryExpressionIdent.Create(0,0); + Ident.Name:=TJSString(AName); // do not lowercase + Result:=Ident; + end; end; function TPasToJSConverter.CreateTypeDecl(El: TPasType; @@ -7233,7 +7470,7 @@ Var RetSt: TJSReturnStatement; begin RetSt:=TJSReturnStatement(CreateElement(TJSReturnStatement,El)); - RetSt.Expr:=CreateBuiltInIdentifierExpr(ResolverResultVar); + RetSt.Expr:=CreatePrimitiveDotExpr(ResolverResultVar); Add(RetSt); end; @@ -7351,8 +7588,8 @@ var exit; Call:=CreateCallExpression(El); AncestorPath:=CreateReferencePath(Ancestor,ClassContext,rpkPathAndName); - Call.Expr:=CreateBuiltInIdentifierExpr(AncestorPath+'.'+MemberFuncName[Kind]+'.call'); - Call.AddArg(CreateBuiltInIdentifierExpr('this')); + Call.Expr:=CreatePrimitiveDotExpr(AncestorPath+'.'+MemberFuncName[Kind]+'.call'); + Call.AddArg(CreatePrimitiveDotExpr('this')); AddToSourceElements(Src,Call); end; @@ -7500,8 +7737,9 @@ var P: TPasElement; Scope: TPas2JSClassScope; Ancestor: TPasType; - AncestorPath, OwnerName: String; + AncestorPath, OwnerName, DestructorName: String; C: TClass; + AssignSt: TJSSimpleAssignStatement; begin Result:=nil; if El.IsForward then @@ -7541,7 +7779,7 @@ begin OwnerName:=AContext.GetLocalName(El.GetModule); if OwnerName='' then OwnerName:='this'; - Call.AddArg(CreateBuiltInIdentifierExpr(OwnerName)); + Call.AddArg(CreatePrimitiveDotExpr(OwnerName)); // add parameter: string constant '"classname"' ArgEx := CreateLiteralString(El,TransformVariableName(El,AContext)); @@ -7554,7 +7792,7 @@ begin AncestorPath:=TPasClassType(Ancestor).ExternalName else AncestorPath:=CreateReferencePath(Ancestor,AContext,rpkPathAndName); - Call.AddArg(CreateBuiltInIdentifierExpr(AncestorPath)); + Call.AddArg(CreatePrimitiveDotExpr(AncestorPath)); if AncestorIsExternal then begin @@ -7623,7 +7861,21 @@ begin //writeln('TPasToJSConverter.ConvertClassType methods El[',i,']=',GetObjName(P)); if not IsMemberNeeded(P) then continue; if P is TPasProcedure then - NewEl:=ConvertProcedure(TPasProcedure(P),FuncContext) + begin + if IsTObject and (P.ClassType=TPasDestructor) then + begin + DestructorName:=TransformVariableName(P,AContext); + if DestructorName<>'Destroy' then + begin + // add 'rtl.tObjectDestroy="destroy";' + AssignSt:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,P)); + AssignSt.LHS:=CreateMemberExpression([GetBuildInNames(pbivnRTL),GetBuildInNames(pbivnTObjectDestroy)]); + AssignSt.Expr:=CreateLiteralString(P,DestructorName); + AddToSourceElements(Src,AssignSt); + end; + end; + NewEl:=ConvertProcedure(TPasProcedure(P),FuncContext); + end else continue; if NewEl=nil then @@ -8221,7 +8473,7 @@ begin // has nested procs -> add "var self = this;" FuncContext.AddLocalVar(FBuiltInNames[pbivnSelf],FuncContext.ThisPas); SelfSt:=CreateVarStatement(FBuiltInNames[pbivnSelf], - CreateBuiltInIdentifierExpr('this'),El); + CreatePrimitiveDotExpr('this'),El); AddBodyStatement(SelfSt,BodyPas); if ImplProcScope.SelfArg<>nil then begin @@ -8397,7 +8649,7 @@ begin // default else: throw exceptobject Last.BFalse:=TJSThrowStatement(CreateElement(TJSThrowStatement,El)); TJSThrowStatement(Last.BFalse).A:= - CreateBuiltInIdentifierExpr(FBuiltInNames[pbivnExceptObject]); + CreatePrimitiveDotExpr(FBuiltInNames[pbivnExceptObject]); end; end else @@ -8602,7 +8854,7 @@ begin ImplContext.ThisPas:=El; ModVarName:=FBuiltInNames[pbivnModule]; AddToSourceElements(Src,CreateVarStatement(ModVarName, - CreateBuiltInIdentifierExpr('this'),El)); + CreatePrimitiveDotExpr('this'),El)); ImplContext.AddLocalVar(ModVarName,El); // add var $impl = $mod.$impl @@ -8924,7 +9176,7 @@ begin if El is TPasClassType then begin // use this - Result:=CreateBuiltInIdentifierExpr(FBuiltInNames[pbivnRTTILocal]); + Result:=CreatePrimitiveDotExpr(FBuiltInNames[pbivnRTTILocal]); exit; end else @@ -9478,7 +9730,7 @@ begin if El.ExceptObject<>Nil then E:=ConvertElement(El.ExceptObject,AContext) else - E:=CreateBuiltInIdentifierExpr(FBuiltInNames[pbivnExceptObject]); + E:=CreatePrimitiveDotExpr(FBuiltInNames[pbivnExceptObject]); T:=TJSThrowStatement(CreateElement(TJSThrowStatement,El)); T.A:=E; Result:=T; @@ -9506,7 +9758,7 @@ begin LeftIsProcType:=AContext.Resolver.IsProcedureType(AssignContext.LeftResolved,true); if LeftIsProcType then begin - if msDelphi in AContext.CurrentModeswitches then + if msDelphi in AContext.CurrentModeSwitches then Include(Flags,rcNoImplicitProc) else Include(Flags,rcNoImplicitProcType); @@ -9515,7 +9767,7 @@ begin {$IFDEF VerbosePas2JS} writeln('TPasToJSConverter.ConvertAssignStatement Left={',GetResolverResultDbg(AssignContext.LeftResolved),'} Right={',GetResolverResultDbg(AssignContext.RightResolved),'}'); {$ENDIF} - if LeftIsProcType and (msDelphi in AContext.CurrentModeswitches) + if LeftIsProcType and (msDelphi in AContext.CurrentModeSwitches) and (AssignContext.RightResolved.BaseType=btProc) then begin // Delphi allows assigning a proc without @: proctype:=proc @@ -9849,13 +10101,21 @@ function TPasToJSConverter.ConvertSimpleStatement(El: TPasImplSimple; Var E : TJSElement; + C: TClass; begin E:=ConvertElement(EL.Expr,AContext); if E=nil then exit(nil); // e.g. "inherited;" without ancestor proc - Result:=TJSExpressionStatement(CreateElement(TJSExpressionStatement,El)); - TJSExpressionStatement(Result).A:=E; + C:=E.ClassType; + if (C=TJSExpressionStatement) + or (C=TJSStatementList) then + Result:=E + else + begin + Result:=TJSExpressionStatement(CreateElement(TJSExpressionStatement,El)); + TJSExpressionStatement(Result).A:=E; + end; end; function TPasToJSConverter.ConvertWithStatement(El: TPasImplWithDo; @@ -9894,12 +10154,28 @@ begin PasExpr:=TPasElement(El.Expressions[i]); Expr:=ConvertElement(PasExpr,AContext); - // create unique local var name WithExprScope:=WithScope.ExpressionScopes[i] as TPas2JSWithExprScope; - WithExprScope.WithVarName:=FuncContext.CreateLocalIdentifier(FBuiltInNames[pbivnWith]); - // create local "var $with1 = expr;" - V:=CreateVarStatement(WithExprScope.WithVarName,Expr,PasExpr); - AddToStatementList(FirstSt,LastSt,V,PasExpr); + if (Expr is TJSPrimaryExpressionIdent) + and IsValidJSIdentifier(TJSPrimaryExpressionIdent(Expr).Name) then + begin + // expression is already a local variable + WithExprScope.WithVarName:=String(TJSPrimaryExpressionIdent(Expr).Name); + Expr.Free; + end + else if Expr is TJSPrimaryExpressionThis then + begin + // expression is 'this' + WithExprScope.WithVarName:='this'; + Expr.Free; + end + else + begin + // create unique local var name + WithExprScope.WithVarName:=FuncContext.CreateLocalIdentifier(FBuiltInNames[pbivnWith]); + // create local "var $with1 = expr;" + V:=CreateVarStatement(WithExprScope.WithVarName,Expr,PasExpr); + AddToStatementList(FirstSt,LastSt,V,PasExpr); + end; end; if Assigned(El.Body) then begin @@ -9953,6 +10229,11 @@ begin Result:=true; end; +function TPasToJSConverter.IsSystemUnit(aModule: TPasModule): boolean; +begin + Result:=CompareText(aModule.Name,'system')=0; +end; + function TPasToJSConverter.HasTypeInfo(El: TPasType; AContext: TConvertContext ): boolean; begin @@ -10052,25 +10333,23 @@ function TPasToJSConverter.CreateUsesList(UsesSection: TPasSection; AContext: TConvertContext): TJSArrayLiteral; var ArgArray: TJSArrayLiteral; - k: Integer; - El: TPasElement; + i: Integer; anUnitName: String; ArgEx: TJSLiteral; - UsesList: TFPList; + UsesClause: TPasUsesClause; + aModule: TPasModule; begin - UsesList:=UsesSection.UsesList; + UsesClause:=UsesSection.UsesClause; ArgArray:=TJSArrayLiteral.Create(0,0); - if UsesList<>nil then - for k:=0 to UsesList.Count-1 do - begin - El:=TPasElement(UsesList[k]); - if not (El is TPasModule) then continue; - if (not IsElementUsed(El)) and (CompareText('system',El.Name)<>0) then - continue; - anUnitName := TransformVariableName(TPasModule(El),AContext); - ArgEx := CreateLiteralString(UsesSection,anUnitName); - ArgArray.Elements.AddElement.Expr := ArgEx; - end; + for i:=0 to length(UsesClause)-1 do + begin + aModule:=UsesClause[i].Module as TPasModule; + if (not IsElementUsed(aModule)) and not IsSystemUnit(aModule) then + continue; + anUnitName := TransformModuleName(aModule,false,AContext); + ArgEx := CreateLiteralString(UsesSection,anUnitName); + ArgArray.Elements.AddElement.Expr := ArgEx; + end; Result:=ArgArray; end; @@ -10397,7 +10676,7 @@ begin begin // aChar -> aChar.charCodeAt() Call:=TJSCallExpression(CreateElement(TJSCallExpression,Expr)); - Call.Expr:=CreateDotExpression(Expr,Result,CreateBuiltInIdentifierExpr('charCodeAt')); + Call.Expr:=CreateDotExpression(Expr,Result,CreatePrimitiveDotExpr('charCodeAt')); Result:=Call; end else if ExprResolved.BaseType=btContext then @@ -10412,6 +10691,14 @@ begin end; end; +function TPasToJSConverter.ClonePrimaryExpression(El: TJSPrimaryExpression; + Src: TPasElement): TJSPrimaryExpression; +begin + Result:=TJSPrimaryExpression(CreateElement(TJSElementClass(El.ClassType),Src)); + if Result.ClassType=TJSPrimaryExpressionIdent then + TJSPrimaryExpressionIdent(Result).Name:=TJSPrimaryExpressionIdent(El).Name; +end; + function TPasToJSConverter.CreateRecordInit(aRecord: TPasRecordType; Expr: TPasElement; El: TPasElement; AContext: TConvertContext): TJSElement; // new recordtype() @@ -10774,7 +11061,7 @@ end; function TPasToJSConverter.CreateReferencePathExpr(El: TPasElement; AContext: TConvertContext; Full: boolean; Ref: TResolvedReference - ): TJSPrimaryExpressionIdent; + ): TJSElement; var Name: String; begin @@ -10782,7 +11069,7 @@ begin writeln('TPasToJSConverter.CreateReferencePathExpr El="',GetObjName(El),'" El.Parent=',GetObjName(El.Parent)); {$ENDIF} Name:=CreateReferencePath(El,AContext,rpkPathAndName,Full,Ref); - Result:=CreateBuiltInIdentifierExpr(Name); + Result:=CreatePrimitiveDotExpr(Name); end; procedure TPasToJSConverter.CreateProcedureCall(var Call: TJSCallExpression; @@ -11030,12 +11317,12 @@ begin // GetExpr: this.p.readvar // Will create "{p:GetPathExpr, get:function(){return GetExpr;}, // set:function(v){GetExpr = v;}}" - GetPathExpr:=CreateBuiltInIdentifierExpr(LeftStr(GetPath,GetDotPos-1)); - GetExpr:=CreateDotExpression(El,CreateBuiltInIdentifierExpr('this.'+GetPathName), - CreateBuiltInIdentifierExpr(copy(GetPath,GetDotPos+1))); + GetPathExpr:=CreatePrimitiveDotExpr(LeftStr(GetPath,GetDotPos-1)); + GetExpr:=CreateDotExpression(El,CreatePrimitiveDotExpr('this.'+GetPathName), + CreatePrimitiveDotExpr(copy(GetPath,GetDotPos+1))); if ParamContext.Setter=nil then - SetExpr:=CreateDotExpression(El,CreateBuiltInIdentifierExpr('this.'+GetPathName), - CreateBuiltInIdentifierExpr(copy(GetPath,GetDotPos+1))); + SetExpr:=CreateDotExpression(El,CreatePrimitiveDotExpr('this.'+GetPathName), + CreatePrimitiveDotExpr(copy(GetPath,GetDotPos+1))); end else begin @@ -11043,7 +11330,7 @@ begin GetExpr:=FullGetter; FullGetter:=nil; if ParamContext.Setter=nil then - SetExpr:=CreateBuiltInIdentifierExpr(GetPath); + SetExpr:=CreatePrimitiveDotExpr(GetPath); end; if ParamContext.Setter<>nil then @@ -11059,15 +11346,15 @@ begin if LeftStr(GetPath,GetDotPos)=LeftStr(SetPath,SetDotPos) then begin // use GetPathExpr for setter - SetExpr:=CreateDotExpression(El,CreateBuiltInIdentifierExpr('this.'+GetPathName), - CreateBuiltInIdentifierExpr(copy(SetPath,GetDotPos+1))); + SetExpr:=CreateDotExpression(El,CreatePrimitiveDotExpr('this.'+GetPathName), + CreatePrimitiveDotExpr(copy(SetPath,GetDotPos+1))); end else begin // setter needs its own SetPathExpr - SetPathExpr:=CreateBuiltInIdentifierExpr(LeftStr(SetPath,SetDotPos-1)); - SetExpr:=CreateDotExpression(El,CreateBuiltInIdentifierExpr('this.'+SetPathName), - CreateBuiltInIdentifierExpr(copy(SetPath,GetDotPos+1))); + SetPathExpr:=CreatePrimitiveDotExpr(LeftStr(SetPath,SetDotPos-1)); + SetExpr:=CreateDotExpression(El,CreatePrimitiveDotExpr('this.'+SetPathName), + CreatePrimitiveDotExpr(copy(SetPath,GetDotPos+1))); end; end; end; @@ -11086,12 +11373,12 @@ begin // SetExpr: this.p.i DotExpr:=TJSDotMemberExpression(FullGetter); GetPathExpr:=DotExpr.MExpr; - DotExpr.MExpr:=CreateBuiltInIdentifierExpr('this.'+GetPathName); + DotExpr.MExpr:=CreatePrimitiveDotExpr('this.'+GetPathName); GetExpr:=DotExpr; FullGetter:=nil; SetExpr:=CreateDotExpression(El, - CreateBuiltInIdentifierExpr('this.'+GetPathName), - CreateBuiltInIdentifierExpr(String(DotExpr.Name))); + CreatePrimitiveDotExpr('this.'+GetPathName), + CreatePrimitiveDotExpr(String(DotExpr.Name))); end else if FullGetter.ClassType=TJSBracketMemberExpression then begin @@ -11107,12 +11394,12 @@ begin ParamExpr:=BracketExpr.Name; // create "a:value" - BracketExpr.Name:=CreateBuiltInIdentifierExpr('this.'+ParamName); + BracketExpr.Name:=CreatePrimitiveDotExpr('this.'+ParamName); AddVar(ParamName,ParamExpr); // create GetPathExpr "this.arr" GetPathExpr:=BracketExpr.MExpr; - BracketExpr.MExpr:=CreateBuiltInIdentifierExpr('this.'+GetPathName); + BracketExpr.MExpr:=CreatePrimitiveDotExpr('this.'+GetPathName); // GetExpr "this.p[this.a]" GetExpr:=BracketExpr; @@ -11121,8 +11408,8 @@ begin // SetExpr "this.p[this.a]" BracketExpr:=TJSBracketMemberExpression(CreateElement(TJSBracketMemberExpression,El)); SetExpr:=BracketExpr; - BracketExpr.MExpr:=CreateBuiltInIdentifierExpr('this.'+GetPathName); - BracketExpr.Name:=CreateBuiltInIdentifierExpr('this.'+ParamName); + BracketExpr.MExpr:=CreatePrimitiveDotExpr('this.'+GetPathName); + BracketExpr.Name:=CreatePrimitiveDotExpr('this.'+ParamName); end else @@ -11140,7 +11427,7 @@ begin // create SetExpr = v; AssignSt:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,El)); AssignSt.LHS:=SetExpr; - AssignSt.Expr:=CreateBuiltInIdentifierExpr(TempRefObjSetterArgName); + AssignSt.Expr:=CreatePrimitiveDotExpr(TempRefObjSetterArgName); SetExpr:=AssignSt; end else if (SetExpr.ClassType=TJSCallExpression) then @@ -11211,7 +11498,7 @@ begin // create "T.isPrototypeOf(exceptObject)" Call:=CreateCallExpression(El); Call.Expr:=DotExpr; - Call.AddArg(CreateBuiltInIdentifierExpr(FBuiltInNames[pbivnExceptObject])); + Call.AddArg(CreatePrimitiveDotExpr(FBuiltInNames[pbivnExceptObject])); IfSt.Cond:=Call; if El.VarEl<>nil then @@ -11221,7 +11508,7 @@ begin ListLast:=ListFirst; IfSt.BTrue:=ListFirst; V:=CreateVarStatement(TransformVariableName(El,El.VariableName,AContext), - CreateBuiltInIdentifierExpr(FBuiltInNames[pbivnExceptObject]),El); + CreatePrimitiveDotExpr(FBuiltInNames[pbivnExceptObject]),El); ListFirst.A:=V; // add statements AddToStatementList(ListFirst,ListLast,ConvertElement(El.Body,AContext),El); @@ -11443,7 +11730,7 @@ const VarAssignSt.LHS:=CreateSubDeclNameExpr(PasVar,PasVar.Name,FuncContext); VarDotExpr:=TJSDotMemberExpression(CreateElement(TJSDotMemberExpression,PasVar)); VarAssignSt.Expr:=VarDotExpr; - VarDotExpr.MExpr:=CreateBuiltInIdentifierExpr(SrcParamName); + VarDotExpr.MExpr:=CreatePrimitiveDotExpr(SrcParamName); VarDotExpr.Name:=TJSString(TransformVariableName(PasVar,FuncContext)); if (AContext.Resolver<>nil) then begin @@ -11660,7 +11947,7 @@ begin IfSt:=TJSIfStatement(CreateElement(TJSIfStatement,El)); AddToStatementList(BodyFirst,BodyLast,IfSt,El); FD.Body.A:=BodyFirst; - IfSt.Cond:=CreateBuiltInIdentifierExpr(SrcParamName); + IfSt.Cond:=CreatePrimitiveDotExpr(SrcParamName); // add clone statements AddCloneStatements(IfSt,FuncContext); // add init default statements @@ -11692,7 +11979,7 @@ begin // ); Call:=CreateCallExpression(El); Call.Expr:=CreateDotExpression(El,List.B, - CreateBuiltInIdentifierExpr(FBuiltInNames[pbifnRTTIAddFields])); + CreatePrimitiveDotExpr(FBuiltInNames[pbifnRTTIAddFields])); List.B:=Call; AddRTTIFields(Call.Args); end; @@ -11822,13 +12109,35 @@ end; function TPasToJSConverter.TransformModuleName(El: TPasModule; AddModulesPrefix: boolean; AContext: TConvertContext): String; +var + p, StartP: Integer; + aName, Part: String; begin if El is TPasProgram then Result:='program' else - Result:=TransformVariableName(El,AContext); + begin + Result:=''; + aName:=El.Name; + p:=1; + while p<=length(aName) do + begin + StartP:=p; + while (p<=length(aName)) and (aName[p]<>'.') do inc(p); + Part:=copy(aName,StartP,p-StartP); + Part:=TransformVariableName(El,Part,AContext); + if Result<>'' then Result:=Result+'.'; + Result:=Result+Part; + inc(p); + end; + end; if AddModulesPrefix then - Result:=FBuiltInNames[pbivnModules]+'.'+Result; + begin + if Pos('.',Result)>0 then + Result:=FBuiltInNames[pbivnModules]+'["'+Result+'"]' + else + Result:=FBuiltInNames[pbivnModules]+'.'+Result; + end; end; function TPasToJSConverter.IsPreservedWord(const aName: string): boolean; diff --git a/packages/pastojs/tests/tcmodules.pas b/packages/pastojs/tests/tcmodules.pas index 13896fbfd0..bb5d214734 100644 --- a/packages/pastojs/tests/tcmodules.pas +++ b/packages/pastojs/tests/tcmodules.pas @@ -24,8 +24,8 @@ unit tcmodules; interface uses - Classes, SysUtils, fpcunit, testregistry, contnrs, fppas2js, - pastree, PScanner, PasResolver, PParser, jstree, jswriter, jsbase; + Classes, SysUtils, fpcunit, testregistry, contnrs, fppas2js, pastree, + PScanner, PasResolver, PParser, PasResolveEval, jstree, jswriter, jsbase; const // default parser+scanner options @@ -96,6 +96,7 @@ type function GetModuleCount: integer; function GetModules(Index: integer): TTestEnginePasResolver; function OnPasResolverFindUnit(const aUnitName: String): TPasModule; + function FindUnit(const aUnitName: String): TPasModule; protected procedure SetUp; override; procedure TearDown; override; @@ -114,14 +115,16 @@ type procedure AddSystemUnit; virtual; procedure StartProgram(NeedSystemUnit: boolean); virtual; procedure StartUnit(NeedSystemUnit: boolean); virtual; - Procedure ConvertModule; virtual; - Procedure ConvertProgram; virtual; - Procedure ConvertUnit; virtual; + procedure ConvertModule; virtual; + procedure ConvertProgram; virtual; + procedure ConvertUnit; virtual; procedure CheckDottedIdentifier(Msg: string; El: TJSElement; DottedName: string); function GetDottedIdentifier(El: TJSElement): string; procedure CheckSource(Msg,Statements: String; InitStatements: string = ''; ImplStatements: string = ''); virtual; procedure CheckDiff(Msg, Expected, Actual: string); virtual; + procedure SetExpectedScannerError(Msg: string; MsgNumber: integer); + procedure SetExpectedParserError(Msg: string; MsgNumber: integer); procedure SetExpectedPasResolverError(Msg: string; MsgNumber: integer); procedure SetExpectedConverterError(Msg: string; MsgNumber: integer); function IsErrorExpected(E: Exception): boolean; @@ -132,6 +135,7 @@ type procedure HandleException(E: Exception); procedure RaiseException(E: Exception); procedure WriteSources(const aFilename: string; aRow, aCol: integer); + function GetDefaultNamespace: string; property PasProgram: TPasProgram Read FPasProgram; property Modules[Index: integer]: TTestEnginePasResolver read GetModules; property ModuleCount: integer read GetModuleCount; @@ -169,6 +173,10 @@ type Procedure TestEmptyProgramUseStrict; Procedure TestEmptyUnit; Procedure TestEmptyUnitUseStrict; + Procedure TestDottedUnitNames; + Procedure TestDottedUnitExpr; + Procedure Test_ModeFPCFail; + Procedure Test_ModeSwitchCBlocksFail; // vars/const Procedure TestVarInt; @@ -315,6 +323,7 @@ type Procedure TestRecordElementFromFuncResult_AsParams; Procedure TestRecordElementFromWith_AsParams; Procedure TestRecord_Equal; + Procedure TestRecord_TypeCastJSValueToRecord; // ToDo: const record // classes @@ -358,7 +367,11 @@ type Procedure TestClass_NestedSelf; Procedure TestClass_NestedClassSelf; Procedure TestClass_NestedCallInherited; - Procedure TestClass_TObjectFree; // ToDO + Procedure TestClass_TObjectFree; + Procedure TestClass_TObjectFreeNewInstance; + Procedure TestClass_TObjectFreeLowerCase; + Procedure TestClass_TObjectFreeFunctionFail; + Procedure TestClass_TObjectFreePropertyFail; // class of Procedure TestClassOf_Create; @@ -373,6 +386,9 @@ type Procedure TestClassOf_TypeCast; Procedure TestClassOf_ImplicitFunctionCall; + // nested class + Procedure TestNestedClass_Fail; + // external class Procedure TestExternalClass_Var; //ToDo Procedure TestExternalClass_Const; @@ -595,31 +611,51 @@ end; function TCustomTestModule.OnPasResolverFindUnit(const aUnitName: String ): TPasModule; var + DefNamespace: String; +begin + //writeln('TTestModule.OnPasResolverFindUnit START Unit="',aUnitName,'"'); + if (Pos('.',aUnitName)<1) then + begin + DefNamespace:=GetDefaultNamespace; + if DefNamespace<>'' then + begin + Result:=FindUnit(DefNamespace+'.'+aUnitName); + if Result<>nil then exit; + end; + end; + Result:=FindUnit(aUnitName); + if Result<>nil then exit; + writeln('TTestModule.OnPasResolverFindUnit missing unit "',aUnitName,'"'); + Fail('can''t find unit "'+aUnitName+'"'); +end; + +function TCustomTestModule.FindUnit(const aUnitName: String): TPasModule; +var i: Integer; CurEngine: TTestEnginePasResolver; CurUnitName: String; begin - //writeln('TTestModule.OnPasResolverFindUnit START Unit="',aUnitName,'"'); + //writeln('TTestModule.FindUnit START Unit="',aUnitName,'"'); Result:=nil; for i:=0 to ModuleCount-1 do begin CurEngine:=Modules[i]; CurUnitName:=ExtractFileUnitName(CurEngine.Filename); - //writeln('TTestModule.OnPasResolverFindUnit Checking ',i,'/',ModuleCount,' ',CurEngine.Filename,' ',CurUnitName); + //writeln('TTestModule.FindUnit Checking ',i,'/',ModuleCount,' ',CurEngine.Filename,' ',CurUnitName); if CompareText(aUnitName,CurUnitName)=0 then begin Result:=CurEngine.Module; if Result<>nil then exit; - //writeln('TTestModule.OnPasResolverFindUnit PARSING unit "',CurEngine.Filename,'"'); + //writeln('TTestModule.FindUnit PARSING unit "',CurEngine.Filename,'"'); FileResolver.FindSourceFile(aUnitName); CurEngine.Resolver:=TStreamResolver.Create; CurEngine.Resolver.OwnsStreams:=True; - //writeln('TTestResolver.OnPasResolverFindUnit SOURCE=',CurEngine.Source); + //writeln('TTestModule.FindUnit SOURCE=',CurEngine.Source); CurEngine.Resolver.AddStream(CurEngine.FileName,TStringStream.Create(CurEngine.Source)); CurEngine.Scanner:=TPascalScanner.Create(CurEngine.Resolver); CurEngine.Parser:=TTestPasParser.Create(CurEngine.Scanner,CurEngine.Resolver,CurEngine); - CurEngine.Parser.Options:=CurEngine.Parser.Options+po_pas2js; + CurEngine.Parser.Options:=CurEngine.Parser.Options+po_pas2js+[po_KeepScannerError]; if CompareText(CurUnitName,'System')=0 then CurEngine.Parser.ImplicitUses.Clear; CurEngine.Scanner.OpenFile(CurEngine.Filename); @@ -627,20 +663,14 @@ begin CurEngine.Parser.NextToken; CurEngine.Parser.ParseUnit(CurEngine.FModule); except - on E: EParserError do - HandleParserError(E); - on E: EPasResolve do - HandlePasResolveError(E); on E: Exception do HandleException(E); end; - //writeln('TTestModule.OnPasResolverFindUnit END ',CurUnitName); + //writeln('TTestModule.FindUnit END ',CurUnitName); Result:=CurEngine.Module; exit; end; end; - writeln('TTestModule.OnPasResolverFindUnit missing unit "',aUnitName,'"'); - Fail('can''t find unit "'+aUnitName+'"'); end; procedure TCustomTestModule.SetUp; @@ -659,7 +689,7 @@ begin FScanner.CurrentModeSwitches:=OBJFPCModeSwitches*msAllPas2jsModeSwitches+msAllPas2jsModeSwitchesReadOnly; FEngine:=AddModule(Filename); FParser:=TTestPasParser.Create(FScanner,FFileResolver,FEngine); - Parser.Options:=Parser.Options+po_pas2js; + Parser.Options:=Parser.Options+po_pas2js+[po_KeepScannerError]; FModule:=Nil; FConverter:=TPasToJSConverter.Create; FConverter.Options:=co_tcmodules; @@ -732,12 +762,6 @@ begin StartParsing; Parser.ParseMain(FModule); except - on E: EParserError do - HandleParserError(E); - on E: EPasResolve do - HandlePasResolveError(E); - on E: EPas2JS do - HandlePas2JSError(E); on E: Exception do HandleException(E); end; @@ -846,7 +870,7 @@ begin AddSystemUnit else Parser.ImplicitUses.Clear; - Add('program test1;'); + Add('program '+ExtractFileUnitName(Filename)+';'); Add(''); end; @@ -921,14 +945,6 @@ begin try FJSModule:=FConverter.ConvertPasElement(Module,Engine) as TJSSourceElements; except - on E: EScannerError do - HandleScannerError(E); - on E: EParserError do - HandleParserError(E); - on E: EPasResolve do - HandlePasResolveError(E); - on E: EPas2JS do - HandlePas2JSError(E); on E: Exception do HandleException(E); end; @@ -1199,6 +1215,22 @@ begin until false; end; +procedure TCustomTestModule.SetExpectedScannerError(Msg: string; + MsgNumber: integer); +begin + ExpectedErrorClass:=EScannerError; + ExpectedErrorMsg:=Msg; + ExpectedErrorNumber:=MsgNumber; +end; + +procedure TCustomTestModule.SetExpectedParserError(Msg: string; + MsgNumber: integer); +begin + ExpectedErrorClass:=EParserError; + ExpectedErrorMsg:=Msg; + ExpectedErrorNumber:=MsgNumber; +end; + procedure TCustomTestModule.SetExpectedPasResolverError(Msg: string; MsgNumber: integer); begin @@ -1225,6 +1257,10 @@ begin MsgNumber:=EPas2JS(E).MsgNumber else if E is EPasResolve then MsgNumber:=EPasResolve(E).MsgNumber + else if E is EParserError then + MsgNumber:=Parser.LastMsgNumber + else if E is EScannerError then + MsgNumber:=Scanner.LastMsgNumber else MsgNumber:=0; Result:=(MsgNumber=ExpectedErrorNumber) and (E.Message=ExpectedErrorMsg); @@ -1280,13 +1316,24 @@ end; procedure TCustomTestModule.HandleException(E: Exception); begin - if IsErrorExpected(E) then exit; - if not (E is EAssertionFailedError) then + if E is EScannerError then + HandleScannerError(EScannerError(E)) + else if E is EParserError then + HandleParserError(EParserError(E)) + else if E is EPasResolve then + HandlePasResolveError(EPasResolve(E)) + else if E is EPas2JS then + HandlePas2JSError(EPas2JS(E)) + else begin - WriteSources('',0,0); - writeln('ERROR: TCustomTestModule.HandleException '+E.ClassName+':'+E.Message); + if IsErrorExpected(E) then exit; + if not (E is EAssertionFailedError) then + begin + WriteSources('',0,0); + writeln('ERROR: TCustomTestModule.HandleException '+E.ClassName+':'+E.Message); + end; + RaiseException(E); end; - RaiseException(E); end; procedure TCustomTestModule.RaiseException(E: Exception); @@ -1299,6 +1346,10 @@ begin MsgNumber:=EPas2JS(E).MsgNumber else if E is EPasResolve then MsgNumber:=EPasResolve(E).MsgNumber + else if E is EParserError then + MsgNumber:=Parser.LastMsgNumber + else if E is EScannerError then + MsgNumber:=Scanner.LastMsgNumber else MsgNumber:=0; AssertEquals('Expected error message ('+IntToStr(ExpectedErrorNumber)+')','{'+ExpectedErrorMsg+'}','{'+E.Message+'}'); @@ -1345,6 +1396,17 @@ begin end; end; +function TCustomTestModule.GetDefaultNamespace: string; +var + C: TClass; +begin + Result:=''; + if FModule=nil then exit; + C:=FModule.ClassType; + if (C=TPasProgram) or (C=TPasLibrary) or (C=TPasPackage) then + Result:=Engine.DefaultNameSpace; +end; + { TTestModule } procedure TTestModule.TestEmptyProgram; @@ -1390,6 +1452,82 @@ begin ''); end; +procedure TTestModule.TestDottedUnitNames; +begin + AddModuleWithIntfImplSrc('NS1.Unit2.pas', + LinesToStr([ + 'var iV: longint;' + ]), + ''); + + FFilename:='ns1.test1.pp'; + StartProgram(true); + Add('uses unIt2;'); + Add('implementation'); + Add('var'); + Add(' i: longint;'); + Add('begin'); + Add(' i:=iv;'); + Add(' i:=uNit2.iv;'); + Add(' i:=Ns1.TEst1.i;'); + ConvertProgram; + CheckSource('TestDottedUnitNames', + LinesToStr([ + 'this.i = 0;', + '']), + LinesToStr([ // this.$init + '$mod.i = pas["NS1.Unit2"].iV;', + '$mod.i = pas["NS1.Unit2"].iV;', + '$mod.i = $mod.i;', + '']) ); +end; + +procedure TTestModule.TestDottedUnitExpr; +begin + AddModuleWithIntfImplSrc('NS2.SubNs2.Unit2.pas', + LinesToStr([ + 'procedure DoIt;' + ]), + 'procedure DoIt; begin end;'); + + FFilename:='Ns1.SubNs1.Test1.pp'; + StartProgram(true); + Add('uses Ns2.sUbnS2.unIt2;'); + Add('implementation'); + Add('var'); + Add(' i: longint;'); + Add('begin'); + Add(' ns2.subns2.unit2.doit;'); + Add(' i:=Ns1.SubNS1.TEst1.i;'); + ConvertProgram; + CheckSource('TestDottedUnitExpr', + LinesToStr([ + 'this.i = 0;', + '']), + LinesToStr([ // this.$init + 'pas["NS2.SubNs2.Unit2"].DoIt();', + '$mod.i = $mod.i;', + '']) ); +end; + +procedure TTestModule.Test_ModeFPCFail; +begin + StartProgram(false); + Add('{$mode FPC}'); + Add('begin'); + SetExpectedScannerError('Invalid mode: "FPC"',nErrInvalidMode); + ConvertProgram; +end; + +procedure TTestModule.Test_ModeSwitchCBlocksFail; +begin + StartProgram(false); + Add('{$modeswitch cblocks-}'); + Add('begin'); + SetExpectedScannerError('Invalid mode switch: "cblocks-"',nErrInvalidModeSwitch); + ConvertProgram; +end; + procedure TTestModule.TestVarInt; begin StartProgram(false); @@ -5657,13 +5795,13 @@ begin Add('function GetRec(vB: integer = 0): TRecord;'); Add('begin'); Add('end;'); - Add('procedure DoIt(vG: integer; const vH: integer; var vI: integer);'); + Add('procedure DoIt(vG: integer; const vH: integer);'); Add('begin'); Add('end;'); Add('begin'); - Add(' doit(getrec.i,getrec.i,getrec.i);'); - Add(' doit(getrec().i,getrec().i,getrec().i);'); - Add(' doit(getrec(1).i,getrec(2).i,getrec(3).i);'); + Add(' doit(getrec.i,getrec.i);'); + Add(' doit(getrec().i,getrec().i);'); + Add(' doit(getrec(1).i,getrec(2).i);'); ConvertProgram; CheckSource('TestRecordElementFromFuncResult_AsParams', LinesToStr([ // statements @@ -5681,37 +5819,13 @@ begin ' var Result = new $mod.TRecord();', ' return Result;', '};', - 'this.DoIt = function (vG,vH,vI) {', + 'this.DoIt = function (vG,vH) {', '};' ]), LinesToStr([ - '$mod.DoIt($mod.GetRec(0).i,$mod.GetRec(0).i,{', - ' p: $mod.GetRec(0),', - ' get: function () {', - ' return this.p.i;', - ' },', - ' set: function (v) {', - ' this.p.i = v;', - ' }', - '});', - '$mod.DoIt($mod.GetRec(0).i,$mod.GetRec(0).i,{', - ' p: $mod.GetRec(0),', - ' get: function () {', - ' return this.p.i;', - ' },', - ' set: function (v) {', - ' this.p.i = v;', - ' }', - '});', - '$mod.DoIt($mod.GetRec(1).i,$mod.GetRec(2).i,{', - ' p: $mod.GetRec(3),', - ' get: function () {', - ' return this.p.i;', - ' },', - ' set: function (v) {', - ' this.p.i = v;', - ' }', - '});', + '$mod.DoIt($mod.GetRec(0).i,$mod.GetRec(0).i);', + '$mod.DoIt($mod.GetRec(0).i,$mod.GetRec(0).i);', + '$mod.DoIt($mod.GetRec(1).i,$mod.GetRec(2).i);', ''])); end; @@ -5826,6 +5940,39 @@ begin ''])); end; +procedure TTestModule.TestRecord_TypeCastJSValueToRecord; +begin + StartProgram(false); + Add('type'); + Add(' TRecord = record'); + Add(' i: longint;'); + Add(' end;'); + Add('var'); + Add(' Jv: jsvalue;'); + Add(' Rec: trecord;'); + Add('begin'); + Add(' rec:=trecord(jv);'); + ConvertProgram; + CheckSource('TestRecord_TypeCastJSValueToRecord', + LinesToStr([ // statements + 'this.TRecord = function (s) {', + ' if (s) {', + ' this.i = s.i;', + ' } else {', + ' this.i = 0;', + ' };', + ' this.$equal = function (b) {', + ' return this.i == b.i;', + ' };', + '};', + 'this.Jv = undefined;', + 'this.Rec = new $mod.TRecord();' + ]), + LinesToStr([ + '$mod.Rec = new $mod.TRecord(rtl.getObject($mod.Jv));', + ''])); +end; + procedure TTestModule.TestClass_TObjectDefaultConstructor; begin StartProgram(false); @@ -7802,18 +7949,16 @@ begin ' if (5 == this.cI) ;', ' if (this.cI == 6) ;', ' if (7 == this.cI) ;', - ' var $with1 = this;', - ' if ($with1.cI == 11) ;', - ' if (12 == $with1.cI) ;', + ' if (this.cI == 11) ;', + ' if (12 == this.cI) ;', ' };', ' this.DoMore = function () {', ' if (this.cI == 8) ;', ' if (this.cI == 9) ;', ' if (10 == this.cI) ;', ' if (11 == this.cI) ;', - ' var $with1 = this;', - ' if ($with1.cI == 13) ;', - ' if (14 == $with1.cI) ;', + ' if (this.cI == 13) ;', + ' if (14 == this.cI) ;', ' };', '});', 'this.Obj = null;', @@ -8066,8 +8211,6 @@ end; procedure TTestModule.TestClass_TObjectFree; begin - exit; - StartProgram(false); Add([ 'type', @@ -8084,24 +8227,30 @@ begin ' o.free;', ' o.free();', ' l.free;', + ' l.free();', ' o.obj.free;', ' o.obj.free();', + ' with o do obj.free;', + ' with o do obj.free();', ' result.Free;', ' result.Free();', 'end;', 'var o: tobject;', + ' a: array of tobject;', 'begin', ' o.free;', ' o.obj.free;', + ' a[1+2].free;', '']); ConvertProgram; - CheckSource('TestClass_NestedCallInherited', + CheckSource('TestClass_TObjectFree', LinesToStr([ // statements 'rtl.createClass($mod, "TObject", null, function () {', ' this.$init = function () {', ' this.Obj = null;', ' };', ' this.$final = function () {', + ' this.Obj = undefined;', ' };', ' this.Free = function () {', ' };', @@ -8109,14 +8258,140 @@ begin 'this.DoIt = function (o) {', ' var Result = null;', ' var l = null;', + ' o = rtl.freeLoc(o);', + ' o = rtl.freeLoc(o);', + ' l = rtl.freeLoc(l);', + ' l = rtl.freeLoc(l);', + ' rtl.free(o, "Obj");', + ' rtl.free(o, "Obj");', + ' rtl.free(o, "Obj");', + ' rtl.free(o, "Obj");', + ' Result = rtl.freeLoc(Result);', + ' Result = rtl.freeLoc(Result);', ' return Result;', '};', 'this.o = null;', + 'this.a = [];', + '']), + LinesToStr([ // $mod.$main + 'rtl.free($mod, "o");', + 'rtl.free($mod.o, "Obj");', + 'rtl.free($mod.a, 1 + 2);', + ''])); +end; + +procedure TTestModule.TestClass_TObjectFreeNewInstance; +begin + StartProgram(false); + Add([ + 'type', + ' TObject = class', + ' constructor Create;', + ' procedure Free;', + ' end;', + 'constructor TObject.Create; begin end;', + 'procedure tobject.free; begin end;', + 'begin', + ' with tobject.create do free;', + '']); + ConvertProgram; + CheckSource('TestClass_TObjectFreeNewInstance', + LinesToStr([ // statements + 'rtl.createClass($mod, "TObject", null, function () {', + ' this.$init = function () {', + ' };', + ' this.$final = function () {', + ' };', + ' this.Create = function () {', + ' };', + ' this.Free = function () {', + ' };', + '});', + '']), + LinesToStr([ // $mod.$main + 'var $with1 = $mod.TObject.$create("Create");', + '$with1=rtl.freeLoc($with1);', + ''])); +end; + +procedure TTestModule.TestClass_TObjectFreeLowerCase; +begin + StartProgram(false); + Add([ + 'type', + ' TObject = class', + ' destructor Destroy;', + ' procedure Free;', + ' end;', + 'destructor TObject.Destroy; begin end;', + 'procedure tobject.free; begin end;', + 'var o: tobject;', + 'begin', + ' o.free;', + '']); + Converter.UseLowerCase:=true; + ConvertProgram; + CheckSource('TestClass_TObjectFreeLowerCase', + LinesToStr([ // statements + 'rtl.createClass($mod, "tobject", null, function () {', + ' this.$init = function () {', + ' };', + ' this.$final = function () {', + ' };', + ' rtl.tObjectDestroy = "destroy";', + ' this.destroy = function () {', + ' };', + ' this.free = function () {', + ' };', + '});', + 'this.o = null;', '']), LinesToStr([ // $mod.$main + 'rtl.free($mod, "o");', ''])); end; +procedure TTestModule.TestClass_TObjectFreeFunctionFail; +begin + StartProgram(false); + Add([ + 'type', + ' TObject = class', + ' procedure Free;', + ' function GetObj: tobject; virtual; abstract;', + ' end;', + 'procedure tobject.free;', + 'begin', + 'end;', + 'var o: tobject;', + 'begin', + ' o.getobj.free;', + '']); + SetExpectedPasResolverError(sFreeNeedsVar,nFreeNeedsVar); + ConvertProgram; +end; + +procedure TTestModule.TestClass_TObjectFreePropertyFail; +begin + StartProgram(false); + Add([ + 'type', + ' TObject = class', + ' procedure Free;', + ' FObj: TObject;', + ' property Obj: tobject read FObj write FObj;', + ' end;', + 'procedure tobject.free;', + 'begin', + 'end;', + 'var o: tobject;', + 'begin', + ' o.obj.free;', + '']); + SetExpectedPasResolverError(sFreeNeedsVar,nFreeNeedsVar); + ConvertProgram; +end; + procedure TTestModule.TestClassOf_Create; begin StartProgram(false); @@ -8634,6 +8909,20 @@ begin ''])); end; +procedure TTestModule.TestNestedClass_Fail; +begin + StartProgram(false); + Add([ + 'type', + ' TObject = class', + ' type TNested = longint;', + ' end;', + 'begin']); + SetExpectedPasResolverError('not yet implemented: TNested:TPasAliasType [20170608232534] nested types', + nNotYetImplemented); + ConvertProgram; +end; + procedure TTestModule.TestExternalClass_Var; begin StartProgram(false); @@ -9118,7 +9407,7 @@ begin Add(' a:=test1.texta.new();'); Add(' a:=test1.texta.new(3);'); ConvertProgram; - CheckSource('TestExternalClass_ObjectCreate', + CheckSource('TestExternalClass_New', LinesToStr([ // statements 'this.A = null;', '']), @@ -9126,10 +9415,9 @@ begin '$mod.A = new ExtA();', '$mod.A = new ExtA();', '$mod.A = new ExtA(1,2);', - 'var $with1 = ExtA;', - '$mod.A = new $with1();', - '$mod.A = new $with1();', - '$mod.A = new $with1(2,2);', + '$mod.A = new ExtA();', + '$mod.A = new ExtA();', + '$mod.A = new ExtA(2,2);', '$mod.A = new ExtA();', '$mod.A = new ExtA();', '$mod.A = new ExtA(3,2);', diff --git a/packages/pastojs/tests/tcoptimizations.pas b/packages/pastojs/tests/tcoptimizations.pas index 2bc225c5ed..a476e2be81 100644 --- a/packages/pastojs/tests/tcoptimizations.pas +++ b/packages/pastojs/tests/tcoptimizations.pas @@ -25,7 +25,7 @@ interface uses Classes, SysUtils, testregistry, fppas2js, pastree, - PScanner, PasUseAnalyzer, PasResolver, + PScanner, PasUseAnalyzer, PasResolver, PasResolveEval, tcmodules; type |