summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormarco <marco@3ad0048d-3df7-0310-abae-a5850022a9f2>2017-06-19 11:46:56 +0000
committermarco <marco@3ad0048d-3df7-0310-abae-a5850022a9f2>2017-06-19 11:46:56 +0000
commit35944ee1860ae65f8cd8d3dc0f56c1ba208bcc3e (patch)
tree4e3e5ba77ba5b98e798973aca3036811dac7bf4a
parent916559cb1fd5bb6ece7cfe9400e58a221b51bb3f (diff)
downloadfpc-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.pp647
-rw-r--r--packages/pastojs/tests/tcmodules.pas458
-rw-r--r--packages/pastojs/tests/tcoptimizations.pas2
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