diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..5378fe0 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* -text \ No newline at end of file diff --git a/.gitignore b/.gitignore index e6008d0..2345128 100644 --- a/.gitignore +++ b/.gitignore @@ -18,19 +18,25 @@ *.lib *.sbr *.scc -[Bb]in/ -[Dd]ebug/ +[Bb]in +[Dd]ebug*/ obj/ -[Rr]elease/ +[Rr]elease*/ _ReSharper*/ -[Tt]humbs.db -[Tt]est[Rr]esult* -[Bb]uild[Ll]og.* *.[Pp]ublish.xml *.resharper* AppData/ +App_Data/ *.log.* [Ll]ogs/ -[Dd]ata/ [Pp]ackages/ -[Dd]atabase/ \ No newline at end of file +[Tt]humbs.db +[Tt]est[Rr]esult* +[Bb]uild[Ll]og.* +*.sln.DotSettings.* +*.ncrunchproject +*.ncrunchsolution +*.nupkg +.vs +.vscode +*.orig \ No newline at end of file diff --git a/.nuget/NuGet.Config b/.nuget/NuGet.Config deleted file mode 100644 index 67f8ea0..0000000 --- a/.nuget/NuGet.Config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.nuget/NuGet.exe b/.nuget/NuGet.exe deleted file mode 100644 index 34ad49b..0000000 Binary files a/.nuget/NuGet.exe and /dev/null differ diff --git a/.nuget/NuGet.targets b/.nuget/NuGet.targets deleted file mode 100644 index 25380fe..0000000 --- a/.nuget/NuGet.targets +++ /dev/null @@ -1,52 +0,0 @@ - - - - $(MSBuildProjectDirectory)\..\ - $([System.IO.Path]::Combine($(SolutionDir), ".nuget")) - $(NuGetToolsPath)\nuget.exe - $([System.IO.Path]::Combine($(ProjectDir), "packages.config")) - $([System.IO.Path]::Combine($(SolutionDir), "packages")) - $(TargetDir.Trim('\\')) - - - "" - - - false - - - false - - - "$(NuGetExePath)" install "$(PackagesConfig)" -source $(PackageSources) -o "$(PackagesDir)" - "$(NuGetExePath)" pack "$(ProjectPath)" -p Configuration=$(Configuration) -o "$(PackageOutputDir)" -symbols - - - - RestorePackages; - $(BuildDependsOn); - - - - - $(BuildDependsOn); - BuildPackage; - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..889e5c1 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,5 @@ +# Contributor Covenant Code of Conduct + +Please refer to the [code of conduct](https://github.com/OpenRealEstate/OpenRealEstate/blob/master/CODE_OF_CONDUCT.md) document that applies to all repositories in the OpenRealEstate organisation. + +--- \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..566383e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,5 @@ +# Contributing + +Please refer to the [contributing](https://github.com/OpenRealEstate/OpenRealEstate/blob/master/CONTRIBUTING.md) document that applies to all repositories in the OpenRealEstate organisation. + +--- \ No newline at end of file diff --git a/Code/EcmaScript.NET/..svnbridge/.svnbridge b/Code/EcmaScript.NET/..svnbridge/.svnbridge deleted file mode 100644 index a580b9e..0000000 --- a/Code/EcmaScript.NET/..svnbridge/.svnbridge +++ /dev/null @@ -1,6 +0,0 @@ -svn:ignorebin -obj -svn:ignorebin -obj -*.user - \ No newline at end of file diff --git a/Code/EcmaScript.NET/AssemblyInfo.cs b/Code/EcmaScript.NET/AssemblyInfo.cs deleted file mode 100644 index 546c972..0000000 --- a/Code/EcmaScript.NET/AssemblyInfo.cs +++ /dev/null @@ -1,25 +0,0 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Security; - -[assembly: AssemblyTitle("EcmaScript.NET")] -[assembly: AssemblyVersion("1.0.1.0")] - -[assembly: AssemblyDescriptionAttribute("Modified version of the EcmaScript.NET assembly.")] - -[assembly: CLSCompliant(true)] -[assembly: AllowPartiallyTrustedCallers] -[assembly: AssemblyFileVersionAttribute("1.0.1.0")] diff --git a/Code/EcmaScript.NET/EcmaScript.NET.csproj b/Code/EcmaScript.NET/EcmaScript.NET.csproj deleted file mode 100644 index 4a8e1b8..0000000 --- a/Code/EcmaScript.NET/EcmaScript.NET.csproj +++ /dev/null @@ -1,403 +0,0 @@ - - - - Local - 9.0.30729 - 2.0 - {ED3DF8AE-9541-4C95-93C5-8C417D4CBA50} - Debug - AnyCPU - - - - - EcmaScript.NET - - - JScript - Grid - IE50 - false - Library - EcmaScript.NET - OnBuildSuccess - - - - - - - - - - - - - - - true - 3.5 - false - v2.0 - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - true - - ..\..\..\YUICompressor\ - true - - - bin\Debug\ - false - 285212672 - false - - - - - - - true - 4096 - false - 0162;0164;0675 - true - false - false - false - 4 - full - prompt - AllRules.ruleset - - - bin\Release\ - false - 4194304 - false - - - TRACE - - - false - 512 - false - - - true - false - false - false - 4 - none - prompt - AllRules.ruleset - - - - mscorlib - - - System - - - - - - - Code - - - - - - - Code - - - - - - - Code - - - Code - - - - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - - - - - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - - - - - Code - - - Code - - - Code - - - Designer - - - - - - - - - - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 2.0 %28x86%29 - true - - - False - .NET Framework 3.0 %28x86%29 - false - - - False - .NET Framework 3.5 - false - - - False - .NET Framework 3.5 SP1 - false - - - - - - - - - - - \ No newline at end of file diff --git a/EcmaScript.NET.sln b/EcmaScript.NET.sln index f6e8362..470a86d 100644 --- a/EcmaScript.NET.sln +++ b/EcmaScript.NET.sln @@ -1,36 +1,25 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EcmaScript.NET", "Code\EcmaScript.NET\EcmaScript.NET.csproj", "{ED3DF8AE-9541-4C95-93C5-8C417D4CBA50}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{A2C19DA0-637F-462F-B950-53F7FE2F725C}" - ProjectSection(SolutionItems) = preProject - .nuget\NuGet.exe = .nuget\NuGet.exe - .nuget\NuGet.targets = .nuget\NuGet.targets - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|Mixed Platforms = Debug|Mixed Platforms - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|Mixed Platforms = Release|Mixed Platforms - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {ED3DF8AE-9541-4C95-93C5-8C417D4CBA50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {ED3DF8AE-9541-4C95-93C5-8C417D4CBA50}.Debug|Any CPU.Build.0 = Debug|Any CPU - {ED3DF8AE-9541-4C95-93C5-8C417D4CBA50}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {ED3DF8AE-9541-4C95-93C5-8C417D4CBA50}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {ED3DF8AE-9541-4C95-93C5-8C417D4CBA50}.Debug|x86.ActiveCfg = Debug|Any CPU - {ED3DF8AE-9541-4C95-93C5-8C417D4CBA50}.Release|Any CPU.ActiveCfg = Release|Any CPU - {ED3DF8AE-9541-4C95-93C5-8C417D4CBA50}.Release|Any CPU.Build.0 = Release|Any CPU - {ED3DF8AE-9541-4C95-93C5-8C417D4CBA50}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {ED3DF8AE-9541-4C95-93C5-8C417D4CBA50}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {ED3DF8AE-9541-4C95-93C5-8C417D4CBA50}.Release|x86.ActiveCfg = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27130.2024 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EcmaScript.NET", "src\EcmaScript.NET\EcmaScript.NET.csproj", "{93F6B750-891E-4E71-925A-5B40619BB3E2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {93F6B750-891E-4E71-925A-5B40619BB3E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {93F6B750-891E-4E71-925A-5B40619BB3E2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {93F6B750-891E-4E71-925A-5B40619BB3E2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {93F6B750-891E-4E71-925A-5B40619BB3E2}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {80F0CE8A-6E72-4246-A0F0-1D9C91E91DFD} + EndGlobalSection +EndGlobal diff --git a/EcmaScript.NET.sln.DotSettings b/EcmaScript.NET.sln.DotSettings index 9ce3616..1e2b3e6 100644 --- a/EcmaScript.NET.sln.DotSettings +++ b/EcmaScript.NET.sln.DotSettings @@ -1,226 +1,99 @@  + True + True + ERROR + ERROR + ERROR + ERROR + DO_NOT_SHOW + DO_NOT_SHOW + DO_NOT_SHOW + ERROR + ERROR + ERROR + <?xml version="1.0" encoding="utf-16"?><Profile name="PK Code Cleanup"><AspOptimizeRegisterDirectives>True</AspOptimizeRegisterDirectives><HtmlReformatCode>True</HtmlReformatCode><CSArrangeThisQualifier>True</CSArrangeThisQualifier><CSRemoveCodeRedundancies>True</CSRemoveCodeRedundancies><CSUseAutoProperty>True</CSUseAutoProperty><CSMakeFieldReadonly>True</CSMakeFieldReadonly><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName></RegionName></CSOptimizeUsings><CSShortenReferences>True</CSShortenReferences><CSReformatCode>True</CSReformatCode><CSReorderTypeMembers>True</CSReorderTypeMembers><VBOptimizeImports>True</VBOptimizeImports><VBShortenReferences>True</VBShortenReferences><VBReformatCode>True</VBReformatCode><JsReformatCode>True</JsReformatCode><JsInsertSemicolon>True</JsInsertSemicolon><CssAlphabetizeProperties>True</CssAlphabetizeProperties><CssReformatCode>True</CssReformatCode><XMLReformatCode>True</XMLReformatCode><StyleCop.Documentation><SA1600ElementsMustBeDocumented>False</SA1600ElementsMustBeDocumented><SA1604ElementDocumentationMustHaveSummary>False</SA1604ElementDocumentationMustHaveSummary><SA1609PropertyDocumentationMustHaveValueDocumented>False</SA1609PropertyDocumentationMustHaveValueDocumented><SA1611ElementParametersMustBeDocumented>False</SA1611ElementParametersMustBeDocumented><SA1615ElementReturnValueMustBeDocumented>False</SA1615ElementReturnValueMustBeDocumented><SA1617VoidReturnValueMustNotBeDocumented>False</SA1617VoidReturnValueMustNotBeDocumented><SA1618GenericTypeParametersMustBeDocumented>False</SA1618GenericTypeParametersMustBeDocumented><SA1626SingleLineCommentsMustNotUseDocumentationStyleSlashes>True</SA1626SingleLineCommentsMustNotUseDocumentationStyleSlashes><SA1628DocumentationTextMustBeginWithACapitalLetter>True</SA1628DocumentationTextMustBeginWithACapitalLetter><SA1629DocumentationTextMustEndWithAPeriod>True</SA1629DocumentationTextMustEndWithAPeriod><SA1633SA1641UpdateFileHeader>Ignore</SA1633SA1641UpdateFileHeader><SA1639FileHeaderMustHaveSummary>False</SA1639FileHeaderMustHaveSummary><SA1642ConstructorSummaryDocumentationMustBeginWithStandardText>False</SA1642ConstructorSummaryDocumentationMustBeginWithStandardText><SA1643DestructorSummaryDocumentationMustBeginWithStandardText>False</SA1643DestructorSummaryDocumentationMustBeginWithStandardText><SA1644DocumentationHeadersMustNotContainBlankLines>False</SA1644DocumentationHeadersMustNotContainBlankLines></StyleCop.Documentation></Profile> + PK Code Cleanup + public protected internal private new abstract virtual sealed override static readonly extern unsafe volatile async + True + True + True + True + True + True + True + True NEXT_LINE + 0 NEXT_LINE + MULTILINE + True + ALWAYS_ADD NEXT_LINE - <?xml version="1.0" encoding="utf-8" ?> - -<!-- -I. Overall - -I.1 Each pattern can have <Match>....</Match> element. For the given type declaration, the pattern with the match, evaluated to 'true' with the largest weight, will be used -I.2 Each pattern consists of the sequence of <Entry>...</Entry> elements. Type member declarations are distributed between entries -I.3 If pattern has RemoveAllRegions="true" attribute, then all regions will be cleared prior to reordering. Otherwise, only auto-generated regions will be cleared -I.4 The contents of each entry is sorted by given keys (First key is primary, next key is secondary, etc). Then the declarations are grouped and en-regioned by given property - -II. Available match operands - -Each operand may have Weight="..." attribute. This weight will be added to the match weight if the operand is evaluated to 'true'. -The default weight is 1 - -II.1 Boolean functions: -II.1.1 <And>....</And> -II.1.2 <Or>....</Or> -II.1.3 <Not>....</Not> - -II.2 Operands -II.2.1 <Kind Is="..."/>. Kinds are: class, struct, interface, enum, delegate, type, constructor, destructor, property, indexer, method, operator, field, constant, event, member -II.2.2 <Name Is="..." [IgnoreCase="true/false"] />. The 'Is' attribute contains regular expression -II.2.3 <HasAttribute CLRName="..." [Inherit="true/false"] />. The 'CLRName' attribute contains regular expression -II.2.4 <Access Is="..."/>. The 'Is' values are: public, protected, internal, protected internal, private -II.2.5 <Static/> -II.2.6 <Abstract/> -II.2.7 <Virtual/> -II.2.8 <Override/> -II.2.9 <Sealed/> -II.2.10 <Readonly/> -II.2.11 <ImplementsInterface CLRName="..."/>. The 'CLRName' attribute contains regular expression -II.2.12 <HandlesEvent /> ---> - -<Patterns xmlns="urn:shemas-jetbrains-com:member-reordering-patterns"> - - <!--Do not reorder COM interfaces and structs marked by StructLayout attribute--> - <Pattern> - <Match> - <Or Weight="100"> - <And> - <Kind Is="interface"/> - <Or> - <HasAttribute CLRName="System.Runtime.InteropServices.InterfaceTypeAttribute"/> - <HasAttribute CLRName="System.Runtime.InteropServices.ComImport"/> - </Or> - </And> - <HasAttribute CLRName="System.Runtime.InteropServices.StructLayoutAttribute"/> - </Or> - </Match> - </Pattern> - - <!--Special formatting of NUnit test fixture--> - <Pattern RemoveAllRegions="true"> - <Match> - <And Weight="100"> - <Kind Is="class"/> - <HasAttribute CLRName="NUnit.Framework.TestFixtureAttribute" Inherit="true"/> - </And> - </Match> - - <!--Setup/Teardow--> - <Entry> - <Match> - <And> - <Kind Is="method"/> - <Or> - <HasAttribute CLRName="NUnit.Framework.SetUpAttribute" Inherit="true"/> - <HasAttribute CLRName="NUnit.Framework.TearDownAttribute" Inherit="true"/> - <HasAttribute CLRName="NUnit.Framework.FixtureSetUpAttribute" Inherit="true"/> - <HasAttribute CLRName="NUnit.Framework.FixtureTearDownAttribute" Inherit="true"/> - </Or> - </And> - </Match> - <Group Region="Setup/Teardown"/> - </Entry> - - <!--All other members--> - <Entry/> - - <!--Test methods--> - <Entry> - <Match> - <And Weight="100"> - <Kind Is="method"/> - <HasAttribute CLRName="NUnit.Framework.TestAttribute" Inherit="false"/> - </And> - </Match> - <Sort> - <Name/> - </Sort> - </Entry> - </Pattern> - - <!--Default pattern--> - <Pattern> - - <!--public delegate--> - <Entry> - <Match> - <And Weight="100"> - <Access Is="public"/> - <Kind Is="delegate"/> - </And> - </Match> - <Sort> - <Name/> - </Sort> - <Group Region="Delegates"/> - </Entry> - - <!--public enum--> - <Entry> - <Match> - <And Weight="100"> - <Access Is="public"/> - <Kind Is="enum"/> - </And> - </Match> - <Sort> - <Name/> - </Sort> - <Group> - <Name Region="${Name} enum"/> - </Group> - </Entry> - - <!--static fields and constants--> - <Entry> - <Match> - <Or> - <Kind Is="constant"/> - <And> - <Kind Is="field"/> - <Static/> - </And> - </Or> - </Match> - <Sort> - <Kind Order="constant field"/> - </Sort> - </Entry> - - <!--instance fields--> - <Entry> - <Match> - <And> - <Kind Is="field"/> - <Not> - <Static/> - </Not> - </And> - </Match> - <Sort> - <Readonly/> - <Name/> - </Sort> - </Entry> - - <!--Constructors. Place static one first--> - <Entry> - <Match> - <Kind Is="constructor"/> - </Match> - <Sort> - <Static/> - </Sort> - </Entry> - - <!--properties, indexers--> - <Entry> - <Match> - <Or> - <Kind Is="property"/> - <Kind Is="indexer"/> - </Or> - </Match> - </Entry> - - <!--interface implementations--> - <Entry> - <Match> - <And Weight="100"> - <Kind Is="member"/> - <ImplementsInterface/> - </And> - </Match> - <Sort> - <ImplementsInterface Immediate="true"/> - </Sort> - <Group> - <ImplementsInterface Immediate="true" Region="${ImplementsInterface} Members"/> - </Group> - </Entry> - - <!--all other members--> - <Entry/> - - <!--nested types--> - <Entry> - <Match> - <Kind Is="type"/> - </Match> - <Sort> - <Name/> - </Sort> - </Entry> - </Pattern> - -</Patterns> - + NEXT_LINE + True + NEXT_LINE + NEVER + NEVER + DO_NOT_USE + LINE_BREAK + False + NEXT_LINE + False + CHOP_IF_LONG + CHOP_ALWAYS + True + CHOP_IF_LONG + CHOP_ALWAYS + 160 + True + CHOP_ALWAYS + CHOP_ALWAYS + CHOP_ALWAYS + CHOP_ALWAYS + PBKDF + <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /> + TEMP_FOLDER + TEMP_FOLDER + + + + + + + True + + + + + + + True + 3 + True + True + True + True + True + True + True + True + True + + + + + True + True True True + False False True 4 False None - False \ No newline at end of file + False + <data /> + <data><IncludeFilters /><ExcludeFilters /></data> diff --git a/NuGet Packages/EcmaScript.Net.1.0.0.0.nupkg b/NuGet Packages/EcmaScript.Net.1.0.0.0.nupkg deleted file mode 100644 index db25f29..0000000 Binary files a/NuGet Packages/EcmaScript.Net.1.0.0.0.nupkg and /dev/null differ diff --git a/NuGet Packages/EcmaScript.Net.1.0.1.0.nupkg b/NuGet Packages/EcmaScript.Net.1.0.1.0.nupkg deleted file mode 100644 index 2601139..0000000 Binary files a/NuGet Packages/EcmaScript.Net.1.0.1.0.nupkg and /dev/null differ diff --git a/README.md b/README.md new file mode 100644 index 0000000..7e3488f --- /dev/null +++ b/README.md @@ -0,0 +1,27 @@ + + +# EcmaScript.NET + +This is a port of the Java EcmaScript code, which was originally found under the Java YUICompressor code, circa 2011 or so. + +It was mainly used for the [YUICompress.NET](https://github.com/YUICompressor-NET) project. + + +[![Build status](https://ci.appveyor.com/api/projects/status/8sqlf3tbrnpw4lmj/branch/master?svg=true)](https://ci.appveyor.com/project/PureKrome/ecmascript-net) [![NuGet](https://img.shields.io/nuget/v/EcmaScript.Net.svg)](https://www.nuget.org/packages/ecmascript.net) [![NuGet](https://img.shields.io/nuget/dt/EcmaScript.Net.svg)](https://www.nuget.org/packages/OpenRealEstate.NET.Core) [![MyGet Pre Release](https://img.shields.io/myget/pk-development/vpre/EcmaScript.Net.svg)]() + +--- + +## Maintenance Mode. +This project is in maintenance mode as is only updated when other contributors provide any PR's - no one is actively developing/working on, this project. + +## Contributing + +Discussions and pull requests are encouraged :) Please refer to the [contributing](https://github.com/PureKrome/EcmaScript.NET/blob/master/CONTRIBUTING.md) document which goes into detail about how to do this. + +## Code of Conduct +Yep, we also have a [code of conduct](https://github.com/PureKrome/EcmaScript.NET/blob/master/CODE_OF_CONDUCT.md) which all participants are required to respect and follow. + +## Feedback +Yep, refer to the [contributing page](https://github.com/PureKrome/EcmaScript.NET/blob/master/CONTRIBUTING.md) about how best to give feedback - either good or needs-improvement :) + +--- diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..ccbb862 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,60 @@ + +version: '{build}.0.0-dev' +configuration: Release +os: Visual Studio 2017 +pull_requests: + do_not_increment_build_number: true + +environment: + nuget_apiKey: + secure: jfcUvHZhgnUboplqTBDWr8mG5PIlrgBv5TA2fhhop4ZSiDxskyy+RtYyeHoduJFR + myget_apiKey: + secure: AguzfXNvHBkkDebTyWHK7o9OC5YL3eszT6u7PnzGq2G7ozdUypDlGGpcFsTRYHEc + +# Override the 'version' if this is a GH-tag-commit -or- this is a custom branch (i.e not 'master'). +init: + - ps: | + if ($env:APPVEYOR_REPO_TAG -eq $TRUE) + { + Write-Host " !! Commit is Tagged and branch is 'master' - forcing build version to tag-value." -ForegroundColor Red; + Update-AppveyorBuild -Version "$env:APPVEYOR_REPO_TAG_NAME" + } + iex ((new-object net.webclient).DownloadString('https://gist.githubusercontent.com/PureKrome/0f79e25693d574807939/raw/f5b40256fc2ca77d49f1c7773d28406152544c1e/appveyor-build-info.ps')) + + +before_build: + # Display .NET Core version + - cmd: dotnet --info + # Display minimal restore text + - cmd: dotnet restore --verbosity quiet + +build_script: + - dotnet build -c %CONFIGURATION% -v minimal /p:Version=%APPVEYOR_BUILD_VERSION% --no-restore + +test_script: + - dotnet pack -c %CONFIGURATION% /p:Version=%APPVEYOR_BUILD_VERSION% --no-restore --no-build + +artifacts: + - path: '**\*.nupkg' + name: packaged-nugets + type: NuGetPackage + +deploy: + - provider: NuGet + server: https://www.myget.org/F/pk-development/api/v2/package + api_key: + secure: AguzfXNvHBkkDebTyWHK7o9OC5YL3eszT6u7PnzGq2G7ozdUypDlGGpcFsTRYHEc + skip_symbols: true + artifact: packaged-nugets + on: + appveyor_repo_tag: false + - provider: NuGet + api_key: + secure: jfcUvHZhgnUboplqTBDWr8mG5PIlrgBv5TA2fhhop4ZSiDxskyy+RtYyeHoduJFR + skip_symbols: true + artifact: packaged-nugets + on: + appveyor_repo_tag: true + +cache: + - packages -> **\packages.config \ No newline at end of file diff --git a/Code/EcmaScript.NET/Arguments.cs b/src/EcmaScript.NET/Arguments.cs similarity index 96% rename from Code/EcmaScript.NET/Arguments.cs rename to src/EcmaScript.NET/Arguments.cs index 2205814..3c577ba 100644 --- a/Code/EcmaScript.NET/Arguments.cs +++ b/src/EcmaScript.NET/Arguments.cs @@ -1,328 +1,328 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; - -using EcmaScript.NET.Types; - -namespace EcmaScript.NET -{ - - - /// This class implements the "arguments" object. - /// - /// See ECMA 10.1.8 - /// - /// - sealed class Arguments : IdScriptableObject - { - override public string ClassName - { - get - { - return "Arguments"; - } - - } - override protected internal int MaxInstanceId - { - get - { - return MAX_INSTANCE_ID; - } - - } - - - public Arguments (BuiltinCall activation) - { - this.activation = activation; - - IScriptable parent = activation.ParentScope; - ParentScope = parent; - SetPrototype (ScriptableObject.GetObjectPrototype (parent)); - - args = activation.originalArgs; - lengthObj = (int)args.Length; - - BuiltinFunction f = activation.function; - calleeObj = f; - - Context.Versions version = f.LanguageVersion; - if (version <= Context.Versions.JS1_3 && version != Context.Versions.Default) { - callerObj = null; - } - else { - callerObj = UniqueTag.NotFound; - } - } - - public override bool Has (int index, IScriptable start) - { - if (0 <= index && index < args.Length) { - if (args [index] != UniqueTag.NotFound) { - return true; - } - } - return base.Has (index, start); - } - - public override object Get (int index, IScriptable start) - { - if (0 <= index && index < args.Length) { - object value = args [index]; - if (value != UniqueTag.NotFound) { - if (sharedWithActivation (index)) { - BuiltinFunction f = activation.function; - string argName = f.getParamOrVarName (index); - value = activation.Get (argName, activation); - if (value == UniqueTag.NotFound) - Context.CodeBug (); - } - return value; - } - } - return base.Get (index, start); - } - - private bool sharedWithActivation (int index) - { - BuiltinFunction f = activation.function; - int definedCount = f.ParamCount; - if (index < definedCount) { - // Check if argument is not hidden by later argument with the same - // name as hidden arguments are not shared with activation - if (index < definedCount - 1) { - string argName = f.getParamOrVarName (index); - for (int i = index + 1; i < definedCount; i++) { - if (argName.Equals (f.getParamOrVarName (i))) { - return false; - } - } - } - return true; - } - return false; - } - - public override object Put (int index, IScriptable start, object value) - { - if (0 <= index && index < args.Length) { - if (args [index] != UniqueTag.NotFound) { - if (sharedWithActivation (index)) { - string argName; - argName = activation.function.getParamOrVarName (index); - return activation.Put (argName, activation, value); - } - lock (this) { - if (args [index] != UniqueTag.NotFound) { - if (args == activation.originalArgs) { - args = new object [args.Length]; - args.CopyTo (args, 0); - } - args [index] = value; - return value; - } - } - } - } - return base.Put (index, start, value); - } - - public override void Delete (int index) - { - if (0 <= index && index < args.Length) { - lock (this) { - if (args [index] != UniqueTag.NotFound) { - if (args == activation.originalArgs) { - args = new object [args.Length]; - args.CopyTo (args, 0); - } - args [index] = UniqueTag.NotFound; - return; - } - } - } - base.Delete (index); - } - - #region InstanceIds - private const int Id_callee = 1; - private const int Id_length = 2; - private const int Id_caller = 3; - private const int MAX_INSTANCE_ID = 3; - #endregion - - protected internal override int FindInstanceIdInfo (string s) - { - int id; - #region Generated InstanceId Switch - L0: { - id = 0; - string X = null; - int c; - if (s.Length == 6) { - c = s [5]; - if (c == 'e') { X = "callee"; id = Id_callee; } - else if (c == 'h') { X = "length"; id = Id_length; } - else if (c == 'r') { X = "caller"; id = Id_caller; } - } - if (X != null && X != s && !X.Equals (s)) - id = 0; - } - EL0: - #endregion - - if (id == 0) - return base.FindInstanceIdInfo (s); - - int attr; - switch (id) { - - case Id_callee: - case Id_caller: - case Id_length: - attr = DONTENUM; - break; - - default: - throw new ApplicationException (); - - } - return InstanceIdInfo (attr, id); - } - - // #/string_id_map# - - protected internal override string GetInstanceIdName (int id) - { - switch (id) { - - case Id_callee: - return "callee"; - - case Id_length: - return "length"; - - case Id_caller: - return "caller"; - } - return null; - } - - protected internal override object GetInstanceIdValue (int id) - { - switch (id) { - - case Id_callee: - return calleeObj; - - case Id_length: - return lengthObj; - - case Id_caller: { - object value = callerObj; - if (value == UniqueTag.NullValue) { - value = null; - } - else if (value == null) { - BuiltinCall caller = activation.parentActivationCall; - if (caller != null) { - value = caller.Get ("arguments", caller); - } - else { - value = null; - } - } - return value; - } - } - return base.GetInstanceIdValue (id); - } - - protected internal override void SetInstanceIdValue (int id, object value) - { - switch (id) { - - case Id_callee: - calleeObj = value; - return; - - case Id_length: - lengthObj = value; - return; - - case Id_caller: - callerObj = (value != null) ? value : UniqueTag.NullValue; - return; - } - base.SetInstanceIdValue (id, value); - } - - internal override object [] GetIds (bool getAll) - { - object [] ids = base.GetIds (getAll); - if (getAll && args.Length != 0) { - bool [] present = null; - int extraCount = args.Length; - for (int i = 0; i != ids.Length; ++i) { - object id = ids [i]; - if (id is int) { - int index = ((int)id); - if (0 <= index && index < args.Length) { - if (present == null) { - present = new bool [args.Length]; - } - if (!present [index]) { - present [index] = true; - extraCount--; - } - } - } - } - if (extraCount != 0) { - object [] tmp = new object [extraCount + ids.Length]; - Array.Copy (ids, 0, tmp, extraCount, ids.Length); - ids = tmp; - int offset = 0; - for (int i = 0; i != args.Length; ++i) { - if (present == null || !present [i]) { - ids [offset] = (int)i; - ++offset; - } - } - if (offset != extraCount) - Context.CodeBug (); - } - } - return ids; - } - - // Fields to hold caller, callee and length properties, - // where NOT_FOUND value tags deleted properties. - // In addition if callerObj == NullValue, it tags null for scripts, as - // initial callerObj == null means access to caller arguments available - // only in JS <= 1.3 scripts - private object callerObj; - private object calleeObj; - private object lengthObj; - - private BuiltinCall activation; - - // Initially args holds activation.getOriginalArgs(), but any modification - // of its elements triggers creation of a copy. If its element holds NOT_FOUND, - // it indicates deleted index, in which case super class is queried. - private object [] args; - - } -} +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; + +using EcmaScript.NET.Types; + +namespace EcmaScript.NET +{ + + + /// This class implements the "arguments" object. + /// + /// See ECMA 10.1.8 + /// + /// + sealed class Arguments : IdScriptableObject + { + override public string ClassName + { + get + { + return "Arguments"; + } + + } + override protected internal int MaxInstanceId + { + get + { + return MAX_INSTANCE_ID; + } + + } + + + public Arguments (BuiltinCall activation) + { + this.activation = activation; + + IScriptable parent = activation.ParentScope; + ParentScope = parent; + SetPrototype (ScriptableObject.GetObjectPrototype (parent)); + + args = activation.originalArgs; + lengthObj = (int)args.Length; + + BuiltinFunction f = activation.function; + calleeObj = f; + + Context.Versions version = f.LanguageVersion; + if (version <= Context.Versions.JS1_3 && version != Context.Versions.Default) { + callerObj = null; + } + else { + callerObj = UniqueTag.NotFound; + } + } + + public override bool Has (int index, IScriptable start) + { + if (0 <= index && index < args.Length) { + if (args [index] != UniqueTag.NotFound) { + return true; + } + } + return base.Has (index, start); + } + + public override object Get (int index, IScriptable start) + { + if (0 <= index && index < args.Length) { + object value = args [index]; + if (value != UniqueTag.NotFound) { + if (sharedWithActivation (index)) { + BuiltinFunction f = activation.function; + string argName = f.getParamOrVarName (index); + value = activation.Get (argName, activation); + if (value == UniqueTag.NotFound) + Context.CodeBug (); + } + return value; + } + } + return base.Get (index, start); + } + + private bool sharedWithActivation (int index) + { + BuiltinFunction f = activation.function; + int definedCount = f.ParamCount; + if (index < definedCount) { + // Check if argument is not hidden by later argument with the same + // name as hidden arguments are not shared with activation + if (index < definedCount - 1) { + string argName = f.getParamOrVarName (index); + for (int i = index + 1; i < definedCount; i++) { + if (argName.Equals (f.getParamOrVarName (i))) { + return false; + } + } + } + return true; + } + return false; + } + + public override object Put (int index, IScriptable start, object value) + { + if (0 <= index && index < args.Length) { + if (args [index] != UniqueTag.NotFound) { + if (sharedWithActivation (index)) { + string argName; + argName = activation.function.getParamOrVarName (index); + return activation.Put (argName, activation, value); + } + lock (this) { + if (args [index] != UniqueTag.NotFound) { + if (args == activation.originalArgs) { + args = new object [args.Length]; + args.CopyTo (args, 0); + } + args [index] = value; + return value; + } + } + } + } + return base.Put (index, start, value); + } + + public override void Delete (int index) + { + if (0 <= index && index < args.Length) { + lock (this) { + if (args [index] != UniqueTag.NotFound) { + if (args == activation.originalArgs) { + args = new object [args.Length]; + args.CopyTo (args, 0); + } + args [index] = UniqueTag.NotFound; + return; + } + } + } + base.Delete (index); + } + + #region InstanceIds + private const int Id_callee = 1; + private const int Id_length = 2; + private const int Id_caller = 3; + private const int MAX_INSTANCE_ID = 3; + #endregion + + protected internal override int FindInstanceIdInfo (string s) + { + int id; + #region Generated InstanceId Switch + L0: { + id = 0; + string X = null; + int c; + if (s.Length == 6) { + c = s [5]; + if (c == 'e') { X = "callee"; id = Id_callee; } + else if (c == 'h') { X = "length"; id = Id_length; } + else if (c == 'r') { X = "caller"; id = Id_caller; } + } + if (X != null && X != s && !X.Equals (s)) + id = 0; + } + EL0: + #endregion + + if (id == 0) + return base.FindInstanceIdInfo (s); + + int attr; + switch (id) { + + case Id_callee: + case Id_caller: + case Id_length: + attr = DONTENUM; + break; + + default: + throw new Exception (); + + } + return InstanceIdInfo (attr, id); + } + + // #/string_id_map# + + protected internal override string GetInstanceIdName (int id) + { + switch (id) { + + case Id_callee: + return "callee"; + + case Id_length: + return "length"; + + case Id_caller: + return "caller"; + } + return null; + } + + protected internal override object GetInstanceIdValue (int id) + { + switch (id) { + + case Id_callee: + return calleeObj; + + case Id_length: + return lengthObj; + + case Id_caller: { + object value = callerObj; + if (value == UniqueTag.NullValue) { + value = null; + } + else if (value == null) { + BuiltinCall caller = activation.parentActivationCall; + if (caller != null) { + value = caller.Get ("arguments", caller); + } + else { + value = null; + } + } + return value; + } + } + return base.GetInstanceIdValue (id); + } + + protected internal override void SetInstanceIdValue (int id, object value) + { + switch (id) { + + case Id_callee: + calleeObj = value; + return; + + case Id_length: + lengthObj = value; + return; + + case Id_caller: + callerObj = (value != null) ? value : UniqueTag.NullValue; + return; + } + base.SetInstanceIdValue (id, value); + } + + internal override object [] GetIds (bool getAll) + { + object [] ids = base.GetIds (getAll); + if (getAll && args.Length != 0) { + bool [] present = null; + int extraCount = args.Length; + for (int i = 0; i != ids.Length; ++i) { + object id = ids [i]; + if (id is int) { + int index = ((int)id); + if (0 <= index && index < args.Length) { + if (present == null) { + present = new bool [args.Length]; + } + if (!present [index]) { + present [index] = true; + extraCount--; + } + } + } + } + if (extraCount != 0) { + object [] tmp = new object [extraCount + ids.Length]; + Array.Copy (ids, 0, tmp, extraCount, ids.Length); + ids = tmp; + int offset = 0; + for (int i = 0; i != args.Length; ++i) { + if (present == null || !present [i]) { + ids [offset] = (int)i; + ++offset; + } + } + if (offset != extraCount) + Context.CodeBug (); + } + } + return ids; + } + + // Fields to hold caller, callee and length properties, + // where NOT_FOUND value tags deleted properties. + // In addition if callerObj == NullValue, it tags null for scripts, as + // initial callerObj == null means access to caller arguments available + // only in JS <= 1.3 scripts + private object callerObj; + private object calleeObj; + private object lengthObj; + + private BuiltinCall activation; + + // Initially args holds activation.getOriginalArgs(), but any modification + // of its elements triggers creation of a copy. If its element holds NOT_FOUND, + // it indicates deleted index, in which case super class is queried. + private object [] args; + + } +} diff --git a/Code/EcmaScript.NET/Attributes/EcmaScriptClassAttribute.cs b/src/EcmaScript.NET/Attributes/EcmaScriptClassAttribute.cs similarity index 96% rename from Code/EcmaScript.NET/Attributes/EcmaScriptClassAttribute.cs rename to src/EcmaScript.NET/Attributes/EcmaScriptClassAttribute.cs index 8007f71..b4e0f40 100644 --- a/Code/EcmaScript.NET/Attributes/EcmaScriptClassAttribute.cs +++ b/src/EcmaScript.NET/Attributes/EcmaScriptClassAttribute.cs @@ -1,40 +1,40 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; -using System.Text; - -namespace EcmaScript.NET.Attributes -{ - - [AttributeUsage (AttributeTargets.Class)] - public class EcmaScriptClassAttribute : Attribute - { - - private string m_Name = string.Empty; - - /// - /// Name of this class - /// - public string Name - { - get { return m_Name; } - } - - public EcmaScriptClassAttribute (string name) - { - m_Name = name; - } - - } -} +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; +using System.Text; + +namespace EcmaScript.NET.Attributes +{ + + [AttributeUsage (AttributeTargets.Class)] + public class EcmaScriptClassAttribute : Attribute + { + + private string m_Name = string.Empty; + + /// + /// Name of this class + /// + public string Name + { + get { return m_Name; } + } + + public EcmaScriptClassAttribute (string name) + { + m_Name = name; + } + + } +} diff --git a/Code/EcmaScript.NET/Attributes/EcmaScriptFunctionAttribute.cs b/src/EcmaScript.NET/Attributes/EcmaScriptFunctionAttribute.cs similarity index 96% rename from Code/EcmaScript.NET/Attributes/EcmaScriptFunctionAttribute.cs rename to src/EcmaScript.NET/Attributes/EcmaScriptFunctionAttribute.cs index 46bfb40..da7d5be 100644 --- a/Code/EcmaScript.NET/Attributes/EcmaScriptFunctionAttribute.cs +++ b/src/EcmaScript.NET/Attributes/EcmaScriptFunctionAttribute.cs @@ -1,50 +1,50 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; -using System.Reflection; -using System.Text; - -namespace EcmaScript.NET.Attributes -{ - - [AttributeUsage (AttributeTargets.Method - | AttributeTargets.Constructor)] - public class EcmaScriptFunctionAttribute : Attribute - { - - private string m_Name = string.Empty; - - /// - /// Name of this function - /// - public string Name - { - get { return m_Name; } - } - - public EcmaScriptFunctionAttribute () - : this (string.Empty) - { - ; - } - - public EcmaScriptFunctionAttribute (string name) - { - m_Name = name; - } - - internal MethodInfo MethodInfo; - - } -} +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; +using System.Reflection; +using System.Text; + +namespace EcmaScript.NET.Attributes +{ + + [AttributeUsage (AttributeTargets.Method + | AttributeTargets.Constructor)] + public class EcmaScriptFunctionAttribute : Attribute + { + + private string m_Name = string.Empty; + + /// + /// Name of this function + /// + public string Name + { + get { return m_Name; } + } + + public EcmaScriptFunctionAttribute () + : this (string.Empty) + { + ; + } + + public EcmaScriptFunctionAttribute (string name) + { + m_Name = name; + } + + internal MethodInfo MethodInfo; + + } +} diff --git a/Code/EcmaScript.NET/Attributes/EcmaScriptPropertyAccess.cs b/src/EcmaScript.NET/Attributes/EcmaScriptPropertyAccess.cs similarity index 96% rename from Code/EcmaScript.NET/Attributes/EcmaScriptPropertyAccess.cs rename to src/EcmaScript.NET/Attributes/EcmaScriptPropertyAccess.cs index ea32e0c..09fd6a1 100644 --- a/Code/EcmaScript.NET/Attributes/EcmaScriptPropertyAccess.cs +++ b/src/EcmaScript.NET/Attributes/EcmaScriptPropertyAccess.cs @@ -1,26 +1,26 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; - -namespace EcmaScript.NET.Attributes -{ - - public enum EcmaScriptPropertyAccess - { - AsDeclared = 0, - Get = 1, - Set = 2 - } - -} +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; + +namespace EcmaScript.NET.Attributes +{ + + public enum EcmaScriptPropertyAccess + { + AsDeclared = 0, + Get = 1, + Set = 2 + } + +} diff --git a/Code/EcmaScript.NET/Attributes/EcmaScriptPropertyAttribute.cs b/src/EcmaScript.NET/Attributes/EcmaScriptPropertyAttribute.cs similarity index 96% rename from Code/EcmaScript.NET/Attributes/EcmaScriptPropertyAttribute.cs rename to src/EcmaScript.NET/Attributes/EcmaScriptPropertyAttribute.cs index 9944015..24704d4 100644 --- a/Code/EcmaScript.NET/Attributes/EcmaScriptPropertyAttribute.cs +++ b/src/EcmaScript.NET/Attributes/EcmaScriptPropertyAttribute.cs @@ -1,59 +1,59 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - - -using System; -using System.Text; - -namespace EcmaScript.NET.Attributes -{ - - [AttributeUsage (AttributeTargets.Property - | AttributeTargets.Field)] - public class EcmaScriptPropertyAttribute : Attribute - { - - private string m_Name = string.Empty; - - /// - /// Name of this property - /// - public string Name - { - get { return m_Name; } - } - - private EcmaScriptPropertyAccess m_Access = EcmaScriptPropertyAccess.AsDeclared; - - /// - /// Access - /// - public EcmaScriptPropertyAccess Access - { - get { return m_Access; } - } - - public EcmaScriptPropertyAttribute (string name) - { - m_Name = name; - } - - public EcmaScriptPropertyAttribute (string name, EcmaScriptPropertyAccess access) - { - m_Name = name; - m_Access = access; - } - - } - -} +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + + +using System; +using System.Text; + +namespace EcmaScript.NET.Attributes +{ + + [AttributeUsage (AttributeTargets.Property + | AttributeTargets.Field)] + public class EcmaScriptPropertyAttribute : Attribute + { + + private string m_Name = string.Empty; + + /// + /// Name of this property + /// + public string Name + { + get { return m_Name; } + } + + private EcmaScriptPropertyAccess m_Access = EcmaScriptPropertyAccess.AsDeclared; + + /// + /// Access + /// + public EcmaScriptPropertyAccess Access + { + get { return m_Access; } + } + + public EcmaScriptPropertyAttribute (string name) + { + m_Name = name; + } + + public EcmaScriptPropertyAttribute (string name, EcmaScriptPropertyAccess access) + { + m_Name = name; + m_Access = access; + } + + } + +} diff --git a/Code/EcmaScript.NET/BaseFunction.cs b/src/EcmaScript.NET/BaseFunction.cs similarity index 96% rename from Code/EcmaScript.NET/BaseFunction.cs rename to src/EcmaScript.NET/BaseFunction.cs index ad364c9..4a37579 100644 --- a/Code/EcmaScript.NET/BaseFunction.cs +++ b/src/EcmaScript.NET/BaseFunction.cs @@ -1,602 +1,602 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; - -using EcmaScript.NET.Types; - -namespace EcmaScript.NET -{ - - /// The base class for Function objects - /// See ECMA 15.3. - /// - public class BaseFunction : IdScriptableObject, IFunction - { - override public string ClassName - { - get - { - return "Function"; - } - - } - override protected internal int MaxInstanceId - { - get - { - return MAX_INSTANCE_ID; - } - - } - /// Make value as DontEnum, DontDelete, ReadOnly - /// prototype property of this Function object - /// - virtual public object ImmunePrototypeProperty - { - set - { - if (isPrototypePropertyImmune) { - throw new ApplicationException (); - } - prototypeProperty = (value != null) ? value : UniqueTag.NullValue; - isPrototypePropertyImmune = true; - } - - } - virtual public int Arity - { - get - { - return 0; - } - - } - virtual public int Length - { - get - { - return 0; - } - - } - virtual public string FunctionName - { - get - { - return ""; - } - - } - virtual internal object PrototypeProperty - { - get - { - object result = prototypeProperty; - if (result == null) { - lock (this) { - result = prototypeProperty; - if (result == null) { - SetupDefaultPrototype (); - result = prototypeProperty; - } - } - } - else if (result == UniqueTag.NullValue) { - result = null; - } - return result; - } - - } - private object Arguments - { - get - { - // .arguments is deprecated, so we use a slow - // way of getting it that doesn't add to the invocation cost. - // TODO: add warning, error based on version - object value = DefaultGet ("arguments"); - if (value != UniqueTag.NotFound) { - // Should after changing .arguments its - // activation still be available during Function call? - // This code assumes it should not: - // defaultGet("arguments") != NOT_FOUND - // means assigned arguments - return value; - } - Context cx = Context.CurrentContext; - BuiltinCall activation = ScriptRuntime.findFunctionActivation (cx, this); - return (activation == null) ? null : activation.Get ("arguments", activation); - } - - } - - private static readonly object FUNCTION_TAG = new object (); - - internal static void Init (IScriptable scope, bool zealed) - { - BaseFunction obj = new BaseFunction (); - obj.isPrototypePropertyImmune = true; - obj.ExportAsJSClass (MAX_PROTOTYPE_ID, scope, zealed); - } - - public BaseFunction () - { - } - - public BaseFunction (IScriptable scope, IScriptable prototype) - : base (scope, prototype) - { - } - - /// Implements the instanceof operator for JavaScript Function objects. - ///

- /// - /// foo = new Foo();
- /// foo instanceof Foo; // true
- ///
- /// - ///

- /// The value that appeared on the LHS of the instanceof - /// operator - /// - /// true if the "prototype" property of "this" appears in - /// value's prototype chain - /// - /// - public override bool HasInstance (IScriptable instance) - { - object protoProp = ScriptableObject.GetProperty (this, "prototype"); - if (protoProp is IScriptable) { - return ScriptRuntime.jsDelegatesTo (instance, (IScriptable)protoProp); - } - throw ScriptRuntime.TypeErrorById ("msg.instanceof.bad.prototype", FunctionName); - } - - - #region InstanceIds - private const int Id_length = 1; - private const int Id_arity = 2; - private const int Id_name = 3; - private const int Id_prototype = 4; - private const int Id_arguments = 5; - private const int MAX_INSTANCE_ID = 5; - #endregion - - protected internal override int FindInstanceIdInfo (string s) - { - int id; - #region Generated InstanceId Switch - L0: { - id = 0; - string X = null; - int c; - L: - switch (s.Length) { - case 4: - X = "name"; - id = Id_name; - break; - case 5: - X = "arity"; - id = Id_arity; - break; - case 6: - X = "length"; - id = Id_length; - break; - case 9: - c = s [0]; - if (c == 'a') { X = "arguments"; id = Id_arguments; } - else if (c == 'p') { X = "prototype"; id = Id_prototype; } - break; - } - if (X != null && X != s && !X.Equals (s)) - id = 0; - } - EL0: - - #endregion - - if (id == 0) - return base.FindInstanceIdInfo (s); - - int attr; - switch (id) { - - case Id_length: - case Id_arity: - case Id_name: - attr = DONTENUM | READONLY | PERMANENT; - break; - - case Id_prototype: - // As of ECMA 15.3.2.1 prototype will be PERMANENT if it's not with "Function" - attr = (isPrototypePropertyImmune) ? DONTENUM | READONLY | PERMANENT : PERMANENT; - break; - - case Id_arguments: - attr = DONTENUM | PERMANENT; - break; - - default: - throw new ApplicationException (); - - } - return InstanceIdInfo (attr, id); - } - - protected internal override string GetInstanceIdName (int id) - { - switch (id) { - - case Id_length: - return "length"; - case Id_arity: - return "arity"; - case Id_name: - return "name"; - case Id_prototype: - return "prototype"; - case Id_arguments: - return "arguments"; - } - return base.GetInstanceIdName (id); - } - - - protected internal override object GetInstanceIdValue (int id) - { - switch (id) { - case Id_length: - return Length; - case Id_arity: - return Arity; - case Id_name: - return FunctionName; - case Id_prototype: - return PrototypeProperty; - case Id_arguments: - return Arguments; - } - return base.GetInstanceIdValue (id); - } - - protected internal override void SetInstanceIdValue (int id, object value) - { - if (id == Id_prototype) { - if (!isPrototypePropertyImmune) { - prototypeProperty = (value != null) ? value : UniqueTag.NullValue; - } - return; - } - else if (id == Id_arguments) { - if (value == UniqueTag.NotFound) { - // This should not be called since "arguments" is PERMANENT - Context.CodeBug (); - } - DefaultPut ("arguments", value); - } - base.SetInstanceIdValue (id, value); - } - - protected internal override void FillConstructorProperties (IdFunctionObject ctor) - { - // Fix up bootstrapping problem: getPrototype of the IdFunctionObject - // can not return Function.prototype because Function object is not - // yet defined. - ctor.SetPrototype (this); - base.FillConstructorProperties (ctor); - } - - protected internal override void InitPrototypeId (int id) - { - string s; - int arity; - switch (id) { - - case Id_constructor: - arity = 1; - s = "constructor"; - break; - - case Id_toString: - arity = 1; - s = "toString"; - break; - - case Id_toSource: - arity = 1; - s = "toSource"; - break; - - case Id_apply: - arity = 2; - s = "apply"; - break; - - case Id_call: - arity = 1; - s = "call"; - break; - - default: - throw new ArgumentException (Convert.ToString (id)); - - } - InitPrototypeMethod (FUNCTION_TAG, id, s, arity); - } - - public override object ExecIdCall (IdFunctionObject f, Context cx, IScriptable scope, IScriptable thisObj, object [] args) - { - if (!f.HasTag (FUNCTION_TAG)) { - return base.ExecIdCall (f, cx, scope, thisObj, args); - } - int id = f.MethodId; - switch (id) { - - case Id_constructor: - return JsConstructor (cx, scope, args); - - - case Id_toString: { - BaseFunction realf = RealFunction (thisObj, f); - int indent = ScriptConvert.ToInt32 (args, 0); - return realf.Decompile (indent, Decompiler.TO_STRING_FLAG); - } - - - case Id_toSource: { - BaseFunction realf = RealFunction (thisObj, f); - int indent = 0; - int flags = Decompiler.TO_SOURCE_FLAG; - if (args.Length != 0) { - indent = ScriptConvert.ToInt32 (args [0]); - if (indent >= 0) { - flags = 0; - } - else { - indent = 0; - } - } - return realf.Decompile (indent, flags); - } - - - case Id_apply: - case Id_call: - return ScriptRuntime.applyOrCall (id == Id_apply, cx, scope, thisObj, args); - } - throw new ArgumentException (Convert.ToString (id)); - } - - private BaseFunction RealFunction (IScriptable thisObj, IdFunctionObject f) - { - object x = thisObj.GetDefaultValue (typeof (IFunction)); - if (x is BaseFunction) { - return (BaseFunction)x; - } - throw ScriptRuntime.TypeErrorById ("msg.incompat.call", f.FunctionName); - } - - protected internal virtual IScriptable GetClassPrototype() - { - object protoVal = PrototypeProperty; - if (protoVal is IScriptable) { - return (IScriptable)protoVal; - } - return getClassPrototype (this, "Object"); - } - - /// Should be overridden. - public virtual object Call (Context cx, IScriptable scope, IScriptable thisObj, object [] args) - { - return Undefined.Value; - } - - public virtual IScriptable Construct (Context cx, IScriptable scope, object [] args) - { - IScriptable result = CreateObject (cx, scope); - if (result != null) { - object val = Call (cx, scope, result, args); - if (val is IScriptable) { - result = (IScriptable)val; - } - } - else { - object val = Call (cx, scope, null, args); - if (!(val is IScriptable)) { - // It is program error not to return Scriptable from - // the call method if createObject returns null. - throw new ApplicationException ("Bad implementaion of call as constructor, name=" + FunctionName + " in " + GetType ().FullName); - } - result = (IScriptable)val; - if (result.GetPrototype () == null) { - result.SetPrototype (GetClassPrototype ()); - } - if (result.ParentScope == null) { - IScriptable parent = ParentScope; - if (result != parent) { - result.ParentScope = parent; - } - } - } - return result; - } - - /// Creates new script object. - /// The default implementation of {@link #construct} uses the method to - /// to get the value for thisObj argument when invoking - /// {@link #call}. - /// The methos is allowed to return null to indicate that - /// {@link #call} will create a new object itself. In this case - /// {@link #construct} will set scope and prototype on the result - /// {@link #call} unless they are already set. - /// - public virtual IScriptable CreateObject (Context cx, IScriptable scope) - { - IScriptable newInstance = new BuiltinObject (); - newInstance.SetPrototype (GetClassPrototype ()); - newInstance.ParentScope = ParentScope; - return newInstance; - } - - /// Decompile the source information associated with this js - /// function/script back into a string. - /// - /// - /// How much to indent the decompiled result. - /// - /// - /// Flags specifying format of decompilation output. - /// - internal virtual string Decompile (int indent, int flags) - { - System.Text.StringBuilder sb = new System.Text.StringBuilder (); - bool justbody = (0 != (flags & Decompiler.ONLY_BODY_FLAG)); - if (!justbody) { - sb.Append ("function "); - sb.Append (FunctionName); - sb.Append ("() {\n\t"); - } - sb.Append ("[native code, arity="); - sb.Append (Arity); - sb.Append ("]\n"); - if (!justbody) { - sb.Append ("}\n"); - } - return sb.ToString (); - } - - private void SetupDefaultPrototype () - { - BuiltinObject obj = new BuiltinObject (); - obj.DefineProperty ("constructor", this, ScriptableObject.DONTENUM); - // put the prototype property into the object now, then in the - // wacky case of a user defining a function Object(), we don't - // get an infinite loop trying to find the prototype. - prototypeProperty = obj; - IScriptable proto = GetObjectPrototype (this); - if (proto != obj) { - // not the one we just made, it must remain grounded - obj.SetPrototype (proto); - } - } - - private static object JsConstructor (Context cx, IScriptable scope, object [] args) - { - int arglen = args.Length; - System.Text.StringBuilder sourceBuf = new System.Text.StringBuilder (); - - sourceBuf.Append ("function "); - /* version != 1.2 Function constructor behavior - - * print 'anonymous' as the function name if the - * version (under which the function was compiled) is - * less than 1.2... or if it's greater than 1.2, because - * we need to be closer to ECMA. - */ - if (cx.Version != Context.Versions.JS1_2) { - sourceBuf.Append ("anonymous"); - } - sourceBuf.Append ('('); - - // Append arguments as coma separated strings - for (int i = 0; i < arglen - 1; i++) { - if (i > 0) { - sourceBuf.Append (','); - } - sourceBuf.Append (ScriptConvert.ToString (args [i])); - } - sourceBuf.Append (") {"); - if (arglen != 0) { - // append function body - string funBody = ScriptConvert.ToString (args [arglen - 1]); - sourceBuf.Append (funBody); - } - sourceBuf.Append ('}'); - string source = sourceBuf.ToString (); - - int [] linep = new int [1]; - string filename = Context.GetSourcePositionFromStack (linep); - if (filename == null) { - filename = ""; - linep [0] = 1; - } - - string sourceURI = ScriptRuntime.makeUrlForGeneratedScript (false, filename, linep [0]); - - IScriptable global = ScriptableObject.GetTopLevelScope (scope); - - ErrorReporter reporter; - reporter = DefaultErrorReporter.ForEval (cx.ErrorReporter); - - // Compile with explicit interpreter instance to force interpreter - // mode. - return cx.CompileFunction (global, source, new Interpreter (), reporter, sourceURI, 1, (object)null); - } - - #region PrototypeIds - private const int Id_constructor = 1; - private const int Id_toString = 2; - private const int Id_toSource = 3; - private const int Id_apply = 4; - private const int Id_call = 5; - private const int MAX_PROTOTYPE_ID = 5; - #endregion - - protected internal override int FindPrototypeId (string s) - { - int id; - #region Generated PrototypeId Switch - L0: { - id = 0; - string X = null; - int c; - L: - switch (s.Length) { - case 4: - X = "call"; - id = Id_call; - break; - case 5: - X = "apply"; - id = Id_apply; - break; - case 8: - c = s [3]; - if (c == 'o') { X = "toSource"; id = Id_toSource; } - else if (c == 't') { X = "toString"; id = Id_toString; } - break; - case 11: - X = "constructor"; - id = Id_constructor; - break; - } - if (X != null && X != s && !X.Equals (s)) - id = 0; - } - EL0: - - #endregion - return id; - } - - - private object prototypeProperty; - private bool isPrototypePropertyImmune; - - - } -} - +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; + +using EcmaScript.NET.Types; + +namespace EcmaScript.NET +{ + + /// The base class for Function objects + /// See ECMA 15.3. + /// + public class BaseFunction : IdScriptableObject, IFunction + { + override public string ClassName + { + get + { + return "Function"; + } + + } + override protected internal int MaxInstanceId + { + get + { + return MAX_INSTANCE_ID; + } + + } + /// Make value as DontEnum, DontDelete, ReadOnly + /// prototype property of this Function object + /// + virtual public object ImmunePrototypeProperty + { + set + { + if (isPrototypePropertyImmune) { + throw new Exception (); + } + prototypeProperty = (value != null) ? value : UniqueTag.NullValue; + isPrototypePropertyImmune = true; + } + + } + virtual public int Arity + { + get + { + return 0; + } + + } + virtual public int Length + { + get + { + return 0; + } + + } + virtual public string FunctionName + { + get + { + return ""; + } + + } + virtual internal object PrototypeProperty + { + get + { + object result = prototypeProperty; + if (result == null) { + lock (this) { + result = prototypeProperty; + if (result == null) { + SetupDefaultPrototype (); + result = prototypeProperty; + } + } + } + else if (result == UniqueTag.NullValue) { + result = null; + } + return result; + } + + } + private object Arguments + { + get + { + // .arguments is deprecated, so we use a slow + // way of getting it that doesn't add to the invocation cost. + // TODO: add warning, error based on version + object value = DefaultGet ("arguments"); + if (value != UniqueTag.NotFound) { + // Should after changing .arguments its + // activation still be available during Function call? + // This code assumes it should not: + // defaultGet("arguments") != NOT_FOUND + // means assigned arguments + return value; + } + Context cx = Context.CurrentContext; + BuiltinCall activation = ScriptRuntime.findFunctionActivation (cx, this); + return (activation == null) ? null : activation.Get ("arguments", activation); + } + + } + + private static readonly object FUNCTION_TAG = new object (); + + internal static void Init (IScriptable scope, bool zealed) + { + BaseFunction obj = new BaseFunction (); + obj.isPrototypePropertyImmune = true; + obj.ExportAsJSClass (MAX_PROTOTYPE_ID, scope, zealed); + } + + public BaseFunction () + { + } + + public BaseFunction (IScriptable scope, IScriptable prototype) + : base (scope, prototype) + { + } + + /// Implements the instanceof operator for JavaScript Function objects. + ///

+ /// + /// foo = new Foo();
+ /// foo instanceof Foo; // true
+ ///
+ /// + ///

+ /// The value that appeared on the LHS of the instanceof + /// operator + /// + /// true if the "prototype" property of "this" appears in + /// value's prototype chain + /// + /// + public override bool HasInstance (IScriptable instance) + { + object protoProp = ScriptableObject.GetProperty (this, "prototype"); + if (protoProp is IScriptable) { + return ScriptRuntime.jsDelegatesTo (instance, (IScriptable)protoProp); + } + throw ScriptRuntime.TypeErrorById ("msg.instanceof.bad.prototype", FunctionName); + } + + + #region InstanceIds + private const int Id_length = 1; + private const int Id_arity = 2; + private const int Id_name = 3; + private const int Id_prototype = 4; + private const int Id_arguments = 5; + private const int MAX_INSTANCE_ID = 5; + #endregion + + protected internal override int FindInstanceIdInfo (string s) + { + int id; + #region Generated InstanceId Switch + L0: { + id = 0; + string X = null; + int c; + L: + switch (s.Length) { + case 4: + X = "name"; + id = Id_name; + break; + case 5: + X = "arity"; + id = Id_arity; + break; + case 6: + X = "length"; + id = Id_length; + break; + case 9: + c = s [0]; + if (c == 'a') { X = "arguments"; id = Id_arguments; } + else if (c == 'p') { X = "prototype"; id = Id_prototype; } + break; + } + if (X != null && X != s && !X.Equals (s)) + id = 0; + } + EL0: + + #endregion + + if (id == 0) + return base.FindInstanceIdInfo (s); + + int attr; + switch (id) { + + case Id_length: + case Id_arity: + case Id_name: + attr = DONTENUM | READONLY | PERMANENT; + break; + + case Id_prototype: + // As of ECMA 15.3.2.1 prototype will be PERMANENT if it's not with "Function" + attr = (isPrototypePropertyImmune) ? DONTENUM | READONLY | PERMANENT : PERMANENT; + break; + + case Id_arguments: + attr = DONTENUM | PERMANENT; + break; + + default: + throw new Exception (); + + } + return InstanceIdInfo (attr, id); + } + + protected internal override string GetInstanceIdName (int id) + { + switch (id) { + + case Id_length: + return "length"; + case Id_arity: + return "arity"; + case Id_name: + return "name"; + case Id_prototype: + return "prototype"; + case Id_arguments: + return "arguments"; + } + return base.GetInstanceIdName (id); + } + + + protected internal override object GetInstanceIdValue (int id) + { + switch (id) { + case Id_length: + return Length; + case Id_arity: + return Arity; + case Id_name: + return FunctionName; + case Id_prototype: + return PrototypeProperty; + case Id_arguments: + return Arguments; + } + return base.GetInstanceIdValue (id); + } + + protected internal override void SetInstanceIdValue (int id, object value) + { + if (id == Id_prototype) { + if (!isPrototypePropertyImmune) { + prototypeProperty = (value != null) ? value : UniqueTag.NullValue; + } + return; + } + else if (id == Id_arguments) { + if (value == UniqueTag.NotFound) { + // This should not be called since "arguments" is PERMANENT + Context.CodeBug (); + } + DefaultPut ("arguments", value); + } + base.SetInstanceIdValue (id, value); + } + + protected internal override void FillConstructorProperties (IdFunctionObject ctor) + { + // Fix up bootstrapping problem: getPrototype of the IdFunctionObject + // can not return Function.prototype because Function object is not + // yet defined. + ctor.SetPrototype (this); + base.FillConstructorProperties (ctor); + } + + protected internal override void InitPrototypeId (int id) + { + string s; + int arity; + switch (id) { + + case Id_constructor: + arity = 1; + s = "constructor"; + break; + + case Id_toString: + arity = 1; + s = "toString"; + break; + + case Id_toSource: + arity = 1; + s = "toSource"; + break; + + case Id_apply: + arity = 2; + s = "apply"; + break; + + case Id_call: + arity = 1; + s = "call"; + break; + + default: + throw new ArgumentException (Convert.ToString (id)); + + } + InitPrototypeMethod (FUNCTION_TAG, id, s, arity); + } + + public override object ExecIdCall (IdFunctionObject f, Context cx, IScriptable scope, IScriptable thisObj, object [] args) + { + if (!f.HasTag (FUNCTION_TAG)) { + return base.ExecIdCall (f, cx, scope, thisObj, args); + } + int id = f.MethodId; + switch (id) { + + case Id_constructor: + return JsConstructor (cx, scope, args); + + + case Id_toString: { + BaseFunction realf = RealFunction (thisObj, f); + int indent = ScriptConvert.ToInt32 (args, 0); + return realf.Decompile (indent, Decompiler.TO_STRING_FLAG); + } + + + case Id_toSource: { + BaseFunction realf = RealFunction (thisObj, f); + int indent = 0; + int flags = Decompiler.TO_SOURCE_FLAG; + if (args.Length != 0) { + indent = ScriptConvert.ToInt32 (args [0]); + if (indent >= 0) { + flags = 0; + } + else { + indent = 0; + } + } + return realf.Decompile (indent, flags); + } + + + case Id_apply: + case Id_call: + return ScriptRuntime.applyOrCall (id == Id_apply, cx, scope, thisObj, args); + } + throw new ArgumentException (Convert.ToString (id)); + } + + private BaseFunction RealFunction (IScriptable thisObj, IdFunctionObject f) + { + object x = thisObj.GetDefaultValue (typeof (IFunction)); + if (x is BaseFunction) { + return (BaseFunction)x; + } + throw ScriptRuntime.TypeErrorById ("msg.incompat.call", f.FunctionName); + } + + protected internal virtual IScriptable GetClassPrototype() + { + object protoVal = PrototypeProperty; + if (protoVal is IScriptable) { + return (IScriptable)protoVal; + } + return getClassPrototype (this, "Object"); + } + + /// Should be overridden. + public virtual object Call (Context cx, IScriptable scope, IScriptable thisObj, object [] args) + { + return Undefined.Value; + } + + public virtual IScriptable Construct (Context cx, IScriptable scope, object [] args) + { + IScriptable result = CreateObject (cx, scope); + if (result != null) { + object val = Call (cx, scope, result, args); + if (val is IScriptable) { + result = (IScriptable)val; + } + } + else { + object val = Call (cx, scope, null, args); + if (!(val is IScriptable)) { + // It is program error not to return Scriptable from + // the call method if createObject returns null. + throw new Exception ("Bad implementaion of call as constructor, name=" + FunctionName + " in " + GetType ().FullName); + } + result = (IScriptable)val; + if (result.GetPrototype () == null) { + result.SetPrototype (GetClassPrototype ()); + } + if (result.ParentScope == null) { + IScriptable parent = ParentScope; + if (result != parent) { + result.ParentScope = parent; + } + } + } + return result; + } + + /// Creates new script object. + /// The default implementation of {@link #construct} uses the method to + /// to get the value for thisObj argument when invoking + /// {@link #call}. + /// The methos is allowed to return null to indicate that + /// {@link #call} will create a new object itself. In this case + /// {@link #construct} will set scope and prototype on the result + /// {@link #call} unless they are already set. + /// + public virtual IScriptable CreateObject (Context cx, IScriptable scope) + { + IScriptable newInstance = new BuiltinObject (); + newInstance.SetPrototype (GetClassPrototype ()); + newInstance.ParentScope = ParentScope; + return newInstance; + } + + /// Decompile the source information associated with this js + /// function/script back into a string. + /// + /// + /// How much to indent the decompiled result. + /// + /// + /// Flags specifying format of decompilation output. + /// + internal virtual string Decompile (int indent, int flags) + { + System.Text.StringBuilder sb = new System.Text.StringBuilder (); + bool justbody = (0 != (flags & Decompiler.ONLY_BODY_FLAG)); + if (!justbody) { + sb.Append ("function "); + sb.Append (FunctionName); + sb.Append ("() {\n\t"); + } + sb.Append ("[native code, arity="); + sb.Append (Arity); + sb.Append ("]\n"); + if (!justbody) { + sb.Append ("}\n"); + } + return sb.ToString (); + } + + private void SetupDefaultPrototype () + { + BuiltinObject obj = new BuiltinObject (); + obj.DefineProperty ("constructor", this, ScriptableObject.DONTENUM); + // put the prototype property into the object now, then in the + // wacky case of a user defining a function Object(), we don't + // get an infinite loop trying to find the prototype. + prototypeProperty = obj; + IScriptable proto = GetObjectPrototype (this); + if (proto != obj) { + // not the one we just made, it must remain grounded + obj.SetPrototype (proto); + } + } + + private static object JsConstructor (Context cx, IScriptable scope, object [] args) + { + int arglen = args.Length; + System.Text.StringBuilder sourceBuf = new System.Text.StringBuilder (); + + sourceBuf.Append ("function "); + /* version != 1.2 Function constructor behavior - + * print 'anonymous' as the function name if the + * version (under which the function was compiled) is + * less than 1.2... or if it's greater than 1.2, because + * we need to be closer to ECMA. + */ + if (cx.Version != Context.Versions.JS1_2) { + sourceBuf.Append ("anonymous"); + } + sourceBuf.Append ('('); + + // Append arguments as coma separated strings + for (int i = 0; i < arglen - 1; i++) { + if (i > 0) { + sourceBuf.Append (','); + } + sourceBuf.Append (ScriptConvert.ToString (args [i])); + } + sourceBuf.Append (") {"); + if (arglen != 0) { + // append function body + string funBody = ScriptConvert.ToString (args [arglen - 1]); + sourceBuf.Append (funBody); + } + sourceBuf.Append ('}'); + string source = sourceBuf.ToString (); + + int [] linep = new int [1]; + string filename = Context.GetSourcePositionFromStack (linep); + if (filename == null) { + filename = ""; + linep [0] = 1; + } + + string sourceURI = ScriptRuntime.makeUrlForGeneratedScript (false, filename, linep [0]); + + IScriptable global = ScriptableObject.GetTopLevelScope (scope); + + ErrorReporter reporter; + reporter = DefaultErrorReporter.ForEval (cx.ErrorReporter); + + // Compile with explicit interpreter instance to force interpreter + // mode. + return cx.CompileFunction (global, source, new Interpreter (), reporter, sourceURI, 1, (object)null); + } + + #region PrototypeIds + private const int Id_constructor = 1; + private const int Id_toString = 2; + private const int Id_toSource = 3; + private const int Id_apply = 4; + private const int Id_call = 5; + private const int MAX_PROTOTYPE_ID = 5; + #endregion + + protected internal override int FindPrototypeId (string s) + { + int id; + #region Generated PrototypeId Switch + L0: { + id = 0; + string X = null; + int c; + L: + switch (s.Length) { + case 4: + X = "call"; + id = Id_call; + break; + case 5: + X = "apply"; + id = Id_apply; + break; + case 8: + c = s [3]; + if (c == 'o') { X = "toSource"; id = Id_toSource; } + else if (c == 't') { X = "toString"; id = Id_toString; } + break; + case 11: + X = "constructor"; + id = Id_constructor; + break; + } + if (X != null && X != s && !X.Equals (s)) + id = 0; + } + EL0: + + #endregion + return id; + } + + + private object prototypeProperty; + private bool isPrototypePropertyImmune; + + + } +} + diff --git a/Code/EcmaScript.NET/Collections/ObjArray.cs b/src/EcmaScript.NET/Collections/ObjArray.cs similarity index 94% rename from Code/EcmaScript.NET/Collections/ObjArray.cs rename to src/EcmaScript.NET/Collections/ObjArray.cs index 6b94dea..b8d0057 100644 --- a/Code/EcmaScript.NET/Collections/ObjArray.cs +++ b/src/EcmaScript.NET/Collections/ObjArray.cs @@ -1,501 +1,501 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; -using System.Runtime.InteropServices; - -namespace EcmaScript.NET.Collections -{ - - /// Implementation of resizable array with focus on minimizing memory usage by storing few initial array elements in object fields. Can also be used as a stack. - - - public class ObjArray - { - virtual public bool Sealed - { - get - { - return zealed; - } - - } - virtual public bool Empty - { - get - { - return m_Size == 0; - } - - } - virtual public int Size - { - set - { - if (value < 0) - throw new ArgumentException (); - if (zealed) - throw onSeledMutation (); - int N = m_Size; - if (value < N) { - for (int i = value; i != N; ++i) { - SetImpl (i, (object)null); - } - } - else if (value > N) { - if (value > FIELDS_STORE_SIZE) { - ensureCapacity (value); - } - } - m_Size = value; - } - - } - - public ObjArray () - { - } - - public void seal () - { - zealed = true; - } - - public int size () - { - return m_Size; - } - - public object Get (int index) - { - if (!(0 <= index && index < m_Size)) - throw onInvalidIndex (index, m_Size); - return GetImpl (index); - } - - public void Set (int index, object value) - { - if (!(0 <= index && index < m_Size)) - throw onInvalidIndex (index, m_Size); - if (zealed) - throw onSeledMutation (); - SetImpl (index, value); - } - - private object GetImpl (int index) - { - switch (index) { - - case 0: - return f0; - - case 1: - return f1; - - case 2: - return f2; - - case 3: - return f3; - - case 4: - return f4; - } - return data [index - FIELDS_STORE_SIZE]; - } - - private void SetImpl (int index, object value) - { - switch (index) { - - case 0: - f0 = value; - break; - - case 1: - f1 = value; - break; - - case 2: - f2 = value; - break; - - case 3: - f3 = value; - break; - - case 4: - f4 = value; - break; - - default: - data [index - FIELDS_STORE_SIZE] = value; - break; - - } - } - - public virtual int indexOf (object obj) - { - int N = m_Size; - for (int i = 0; i != N; ++i) { - object current = GetImpl (i); - if (current == obj || (current != null && current.Equals (obj))) { - return i; - } - } - return -1; - } - - public virtual int lastIndexOf (object obj) - { - for (int i = m_Size; i != 0; ) { - --i; - object current = GetImpl (i); - if (current == obj || (current != null && current.Equals (obj))) { - return i; - } - } - return -1; - } - - public object peek () - { - int N = m_Size; - if (N == 0) - throw onEmptyStackTopRead (); - return GetImpl (N - 1); - } - - public object pop () - { - if (zealed) - throw onSeledMutation (); - int N = m_Size; - --N; - object top; - switch (N) { - - case -1: - throw onEmptyStackTopRead (); - - case 0: - top = f0; - f0 = null; - break; - - case 1: - top = f1; - f1 = null; - break; - - case 2: - top = f2; - f2 = null; - break; - - case 3: - top = f3; - f3 = null; - break; - - case 4: - top = f4; - f4 = null; - break; - - default: - top = data [N - FIELDS_STORE_SIZE]; - data [N - FIELDS_STORE_SIZE] = null; - break; - - } - m_Size = N; - return top; - } - - public void push (object value) - { - add (value); - } - - public void add (object value) - { - if (zealed) - throw onSeledMutation (); - int N = m_Size; - if (N >= FIELDS_STORE_SIZE) { - ensureCapacity (N + 1); - } - m_Size = N + 1; - SetImpl (N, value); - } - - public void add (int index, object value) - { - int N = m_Size; - if (!(0 <= index && index <= N)) - throw onInvalidIndex (index, N + 1); - if (zealed) - throw onSeledMutation (); - object tmp; - switch (index) { - - case 0: - if (N == 0) { - f0 = value; - break; - } - tmp = f0; - f0 = value; - value = tmp; - goto case 1; - - case 1: - if (N == 1) { - f1 = value; - break; - } - tmp = f1; - f1 = value; - value = tmp; - goto case 2; - - case 2: - if (N == 2) { - f2 = value; - break; - } - tmp = f2; - f2 = value; - value = tmp; - goto case 3; - - case 3: - if (N == 3) { - f3 = value; - break; - } - tmp = f3; - f3 = value; - value = tmp; - goto case 4; - - case 4: - if (N == 4) { - f4 = value; - break; - } - tmp = f4; - f4 = value; - value = tmp; - - index = FIELDS_STORE_SIZE; - goto default; - - default: - ensureCapacity (N + 1); - if (index != N) { - Array.Copy (data, index - FIELDS_STORE_SIZE, data, index - FIELDS_STORE_SIZE + 1, N - index); - } - data [index - FIELDS_STORE_SIZE] = value; - break; - - } - m_Size = N + 1; - } - - public void remove (int index) - { - int N = m_Size; - if (!(0 <= index && index < N)) - throw onInvalidIndex (index, N); - if (zealed) - throw onSeledMutation (); - --N; - switch (index) { - - case 0: - if (N == 0) { - f0 = null; - break; - } - f0 = f1; - goto case 1; - - case 1: - if (N == 1) { - f1 = null; - break; - } - f1 = f2; - goto case 2; - - case 2: - if (N == 2) { - f2 = null; - break; - } - f2 = f3; - goto case 3; - - case 3: - if (N == 3) { - f3 = null; - break; - } - f3 = f4; - goto case 4; - - case 4: - if (N == 4) { - f4 = null; - break; - } - f4 = data [0]; - - index = FIELDS_STORE_SIZE; - goto default; - - default: - if (index != N) { - Array.Copy (data, index - FIELDS_STORE_SIZE + 1, data, index - FIELDS_STORE_SIZE, N - index); - } - data [N - FIELDS_STORE_SIZE] = null; - break; - - } - m_Size = N; - } - - public void clear () - { - if (zealed) - throw onSeledMutation (); - int N = m_Size; - for (int i = 0; i != N; ++i) { - SetImpl (i, (object)null); - } - m_Size = 0; - } - - public object [] ToArray () - { - object [] array = new object [m_Size]; - ToArray (array, 0); - return array; - } - - public void ToArray (object [] array) - { - ToArray (array, 0); - } - - public void ToArray (object [] array, int offset) - { - int N = m_Size; - switch (N) { - - default: - Array.Copy (data, 0, array, offset + FIELDS_STORE_SIZE, N - FIELDS_STORE_SIZE); - goto case 5; - - - case 5: - array [offset + 4] = f4; - goto case 4; - - case 4: - array [offset + 3] = f3; - goto case 3; - - case 3: - array [offset + 2] = f2; - goto case 2; - - case 2: - array [offset + 1] = f1; - goto case 1; - - case 1: - array [offset + 0] = f0; - goto case 0; - - case 0: - break; - } - } - - private void ensureCapacity (int minimalCapacity) - { - int required = minimalCapacity - FIELDS_STORE_SIZE; - if (required <= 0) - throw new ArgumentException (); - if (data == null) { - int alloc = FIELDS_STORE_SIZE * 2; - if (alloc < required) { - alloc = required; - } - data = new object [alloc]; - } - else { - int alloc = data.Length; - if (alloc < required) { - if (alloc <= FIELDS_STORE_SIZE) { - alloc = FIELDS_STORE_SIZE * 2; - } - else { - alloc *= 2; - } - if (alloc < required) { - alloc = required; - } - object [] tmp = new object [alloc]; - if (m_Size > FIELDS_STORE_SIZE) { - Array.Copy (data, 0, tmp, 0, m_Size - FIELDS_STORE_SIZE); - } - data = tmp; - } - } - } - - private static ApplicationException onInvalidIndex (int index, int upperBound) - { - // \u2209 is "NOT ELEMENT OF" - string msg = index + " \u2209 [0, " + upperBound + ')'; - throw new System.IndexOutOfRangeException (msg); - } - - private static ApplicationException onEmptyStackTopRead () - { - throw new ApplicationException ("Empty stack"); - } - - private static ApplicationException onSeledMutation () - { - throw new ApplicationException ("Attempt to modify sealed array"); - } - - - // Number of data elements - private int m_Size; - - private bool zealed; - - private const int FIELDS_STORE_SIZE = 5; - - private object f0, f1, f2, f3, f4; - - private object [] data; - } +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; +using System.Runtime.InteropServices; + +namespace EcmaScript.NET.Collections +{ + + /// Implementation of resizable array with focus on minimizing memory usage by storing few initial array elements in object fields. Can also be used as a stack. + + + public class ObjArray + { + virtual public bool Sealed + { + get + { + return zealed; + } + + } + virtual public bool Empty + { + get + { + return m_Size == 0; + } + + } + virtual public int Size + { + set + { + if (value < 0) + throw new ArgumentException (); + if (zealed) + throw onSeledMutation (); + int N = m_Size; + if (value < N) { + for (int i = value; i != N; ++i) { + SetImpl (i, (object)null); + } + } + else if (value > N) { + if (value > FIELDS_STORE_SIZE) { + ensureCapacity (value); + } + } + m_Size = value; + } + + } + + public ObjArray () + { + } + + public void seal () + { + zealed = true; + } + + public int size () + { + return m_Size; + } + + public object Get (int index) + { + if (!(0 <= index && index < m_Size)) + throw onInvalidIndex (index, m_Size); + return GetImpl (index); + } + + public void Set (int index, object value) + { + if (!(0 <= index && index < m_Size)) + throw onInvalidIndex (index, m_Size); + if (zealed) + throw onSeledMutation (); + SetImpl (index, value); + } + + private object GetImpl (int index) + { + switch (index) { + + case 0: + return f0; + + case 1: + return f1; + + case 2: + return f2; + + case 3: + return f3; + + case 4: + return f4; + } + return data [index - FIELDS_STORE_SIZE]; + } + + private void SetImpl (int index, object value) + { + switch (index) { + + case 0: + f0 = value; + break; + + case 1: + f1 = value; + break; + + case 2: + f2 = value; + break; + + case 3: + f3 = value; + break; + + case 4: + f4 = value; + break; + + default: + data [index - FIELDS_STORE_SIZE] = value; + break; + + } + } + + public virtual int indexOf (object obj) + { + int N = m_Size; + for (int i = 0; i != N; ++i) { + object current = GetImpl (i); + if (current == obj || (current != null && current.Equals (obj))) { + return i; + } + } + return -1; + } + + public virtual int lastIndexOf (object obj) + { + for (int i = m_Size; i != 0; ) { + --i; + object current = GetImpl (i); + if (current == obj || (current != null && current.Equals (obj))) { + return i; + } + } + return -1; + } + + public object peek () + { + int N = m_Size; + if (N == 0) + throw onEmptyStackTopRead (); + return GetImpl (N - 1); + } + + public object pop () + { + if (zealed) + throw onSeledMutation (); + int N = m_Size; + --N; + object top; + switch (N) { + + case -1: + throw onEmptyStackTopRead (); + + case 0: + top = f0; + f0 = null; + break; + + case 1: + top = f1; + f1 = null; + break; + + case 2: + top = f2; + f2 = null; + break; + + case 3: + top = f3; + f3 = null; + break; + + case 4: + top = f4; + f4 = null; + break; + + default: + top = data [N - FIELDS_STORE_SIZE]; + data [N - FIELDS_STORE_SIZE] = null; + break; + + } + m_Size = N; + return top; + } + + public void push (object value) + { + add (value); + } + + public void add (object value) + { + if (zealed) + throw onSeledMutation (); + int N = m_Size; + if (N >= FIELDS_STORE_SIZE) { + ensureCapacity (N + 1); + } + m_Size = N + 1; + SetImpl (N, value); + } + + public void add (int index, object value) + { + int N = m_Size; + if (!(0 <= index && index <= N)) + throw onInvalidIndex (index, N + 1); + if (zealed) + throw onSeledMutation (); + object tmp; + switch (index) { + + case 0: + if (N == 0) { + f0 = value; + break; + } + tmp = f0; + f0 = value; + value = tmp; + goto case 1; + + case 1: + if (N == 1) { + f1 = value; + break; + } + tmp = f1; + f1 = value; + value = tmp; + goto case 2; + + case 2: + if (N == 2) { + f2 = value; + break; + } + tmp = f2; + f2 = value; + value = tmp; + goto case 3; + + case 3: + if (N == 3) { + f3 = value; + break; + } + tmp = f3; + f3 = value; + value = tmp; + goto case 4; + + case 4: + if (N == 4) { + f4 = value; + break; + } + tmp = f4; + f4 = value; + value = tmp; + + index = FIELDS_STORE_SIZE; + goto default; + + default: + ensureCapacity (N + 1); + if (index != N) { + Array.Copy (data, index - FIELDS_STORE_SIZE, data, index - FIELDS_STORE_SIZE + 1, N - index); + } + data [index - FIELDS_STORE_SIZE] = value; + break; + + } + m_Size = N + 1; + } + + public void remove (int index) + { + int N = m_Size; + if (!(0 <= index && index < N)) + throw onInvalidIndex (index, N); + if (zealed) + throw onSeledMutation (); + --N; + switch (index) { + + case 0: + if (N == 0) { + f0 = null; + break; + } + f0 = f1; + goto case 1; + + case 1: + if (N == 1) { + f1 = null; + break; + } + f1 = f2; + goto case 2; + + case 2: + if (N == 2) { + f2 = null; + break; + } + f2 = f3; + goto case 3; + + case 3: + if (N == 3) { + f3 = null; + break; + } + f3 = f4; + goto case 4; + + case 4: + if (N == 4) { + f4 = null; + break; + } + f4 = data [0]; + + index = FIELDS_STORE_SIZE; + goto default; + + default: + if (index != N) { + Array.Copy (data, index - FIELDS_STORE_SIZE + 1, data, index - FIELDS_STORE_SIZE, N - index); + } + data [N - FIELDS_STORE_SIZE] = null; + break; + + } + m_Size = N; + } + + public void clear () + { + if (zealed) + throw onSeledMutation (); + int N = m_Size; + for (int i = 0; i != N; ++i) { + SetImpl (i, (object)null); + } + m_Size = 0; + } + + public object [] ToArray () + { + object [] array = new object [m_Size]; + ToArray (array, 0); + return array; + } + + public void ToArray (object [] array) + { + ToArray (array, 0); + } + + public void ToArray (object [] array, int offset) + { + int N = m_Size; + switch (N) { + + default: + Array.Copy (data, 0, array, offset + FIELDS_STORE_SIZE, N - FIELDS_STORE_SIZE); + goto case 5; + + + case 5: + array [offset + 4] = f4; + goto case 4; + + case 4: + array [offset + 3] = f3; + goto case 3; + + case 3: + array [offset + 2] = f2; + goto case 2; + + case 2: + array [offset + 1] = f1; + goto case 1; + + case 1: + array [offset + 0] = f0; + goto case 0; + + case 0: + break; + } + } + + private void ensureCapacity (int minimalCapacity) + { + int required = minimalCapacity - FIELDS_STORE_SIZE; + if (required <= 0) + throw new ArgumentException (); + if (data == null) { + int alloc = FIELDS_STORE_SIZE * 2; + if (alloc < required) { + alloc = required; + } + data = new object [alloc]; + } + else { + int alloc = data.Length; + if (alloc < required) { + if (alloc <= FIELDS_STORE_SIZE) { + alloc = FIELDS_STORE_SIZE * 2; + } + else { + alloc *= 2; + } + if (alloc < required) { + alloc = required; + } + object [] tmp = new object [alloc]; + if (m_Size > FIELDS_STORE_SIZE) { + Array.Copy (data, 0, tmp, 0, m_Size - FIELDS_STORE_SIZE); + } + data = tmp; + } + } + } + + private static Exception onInvalidIndex (int index, int upperBound) + { + // \u2209 is "NOT ELEMENT OF" + string msg = index + " \u2209 [0, " + upperBound + ')'; + throw new System.IndexOutOfRangeException (msg); + } + + private static Exception onEmptyStackTopRead () + { + throw new Exception ("Empty stack"); + } + + private static Exception onSeledMutation () + { + throw new Exception ("Attempt to modify sealed array"); + } + + + // Number of data elements + private int m_Size; + + private bool zealed; + + private const int FIELDS_STORE_SIZE = 5; + + private object f0, f1, f2, f3, f4; + + private object [] data; + } } \ No newline at end of file diff --git a/Code/EcmaScript.NET/Collections/ObjToIntMap.cs b/src/EcmaScript.NET/Collections/ObjToIntMap.cs similarity index 97% rename from Code/EcmaScript.NET/Collections/ObjToIntMap.cs rename to src/EcmaScript.NET/Collections/ObjToIntMap.cs index 7e3f60e..e42937f 100644 --- a/Code/EcmaScript.NET/Collections/ObjToIntMap.cs +++ b/src/EcmaScript.NET/Collections/ObjToIntMap.cs @@ -1,476 +1,476 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; -using System.Runtime.InteropServices; - - -namespace EcmaScript.NET.Collections -{ - - /// Map to associate objects to integers. - /// The map does not synchronize any of its operation, so either use - /// it from a single thread or do own synchronization or perform all mutation - /// operations on one thread before passing the map to others - /// - /// - public class ObjToIntMap - { - virtual public bool Empty - { - get - { - return keyCount == 0; - } - - } - - // Map implementation via hashtable, - // follows "The Art of Computer Programming" by Donald E. Knuth - - // ObjToIntMap is a copy cat of ObjToIntMap with API adjusted to object keys - - public class Iterator - { - virtual public object Key - { - get - { - object key = keys [cursor]; - if (key == UniqueTag.NullValue) { - key = null; - } - return key; - } - - } - virtual public int Value - { - get - { - return values [cursor]; - } - - set - { - values [cursor] = value; - } - - } - - internal Iterator (ObjToIntMap master) - { - this.master = master; - } - - internal void Init (object [] keys, int [] values, int keyCount) - { - this.keys = keys; - this.values = values; - this.cursor = -1; - this.remaining = keyCount; - } - - public virtual void start () - { - master.initIterator (this); - next (); - } - - public virtual bool done () - { - return remaining < 0; - } - - public virtual void next () - { - if (remaining == -1) - Context.CodeBug (); - if (remaining == 0) { - remaining = -1; - cursor = -1; - } - else { - for (++cursor; ; ++cursor) { - object key = keys [cursor]; - if (key != null && key != ObjToIntMap.DELETED) { - --remaining; - break; - } - } - } - } - - internal ObjToIntMap master; - private int cursor; - private int remaining; - private object [] keys; - private int [] values; - } - - public ObjToIntMap () - : this (4) - { - } - - public ObjToIntMap (int keyCountHint) - { - if (keyCountHint < 0) - Context.CodeBug (); - // Table grow when number of stored keys >= 3/4 of max capacity - int minimalCapacity = keyCountHint * 4 / 3; - int i; - for (i = 2; (1 << i) < minimalCapacity; ++i) { - } - power = i; - if (check && power < 2) - Context.CodeBug (); - } - - public virtual int size () - { - return keyCount; - } - - public virtual bool has (object key) - { - if (key == null) { - key = UniqueTag.NullValue; - } - return 0 <= findIndex (key); - } - - /// Get integer value assigned with key. - /// key integer value or defaultValue if key is absent - /// - public virtual int Get (object key, int defaultValue) - { - if (key == null) { - key = UniqueTag.NullValue; - } - int index = findIndex (key); - if (0 <= index) { - return values [index]; - } - return defaultValue; - } - - /// Get integer value assigned with key. - /// key integer value - /// - /// RuntimeException if key does not exist - public virtual int getExisting (object key) - { - if (key == null) { - key = UniqueTag.NullValue; - } - int index = findIndex (key); - if (0 <= index) { - return values [index]; - } - // Key must exist - Context.CodeBug (); - return 0; - } - - public virtual void put (object key, int value) - { - if (key == null) { - key = UniqueTag.NullValue; - } - int index = ensureIndex (key); - values [index] = value; - } - - /// If table already contains a key that equals to keyArg, return that key - /// while setting its value to zero, otherwise add keyArg with 0 value to - /// the table and return it. - /// - public virtual object intern (object keyArg) - { - bool nullKey = false; - if (keyArg == null) { - nullKey = true; - keyArg = UniqueTag.NullValue; - } - int index = ensureIndex (keyArg); - values [index] = 0; - return (nullKey) ? null : keys [index]; - } - - public virtual void remove (object key) - { - if (key == null) { - key = UniqueTag.NullValue; - } - int index = findIndex (key); - if (0 <= index) { - keys [index] = DELETED; - --keyCount; - } - } - - public virtual void clear () - { - int i = keys.Length; - while (i != 0) { - keys [--i] = null; - } - keyCount = 0; - occupiedCount = 0; - } - - public virtual Iterator newIterator () - { - return new Iterator (this); - } - - // The sole purpose of the method is to avoid accessing private fields - // from the Iterator inner class to workaround JDK 1.1 compiler bug which - // generates code triggering VerifierError on recent JVMs - internal void initIterator (Iterator i) - { - i.Init (keys, values, keyCount); - } - - /// Return array of present keys - public virtual object [] getKeys () - { - object [] array = new object [keyCount]; - getKeys (array, 0); - return array; - } - - public virtual void getKeys (object [] array, int offset) - { - int count = keyCount; - for (int i = 0; count != 0; ++i) { - object key = keys [i]; - if (key != null && key != DELETED) { - if (key == UniqueTag.NullValue) { - key = null; - } - array [offset] = key; - ++offset; - --count; - } - } - } - - private static int tableLookupStep (int fraction, int mask, int power) - { - int shift = 32 - 2 * power; - if (shift >= 0) { - return ((int)(((uint)fraction >> shift)) & mask) | 1; - } - else { - return (fraction & (int)((uint)mask >> -shift)) | 1; - } - } - - private int findIndex (object key) - { - if (keys != null) { - int hash = key.GetHashCode (); - int fraction = hash * A; - int index = (int)((uint)fraction >> (32 - power)); - object test = keys [index]; - if (test != null) { - int N = 1 << power; - if (test == key || (values [N + index] == hash && test.Equals (key))) { - return index; - } - // Search in table after first failed attempt - int mask = N - 1; - int step = tableLookupStep (fraction, mask, power); - int n = 0; - for (; ; ) { - if (check) { - if (n >= occupiedCount) - Context.CodeBug (); - ++n; - } - index = (index + step) & mask; - test = keys [index]; - if (test == null) { - break; - } - if (test == key || (values [N + index] == hash && test.Equals (key))) { - return index; - } - } - } - } - return -1; - } - - // Insert key that is not present to table without deleted entries - // and enough free space - private int insertNewKey (object key, int hash) - { - if (check && occupiedCount != keyCount) - Context.CodeBug (); - if (check && keyCount == 1 << power) - Context.CodeBug (); - int fraction = hash * A; - int index = (int)((uint)fraction >> (32 - power)); - int N = 1 << power; - if (keys [index] != null) { - int mask = N - 1; - int step = tableLookupStep (fraction, mask, power); - int firstIndex = index; - do { - if (check && keys [index] == DELETED) - Context.CodeBug (); - index = (index + step) & mask; - if (check && firstIndex == index) - Context.CodeBug (); - } - while (keys [index] != null); - } - keys [index] = key; - values [N + index] = hash; - ++occupiedCount; - ++keyCount; - - return index; - } - - private void rehashTable () - { - if (keys == null) { - if (check && keyCount != 0) - Context.CodeBug (); - if (check && occupiedCount != 0) - Context.CodeBug (); - int N = 1 << power; - keys = new object [N]; - values = new int [2 * N]; - } - else { - // Check if removing deleted entries would free enough space - if (keyCount * 2 >= occupiedCount) { - // Need to grow: less then half of deleted entries - ++power; - } - int N = 1 << power; - object [] oldKeys = keys; - int [] oldValues = values; - int oldN = oldKeys.Length; - keys = new object [N]; - values = new int [2 * N]; - - int remaining = keyCount; - occupiedCount = keyCount = 0; - for (int i = 0; remaining != 0; ++i) { - object key = oldKeys [i]; - if (key != null && key != DELETED) { - int keyHash = oldValues [oldN + i]; - int index = insertNewKey (key, keyHash); - values [index] = oldValues [i]; - --remaining; - } - } - } - } - - // Ensure key index creating one if necessary - private int ensureIndex (object key) - { - int hash = key.GetHashCode (); - int index = -1; - int firstDeleted = -1; - if (keys != null) { - int fraction = hash * A; - index = (int)((uint)fraction >> (32 - power)); - object test = keys [index]; - if (test != null) { - int N = 1 << power; - if (test == key || (values [N + index] == hash && test.Equals (key))) { - return index; - } - if (test == DELETED) { - firstDeleted = index; - } - - // Search in table after first failed attempt - int mask = N - 1; - int step = tableLookupStep (fraction, mask, power); - int n = 0; - for (; ; ) { - if (check) { - if (n >= occupiedCount) - Context.CodeBug (); - ++n; - } - index = (index + step) & mask; - test = keys [index]; - if (test == null) { - break; - } - if (test == key || (values [N + index] == hash && test.Equals (key))) { - return index; - } - if (test == DELETED && firstDeleted < 0) { - firstDeleted = index; - } - } - } - } - // Inserting of new key - if (check && keys != null && keys [index] != null) - Context.CodeBug (); - if (firstDeleted >= 0) { - index = firstDeleted; - } - else { - // Need to consume empty entry: check occupation level - if (keys == null || occupiedCount * 4 >= (1 << power) * 3) { - // Too litle unused entries: rehash - rehashTable (); - return insertNewKey (key, hash); - } - ++occupiedCount; - } - keys [index] = key; - values [(1 << power) + index] = hash; - ++keyCount; - return index; - } - - // A == golden_ratio * (1 << 32) = ((sqrt(5) - 1) / 2) * (1 << 32) - // See Knuth etc. - private const int A = unchecked ((int)0x9e3779b9); - - private static readonly object DELETED = new object (); - - // Structure of kyes and values arrays (N == 1 << power): - // keys[0 <= i < N]: key value or null or DELETED mark - // values[0 <= i < N]: value of key at keys[i] - // values[N <= i < 2*N]: hash code of key at keys[i-N] - - - private object [] keys; - - private int [] values; - - private int power; - private int keyCount; - - private int occupiedCount; // == keyCount + deleted_count - - // If true, enables consitency checks - private static readonly bool check = false; // TODO: make me a preprocessor directive - - } +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; +using System.Runtime.InteropServices; + + +namespace EcmaScript.NET.Collections +{ + + /// Map to associate objects to integers. + /// The map does not synchronize any of its operation, so either use + /// it from a single thread or do own synchronization or perform all mutation + /// operations on one thread before passing the map to others + /// + /// + public class ObjToIntMap + { + virtual public bool Empty + { + get + { + return keyCount == 0; + } + + } + + // Map implementation via hashtable, + // follows "The Art of Computer Programming" by Donald E. Knuth + + // ObjToIntMap is a copy cat of ObjToIntMap with API adjusted to object keys + + public class Iterator + { + virtual public object Key + { + get + { + object key = keys [cursor]; + if (key == UniqueTag.NullValue) { + key = null; + } + return key; + } + + } + virtual public int Value + { + get + { + return values [cursor]; + } + + set + { + values [cursor] = value; + } + + } + + internal Iterator (ObjToIntMap master) + { + this.master = master; + } + + internal void Init (object [] keys, int [] values, int keyCount) + { + this.keys = keys; + this.values = values; + this.cursor = -1; + this.remaining = keyCount; + } + + public virtual void start () + { + master.initIterator (this); + next (); + } + + public virtual bool done () + { + return remaining < 0; + } + + public virtual void next () + { + if (remaining == -1) + Context.CodeBug (); + if (remaining == 0) { + remaining = -1; + cursor = -1; + } + else { + for (++cursor; ; ++cursor) { + object key = keys [cursor]; + if (key != null && key != ObjToIntMap.DELETED) { + --remaining; + break; + } + } + } + } + + internal ObjToIntMap master; + private int cursor; + private int remaining; + private object [] keys; + private int [] values; + } + + public ObjToIntMap () + : this (4) + { + } + + public ObjToIntMap (int keyCountHint) + { + if (keyCountHint < 0) + Context.CodeBug (); + // Table grow when number of stored keys >= 3/4 of max capacity + int minimalCapacity = keyCountHint * 4 / 3; + int i; + for (i = 2; (1 << i) < minimalCapacity; ++i) { + } + power = i; + if (check && power < 2) + Context.CodeBug (); + } + + public virtual int size () + { + return keyCount; + } + + public virtual bool has (object key) + { + if (key == null) { + key = UniqueTag.NullValue; + } + return 0 <= findIndex (key); + } + + /// Get integer value assigned with key. + /// key integer value or defaultValue if key is absent + /// + public virtual int Get (object key, int defaultValue) + { + if (key == null) { + key = UniqueTag.NullValue; + } + int index = findIndex (key); + if (0 <= index) { + return values [index]; + } + return defaultValue; + } + + /// Get integer value assigned with key. + /// key integer value + /// + /// RuntimeException if key does not exist + public virtual int getExisting (object key) + { + if (key == null) { + key = UniqueTag.NullValue; + } + int index = findIndex (key); + if (0 <= index) { + return values [index]; + } + // Key must exist + Context.CodeBug (); + return 0; + } + + public virtual void put (object key, int value) + { + if (key == null) { + key = UniqueTag.NullValue; + } + int index = ensureIndex (key); + values [index] = value; + } + + /// If table already contains a key that equals to keyArg, return that key + /// while setting its value to zero, otherwise add keyArg with 0 value to + /// the table and return it. + /// + public virtual object intern (object keyArg) + { + bool nullKey = false; + if (keyArg == null) { + nullKey = true; + keyArg = UniqueTag.NullValue; + } + int index = ensureIndex (keyArg); + values [index] = 0; + return (nullKey) ? null : keys [index]; + } + + public virtual void remove (object key) + { + if (key == null) { + key = UniqueTag.NullValue; + } + int index = findIndex (key); + if (0 <= index) { + keys [index] = DELETED; + --keyCount; + } + } + + public virtual void clear () + { + int i = keys.Length; + while (i != 0) { + keys [--i] = null; + } + keyCount = 0; + occupiedCount = 0; + } + + public virtual Iterator newIterator () + { + return new Iterator (this); + } + + // The sole purpose of the method is to avoid accessing private fields + // from the Iterator inner class to workaround JDK 1.1 compiler bug which + // generates code triggering VerifierError on recent JVMs + internal void initIterator (Iterator i) + { + i.Init (keys, values, keyCount); + } + + /// Return array of present keys + public virtual object [] getKeys () + { + object [] array = new object [keyCount]; + getKeys (array, 0); + return array; + } + + public virtual void getKeys (object [] array, int offset) + { + int count = keyCount; + for (int i = 0; count != 0; ++i) { + object key = keys [i]; + if (key != null && key != DELETED) { + if (key == UniqueTag.NullValue) { + key = null; + } + array [offset] = key; + ++offset; + --count; + } + } + } + + private static int tableLookupStep (int fraction, int mask, int power) + { + int shift = 32 - 2 * power; + if (shift >= 0) { + return ((int)(((uint)fraction >> shift)) & mask) | 1; + } + else { + return (fraction & (int)((uint)mask >> -shift)) | 1; + } + } + + private int findIndex (object key) + { + if (keys != null) { + int hash = key.GetHashCode (); + int fraction = hash * A; + int index = (int)((uint)fraction >> (32 - power)); + object test = keys [index]; + if (test != null) { + int N = 1 << power; + if (test == key || (values [N + index] == hash && test.Equals (key))) { + return index; + } + // Search in table after first failed attempt + int mask = N - 1; + int step = tableLookupStep (fraction, mask, power); + int n = 0; + for (; ; ) { + if (check) { + if (n >= occupiedCount) + Context.CodeBug (); + ++n; + } + index = (index + step) & mask; + test = keys [index]; + if (test == null) { + break; + } + if (test == key || (values [N + index] == hash && test.Equals (key))) { + return index; + } + } + } + } + return -1; + } + + // Insert key that is not present to table without deleted entries + // and enough free space + private int insertNewKey (object key, int hash) + { + if (check && occupiedCount != keyCount) + Context.CodeBug (); + if (check && keyCount == 1 << power) + Context.CodeBug (); + int fraction = hash * A; + int index = (int)((uint)fraction >> (32 - power)); + int N = 1 << power; + if (keys [index] != null) { + int mask = N - 1; + int step = tableLookupStep (fraction, mask, power); + int firstIndex = index; + do { + if (check && keys [index] == DELETED) + Context.CodeBug (); + index = (index + step) & mask; + if (check && firstIndex == index) + Context.CodeBug (); + } + while (keys [index] != null); + } + keys [index] = key; + values [N + index] = hash; + ++occupiedCount; + ++keyCount; + + return index; + } + + private void rehashTable () + { + if (keys == null) { + if (check && keyCount != 0) + Context.CodeBug (); + if (check && occupiedCount != 0) + Context.CodeBug (); + int N = 1 << power; + keys = new object [N]; + values = new int [2 * N]; + } + else { + // Check if removing deleted entries would free enough space + if (keyCount * 2 >= occupiedCount) { + // Need to grow: less then half of deleted entries + ++power; + } + int N = 1 << power; + object [] oldKeys = keys; + int [] oldValues = values; + int oldN = oldKeys.Length; + keys = new object [N]; + values = new int [2 * N]; + + int remaining = keyCount; + occupiedCount = keyCount = 0; + for (int i = 0; remaining != 0; ++i) { + object key = oldKeys [i]; + if (key != null && key != DELETED) { + int keyHash = oldValues [oldN + i]; + int index = insertNewKey (key, keyHash); + values [index] = oldValues [i]; + --remaining; + } + } + } + } + + // Ensure key index creating one if necessary + private int ensureIndex (object key) + { + int hash = key.GetHashCode (); + int index = -1; + int firstDeleted = -1; + if (keys != null) { + int fraction = hash * A; + index = (int)((uint)fraction >> (32 - power)); + object test = keys [index]; + if (test != null) { + int N = 1 << power; + if (test == key || (values [N + index] == hash && test.Equals (key))) { + return index; + } + if (test == DELETED) { + firstDeleted = index; + } + + // Search in table after first failed attempt + int mask = N - 1; + int step = tableLookupStep (fraction, mask, power); + int n = 0; + for (; ; ) { + if (check) { + if (n >= occupiedCount) + Context.CodeBug (); + ++n; + } + index = (index + step) & mask; + test = keys [index]; + if (test == null) { + break; + } + if (test == key || (values [N + index] == hash && test.Equals (key))) { + return index; + } + if (test == DELETED && firstDeleted < 0) { + firstDeleted = index; + } + } + } + } + // Inserting of new key + if (check && keys != null && keys [index] != null) + Context.CodeBug (); + if (firstDeleted >= 0) { + index = firstDeleted; + } + else { + // Need to consume empty entry: check occupation level + if (keys == null || occupiedCount * 4 >= (1 << power) * 3) { + // Too litle unused entries: rehash + rehashTable (); + return insertNewKey (key, hash); + } + ++occupiedCount; + } + keys [index] = key; + values [(1 << power) + index] = hash; + ++keyCount; + return index; + } + + // A == golden_ratio * (1 << 32) = ((sqrt(5) - 1) / 2) * (1 << 32) + // See Knuth etc. + private const int A = unchecked ((int)0x9e3779b9); + + private static readonly object DELETED = new object (); + + // Structure of kyes and values arrays (N == 1 << power): + // keys[0 <= i < N]: key value or null or DELETED mark + // values[0 <= i < N]: value of key at keys[i] + // values[N <= i < 2*N]: hash code of key at keys[i-N] + + + private object [] keys; + + private int [] values; + + private int power; + private int keyCount; + + private int occupiedCount; // == keyCount + deleted_count + + // If true, enables consitency checks + private static readonly bool check = false; // TODO: make me a preprocessor directive + + } } \ No newline at end of file diff --git a/Code/EcmaScript.NET/Collections/UintMap.cs b/src/EcmaScript.NET/Collections/UintMap.cs similarity index 97% rename from Code/EcmaScript.NET/Collections/UintMap.cs rename to src/EcmaScript.NET/Collections/UintMap.cs index 3f77b66..4f3360e 100644 --- a/Code/EcmaScript.NET/Collections/UintMap.cs +++ b/src/EcmaScript.NET/Collections/UintMap.cs @@ -1,428 +1,428 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; -using System.Runtime.InteropServices; - -namespace EcmaScript.NET.Collections -{ - - /// Map to associate non-negative integers to objects or integers. - /// The map does not synchronize any of its operation, so either use - /// it from a single thread or do own synchronization or perform all mutation - /// operations on one thread before passing the map to others. - /// - /// - - public class UintMap - { - virtual public bool Empty - { - get - { - return keyCount == 0; - } - - } - /// Return array of present keys - virtual public int [] Keys - { - get - { - int [] keys = this.keys; - int n = keyCount; - int [] result = new int [n]; - for (int i = 0; n != 0; ++i) { - int entry = keys [i]; - if (entry != EMPTY && entry != DELETED) { - result [--n] = entry; - } - } - return result; - } - - } - - // Map implementation via hashtable, - // follows "The Art of Computer Programming" by Donald E. Knuth - - public UintMap () - : this (4) - { - } - - public UintMap (int initialCapacity) - { - if (initialCapacity < 0) - Context.CodeBug (); - // Table grow when number of stored keys >= 3/4 of max capacity - int minimalCapacity = initialCapacity * 4 / 3; - int i; - for (i = 2; (1 << i) < minimalCapacity; ++i) { - } - power = i; - if (check && power < 2) - Context.CodeBug (); - } - - public virtual int size () - { - return keyCount; - } - - public virtual bool has (int key) - { - if (key < 0) - Context.CodeBug (); - return 0 <= findIndex (key); - } - - /// Get object value assigned with key. - /// key object value or null if key is absent - /// - public virtual object getObject (int key) - { - if (key < 0) - Context.CodeBug (); - if (values != null) { - int index = findIndex (key); - if (0 <= index) { - return values [index]; - } - } - return null; - } - - /// Get integer value assigned with key. - /// key integer value or defaultValue if key is absent - /// - public virtual int getInt (int key, int defaultValue) - { - if (key < 0) - Context.CodeBug (); - int index = findIndex (key); - if (0 <= index) { - if (ivaluesShift != 0) { - return keys [ivaluesShift + index]; - } - return 0; - } - return defaultValue; - } - - /// Get integer value assigned with key. - /// key integer value or defaultValue if key does not exist or does - /// not have int value - /// - /// RuntimeException if key does not exist - public virtual int getExistingInt (int key) - { - if (key < 0) - Context.CodeBug (); - int index = findIndex (key); - if (0 <= index) { - if (ivaluesShift != 0) { - return keys [ivaluesShift + index]; - } - return 0; - } - // Key must exist - Context.CodeBug (); - return 0; - } - - /// Set object value of the key. - /// If key does not exist, also set its int value to 0. - /// - public virtual void put (int key, object value) - { - if (key < 0) - Context.CodeBug (); - int index = ensureIndex (key, false); - if (values == null) { - values = new object [1 << power]; - } - values [index] = value; - } - - /// Set int value of the key. - /// If key does not exist, also set its object value to null. - /// - public virtual void put (int key, int value) - { - if (key < 0) - Context.CodeBug (); - int index = ensureIndex (key, true); - if (ivaluesShift == 0) { - int N = 1 << power; - // keys.length can be N * 2 after clear which set ivaluesShift to 0 - if (keys.Length != N * 2) { - int [] tmp = new int [N * 2]; - Array.Copy (keys, 0, tmp, 0, N); - keys = tmp; - } - ivaluesShift = N; - } - keys [ivaluesShift + index] = value; - } - - public virtual void remove (int key) - { - if (key < 0) - Context.CodeBug (); - int index = findIndex (key); - if (0 <= index) { - keys [index] = DELETED; - --keyCount; - // Allow to GC value and make sure that new key with the deleted - // slot shall get proper default values - if (values != null) { - values [index] = null; - } - if (ivaluesShift != 0) { - keys [ivaluesShift + index] = 0; - } - } - } - - public virtual void clear () - { - int N = 1 << power; - if (keys != null) { - for (int i = 0; i != N; ++i) { - keys [i] = EMPTY; - } - if (values != null) { - for (int i = 0; i != N; ++i) { - values [i] = null; - } - } - } - ivaluesShift = 0; - keyCount = 0; - occupiedCount = 0; - } - - private static int tableLookupStep (int fraction, int mask, int power) - { - int shift = 32 - 2 * power; - if (shift >= 0) { - return (((int)((uint)fraction >> shift)) & mask) | 1; - } - else { - return (fraction & (int)((uint)mask >> -shift)) | 1; - } - } - - private int findIndex (int key) - { - int [] keys = this.keys; - if (keys != null) { - int fraction = key * A; - int index = (int)((uint)fraction >> (32 - power)); - int entry = keys [index]; - if (entry == key) { - return index; - } - if (entry != EMPTY) { - // Search in table after first failed attempt - int mask = (1 << power) - 1; - int step = tableLookupStep (fraction, mask, power); - int n = 0; - do { - if (check) { - if (n >= occupiedCount) - Context.CodeBug (); - ++n; - } - index = (index + step) & mask; - entry = keys [index]; - if (entry == key) { - return index; - } - } - while (entry != EMPTY); - } - } - return -1; - } - - // Insert key that is not present to table without deleted entries - // and enough free space - private int insertNewKey (int key) - { - if (check && occupiedCount != keyCount) - Context.CodeBug (); - if (check && keyCount == 1 << power) - Context.CodeBug (); - int [] keys = this.keys; - int fraction = key * A; - int index = (int)((uint)fraction >> (32 - power)); - if (keys [index] != EMPTY) { - int mask = (1 << power) - 1; - int step = tableLookupStep (fraction, mask, power); - int firstIndex = index; - do { - if (check && keys [index] == DELETED) - Context.CodeBug (); - index = (index + step) & mask; - if (check && firstIndex == index) - Context.CodeBug (); - } - while (keys [index] != EMPTY); - } - keys [index] = key; - ++occupiedCount; - ++keyCount; - return index; - } - - private void rehashTable (bool ensureIntSpace) - { - if (keys != null) { - // Check if removing deleted entries would free enough space - if (keyCount * 2 >= occupiedCount) { - // Need to grow: less then half of deleted entries - ++power; - } - } - int N = 1 << power; - int [] old = keys; - int oldShift = ivaluesShift; - if (oldShift == 0 && !ensureIntSpace) { - keys = new int [N]; - } - else { - ivaluesShift = N; - keys = new int [N * 2]; - } - for (int i = 0; i != N; ++i) { - keys [i] = EMPTY; - } - - object [] oldValues = values; - if (oldValues != null) { - values = new object [N]; - } - - int oldCount = keyCount; - occupiedCount = 0; - if (oldCount != 0) { - keyCount = 0; - for (int i = 0, remaining = oldCount; remaining != 0; ++i) { - int key = old [i]; - if (key != EMPTY && key != DELETED) { - int index = insertNewKey (key); - if (oldValues != null) { - values [index] = oldValues [i]; - } - if (oldShift != 0) { - keys [ivaluesShift + index] = old [oldShift + i]; - } - --remaining; - } - } - } - } - - // Ensure key index creating one if necessary - private int ensureIndex (int key, bool intType) - { - int index = -1; - int firstDeleted = -1; - int [] keys = this.keys; - if (keys != null) { - int fraction = key * A; - index = (int)((uint)fraction >> (32 - power)); - int entry = keys [index]; - if (entry == key) { - return index; - } - if (entry != EMPTY) { - if (entry == DELETED) { - firstDeleted = index; - } - // Search in table after first failed attempt - int mask = (1 << power) - 1; - int step = tableLookupStep (fraction, mask, power); - int n = 0; - do { - if (check) { - if (n >= occupiedCount) - Context.CodeBug (); - ++n; - } - index = (index + step) & mask; - entry = keys [index]; - if (entry == key) { - return index; - } - if (entry == DELETED && firstDeleted < 0) { - firstDeleted = index; - } - } - while (entry != EMPTY); - } - } - // Inserting of new key - if (check && keys != null && keys [index] != EMPTY) - Context.CodeBug (); - if (firstDeleted >= 0) { - index = firstDeleted; - } - else { - // Need to consume empty entry: check occupation level - if (keys == null || occupiedCount * 4 >= (1 << power) * 3) { - // Too litle unused entries: rehash - rehashTable (intType); - keys = this.keys; - return insertNewKey (key); - } - ++occupiedCount; - } - keys [index] = key; - ++keyCount; - return index; - } - // A == golden_ratio * (1 << 32) = ((sqrt(5) - 1) / 2) * (1 << 32) - // See Knuth etc. - private const int A = unchecked ((int)0x9e3779b9); - - private const int EMPTY = -1; - private const int DELETED = -2; - - // Structure of kyes and values arrays (N == 1 << power): - // keys[0 <= i < N]: key value or EMPTY or DELETED mark - // values[0 <= i < N]: value of key at keys[i] - // keys[N <= i < 2N]: int values of keys at keys[i - N] - - - private int [] keys; - - private object [] values; - - private int power; - private int keyCount; - - private int occupiedCount; // == keyCount + deleted_count - - // If ivaluesShift != 0, keys[ivaluesShift + index] contains integer - // values associated with keys - - private int ivaluesShift; - - // If true, enables consitency checks - private static readonly bool check = false; // TODO: make me a preprocessor directive - - } +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; +using System.Runtime.InteropServices; + +namespace EcmaScript.NET.Collections +{ + + /// Map to associate non-negative integers to objects or integers. + /// The map does not synchronize any of its operation, so either use + /// it from a single thread or do own synchronization or perform all mutation + /// operations on one thread before passing the map to others. + /// + /// + + public class UintMap + { + virtual public bool Empty + { + get + { + return keyCount == 0; + } + + } + /// Return array of present keys + virtual public int [] Keys + { + get + { + int [] keys = this.keys; + int n = keyCount; + int [] result = new int [n]; + for (int i = 0; n != 0; ++i) { + int entry = keys [i]; + if (entry != EMPTY && entry != DELETED) { + result [--n] = entry; + } + } + return result; + } + + } + + // Map implementation via hashtable, + // follows "The Art of Computer Programming" by Donald E. Knuth + + public UintMap () + : this (4) + { + } + + public UintMap (int initialCapacity) + { + if (initialCapacity < 0) + Context.CodeBug (); + // Table grow when number of stored keys >= 3/4 of max capacity + int minimalCapacity = initialCapacity * 4 / 3; + int i; + for (i = 2; (1 << i) < minimalCapacity; ++i) { + } + power = i; + if (check && power < 2) + Context.CodeBug (); + } + + public virtual int size () + { + return keyCount; + } + + public virtual bool has (int key) + { + if (key < 0) + Context.CodeBug (); + return 0 <= findIndex (key); + } + + /// Get object value assigned with key. + /// key object value or null if key is absent + /// + public virtual object getObject (int key) + { + if (key < 0) + Context.CodeBug (); + if (values != null) { + int index = findIndex (key); + if (0 <= index) { + return values [index]; + } + } + return null; + } + + /// Get integer value assigned with key. + /// key integer value or defaultValue if key is absent + /// + public virtual int getInt (int key, int defaultValue) + { + if (key < 0) + Context.CodeBug (); + int index = findIndex (key); + if (0 <= index) { + if (ivaluesShift != 0) { + return keys [ivaluesShift + index]; + } + return 0; + } + return defaultValue; + } + + /// Get integer value assigned with key. + /// key integer value or defaultValue if key does not exist or does + /// not have int value + /// + /// RuntimeException if key does not exist + public virtual int getExistingInt (int key) + { + if (key < 0) + Context.CodeBug (); + int index = findIndex (key); + if (0 <= index) { + if (ivaluesShift != 0) { + return keys [ivaluesShift + index]; + } + return 0; + } + // Key must exist + Context.CodeBug (); + return 0; + } + + /// Set object value of the key. + /// If key does not exist, also set its int value to 0. + /// + public virtual void put (int key, object value) + { + if (key < 0) + Context.CodeBug (); + int index = ensureIndex (key, false); + if (values == null) { + values = new object [1 << power]; + } + values [index] = value; + } + + /// Set int value of the key. + /// If key does not exist, also set its object value to null. + /// + public virtual void put (int key, int value) + { + if (key < 0) + Context.CodeBug (); + int index = ensureIndex (key, true); + if (ivaluesShift == 0) { + int N = 1 << power; + // keys.length can be N * 2 after clear which set ivaluesShift to 0 + if (keys.Length != N * 2) { + int [] tmp = new int [N * 2]; + Array.Copy (keys, 0, tmp, 0, N); + keys = tmp; + } + ivaluesShift = N; + } + keys [ivaluesShift + index] = value; + } + + public virtual void remove (int key) + { + if (key < 0) + Context.CodeBug (); + int index = findIndex (key); + if (0 <= index) { + keys [index] = DELETED; + --keyCount; + // Allow to GC value and make sure that new key with the deleted + // slot shall get proper default values + if (values != null) { + values [index] = null; + } + if (ivaluesShift != 0) { + keys [ivaluesShift + index] = 0; + } + } + } + + public virtual void clear () + { + int N = 1 << power; + if (keys != null) { + for (int i = 0; i != N; ++i) { + keys [i] = EMPTY; + } + if (values != null) { + for (int i = 0; i != N; ++i) { + values [i] = null; + } + } + } + ivaluesShift = 0; + keyCount = 0; + occupiedCount = 0; + } + + private static int tableLookupStep (int fraction, int mask, int power) + { + int shift = 32 - 2 * power; + if (shift >= 0) { + return (((int)((uint)fraction >> shift)) & mask) | 1; + } + else { + return (fraction & (int)((uint)mask >> -shift)) | 1; + } + } + + private int findIndex (int key) + { + int [] keys = this.keys; + if (keys != null) { + int fraction = key * A; + int index = (int)((uint)fraction >> (32 - power)); + int entry = keys [index]; + if (entry == key) { + return index; + } + if (entry != EMPTY) { + // Search in table after first failed attempt + int mask = (1 << power) - 1; + int step = tableLookupStep (fraction, mask, power); + int n = 0; + do { + if (check) { + if (n >= occupiedCount) + Context.CodeBug (); + ++n; + } + index = (index + step) & mask; + entry = keys [index]; + if (entry == key) { + return index; + } + } + while (entry != EMPTY); + } + } + return -1; + } + + // Insert key that is not present to table without deleted entries + // and enough free space + private int insertNewKey (int key) + { + if (check && occupiedCount != keyCount) + Context.CodeBug (); + if (check && keyCount == 1 << power) + Context.CodeBug (); + int [] keys = this.keys; + int fraction = key * A; + int index = (int)((uint)fraction >> (32 - power)); + if (keys [index] != EMPTY) { + int mask = (1 << power) - 1; + int step = tableLookupStep (fraction, mask, power); + int firstIndex = index; + do { + if (check && keys [index] == DELETED) + Context.CodeBug (); + index = (index + step) & mask; + if (check && firstIndex == index) + Context.CodeBug (); + } + while (keys [index] != EMPTY); + } + keys [index] = key; + ++occupiedCount; + ++keyCount; + return index; + } + + private void rehashTable (bool ensureIntSpace) + { + if (keys != null) { + // Check if removing deleted entries would free enough space + if (keyCount * 2 >= occupiedCount) { + // Need to grow: less then half of deleted entries + ++power; + } + } + int N = 1 << power; + int [] old = keys; + int oldShift = ivaluesShift; + if (oldShift == 0 && !ensureIntSpace) { + keys = new int [N]; + } + else { + ivaluesShift = N; + keys = new int [N * 2]; + } + for (int i = 0; i != N; ++i) { + keys [i] = EMPTY; + } + + object [] oldValues = values; + if (oldValues != null) { + values = new object [N]; + } + + int oldCount = keyCount; + occupiedCount = 0; + if (oldCount != 0) { + keyCount = 0; + for (int i = 0, remaining = oldCount; remaining != 0; ++i) { + int key = old [i]; + if (key != EMPTY && key != DELETED) { + int index = insertNewKey (key); + if (oldValues != null) { + values [index] = oldValues [i]; + } + if (oldShift != 0) { + keys [ivaluesShift + index] = old [oldShift + i]; + } + --remaining; + } + } + } + } + + // Ensure key index creating one if necessary + private int ensureIndex (int key, bool intType) + { + int index = -1; + int firstDeleted = -1; + int [] keys = this.keys; + if (keys != null) { + int fraction = key * A; + index = (int)((uint)fraction >> (32 - power)); + int entry = keys [index]; + if (entry == key) { + return index; + } + if (entry != EMPTY) { + if (entry == DELETED) { + firstDeleted = index; + } + // Search in table after first failed attempt + int mask = (1 << power) - 1; + int step = tableLookupStep (fraction, mask, power); + int n = 0; + do { + if (check) { + if (n >= occupiedCount) + Context.CodeBug (); + ++n; + } + index = (index + step) & mask; + entry = keys [index]; + if (entry == key) { + return index; + } + if (entry == DELETED && firstDeleted < 0) { + firstDeleted = index; + } + } + while (entry != EMPTY); + } + } + // Inserting of new key + if (check && keys != null && keys [index] != EMPTY) + Context.CodeBug (); + if (firstDeleted >= 0) { + index = firstDeleted; + } + else { + // Need to consume empty entry: check occupation level + if (keys == null || occupiedCount * 4 >= (1 << power) * 3) { + // Too litle unused entries: rehash + rehashTable (intType); + keys = this.keys; + return insertNewKey (key); + } + ++occupiedCount; + } + keys [index] = key; + ++keyCount; + return index; + } + // A == golden_ratio * (1 << 32) = ((sqrt(5) - 1) / 2) * (1 << 32) + // See Knuth etc. + private const int A = unchecked ((int)0x9e3779b9); + + private const int EMPTY = -1; + private const int DELETED = -2; + + // Structure of kyes and values arrays (N == 1 << power): + // keys[0 <= i < N]: key value or EMPTY or DELETED mark + // values[0 <= i < N]: value of key at keys[i] + // keys[N <= i < 2N]: int values of keys at keys[i - N] + + + private int [] keys; + + private object [] values; + + private int power; + private int keyCount; + + private int occupiedCount; // == keyCount + deleted_count + + // If ivaluesShift != 0, keys[ivaluesShift + index] contains integer + // values associated with keys + + private int ivaluesShift; + + // If true, enables consitency checks + private static readonly bool check = false; // TODO: make me a preprocessor directive + + } } \ No newline at end of file diff --git a/Code/EcmaScript.NET/CompilerEnvirons.cs b/src/EcmaScript.NET/CompilerEnvirons.cs similarity index 96% rename from Code/EcmaScript.NET/CompilerEnvirons.cs rename to src/EcmaScript.NET/CompilerEnvirons.cs index 4894638..9e376ff 100644 --- a/Code/EcmaScript.NET/CompilerEnvirons.cs +++ b/src/EcmaScript.NET/CompilerEnvirons.cs @@ -1,166 +1,166 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; -using System.Collections; - -namespace EcmaScript.NET -{ - - public class CompilerEnvirons - { - virtual public bool UseDynamicScope - { - get - { - return useDynamicScope; - } - - } - public CompilerEnvirons () - { - errorReporter = DefaultErrorReporter.instance; - languageVersion = Context.Versions.Default; - generateDebugInfo = true; - useDynamicScope = false; - reservedKeywordAsIdentifier = false; - allowMemberExprAsFunctionName = false; - xmlAvailable = true; - optimizationLevel = 0; - generatingSource = true; - } - - public virtual void initFromContext (Context cx) - { - setErrorReporter (cx.ErrorReporter); - this.languageVersion = cx.Version; - useDynamicScope = cx.compileFunctionsWithDynamicScopeFlag; - generateDebugInfo = (!cx.GeneratingDebugChanged || cx.GeneratingDebug); - reservedKeywordAsIdentifier = cx.HasFeature (Context.Features.ReservedKeywordAsIdentifier); - allowMemberExprAsFunctionName = cx.HasFeature (Context.Features.MemberExprAsFunctionName); - xmlAvailable = cx.HasFeature (Context.Features.E4x); - getterAndSetterSupport = cx.HasFeature (Context.Features.GetterAndSetter); - - optimizationLevel = cx.OptimizationLevel; - - generatingSource = cx.GeneratingSource; - activationNames = cx.activationNames; - } - - public ErrorReporter getErrorReporter () - { - return errorReporter; - } - - public virtual void setErrorReporter (ErrorReporter errorReporter) - { - if (errorReporter == null) - throw new ArgumentException (); - this.errorReporter = errorReporter; - } - - public Context.Versions LanguageVersion - { - get - { - return languageVersion; - } - set - { - languageVersion = value; - } - } - - public bool isGenerateDebugInfo () - { - return generateDebugInfo; - } - - public virtual void setGenerateDebugInfo (bool flag) - { - this.generateDebugInfo = flag; - } - - public bool isReservedKeywordAsIdentifier () - { - return reservedKeywordAsIdentifier; - } - - public virtual void setReservedKeywordAsIdentifier (bool flag) - { - reservedKeywordAsIdentifier = flag; - } - - public bool isAllowMemberExprAsFunctionName () - { - return allowMemberExprAsFunctionName; - } - - public virtual void setAllowMemberExprAsFunctionName (bool flag) - { - allowMemberExprAsFunctionName = flag; - } - - public bool isXmlAvailable () - { - return xmlAvailable; - } - - public virtual void setXmlAvailable (bool flag) - { - xmlAvailable = flag; - } - - public int getOptimizationLevel () - { - return optimizationLevel; - } - - public virtual void setOptimizationLevel (int level) - { - Context.CheckOptimizationLevel (level); - this.optimizationLevel = level; - } - - public bool isGeneratingSource () - { - return generatingSource; - } - - /// Specify whether or not source information should be generated. - ///

- /// Without source information, evaluating the "toString" method - /// on JavaScript functions produces only "[native code]" for - /// the body of the function. - /// Note that code generated without source is not fully ECMA - /// conformant. - ///

- public virtual void setGeneratingSource (bool generatingSource) - { - this.generatingSource = generatingSource; - } - - private ErrorReporter errorReporter; - - private Context.Versions languageVersion; - private bool generateDebugInfo; - private bool useDynamicScope; - private bool reservedKeywordAsIdentifier; - private bool allowMemberExprAsFunctionName; - private bool xmlAvailable; - private int optimizationLevel; - private bool generatingSource; - internal Hashtable activationNames; - internal bool getterAndSetterSupport; - } +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; +using System.Collections; + +namespace EcmaScript.NET +{ + + public class CompilerEnvirons + { + virtual public bool UseDynamicScope + { + get + { + return useDynamicScope; + } + + } + public CompilerEnvirons () + { + errorReporter = DefaultErrorReporter.instance; + languageVersion = Context.Versions.Default; + generateDebugInfo = true; + useDynamicScope = false; + reservedKeywordAsIdentifier = false; + allowMemberExprAsFunctionName = false; + xmlAvailable = true; + optimizationLevel = 0; + generatingSource = true; + } + + public virtual void initFromContext (Context cx) + { + setErrorReporter (cx.ErrorReporter); + this.languageVersion = cx.Version; + useDynamicScope = cx.compileFunctionsWithDynamicScopeFlag; + generateDebugInfo = (!cx.GeneratingDebugChanged || cx.GeneratingDebug); + reservedKeywordAsIdentifier = cx.HasFeature (Context.Features.ReservedKeywordAsIdentifier); + allowMemberExprAsFunctionName = cx.HasFeature (Context.Features.MemberExprAsFunctionName); + xmlAvailable = cx.HasFeature (Context.Features.E4x); + getterAndSetterSupport = cx.HasFeature (Context.Features.GetterAndSetter); + + optimizationLevel = cx.OptimizationLevel; + + generatingSource = cx.GeneratingSource; + activationNames = cx.activationNames; + } + + public ErrorReporter getErrorReporter () + { + return errorReporter; + } + + public virtual void setErrorReporter (ErrorReporter errorReporter) + { + if (errorReporter == null) + throw new ArgumentException (); + this.errorReporter = errorReporter; + } + + public Context.Versions LanguageVersion + { + get + { + return languageVersion; + } + set + { + languageVersion = value; + } + } + + public bool isGenerateDebugInfo () + { + return generateDebugInfo; + } + + public virtual void setGenerateDebugInfo (bool flag) + { + this.generateDebugInfo = flag; + } + + public bool isReservedKeywordAsIdentifier () + { + return reservedKeywordAsIdentifier; + } + + public virtual void setReservedKeywordAsIdentifier (bool flag) + { + reservedKeywordAsIdentifier = flag; + } + + public bool isAllowMemberExprAsFunctionName () + { + return allowMemberExprAsFunctionName; + } + + public virtual void setAllowMemberExprAsFunctionName (bool flag) + { + allowMemberExprAsFunctionName = flag; + } + + public bool isXmlAvailable () + { + return xmlAvailable; + } + + public virtual void setXmlAvailable (bool flag) + { + xmlAvailable = flag; + } + + public int getOptimizationLevel () + { + return optimizationLevel; + } + + public virtual void setOptimizationLevel (int level) + { + Context.CheckOptimizationLevel (level); + this.optimizationLevel = level; + } + + public bool isGeneratingSource () + { + return generatingSource; + } + + /// Specify whether or not source information should be generated. + ///

+ /// Without source information, evaluating the "toString" method + /// on JavaScript functions produces only "[native code]" for + /// the body of the function. + /// Note that code generated without source is not fully ECMA + /// conformant. + ///

+ public virtual void setGeneratingSource (bool generatingSource) + { + this.generatingSource = generatingSource; + } + + private ErrorReporter errorReporter; + + private Context.Versions languageVersion; + private bool generateDebugInfo; + private bool useDynamicScope; + private bool reservedKeywordAsIdentifier; + private bool allowMemberExprAsFunctionName; + private bool xmlAvailable; + private int optimizationLevel; + private bool generatingSource; + internal Hashtable activationNames; + internal bool getterAndSetterSupport; + } } \ No newline at end of file diff --git a/Code/EcmaScript.NET/Context.cs b/src/EcmaScript.NET/Context.cs similarity index 96% rename from Code/EcmaScript.NET/Context.cs rename to src/EcmaScript.NET/Context.cs index 4d8432a..cc85274 100644 --- a/Code/EcmaScript.NET/Context.cs +++ b/src/EcmaScript.NET/Context.cs @@ -1,2013 +1,2013 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; -using System.IO; -using System.Threading; -using System.Globalization; -using System.Reflection; -using System.Collections; - -using EcmaScript.NET.Debugging; -using EcmaScript.NET.Collections; -using EcmaScript.NET.Types; -using EcmaScript.NET.Types.Cli; -using EcmaScript.NET.Types.E4X; - -namespace EcmaScript.NET -{ - - /// - /// This class represents the runtime context of an executing script. - /// - /// Before executing a script, an instance of Context must be created - /// and associated with the thread that will be executing the script. - /// The Context will be used to store information about the executing - /// of the script such as the call stack. Contexts are associated with - /// the current thread using the {@link #call(ContextAction)} - /// or {@link #enter()} methods.

- /// - /// Different forms of script execution are supported. Scripts may be - /// evaluated from the source directly, or first compiled and then later - /// executed. Interactive execution is also supported.

- /// - /// Some aspects of script execution, such as type conversions and - /// object creation, may be accessed directly through methods of - /// Context. - /// - ///

- public class Context : IDisposable - { - - public enum Features - { - /// - /// No features at all - /// - None = 0, - - /// - /// Support for E4X - /// - E4x = 1 << 1, - - /// - /// Support for get and set - /// - GetterAndSetter = 1 << 2, - - /// - /// - /// - NonEcmaGetYear = 1 << 3, - - /// Control if dynamic scope should be used for name access. - /// If hasFeature(FEATURE_DYNAMIC_SCOPE) returns true, then the name lookup - /// during name resolution will use the top scope of the script or function - /// which is at the top of JS execution stack instead of the top scope of the - /// script or function from the current stack frame if the top scope of - /// the top stack frame contains the top scope of the current stack frame - /// on its prototype chain. - ///

- /// This is useful to define shared scope containing functions that can - /// be called from scripts and functions using private scopes. - ///

- /// By default {@link #hasFeature(int)} returns false. - ///

- DynamicScope = 1 << 4, - /// Control if member expression as function name extension is available. - /// If hasFeature(FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME) returns - /// true, allow function memberExpression(args) { body } to be - /// syntax sugar for memberExpression = function(args) { body }, - /// when memberExpression is not a simple identifier. - /// See ECMAScript-262, section 11.2 for definition of memberExpression. - /// By default {@link #hasFeature(int)} returns false. - /// - MemberExprAsFunctionName = 1 << 5, - - /// Control if reserved keywords are treated as identifiers. - /// If hasFeature(RESERVED_KEYWORD_AS_IDENTIFIER) returns true, - /// treat future reserved keyword (see Ecma-262, section 7.5.3) as ordinary - /// identifiers but warn about this usage. - /// - /// By default {@link #hasFeature(int)} returns false. - /// - ReservedKeywordAsIdentifier = 1 << 6, - - /// Control if toString() should returns the same result - /// as toSource() when applied to objects and arrays. - /// If hasFeature(FEATURE_TO_STRING_AS_SOURCE) returns true, - /// calling toString() on JS objects gives the same result as - /// calling toSource(). That is it returns JS source with code - /// to create an object with all enumeratable fields of the original object - /// instead of printing [object result of - /// {@link Scriptable#getClassName()}]. - ///

- /// By default {@link #hasFeature(int)} returns true only if - /// the current JS version is set to {@link #Versions.JS1_2}. - ///

- ToStringAsSource = 1 << 7, - - /// Control if properties __proto__ and __parent__ - /// are treated specially. - /// If hasFeature(FEATURE_PARENT_PROTO_PROPRTIES) returns true, - /// treat __parent__ and __proto__ as special properties. - ///

- /// The properties allow to query and set scope and prototype chains for the - /// objects. The special meaning of the properties is available - /// only when they are used as the right hand side of the dot operator. - /// For example, while x.__proto__ = y changes the prototype - /// chain of the object x to point to y, - /// x["__proto__"] = y simply assigns a new value to the property - /// __proto__ in x even when the feature is on. - /// - /// By default {@link #hasFeature(int)} returns true. - ///

- ParentProtoProperties = 1 << 8, - - /// Control if strict variable mode is enabled. - /// When the feature is on Rhino reports runtime errors if assignment - /// to a global variable that does not exist is executed. When the feature - /// is off such assignments creates new variable in the global scope as - /// required by ECMA 262. - ///

- /// By default {@link #hasFeature(int)} returns false. - ///

- StrictVars = 1 << 9, - - /// Control if strict eval mode is enabled. - /// When the feature is on Rhino reports runtime errors if non-string - /// argument is passed to the eval function. When the feature is off - /// eval simply return non-string argument as is without performing any - /// evaluation as required by ECMA 262. - ///

- /// By default {@link #hasFeature(int)} returns false. - ///

- StrictEval = 1 << 10, - - /// - /// Controls if the non-ecma function 'print' is available or not. - /// - NonEcmaPrintFunction = 1 << 11, - - /// - /// Controls if the non-ecma function 'version' is available or not - /// - NonEcmaVersionFunction = 1 << 12, - - /// - /// Controls if the non-ecma function 'options' is available or not - /// - NonEcmaOptionsFunction = 1 << 13, - - Strict = 1 << 14, - - /// - /// Controls if the non-ecma function 'options' is available or not - /// - NonEcmaGcFunction = 1 << 14, - - /// - /// The famous 'it' object (@see js.c:2315) - /// - NonEcmaItObject = 1 << 15, - - } - - public enum Versions - { - Unknown = -1, - Default = 0, - JS1_0 = 100, - JS1_1 = 110, - JS1_2 = 120, - JS1_3 = 130, - JS1_4 = 140, - JS1_5 = 150, - JS1_6 = 160 - } - - /// - /// - /// - public event ContextWrapHandler OnWrap; - - private int m_MaximumInterpreterStackDepth = 8092; - - /// - /// - /// - public int MaximumInterpreterStackDepth - { - get - { - return m_MaximumInterpreterStackDepth; - } - set - { - if (Sealed) - OnSealedMutation (); - m_MaximumInterpreterStackDepth = value; - } - } - - private AppDomain m_AppDomain = null; - - /// - /// Associated app domain - /// - public AppDomain AppDomain - { - get { return m_AppDomain; } - } - - private static LocalDataStoreSlot LocalSlot - { - get - { - LocalDataStoreSlot slot = Thread.GetNamedDataSlot ("Context"); - if (slot == null) { - slot = Thread.AllocateNamedDataSlot ("Context"); - } - return slot; - } - } - - /// Get the current Context. - /// - /// The current Context is per-thread; this method looks up - /// the Context associated with the current thread.

- /// - ///

- /// the Context associated with the current thread, or - /// null if no context is associated with the current - /// thread. - /// - public static Context CurrentContext - { - get - { - Context cx = (Context)Thread.GetData (LocalSlot); - return cx; - } - } - - /// Return {@link ContextFactory} instance used to create this Context - /// or the result of {@link ContextFactory#getGlobal()} if no factory - /// was used for Context creation. - /// - public ContextFactory Factory - { - get - { - ContextFactory result = factory; - if (result == null) { - result = ContextFactory.Global; - } - return result; - } - - } - /// Checks if this is a sealed Context. A sealed Context instance does not - /// allow to modify any of its properties and will throw an exception - /// on any such attempt. - /// - public bool Sealed - { - get - { - return m_Sealed; - } - - } - /// Get the implementation version. - /// - ///

- /// The implementation version is of the form - ///

-        /// "name langVer release relNum date"
-        /// 
- /// where name is the name of the product, langVer is - /// the language version, relNum is the release number, and - /// date is the release date for that specific - /// release in the form "yyyy mm dd". - /// - ///
- /// a string that encodes the product, language version, release - /// number, and date. - /// - public string ImplementationVersion - { - get - { - // TODO: Probably it would be better to embed this directly into source - // TODO: with special build preprocessing but that would require some ant - // TODO: tweaking and then replacing token in resource files was simpler - if (implementationVersion == null) { - implementationVersion = ScriptRuntime.GetMessage ("implementation.version"); - } - return implementationVersion; - } - - } - /// Get the singleton object that represents the JavaScript Undefined value. - public static object UndefinedValue - { - get - { - return Undefined.Value; - } - - } - /// Specify whether or not debug information should be generated. - ///

- /// Setting the generation of debug information on will set the - /// optimization level to zero. - ///

- public bool GeneratingDebug - { - get - { - return generatingDebug; - } - - set - { - if (m_Sealed) - OnSealedMutation (); - generatingDebugChanged = true; - if (value && OptimizationLevel > 0) - OptimizationLevel = 0; - this.generatingDebug = value; - } - - } - - /// Specify whether or not source information should be generated. - ///

- /// Without source information, evaluating the "toString" method - /// on JavaScript functions produces only "[native code]" for - /// the body of the function. - /// Note that code generated without source is not fully ECMA - /// conformant. - ///

- public bool GeneratingSource - { - get - { - return generatingSource; - } - set - { - if (m_Sealed) - OnSealedMutation (); - this.generatingSource = value; - } - - } - /// Set the current optimization level. - ///

- /// The optimization level is expected to be an integer between -1 and - /// 9. Any negative values will be interpreted as -1, and any values - /// greater than 9 will be interpreted as 9. - /// An optimization level of -1 indicates that interpretive mode will - /// always be used. Levels 0 through 9 indicate that class files may - /// be generated. Higher optimization levels trade off compile time - /// performance for runtime performance. - /// The optimizer level can't be set greater than -1 if the optimizer - /// package doesn't exist at run time. - ///

- /// an integer indicating the level of - /// optimization to perform - /// - public int OptimizationLevel - { - get - { - return m_OptimizationLevel; - } - - set - { - if (m_Sealed) - OnSealedMutation (); - if (value == -2) { - // To be compatible with Cocoon fork - value = -1; - } - CheckOptimizationLevel (value); - this.m_OptimizationLevel = value; - } - - } - - /// Return the debugger context data associated with current context. - /// the debugger data, or null if debugger is not attached - /// - public object DebuggerContextData - { - get - { - return debuggerData; - } - - } - - public object Wrap (IScriptable scope, object obj, Type staticType) - { - if (obj == null || obj is IScriptable) - return obj; - if (staticType == null) - staticType = obj.GetType (); - - if (staticType.IsArray) - return new CliArray (scope, obj as Array); - - if (staticType.IsPrimitive) - return obj; - - if (OnWrap != null) { - ContextWrapEventArgs e = new ContextWrapEventArgs (this, scope, obj, staticType); - OnWrap (this, e); - obj = e.Target; - } - - return new CliObject (obj); - } - - /// Get/Set threshold of executed instructions counter that triggers call to - /// observeInstructionCount(). - /// When the threshold is zero, instruction counting is disabled, - /// otherwise each time the run-time executes at least the threshold value - /// of script instructions, observeInstructionCount() will - /// be called. - /// - public int InstructionObserverThreshold - { - get - { - return instructionThreshold; - } - - set - { - if (m_Sealed) - OnSealedMutation (); - if (value < 0) - throw new ArgumentException (); - instructionThreshold = value; - } - - } - - internal RegExpProxy RegExpProxy - { - get - { - if (regExpProxy == null) { - regExpProxy = new Types.RegExp.RegExpImpl (); - } - return regExpProxy; - } - set { - regExpProxy = value; - } - } - internal bool VersionECMA1 - { - get - { - return m_Version == Versions.Default || m_Version >= Versions.JS1_3; - } - - } - public bool GeneratingDebugChanged - { - get - { - return generatingDebugChanged; - } - - } - /// Language versions. - /// - /// All integral values are reserved for future version numbers. - /// - - - - - - - - - - - - public const string languageVersionProperty = "language version"; - public const string errorReporterProperty = "error reporter"; - - /// Convinient value to use as zero-length array of objects. - public static readonly object [] EmptyArgs; - - /// Create a new Context. - /// - /// Note that the Context must be associated with a thread before - /// it can be used to execute a script. - /// - /// - public Context (AppDomain appDomain) - { - if (appDomain == null) - throw new ArgumentNullException ("appDomain"); - Version = Versions.Default; - m_AppDomain = appDomain; - } - - /// Get a context associated with the current thread, creating - /// one if need be. - /// - /// The Context stores the execution state of the JavaScript - /// engine, so it is required that the context be entered - /// before execution may begin. Once a thread has entered - /// a Context, then getCurrentContext() may be called to find - /// the context that is associated with the current thread. - ///

- /// Calling enter() will - /// return either the Context currently associated with the - /// thread, or will create a new context and associate it - /// with the current thread. Each call to enter() - /// must have a matching call to exit(). For example, - ///

-        /// Context cx = Context.enter();
-        /// try {
-        /// ...
-        /// cx.evaluateString(...);
-        /// } finally {
-        /// Context.exit();
-        /// }
-        /// 
- /// Instead of using enter(), exit() pair consider using - /// {@link #call(ContextAction)} which guarantees proper - /// association of Context instances with the current thread and is faster. - /// With this method the above example becomes: - ///
-        /// Context.call(new ContextAction() {
-        /// public Object run(Context cx) {
-        /// ...
-        /// cx.evaluateString(...);
-        /// return null;
-        /// }
-        /// });
-        /// 
- /// - ///
- /// a Context associated with the current thread - /// - public static Context Enter () - { - return Enter (null, AppDomain.CurrentDomain); - } - - public static Context Enter (AppDomain appDomain) - { - return Enter (null, appDomain); - } - - /// Get a Context associated with the current thread, using - /// the given Context if need be. - ///

- /// The same as enter() except that cx - /// is associated with the current thread and returned if - /// the current thread has no associated context and cx - /// is not associated with any other thread. - ///

- /// a Context to associate with the thread if possible - /// - /// a Context associated with the current thread - /// - /// - public static Context Enter (Context cx) - { - return Enter (cx, AppDomain.CurrentDomain); - } - - public static Context Enter (Context cx, AppDomain appDomain) - { - Context old = CurrentContext; - if (old != null) { - if (cx != null && cx != old && cx.enterCount != 0) { - // The suplied context must be the context for - // the current thread if it is already entered - throw new ArgumentException ("Cannot enter Context active on another thread"); - } - if (old.factory != null) { - // Context with associated factory will be released - // automatically and does not need to change enterCount - return old; - } - if (old.m_Sealed) - OnSealedMutation (); - cx = old; - } - else { - if (cx == null) { - cx = new Context (appDomain); - } - else { - if (cx.m_Sealed) - OnSealedMutation (); - } - if (cx.enterCount != 0 || cx.factory != null) { - throw new ApplicationException (); - } - - if (!cx.creationEventWasSent) { - cx.creationEventWasSent = true; - ContextFactory.Global.FireOnContextCreated (cx); - } - } - - if (old == null) { - Thread.SetData (LocalSlot, cx); - } - ++cx.enterCount; - - return cx; - } - - /// Exit a block of code requiring a Context. - /// - /// Calling exit() will remove the association between - /// the current thread and a Context if the prior call to - /// enter() on this thread newly associated a Context - /// with this thread. - /// Once the current thread no longer has an associated Context, - /// it cannot be used to execute JavaScript until it is again associated - /// with a Context. - /// - /// - public static void Exit () - { - Context cx = CurrentContext; - if (cx == null) { - throw new ApplicationException ("Calling Context.exit without previous Context.enter"); - } - if (cx.factory != null) { - // Context with associated factory will be released - // automatically and does not need to change enterCount - return; - } - if (cx.enterCount < 1) - Context.CodeBug (); - if (cx.m_Sealed) - OnSealedMutation (); - --cx.enterCount; - if (cx.enterCount == 0) { - Thread.SetData (LocalSlot, null); - ContextFactory.Global.FireOnContextReleased (cx); - } - } - - - /// Call {@link - /// Callable#call(Context cx, Scriptable scope, Scriptable thisObj, - /// Object[] args)} - /// using the Context instance associated with the current thread. - /// If no Context is associated with the thread, then - /// {@link ContextFactory#makeContext()} will be called to construct - /// new Context instance. The instance will be temporary associated - /// with the thread during call to {@link ContextAction#run(Context)}. - ///

- /// It is allowed to use null for factory argument - /// in which case the factory associated with the scope will be - /// used to create new context instances. - /// - ///

- public static object Call (ContextFactory factory, ICallable callable, IScriptable scope, IScriptable thisObj, object [] args) - { - if (factory == null) { - factory = ContextFactory.Global; - } - - Context cx = CurrentContext; - if (cx != null) { - object result; - if (cx.factory != null) { - result = callable.Call (cx, scope, thisObj, args); - } - else { - // Context was associated with the thread via Context.enter, - // set factory to make Context.enter/exit to be no-op - // during call - cx.factory = factory; - try { - result = callable.Call (cx, scope, thisObj, args); - } - finally { - cx.factory = null; - } - } - return result; - } - - cx = PrepareNewContext (AppDomain.CurrentDomain, factory); - try { - return callable.Call (cx, scope, thisObj, args); - } - finally { - ReleaseContext (cx); - } - } - - internal void InitDefaultFeatures () - { - m_Features = Features.None; - - SetFeature (Features.E4x, (Version == Context.Versions.Default || Version >= Context.Versions.JS1_6)); - SetFeature (Features.GetterAndSetter, (Version == Context.Versions.Default || Version >= Context.Versions.JS1_5)); - SetFeature (Features.NonEcmaGetYear, (Version == Context.Versions.JS1_0 || Version == Context.Versions.JS1_1 || Version == Context.Versions.JS1_2)); - SetFeature (Features.ToStringAsSource, Version == Context.Versions.JS1_2); - SetFeature (Features.ParentProtoProperties, true); - } - - - private static Context PrepareNewContext (AppDomain appDomain, ContextFactory factory) - { - Context cx = new Context (appDomain); - if (cx.factory != null || cx.enterCount != 0) { - throw new ApplicationException ("factory.makeContext() returned Context instance already associated with some thread"); - } - cx.factory = factory; - factory.FireOnContextCreated (cx); - if (factory.Sealed && !cx.Sealed) { - cx.Seal ((object)null); - } - Thread.SetData (LocalSlot, cx); - return cx; - } - - private static void ReleaseContext (Context cx) - { - Thread.SetData (LocalSlot, null); - try { - cx.factory.FireOnContextReleased (cx); - } - finally { - cx.factory = null; - } - } - - - - /// Seal this Context object so any attempt to modify any of its properties - /// including calling {@link #enter()} and {@link #exit()} methods will - /// throw an exception. - ///

- /// If sealKey is not null, calling - /// {@link #unseal(Object sealKey)} with the same key unseals - /// the object. If sealKey is null, unsealing is no longer possible. - /// - ///

- public void Seal (object sealKey) - { - if (m_Sealed) - OnSealedMutation (); - m_Sealed = true; - this.m_SealKey = sealKey; - } - - /// Unseal previously sealed Context object. - /// The sealKey argument should not be null and should match - /// sealKey suplied with the last call to - /// {@link #seal(Object)} or an exception will be thrown. - /// - /// - public void Unseal (object sealKey) - { - if (sealKey == null) - throw new ArgumentException (); - if (this.m_SealKey != sealKey) - throw new ArgumentException (); - if (!m_Sealed) - throw new ApplicationException (); - m_Sealed = false; - this.m_SealKey = null; - } - - internal static void OnSealedMutation () - { - throw new ApplicationException (); - } - - /// - /// Get the current language version. - /// - public Versions Version - { - get - { - return m_Version; - } - set - { - if (m_Sealed) - OnSealedMutation (); - this.m_Version = value; - - InitDefaultFeatures (); - } - } - - public static bool IsValidLanguageVersion (int version) - { - return ToValidLanguageVersion (version) != Versions.Unknown; - } - - public static Versions ToValidLanguageVersion (int version) - { - Versions ver = Versions.Unknown; - if (version > 0 || version < (int)Versions.JS1_6) - ver = (Versions)version; - return ver; - } - - public static void CheckLanguageVersion (int version) - { - if (IsValidLanguageVersion (version)) { - return; - } - throw new ArgumentException ("Bad language version: " + version); - } - - /// Get the current error reporter. - /// - /// - public ErrorReporter ErrorReporter - { - get - { - if (m_ErrorReporter == null) { - return DefaultErrorReporter.instance; - } - return m_ErrorReporter; - } - set - { - if (m_Sealed) - OnSealedMutation (); - if (value == null) - throw new ArgumentException (); - this.m_ErrorReporter = value; - } - } - - /// - /// Get the current locale. Returns the default locale if none has - /// been set. - /// - public CultureInfo CurrentCulture - { - get - { - if (culture == null) - culture = Thread.CurrentThread.CurrentCulture; - return culture; - } - set - { - if (m_Sealed) - OnSealedMutation (); - culture = value; - - // HACK: Needed for example on DateTime.ToString() - if (Thread.CurrentThread.CurrentCulture != culture) - Thread.CurrentThread.CurrentCulture = culture; - } - } - - /// Report a warning using the error reporter for the current thread. - /// - /// - /// the warning message to report - /// - /// a string describing the source, such as a filename - /// - /// the starting line number - /// - /// the text of the line (may be null) - /// - /// the offset into lineSource where problem was detected - /// - - public static void ReportWarning (string message, string sourceName, int lineno, string lineSource, int lineOffset) - { - Context cx = Context.CurrentContext; - cx.ErrorReporter.Warning (message, sourceName, lineno, lineSource, lineOffset); - } - - public static void ReportWarningById (string messageId, params string [] arguments) - { - int [] linep = new int [] { 0 }; - string filename = GetSourcePositionFromStack (linep); - Context.ReportWarning (ScriptRuntime.GetMessage (messageId, arguments), filename, linep [0], null, 0); - } - - /// Report a warning using the error reporter for the current thread. - /// - /// - /// the warning message to report - /// - public static void ReportWarning (string message) - { - int [] linep = new int [] { 0 }; - string filename = GetSourcePositionFromStack (linep); - Context.ReportWarning (message, filename, linep [0], null, 0); - } - - /// Report an error using the error reporter for the current thread. - /// - /// - /// the error message to report - /// - /// a string describing the source, such as a filename - /// - /// the starting line number - /// - /// the text of the line (may be null) - /// - /// the offset into lineSource where problem was detected - /// - public static void ReportError (string message, string sourceName, int lineno, string lineSource, int lineOffset) - { - Context cx = CurrentContext; - if (cx != null) { - cx.ErrorReporter.Error (message, sourceName, lineno, lineSource, lineOffset); - } - else { - throw new EcmaScriptRuntimeException (message, sourceName, lineno, lineSource, lineOffset); - } - } - - /// Report an error using the error reporter for the current thread. - /// - /// - /// the error message to report - /// - - public static void ReportError (string message) - { - int [] linep = new int [] { 0 }; - string filename = GetSourcePositionFromStack (linep); - Context.ReportError (message, filename, linep [0], null, 0); - } - - /// Report a runtime error using the error reporter for the current thread. - /// - /// - /// the error message to report - /// - /// a string describing the source, such as a filename - /// - /// the starting line number - /// - /// the text of the line (may be null) - /// - /// the offset into lineSource where problem was detected - /// - /// a runtime exception that will be thrown to terminate the - /// execution of the script - /// - public static EcmaScriptRuntimeException ReportRuntimeError (string message, string sourceName, int lineno, string lineSource, int lineOffset) - { - Context cx = CurrentContext; - if (cx != null) { - return cx.ErrorReporter.RuntimeError (message, sourceName, lineno, lineSource, lineOffset); - } - else { - throw new EcmaScriptRuntimeException (message, sourceName, lineno, lineSource, lineOffset); - } - } - - - internal static EcmaScriptRuntimeException ReportRuntimeErrorById (string messageId, params object [] args) - { - return ReportRuntimeError (ScriptRuntime.GetMessage (messageId, args)); - } - - /// Report a runtime error using the error reporter for the current thread. - /// - /// - /// the error message to report - /// - public static EcmaScriptRuntimeException ReportRuntimeError (string message) - { - int [] linep = new int [] { 0 }; - string filename = GetSourcePositionFromStack (linep); - return Context.ReportRuntimeError (message, filename, linep [0], null, 0); - } - - /// Initialize the standard objects. - /// - /// Creates instances of the standard objects and their constructors - /// (Object, String, Number, Date, etc.), setting up 'scope' to act - /// as a global object as in ECMA 15.1.

- /// - /// This method must be called to initialize a scope before scripts - /// can be evaluated in that scope.

- /// - /// This method does not affect the Context it is called upon. - /// - ///

- /// the initialized scope - /// - public ScriptableObject InitStandardObjects () - { - return InitStandardObjects (null, false); - } - - /// Initialize the standard objects. - /// - /// Creates instances of the standard objects and their constructors - /// (Object, String, Number, Date, etc.), setting up 'scope' to act - /// as a global object as in ECMA 15.1.

- /// - /// This method must be called to initialize a scope before scripts - /// can be evaluated in that scope.

- /// - /// This method does not affect the Context it is called upon. - /// - ///

- /// the scope to initialize, or null, in which case a new - /// object will be created to serve as the scope - /// - /// the initialized scope. The method returns the value of the scope - /// argument if it is not null or newly allocated scope object which - /// is an instance {@link ScriptableObject}. - /// - public IScriptable InitStandardObjects (ScriptableObject scope) - { - return InitStandardObjects (scope, false); - } - - /// Initialize the standard objects. - /// - /// Creates instances of the standard objects and their constructors - /// (Object, String, Number, Date, etc.), setting up 'scope' to act - /// as a global object as in ECMA 15.1.

- /// - /// This method must be called to initialize a scope before scripts - /// can be evaluated in that scope.

- /// - /// This method does not affect the Context it is called upon.

- /// - /// This form of the method also allows for creating "sealed" standard - /// objects. An object that is sealed cannot have properties added, changed, - /// or removed. This is useful to create a "superglobal" that can be shared - /// among several top-level objects. Note that sealing is not allowed in - /// the current ECMA/ISO language specification, but is likely for - /// the next version. - /// - ///

- /// the scope to initialize, or null, in which case a new - /// object will be created to serve as the scope - /// - /// whether or not to create sealed standard objects that - /// cannot be modified. - /// - /// the initialized scope. The method returns the value of the scope - /// argument if it is not null or newly allocated scope object. - /// - public ScriptableObject InitStandardObjects (ScriptableObject scope, bool zealed) - { - return ScriptRuntime.InitStandardObjects (this, scope, zealed); - } - - /// Evaluate a JavaScript source string. - /// - /// The provided source name and line number are used for error messages - /// and for producing debug information. - /// - /// - /// the scope to execute in - /// - /// the JavaScript source - /// - /// a string describing the source, such as a filename - /// - /// the starting line number - /// - /// an arbitrary object that specifies security - /// information about the origin or owner of the script. For - /// implementations that don't care about security, this value - /// may be null. - /// - /// the result of evaluating the string - /// - public object EvaluateString (IScriptable scope, string source, string sourceName, int lineno, object securityDomain) - { - IScript script = CompileString (source, sourceName, lineno, securityDomain); - if (script != null) { - return script.Exec (this, scope); - } - else { - return null; - } - } - - /// Evaluate a reader as JavaScript source. - /// - /// All characters of the reader are consumed. - /// - /// - /// the scope to execute in - /// - /// the Reader to get JavaScript source from - /// - /// a string describing the source, such as a filename - /// - /// the starting line number - /// - /// an arbitrary object that specifies security - /// information about the origin or owner of the script. For - /// implementations that don't care about security, this value - /// may be null. - /// - /// the result of evaluating the source - /// - /// - /// IOException if an IOException was generated by the Reader - /// - public object EvaluateReader (IScriptable scope, StreamReader sr, string sourceName, int lineno, object securityDomain) - { - IScript script = CompileReader (sr, sourceName, lineno, securityDomain); - - if (script != null) { - return script.Exec (this, scope); - } - return null; - } - - /// Check whether a string is ready to be compiled. - ///

- /// stringIsCompilableUnit is intended to support interactive compilation of - /// javascript. If compiling the string would result in an error - /// that might be fixed by appending more source, this method - /// returns false. In every other case, it returns true. - ///

- /// Interactive shells may accumulate source lines, using this - /// method after each new line is appended to check whether the - /// statement being entered is complete. - /// - ///

- /// the source buffer to check - /// - /// whether the source is ready for compilation - /// - public ScriptOrFnNode IsCompilableUnit (string source) - { - ScriptOrFnNode ret = null; - bool errorseen = false; - CompilerEnvirons compilerEnv = new CompilerEnvirons (); - compilerEnv.initFromContext (this); - // no source name or source text manager, because we're just - // going to throw away the result. - compilerEnv.setGeneratingSource (false); - Parser p = new Parser (compilerEnv, DefaultErrorReporter.instance); - try { - ret = p.Parse (source, null, 1); - } - catch (EcmaScriptRuntimeException) { - errorseen = true; - } - // Return false only if an error occurred as a result of reading past - // the end of the file, i.e. if the source could be fixed by - // appending more source. - if (!(errorseen && p.Eof)) - return ret; - return null; - } - - - /// Compiles the source in the given reader. - ///

- /// Returns a script that may later be executed. - /// Will consume all the source in the reader. - /// - ///

- /// the input reader - /// - /// a string describing the source, such as a filename - /// - /// the starting line number for reporting errors - /// - /// an arbitrary object that specifies security - /// information about the origin or owner of the script. For - /// implementations that don't care about security, this value - /// may be null. - /// - /// a script that may later be executed - /// - /// IOException if an IOException was generated by the Reader - /// - public IScript CompileReader (StreamReader sr, string sourceName, int lineno, object securityDomain) - { - if (lineno < 0) - throw new ArgumentException ("lineno may not be negative", "lineno"); - return (IScript)CompileImpl (null, sr, null, sourceName, lineno, securityDomain, false, null, null); - } - - /// Compiles the source in the given string. - ///

- /// Returns a script that may later be executed. - /// - ///

- /// the source string - /// - /// a string describing the source, such as a filename - /// - /// the starting line number for reporting errors - /// - /// an arbitrary object that specifies security - /// information about the origin or owner of the script. For - /// implementations that don't care about security, this value - /// may be null. - /// - /// a script that may later be executed - /// - public IScript CompileString (string source, string sourceName, int lineno, object securityDomain) - { - if (lineno < 0) { - // For compatibility IllegalArgumentException can not be thrown here - lineno = 0; - } - return CompileString (source, null, null, sourceName, lineno, securityDomain); - } - - internal IScript CompileString (string source, Interpreter compiler, ErrorReporter compilationErrorReporter, string sourceName, int lineno, object securityDomain) - { - return (IScript)CompileImpl (null, null, source, sourceName, lineno, securityDomain, false, compiler, compilationErrorReporter); - } - - /// Compile a JavaScript function. - ///

- /// The function source must be a function definition as defined by - /// ECMA (e.g., "function f(a) { return a; }"). - /// - ///

- /// the scope to compile relative to - /// - /// the function definition source - /// - /// a string describing the source, such as a filename - /// - /// the starting line number - /// - /// an arbitrary object that specifies security - /// information about the origin or owner of the script. For - /// implementations that don't care about security, this value - /// may be null. - /// - /// a Function that may later be called - /// - public IFunction CompileFunction (IScriptable scope, string source, string sourceName, int lineno, object securityDomain) - { - return CompileFunction (scope, source, null, null, sourceName, lineno, securityDomain); - } - - internal IFunction CompileFunction (IScriptable scope, string source, Interpreter compiler, ErrorReporter compilationErrorReporter, string sourceName, int lineno, object securityDomain) - { - return (IFunction)CompileImpl (scope, null, source, sourceName, lineno, securityDomain, true, compiler, compilationErrorReporter); - } - - /// Decompile the script. - ///

- /// The canonical source of the script is returned. - /// - ///

- /// the script to decompile - /// - /// the number of spaces to indent the result - /// - /// a string representing the script source - /// - public string DecompileScript (IScript script, int indent) - { - BuiltinFunction scriptImpl = (BuiltinFunction)script; - return scriptImpl.Decompile (indent, 0); - } - - /// Decompile a JavaScript Function. - ///

- /// Decompiles a previously compiled JavaScript function object to - /// canonical source. - ///

- /// Returns function body of '[native code]' if no decompilation - /// information is available. - /// - ///

- /// the JavaScript function to decompile - /// - /// the number of spaces to indent the result - /// - /// a string representing the function source - /// - public string DecompileFunction (IFunction fun, int indent) - { - if (fun is BaseFunction) - return ((BaseFunction)fun).Decompile (indent, 0); - else - return "function " + fun.ClassName + "() {\n\t[native code]\n}\n"; - } - - /// Decompile the body of a JavaScript Function. - ///

- /// Decompiles the body a previously compiled JavaScript Function - /// object to canonical source, omitting the function header and - /// trailing brace. - /// - /// Returns '[native code]' if no decompilation information is available. - /// - ///

- /// the JavaScript function to decompile - /// - /// the number of spaces to indent the result - /// - /// a string representing the function body source. - /// - public string DecompileFunctionBody (IFunction fun, int indent) - { - if (fun is BaseFunction) { - BaseFunction bf = (BaseFunction)fun; - return bf.Decompile (indent, Decompiler.ONLY_BODY_FLAG); - } - // ALERT: not sure what the right response here is. - return "[native code]\n"; - } - - /// Create a new JavaScript object. - /// - /// Equivalent to evaluating "new Object()". - /// - /// the scope to search for the constructor and to evaluate - /// against - /// - /// the new object - /// - public IScriptable NewObject (IScriptable scope) - { - return NewObject (scope, "Object", ScriptRuntime.EmptyArgs); - } - - /// Create a new JavaScript object by executing the named constructor. - /// - /// The call newObject(scope, "Foo") is equivalent to - /// evaluating "new Foo()". - /// - /// - /// the scope to search for the constructor and to evaluate against - /// - /// the name of the constructor to call - /// - /// the new object - /// - public IScriptable NewObject (IScriptable scope, string constructorName) - { - return NewObject (scope, constructorName, ScriptRuntime.EmptyArgs); - } - - /// Creates a new JavaScript object by executing the named constructor. - /// - /// Searches scope for the named constructor, calls it with - /// the given arguments, and returns the result.

- /// - /// The code - ///

-        /// Object[] args = { "a", "b" };
-        /// newObject(scope, "Foo", args)
- /// is equivalent to evaluating "new Foo('a', 'b')", assuming that the Foo - /// constructor has been defined in scope. - /// - ///
- /// The scope to search for the constructor and to evaluate - /// against - /// - /// the name of the constructor to call - /// - /// the array of arguments for the constructor - /// - /// the new object - /// - public IScriptable NewObject (IScriptable scope, string constructorName, object [] args) - { - scope = ScriptableObject.GetTopLevelScope (scope); - IFunction ctor = ScriptRuntime.getExistingCtor (this, scope, constructorName); - if (args == null) { - args = ScriptRuntime.EmptyArgs; - } - return ctor.Construct (this, scope, args); - } - - /// Create an array with a specified initial length. - ///

- ///

- /// the scope to create the object in - /// - /// the initial length (JavaScript arrays may have - /// additional properties added dynamically). - /// - /// the new array object - /// - public IScriptable NewArray (IScriptable scope, int length) - { - BuiltinArray result = new BuiltinArray (length); - ScriptRuntime.setObjectProtoAndParent (result, scope); - return result; - } - - /// Create an array with a set of initial elements. - /// - /// - /// the scope to create the object in. - /// - /// the initial elements. Each object in this array - /// must be an acceptable JavaScript type and type - /// of array should be exactly Object[], not - /// SomeObjectSubclass[]. - /// - /// the new array object. - /// - public IScriptable NewArray (IScriptable scope, object [] elements) - { - Type elementType = elements.GetType ().GetElementType (); - if (elementType != typeof (object)) - throw new ArgumentException (); - BuiltinArray result = new BuiltinArray (elements); - ScriptRuntime.setObjectProtoAndParent (result, scope); - return result; - } - - /// Get the elements of a JavaScript array. - ///

- /// If the object defines a length property convertible to double number, - /// then the number is converted Uint32 value as defined in Ecma 9.6 - /// and Java array of that size is allocated. - /// The array is initialized with the values obtained by - /// calling get() on object for each value of i in [0,length-1]. If - /// there is not a defined value for a property the Undefined value - /// is used to initialize the corresponding element in the array. The - /// Java array is then returned. - /// If the object doesn't define a length property or it is not a number, - /// empty array is returned. - ///

- /// the JavaScript array or array-like object - /// - /// a Java array of objects - /// - - public object [] GetElements (IScriptable obj) - { - return ScriptRuntime.getArrayElements (obj); - } - - - /// Convenient method to convert java value to its closest representation - /// in JavaScript. - ///

- /// If value is an instance of String, Number, Boolean, Function or - /// Scriptable, it is returned as it and will be treated as the corresponding - /// JavaScript type of string, number, boolean, function and object. - ///

- /// Note that for Number instances during any arithmetic operation in - /// JavaScript the engine will always use the result of - /// Number.doubleValue() resulting in a precision loss if - /// the number can not fit into double. - ///

- /// If value is an instance of Character, it will be converted to string of - /// length 1 and its JavaScript type will be string. - ///

- /// The rest of values will be wrapped as LiveConnect objects - /// by calling {@link WrapFactory#wrap(Context cx, Scriptable scope, - /// Object obj, Class staticType)} as in: - ///

-        /// Context cx = Context.getCurrentContext();
-        /// return cx.getWrapFactory().wrap(cx, scope, value, null);
-        /// 
- /// - ///
- /// any Java object - /// - /// top scope object - /// - /// value suitable to pass to any API that takes JavaScript values. - /// - public static object CliToJS (Context cx, object value, IScriptable scope) - { - if (value is string || CliHelper.IsNumber (value) || value is bool || value is IScriptable) { - return value; - } - else if (value is char) { - return Convert.ToString (((char)value)); - } - else { - Type type = (value as Type); - if (type != null) { - return cx.Wrap (scope, value, (Type)value); - } else { - return cx.Wrap (scope, value, null); - } - } - } - - /// Convert a JavaScript value into the desired type. - /// Uses the semantics defined with LiveConnect3 and throws an - /// Illegal argument exception if the conversion cannot be performed. - /// - /// the JavaScript value to convert - /// - /// the Java type to convert to. Primitive Java - /// types are represented using the TYPE fields in the corresponding - /// wrapper class in java.lang. - /// - /// the converted value - /// - /// EvaluatorException if the conversion cannot be performed - public static object JsToCli (object value, System.Type desiredType) - { - return CliObject.CoerceType (desiredType, value); - } - - - - /// Rethrow the exception wrapping it as the script runtime exception. - /// Unless the exception is instance of {@link EcmaError} or - /// {@link EvaluatorException} it will be wrapped as - /// {@link WrappedException}, a subclass of {@link EvaluatorException}. - /// The resulting exception object always contains - /// source name and line number of script that triggered exception. - ///

- /// This method always throws an exception, its return value is provided - /// only for convenience to allow a usage like: - ///

-        /// throw Context.throwAsScriptRuntimeEx(ex);
-        /// 
- /// to indicate that code after the method is unreachable. - ///
- /// EvaluatorException - /// EcmaError - public static ApplicationException ThrowAsScriptRuntimeEx (Exception e) - { - while ((e is TargetInvocationException)) { - e = ((TargetInvocationException)e).InnerException; - } - if (e is EcmaScriptException) { - throw e; - } - throw new EcmaScriptRuntimeException (e); - } - - public static bool IsValidOptimizationLevel (int optimizationLevel) - { - return -1 <= optimizationLevel && optimizationLevel <= 9; - } - - public static void CheckOptimizationLevel (int optimizationLevel) - { - if (IsValidOptimizationLevel (optimizationLevel)) { - return; - } - throw new ArgumentException ("Optimization level outside [-1..9]: " + optimizationLevel); - } - - /// Set the security controller for this context. - ///

SecurityController may only be set if it is currently null - /// and {@link SecurityController#hasGlobal()} is false. - /// Otherwise a SecurityException is thrown. - ///

- /// a SecurityController object - /// - /// SecurityException if there is already a SecurityController - /// object for this Context or globally installed. - /// - public SecurityController SecurityController - { - set - { - if (m_Sealed) - OnSealedMutation (); - if (value == null) - throw new ArgumentException (); - if (securityController != null) { - throw new System.Security.SecurityException ("Can not overwrite existing SecurityController object"); - } - if (SecurityController.HasGlobal ()) { - throw new System.Security.SecurityException ("Can not overwrite existing global SecurityController object"); - } - securityController = value; - } - get - { - SecurityController global = SecurityController.Global; - if (global != null) { - return global; - } - return securityController; - } - } - - /// Get a value corresponding to a key. - ///

- /// Since the Context is associated with a thread it can be - /// used to maintain values that can be later retrieved using - /// the current thread. - ///

- /// Note that the values are maintained with the Context, so - /// if the Context is disassociated from the thread the values - /// cannot be retreived. Also, if private data is to be maintained - /// in this manner the key should be a java.lang.Object - /// whose reference is not divulged to untrusted code. - ///

- /// the key used to lookup the value - /// - /// a value previously stored using putThreadLocal. - /// - public object GetThreadLocal (object key) - { - if (hashtable == null) - return null; - return hashtable [key]; - } - - /// Put a value that can later be retrieved using a given key. - ///

- ///

- /// the key used to index the value - /// - /// the value to save - /// - public void PutThreadLocal (object key, object value) - { - if (m_Sealed) - OnSealedMutation (); - if (hashtable == null) - hashtable = Hashtable.Synchronized (new Hashtable ()); - hashtable [key] = value; - } - - /// Remove values from thread-local storage. - /// the key for the entry to remove. - /// - public void RemoveThreadLocal (object key) - { - if (m_Sealed) - OnSealedMutation (); - if (hashtable == null) - return; - hashtable.Remove (key); - } - - - - /// Return the current debugger. - /// the debugger, or null if none is attached. - /// - public Debugger Debugger - { - get - { - return m_Debugger; - } - } - - /// Set the associated debugger. - /// the debugger to be used on callbacks from - /// the engine. - /// - /// arbitrary object that debugger can use to store - /// per Context data. - /// - public void SetDebugger (Debugger debugger, object contextData) - { - if (m_Sealed) - OnSealedMutation (); - this.m_Debugger = debugger; - debuggerData = contextData; - } - - /// Return DebuggableScript instance if any associated with the script. - /// If callable supports DebuggableScript implementation, the method - /// returns it. Otherwise null is returned. - /// - public static DebuggableScript getDebuggableView (IScript script) - { - if (script is BuiltinFunction) { - return ((BuiltinFunction)script).DebuggableView; - } - return null; - } - - - private Features m_Features = Features.None; - - /// - /// Controls certain aspects of script semantics. - /// Should be overwritten to alter default behavior. - /// - /// The default implementation calls - /// {@link ContextFactory#hasFeature(Context cx, int featureIndex)} - /// that allows to customize Context behavior without introducing - /// Context subclasses. {@link ContextFactory} documentation gives - /// an example of hasFeature implementation. - /// - /// - /// feature to check - /// - /// true if the feature feature is turned on - /// - public bool HasFeature (Features feature) - { - return (m_Features & feature) == feature; - } - - public void SetFeature (Features feature, bool isEnabled) - { - if (isEnabled) { - m_Features |= feature; - } - else { - if (HasFeature (feature)) - m_Features = m_Features ^ feature; - } - } - - - /// Allow application to monitor counter of executed script instructions - /// in Context subclasses. - /// Run-time calls this when instruction counting is enabled and the counter - /// reaches limit set by setInstructionObserverThreshold(). - /// The method is useful to observe long running scripts and if necessary - /// to terminate them. - ///

- /// The instruction counting support is available only for interpreted - /// scripts generated when the optimization level is set to -1. - ///

- /// The default implementation calls - /// {@link ContextFactory#observeInstructionCount(Context cx, - /// int instructionCount)} - /// that allows to customize Context behavior without introducing - /// Context subclasses. - /// - ///

- /// amount of script instruction executed since - /// last call to observeInstructionCount - /// - /// Error to terminate the script - protected internal void ObserveInstructionCount (int instructionCount) - { - Factory.ObserveInstructionCount (this, instructionCount); - } - - private object CompileImpl (IScriptable scope, StreamReader sourceReader, string sourceString, string sourceName, int lineno, object securityDomain, bool returnFunction, Interpreter compiler, ErrorReporter compilationErrorReporter) - { - if (securityDomain != null && securityController == null) { - throw new ArgumentException ("securityDomain should be null if setSecurityController() was never called"); - } - - // One of sourceReader or sourceString has to be null - if (!(sourceReader == null ^ sourceString == null)) - Context.CodeBug (); - // scope should be given if and only if compiling function - if (!(scope == null ^ returnFunction)) - Context.CodeBug (); - - CompilerEnvirons compilerEnv = new CompilerEnvirons (); - compilerEnv.initFromContext (this); - if (compilationErrorReporter == null) { - compilationErrorReporter = compilerEnv.getErrorReporter (); - } - - if (m_Debugger != null) { - if (sourceReader != null) { - sourceString = sourceReader.ReadToEnd (); - sourceReader = null; - } - } - - Parser p = new Parser (compilerEnv, compilationErrorReporter); - if (returnFunction) { - p.calledByCompileFunction = true; - } - ScriptOrFnNode tree; - if (sourceString != null) { - tree = p.Parse (sourceString, sourceName, lineno); - } - else { - tree = p.Parse (sourceReader, sourceName, lineno); - } - if (returnFunction) { - if (!(tree.FunctionCount == 1 && tree.FirstChild != null && tree.FirstChild.Type == Token.FUNCTION)) { - // TODO: the check just look for the first child - // TODO: and allows for more nodes after it for compatibility - // TODO: with sources like function() {};;; - throw new ArgumentException ("compileFunction only accepts source with single JS function: " + sourceString); - } - } - - - - if (compiler == null) { - compiler = new Interpreter (); - //compiler = new Compiler(); - } - - string encodedSource = p.EncodedSource; - - object bytecode = compiler.Compile (compilerEnv, tree, encodedSource, returnFunction); - - if (m_Debugger != null) { - if (sourceString == null) - Context.CodeBug (); - if (bytecode is DebuggableScript) { - DebuggableScript dscript = (DebuggableScript)bytecode; - NotifyDebugger (this, dscript, sourceString); - } - else { - throw new ApplicationException ("NOT SUPPORTED"); - } - } - - object result; - if (returnFunction) { - result = compiler.CreateFunctionObject (this, scope, bytecode, securityDomain); - } - else { - result = compiler.CreateScriptObject (bytecode, securityDomain); - } - - return result; - } - - private static void NotifyDebugger (Context cx, DebuggableScript dscript, string debugSource) - { - cx.m_Debugger.HandleCompilationDone (cx, dscript, debugSource); - for (int i = 0; i != dscript.FunctionCount; ++i) { - NotifyDebugger (cx, dscript.GetFunction (i), debugSource); - } - } - - internal static string GetSourcePositionFromStack (int [] linep) - { - Context cx = CurrentContext; - if (cx == null) - return null; - if (cx.lastInterpreterFrame != null) { - return Interpreter.GetSourcePositionFromStack (cx, linep); - } - - System.Diagnostics.StackTrace st = new System.Diagnostics.StackTrace (); - System.Diagnostics.StackFrame sf = st.GetFrame (0); - linep [0] = sf.GetFileLineNumber (); - return Path.GetFileName (sf.GetFileName ()); - } - - - /// Add a name to the list of names forcing the creation of real - /// activation objects for functions. - /// - /// - /// the name of the object to add to the list - /// - public void AddActivationName (string name) - { - if (m_Sealed) - OnSealedMutation (); - if (activationNames == null) - activationNames = Hashtable.Synchronized (new Hashtable (5)); - activationNames [name] = name; - } - - /// Check whether the name is in the list of names of objects - /// forcing the creation of activation objects. - /// - /// - /// the name of the object to test - /// - /// - /// true if an function activation object is needed. - /// - public bool IsActivationNeeded (string name) - { - return activationNames != null && activationNames.ContainsKey (name); - } - - /// Remove a name from the list of names forcing the creation of real - /// activation objects for functions. - /// - /// - /// the name of the object to remove from the list - /// - public void RemoveActivationName (string name) - { - if (m_Sealed) - OnSealedMutation (); - if (activationNames != null) - activationNames.Remove (name); - } - - private static string implementationVersion; - - private ContextFactory factory; - private bool m_Sealed; - private object m_SealKey; - - internal IScriptable topCallScope; - internal BuiltinCall currentActivationCall; - internal XMLLib cachedXMLLib; - - // for Objects, Arrays to tag themselves as being printed out, - // so they don't print themselves out recursively. - // Use ObjToIntMap instead of java.util.HashSet for JDK 1.1 compatibility - internal ObjToIntMap iterating; - - internal object interpreterSecurityDomain; - - internal Versions m_Version = Versions.Unknown; - - private SecurityController securityController; - - private ErrorReporter m_ErrorReporter; - internal RegExpProxy regExpProxy; - private System.Globalization.CultureInfo culture; - private bool generatingDebug; - private bool generatingDebugChanged; - private bool generatingSource = true; - internal bool compileFunctionsWithDynamicScopeFlag = false; - internal bool useDynamicScope; - private int m_OptimizationLevel; - - internal Debugger m_Debugger; - private object debuggerData; - private int enterCount; - private Hashtable hashtable; - - private bool creationEventWasSent; - - /// This is the list of names of objects forcing the creation of - /// function activation records. - /// - internal Hashtable activationNames; - - // For the interpreter to store the last frame for error reports etc. - internal object lastInterpreterFrame; - - // For the interpreter to store information about previous invocations - // interpreter invocations - internal ObjArray previousInterpreterInvocations; - - // For instruction counting (interpreter only) - internal int instructionCount; - internal int instructionThreshold; - - // It can be used to return the second index-like result from function - internal int scratchIndex; - - // It can be used to return the second uint32 result from function - internal long scratchUint32; - - // It can be used to return the second Scriptable result from function - internal IScriptable scratchScriptable; - static Context () - { - EmptyArgs = ScriptRuntime.EmptyArgs; - } - - public void Dispose () - { - Context.Exit (); - } - - /// - /// Throws RuntimeException to indicate failed assertion. - /// The function never returns and its return type is RuntimeException - /// only to be able to write throw EcmaScriptHelper.CodeBug() if plain - /// EcmaScriptHelper.CodeBug() triggers unreachable code error. - /// - public static ApplicationException CodeBug () - { - ApplicationException ex = new ApplicationException ("FAILED ASSERTION"); - Console.Error.WriteLine (ex.ToString ()); - throw ex; - } - - } -} +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; +using System.IO; +using System.Threading; +using System.Globalization; +using System.Reflection; +using System.Collections; + +using EcmaScript.NET.Debugging; +using EcmaScript.NET.Collections; +using EcmaScript.NET.Types; +using EcmaScript.NET.Types.Cli; +using EcmaScript.NET.Types.E4X; + +namespace EcmaScript.NET +{ + + /// + /// This class represents the runtime context of an executing script. + /// + /// Before executing a script, an instance of Context must be created + /// and associated with the thread that will be executing the script. + /// The Context will be used to store information about the executing + /// of the script such as the call stack. Contexts are associated with + /// the current thread using the {@link #call(ContextAction)} + /// or {@link #enter()} methods.

+ /// + /// Different forms of script execution are supported. Scripts may be + /// evaluated from the source directly, or first compiled and then later + /// executed. Interactive execution is also supported.

+ /// + /// Some aspects of script execution, such as type conversions and + /// object creation, may be accessed directly through methods of + /// Context. + /// + ///

+ public class Context : IDisposable + { + + public enum Features + { + /// + /// No features at all + /// + None = 0, + + /// + /// Support for E4X + /// + E4x = 1 << 1, + + /// + /// Support for get and set + /// + GetterAndSetter = 1 << 2, + + /// + /// + /// + NonEcmaGetYear = 1 << 3, + + /// Control if dynamic scope should be used for name access. + /// If hasFeature(FEATURE_DYNAMIC_SCOPE) returns true, then the name lookup + /// during name resolution will use the top scope of the script or function + /// which is at the top of JS execution stack instead of the top scope of the + /// script or function from the current stack frame if the top scope of + /// the top stack frame contains the top scope of the current stack frame + /// on its prototype chain. + ///

+ /// This is useful to define shared scope containing functions that can + /// be called from scripts and functions using private scopes. + ///

+ /// By default {@link #hasFeature(int)} returns false. + ///

+ DynamicScope = 1 << 4, + /// Control if member expression as function name extension is available. + /// If hasFeature(FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME) returns + /// true, allow function memberExpression(args) { body } to be + /// syntax sugar for memberExpression = function(args) { body }, + /// when memberExpression is not a simple identifier. + /// See ECMAScript-262, section 11.2 for definition of memberExpression. + /// By default {@link #hasFeature(int)} returns false. + /// + MemberExprAsFunctionName = 1 << 5, + + /// Control if reserved keywords are treated as identifiers. + /// If hasFeature(RESERVED_KEYWORD_AS_IDENTIFIER) returns true, + /// treat future reserved keyword (see Ecma-262, section 7.5.3) as ordinary + /// identifiers but warn about this usage. + /// + /// By default {@link #hasFeature(int)} returns false. + /// + ReservedKeywordAsIdentifier = 1 << 6, + + /// Control if toString() should returns the same result + /// as toSource() when applied to objects and arrays. + /// If hasFeature(FEATURE_TO_STRING_AS_SOURCE) returns true, + /// calling toString() on JS objects gives the same result as + /// calling toSource(). That is it returns JS source with code + /// to create an object with all enumeratable fields of the original object + /// instead of printing [object result of + /// {@link Scriptable#getClassName()}]. + ///

+ /// By default {@link #hasFeature(int)} returns true only if + /// the current JS version is set to {@link #Versions.JS1_2}. + ///

+ ToStringAsSource = 1 << 7, + + /// Control if properties __proto__ and __parent__ + /// are treated specially. + /// If hasFeature(FEATURE_PARENT_PROTO_PROPRTIES) returns true, + /// treat __parent__ and __proto__ as special properties. + ///

+ /// The properties allow to query and set scope and prototype chains for the + /// objects. The special meaning of the properties is available + /// only when they are used as the right hand side of the dot operator. + /// For example, while x.__proto__ = y changes the prototype + /// chain of the object x to point to y, + /// x["__proto__"] = y simply assigns a new value to the property + /// __proto__ in x even when the feature is on. + /// + /// By default {@link #hasFeature(int)} returns true. + ///

+ ParentProtoProperties = 1 << 8, + + /// Control if strict variable mode is enabled. + /// When the feature is on Rhino reports runtime errors if assignment + /// to a global variable that does not exist is executed. When the feature + /// is off such assignments creates new variable in the global scope as + /// required by ECMA 262. + ///

+ /// By default {@link #hasFeature(int)} returns false. + ///

+ StrictVars = 1 << 9, + + /// Control if strict eval mode is enabled. + /// When the feature is on Rhino reports runtime errors if non-string + /// argument is passed to the eval function. When the feature is off + /// eval simply return non-string argument as is without performing any + /// evaluation as required by ECMA 262. + ///

+ /// By default {@link #hasFeature(int)} returns false. + ///

+ StrictEval = 1 << 10, + + /// + /// Controls if the non-ecma function 'print' is available or not. + /// + NonEcmaPrintFunction = 1 << 11, + + /// + /// Controls if the non-ecma function 'version' is available or not + /// + NonEcmaVersionFunction = 1 << 12, + + /// + /// Controls if the non-ecma function 'options' is available or not + /// + NonEcmaOptionsFunction = 1 << 13, + + Strict = 1 << 14, + + /// + /// Controls if the non-ecma function 'options' is available or not + /// + NonEcmaGcFunction = 1 << 14, + + /// + /// The famous 'it' object (@see js.c:2315) + /// + NonEcmaItObject = 1 << 15, + + } + + public enum Versions + { + Unknown = -1, + Default = 0, + JS1_0 = 100, + JS1_1 = 110, + JS1_2 = 120, + JS1_3 = 130, + JS1_4 = 140, + JS1_5 = 150, + JS1_6 = 160 + } + + /// + /// + /// + public event ContextWrapHandler OnWrap; + + private int m_MaximumInterpreterStackDepth = 8092; + + /// + /// + /// + public int MaximumInterpreterStackDepth + { + get + { + return m_MaximumInterpreterStackDepth; + } + set + { + if (Sealed) + OnSealedMutation (); + m_MaximumInterpreterStackDepth = value; + } + } + + private AppDomain m_AppDomain = null; + + /// + /// Associated app domain + /// + public AppDomain AppDomain + { + get { return m_AppDomain; } + } + + private static LocalDataStoreSlot LocalSlot + { + get + { + LocalDataStoreSlot slot = Thread.GetNamedDataSlot ("Context"); + if (slot == null) { + slot = Thread.AllocateNamedDataSlot ("Context"); + } + return slot; + } + } + + /// Get the current Context. + /// + /// The current Context is per-thread; this method looks up + /// the Context associated with the current thread.

+ /// + ///

+ /// the Context associated with the current thread, or + /// null if no context is associated with the current + /// thread. + /// + public static Context CurrentContext + { + get + { + Context cx = (Context)Thread.GetData (LocalSlot); + return cx; + } + } + + /// Return {@link ContextFactory} instance used to create this Context + /// or the result of {@link ContextFactory#getGlobal()} if no factory + /// was used for Context creation. + /// + public ContextFactory Factory + { + get + { + ContextFactory result = factory; + if (result == null) { + result = ContextFactory.Global; + } + return result; + } + + } + /// Checks if this is a sealed Context. A sealed Context instance does not + /// allow to modify any of its properties and will throw an exception + /// on any such attempt. + /// + public bool Sealed + { + get + { + return m_Sealed; + } + + } + /// Get the implementation version. + /// + ///

+ /// The implementation version is of the form + ///

+        /// "name langVer release relNum date"
+        /// 
+ /// where name is the name of the product, langVer is + /// the language version, relNum is the release number, and + /// date is the release date for that specific + /// release in the form "yyyy mm dd". + /// + ///
+ /// a string that encodes the product, language version, release + /// number, and date. + /// + public string ImplementationVersion + { + get + { + // TODO: Probably it would be better to embed this directly into source + // TODO: with special build preprocessing but that would require some ant + // TODO: tweaking and then replacing token in resource files was simpler + if (implementationVersion == null) { + implementationVersion = ScriptRuntime.GetMessage ("implementation.version"); + } + return implementationVersion; + } + + } + /// Get the singleton object that represents the JavaScript Undefined value. + public static object UndefinedValue + { + get + { + return Undefined.Value; + } + + } + /// Specify whether or not debug information should be generated. + ///

+ /// Setting the generation of debug information on will set the + /// optimization level to zero. + ///

+ public bool GeneratingDebug + { + get + { + return generatingDebug; + } + + set + { + if (m_Sealed) + OnSealedMutation (); + generatingDebugChanged = true; + if (value && OptimizationLevel > 0) + OptimizationLevel = 0; + this.generatingDebug = value; + } + + } + + /// Specify whether or not source information should be generated. + ///

+ /// Without source information, evaluating the "toString" method + /// on JavaScript functions produces only "[native code]" for + /// the body of the function. + /// Note that code generated without source is not fully ECMA + /// conformant. + ///

+ public bool GeneratingSource + { + get + { + return generatingSource; + } + set + { + if (m_Sealed) + OnSealedMutation (); + this.generatingSource = value; + } + + } + /// Set the current optimization level. + ///

+ /// The optimization level is expected to be an integer between -1 and + /// 9. Any negative values will be interpreted as -1, and any values + /// greater than 9 will be interpreted as 9. + /// An optimization level of -1 indicates that interpretive mode will + /// always be used. Levels 0 through 9 indicate that class files may + /// be generated. Higher optimization levels trade off compile time + /// performance for runtime performance. + /// The optimizer level can't be set greater than -1 if the optimizer + /// package doesn't exist at run time. + ///

+ /// an integer indicating the level of + /// optimization to perform + /// + public int OptimizationLevel + { + get + { + return m_OptimizationLevel; + } + + set + { + if (m_Sealed) + OnSealedMutation (); + if (value == -2) { + // To be compatible with Cocoon fork + value = -1; + } + CheckOptimizationLevel (value); + this.m_OptimizationLevel = value; + } + + } + + /// Return the debugger context data associated with current context. + /// the debugger data, or null if debugger is not attached + /// + public object DebuggerContextData + { + get + { + return debuggerData; + } + + } + + public object Wrap (IScriptable scope, object obj, Type staticType) + { + if (obj == null || obj is IScriptable) + return obj; + if (staticType == null) + staticType = obj.GetType (); + + if (staticType.IsArray) + return new CliArray (scope, obj as Array); + + if (staticType.IsPrimitive) + return obj; + + if (OnWrap != null) { + ContextWrapEventArgs e = new ContextWrapEventArgs (this, scope, obj, staticType); + OnWrap (this, e); + obj = e.Target; + } + + return new CliObject (obj); + } + + /// Get/Set threshold of executed instructions counter that triggers call to + /// observeInstructionCount(). + /// When the threshold is zero, instruction counting is disabled, + /// otherwise each time the run-time executes at least the threshold value + /// of script instructions, observeInstructionCount() will + /// be called. + /// + public int InstructionObserverThreshold + { + get + { + return instructionThreshold; + } + + set + { + if (m_Sealed) + OnSealedMutation (); + if (value < 0) + throw new ArgumentException (); + instructionThreshold = value; + } + + } + + internal RegExpProxy RegExpProxy + { + get + { + if (regExpProxy == null) { + regExpProxy = new Types.RegExp.RegExpImpl (); + } + return regExpProxy; + } + set { + regExpProxy = value; + } + } + internal bool VersionECMA1 + { + get + { + return m_Version == Versions.Default || m_Version >= Versions.JS1_3; + } + + } + public bool GeneratingDebugChanged + { + get + { + return generatingDebugChanged; + } + + } + /// Language versions. + /// + /// All integral values are reserved for future version numbers. + /// + + + + + + + + + + + + public const string languageVersionProperty = "language version"; + public const string errorReporterProperty = "error reporter"; + + /// Convinient value to use as zero-length array of objects. + public static readonly object [] EmptyArgs; + + /// Create a new Context. + /// + /// Note that the Context must be associated with a thread before + /// it can be used to execute a script. + /// + /// + public Context (AppDomain appDomain) + { + if (appDomain == null) + throw new ArgumentNullException ("appDomain"); + Version = Versions.Default; + m_AppDomain = appDomain; + } + + /// Get a context associated with the current thread, creating + /// one if need be. + /// + /// The Context stores the execution state of the JavaScript + /// engine, so it is required that the context be entered + /// before execution may begin. Once a thread has entered + /// a Context, then getCurrentContext() may be called to find + /// the context that is associated with the current thread. + ///

+ /// Calling enter() will + /// return either the Context currently associated with the + /// thread, or will create a new context and associate it + /// with the current thread. Each call to enter() + /// must have a matching call to exit(). For example, + ///

+        /// Context cx = Context.enter();
+        /// try {
+        /// ...
+        /// cx.evaluateString(...);
+        /// } finally {
+        /// Context.exit();
+        /// }
+        /// 
+ /// Instead of using enter(), exit() pair consider using + /// {@link #call(ContextAction)} which guarantees proper + /// association of Context instances with the current thread and is faster. + /// With this method the above example becomes: + ///
+        /// Context.call(new ContextAction() {
+        /// public Object run(Context cx) {
+        /// ...
+        /// cx.evaluateString(...);
+        /// return null;
+        /// }
+        /// });
+        /// 
+ /// + ///
+ /// a Context associated with the current thread + /// + public static Context Enter () + { + return Enter (null, AppDomain.CurrentDomain); + } + + public static Context Enter (AppDomain appDomain) + { + return Enter (null, appDomain); + } + + /// Get a Context associated with the current thread, using + /// the given Context if need be. + ///

+ /// The same as enter() except that cx + /// is associated with the current thread and returned if + /// the current thread has no associated context and cx + /// is not associated with any other thread. + ///

+ /// a Context to associate with the thread if possible + /// + /// a Context associated with the current thread + /// + /// + public static Context Enter (Context cx) + { + return Enter (cx, AppDomain.CurrentDomain); + } + + public static Context Enter (Context cx, AppDomain appDomain) + { + Context old = CurrentContext; + if (old != null) { + if (cx != null && cx != old && cx.enterCount != 0) { + // The suplied context must be the context for + // the current thread if it is already entered + throw new ArgumentException ("Cannot enter Context active on another thread"); + } + if (old.factory != null) { + // Context with associated factory will be released + // automatically and does not need to change enterCount + return old; + } + if (old.m_Sealed) + OnSealedMutation (); + cx = old; + } + else { + if (cx == null) { + cx = new Context (appDomain); + } + else { + if (cx.m_Sealed) + OnSealedMutation (); + } + if (cx.enterCount != 0 || cx.factory != null) { + throw new Exception (); + } + + if (!cx.creationEventWasSent) { + cx.creationEventWasSent = true; + ContextFactory.Global.FireOnContextCreated (cx); + } + } + + if (old == null) { + Thread.SetData (LocalSlot, cx); + } + ++cx.enterCount; + + return cx; + } + + /// Exit a block of code requiring a Context. + /// + /// Calling exit() will remove the association between + /// the current thread and a Context if the prior call to + /// enter() on this thread newly associated a Context + /// with this thread. + /// Once the current thread no longer has an associated Context, + /// it cannot be used to execute JavaScript until it is again associated + /// with a Context. + /// + /// + public static void Exit () + { + Context cx = CurrentContext; + if (cx == null) { + throw new Exception ("Calling Context.exit without previous Context.enter"); + } + if (cx.factory != null) { + // Context with associated factory will be released + // automatically and does not need to change enterCount + return; + } + if (cx.enterCount < 1) + Context.CodeBug (); + if (cx.m_Sealed) + OnSealedMutation (); + --cx.enterCount; + if (cx.enterCount == 0) { + Thread.SetData (LocalSlot, null); + ContextFactory.Global.FireOnContextReleased (cx); + } + } + + + /// Call {@link + /// Callable#call(Context cx, Scriptable scope, Scriptable thisObj, + /// Object[] args)} + /// using the Context instance associated with the current thread. + /// If no Context is associated with the thread, then + /// {@link ContextFactory#makeContext()} will be called to construct + /// new Context instance. The instance will be temporary associated + /// with the thread during call to {@link ContextAction#run(Context)}. + ///

+ /// It is allowed to use null for factory argument + /// in which case the factory associated with the scope will be + /// used to create new context instances. + /// + ///

+ public static object Call (ContextFactory factory, ICallable callable, IScriptable scope, IScriptable thisObj, object [] args) + { + if (factory == null) { + factory = ContextFactory.Global; + } + + Context cx = CurrentContext; + if (cx != null) { + object result; + if (cx.factory != null) { + result = callable.Call (cx, scope, thisObj, args); + } + else { + // Context was associated with the thread via Context.enter, + // set factory to make Context.enter/exit to be no-op + // during call + cx.factory = factory; + try { + result = callable.Call (cx, scope, thisObj, args); + } + finally { + cx.factory = null; + } + } + return result; + } + + cx = PrepareNewContext (AppDomain.CurrentDomain, factory); + try { + return callable.Call (cx, scope, thisObj, args); + } + finally { + ReleaseContext (cx); + } + } + + internal void InitDefaultFeatures () + { + m_Features = Features.None; + + SetFeature (Features.E4x, (Version == Context.Versions.Default || Version >= Context.Versions.JS1_6)); + SetFeature (Features.GetterAndSetter, (Version == Context.Versions.Default || Version >= Context.Versions.JS1_5)); + SetFeature (Features.NonEcmaGetYear, (Version == Context.Versions.JS1_0 || Version == Context.Versions.JS1_1 || Version == Context.Versions.JS1_2)); + SetFeature (Features.ToStringAsSource, Version == Context.Versions.JS1_2); + SetFeature (Features.ParentProtoProperties, true); + } + + + private static Context PrepareNewContext (AppDomain appDomain, ContextFactory factory) + { + Context cx = new Context (appDomain); + if (cx.factory != null || cx.enterCount != 0) { + throw new Exception ("factory.makeContext() returned Context instance already associated with some thread"); + } + cx.factory = factory; + factory.FireOnContextCreated (cx); + if (factory.Sealed && !cx.Sealed) { + cx.Seal ((object)null); + } + Thread.SetData (LocalSlot, cx); + return cx; + } + + private static void ReleaseContext (Context cx) + { + Thread.SetData (LocalSlot, null); + try { + cx.factory.FireOnContextReleased (cx); + } + finally { + cx.factory = null; + } + } + + + + /// Seal this Context object so any attempt to modify any of its properties + /// including calling {@link #enter()} and {@link #exit()} methods will + /// throw an exception. + ///

+ /// If sealKey is not null, calling + /// {@link #unseal(Object sealKey)} with the same key unseals + /// the object. If sealKey is null, unsealing is no longer possible. + /// + ///

+ public void Seal (object sealKey) + { + if (m_Sealed) + OnSealedMutation (); + m_Sealed = true; + this.m_SealKey = sealKey; + } + + /// Unseal previously sealed Context object. + /// The sealKey argument should not be null and should match + /// sealKey suplied with the last call to + /// {@link #seal(Object)} or an exception will be thrown. + /// + /// + public void Unseal (object sealKey) + { + if (sealKey == null) + throw new ArgumentException (); + if (this.m_SealKey != sealKey) + throw new ArgumentException (); + if (!m_Sealed) + throw new Exception (); + m_Sealed = false; + this.m_SealKey = null; + } + + internal static void OnSealedMutation () + { + throw new Exception (); + } + + /// + /// Get the current language version. + /// + public Versions Version + { + get + { + return m_Version; + } + set + { + if (m_Sealed) + OnSealedMutation (); + this.m_Version = value; + + InitDefaultFeatures (); + } + } + + public static bool IsValidLanguageVersion (int version) + { + return ToValidLanguageVersion (version) != Versions.Unknown; + } + + public static Versions ToValidLanguageVersion (int version) + { + Versions ver = Versions.Unknown; + if (version > 0 || version < (int)Versions.JS1_6) + ver = (Versions)version; + return ver; + } + + public static void CheckLanguageVersion (int version) + { + if (IsValidLanguageVersion (version)) { + return; + } + throw new ArgumentException ("Bad language version: " + version); + } + + /// Get the current error reporter. + /// + /// + public ErrorReporter ErrorReporter + { + get + { + if (m_ErrorReporter == null) { + return DefaultErrorReporter.instance; + } + return m_ErrorReporter; + } + set + { + if (m_Sealed) + OnSealedMutation (); + if (value == null) + throw new ArgumentException (); + this.m_ErrorReporter = value; + } + } + + /// + /// Get the current locale. Returns the default locale if none has + /// been set. + /// + public CultureInfo CurrentCulture + { + get + { + if (culture == null) + culture = Thread.CurrentThread.CurrentCulture; + return culture; + } + set + { + if (m_Sealed) + OnSealedMutation (); + culture = value; + + // HACK: Needed for example on DateTime.ToString() + if (Thread.CurrentThread.CurrentCulture != culture) + Thread.CurrentThread.CurrentCulture = culture; + } + } + + /// Report a warning using the error reporter for the current thread. + /// + /// + /// the warning message to report + /// + /// a string describing the source, such as a filename + /// + /// the starting line number + /// + /// the text of the line (may be null) + /// + /// the offset into lineSource where problem was detected + /// + + public static void ReportWarning (string message, string sourceName, int lineno, string lineSource, int lineOffset) + { + Context cx = Context.CurrentContext; + cx.ErrorReporter.Warning (message, sourceName, lineno, lineSource, lineOffset); + } + + public static void ReportWarningById (string messageId, params string [] arguments) + { + int [] linep = new int [] { 0 }; + string filename = GetSourcePositionFromStack (linep); + Context.ReportWarning (ScriptRuntime.GetMessage (messageId, arguments), filename, linep [0], null, 0); + } + + /// Report a warning using the error reporter for the current thread. + /// + /// + /// the warning message to report + /// + public static void ReportWarning (string message) + { + int [] linep = new int [] { 0 }; + string filename = GetSourcePositionFromStack (linep); + Context.ReportWarning (message, filename, linep [0], null, 0); + } + + /// Report an error using the error reporter for the current thread. + /// + /// + /// the error message to report + /// + /// a string describing the source, such as a filename + /// + /// the starting line number + /// + /// the text of the line (may be null) + /// + /// the offset into lineSource where problem was detected + /// + public static void ReportError (string message, string sourceName, int lineno, string lineSource, int lineOffset) + { + Context cx = CurrentContext; + if (cx != null) { + cx.ErrorReporter.Error (message, sourceName, lineno, lineSource, lineOffset); + } + else { + throw new EcmaScriptRuntimeException (message, sourceName, lineno, lineSource, lineOffset); + } + } + + /// Report an error using the error reporter for the current thread. + /// + /// + /// the error message to report + /// + + public static void ReportError (string message) + { + int [] linep = new int [] { 0 }; + string filename = GetSourcePositionFromStack (linep); + Context.ReportError (message, filename, linep [0], null, 0); + } + + /// Report a runtime error using the error reporter for the current thread. + /// + /// + /// the error message to report + /// + /// a string describing the source, such as a filename + /// + /// the starting line number + /// + /// the text of the line (may be null) + /// + /// the offset into lineSource where problem was detected + /// + /// a runtime exception that will be thrown to terminate the + /// execution of the script + /// + public static EcmaScriptRuntimeException ReportRuntimeError (string message, string sourceName, int lineno, string lineSource, int lineOffset) + { + Context cx = CurrentContext; + if (cx != null) { + return cx.ErrorReporter.RuntimeError (message, sourceName, lineno, lineSource, lineOffset); + } + else { + throw new EcmaScriptRuntimeException (message, sourceName, lineno, lineSource, lineOffset); + } + } + + + internal static EcmaScriptRuntimeException ReportRuntimeErrorById (string messageId, params object [] args) + { + return ReportRuntimeError (ScriptRuntime.GetMessage (messageId, args)); + } + + /// Report a runtime error using the error reporter for the current thread. + /// + /// + /// the error message to report + /// + public static EcmaScriptRuntimeException ReportRuntimeError (string message) + { + int [] linep = new int [] { 0 }; + string filename = GetSourcePositionFromStack (linep); + return Context.ReportRuntimeError (message, filename, linep [0], null, 0); + } + + /// Initialize the standard objects. + /// + /// Creates instances of the standard objects and their constructors + /// (Object, String, Number, Date, etc.), setting up 'scope' to act + /// as a global object as in ECMA 15.1.

+ /// + /// This method must be called to initialize a scope before scripts + /// can be evaluated in that scope.

+ /// + /// This method does not affect the Context it is called upon. + /// + ///

+ /// the initialized scope + /// + public ScriptableObject InitStandardObjects () + { + return InitStandardObjects (null, false); + } + + /// Initialize the standard objects. + /// + /// Creates instances of the standard objects and their constructors + /// (Object, String, Number, Date, etc.), setting up 'scope' to act + /// as a global object as in ECMA 15.1.

+ /// + /// This method must be called to initialize a scope before scripts + /// can be evaluated in that scope.

+ /// + /// This method does not affect the Context it is called upon. + /// + ///

+ /// the scope to initialize, or null, in which case a new + /// object will be created to serve as the scope + /// + /// the initialized scope. The method returns the value of the scope + /// argument if it is not null or newly allocated scope object which + /// is an instance {@link ScriptableObject}. + /// + public IScriptable InitStandardObjects (ScriptableObject scope) + { + return InitStandardObjects (scope, false); + } + + /// Initialize the standard objects. + /// + /// Creates instances of the standard objects and their constructors + /// (Object, String, Number, Date, etc.), setting up 'scope' to act + /// as a global object as in ECMA 15.1.

+ /// + /// This method must be called to initialize a scope before scripts + /// can be evaluated in that scope.

+ /// + /// This method does not affect the Context it is called upon.

+ /// + /// This form of the method also allows for creating "sealed" standard + /// objects. An object that is sealed cannot have properties added, changed, + /// or removed. This is useful to create a "superglobal" that can be shared + /// among several top-level objects. Note that sealing is not allowed in + /// the current ECMA/ISO language specification, but is likely for + /// the next version. + /// + ///

+ /// the scope to initialize, or null, in which case a new + /// object will be created to serve as the scope + /// + /// whether or not to create sealed standard objects that + /// cannot be modified. + /// + /// the initialized scope. The method returns the value of the scope + /// argument if it is not null or newly allocated scope object. + /// + public ScriptableObject InitStandardObjects (ScriptableObject scope, bool zealed) + { + return ScriptRuntime.InitStandardObjects (this, scope, zealed); + } + + /// Evaluate a JavaScript source string. + /// + /// The provided source name and line number are used for error messages + /// and for producing debug information. + /// + /// + /// the scope to execute in + /// + /// the JavaScript source + /// + /// a string describing the source, such as a filename + /// + /// the starting line number + /// + /// an arbitrary object that specifies security + /// information about the origin or owner of the script. For + /// implementations that don't care about security, this value + /// may be null. + /// + /// the result of evaluating the string + /// + public object EvaluateString (IScriptable scope, string source, string sourceName, int lineno, object securityDomain) + { + IScript script = CompileString (source, sourceName, lineno, securityDomain); + if (script != null) { + return script.Exec (this, scope); + } + else { + return null; + } + } + + /// Evaluate a reader as JavaScript source. + /// + /// All characters of the reader are consumed. + /// + /// + /// the scope to execute in + /// + /// the Reader to get JavaScript source from + /// + /// a string describing the source, such as a filename + /// + /// the starting line number + /// + /// an arbitrary object that specifies security + /// information about the origin or owner of the script. For + /// implementations that don't care about security, this value + /// may be null. + /// + /// the result of evaluating the source + /// + /// + /// IOException if an IOException was generated by the Reader + /// + public object EvaluateReader (IScriptable scope, StreamReader sr, string sourceName, int lineno, object securityDomain) + { + IScript script = CompileReader (sr, sourceName, lineno, securityDomain); + + if (script != null) { + return script.Exec (this, scope); + } + return null; + } + + /// Check whether a string is ready to be compiled. + ///

+ /// stringIsCompilableUnit is intended to support interactive compilation of + /// javascript. If compiling the string would result in an error + /// that might be fixed by appending more source, this method + /// returns false. In every other case, it returns true. + ///

+ /// Interactive shells may accumulate source lines, using this + /// method after each new line is appended to check whether the + /// statement being entered is complete. + /// + ///

+ /// the source buffer to check + /// + /// whether the source is ready for compilation + /// + public ScriptOrFnNode IsCompilableUnit (string source) + { + ScriptOrFnNode ret = null; + bool errorseen = false; + CompilerEnvirons compilerEnv = new CompilerEnvirons (); + compilerEnv.initFromContext (this); + // no source name or source text manager, because we're just + // going to throw away the result. + compilerEnv.setGeneratingSource (false); + Parser p = new Parser (compilerEnv, DefaultErrorReporter.instance); + try { + ret = p.Parse (source, null, 1); + } + catch (EcmaScriptRuntimeException) { + errorseen = true; + } + // Return false only if an error occurred as a result of reading past + // the end of the file, i.e. if the source could be fixed by + // appending more source. + if (!(errorseen && p.Eof)) + return ret; + return null; + } + + + /// Compiles the source in the given reader. + ///

+ /// Returns a script that may later be executed. + /// Will consume all the source in the reader. + /// + ///

+ /// the input reader + /// + /// a string describing the source, such as a filename + /// + /// the starting line number for reporting errors + /// + /// an arbitrary object that specifies security + /// information about the origin or owner of the script. For + /// implementations that don't care about security, this value + /// may be null. + /// + /// a script that may later be executed + /// + /// IOException if an IOException was generated by the Reader + /// + public IScript CompileReader (StreamReader sr, string sourceName, int lineno, object securityDomain) + { + if (lineno < 0) + throw new ArgumentException ("lineno may not be negative", "lineno"); + return (IScript)CompileImpl (null, sr, null, sourceName, lineno, securityDomain, false, null, null); + } + + /// Compiles the source in the given string. + ///

+ /// Returns a script that may later be executed. + /// + ///

+ /// the source string + /// + /// a string describing the source, such as a filename + /// + /// the starting line number for reporting errors + /// + /// an arbitrary object that specifies security + /// information about the origin or owner of the script. For + /// implementations that don't care about security, this value + /// may be null. + /// + /// a script that may later be executed + /// + public IScript CompileString (string source, string sourceName, int lineno, object securityDomain) + { + if (lineno < 0) { + // For compatibility IllegalArgumentException can not be thrown here + lineno = 0; + } + return CompileString (source, null, null, sourceName, lineno, securityDomain); + } + + internal IScript CompileString (string source, Interpreter compiler, ErrorReporter compilationErrorReporter, string sourceName, int lineno, object securityDomain) + { + return (IScript)CompileImpl (null, null, source, sourceName, lineno, securityDomain, false, compiler, compilationErrorReporter); + } + + /// Compile a JavaScript function. + ///

+ /// The function source must be a function definition as defined by + /// ECMA (e.g., "function f(a) { return a; }"). + /// + ///

+ /// the scope to compile relative to + /// + /// the function definition source + /// + /// a string describing the source, such as a filename + /// + /// the starting line number + /// + /// an arbitrary object that specifies security + /// information about the origin or owner of the script. For + /// implementations that don't care about security, this value + /// may be null. + /// + /// a Function that may later be called + /// + public IFunction CompileFunction (IScriptable scope, string source, string sourceName, int lineno, object securityDomain) + { + return CompileFunction (scope, source, null, null, sourceName, lineno, securityDomain); + } + + internal IFunction CompileFunction (IScriptable scope, string source, Interpreter compiler, ErrorReporter compilationErrorReporter, string sourceName, int lineno, object securityDomain) + { + return (IFunction)CompileImpl (scope, null, source, sourceName, lineno, securityDomain, true, compiler, compilationErrorReporter); + } + + /// Decompile the script. + ///

+ /// The canonical source of the script is returned. + /// + ///

+ /// the script to decompile + /// + /// the number of spaces to indent the result + /// + /// a string representing the script source + /// + public string DecompileScript (IScript script, int indent) + { + BuiltinFunction scriptImpl = (BuiltinFunction)script; + return scriptImpl.Decompile (indent, 0); + } + + /// Decompile a JavaScript Function. + ///

+ /// Decompiles a previously compiled JavaScript function object to + /// canonical source. + ///

+ /// Returns function body of '[native code]' if no decompilation + /// information is available. + /// + ///

+ /// the JavaScript function to decompile + /// + /// the number of spaces to indent the result + /// + /// a string representing the function source + /// + public string DecompileFunction (IFunction fun, int indent) + { + if (fun is BaseFunction) + return ((BaseFunction)fun).Decompile (indent, 0); + else + return "function " + fun.ClassName + "() {\n\t[native code]\n}\n"; + } + + /// Decompile the body of a JavaScript Function. + ///

+ /// Decompiles the body a previously compiled JavaScript Function + /// object to canonical source, omitting the function header and + /// trailing brace. + /// + /// Returns '[native code]' if no decompilation information is available. + /// + ///

+ /// the JavaScript function to decompile + /// + /// the number of spaces to indent the result + /// + /// a string representing the function body source. + /// + public string DecompileFunctionBody (IFunction fun, int indent) + { + if (fun is BaseFunction) { + BaseFunction bf = (BaseFunction)fun; + return bf.Decompile (indent, Decompiler.ONLY_BODY_FLAG); + } + // ALERT: not sure what the right response here is. + return "[native code]\n"; + } + + /// Create a new JavaScript object. + /// + /// Equivalent to evaluating "new Object()". + /// + /// the scope to search for the constructor and to evaluate + /// against + /// + /// the new object + /// + public IScriptable NewObject (IScriptable scope) + { + return NewObject (scope, "Object", ScriptRuntime.EmptyArgs); + } + + /// Create a new JavaScript object by executing the named constructor. + /// + /// The call newObject(scope, "Foo") is equivalent to + /// evaluating "new Foo()". + /// + /// + /// the scope to search for the constructor and to evaluate against + /// + /// the name of the constructor to call + /// + /// the new object + /// + public IScriptable NewObject (IScriptable scope, string constructorName) + { + return NewObject (scope, constructorName, ScriptRuntime.EmptyArgs); + } + + /// Creates a new JavaScript object by executing the named constructor. + /// + /// Searches scope for the named constructor, calls it with + /// the given arguments, and returns the result.

+ /// + /// The code + ///

+        /// Object[] args = { "a", "b" };
+        /// newObject(scope, "Foo", args)
+ /// is equivalent to evaluating "new Foo('a', 'b')", assuming that the Foo + /// constructor has been defined in scope. + /// + ///
+ /// The scope to search for the constructor and to evaluate + /// against + /// + /// the name of the constructor to call + /// + /// the array of arguments for the constructor + /// + /// the new object + /// + public IScriptable NewObject (IScriptable scope, string constructorName, object [] args) + { + scope = ScriptableObject.GetTopLevelScope (scope); + IFunction ctor = ScriptRuntime.getExistingCtor (this, scope, constructorName); + if (args == null) { + args = ScriptRuntime.EmptyArgs; + } + return ctor.Construct (this, scope, args); + } + + /// Create an array with a specified initial length. + ///

+ ///

+ /// the scope to create the object in + /// + /// the initial length (JavaScript arrays may have + /// additional properties added dynamically). + /// + /// the new array object + /// + public IScriptable NewArray (IScriptable scope, int length) + { + BuiltinArray result = new BuiltinArray (length); + ScriptRuntime.setObjectProtoAndParent (result, scope); + return result; + } + + /// Create an array with a set of initial elements. + /// + /// + /// the scope to create the object in. + /// + /// the initial elements. Each object in this array + /// must be an acceptable JavaScript type and type + /// of array should be exactly Object[], not + /// SomeObjectSubclass[]. + /// + /// the new array object. + /// + public IScriptable NewArray (IScriptable scope, object [] elements) + { + Type elementType = elements.GetType ().GetElementType (); + if (elementType != typeof (object)) + throw new ArgumentException (); + BuiltinArray result = new BuiltinArray (elements); + ScriptRuntime.setObjectProtoAndParent (result, scope); + return result; + } + + /// Get the elements of a JavaScript array. + ///

+ /// If the object defines a length property convertible to double number, + /// then the number is converted Uint32 value as defined in Ecma 9.6 + /// and Java array of that size is allocated. + /// The array is initialized with the values obtained by + /// calling get() on object for each value of i in [0,length-1]. If + /// there is not a defined value for a property the Undefined value + /// is used to initialize the corresponding element in the array. The + /// Java array is then returned. + /// If the object doesn't define a length property or it is not a number, + /// empty array is returned. + ///

+ /// the JavaScript array or array-like object + /// + /// a Java array of objects + /// + + public object [] GetElements (IScriptable obj) + { + return ScriptRuntime.getArrayElements (obj); + } + + + /// Convenient method to convert java value to its closest representation + /// in JavaScript. + ///

+ /// If value is an instance of String, Number, Boolean, Function or + /// Scriptable, it is returned as it and will be treated as the corresponding + /// JavaScript type of string, number, boolean, function and object. + ///

+ /// Note that for Number instances during any arithmetic operation in + /// JavaScript the engine will always use the result of + /// Number.doubleValue() resulting in a precision loss if + /// the number can not fit into double. + ///

+ /// If value is an instance of Character, it will be converted to string of + /// length 1 and its JavaScript type will be string. + ///

+ /// The rest of values will be wrapped as LiveConnect objects + /// by calling {@link WrapFactory#wrap(Context cx, Scriptable scope, + /// Object obj, Class staticType)} as in: + ///

+        /// Context cx = Context.getCurrentContext();
+        /// return cx.getWrapFactory().wrap(cx, scope, value, null);
+        /// 
+ /// + ///
+ /// any Java object + /// + /// top scope object + /// + /// value suitable to pass to any API that takes JavaScript values. + /// + public static object CliToJS (Context cx, object value, IScriptable scope) + { + if (value is string || CliHelper.IsNumber (value) || value is bool || value is IScriptable) { + return value; + } + else if (value is char) { + return Convert.ToString (((char)value)); + } + else { + Type type = (value as Type); + if (type != null) { + return cx.Wrap (scope, value, (Type)value); + } else { + return cx.Wrap (scope, value, null); + } + } + } + + /// Convert a JavaScript value into the desired type. + /// Uses the semantics defined with LiveConnect3 and throws an + /// Illegal argument exception if the conversion cannot be performed. + /// + /// the JavaScript value to convert + /// + /// the Java type to convert to. Primitive Java + /// types are represented using the TYPE fields in the corresponding + /// wrapper class in java.lang. + /// + /// the converted value + /// + /// EvaluatorException if the conversion cannot be performed + public static object JsToCli (object value, System.Type desiredType) + { + return CliObject.CoerceType (desiredType, value); + } + + + + /// Rethrow the exception wrapping it as the script runtime exception. + /// Unless the exception is instance of {@link EcmaError} or + /// {@link EvaluatorException} it will be wrapped as + /// {@link WrappedException}, a subclass of {@link EvaluatorException}. + /// The resulting exception object always contains + /// source name and line number of script that triggered exception. + ///

+ /// This method always throws an exception, its return value is provided + /// only for convenience to allow a usage like: + ///

+        /// throw Context.throwAsScriptRuntimeEx(ex);
+        /// 
+ /// to indicate that code after the method is unreachable. + ///
+ /// EvaluatorException + /// EcmaError + public static Exception ThrowAsScriptRuntimeEx (Exception e) + { + while ((e is TargetInvocationException)) { + e = ((TargetInvocationException)e).InnerException; + } + if (e is EcmaScriptException) { + throw e; + } + throw new EcmaScriptRuntimeException (e); + } + + public static bool IsValidOptimizationLevel (int optimizationLevel) + { + return -1 <= optimizationLevel && optimizationLevel <= 9; + } + + public static void CheckOptimizationLevel (int optimizationLevel) + { + if (IsValidOptimizationLevel (optimizationLevel)) { + return; + } + throw new ArgumentException ("Optimization level outside [-1..9]: " + optimizationLevel); + } + + /// Set the security controller for this context. + ///

SecurityController may only be set if it is currently null + /// and {@link SecurityController#hasGlobal()} is false. + /// Otherwise a SecurityException is thrown. + ///

+ /// a SecurityController object + /// + /// SecurityException if there is already a SecurityController + /// object for this Context or globally installed. + /// + public SecurityController SecurityController + { + set + { + if (m_Sealed) + OnSealedMutation (); + if (value == null) + throw new ArgumentException (); + if (securityController != null) { + throw new System.Security.SecurityException ("Can not overwrite existing SecurityController object"); + } + if (SecurityController.HasGlobal ()) { + throw new System.Security.SecurityException ("Can not overwrite existing global SecurityController object"); + } + securityController = value; + } + get + { + SecurityController global = SecurityController.Global; + if (global != null) { + return global; + } + return securityController; + } + } + + /// Get a value corresponding to a key. + ///

+ /// Since the Context is associated with a thread it can be + /// used to maintain values that can be later retrieved using + /// the current thread. + ///

+ /// Note that the values are maintained with the Context, so + /// if the Context is disassociated from the thread the values + /// cannot be retreived. Also, if private data is to be maintained + /// in this manner the key should be a java.lang.Object + /// whose reference is not divulged to untrusted code. + ///

+ /// the key used to lookup the value + /// + /// a value previously stored using putThreadLocal. + /// + public object GetThreadLocal (object key) + { + if (hashtable == null) + return null; + return hashtable [key]; + } + + /// Put a value that can later be retrieved using a given key. + ///

+ ///

+ /// the key used to index the value + /// + /// the value to save + /// + public void PutThreadLocal (object key, object value) + { + if (m_Sealed) + OnSealedMutation (); + if (hashtable == null) + hashtable = Hashtable.Synchronized (new Hashtable ()); + hashtable [key] = value; + } + + /// Remove values from thread-local storage. + /// the key for the entry to remove. + /// + public void RemoveThreadLocal (object key) + { + if (m_Sealed) + OnSealedMutation (); + if (hashtable == null) + return; + hashtable.Remove (key); + } + + + + /// Return the current debugger. + /// the debugger, or null if none is attached. + /// + public Debugger Debugger + { + get + { + return m_Debugger; + } + } + + /// Set the associated debugger. + /// the debugger to be used on callbacks from + /// the engine. + /// + /// arbitrary object that debugger can use to store + /// per Context data. + /// + public void SetDebugger (Debugger debugger, object contextData) + { + if (m_Sealed) + OnSealedMutation (); + this.m_Debugger = debugger; + debuggerData = contextData; + } + + /// Return DebuggableScript instance if any associated with the script. + /// If callable supports DebuggableScript implementation, the method + /// returns it. Otherwise null is returned. + /// + public static DebuggableScript getDebuggableView (IScript script) + { + if (script is BuiltinFunction) { + return ((BuiltinFunction)script).DebuggableView; + } + return null; + } + + + private Features m_Features = Features.None; + + /// + /// Controls certain aspects of script semantics. + /// Should be overwritten to alter default behavior. + /// + /// The default implementation calls + /// {@link ContextFactory#hasFeature(Context cx, int featureIndex)} + /// that allows to customize Context behavior without introducing + /// Context subclasses. {@link ContextFactory} documentation gives + /// an example of hasFeature implementation. + /// + /// + /// feature to check + /// + /// true if the feature feature is turned on + /// + public bool HasFeature (Features feature) + { + return (m_Features & feature) == feature; + } + + public void SetFeature (Features feature, bool isEnabled) + { + if (isEnabled) { + m_Features |= feature; + } + else { + if (HasFeature (feature)) + m_Features = m_Features ^ feature; + } + } + + + /// Allow application to monitor counter of executed script instructions + /// in Context subclasses. + /// Run-time calls this when instruction counting is enabled and the counter + /// reaches limit set by setInstructionObserverThreshold(). + /// The method is useful to observe long running scripts and if necessary + /// to terminate them. + ///

+ /// The instruction counting support is available only for interpreted + /// scripts generated when the optimization level is set to -1. + ///

+ /// The default implementation calls + /// {@link ContextFactory#observeInstructionCount(Context cx, + /// int instructionCount)} + /// that allows to customize Context behavior without introducing + /// Context subclasses. + /// + ///

+ /// amount of script instruction executed since + /// last call to observeInstructionCount + /// + /// Error to terminate the script + protected internal void ObserveInstructionCount (int instructionCount) + { + Factory.ObserveInstructionCount (this, instructionCount); + } + + private object CompileImpl (IScriptable scope, StreamReader sourceReader, string sourceString, string sourceName, int lineno, object securityDomain, bool returnFunction, Interpreter compiler, ErrorReporter compilationErrorReporter) + { + if (securityDomain != null && securityController == null) { + throw new ArgumentException ("securityDomain should be null if setSecurityController() was never called"); + } + + // One of sourceReader or sourceString has to be null + if (!(sourceReader == null ^ sourceString == null)) + Context.CodeBug (); + // scope should be given if and only if compiling function + if (!(scope == null ^ returnFunction)) + Context.CodeBug (); + + CompilerEnvirons compilerEnv = new CompilerEnvirons (); + compilerEnv.initFromContext (this); + if (compilationErrorReporter == null) { + compilationErrorReporter = compilerEnv.getErrorReporter (); + } + + if (m_Debugger != null) { + if (sourceReader != null) { + sourceString = sourceReader.ReadToEnd (); + sourceReader = null; + } + } + + Parser p = new Parser (compilerEnv, compilationErrorReporter); + if (returnFunction) { + p.calledByCompileFunction = true; + } + ScriptOrFnNode tree; + if (sourceString != null) { + tree = p.Parse (sourceString, sourceName, lineno); + } + else { + tree = p.Parse (sourceReader, sourceName, lineno); + } + if (returnFunction) { + if (!(tree.FunctionCount == 1 && tree.FirstChild != null && tree.FirstChild.Type == Token.FUNCTION)) { + // TODO: the check just look for the first child + // TODO: and allows for more nodes after it for compatibility + // TODO: with sources like function() {};;; + throw new ArgumentException ("compileFunction only accepts source with single JS function: " + sourceString); + } + } + + + + if (compiler == null) { + compiler = new Interpreter (); + //compiler = new Compiler(); + } + + string encodedSource = p.EncodedSource; + + object bytecode = compiler.Compile (compilerEnv, tree, encodedSource, returnFunction); + + if (m_Debugger != null) { + if (sourceString == null) + Context.CodeBug (); + if (bytecode is DebuggableScript) { + DebuggableScript dscript = (DebuggableScript)bytecode; + NotifyDebugger (this, dscript, sourceString); + } + else { + throw new Exception ("NOT SUPPORTED"); + } + } + + object result; + if (returnFunction) { + result = compiler.CreateFunctionObject (this, scope, bytecode, securityDomain); + } + else { + result = compiler.CreateScriptObject (bytecode, securityDomain); + } + + return result; + } + + private static void NotifyDebugger (Context cx, DebuggableScript dscript, string debugSource) + { + cx.m_Debugger.HandleCompilationDone (cx, dscript, debugSource); + for (int i = 0; i != dscript.FunctionCount; ++i) { + NotifyDebugger (cx, dscript.GetFunction (i), debugSource); + } + } + + internal static string GetSourcePositionFromStack (int [] linep) + { + Context cx = CurrentContext; + if (cx == null) + return null; + if (cx.lastInterpreterFrame != null) { + return Interpreter.GetSourcePositionFromStack (cx, linep); + } + + System.Diagnostics.StackTrace st = new System.Diagnostics.StackTrace (); + System.Diagnostics.StackFrame sf = st.GetFrame (0); + linep [0] = sf.GetFileLineNumber (); + return Path.GetFileName (sf.GetFileName ()); + } + + + /// Add a name to the list of names forcing the creation of real + /// activation objects for functions. + /// + /// + /// the name of the object to add to the list + /// + public void AddActivationName (string name) + { + if (m_Sealed) + OnSealedMutation (); + if (activationNames == null) + activationNames = Hashtable.Synchronized (new Hashtable (5)); + activationNames [name] = name; + } + + /// Check whether the name is in the list of names of objects + /// forcing the creation of activation objects. + /// + /// + /// the name of the object to test + /// + /// + /// true if an function activation object is needed. + /// + public bool IsActivationNeeded (string name) + { + return activationNames != null && activationNames.ContainsKey (name); + } + + /// Remove a name from the list of names forcing the creation of real + /// activation objects for functions. + /// + /// + /// the name of the object to remove from the list + /// + public void RemoveActivationName (string name) + { + if (m_Sealed) + OnSealedMutation (); + if (activationNames != null) + activationNames.Remove (name); + } + + private static string implementationVersion; + + private ContextFactory factory; + private bool m_Sealed; + private object m_SealKey; + + internal IScriptable topCallScope; + internal BuiltinCall currentActivationCall; + internal XMLLib cachedXMLLib; + + // for Objects, Arrays to tag themselves as being printed out, + // so they don't print themselves out recursively. + // Use ObjToIntMap instead of java.util.HashSet for JDK 1.1 compatibility + internal ObjToIntMap iterating; + + internal object interpreterSecurityDomain; + + internal Versions m_Version = Versions.Unknown; + + private SecurityController securityController; + + private ErrorReporter m_ErrorReporter; + internal RegExpProxy regExpProxy; + private System.Globalization.CultureInfo culture; + private bool generatingDebug; + private bool generatingDebugChanged; + private bool generatingSource = true; + internal bool compileFunctionsWithDynamicScopeFlag = false; + internal bool useDynamicScope; + private int m_OptimizationLevel; + + internal Debugger m_Debugger; + private object debuggerData; + private int enterCount; + private Hashtable hashtable; + + private bool creationEventWasSent; + + /// This is the list of names of objects forcing the creation of + /// function activation records. + /// + internal Hashtable activationNames; + + // For the interpreter to store the last frame for error reports etc. + internal object lastInterpreterFrame; + + // For the interpreter to store information about previous invocations + // interpreter invocations + internal ObjArray previousInterpreterInvocations; + + // For instruction counting (interpreter only) + internal int instructionCount; + internal int instructionThreshold; + + // It can be used to return the second index-like result from function + internal int scratchIndex; + + // It can be used to return the second uint32 result from function + internal long scratchUint32; + + // It can be used to return the second Scriptable result from function + internal IScriptable scratchScriptable; + static Context () + { + EmptyArgs = ScriptRuntime.EmptyArgs; + } + + public void Dispose () + { + Context.Exit (); + } + + /// + /// Throws RuntimeException to indicate failed assertion. + /// The function never returns and its return type is RuntimeException + /// only to be able to write throw EcmaScriptHelper.CodeBug() if plain + /// EcmaScriptHelper.CodeBug() triggers unreachable code error. + /// + public static Exception CodeBug () + { + Exception ex = new Exception ("FAILED ASSERTION"); + Console.Error.WriteLine (ex.ToString ()); + throw ex; + } + + } +} diff --git a/Code/EcmaScript.NET/ContextEvents.cs b/src/EcmaScript.NET/ContextEvents.cs similarity index 95% rename from Code/EcmaScript.NET/ContextEvents.cs rename to src/EcmaScript.NET/ContextEvents.cs index d7ce984..20a6e39 100644 --- a/Code/EcmaScript.NET/ContextEvents.cs +++ b/src/EcmaScript.NET/ContextEvents.cs @@ -1,105 +1,105 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; - -namespace EcmaScript.NET -{ - - public class ContextEventArgs : EventArgs - { - - private Context m_Context = null; - - /// - /// The underlying context - /// - public Context Context - { - get { return m_Context; } - } - - /// - /// Creates a new ContextEventArgs object - /// - /// - public ContextEventArgs (Context cx) - { - m_Context = cx; - } - - } - - public class ContextScriptableEventArgs : ContextEventArgs - { - - - private IScriptable m_Scope = null; - - public ContextScriptableEventArgs (Context cx, IScriptable scope) - : base (cx) - { - m_Scope = scope; - } - - public IScriptable Scope - { - get { return m_Scope; } - } - - } - - - /// - /// - /// - public class ContextWrapEventArgs : ContextScriptableEventArgs - { - - private object m_Source = null; - - public object Source - { - get { return m_Source; } - } - - private object m_Target = null; - - public object Target - { - get { return m_Target; } - set { m_Target = null; } - } - - - private Type m_StaticType = null; - - public Type staticType - { - get { return m_StaticType; } - } - - public ContextWrapEventArgs (Context cx, IScriptable scope, object obj, Type staticType) - : base (cx, scope) - { - m_Source = obj; - m_StaticType = staticType; - } - - } - - public delegate void ContextEventHandler (object sender, ContextEventArgs e); - - public delegate void ContextWrapHandler (object sender, ContextWrapEventArgs e); - -} +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; + +namespace EcmaScript.NET +{ + + public class ContextEventArgs : EventArgs + { + + private Context m_Context = null; + + /// + /// The underlying context + /// + public Context Context + { + get { return m_Context; } + } + + /// + /// Creates a new ContextEventArgs object + /// + /// + public ContextEventArgs (Context cx) + { + m_Context = cx; + } + + } + + public class ContextScriptableEventArgs : ContextEventArgs + { + + + private IScriptable m_Scope = null; + + public ContextScriptableEventArgs (Context cx, IScriptable scope) + : base (cx) + { + m_Scope = scope; + } + + public IScriptable Scope + { + get { return m_Scope; } + } + + } + + + /// + /// + /// + public class ContextWrapEventArgs : ContextScriptableEventArgs + { + + private object m_Source = null; + + public object Source + { + get { return m_Source; } + } + + private object m_Target = null; + + public object Target + { + get { return m_Target; } + set { m_Target = null; } + } + + + private Type m_StaticType = null; + + public Type staticType + { + get { return m_StaticType; } + } + + public ContextWrapEventArgs (Context cx, IScriptable scope, object obj, Type staticType) + : base (cx, scope) + { + m_Source = obj; + m_StaticType = staticType; + } + + } + + public delegate void ContextEventHandler (object sender, ContextEventArgs e); + + public delegate void ContextWrapHandler (object sender, ContextWrapEventArgs e); + +} diff --git a/Code/EcmaScript.NET/ContextFactory.cs b/src/EcmaScript.NET/ContextFactory.cs similarity index 95% rename from Code/EcmaScript.NET/ContextFactory.cs rename to src/EcmaScript.NET/ContextFactory.cs index 65819fc..f80d038 100644 --- a/Code/EcmaScript.NET/ContextFactory.cs +++ b/src/EcmaScript.NET/ContextFactory.cs @@ -1,150 +1,150 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; - -namespace EcmaScript.NET -{ - - /// Factory class that Rhino runtime use to create new {@link Context} - /// instances or to notify about Context execution. - ///

- /// When Rhino runtime needs to create new {@link Context} instance during - /// execution of {@link Context#enter()} or {@link Context}, it will call - /// {@link #makeContext()} of the current global ContextFactory. - /// See {@link #getGlobal()} and {@link #initGlobal(ContextFactory)}. - ///

- /// It is also possible to use explicit ContextFactory instances for Context - /// creation. This is useful to have a set of independent Rhino runtime - /// instances under single JVM. See {@link #call(ContextAction)}. - ///

- ///

- - public class ContextFactory - { - /// Get global ContextFactory. - /// - /// - public static ContextFactory Global - { - get - { - return global; - } - - } - - /// Checks if this is a sealed ContextFactory. - virtual public bool Sealed - { - get - { - return zealed; - } - - } - private static volatile bool hasCustomGlobal; - private static ContextFactory global = new ContextFactory (); - - private volatile bool zealed; - - - /// Notify about newly created {@link Context} object. - public event ContextEventHandler OnContextCreated; - - /// Notify that the specified {@link Context} instance is no longer - /// associated with the current thread. - /// - public event ContextEventHandler OnContextReleased; - - /// Check if global factory was set. - /// Return true to indicate that {@link #initGlobal(ContextFactory)} was - /// already called and false to indicate that the global factory was not - /// explicitly set. - /// - /// - public static bool HasExplicitGlobal - { - get - { - return hasCustomGlobal; - } - } - - /// Set global ContextFactory. - /// The method can only be called once. - /// - /// - public static void InitGlobal (ContextFactory factory) - { - if (factory == null) { - throw new ArgumentException (); - } - if (hasCustomGlobal) { - throw new ApplicationException (); - } - hasCustomGlobal = true; - global = factory; - } - - - /// Execute top call to script or function. - /// When the runtime is about to execute a script or function that will - /// create the first stack frame with scriptable code, it calls this method - /// to perform the real call. In this way execution of any script - /// happens inside this function. - /// - protected internal virtual object DoTopCall (ICallable callable, Context cx, IScriptable scope, IScriptable thisObj, object [] args) - { - return callable.Call (cx, scope, thisObj, args); - } - - /// Implementation of - /// {@link Context#observeInstructionCount(int instructionCount)}. - /// This can be used to customize {@link Context} without introducing - /// additional subclasses. - /// - protected internal virtual void ObserveInstructionCount (Context cx, int instructionCount) - { - } - - protected internal virtual void FireOnContextCreated (Context cx) - { - if (OnContextCreated != null) - OnContextCreated (this, new ContextEventArgs (cx)); - } - - protected internal virtual void FireOnContextReleased (Context cx) - { - if (OnContextReleased != null) - OnContextReleased (this, new ContextEventArgs (cx)); - } - - - /// Seal this ContextFactory so any attempt to modify it like to add or - /// remove its listeners will throw an exception. - /// - public void Seal () - { - CheckNotSealed (); - zealed = true; - } - - protected internal void CheckNotSealed () - { - if (zealed) - throw new ApplicationException (); - } - - } +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; + +namespace EcmaScript.NET +{ + + /// Factory class that Rhino runtime use to create new {@link Context} + /// instances or to notify about Context execution. + ///

+ /// When Rhino runtime needs to create new {@link Context} instance during + /// execution of {@link Context#enter()} or {@link Context}, it will call + /// {@link #makeContext()} of the current global ContextFactory. + /// See {@link #getGlobal()} and {@link #initGlobal(ContextFactory)}. + ///

+ /// It is also possible to use explicit ContextFactory instances for Context + /// creation. This is useful to have a set of independent Rhino runtime + /// instances under single JVM. See {@link #call(ContextAction)}. + ///

+ ///

+ + public class ContextFactory + { + /// Get global ContextFactory. + /// + /// + public static ContextFactory Global + { + get + { + return global; + } + + } + + /// Checks if this is a sealed ContextFactory. + virtual public bool Sealed + { + get + { + return zealed; + } + + } + private static volatile bool hasCustomGlobal; + private static ContextFactory global = new ContextFactory (); + + private volatile bool zealed; + + + /// Notify about newly created {@link Context} object. + public event ContextEventHandler OnContextCreated; + + /// Notify that the specified {@link Context} instance is no longer + /// associated with the current thread. + /// + public event ContextEventHandler OnContextReleased; + + /// Check if global factory was set. + /// Return true to indicate that {@link #initGlobal(ContextFactory)} was + /// already called and false to indicate that the global factory was not + /// explicitly set. + /// + /// + public static bool HasExplicitGlobal + { + get + { + return hasCustomGlobal; + } + } + + /// Set global ContextFactory. + /// The method can only be called once. + /// + /// + public static void InitGlobal (ContextFactory factory) + { + if (factory == null) { + throw new ArgumentException (); + } + if (hasCustomGlobal) { + throw new Exception (); + } + hasCustomGlobal = true; + global = factory; + } + + + /// Execute top call to script or function. + /// When the runtime is about to execute a script or function that will + /// create the first stack frame with scriptable code, it calls this method + /// to perform the real call. In this way execution of any script + /// happens inside this function. + /// + protected internal virtual object DoTopCall (ICallable callable, Context cx, IScriptable scope, IScriptable thisObj, object [] args) + { + return callable.Call (cx, scope, thisObj, args); + } + + /// Implementation of + /// {@link Context#observeInstructionCount(int instructionCount)}. + /// This can be used to customize {@link Context} without introducing + /// additional subclasses. + /// + protected internal virtual void ObserveInstructionCount (Context cx, int instructionCount) + { + } + + protected internal virtual void FireOnContextCreated (Context cx) + { + if (OnContextCreated != null) + OnContextCreated (this, new ContextEventArgs (cx)); + } + + protected internal virtual void FireOnContextReleased (Context cx) + { + if (OnContextReleased != null) + OnContextReleased (this, new ContextEventArgs (cx)); + } + + + /// Seal this ContextFactory so any attempt to modify it like to add or + /// remove its listeners will throw an exception. + /// + public void Seal () + { + CheckNotSealed (); + zealed = true; + } + + protected internal void CheckNotSealed () + { + if (zealed) + throw new Exception (); + } + + } } \ No newline at end of file diff --git a/Code/EcmaScript.NET/Continuation.cs b/src/EcmaScript.NET/Continuation.cs similarity index 96% rename from Code/EcmaScript.NET/Continuation.cs rename to src/EcmaScript.NET/Continuation.cs index bd0d89e..7df3552 100644 --- a/Code/EcmaScript.NET/Continuation.cs +++ b/src/EcmaScript.NET/Continuation.cs @@ -1,130 +1,130 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; -using EcmaScript.NET; - -namespace EcmaScript.NET -{ - - - public sealed class Continuation : IdScriptableObject, IFunction - { - public object Implementation - { - get - { - return implementation; - } - - } - override public string ClassName - { - get - { - return "Continuation"; - } - - } - - - private static readonly object FTAG = new object (); - - private object implementation; - - public static void Init (IScriptable scope, bool zealed) - { - Continuation obj = new Continuation (); - obj.ExportAsJSClass (MAX_PROTOTYPE_ID, scope, zealed); - } - - public void initImplementation (object implementation) - { - this.implementation = implementation; - } - - public IScriptable Construct (Context cx, IScriptable scope, object [] args) - { - throw Context.ReportRuntimeError ("Direct call is not supported"); - } - - public object Call (Context cx, IScriptable scope, IScriptable thisObj, object [] args) - { - return Interpreter.restartContinuation (this, cx, scope, args); - } - - public static bool IsContinuationConstructor (IdFunctionObject f) - { - if (f.HasTag (FTAG) && f.MethodId == Id_constructor) { - return true; - } - return false; - } - - protected internal override void InitPrototypeId (int id) - { - string s; - int arity; - switch (id) { - - case Id_constructor: - arity = 0; - s = "constructor"; - break; - - default: - throw new ArgumentException (Convert.ToString (id)); - - } - InitPrototypeMethod (FTAG, id, s, arity); - } - - public override object ExecIdCall (IdFunctionObject f, Context cx, IScriptable scope, IScriptable thisObj, object [] args) - { - if (!f.HasTag (FTAG)) { - return base.ExecIdCall (f, cx, scope, thisObj, args); - } - int id = f.MethodId; - switch (id) { - - case Id_constructor: - throw Context.ReportRuntimeError ("Direct call is not supported"); - } - throw new ArgumentException (Convert.ToString (id)); - } - - // #string_id_map# - - protected internal override int FindPrototypeId (string s) - { - int id; - #region Generated PrototypeId Switch - L0: { - id = 0; - string X = null; - if (s.Length == 11) { X = "constructor"; id = Id_constructor; } - if (X != null && X != s && !X.Equals (s)) - id = 0; - } - EL0: - #endregion - return id; - } - - #region PrototypeIds - private const int Id_constructor = 1; - private const int MAX_PROTOTYPE_ID = 1; - #endregion - - } -} +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; +using EcmaScript.NET; + +namespace EcmaScript.NET +{ + + + public sealed class Continuation : IdScriptableObject, IFunction + { + public object Implementation + { + get + { + return implementation; + } + + } + override public string ClassName + { + get + { + return "Continuation"; + } + + } + + + private static readonly object FTAG = new object (); + + private object implementation; + + public static void Init (IScriptable scope, bool zealed) + { + Continuation obj = new Continuation (); + obj.ExportAsJSClass (MAX_PROTOTYPE_ID, scope, zealed); + } + + public void initImplementation (object implementation) + { + this.implementation = implementation; + } + + public IScriptable Construct (Context cx, IScriptable scope, object [] args) + { + throw Context.ReportRuntimeError ("Direct call is not supported"); + } + + public object Call (Context cx, IScriptable scope, IScriptable thisObj, object [] args) + { + return Interpreter.restartContinuation (this, cx, scope, args); + } + + public static bool IsContinuationConstructor (IdFunctionObject f) + { + if (f.HasTag (FTAG) && f.MethodId == Id_constructor) { + return true; + } + return false; + } + + protected internal override void InitPrototypeId (int id) + { + string s; + int arity; + switch (id) { + + case Id_constructor: + arity = 0; + s = "constructor"; + break; + + default: + throw new ArgumentException (Convert.ToString (id)); + + } + InitPrototypeMethod (FTAG, id, s, arity); + } + + public override object ExecIdCall (IdFunctionObject f, Context cx, IScriptable scope, IScriptable thisObj, object [] args) + { + if (!f.HasTag (FTAG)) { + return base.ExecIdCall (f, cx, scope, thisObj, args); + } + int id = f.MethodId; + switch (id) { + + case Id_constructor: + throw Context.ReportRuntimeError ("Direct call is not supported"); + } + throw new ArgumentException (Convert.ToString (id)); + } + + // #string_id_map# + + protected internal override int FindPrototypeId (string s) + { + int id; + #region Generated PrototypeId Switch + L0: { + id = 0; + string X = null; + if (s.Length == 11) { X = "constructor"; id = Id_constructor; } + if (X != null && X != s && !X.Equals (s)) + id = 0; + } + EL0: + #endregion + return id; + } + + #region PrototypeIds + private const int Id_constructor = 1; + private const int MAX_PROTOTYPE_ID = 1; + #endregion + + } +} diff --git a/Code/EcmaScript.NET/Debugging/DebugFrame.cs b/src/EcmaScript.NET/Debugging/DebugFrame.cs similarity index 97% rename from Code/EcmaScript.NET/Debugging/DebugFrame.cs rename to src/EcmaScript.NET/Debugging/DebugFrame.cs index d8f263e..a6b7208 100644 --- a/Code/EcmaScript.NET/Debugging/DebugFrame.cs +++ b/src/EcmaScript.NET/Debugging/DebugFrame.cs @@ -1,68 +1,68 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -// API class -using System; -using Context = EcmaScript.NET.Context; -using Scriptable = EcmaScript.NET.IScriptable; -namespace EcmaScript.NET.Debugging -{ - - /// Interface to implement if the application is interested in receiving debug - /// information during execution of a particular script or function. - /// - public interface DebugFrame - { - - /// Called when execution is ready to start bytecode interpretation for entered a particular function or script. - /// current Context for this thread - /// - /// the activation scope for the function or script. - /// - /// value of the JavaScript this object - /// - /// the array of arguments - /// - void OnEnter (Context cx, IScriptable activation, IScriptable thisObj, object [] args); - /// Called when executed code reaches new line in the source. - /// current Context for this thread - /// - /// current line number in the script source - /// - void OnLineChange (Context cx, int lineNumber); - - /// Called when thrown exception is handled by the function or script. - /// current Context for this thread - /// - /// exception object - /// - void OnExceptionThrown (Context cx, Exception ex); - - /// Called when the function or script for this frame is about to return. - /// current Context for this thread - /// - /// if true function will leave by throwing exception, otherwise it - /// will execute normal return - /// - /// function result in case of normal return or - /// exception object if about to throw exception - /// - void OnExit (Context cx, bool byThrow, object resultOrException); - - /// - /// Called when the function or script executes a 'debugger' statement. - /// - /// current Context for this thread - void OnDebuggerStatement(Context cx); - } +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +// API class +using System; +using Context = EcmaScript.NET.Context; +using Scriptable = EcmaScript.NET.IScriptable; +namespace EcmaScript.NET.Debugging +{ + + /// Interface to implement if the application is interested in receiving debug + /// information during execution of a particular script or function. + /// + public interface DebugFrame + { + + /// Called when execution is ready to start bytecode interpretation for entered a particular function or script. + /// current Context for this thread + /// + /// the activation scope for the function or script. + /// + /// value of the JavaScript this object + /// + /// the array of arguments + /// + void OnEnter (Context cx, IScriptable activation, IScriptable thisObj, object [] args); + /// Called when executed code reaches new line in the source. + /// current Context for this thread + /// + /// current line number in the script source + /// + void OnLineChange (Context cx, int lineNumber); + + /// Called when thrown exception is handled by the function or script. + /// current Context for this thread + /// + /// exception object + /// + void OnExceptionThrown (Context cx, Exception ex); + + /// Called when the function or script for this frame is about to return. + /// current Context for this thread + /// + /// if true function will leave by throwing exception, otherwise it + /// will execute normal return + /// + /// function result in case of normal return or + /// exception object if about to throw exception + /// + void OnExit (Context cx, bool byThrow, object resultOrException); + + /// + /// Called when the function or script executes a 'debugger' statement. + /// + /// current Context for this thread + void OnDebuggerStatement(Context cx); + } } \ No newline at end of file diff --git a/Code/EcmaScript.NET/Debugging/DebuggableObject.cs b/src/EcmaScript.NET/Debugging/DebuggableObject.cs similarity index 97% rename from Code/EcmaScript.NET/Debugging/DebuggableObject.cs rename to src/EcmaScript.NET/Debugging/DebuggableObject.cs index b960823..73bacc2 100644 --- a/Code/EcmaScript.NET/Debugging/DebuggableObject.cs +++ b/src/EcmaScript.NET/Debugging/DebuggableObject.cs @@ -1,40 +1,40 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -// API class -using System; -namespace EcmaScript.NET.Debugging -{ - - /// This interface exposes debugging information from objects. - public interface DebuggableObject - { - /// Returns an array of ids for the properties of the object. - /// - ///

All properties, even those with attribute {DontEnum}, are listed. - /// This allows the debugger to display all properties of the object.

- /// - ///

- /// an array of java.lang.Objects with an entry for every - /// listed property. Properties accessed via an integer index will - /// have a corresponding - /// Integer entry in the returned array. Properties accessed by - /// a String will have a String entry in the returned array. - /// - object [] AllIds - { - get; - - } - } +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +// API class +using System; +namespace EcmaScript.NET.Debugging +{ + + /// This interface exposes debugging information from objects. + public interface DebuggableObject + { + /// Returns an array of ids for the properties of the object. + /// + ///

All properties, even those with attribute {DontEnum}, are listed. + /// This allows the debugger to display all properties of the object.

+ /// + ///

+ /// an array of java.lang.Objects with an entry for every + /// listed property. Properties accessed via an integer index will + /// have a corresponding + /// Integer entry in the returned array. Properties accessed by + /// a String will have a String entry in the returned array. + /// + object [] AllIds + { + get; + + } + } } \ No newline at end of file diff --git a/Code/EcmaScript.NET/Debugging/DebuggableScript.cs b/src/EcmaScript.NET/Debugging/DebuggableScript.cs similarity index 96% rename from Code/EcmaScript.NET/Debugging/DebuggableScript.cs rename to src/EcmaScript.NET/Debugging/DebuggableScript.cs index c192aff..c997228 100644 --- a/Code/EcmaScript.NET/Debugging/DebuggableScript.cs +++ b/src/EcmaScript.NET/Debugging/DebuggableScript.cs @@ -1,108 +1,108 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -// API class -using System; -namespace EcmaScript.NET.Debugging -{ - - /// This interface exposes debugging information from executable - /// code (either functions or top-level scripts). - /// - public interface DebuggableScript - { - bool TopLevel - { - get; - - } - /// Get name of the function described by this script. - /// Return null or an empty string if this script is not function. - /// - string FunctionName - { - get; - - } - /// Get number of declared parameters in function. - /// Return 0 if this script is not function. - /// - /// - int ParamCount - { - get; - - } - /// Get number of declared parameters and local variables. - /// Return number of declared global variables if this script is not - /// function. - /// - /// - int ParamAndVarCount - { - get; - - } - /// Get the name of the source (usually filename or URL) - /// of the script. - /// - string SourceName - { - get; - - } - /// Returns true if this script or function were runtime-generated - /// from JavaScript using eval function or Function - /// or Script constructors. - /// - bool GeneratedScript - { - get; - - } - /// Get array containing the line numbers that - /// that can be passed to DebugFrame.onLineChange(). - /// Note that line order in the resulting array is arbitrary - /// - int [] LineNumbers - { - get; - - } - int FunctionCount - { - get; - - } - DebuggableScript Parent - { - get; - - } - - /// Returns true if this is a function, false if it is a script. - bool IsFunction (); - - /// Get name of a declared parameter or local variable. - /// index should be less then {@link #getParamAndVarCount()}. - /// If index < {@link #getParamCount()}, return - /// the name of the corresponding parameter, otherwise return the name - /// of variable. - /// If this script is not function, return the name of the declared - /// global variable. - /// - string GetParamOrVarName (int index); - - DebuggableScript GetFunction (int index); - } +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +// API class +using System; +namespace EcmaScript.NET.Debugging +{ + + /// This interface exposes debugging information from executable + /// code (either functions or top-level scripts). + /// + public interface DebuggableScript + { + bool TopLevel + { + get; + + } + /// Get name of the function described by this script. + /// Return null or an empty string if this script is not function. + /// + string FunctionName + { + get; + + } + /// Get number of declared parameters in function. + /// Return 0 if this script is not function. + /// + /// + int ParamCount + { + get; + + } + /// Get number of declared parameters and local variables. + /// Return number of declared global variables if this script is not + /// function. + /// + /// + int ParamAndVarCount + { + get; + + } + /// Get the name of the source (usually filename or URL) + /// of the script. + /// + string SourceName + { + get; + + } + /// Returns true if this script or function were runtime-generated + /// from JavaScript using eval function or Function + /// or Script constructors. + /// + bool GeneratedScript + { + get; + + } + /// Get array containing the line numbers that + /// that can be passed to DebugFrame.onLineChange(). + /// Note that line order in the resulting array is arbitrary + /// + int [] LineNumbers + { + get; + + } + int FunctionCount + { + get; + + } + DebuggableScript Parent + { + get; + + } + + /// Returns true if this is a function, false if it is a script. + bool IsFunction (); + + /// Get name of a declared parameter or local variable. + /// index should be less then {@link #getParamAndVarCount()}. + /// If index < {@link #getParamCount()}, return + /// the name of the corresponding parameter, otherwise return the name + /// of variable. + /// If this script is not function, return the name of the declared + /// global variable. + /// + string GetParamOrVarName (int index); + + DebuggableScript GetFunction (int index); + } } \ No newline at end of file diff --git a/Code/EcmaScript.NET/Debugging/Debugger.cs b/src/EcmaScript.NET/Debugging/Debugger.cs similarity index 97% rename from Code/EcmaScript.NET/Debugging/Debugger.cs rename to src/EcmaScript.NET/Debugging/Debugger.cs index 970f451..06b279a 100644 --- a/Code/EcmaScript.NET/Debugging/Debugger.cs +++ b/src/EcmaScript.NET/Debugging/Debugger.cs @@ -1,43 +1,43 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -// API class -using System; -using Context = EcmaScript.NET.Context; -namespace EcmaScript.NET.Debugging -{ - - /// Interface to implement if the application is interested in receiving debug - /// information. - /// - public interface Debugger - { - - /// Called when compilation of a particular function or script into internal - /// bytecode is done. - /// - /// current Context for this thread - /// - /// object describing the function or script - /// - /// the function or script source - /// - void HandleCompilationDone (Context cx, DebuggableScript fnOrScript, string source); - - /// Called when execution entered a particular function or script. - /// implementation of DebugFrame which receives debug information during - /// the function or script execution or null otherwise - /// - DebugFrame GetFrame (Context cx, DebuggableScript fnOrScript); - } +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +// API class +using System; +using Context = EcmaScript.NET.Context; +namespace EcmaScript.NET.Debugging +{ + + /// Interface to implement if the application is interested in receiving debug + /// information. + /// + public interface Debugger + { + + /// Called when compilation of a particular function or script into internal + /// bytecode is done. + /// + /// current Context for this thread + /// + /// object describing the function or script + /// + /// the function or script source + /// + void HandleCompilationDone (Context cx, DebuggableScript fnOrScript, string source); + + /// Called when execution entered a particular function or script. + /// implementation of DebugFrame which receives debug information during + /// the function or script execution or null otherwise + /// + DebugFrame GetFrame (Context cx, DebuggableScript fnOrScript); + } } \ No newline at end of file diff --git a/Code/EcmaScript.NET/Decompiler.cs b/src/EcmaScript.NET/Decompiler.cs similarity index 96% rename from Code/EcmaScript.NET/Decompiler.cs rename to src/EcmaScript.NET/Decompiler.cs index 5eed814..77f6496 100644 --- a/Code/EcmaScript.NET/Decompiler.cs +++ b/src/EcmaScript.NET/Decompiler.cs @@ -1,1032 +1,1032 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; - -using EcmaScript.NET.Collections; - -namespace EcmaScript.NET -{ - - /// The following class save decompilation information about the source. - /// Source information is returned from the parser as a String - /// associated with function nodes and with the toplevel script. When - /// saved in the constant pool of a class, this string will be UTF-8 - /// encoded, and token values will occupy a single byte. - /// Source is saved (mostly) as token numbers. The tokens saved pretty - /// much correspond to the token stream of a 'canonical' representation - /// of the input program, as directed by the parser. (There were a few - /// cases where tokens could have been left out where decompiler could - /// easily reconstruct them, but I left them in for clarity). (I also - /// looked adding source collection to TokenStream instead, where I - /// could have limited the changes to a few lines in getToken... but - /// this wouldn't have saved any space in the resulting source - /// representation, and would have meant that I'd have to duplicate - /// parser logic in the decompiler to disambiguate situations where - /// newlines are important.) The function decompile expands the - /// tokens back into their string representations, using simple - /// lookahead to correct spacing and indentation. - /// - /// Assignments are saved as two-token pairs (Token.ASSIGN, op). Number tokens - /// are stored inline, as a NUMBER token, a character representing the type, and - /// either 1 or 4 characters representing the bit-encoding of the number. String - /// types NAME, STRING and OBJECT are currently stored as a token type, - /// followed by a character giving the length of the string (assumed to - /// be less than 2^16), followed by the characters of the string - /// inlined into the source string. Changing this to some reference to - /// to the string in the compiled class' constant pool would probably - /// save a lot of space... but would require some method of deriving - /// the final constant pool entry from information available at parse - /// time. - /// - public class Decompiler - { - internal string EncodedSource - { - get - { - return SourceToString(0); - } - - } - internal int CurrentOffset - { - get - { - return sourceTop; - } - - } - /// Flag to indicate that the decompilation should omit the - /// function header and trailing brace. - /// - public const int ONLY_BODY_FLAG = 1 << 0; - - /// Flag to indicate that the decompilation generates toSource result. - public const int TO_SOURCE_FLAG = 1 << 1; - - public const int TO_STRING_FLAG = 1 << 2; - - /// Decompilation property to specify initial ident value. - public const int INITIAL_INDENT_PROP = 1; - - /// Decompilation property to specify default identation offset. - public const int INDENT_GAP_PROP = 2; - - /// Decompilation property to specify identation offset for case labels. - public const int CASE_GAP_PROP = 3; - - // Marker to denote the last RC of function so it can be distinguished from - // the last RC of object literals in case of function expressions - private const int FUNCTION_END = 147; - - internal int MarkFunctionStart(int functionType) - { - int savedOffset = CurrentOffset; - AddToken(Token.FUNCTION); - Append((char)functionType); - return savedOffset; - } - - internal int MarkFunctionEnd(int functionStart) - { - int offset = CurrentOffset; - Append((char)FUNCTION_END); - return offset; - } - - internal void AddToken(int token) - { - if (!(0 <= token && token <= Token.LAST_TOKEN)) - throw new ArgumentException(); - - Append((char)token); - } - - internal void AddEol(int token) - { - if (!(0 <= token && token <= Token.LAST_TOKEN)) - throw new ArgumentException(); - - Append((char)token); - Append((char)Token.EOL); - } - - internal void AddName(string str) - { - AddToken(Token.NAME); - AppendString(str); - } - - internal void AddString(string str) - { - AddToken(Token.STRING); - AppendString(str); - } - - internal void AddRegexp(string regexp, string flags) - { - AddToken(Token.REGEXP); - AppendString('/' + regexp + '/' + flags); - } - - internal void AddJScriptConditionalComment(String str) - { - AddToken(Token.CONDCOMMENT); - AppendString(str); - } - - internal void AddPreservedComment(String str) - { - AddToken(Token.KEEPCOMMENT); - AppendString(str); - } - - internal void AddNumber(double n) - { - AddToken(Token.NUMBER); - - /* encode the number in the source stream. - * Save as NUMBER type (char | char char char char) - * where type is - * 'D' - double, 'S' - short, 'J' - long. - - * We need to retain float vs. integer type info to keep the - * behavior of liveconnect type-guessing the same after - * decompilation. (Liveconnect tries to present 1.0 to Java - * as a float/double) - * OPT: This is no longer true. We could compress the format. - - * This may not be the most space-efficient encoding; - * the chars created below may take up to 3 bytes in - * constant pool UTF-8 encoding, so a Double could take - * up to 12 bytes. - */ - - long lbits = (long)n; - if (lbits != n) - { - // if it's floating point, save as a Double bit pattern. - // (12/15/97 our scanner only returns Double for f.p.) - lbits = BitConverter.DoubleToInt64Bits(n); - Append('D'); - Append((char)(lbits >> 48)); - Append((char)(lbits >> 32)); - Append((char)(lbits >> 16)); - Append((char)lbits); - } - else - { - // we can ignore negative values, bc they're already prefixed - // by NEG - if (lbits < 0) - Context.CodeBug(); - - // will it fit in a char? - // this gives a short encoding for integer values up to 2^16. - if (lbits <= char.MaxValue) - { - Append('S'); - Append((char)lbits); - } - else - { - // Integral, but won't fit in a char. Store as a long. - Append('J'); - Append((char)(lbits >> 48)); - Append((char)(lbits >> 32)); - Append((char)(lbits >> 16)); - Append((char)lbits); - } - } - } - - private void AppendString(string str) - { - int L = str.Length; - int lengthEncodingSize = 1; - if (L >= 0x8000) - { - lengthEncodingSize = 2; - } - int nextTop = sourceTop + lengthEncodingSize + L; - if (nextTop > sourceBuffer.Length) - { - IncreaseSourceCapacity(nextTop); - } - if (L >= 0x8000) - { - // Use 2 chars to encode strings exceeding 32K, were the highest - // bit in the first char indicates presence of the next byte - sourceBuffer[sourceTop] = (char)(0x8000 | (int)((uint)L >> 16)); - ++sourceTop; - } - sourceBuffer[sourceTop] = (char)L; - ++sourceTop; - str.ToCharArray(0, L).CopyTo(sourceBuffer, sourceTop); - sourceTop = nextTop; - } - - private void Append(char c) - { - if (sourceTop == sourceBuffer.Length) - { - IncreaseSourceCapacity(sourceTop + 1); - } - sourceBuffer[sourceTop] = c; - ++sourceTop; - } - - private void IncreaseSourceCapacity(int minimalCapacity) - { - // Call this only when capacity increase is must - if (minimalCapacity <= sourceBuffer.Length) - Context.CodeBug(); - int newCapacity = sourceBuffer.Length * 2; - if (newCapacity < minimalCapacity) - { - newCapacity = minimalCapacity; - } - char[] tmp = new char[newCapacity]; - Array.Copy(sourceBuffer, 0, tmp, 0, sourceTop); - sourceBuffer = tmp; - } - - private string SourceToString(int offset) - { - if (offset < 0 || sourceTop < offset) - Context.CodeBug(); - return new string(sourceBuffer, offset, sourceTop - offset); - } - - /// Decompile the source information associated with this js - /// function/script back into a string. For the most part, this - /// just means translating tokens back to their string - /// representations; there's a little bit of lookahead logic to - /// decide the proper spacing/indentation. Most of the work in - /// mapping the original source to the prettyprinted decompiled - /// version is done by the parser. - /// - /// - /// encoded source tree presentation - /// - /// - /// flags to select output format - /// - /// - /// indentation properties - /// - /// - public static string Decompile(string source, int flags, UintMap properties) - { - int length = source.Length; - if (length == 0) - { - return ""; - } - - int indent = properties.getInt(INITIAL_INDENT_PROP, 0); - if (indent < 0) - throw new ArgumentException(); - int indentGap = properties.getInt(INDENT_GAP_PROP, 4); - if (indentGap < 0) - throw new ArgumentException(); - int caseGap = properties.getInt(CASE_GAP_PROP, 2); - if (caseGap < 0) - throw new ArgumentException(); - - System.Text.StringBuilder result = new System.Text.StringBuilder(); - bool justFunctionBody = (0 != (flags & Decompiler.ONLY_BODY_FLAG)); - bool toSource = (0 != (flags & Decompiler.TO_SOURCE_FLAG)); - bool toString = (0 != (flags & Decompiler.TO_STRING_FLAG)); - - // Spew tokens in source, for debugging. - // as TYPE number char - if (printSource) - { - System.Console.Error.WriteLine("length:" + length); - for (int i = 0; i < length; ++i) - { - // Note that tokenToName will fail unless Context.printTrees - // is true. - string tokenname = null; - if (Token.printNames) - { - tokenname = Token.name(source[i]); - } - if (tokenname == null) - { - tokenname = "---"; - } - string pad = tokenname.Length > 7 ? "\t" : "\t\t"; - System.Console.Error.WriteLine(tokenname + pad + (int)source[i] + "\t'" + ScriptRuntime.escapeString(source.Substring(i, (i + 1) - (i))) + "'"); - } - System.Console.Error.WriteLine(); - } - - int braceNesting = 0; - bool afterFirstEOL = false; - int i2 = 0; - int topFunctionType; - if (source[i2] == Token.SCRIPT) - { - ++i2; - topFunctionType = -1; - } - else - { - topFunctionType = source[i2 + 1]; - } - - if (!toSource) - { - if (!toString) - { - // add an initial newline to exactly match js. - result.Append('\n'); - } - for (int j = 0; j < indent; j++) - result.Append(' '); - } - else - { - if (topFunctionType == FunctionNode.FUNCTION_EXPRESSION) - { - result.Append('('); - } - } - - while (i2 < length) - { - switch (source[i2]) - { - - case (char)(Token.NAME): - case (char)(Token.REGEXP): // re-wrapped in '/'s in parser... - i2 = PrintSourceString(source, i2 + 1, false, result); - continue; - - - case (char)(Token.STRING): - i2 = PrintSourceString(source, i2 + 1, true, result); - continue; - - - case (char)(Token.NUMBER): - i2 = PrintSourceNumber(source, i2 + 1, result); - continue; - - - case (char)(Token.TRUE): - result.Append("true"); - break; - - - case (char)(Token.FALSE): - result.Append("false"); - break; - - - case (char)(Token.NULL): - result.Append("null"); - break; - - - case (char)(Token.THIS): - result.Append("this"); - break; - - - case (char)(Token.FUNCTION): - ++i2; // skip function type - result.Append("function "); - break; - - - case (char)(FUNCTION_END): - // Do nothing - break; - - - case (char)(Token.COMMA): - result.Append(", "); - break; - - - case (char)(Token.LC): - ++braceNesting; - if (Token.EOL == GetNext(source, length, i2)) - indent += indentGap; - result.Append('{'); - break; - - - case (char)(Token.RC): - { - --braceNesting; - /* don't print the closing RC if it closes the - * toplevel function and we're called from - * decompileFunctionBody. - */ - if (justFunctionBody && braceNesting == 0) - break; - - result.Append('}'); - switch (GetNext(source, length, i2)) - { - - case Token.EOL: - case FUNCTION_END: - indent -= indentGap; - break; - - case Token.WHILE: - case Token.ELSE: - indent -= indentGap; - result.Append(' '); - break; - } - break; - } - - case (char)(Token.LP): - result.Append('('); - break; - - - case (char)(Token.RP): - result.Append(')'); - if (Token.LC == GetNext(source, length, i2)) - result.Append(' '); - break; - - - case (char)(Token.LB): - result.Append('['); - break; - - - case (char)(Token.RB): - result.Append(']'); - break; - - - case (char)(Token.EOL): - { - if (toSource) - break; - bool newLine = true; - if (!afterFirstEOL) - { - afterFirstEOL = true; - if (justFunctionBody) - { - /* throw away just added 'function name(...) {' - * and restore the original indent - */ - result.Length = 0; - indent -= indentGap; - newLine = false; - } - } - if (newLine) - { - result.Append('\n'); - } - - /* add indent if any tokens remain, - * less setback if next token is - * a label, case or default. - */ - if (i2 + 1 < length) - { - int less = 0; - int nextToken = source[i2 + 1]; - if (nextToken == Token.CASE || nextToken == Token.DEFAULT) - { - less = indentGap - caseGap; - } - else if (nextToken == Token.RC) - { - less = indentGap; - } - /* elaborate check against label... skip past a - * following inlined NAME and look for a COLON. - */ - else if (nextToken == Token.NAME) - { - int afterName = GetSourceStringEnd(source, i2 + 2); - if (source[afterName] == Token.COLON) - less = indentGap; - } - - for (; less < indent; less++) - result.Append(' '); - } - break; - } - - case (char)(Token.DOT): - result.Append('.'); - break; - - - case (char)(Token.NEW): - result.Append("new "); - break; - - - case (char)(Token.DELPROP): - result.Append("delete "); - break; - - - case (char)(Token.IF): - result.Append("if "); - break; - - - case (char)(Token.ELSE): - result.Append("else "); - break; - - - case (char)(Token.FOR): - result.Append("for "); - break; - - - case (char)(Token.IN): - result.Append(" in "); - break; - - - case (char)(Token.WITH): - result.Append("with "); - break; - - - case (char)(Token.WHILE): - result.Append("while "); - break; - - - case (char)(Token.DO): - result.Append("do "); - break; - - - case (char)(Token.TRY): - result.Append("try "); - break; - - - case (char)(Token.CATCH): - result.Append("catch "); - break; - - - case (char)(Token.FINALLY): - result.Append("finally "); - break; - - - case (char)(Token.THROW): - result.Append("throw "); - break; - - - case (char)(Token.SWITCH): - result.Append("switch "); - break; - - - case (char)(Token.BREAK): - result.Append("break"); - if (Token.NAME == GetNext(source, length, i2)) - result.Append(' '); - break; - - - case (char)(Token.CONTINUE): - result.Append("continue"); - if (Token.NAME == GetNext(source, length, i2)) - result.Append(' '); - break; - - - case (char)(Token.CASE): - result.Append("case "); - break; - - - case (char)(Token.DEFAULT): - result.Append("default"); - break; - - - case (char)(Token.RETURN): - result.Append("return"); - if (Token.SEMI != GetNext(source, length, i2)) - result.Append(' '); - break; - - - case (char)(Token.VAR): - result.Append("var "); - break; - - - case (char)(Token.SEMI): - result.Append(';'); - if (Token.EOL != GetNext(source, length, i2)) - { - // separators in FOR - result.Append(' '); - } - break; - - - case (char)(Token.ASSIGN): - result.Append(" = "); - break; - - - case (char)(Token.ASSIGN_ADD): - result.Append(" += "); - break; - - - case (char)(Token.ASSIGN_SUB): - result.Append(" -= "); - break; - - - case (char)(Token.ASSIGN_MUL): - result.Append(" *= "); - break; - - - case (char)(Token.ASSIGN_DIV): - result.Append(" /= "); - break; - - - case (char)(Token.ASSIGN_MOD): - result.Append(" %= "); - break; - - - case (char)(Token.ASSIGN_BITOR): - result.Append(" |= "); - break; - - - case (char)(Token.ASSIGN_BITXOR): - result.Append(" ^= "); - break; - - - case (char)(Token.ASSIGN_BITAND): - result.Append(" &= "); - break; - - - case (char)(Token.ASSIGN_LSH): - result.Append(" <<= "); - break; - - - case (char)(Token.ASSIGN_RSH): - result.Append(" >>= "); - break; - - - case (char)(Token.ASSIGN_URSH): - result.Append(" >>>= "); - break; - - - case (char)(Token.HOOK): - result.Append(" ? "); - break; - - - case (char)(Token.OBJECTLIT): - // pun OBJECTLIT to mean colon in objlit property - // initialization. - // This needs to be distinct from COLON in the general case - // to distinguish from the colon in a ternary... which needs - // different spacing. - result.Append(':'); - break; - - - case (char)(Token.COLON): - if (Token.EOL == GetNext(source, length, i2)) - // it's the end of a label - result.Append(':'); - // it's the middle part of a ternary - else - result.Append(" : "); - break; - - - case (char)(Token.OR): - result.Append(" || "); - break; - - - case (char)(Token.AND): - result.Append(" && "); - break; - - - case (char)(Token.BITOR): - result.Append(" | "); - break; - - - case (char)(Token.BITXOR): - result.Append(" ^ "); - break; - - - case (char)(Token.BITAND): - result.Append(" & "); - break; - - - case (char)(Token.SHEQ): - result.Append(" === "); - break; - - - case (char)(Token.SHNE): - result.Append(" !== "); - break; - - - case (char)(Token.EQ): - result.Append(" == "); - break; - - - case (char)(Token.NE): - result.Append(" != "); - break; - - - case (char)(Token.LE): - result.Append(" <= "); - break; - - - case (char)(Token.LT): - result.Append(" < "); - break; - - - case (char)(Token.GE): - result.Append(" >= "); - break; - - - case (char)(Token.GT): - result.Append(" > "); - break; - - - case (char)(Token.INSTANCEOF): - result.Append(" instanceof "); - break; - - - case (char)(Token.LSH): - result.Append(" << "); - break; - - - case (char)(Token.RSH): - result.Append(" >> "); - break; - - - case (char)(Token.URSH): - result.Append(" >>> "); - break; - - - case (char)(Token.TYPEOF): - result.Append("typeof "); - break; - - - case (char)(Token.VOID): - result.Append("void "); - break; - - - case (char)(Token.NOT): - result.Append('!'); - break; - - - case (char)(Token.BITNOT): - result.Append('~'); - break; - - - case (char)(Token.POS): - result.Append('+'); - break; - - - case (char)(Token.NEG): - result.Append('-'); - break; - - - case (char)(Token.INC): - result.Append("++"); - break; - - - case (char)(Token.DEC): - result.Append("--"); - break; - - - case (char)(Token.ADD): - result.Append(" + "); - break; - - - case (char)(Token.SUB): - result.Append(" - "); - break; - - - case (char)(Token.MUL): - result.Append(" * "); - break; - - - case (char)(Token.DIV): - result.Append(" / "); - break; - - - case (char)(Token.MOD): - result.Append(" % "); - break; - - - case (char)(Token.COLONCOLON): - result.Append("::"); - break; - - - case (char)(Token.DOTDOT): - result.Append(".."); - break; - - - case (char)(Token.DOTQUERY): - result.Append(".("); - break; - - - case (char)(Token.XMLATTR): - result.Append('@'); - break; - - - default: - // If we don't know how to decompile it, raise an exception. - throw new ApplicationException(); - - } - ++i2; - } - - if (!toSource) - { - // add that trailing newline if it's an outermost function. - if (!justFunctionBody && !toString) - result.Append('\n'); - } - else - { - if (topFunctionType == FunctionNode.FUNCTION_EXPRESSION) - { - result.Append(')'); - } - } - - return result.ToString(); - } - - private static int GetNext(string source, int length, int i) - { - return (i + 1 < length) ? source[i + 1] : Token.EOF; - } - - private static int GetSourceStringEnd(string source, int offset) - { - return PrintSourceString(source, offset, false, null); - } - - private static int PrintSourceString(string source, int offset, bool asQuotedString, System.Text.StringBuilder sb) - { - int length = source[offset]; - ++offset; - if ((0x8000 & length) != 0) - { - length = ((0x7FFF & length) << 16) | source[offset]; - ++offset; - } - if (sb != null) - { - string str = source.Substring(offset, (offset + length) - (offset)); - if (!asQuotedString) - { - sb.Append(str); - } - else - { - sb.Append('"'); - sb.Append(ScriptRuntime.escapeString(str)); - sb.Append('"'); - } - } - return offset + length; - } - - private static int PrintSourceNumber(string source, int offset, System.Text.StringBuilder sb) - { - double number = 0.0; - char type = source[offset]; - ++offset; - if (type == 'S') - { - if (sb != null) - { - int ival = source[offset]; - number = ival; - } - ++offset; - } - else if (type == 'J' || type == 'D') - { - if (sb != null) - { - long lbits; - lbits = (long)source[offset] << 48; - lbits |= (long)source[offset + 1] << 32; - lbits |= (long)source[offset + 2] << 16; - lbits |= (long)source[offset + 3]; - if (type == 'J') - { - number = lbits; - } - else - { - number = BitConverter.Int64BitsToDouble(lbits); - } - } - offset += 4; - } - else - { - // Bad source - throw new ApplicationException(); - } - if (sb != null) - { - sb.Append(ScriptConvert.ToString(number, 10)); - } - return offset; - } - - private char[] sourceBuffer = new char[128]; - - // Per script/function source buffer top: parent source does not include a - // nested functions source and uses function index as a reference instead. - private int sourceTop; - - // whether to do a debug print of the source information, when decompiling. - private static bool printSource = false; // TODO: make preprocessor directive - - } +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; + +using EcmaScript.NET.Collections; + +namespace EcmaScript.NET +{ + + /// The following class save decompilation information about the source. + /// Source information is returned from the parser as a String + /// associated with function nodes and with the toplevel script. When + /// saved in the constant pool of a class, this string will be UTF-8 + /// encoded, and token values will occupy a single byte. + /// Source is saved (mostly) as token numbers. The tokens saved pretty + /// much correspond to the token stream of a 'canonical' representation + /// of the input program, as directed by the parser. (There were a few + /// cases where tokens could have been left out where decompiler could + /// easily reconstruct them, but I left them in for clarity). (I also + /// looked adding source collection to TokenStream instead, where I + /// could have limited the changes to a few lines in getToken... but + /// this wouldn't have saved any space in the resulting source + /// representation, and would have meant that I'd have to duplicate + /// parser logic in the decompiler to disambiguate situations where + /// newlines are important.) The function decompile expands the + /// tokens back into their string representations, using simple + /// lookahead to correct spacing and indentation. + /// + /// Assignments are saved as two-token pairs (Token.ASSIGN, op). Number tokens + /// are stored inline, as a NUMBER token, a character representing the type, and + /// either 1 or 4 characters representing the bit-encoding of the number. String + /// types NAME, STRING and OBJECT are currently stored as a token type, + /// followed by a character giving the length of the string (assumed to + /// be less than 2^16), followed by the characters of the string + /// inlined into the source string. Changing this to some reference to + /// to the string in the compiled class' constant pool would probably + /// save a lot of space... but would require some method of deriving + /// the final constant pool entry from information available at parse + /// time. + /// + public class Decompiler + { + internal string EncodedSource + { + get + { + return SourceToString(0); + } + + } + internal int CurrentOffset + { + get + { + return sourceTop; + } + + } + /// Flag to indicate that the decompilation should omit the + /// function header and trailing brace. + /// + public const int ONLY_BODY_FLAG = 1 << 0; + + /// Flag to indicate that the decompilation generates toSource result. + public const int TO_SOURCE_FLAG = 1 << 1; + + public const int TO_STRING_FLAG = 1 << 2; + + /// Decompilation property to specify initial ident value. + public const int INITIAL_INDENT_PROP = 1; + + /// Decompilation property to specify default identation offset. + public const int INDENT_GAP_PROP = 2; + + /// Decompilation property to specify identation offset for case labels. + public const int CASE_GAP_PROP = 3; + + // Marker to denote the last RC of function so it can be distinguished from + // the last RC of object literals in case of function expressions + private const int FUNCTION_END = 147; + + internal int MarkFunctionStart(int functionType) + { + int savedOffset = CurrentOffset; + AddToken(Token.FUNCTION); + Append((char)functionType); + return savedOffset; + } + + internal int MarkFunctionEnd(int functionStart) + { + int offset = CurrentOffset; + Append((char)FUNCTION_END); + return offset; + } + + internal void AddToken(int token) + { + if (!(0 <= token && token <= Token.LAST_TOKEN)) + throw new ArgumentException(); + + Append((char)token); + } + + internal void AddEol(int token) + { + if (!(0 <= token && token <= Token.LAST_TOKEN)) + throw new ArgumentException(); + + Append((char)token); + Append((char)Token.EOL); + } + + internal void AddName(string str) + { + AddToken(Token.NAME); + AppendString(str); + } + + internal void AddString(string str) + { + AddToken(Token.STRING); + AppendString(str); + } + + internal void AddRegexp(string regexp, string flags) + { + AddToken(Token.REGEXP); + AppendString('/' + regexp + '/' + flags); + } + + internal void AddJScriptConditionalComment(String str) + { + AddToken(Token.CONDCOMMENT); + AppendString(str); + } + + internal void AddPreservedComment(String str) + { + AddToken(Token.KEEPCOMMENT); + AppendString(str); + } + + internal void AddNumber(double n) + { + AddToken(Token.NUMBER); + + /* encode the number in the source stream. + * Save as NUMBER type (char | char char char char) + * where type is + * 'D' - double, 'S' - short, 'J' - long. + + * We need to retain float vs. integer type info to keep the + * behavior of liveconnect type-guessing the same after + * decompilation. (Liveconnect tries to present 1.0 to Java + * as a float/double) + * OPT: This is no longer true. We could compress the format. + + * This may not be the most space-efficient encoding; + * the chars created below may take up to 3 bytes in + * constant pool UTF-8 encoding, so a Double could take + * up to 12 bytes. + */ + + long lbits = (long)n; + if (lbits != n) + { + // if it's floating point, save as a Double bit pattern. + // (12/15/97 our scanner only returns Double for f.p.) + lbits = BitConverter.DoubleToInt64Bits(n); + Append('D'); + Append((char)(lbits >> 48)); + Append((char)(lbits >> 32)); + Append((char)(lbits >> 16)); + Append((char)lbits); + } + else + { + // we can ignore negative values, bc they're already prefixed + // by NEG + if (lbits < 0) + Context.CodeBug(); + + // will it fit in a char? + // this gives a short encoding for integer values up to 2^16. + if (lbits <= char.MaxValue) + { + Append('S'); + Append((char)lbits); + } + else + { + // Integral, but won't fit in a char. Store as a long. + Append('J'); + Append((char)(lbits >> 48)); + Append((char)(lbits >> 32)); + Append((char)(lbits >> 16)); + Append((char)lbits); + } + } + } + + private void AppendString(string str) + { + int L = str.Length; + int lengthEncodingSize = 1; + if (L >= 0x8000) + { + lengthEncodingSize = 2; + } + int nextTop = sourceTop + lengthEncodingSize + L; + if (nextTop > sourceBuffer.Length) + { + IncreaseSourceCapacity(nextTop); + } + if (L >= 0x8000) + { + // Use 2 chars to encode strings exceeding 32K, were the highest + // bit in the first char indicates presence of the next byte + sourceBuffer[sourceTop] = (char)(0x8000 | (int)((uint)L >> 16)); + ++sourceTop; + } + sourceBuffer[sourceTop] = (char)L; + ++sourceTop; + str.ToCharArray(0, L).CopyTo(sourceBuffer, sourceTop); + sourceTop = nextTop; + } + + private void Append(char c) + { + if (sourceTop == sourceBuffer.Length) + { + IncreaseSourceCapacity(sourceTop + 1); + } + sourceBuffer[sourceTop] = c; + ++sourceTop; + } + + private void IncreaseSourceCapacity(int minimalCapacity) + { + // Call this only when capacity increase is must + if (minimalCapacity <= sourceBuffer.Length) + Context.CodeBug(); + int newCapacity = sourceBuffer.Length * 2; + if (newCapacity < minimalCapacity) + { + newCapacity = minimalCapacity; + } + char[] tmp = new char[newCapacity]; + Array.Copy(sourceBuffer, 0, tmp, 0, sourceTop); + sourceBuffer = tmp; + } + + private string SourceToString(int offset) + { + if (offset < 0 || sourceTop < offset) + Context.CodeBug(); + return new string(sourceBuffer, offset, sourceTop - offset); + } + + /// Decompile the source information associated with this js + /// function/script back into a string. For the most part, this + /// just means translating tokens back to their string + /// representations; there's a little bit of lookahead logic to + /// decide the proper spacing/indentation. Most of the work in + /// mapping the original source to the prettyprinted decompiled + /// version is done by the parser. + /// + /// + /// encoded source tree presentation + /// + /// + /// flags to select output format + /// + /// + /// indentation properties + /// + /// + public static string Decompile(string source, int flags, UintMap properties) + { + int length = source.Length; + if (length == 0) + { + return ""; + } + + int indent = properties.getInt(INITIAL_INDENT_PROP, 0); + if (indent < 0) + throw new ArgumentException(); + int indentGap = properties.getInt(INDENT_GAP_PROP, 4); + if (indentGap < 0) + throw new ArgumentException(); + int caseGap = properties.getInt(CASE_GAP_PROP, 2); + if (caseGap < 0) + throw new ArgumentException(); + + System.Text.StringBuilder result = new System.Text.StringBuilder(); + bool justFunctionBody = (0 != (flags & Decompiler.ONLY_BODY_FLAG)); + bool toSource = (0 != (flags & Decompiler.TO_SOURCE_FLAG)); + bool toString = (0 != (flags & Decompiler.TO_STRING_FLAG)); + + // Spew tokens in source, for debugging. + // as TYPE number char + if (printSource) + { + System.Console.Error.WriteLine("length:" + length); + for (int i = 0; i < length; ++i) + { + // Note that tokenToName will fail unless Context.printTrees + // is true. + string tokenname = null; + if (Token.printNames) + { + tokenname = Token.name(source[i]); + } + if (tokenname == null) + { + tokenname = "---"; + } + string pad = tokenname.Length > 7 ? "\t" : "\t\t"; + System.Console.Error.WriteLine(tokenname + pad + (int)source[i] + "\t'" + ScriptRuntime.escapeString(source.Substring(i, (i + 1) - (i))) + "'"); + } + System.Console.Error.WriteLine(); + } + + int braceNesting = 0; + bool afterFirstEOL = false; + int i2 = 0; + int topFunctionType; + if (source[i2] == Token.SCRIPT) + { + ++i2; + topFunctionType = -1; + } + else + { + topFunctionType = source[i2 + 1]; + } + + if (!toSource) + { + if (!toString) + { + // add an initial newline to exactly match js. + result.Append('\n'); + } + for (int j = 0; j < indent; j++) + result.Append(' '); + } + else + { + if (topFunctionType == FunctionNode.FUNCTION_EXPRESSION) + { + result.Append('('); + } + } + + while (i2 < length) + { + switch (source[i2]) + { + + case (char)(Token.NAME): + case (char)(Token.REGEXP): // re-wrapped in '/'s in parser... + i2 = PrintSourceString(source, i2 + 1, false, result); + continue; + + + case (char)(Token.STRING): + i2 = PrintSourceString(source, i2 + 1, true, result); + continue; + + + case (char)(Token.NUMBER): + i2 = PrintSourceNumber(source, i2 + 1, result); + continue; + + + case (char)(Token.TRUE): + result.Append("true"); + break; + + + case (char)(Token.FALSE): + result.Append("false"); + break; + + + case (char)(Token.NULL): + result.Append("null"); + break; + + + case (char)(Token.THIS): + result.Append("this"); + break; + + + case (char)(Token.FUNCTION): + ++i2; // skip function type + result.Append("function "); + break; + + + case (char)(FUNCTION_END): + // Do nothing + break; + + + case (char)(Token.COMMA): + result.Append(", "); + break; + + + case (char)(Token.LC): + ++braceNesting; + if (Token.EOL == GetNext(source, length, i2)) + indent += indentGap; + result.Append('{'); + break; + + + case (char)(Token.RC): + { + --braceNesting; + /* don't print the closing RC if it closes the + * toplevel function and we're called from + * decompileFunctionBody. + */ + if (justFunctionBody && braceNesting == 0) + break; + + result.Append('}'); + switch (GetNext(source, length, i2)) + { + + case Token.EOL: + case FUNCTION_END: + indent -= indentGap; + break; + + case Token.WHILE: + case Token.ELSE: + indent -= indentGap; + result.Append(' '); + break; + } + break; + } + + case (char)(Token.LP): + result.Append('('); + break; + + + case (char)(Token.RP): + result.Append(')'); + if (Token.LC == GetNext(source, length, i2)) + result.Append(' '); + break; + + + case (char)(Token.LB): + result.Append('['); + break; + + + case (char)(Token.RB): + result.Append(']'); + break; + + + case (char)(Token.EOL): + { + if (toSource) + break; + bool newLine = true; + if (!afterFirstEOL) + { + afterFirstEOL = true; + if (justFunctionBody) + { + /* throw away just added 'function name(...) {' + * and restore the original indent + */ + result.Length = 0; + indent -= indentGap; + newLine = false; + } + } + if (newLine) + { + result.Append('\n'); + } + + /* add indent if any tokens remain, + * less setback if next token is + * a label, case or default. + */ + if (i2 + 1 < length) + { + int less = 0; + int nextToken = source[i2 + 1]; + if (nextToken == Token.CASE || nextToken == Token.DEFAULT) + { + less = indentGap - caseGap; + } + else if (nextToken == Token.RC) + { + less = indentGap; + } + /* elaborate check against label... skip past a + * following inlined NAME and look for a COLON. + */ + else if (nextToken == Token.NAME) + { + int afterName = GetSourceStringEnd(source, i2 + 2); + if (source[afterName] == Token.COLON) + less = indentGap; + } + + for (; less < indent; less++) + result.Append(' '); + } + break; + } + + case (char)(Token.DOT): + result.Append('.'); + break; + + + case (char)(Token.NEW): + result.Append("new "); + break; + + + case (char)(Token.DELPROP): + result.Append("delete "); + break; + + + case (char)(Token.IF): + result.Append("if "); + break; + + + case (char)(Token.ELSE): + result.Append("else "); + break; + + + case (char)(Token.FOR): + result.Append("for "); + break; + + + case (char)(Token.IN): + result.Append(" in "); + break; + + + case (char)(Token.WITH): + result.Append("with "); + break; + + + case (char)(Token.WHILE): + result.Append("while "); + break; + + + case (char)(Token.DO): + result.Append("do "); + break; + + + case (char)(Token.TRY): + result.Append("try "); + break; + + + case (char)(Token.CATCH): + result.Append("catch "); + break; + + + case (char)(Token.FINALLY): + result.Append("finally "); + break; + + + case (char)(Token.THROW): + result.Append("throw "); + break; + + + case (char)(Token.SWITCH): + result.Append("switch "); + break; + + + case (char)(Token.BREAK): + result.Append("break"); + if (Token.NAME == GetNext(source, length, i2)) + result.Append(' '); + break; + + + case (char)(Token.CONTINUE): + result.Append("continue"); + if (Token.NAME == GetNext(source, length, i2)) + result.Append(' '); + break; + + + case (char)(Token.CASE): + result.Append("case "); + break; + + + case (char)(Token.DEFAULT): + result.Append("default"); + break; + + + case (char)(Token.RETURN): + result.Append("return"); + if (Token.SEMI != GetNext(source, length, i2)) + result.Append(' '); + break; + + + case (char)(Token.VAR): + result.Append("var "); + break; + + + case (char)(Token.SEMI): + result.Append(';'); + if (Token.EOL != GetNext(source, length, i2)) + { + // separators in FOR + result.Append(' '); + } + break; + + + case (char)(Token.ASSIGN): + result.Append(" = "); + break; + + + case (char)(Token.ASSIGN_ADD): + result.Append(" += "); + break; + + + case (char)(Token.ASSIGN_SUB): + result.Append(" -= "); + break; + + + case (char)(Token.ASSIGN_MUL): + result.Append(" *= "); + break; + + + case (char)(Token.ASSIGN_DIV): + result.Append(" /= "); + break; + + + case (char)(Token.ASSIGN_MOD): + result.Append(" %= "); + break; + + + case (char)(Token.ASSIGN_BITOR): + result.Append(" |= "); + break; + + + case (char)(Token.ASSIGN_BITXOR): + result.Append(" ^= "); + break; + + + case (char)(Token.ASSIGN_BITAND): + result.Append(" &= "); + break; + + + case (char)(Token.ASSIGN_LSH): + result.Append(" <<= "); + break; + + + case (char)(Token.ASSIGN_RSH): + result.Append(" >>= "); + break; + + + case (char)(Token.ASSIGN_URSH): + result.Append(" >>>= "); + break; + + + case (char)(Token.HOOK): + result.Append(" ? "); + break; + + + case (char)(Token.OBJECTLIT): + // pun OBJECTLIT to mean colon in objlit property + // initialization. + // This needs to be distinct from COLON in the general case + // to distinguish from the colon in a ternary... which needs + // different spacing. + result.Append(':'); + break; + + + case (char)(Token.COLON): + if (Token.EOL == GetNext(source, length, i2)) + // it's the end of a label + result.Append(':'); + // it's the middle part of a ternary + else + result.Append(" : "); + break; + + + case (char)(Token.OR): + result.Append(" || "); + break; + + + case (char)(Token.AND): + result.Append(" && "); + break; + + + case (char)(Token.BITOR): + result.Append(" | "); + break; + + + case (char)(Token.BITXOR): + result.Append(" ^ "); + break; + + + case (char)(Token.BITAND): + result.Append(" & "); + break; + + + case (char)(Token.SHEQ): + result.Append(" === "); + break; + + + case (char)(Token.SHNE): + result.Append(" !== "); + break; + + + case (char)(Token.EQ): + result.Append(" == "); + break; + + + case (char)(Token.NE): + result.Append(" != "); + break; + + + case (char)(Token.LE): + result.Append(" <= "); + break; + + + case (char)(Token.LT): + result.Append(" < "); + break; + + + case (char)(Token.GE): + result.Append(" >= "); + break; + + + case (char)(Token.GT): + result.Append(" > "); + break; + + + case (char)(Token.INSTANCEOF): + result.Append(" instanceof "); + break; + + + case (char)(Token.LSH): + result.Append(" << "); + break; + + + case (char)(Token.RSH): + result.Append(" >> "); + break; + + + case (char)(Token.URSH): + result.Append(" >>> "); + break; + + + case (char)(Token.TYPEOF): + result.Append("typeof "); + break; + + + case (char)(Token.VOID): + result.Append("void "); + break; + + + case (char)(Token.NOT): + result.Append('!'); + break; + + + case (char)(Token.BITNOT): + result.Append('~'); + break; + + + case (char)(Token.POS): + result.Append('+'); + break; + + + case (char)(Token.NEG): + result.Append('-'); + break; + + + case (char)(Token.INC): + result.Append("++"); + break; + + + case (char)(Token.DEC): + result.Append("--"); + break; + + + case (char)(Token.ADD): + result.Append(" + "); + break; + + + case (char)(Token.SUB): + result.Append(" - "); + break; + + + case (char)(Token.MUL): + result.Append(" * "); + break; + + + case (char)(Token.DIV): + result.Append(" / "); + break; + + + case (char)(Token.MOD): + result.Append(" % "); + break; + + + case (char)(Token.COLONCOLON): + result.Append("::"); + break; + + + case (char)(Token.DOTDOT): + result.Append(".."); + break; + + + case (char)(Token.DOTQUERY): + result.Append(".("); + break; + + + case (char)(Token.XMLATTR): + result.Append('@'); + break; + + + default: + // If we don't know how to decompile it, raise an exception. + throw new Exception(); + + } + ++i2; + } + + if (!toSource) + { + // add that trailing newline if it's an outermost function. + if (!justFunctionBody && !toString) + result.Append('\n'); + } + else + { + if (topFunctionType == FunctionNode.FUNCTION_EXPRESSION) + { + result.Append(')'); + } + } + + return result.ToString(); + } + + private static int GetNext(string source, int length, int i) + { + return (i + 1 < length) ? source[i + 1] : Token.EOF; + } + + private static int GetSourceStringEnd(string source, int offset) + { + return PrintSourceString(source, offset, false, null); + } + + private static int PrintSourceString(string source, int offset, bool asQuotedString, System.Text.StringBuilder sb) + { + int length = source[offset]; + ++offset; + if ((0x8000 & length) != 0) + { + length = ((0x7FFF & length) << 16) | source[offset]; + ++offset; + } + if (sb != null) + { + string str = source.Substring(offset, (offset + length) - (offset)); + if (!asQuotedString) + { + sb.Append(str); + } + else + { + sb.Append('"'); + sb.Append(ScriptRuntime.escapeString(str)); + sb.Append('"'); + } + } + return offset + length; + } + + private static int PrintSourceNumber(string source, int offset, System.Text.StringBuilder sb) + { + double number = 0.0; + char type = source[offset]; + ++offset; + if (type == 'S') + { + if (sb != null) + { + int ival = source[offset]; + number = ival; + } + ++offset; + } + else if (type == 'J' || type == 'D') + { + if (sb != null) + { + long lbits; + lbits = (long)source[offset] << 48; + lbits |= (long)source[offset + 1] << 32; + lbits |= (long)source[offset + 2] << 16; + lbits |= (long)source[offset + 3]; + if (type == 'J') + { + number = lbits; + } + else + { + number = BitConverter.Int64BitsToDouble(lbits); + } + } + offset += 4; + } + else + { + // Bad source + throw new Exception(); + } + if (sb != null) + { + sb.Append(ScriptConvert.ToString(number, 10)); + } + return offset; + } + + private char[] sourceBuffer = new char[128]; + + // Per script/function source buffer top: parent source does not include a + // nested functions source and uses function index as a reference instead. + private int sourceTop; + + // whether to do a debug print of the source information, when decompiling. + private static bool printSource = false; // TODO: make preprocessor directive + + } } \ No newline at end of file diff --git a/Code/EcmaScript.NET/DefaultErrorReporter.cs b/src/EcmaScript.NET/DefaultErrorReporter.cs similarity index 97% rename from Code/EcmaScript.NET/DefaultErrorReporter.cs rename to src/EcmaScript.NET/DefaultErrorReporter.cs index 6e5fbc3..db2979a 100644 --- a/Code/EcmaScript.NET/DefaultErrorReporter.cs +++ b/src/EcmaScript.NET/DefaultErrorReporter.cs @@ -1,74 +1,74 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; - -namespace EcmaScript.NET -{ - - /// This is the default error reporter for JavaScript. - /// - /// - class DefaultErrorReporter : ErrorReporter - { - internal static readonly DefaultErrorReporter instance = new DefaultErrorReporter (); - - private bool forEval; - private ErrorReporter chainedReporter; - - private DefaultErrorReporter () - { - } - - internal static ErrorReporter ForEval (ErrorReporter reporter) - { - DefaultErrorReporter r = new DefaultErrorReporter (); - r.forEval = true; - r.chainedReporter = reporter; - return r; - } - - public virtual void Warning (string message, string sourceURI, int line, string lineText, int lineOffset) - { - if (chainedReporter != null) { - chainedReporter.Warning (message, sourceURI, line, lineText, lineOffset); - } - else { - Console.Error.WriteLine ("strict warning: " + message); - } - } - - public virtual void Error (string message, string sourceURI, int line, string lineText, int lineOffset) - { - if (forEval) { - throw ScriptRuntime.ConstructError ("SyntaxError", message, sourceURI, line, lineText, lineOffset); - } - if (chainedReporter != null) { - chainedReporter.Error (message, sourceURI, line, lineText, lineOffset); - } - else { - throw RuntimeError (message, sourceURI, line, lineText, lineOffset); - } - } - - public virtual EcmaScriptRuntimeException RuntimeError (string message, string sourceURI, int line, string lineText, int lineOffset) - { - if (chainedReporter != null) { - return chainedReporter.RuntimeError (message, sourceURI, line, lineText, lineOffset); - } - else { - return new EcmaScriptRuntimeException (message, sourceURI, line, lineText, lineOffset); - } - } - } +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; + +namespace EcmaScript.NET +{ + + /// This is the default error reporter for JavaScript. + /// + /// + class DefaultErrorReporter : ErrorReporter + { + internal static readonly DefaultErrorReporter instance = new DefaultErrorReporter (); + + private bool forEval; + private ErrorReporter chainedReporter; + + private DefaultErrorReporter () + { + } + + internal static ErrorReporter ForEval (ErrorReporter reporter) + { + DefaultErrorReporter r = new DefaultErrorReporter (); + r.forEval = true; + r.chainedReporter = reporter; + return r; + } + + public virtual void Warning (string message, string sourceURI, int line, string lineText, int lineOffset) + { + if (chainedReporter != null) { + chainedReporter.Warning (message, sourceURI, line, lineText, lineOffset); + } + else { + Console.Error.WriteLine ("strict warning: " + message); + } + } + + public virtual void Error (string message, string sourceURI, int line, string lineText, int lineOffset) + { + if (forEval) { + throw ScriptRuntime.ConstructError ("SyntaxError", message, sourceURI, line, lineText, lineOffset); + } + if (chainedReporter != null) { + chainedReporter.Error (message, sourceURI, line, lineText, lineOffset); + } + else { + throw RuntimeError (message, sourceURI, line, lineText, lineOffset); + } + } + + public virtual EcmaScriptRuntimeException RuntimeError (string message, string sourceURI, int line, string lineText, int lineOffset) + { + if (chainedReporter != null) { + return chainedReporter.RuntimeError (message, sourceURI, line, lineText, lineOffset); + } + else { + return new EcmaScriptRuntimeException (message, sourceURI, line, lineText, lineOffset); + } + } + } } \ No newline at end of file diff --git a/Code/EcmaScript.NET/Delegator.cs b/src/EcmaScript.NET/Delegator.cs similarity index 96% rename from Code/EcmaScript.NET/Delegator.cs rename to src/EcmaScript.NET/Delegator.cs index 3c10496..021fff4 100644 --- a/Code/EcmaScript.NET/Delegator.cs +++ b/src/EcmaScript.NET/Delegator.cs @@ -1,243 +1,243 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; - -using EcmaScript.NET.Types; - -namespace EcmaScript.NET -{ - - /// This is a helper class for implementing wrappers around Scriptable - /// objects. It implements the Function interface and delegates all - /// invocations to a delegee Scriptable object. The normal use of this - /// class involves creating a sub-class and overriding one or more of - /// the methods. - /// - /// A useful application is the implementation of interceptors, - /// pre/post conditions, debugging. - /// - /// - public class Delegator : IFunction - { - - /// Retrieve the delegee. - /// - /// - /// the delegee - /// - /// Set the delegee. - /// - /// - /// the delegee - /// - virtual public IScriptable Delegee - { - get - { - return obj; - } - - set - { - this.obj = value; - } - - } - - virtual public string ClassName - { - get - { - return obj.ClassName; - } - - } - - - virtual public IScriptable ParentScope - { - get - { - return obj.ParentScope; - } - - set - { - obj.ParentScope = value; - } - - } - - protected internal IScriptable obj = null; - - /// Create a Delegator prototype. - /// - /// This constructor should only be used for creating prototype - /// objects of Delegator. - /// - /// - - public Delegator () - { - } - - /// Create a new Delegator that forwards requests to a delegee - /// Scriptable object. - /// - /// - /// the delegee - /// - public Delegator (IScriptable obj) - { - this.obj = obj; - } - - /// Crete new Delegator instance. - /// The default implementation calls this.getClass().newInstance(). - /// - /// - protected internal virtual Delegator NewInstance () - { - try { - return (Delegator)System.Activator.CreateInstance (this.GetType ()); - } - catch (Exception ex) { - throw Context.ThrowAsScriptRuntimeEx (ex); - } - } - - public virtual object Get (string name, IScriptable start) - { - return obj.Get (name, start); - } - - public virtual object Get (int index, IScriptable start) - { - return obj.Get (index, start); - } - - public virtual bool Has (string name, IScriptable start) - { - return obj.Has (name, start); - } - - public virtual bool Has (int index, IScriptable start) - { - return obj.Has (index, start); - } - - public virtual object Put (string name, IScriptable start, object value) - { - return obj.Put (name, start, value); - } - - public virtual object Put (int index, IScriptable start, object value) - { - return obj.Put (index, start, value); - } - - public virtual void Delete (string name) - { - obj.Delete (name); - } - - public virtual void Delete (int index) - { - obj.Delete (index); - } - - public virtual IScriptable GetPrototype () - { - return obj.GetPrototype (); - } - - public virtual void SetPrototype (IScriptable prototype) - { - obj.SetPrototype (prototype); - } - - public virtual object [] GetIds () - { - return obj.GetIds (); - } - /// Note that this method does not get forwarded to the delegee if - /// the hint parameter is null, - /// typeof(Scriptable) or - /// typeof(Function). Instead the object - /// itself is returned. - /// - /// - /// the type hint - /// - /// the default value - /// - /// - public virtual object GetDefaultValue (Type hint) - { - return (hint == null - || hint == typeof (IScriptable) - || hint == typeof (IFunction)) - ? this : obj.GetDefaultValue (hint); - } - - public virtual bool HasInstance (IScriptable instance) - { - return obj.HasInstance (instance); - } - - public virtual object Call (Context cx, IScriptable scope, IScriptable thisObj, object [] args) - { - return ((IFunction)obj).Call (cx, scope, thisObj, args); - } - - /// Note that if the delegee is null, - /// this method creates a new instance of the Delegator itself - /// rathert than forwarding the call to the - /// delegee. This permits the use of Delegator - /// prototypes. - /// - /// - /// the current Context for this thread - /// - /// an enclosing scope of the caller except - /// when the function is called from a closure. - /// - /// the array of arguments - /// - /// the allocated object - /// - /// - - public virtual IScriptable Construct (Context cx, IScriptable scope, object [] args) - { - if (obj == null) { - //this little trick allows us to declare prototype objects for - //Delegators - Delegator n = NewInstance (); - IScriptable delegee; - if (args.Length == 0) { - delegee = new BuiltinObject (); - } - else { - delegee = ScriptConvert.ToObject (cx, scope, args [0]); - } - n.Delegee = delegee; - return n; - } - else { - return ((IFunction)obj).Construct (cx, scope, args); - } - } - } +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; + +using EcmaScript.NET.Types; + +namespace EcmaScript.NET +{ + + /// This is a helper class for implementing wrappers around Scriptable + /// objects. It implements the Function interface and delegates all + /// invocations to a delegee Scriptable object. The normal use of this + /// class involves creating a sub-class and overriding one or more of + /// the methods. + /// + /// A useful application is the implementation of interceptors, + /// pre/post conditions, debugging. + /// + /// + public class Delegator : IFunction + { + + /// Retrieve the delegee. + /// + /// + /// the delegee + /// + /// Set the delegee. + /// + /// + /// the delegee + /// + virtual public IScriptable Delegee + { + get + { + return obj; + } + + set + { + this.obj = value; + } + + } + + virtual public string ClassName + { + get + { + return obj.ClassName; + } + + } + + + virtual public IScriptable ParentScope + { + get + { + return obj.ParentScope; + } + + set + { + obj.ParentScope = value; + } + + } + + protected internal IScriptable obj = null; + + /// Create a Delegator prototype. + /// + /// This constructor should only be used for creating prototype + /// objects of Delegator. + /// + /// + + public Delegator () + { + } + + /// Create a new Delegator that forwards requests to a delegee + /// Scriptable object. + /// + /// + /// the delegee + /// + public Delegator (IScriptable obj) + { + this.obj = obj; + } + + /// Crete new Delegator instance. + /// The default implementation calls this.getClass().newInstance(). + /// + /// + protected internal virtual Delegator NewInstance () + { + try { + return (Delegator)System.Activator.CreateInstance (this.GetType ()); + } + catch (Exception ex) { + throw Context.ThrowAsScriptRuntimeEx (ex); + } + } + + public virtual object Get (string name, IScriptable start) + { + return obj.Get (name, start); + } + + public virtual object Get (int index, IScriptable start) + { + return obj.Get (index, start); + } + + public virtual bool Has (string name, IScriptable start) + { + return obj.Has (name, start); + } + + public virtual bool Has (int index, IScriptable start) + { + return obj.Has (index, start); + } + + public virtual object Put (string name, IScriptable start, object value) + { + return obj.Put (name, start, value); + } + + public virtual object Put (int index, IScriptable start, object value) + { + return obj.Put (index, start, value); + } + + public virtual void Delete (string name) + { + obj.Delete (name); + } + + public virtual void Delete (int index) + { + obj.Delete (index); + } + + public virtual IScriptable GetPrototype () + { + return obj.GetPrototype (); + } + + public virtual void SetPrototype (IScriptable prototype) + { + obj.SetPrototype (prototype); + } + + public virtual object [] GetIds () + { + return obj.GetIds (); + } + /// Note that this method does not get forwarded to the delegee if + /// the hint parameter is null, + /// typeof(Scriptable) or + /// typeof(Function). Instead the object + /// itself is returned. + /// + /// + /// the type hint + /// + /// the default value + /// + /// + public virtual object GetDefaultValue (Type hint) + { + return (hint == null + || hint == typeof (IScriptable) + || hint == typeof (IFunction)) + ? this : obj.GetDefaultValue (hint); + } + + public virtual bool HasInstance (IScriptable instance) + { + return obj.HasInstance (instance); + } + + public virtual object Call (Context cx, IScriptable scope, IScriptable thisObj, object [] args) + { + return ((IFunction)obj).Call (cx, scope, thisObj, args); + } + + /// Note that if the delegee is null, + /// this method creates a new instance of the Delegator itself + /// rathert than forwarding the call to the + /// delegee. This permits the use of Delegator + /// prototypes. + /// + /// + /// the current Context for this thread + /// + /// an enclosing scope of the caller except + /// when the function is called from a closure. + /// + /// the array of arguments + /// + /// the allocated object + /// + /// + + public virtual IScriptable Construct (Context cx, IScriptable scope, object [] args) + { + if (obj == null) { + //this little trick allows us to declare prototype objects for + //Delegators + Delegator n = NewInstance (); + IScriptable delegee; + if (args.Length == 0) { + delegee = new BuiltinObject (); + } + else { + delegee = ScriptConvert.ToObject (cx, scope, args [0]); + } + n.Delegee = delegee; + return n; + } + else { + return ((IFunction)obj).Construct (cx, scope, args); + } + } + } } \ No newline at end of file diff --git a/src/EcmaScript.NET/EcmaScript.NET.csproj b/src/EcmaScript.NET/EcmaScript.NET.csproj new file mode 100644 index 0000000..1fb4d0a --- /dev/null +++ b/src/EcmaScript.NET/EcmaScript.NET.csproj @@ -0,0 +1,21 @@ + + + + netstandard2.0;net45 + 0.0.0 + Pure Krome + World Domination Technologies + This is a custom modified version of the EcmaScript code, mainly created for YUICompressor.NET. + 2011 + https://github.com/PureKrome/EcmaScript.NET/blob/master/License.txt + https://github.com/PureKrome/EcmaScript.NET + https://github.com/PureKrome/EcmaScript.NET + .net c# + ecmascript, javascript + + + + + + + diff --git a/Code/EcmaScript.NET/EcmaScriptError.cs b/src/EcmaScript.NET/EcmaScriptError.cs similarity index 97% rename from Code/EcmaScript.NET/EcmaScriptError.cs rename to src/EcmaScript.NET/EcmaScriptError.cs index ebe0713..aa71673 100644 --- a/Code/EcmaScript.NET/EcmaScriptError.cs +++ b/src/EcmaScript.NET/EcmaScriptError.cs @@ -1,99 +1,99 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; - -namespace EcmaScript.NET -{ - - /// The class of exceptions raised by the engine as described in - /// ECMA edition 3. See section 15.11.6 in particular. - /// - public class EcmaScriptError : EcmaScriptException - { - /// Gets the name of the error. - /// - /// ECMA edition 3 defines the following - /// errors: EvalError, RangeError, ReferenceError, - /// SyntaxError, TypeError, and URIError. Additional error names - /// may be added in the future. - /// - /// See ECMA edition 3, 15.11.7.9. - /// - /// - /// the name of the error. - /// - public virtual string Name - { - get - { - return m_ErrorName; - } - - } - - public override string Message - { - get - { - return string.Format ( - "\"{0}\", {1} at line {2}: {3}", - base.SourceName, Name, base.LineNumber, ErrorMessage); - } - } - - /// Gets the message corresponding to the error. - /// - /// See ECMA edition 3, 15.11.7.10. - /// - /// - /// an implemenation-defined string describing the error. - /// - public virtual string ErrorMessage - { - get - { - return m_ErrorMessage; - } - - } - - string m_ErrorName; - string m_ErrorMessage; - - /// Create an exception with the specified detail message. - /// - /// Errors internal to the JavaScript engine will simply throw a - /// RuntimeException. - /// - /// - /// the name of the source reponsible for the error - /// - /// the line number of the source - /// - /// the columnNumber of the source (may be zero if - /// unknown) - /// - /// the source of the line containing the error (may be - /// null if unknown) - /// - internal EcmaScriptError (string errorName, string errorMessage, string sourceName, int lineNumber, string lineSource, int columnNumber) - { - RecordErrorOrigin (sourceName, lineNumber, lineSource, columnNumber); - this.m_ErrorName = errorName; - this.m_ErrorMessage = errorMessage; - } - - } - +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; + +namespace EcmaScript.NET +{ + + /// The class of exceptions raised by the engine as described in + /// ECMA edition 3. See section 15.11.6 in particular. + /// + public class EcmaScriptError : EcmaScriptException + { + /// Gets the name of the error. + /// + /// ECMA edition 3 defines the following + /// errors: EvalError, RangeError, ReferenceError, + /// SyntaxError, TypeError, and URIError. Additional error names + /// may be added in the future. + /// + /// See ECMA edition 3, 15.11.7.9. + /// + /// + /// the name of the error. + /// + public virtual string Name + { + get + { + return m_ErrorName; + } + + } + + public override string Message + { + get + { + return string.Format ( + "\"{0}\", {1} at line {2}: {3}", + base.SourceName, Name, base.LineNumber, ErrorMessage); + } + } + + /// Gets the message corresponding to the error. + /// + /// See ECMA edition 3, 15.11.7.10. + /// + /// + /// an implemenation-defined string describing the error. + /// + public virtual string ErrorMessage + { + get + { + return m_ErrorMessage; + } + + } + + string m_ErrorName; + string m_ErrorMessage; + + /// Create an exception with the specified detail message. + /// + /// Errors internal to the JavaScript engine will simply throw a + /// RuntimeException. + /// + /// + /// the name of the source reponsible for the error + /// + /// the line number of the source + /// + /// the columnNumber of the source (may be zero if + /// unknown) + /// + /// the source of the line containing the error (may be + /// null if unknown) + /// + internal EcmaScriptError (string errorName, string errorMessage, string sourceName, int lineNumber, string lineSource, int columnNumber) + { + RecordErrorOrigin (sourceName, lineNumber, lineSource, columnNumber); + this.m_ErrorName = errorName; + this.m_ErrorMessage = errorMessage; + } + + } + } \ No newline at end of file diff --git a/Code/EcmaScript.NET/EcmaScriptException.cs b/src/EcmaScript.NET/EcmaScriptException.cs similarity index 94% rename from Code/EcmaScript.NET/EcmaScriptException.cs rename to src/EcmaScript.NET/EcmaScriptException.cs index 7b42740..bb75e19 100644 --- a/Code/EcmaScript.NET/EcmaScriptException.cs +++ b/src/EcmaScript.NET/EcmaScriptException.cs @@ -1,221 +1,220 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; -using System.IO; -using System.Text; - -namespace EcmaScript.NET -{ - - /// - /// The class of exceptions thrown by the JavaScript engine. - /// - public abstract class EcmaScriptException : ApplicationException - { - - public override string Message - { - get - { - string details = base.Message; - if (m_SourceName == null || m_LineNumber <= 0) { - return details; - } - StringBuilder buf = new StringBuilder (details); - buf.Append (" ("); - if (m_SourceName != null) { - buf.Append (m_SourceName); - } - if (m_LineNumber > 0) { - buf.Append ('#'); - buf.Append (m_LineNumber); - } - buf.Append (')'); - return buf.ToString (); - } - - } - - internal EcmaScriptException () - { - Interpreter.captureInterpreterStackInfo (this); - } - - internal EcmaScriptException (string details) - : base (details) - { - Interpreter.captureInterpreterStackInfo (this); - } - - - internal EcmaScriptException (string details, Exception innerException) - : base (details, innerException) - { - Interpreter.captureInterpreterStackInfo (this); - } - /// Get the uri of the script source containing the error, or null - /// if that information is not available. - /// - public virtual string SourceName - { - get - { - return m_SourceName; - } - } - - /// Initialize the uri of the script source containing the error. - /// - /// - /// the uri of the script source reponsible for the error. - /// It should not be null. - /// - /// - /// IllegalStateException if the method is called more then once. - public void InitSourceName (string sourceName) - { - if (sourceName == null) - throw new ArgumentException (); - if (this.m_SourceName != null) - throw new ApplicationException (); - this.m_SourceName = sourceName; - } - - /// Returns the line number of the statement causing the error, - /// or zero if not available. - /// - public int LineNumber - { - get - { - return m_LineNumber; - } - } - - /// Initialize the line number of the script statement causing the error. - /// - /// - /// the line number in the script source. - /// It should be positive number. - /// - /// - /// IllegalStateException if the method is called more then once. - public void InitLineNumber (int lineNumber) - { - if (lineNumber <= 0) - throw new ArgumentException (Convert.ToString (lineNumber)); - if (this.m_LineNumber > 0) - throw new ApplicationException (); - this.m_LineNumber = lineNumber; - } - - /// The column number of the location of the error, or zero if unknown. - public int ColumnNumber - { - get - { - return m_ColumnNumber; - } - } - - /// Initialize the column number of the script statement causing the error. - /// - /// - /// the column number in the script source. - /// It should be positive number. - /// - /// - /// IllegalStateException if the method is called more then once. - public void InitColumnNumber (int columnNumber) - { - if (columnNumber <= 0) - throw new ArgumentException (Convert.ToString (columnNumber)); - if (this.m_ColumnNumber > 0) - throw new ApplicationException (); - this.m_ColumnNumber = columnNumber; - } - - /// The source text of the line causing the error, or null if unknown. - public string LineSource - { - get - { - return m_LineSource; - } - } - - /// Initialize the text of the source line containing the error. - /// - /// - /// the text of the source line reponsible for the error. - /// It should not be null. - /// - /// - /// IllegalStateException if the method is called more then once. - public void InitLineSource (string lineSource) - { - if (lineSource == null) - throw new ArgumentException (); - if (this.m_LineSource != null) - throw new ApplicationException (); - this.m_LineSource = lineSource; - } - - internal void RecordErrorOrigin (string sourceName, int lineNumber, string lineSource, int columnNumber) - { - if (sourceName != null) { - InitSourceName (sourceName); - } - if (lineNumber != 0) { - InitLineNumber (lineNumber); - } - if (lineSource != null) { - InitLineSource (lineSource); - } - if (columnNumber != 0) { - InitColumnNumber (columnNumber); - } - InitScriptStackTrace (); - } - - private string m_SourceName; - private int m_LineNumber; - private string m_LineSource; - private int m_ColumnNumber; - - internal object m_InterpreterStackInfo; - internal int [] m_InterpreterLineData; - - private void InitScriptStackTrace () { - m_ScriptStackTrace = Interpreter.GetStack (this); - } - private string m_ScriptStackTrace = null; - public string ScriptStackTrace - { - get - { - - return m_ScriptStackTrace; - } - } - - public override string ToString () - { - if (this.StackTrace != null) - return Interpreter.getPatchedStack (this, this.StackTrace.ToString ()); - return ScriptStackTrace; - } - - } +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; +using System.Text; + +namespace EcmaScript.NET +{ + + /// + /// The class of exceptions thrown by the JavaScript engine. + /// + public abstract class EcmaScriptException : Exception + { + + public override string Message + { + get + { + string details = base.Message; + if (m_SourceName == null || m_LineNumber <= 0) { + return details; + } + StringBuilder buf = new StringBuilder (details); + buf.Append (" ("); + if (m_SourceName != null) { + buf.Append (m_SourceName); + } + if (m_LineNumber > 0) { + buf.Append ('#'); + buf.Append (m_LineNumber); + } + buf.Append (')'); + return buf.ToString (); + } + + } + + internal EcmaScriptException () + { + Interpreter.captureInterpreterStackInfo (this); + } + + internal EcmaScriptException (string details) + : base (details) + { + Interpreter.captureInterpreterStackInfo (this); + } + + + internal EcmaScriptException (string details, Exception innerException) + : base (details, innerException) + { + Interpreter.captureInterpreterStackInfo (this); + } + /// Get the uri of the script source containing the error, or null + /// if that information is not available. + /// + public virtual string SourceName + { + get + { + return m_SourceName; + } + } + + /// Initialize the uri of the script source containing the error. + /// + /// + /// the uri of the script source reponsible for the error. + /// It should not be null. + /// + /// + /// IllegalStateException if the method is called more then once. + public void InitSourceName (string sourceName) + { + if (sourceName == null) + throw new ArgumentException (); + if (this.m_SourceName != null) + throw new Exception (); + this.m_SourceName = sourceName; + } + + /// Returns the line number of the statement causing the error, + /// or zero if not available. + /// + public int LineNumber + { + get + { + return m_LineNumber; + } + } + + /// Initialize the line number of the script statement causing the error. + /// + /// + /// the line number in the script source. + /// It should be positive number. + /// + /// + /// IllegalStateException if the method is called more then once. + public void InitLineNumber (int lineNumber) + { + if (lineNumber <= 0) + throw new ArgumentException (Convert.ToString (lineNumber)); + if (this.m_LineNumber > 0) + throw new Exception (); + this.m_LineNumber = lineNumber; + } + + /// The column number of the location of the error, or zero if unknown. + public int ColumnNumber + { + get + { + return m_ColumnNumber; + } + } + + /// Initialize the column number of the script statement causing the error. + /// + /// + /// the column number in the script source. + /// It should be positive number. + /// + /// + /// IllegalStateException if the method is called more then once. + public void InitColumnNumber (int columnNumber) + { + if (columnNumber <= 0) + throw new ArgumentException (Convert.ToString (columnNumber)); + if (this.m_ColumnNumber > 0) + throw new Exception (); + this.m_ColumnNumber = columnNumber; + } + + /// The source text of the line causing the error, or null if unknown. + public string LineSource + { + get + { + return m_LineSource; + } + } + + /// Initialize the text of the source line containing the error. + /// + /// + /// the text of the source line reponsible for the error. + /// It should not be null. + /// + /// + /// IllegalStateException if the method is called more then once. + public void InitLineSource (string lineSource) + { + if (lineSource == null) + throw new ArgumentException (); + if (this.m_LineSource != null) + throw new Exception (); + this.m_LineSource = lineSource; + } + + internal void RecordErrorOrigin (string sourceName, int lineNumber, string lineSource, int columnNumber) + { + if (sourceName != null) { + InitSourceName (sourceName); + } + if (lineNumber != 0) { + InitLineNumber (lineNumber); + } + if (lineSource != null) { + InitLineSource (lineSource); + } + if (columnNumber != 0) { + InitColumnNumber (columnNumber); + } + InitScriptStackTrace (); + } + + private string m_SourceName; + private int m_LineNumber; + private string m_LineSource; + private int m_ColumnNumber; + + internal object m_InterpreterStackInfo; + internal int [] m_InterpreterLineData; + + private void InitScriptStackTrace () { + m_ScriptStackTrace = Interpreter.GetStack (this); + } + private string m_ScriptStackTrace = null; + public string ScriptStackTrace + { + get + { + + return m_ScriptStackTrace; + } + } + + public override string ToString () + { + if (this.StackTrace != null) + return Interpreter.getPatchedStack (this, this.StackTrace.ToString ()); + return ScriptStackTrace; + } + + } } \ No newline at end of file diff --git a/Code/EcmaScript.NET/EcmaScriptRuntimeException.cs b/src/EcmaScript.NET/EcmaScriptRuntimeException.cs similarity index 97% rename from Code/EcmaScript.NET/EcmaScriptRuntimeException.cs rename to src/EcmaScript.NET/EcmaScriptRuntimeException.cs index 6ff1f34..a67999a 100644 --- a/Code/EcmaScript.NET/EcmaScriptRuntimeException.cs +++ b/src/EcmaScript.NET/EcmaScriptRuntimeException.cs @@ -1,84 +1,84 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; - -namespace EcmaScript.NET -{ - - /// The class of exceptions thrown by the JavaScript engine. - - public class EcmaScriptRuntimeException : EcmaScriptException - { - - public EcmaScriptRuntimeException (Exception innerException) - : base (innerException.Message, innerException) - { - int [] linep = new int [] { 0 }; - string sourceName = Context.GetSourcePositionFromStack (linep); - int lineNumber = linep [0]; - if (sourceName != null) { - InitSourceName (sourceName); - } - if (lineNumber != 0) { - InitLineNumber (lineNumber); - } - } - - public EcmaScriptRuntimeException (string detail) - : base (detail) - { - } - - /// Create an exception with the specified detail message. - /// - /// Errors internal to the JavaScript engine will simply throw a - /// RuntimeException. - /// - /// - /// the error message - /// - /// the name of the source reponsible for the error - /// - /// the line number of the source - /// - public EcmaScriptRuntimeException (string detail, string sourceName, int lineNumber) - : this (detail, sourceName, lineNumber, null, 0) - { - } - - /// Create an exception with the specified detail message. - /// - /// Errors internal to the JavaScript engine will simply throw a - /// RuntimeException. - /// - /// - /// the error message - /// - /// the name of the source reponsible for the error - /// - /// the line number of the source - /// - /// the columnNumber of the source (may be zero if - /// unknown) - /// - /// the source of the line containing the error (may be - /// null if unknown) - /// - public EcmaScriptRuntimeException (string detail, string sourceName, int lineNumber, string lineSource, int columnNumber) - : base (detail) - { - RecordErrorOrigin (sourceName, lineNumber, lineSource, columnNumber); - } - } +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; + +namespace EcmaScript.NET +{ + + /// The class of exceptions thrown by the JavaScript engine. + + public class EcmaScriptRuntimeException : EcmaScriptException + { + + public EcmaScriptRuntimeException (Exception innerException) + : base (innerException.Message, innerException) + { + int [] linep = new int [] { 0 }; + string sourceName = Context.GetSourcePositionFromStack (linep); + int lineNumber = linep [0]; + if (sourceName != null) { + InitSourceName (sourceName); + } + if (lineNumber != 0) { + InitLineNumber (lineNumber); + } + } + + public EcmaScriptRuntimeException (string detail) + : base (detail) + { + } + + /// Create an exception with the specified detail message. + /// + /// Errors internal to the JavaScript engine will simply throw a + /// RuntimeException. + /// + /// + /// the error message + /// + /// the name of the source reponsible for the error + /// + /// the line number of the source + /// + public EcmaScriptRuntimeException (string detail, string sourceName, int lineNumber) + : this (detail, sourceName, lineNumber, null, 0) + { + } + + /// Create an exception with the specified detail message. + /// + /// Errors internal to the JavaScript engine will simply throw a + /// RuntimeException. + /// + /// + /// the error message + /// + /// the name of the source reponsible for the error + /// + /// the line number of the source + /// + /// the columnNumber of the source (may be zero if + /// unknown) + /// + /// the source of the line containing the error (may be + /// null if unknown) + /// + public EcmaScriptRuntimeException (string detail, string sourceName, int lineNumber, string lineSource, int columnNumber) + : base (detail) + { + RecordErrorOrigin (sourceName, lineNumber, lineSource, columnNumber); + } + } } \ No newline at end of file diff --git a/Code/EcmaScript.NET/EcmaScriptThrow.cs b/src/EcmaScript.NET/EcmaScriptThrow.cs similarity index 96% rename from Code/EcmaScript.NET/EcmaScriptThrow.cs rename to src/EcmaScript.NET/EcmaScriptThrow.cs index a52b857..451c147 100644 --- a/Code/EcmaScript.NET/EcmaScriptThrow.cs +++ b/src/EcmaScript.NET/EcmaScriptThrow.cs @@ -1,63 +1,63 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; - -namespace EcmaScript.NET -{ - - /// Java reflection of JavaScript exceptions. - /// Instances of this class are thrown by the JavaScript 'throw' keyword. - /// - /// - - public class EcmaScriptThrow : EcmaScriptException - { - - /// the value wrapped by this exception - /// - public virtual object Value - { - get - { - return value; - } - - } - - /// - /// Create a JavaScript exception wrapping the given JavaScript value - /// - /// the JavaScript value thrown. - public EcmaScriptThrow (object value, string sourceName, int lineNumber) - { - RecordErrorOrigin (sourceName, lineNumber, null, 0); - this.value = value; - } - - public override string Message - { - get - { - IScriptable scriptable = (value as IScriptable); - if (scriptable != null) { - // to prevent potential of evaluation and throwing more exceptions - return ScriptRuntime.DefaultObjectToString (scriptable); - } - return ScriptConvert.ToString (value); - } - } - - private object value; - } +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; + +namespace EcmaScript.NET +{ + + /// Java reflection of JavaScript exceptions. + /// Instances of this class are thrown by the JavaScript 'throw' keyword. + /// + /// + + public class EcmaScriptThrow : EcmaScriptException + { + + /// the value wrapped by this exception + /// + public virtual object Value + { + get + { + return value; + } + + } + + /// + /// Create a JavaScript exception wrapping the given JavaScript value + /// + /// the JavaScript value thrown. + public EcmaScriptThrow (object value, string sourceName, int lineNumber) + { + RecordErrorOrigin (sourceName, lineNumber, null, 0); + this.value = value; + } + + public override string Message + { + get + { + IScriptable scriptable = (value as IScriptable); + if (scriptable != null) { + // to prevent potential of evaluation and throwing more exceptions + return ScriptRuntime.DefaultObjectToString (scriptable); + } + return ScriptConvert.ToString (value); + } + } + + private object value; + } } \ No newline at end of file diff --git a/Code/EcmaScript.NET/ErrorReporter.cs b/src/EcmaScript.NET/ErrorReporter.cs similarity index 97% rename from Code/EcmaScript.NET/ErrorReporter.cs rename to src/EcmaScript.NET/ErrorReporter.cs index 608442c..a8a9d45 100644 --- a/Code/EcmaScript.NET/ErrorReporter.cs +++ b/src/EcmaScript.NET/ErrorReporter.cs @@ -1,91 +1,91 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; - -namespace EcmaScript.NET -{ - - /// This is interface defines a protocol for the reporting of - /// errors during JavaScript translation or execution. - /// - /// - - public interface ErrorReporter - { - - /// Report a warning. - /// - /// The implementing class may choose to ignore the warning - /// if it desires. - /// - /// - /// a String describing the warning - /// - /// a String describing the JavaScript source - /// where the warning occured; typically a filename or URL - /// - /// the line number associated with the warning - /// - /// the text of the line (may be null) - /// - /// the offset into lineSource where problem was detected - /// - void Warning (string message, string sourceName, int line, string lineSource, int lineOffset); - - /// Report an error. - /// - /// The implementing class is free to throw an exception if - /// it desires. - /// - /// If execution has not yet begun, the JavaScript engine is - /// free to find additional errors rather than terminating - /// the translation. It will not execute a script that had - /// errors, however. - /// - /// - /// a String describing the error - /// - /// a String describing the JavaScript source - /// where the error occured; typically a filename or URL - /// - /// the line number associated with the error - /// - /// the text of the line (may be null) - /// - /// the offset into lineSource where problem was detected - /// - void Error (string message, string sourceName, int line, string lineSource, int lineOffset); - - /// Creates an EvaluatorException that may be thrown. - /// - /// runtimeErrors, unlike errors, will always terminate the - /// current script. - /// - /// - /// a String describing the error - /// - /// a String describing the JavaScript source - /// where the error occured; typically a filename or URL - /// - /// the line number associated with the error - /// - /// the text of the line (may be null) - /// - /// the offset into lineSource where problem was detected - /// - /// an EvaluatorException that will be thrown. - /// - EcmaScriptRuntimeException RuntimeError (string message, string sourceName, int line, string lineSource, int lineOffset); - } +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; + +namespace EcmaScript.NET +{ + + /// This is interface defines a protocol for the reporting of + /// errors during JavaScript translation or execution. + /// + /// + + public interface ErrorReporter + { + + /// Report a warning. + /// + /// The implementing class may choose to ignore the warning + /// if it desires. + /// + /// + /// a String describing the warning + /// + /// a String describing the JavaScript source + /// where the warning occured; typically a filename or URL + /// + /// the line number associated with the warning + /// + /// the text of the line (may be null) + /// + /// the offset into lineSource where problem was detected + /// + void Warning (string message, string sourceName, int line, string lineSource, int lineOffset); + + /// Report an error. + /// + /// The implementing class is free to throw an exception if + /// it desires. + /// + /// If execution has not yet begun, the JavaScript engine is + /// free to find additional errors rather than terminating + /// the translation. It will not execute a script that had + /// errors, however. + /// + /// + /// a String describing the error + /// + /// a String describing the JavaScript source + /// where the error occured; typically a filename or URL + /// + /// the line number associated with the error + /// + /// the text of the line (may be null) + /// + /// the offset into lineSource where problem was detected + /// + void Error (string message, string sourceName, int line, string lineSource, int lineOffset); + + /// Creates an EvaluatorException that may be thrown. + /// + /// runtimeErrors, unlike errors, will always terminate the + /// current script. + /// + /// + /// a String describing the error + /// + /// a String describing the JavaScript source + /// where the error occured; typically a filename or URL + /// + /// the line number associated with the error + /// + /// the text of the line (may be null) + /// + /// the offset into lineSource where problem was detected + /// + /// an EvaluatorException that will be thrown. + /// + EcmaScriptRuntimeException RuntimeError (string message, string sourceName, int line, string lineSource, int lineOffset); + } } \ No newline at end of file diff --git a/Code/EcmaScript.NET/FunctionNode.cs b/src/EcmaScript.NET/FunctionNode.cs similarity index 96% rename from Code/EcmaScript.NET/FunctionNode.cs rename to src/EcmaScript.NET/FunctionNode.cs index 0861b42..74bee4b 100644 --- a/Code/EcmaScript.NET/FunctionNode.cs +++ b/src/EcmaScript.NET/FunctionNode.cs @@ -1,85 +1,85 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; - -namespace EcmaScript.NET -{ - - public class FunctionNode : ScriptOrFnNode - { - virtual public string FunctionName - { - get - { - return functionName; - } - - } - virtual public bool IgnoreDynamicScope - { - get - { - return itsIgnoreDynamicScope; - } - - } - virtual public int FunctionType - { - get - { - return itsFunctionType; - } - - } - - public FunctionNode (string name) - : base (Token.FUNCTION) - { - functionName = name; - } - - public virtual bool RequiresActivation - { - - get - { - return itsNeedsActivation; - } - } - - /// - /// There are three types of functions that can be defined. The first - /// is a function statement. This is a function appearing as a top-level - /// statement (i.e., not nested inside some other statement) in either a - /// script or a function. - /// - /// The second is a function expression, which is a function appearing in - /// an expression except for the third type, which is... - /// - /// The third type is a function expression where the expression is the - /// top-level expression in an expression statement. - /// - /// The three types of functions have different treatment and must be - /// distinquished. - /// - public const int FUNCTION_STATEMENT = 1; - public const int FUNCTION_EXPRESSION = 2; - public const int FUNCTION_EXPRESSION_STATEMENT = 3; - - internal string functionName; - internal bool itsNeedsActivation; - internal int itsFunctionType; - internal bool itsIgnoreDynamicScope; - } +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; + +namespace EcmaScript.NET +{ + + public class FunctionNode : ScriptOrFnNode + { + virtual public string FunctionName + { + get + { + return functionName; + } + + } + virtual public bool IgnoreDynamicScope + { + get + { + return itsIgnoreDynamicScope; + } + + } + virtual public int FunctionType + { + get + { + return itsFunctionType; + } + + } + + public FunctionNode (string name) + : base (Token.FUNCTION) + { + functionName = name; + } + + public virtual bool RequiresActivation + { + + get + { + return itsNeedsActivation; + } + } + + /// + /// There are three types of functions that can be defined. The first + /// is a function statement. This is a function appearing as a top-level + /// statement (i.e., not nested inside some other statement) in either a + /// script or a function. + /// + /// The second is a function expression, which is a function appearing in + /// an expression except for the third type, which is... + /// + /// The third type is a function expression where the expression is the + /// top-level expression in an expression statement. + /// + /// The three types of functions have different treatment and must be + /// distinquished. + /// + public const int FUNCTION_STATEMENT = 1; + public const int FUNCTION_EXPRESSION = 2; + public const int FUNCTION_EXPRESSION_STATEMENT = 3; + + internal string functionName; + internal bool itsNeedsActivation; + internal int itsFunctionType; + internal bool itsIgnoreDynamicScope; + } } \ No newline at end of file diff --git a/Code/EcmaScript.NET/Helpers/CliHelper.cs b/src/EcmaScript.NET/Helpers/CliHelper.cs similarity index 96% rename from Code/EcmaScript.NET/Helpers/CliHelper.cs rename to src/EcmaScript.NET/Helpers/CliHelper.cs index f743f93..71238c0 100644 --- a/Code/EcmaScript.NET/Helpers/CliHelper.cs +++ b/src/EcmaScript.NET/Helpers/CliHelper.cs @@ -1,286 +1,286 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; -using System.Text; -using System.Reflection; -using System.Collections; -using System.Runtime.InteropServices; - -namespace EcmaScript.NET -{ - - sealed class CliHelper - { - - private CliHelper () - { - ; - } - - internal static Type GetType (string className) - { - try { - return Type.GetType (className); - } - catch { - ; - } - return null; - } - - internal static bool IsNegativeZero (double d) - { - if (double.IsNaN (d)) - return false; - if (d != 0.0) - return false; - return (double.PositiveInfinity / d) == double.NegativeInfinity; - } - - internal static bool IsPositiveZero (double d) - { - if (double.IsNaN (d)) - return false; - if (d != 0.0) - return false; - return (double.PositiveInfinity / d) == double.PositiveInfinity; - } - - internal static new bool Equals (object o1, object o2) - { - if (o1 == null && o2 == null) - return true; - if (o1 == null || o2 == null) - return false; - return o1.Equals (o2); - } - - internal static string ToSignature (ConstructorInfo ci) - { - StringBuilder sb = new StringBuilder (); - sb.Append (ToSignature (ci.DeclaringType)); - sb.Append (ToSignature ('(', ci.GetParameters (), ')')); - return sb.ToString (); - } - internal static string ToSignature (PropertyInfo pi) - { - StringBuilder sb = new StringBuilder (); - sb.Append (ToSignature (pi.PropertyType)); - sb.Append (" "); - sb.Append (ToSignature (pi.DeclaringType)); - sb.Append ("."); - sb.Append (pi.Name); - sb.Append (ToSignature ('[', pi.GetIndexParameters (), ']')); - return sb.ToString (); - } - - internal static string ToSignature (FieldInfo fi) - { - StringBuilder sb = new StringBuilder (); - sb.Append (ToSignature (fi.FieldType)); - sb.Append (" "); - sb.Append (ToSignature (fi.DeclaringType)); - sb.Append ("."); - sb.Append (fi.Name); - return sb.ToString (); - } - - internal static string ToSignature (object [] args) - { - StringBuilder sb = new StringBuilder (); - for (int i = 0; i < args.Length; i++) { - if (i > 0) - sb.Append (", "); - sb.Append (ToSignature (args [0].GetType ())); - } - return sb.ToString (); - } - - internal static string ToSignature (MemberInfo mi) - { - if (mi is PropertyInfo) - return ToSignature ((PropertyInfo)mi); - if (mi is FieldInfo) - return ToSignature ((FieldInfo)mi); - if (mi is ConstructorInfo) - return ToSignature ((ConstructorInfo)mi); - if (mi is MethodInfo) - return ToSignature ((MethodInfo)mi); - return "[unknown: " + mi.GetType ().FullName + "]"; - } - - internal static string ToSignature (char parenOpen, ParameterInfo [] pi, char parenClose) - { - StringBuilder sb = new StringBuilder (); - sb.Append (parenOpen); - for (int i = 0; i < pi.Length; i++) { - if (i > 0) - sb.Append (", "); - if (pi [i].IsOut) - sb.Append ("out "); - if (pi [i].IsIn) - sb.Append ("in "); - if (IsParamsParameter (pi [i])) - sb.Append ("params "); - sb.Append (ToSignature (pi [i].ParameterType)); - sb.Append (" "); - sb.Append (pi [i].Name); - } - sb.Append (parenClose); - return sb.ToString (); - } - - - internal static string ToSignature (MethodInfo mi) - { - StringBuilder sb = new StringBuilder (); - sb.Append (ToSignature (mi.ReturnType)); - sb.Append (" "); - sb.Append (ToSignature (mi.DeclaringType)); - sb.Append ("."); - sb.Append (mi.Name); - sb.Append (ToSignature ('(', mi.GetParameters (), ')')); - return sb.ToString (); - } - - internal static bool HasParamsParameter (MethodBase mb) - { - return HasParamsParameter (mb.GetParameters ()); - } - - internal static bool HasParamsParameter (ParameterInfo [] pis) - { - for (int i = 0; i < pis.Length; i++) - if (IsParamsParameter (pis [i])) - return true; - return false; - } - - internal static bool IsParamsParameter (ParameterInfo pi) - { - ParamArrayAttribute attr = (ParamArrayAttribute) - CliHelper.GetCustomAttribute (typeof (ParamArrayAttribute), pi); - return (attr != null); - } - - - internal static string ToSignature (Type type) - { - if (type.IsArray) { - string ret = ToSignature (type.GetElementType ()); - for (int i = 0; i < type.GetArrayRank (); i++) - ret += "[]"; - return ret; - } - if (type == typeof (short)) - return "short"; - if (type == typeof (ushort)) - return "ushort"; - if (type == typeof (int)) - return "int"; - if (type == typeof (uint)) - return "uint"; - if (type == typeof (ulong)) - return "ulong"; - if (type == typeof (long)) - return "long"; - if (type == typeof (void)) - return "void"; - if (type == typeof (bool)) - return "bool"; - if (type == typeof (double)) - return "double"; - if (type == typeof (decimal)) - return "decimal"; - if (type == typeof (object)) - return "object"; - return type.FullName; - } - - internal static bool IsNumberType (Type type) - { - return ( - type == typeof (Int16) - || type == typeof (UInt16) - || type == typeof (Int32) - || type == typeof (UInt32) - || type == typeof (Int64) - || type == typeof (UInt64) - || type == typeof (Single) - || type == typeof (Double) - || type == typeof (Decimal)); - } - - internal static bool IsNumber (object value) - { - return ( - value is Int16 - || value is UInt16 - || value is Int32 - || value is UInt32 - || value is Int64 - || value is UInt64 - || value is Single - || value is Double - || value is Decimal); - } - - internal static object CreateInstance (Type cl) - { - try { - return System.Activator.CreateInstance (cl); - } - catch { - ; - } - return null; - } - - internal static object GetCustomAttribute (Type type, Type attribute) - { - object [] attributes = type.GetCustomAttributes (attribute, true); - if (attributes.Length < 1) - return null; - return attributes [0]; - } - - internal static object GetCustomAttribute (Type type, MemberInfo mi) - { - object attribute = null; - object [] attributes = mi.GetCustomAttributes (type, true); - if (attributes.Length > 0) - attribute = attributes [0]; - return attribute; - } - - internal static object GetCustomAttribute (Type type, ParameterInfo pi) - { - object [] attributes = pi.GetCustomAttributes (type, true); - if (attributes.Length < 1) - return null; - return attributes [0]; - } - - internal static Type [] GetParameterTypes (ParameterInfo [] parameters) - { - Type [] types = new Type [parameters.Length]; - for (int i = 0; i < types.Length; i++) { - types [i] = parameters [i].ParameterType; - } - return types; - } - - } - -} +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; +using System.Text; +using System.Reflection; +using System.Collections; +using System.Runtime.InteropServices; + +namespace EcmaScript.NET +{ + + sealed class CliHelper + { + + private CliHelper () + { + ; + } + + internal static Type GetType (string className) + { + try { + return Type.GetType (className); + } + catch { + ; + } + return null; + } + + internal static bool IsNegativeZero (double d) + { + if (double.IsNaN (d)) + return false; + if (d != 0.0) + return false; + return (double.PositiveInfinity / d) == double.NegativeInfinity; + } + + internal static bool IsPositiveZero (double d) + { + if (double.IsNaN (d)) + return false; + if (d != 0.0) + return false; + return (double.PositiveInfinity / d) == double.PositiveInfinity; + } + + internal static new bool Equals (object o1, object o2) + { + if (o1 == null && o2 == null) + return true; + if (o1 == null || o2 == null) + return false; + return o1.Equals (o2); + } + + internal static string ToSignature (ConstructorInfo ci) + { + StringBuilder sb = new StringBuilder (); + sb.Append (ToSignature (ci.DeclaringType)); + sb.Append (ToSignature ('(', ci.GetParameters (), ')')); + return sb.ToString (); + } + internal static string ToSignature (PropertyInfo pi) + { + StringBuilder sb = new StringBuilder (); + sb.Append (ToSignature (pi.PropertyType)); + sb.Append (" "); + sb.Append (ToSignature (pi.DeclaringType)); + sb.Append ("."); + sb.Append (pi.Name); + sb.Append (ToSignature ('[', pi.GetIndexParameters (), ']')); + return sb.ToString (); + } + + internal static string ToSignature (FieldInfo fi) + { + StringBuilder sb = new StringBuilder (); + sb.Append (ToSignature (fi.FieldType)); + sb.Append (" "); + sb.Append (ToSignature (fi.DeclaringType)); + sb.Append ("."); + sb.Append (fi.Name); + return sb.ToString (); + } + + internal static string ToSignature (object [] args) + { + StringBuilder sb = new StringBuilder (); + for (int i = 0; i < args.Length; i++) { + if (i > 0) + sb.Append (", "); + sb.Append (ToSignature (args [0].GetType ())); + } + return sb.ToString (); + } + + internal static string ToSignature (MemberInfo mi) + { + if (mi is PropertyInfo) + return ToSignature ((PropertyInfo)mi); + if (mi is FieldInfo) + return ToSignature ((FieldInfo)mi); + if (mi is ConstructorInfo) + return ToSignature ((ConstructorInfo)mi); + if (mi is MethodInfo) + return ToSignature ((MethodInfo)mi); + return "[unknown: " + mi.GetType ().FullName + "]"; + } + + internal static string ToSignature (char parenOpen, ParameterInfo [] pi, char parenClose) + { + StringBuilder sb = new StringBuilder (); + sb.Append (parenOpen); + for (int i = 0; i < pi.Length; i++) { + if (i > 0) + sb.Append (", "); + if (pi [i].IsOut) + sb.Append ("out "); + if (pi [i].IsIn) + sb.Append ("in "); + if (IsParamsParameter (pi [i])) + sb.Append ("params "); + sb.Append (ToSignature (pi [i].ParameterType)); + sb.Append (" "); + sb.Append (pi [i].Name); + } + sb.Append (parenClose); + return sb.ToString (); + } + + + internal static string ToSignature (MethodInfo mi) + { + StringBuilder sb = new StringBuilder (); + sb.Append (ToSignature (mi.ReturnType)); + sb.Append (" "); + sb.Append (ToSignature (mi.DeclaringType)); + sb.Append ("."); + sb.Append (mi.Name); + sb.Append (ToSignature ('(', mi.GetParameters (), ')')); + return sb.ToString (); + } + + internal static bool HasParamsParameter (MethodBase mb) + { + return HasParamsParameter (mb.GetParameters ()); + } + + internal static bool HasParamsParameter (ParameterInfo [] pis) + { + for (int i = 0; i < pis.Length; i++) + if (IsParamsParameter (pis [i])) + return true; + return false; + } + + internal static bool IsParamsParameter (ParameterInfo pi) + { + ParamArrayAttribute attr = (ParamArrayAttribute) + CliHelper.GetCustomAttribute (typeof (ParamArrayAttribute), pi); + return (attr != null); + } + + + internal static string ToSignature (Type type) + { + if (type.IsArray) { + string ret = ToSignature (type.GetElementType ()); + for (int i = 0; i < type.GetArrayRank (); i++) + ret += "[]"; + return ret; + } + if (type == typeof (short)) + return "short"; + if (type == typeof (ushort)) + return "ushort"; + if (type == typeof (int)) + return "int"; + if (type == typeof (uint)) + return "uint"; + if (type == typeof (ulong)) + return "ulong"; + if (type == typeof (long)) + return "long"; + if (type == typeof (void)) + return "void"; + if (type == typeof (bool)) + return "bool"; + if (type == typeof (double)) + return "double"; + if (type == typeof (decimal)) + return "decimal"; + if (type == typeof (object)) + return "object"; + return type.FullName; + } + + internal static bool IsNumberType (Type type) + { + return ( + type == typeof (Int16) + || type == typeof (UInt16) + || type == typeof (Int32) + || type == typeof (UInt32) + || type == typeof (Int64) + || type == typeof (UInt64) + || type == typeof (Single) + || type == typeof (Double) + || type == typeof (Decimal)); + } + + internal static bool IsNumber (object value) + { + return ( + value is Int16 + || value is UInt16 + || value is Int32 + || value is UInt32 + || value is Int64 + || value is UInt64 + || value is Single + || value is Double + || value is Decimal); + } + + internal static object CreateInstance (Type cl) + { + try { + return System.Activator.CreateInstance (cl); + } + catch { + ; + } + return null; + } + + internal static object GetCustomAttribute (Type type, Type attribute) + { + object [] attributes = type.GetCustomAttributes (attribute, true); + if (attributes.Length < 1) + return null; + return attributes [0]; + } + + internal static object GetCustomAttribute (Type type, MemberInfo mi) + { + object attribute = null; + object [] attributes = mi.GetCustomAttributes (type, true); + if (attributes.Length > 0) + attribute = attributes [0]; + return attribute; + } + + internal static object GetCustomAttribute (Type type, ParameterInfo pi) + { + object [] attributes = pi.GetCustomAttributes (type, true); + if (attributes.Length < 1) + return null; + return attributes [0]; + } + + internal static Type [] GetParameterTypes (ParameterInfo [] parameters) + { + Type [] types = new Type [parameters.Length]; + for (int i = 0; i < types.Length; i++) { + types [i] = parameters [i].ParameterType; + } + return types; + } + + } + +} diff --git a/Code/EcmaScript.NET/Helpers/StackOverflowVerifier.cs b/src/EcmaScript.NET/Helpers/StackOverflowVerifier.cs similarity index 95% rename from Code/EcmaScript.NET/Helpers/StackOverflowVerifier.cs rename to src/EcmaScript.NET/Helpers/StackOverflowVerifier.cs index 3ae9a6f..a28a48f 100644 --- a/Code/EcmaScript.NET/Helpers/StackOverflowVerifier.cs +++ b/src/EcmaScript.NET/Helpers/StackOverflowVerifier.cs @@ -1,39 +1,39 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace EcmaScript.NET.Helpers -{ - - public class StackOverflowVerifier : IDisposable - { - - [ThreadStatic] - private static long m_Counter = long.MinValue; - - private int m_MaxStackSize = 0; - - public StackOverflowVerifier (int maxStackSize) - { - m_MaxStackSize = maxStackSize; - - ChangeStackDepth (+1); - } - - public void Dispose () - { - ChangeStackDepth (-1); - } - - void ChangeStackDepth (int offset) - { - if (m_Counter == long.MinValue) - m_Counter = 0; - m_Counter += offset; - if (m_Counter > m_MaxStackSize) - throw new StackOverflowVerifierException (); - } - - } - -} +using System; +using System.Collections.Generic; +using System.Text; + +namespace EcmaScript.NET.Helpers +{ + + public class StackOverflowVerifier : IDisposable + { + + [ThreadStatic] + private static long m_Counter = long.MinValue; + + private int m_MaxStackSize = 0; + + public StackOverflowVerifier (int maxStackSize) + { + m_MaxStackSize = maxStackSize; + + ChangeStackDepth (+1); + } + + public void Dispose () + { + ChangeStackDepth (-1); + } + + void ChangeStackDepth (int offset) + { + if (m_Counter == long.MinValue) + m_Counter = 0; + m_Counter += offset; + if (m_Counter > m_MaxStackSize) + throw new StackOverflowVerifierException (); + } + + } + +} diff --git a/Code/EcmaScript.NET/Helpers/StackOverflowVerifierException.cs b/src/EcmaScript.NET/Helpers/StackOverflowVerifierException.cs similarity index 64% rename from Code/EcmaScript.NET/Helpers/StackOverflowVerifierException.cs rename to src/EcmaScript.NET/Helpers/StackOverflowVerifierException.cs index e7fdbe6..adc29ec 100644 --- a/Code/EcmaScript.NET/Helpers/StackOverflowVerifierException.cs +++ b/src/EcmaScript.NET/Helpers/StackOverflowVerifierException.cs @@ -1,10 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace EcmaScript.NET.Helpers -{ - class StackOverflowVerifierException : ApplicationException - { - } -} +using System; +using System.Collections.Generic; +using System.Text; + +namespace EcmaScript.NET.Helpers +{ + class StackOverflowVerifierException : Exception + { + } +} diff --git a/Code/EcmaScript.NET/ICallable.cs b/src/EcmaScript.NET/ICallable.cs similarity index 97% rename from Code/EcmaScript.NET/ICallable.cs rename to src/EcmaScript.NET/ICallable.cs index ce66138..81ae0c8 100644 --- a/Code/EcmaScript.NET/ICallable.cs +++ b/src/EcmaScript.NET/ICallable.cs @@ -1,39 +1,39 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; - -namespace EcmaScript.NET -{ - - /// Generic notion of callable object that can execute some script-related code - /// upon request with specified values for script scope and this objects. - /// - public interface ICallable - { - /// Perform the call. - /// - /// - /// the current Context for this thread - /// - /// the scope to use to resolve properties. - /// - /// the JavaScript this object - /// - /// the array of arguments - /// - /// the result of the call - /// - object Call (Context cx, IScriptable scope, IScriptable thisObj, object [] args); - } +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; + +namespace EcmaScript.NET +{ + + /// Generic notion of callable object that can execute some script-related code + /// upon request with specified values for script scope and this objects. + /// + public interface ICallable + { + /// Perform the call. + /// + /// + /// the current Context for this thread + /// + /// the scope to use to resolve properties. + /// + /// the JavaScript this object + /// + /// the array of arguments + /// + /// the result of the call + /// + object Call (Context cx, IScriptable scope, IScriptable thisObj, object [] args); + } } \ No newline at end of file diff --git a/Code/EcmaScript.NET/IFunction.cs b/src/EcmaScript.NET/IFunction.cs similarity index 97% rename from Code/EcmaScript.NET/IFunction.cs rename to src/EcmaScript.NET/IFunction.cs index 6060edb..bf14607 100644 --- a/Code/EcmaScript.NET/IFunction.cs +++ b/src/EcmaScript.NET/IFunction.cs @@ -1,44 +1,44 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; - -namespace EcmaScript.NET -{ - - /// This is interface that all functions in JavaScript must implement. - /// The interface provides for calling functions and constructors. - /// - /// - public interface IFunction : IScriptable, ICallable - { - - /// Call the function as a constructor. - /// - /// This method is invoked by the runtime in order to satisfy a use - /// of the JavaScript new operator. This method is - /// expected to create a new object and return it. - /// - /// - /// the current Context for this thread - /// - /// an enclosing scope of the caller except - /// when the function is called from a closure. - /// - /// the array of arguments - /// - /// the allocated object - /// - IScriptable Construct (Context cx, IScriptable scope, object [] args); - } +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; + +namespace EcmaScript.NET +{ + + /// This is interface that all functions in JavaScript must implement. + /// The interface provides for calling functions and constructors. + /// + /// + public interface IFunction : IScriptable, ICallable + { + + /// Call the function as a constructor. + /// + /// This method is invoked by the runtime in order to satisfy a use + /// of the JavaScript new operator. This method is + /// expected to create a new object and return it. + /// + /// + /// the current Context for this thread + /// + /// an enclosing scope of the caller except + /// when the function is called from a closure. + /// + /// the array of arguments + /// + /// the allocated object + /// + IScriptable Construct (Context cx, IScriptable scope, object [] args); + } } \ No newline at end of file diff --git a/Code/EcmaScript.NET/IIdEnumerable.cs b/src/EcmaScript.NET/IIdEnumerable.cs similarity index 96% rename from Code/EcmaScript.NET/IIdEnumerable.cs rename to src/EcmaScript.NET/IIdEnumerable.cs index a841d2a..7261c9b 100644 --- a/Code/EcmaScript.NET/IIdEnumerable.cs +++ b/src/EcmaScript.NET/IIdEnumerable.cs @@ -1,27 +1,27 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ -using System; - -namespace EcmaScript.NET -{ - - /// - /// Summary description for IdEnumerable. - /// - internal interface IIdEnumerable - { - - IdEnumeration GetEnumeration (Context cx, bool enumValues); - - } -} +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ +using System; + +namespace EcmaScript.NET +{ + + /// + /// Summary description for IdEnumerable. + /// + internal interface IIdEnumerable + { + + IdEnumeration GetEnumeration (Context cx, bool enumValues); + + } +} diff --git a/Code/EcmaScript.NET/IIdFunctionCall.cs b/src/EcmaScript.NET/IIdFunctionCall.cs similarity index 97% rename from Code/EcmaScript.NET/IIdFunctionCall.cs rename to src/EcmaScript.NET/IIdFunctionCall.cs index ff07c09..93736f9 100644 --- a/Code/EcmaScript.NET/IIdFunctionCall.cs +++ b/src/EcmaScript.NET/IIdFunctionCall.cs @@ -1,34 +1,34 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; - -namespace EcmaScript.NET -{ - - /// - /// Master for id-based functions that knows their properties and how to - /// execute them. - /// - public interface IIdFunctionCall - { - - /// - /// 'thisObj' will be null if invoked as constructor, in which case - /// instance of Scriptable should be returned - /// - object ExecIdCall (IdFunctionObject f, Context cx, IScriptable scope, IScriptable thisObj, object [] args); - - } - +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; + +namespace EcmaScript.NET +{ + + /// + /// Master for id-based functions that knows their properties and how to + /// execute them. + /// + public interface IIdFunctionCall + { + + /// + /// 'thisObj' will be null if invoked as constructor, in which case + /// instance of Scriptable should be returned + /// + object ExecIdCall (IdFunctionObject f, Context cx, IScriptable scope, IScriptable thisObj, object [] args); + + } + } \ No newline at end of file diff --git a/Code/EcmaScript.NET/IRef.cs b/src/EcmaScript.NET/IRef.cs similarity index 96% rename from Code/EcmaScript.NET/IRef.cs rename to src/EcmaScript.NET/IRef.cs index 3729177..36f0510 100644 --- a/Code/EcmaScript.NET/IRef.cs +++ b/src/EcmaScript.NET/IRef.cs @@ -1,34 +1,34 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; - -namespace EcmaScript.NET -{ - - /// Generic notion of reference object that know how to query/modify the - /// target objects based on some property/index. - /// - - public interface IRef - { - bool Has (Context cx); - - object Get (Context cx); - - object Set (Context cx, object value); - - bool Delete (Context cx); - - } +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; + +namespace EcmaScript.NET +{ + + /// Generic notion of reference object that know how to query/modify the + /// target objects based on some property/index. + /// + + public interface IRef + { + bool Has (Context cx); + + object Get (Context cx); + + object Set (Context cx, object value); + + bool Delete (Context cx); + + } } \ No newline at end of file diff --git a/Code/EcmaScript.NET/IRefCallable.cs b/src/EcmaScript.NET/IRefCallable.cs similarity index 97% rename from Code/EcmaScript.NET/IRefCallable.cs rename to src/EcmaScript.NET/IRefCallable.cs index 7300ca0..2e676e1 100644 --- a/Code/EcmaScript.NET/IRefCallable.cs +++ b/src/EcmaScript.NET/IRefCallable.cs @@ -1,39 +1,39 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; - -namespace EcmaScript.NET -{ - - /// - /// Object that can allows assignments to the result of function calls. - /// - public interface IRefCallable : ICallable - { - /// Perform function call in reference context. - /// The args array reference should not be stored in any object that is - /// can be GC-reachable after this method returns. If this is necessary, - /// for example, to implement {@link Ref} methods, then store args.clone(), - /// not args array itself. - /// - /// - /// the current Context for this thread - /// - /// the JavaScript this object - /// - /// the array of arguments - /// - IRef RefCall (Context cx, IScriptable thisObj, object [] args); - } +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; + +namespace EcmaScript.NET +{ + + /// + /// Object that can allows assignments to the result of function calls. + /// + public interface IRefCallable : ICallable + { + /// Perform function call in reference context. + /// The args array reference should not be stored in any object that is + /// can be GC-reachable after this method returns. If this is necessary, + /// for example, to implement {@link Ref} methods, then store args.clone(), + /// not args array itself. + /// + /// + /// the current Context for this thread + /// + /// the JavaScript this object + /// + /// the array of arguments + /// + IRef RefCall (Context cx, IScriptable thisObj, object [] args); + } } \ No newline at end of file diff --git a/Code/EcmaScript.NET/IScript.cs b/src/EcmaScript.NET/IScript.cs similarity index 97% rename from Code/EcmaScript.NET/IScript.cs rename to src/EcmaScript.NET/IScript.cs index bab0195..124b23c 100644 --- a/Code/EcmaScript.NET/IScript.cs +++ b/src/EcmaScript.NET/IScript.cs @@ -1,47 +1,47 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; - -namespace EcmaScript.NET -{ - - /// All compiled scripts implement this interface. - ///

- /// This class encapsulates script execution relative to an - /// object scope. - ///

- public interface IScript - { - - /// Execute the script. - ///

- /// The script is executed in a particular runtime Context, which - /// must be associated with the current thread. - /// The script is executed relative to a scope--definitions and - /// uses of global top-level variables and functions will access - /// properties of the scope object. For compliant ECMA - /// programs, the scope must be an object that has been initialized - /// as a global object using Context.initStandardObjects. - ///

- /// - ///

- /// the Context associated with the current thread - /// - /// the scope to execute relative to - /// - /// the result of executing the script - /// - object Exec (Context cx, IScriptable scope); - } +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; + +namespace EcmaScript.NET +{ + + /// All compiled scripts implement this interface. + ///

+ /// This class encapsulates script execution relative to an + /// object scope. + ///

+ public interface IScript + { + + /// Execute the script. + ///

+ /// The script is executed in a particular runtime Context, which + /// must be associated with the current thread. + /// The script is executed relative to a scope--definitions and + /// uses of global top-level variables and functions will access + /// properties of the scope object. For compliant ECMA + /// programs, the scope must be an object that has been initialized + /// as a global object using Context.initStandardObjects. + ///

+ /// + ///

+ /// the Context associated with the current thread + /// + /// the scope to execute relative to + /// + /// the result of executing the script + /// + object Exec (Context cx, IScriptable scope); + } } \ No newline at end of file diff --git a/Code/EcmaScript.NET/IScriptable.cs b/src/EcmaScript.NET/IScriptable.cs similarity index 97% rename from Code/EcmaScript.NET/IScriptable.cs rename to src/EcmaScript.NET/IScriptable.cs index 5ec92d8..5523082 100644 --- a/Code/EcmaScript.NET/IScriptable.cs +++ b/src/EcmaScript.NET/IScriptable.cs @@ -1,306 +1,306 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -// API class -using System; - -namespace EcmaScript.NET -{ - - /// - /// This is interface that all objects in JavaScript must implement. - /// The interface provides for the management of properties and for - /// performing conversions. - ///

- /// Host system implementors may find it easier to extend the ScriptableObject - /// class rather than implementing Scriptable when writing host objects. - ///

- /// There are many static methods defined in ScriptableObject that perform - /// the multiple calls to the Scriptable interface needed in order to - /// manipulate properties in prototype chains. - ///

- /// - ///

- public interface IScriptable - { - - /// Get the name of the set of objects implemented by this Java class. - /// This corresponds to the [[Class]] operation in ECMA and is used - /// by Object.prototype.toString() in ECMA.

- /// See ECMA 8.6.2 and 15.2.4.2. - ///

- string ClassName - { - get; - - } - - /// Get the parent scope of the object. - /// the parent scope - /// - /// Set the parent scope of the object. - /// the parent scope to set - /// - IScriptable ParentScope - { - get; - set; - } - - /// Get a named property from the object. - /// - /// Looks property up in this object and returns the associated value - /// if found. Returns NOT_FOUND if not found. - /// Note that this method is not expected to traverse the prototype - /// chain. This is different from the ECMA [[Get]] operation. - /// - /// Depending on the property selector, the runtime will call - /// this method or the form of get that takes an - /// integer: - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - ///
JavaScript codeJava code
a.b a.get("b", a)
a["foo"] a.get("foo", a)
a[3] a.get(3, a)
a["3"] a.get(3, a)
a[3.0] a.get(3, a)
a["3.0"] a.get("3.0", a)
a[1.1] a.get("1.1", a)
a[-4] a.get(-4, a)
- ///

- /// The values that may be returned are limited to the following: - ///

    - ///
  • java.lang.Boolean objects
  • - ///
  • java.lang.String objects
  • - ///
  • java.lang.Number objects
  • - ///
  • EcmaScript.NET.Scriptable objects
  • - ///
  • null
  • - ///
  • The value returned by Context.getUndefinedValue()
  • - ///
  • NOT_FOUND
  • - ///
- ///
- /// the name of the property - /// - /// the object in which the lookup began - /// - /// the value of the property (may be null), or NOT_FOUND - /// - object Get (string name, IScriptable start); - - /// Get a property from the object selected by an integral index. - /// - /// Identical to get(String, Scriptable) except that - /// an integral index is used to select the property. - /// - /// - /// the numeric index for the property - /// - /// the object in which the lookup began - /// - /// the value of the property (may be null), or NOT_FOUND - /// - object Get (int index, IScriptable start); - - /// Indicates whether or not a named property is defined in an object. - /// - /// Does not traverse the prototype chain.

- /// - /// The property is specified by a String name - /// as defined for the get method.

- /// - ///

- /// the name of the property - /// - /// the object in which the lookup began - /// - /// true if and only if the named property is found in the object - /// - bool Has (string name, IScriptable start); - - /// Indicates whether or not an indexed property is defined in an object. - /// - /// Does not traverse the prototype chain.

- /// - /// The property is specified by an integral index - /// as defined for the get method.

- /// - ///

- /// the numeric index for the property - /// - /// the object in which the lookup began - /// - /// true if and only if the indexed property is found in the object - /// - bool Has (int index, IScriptable start); - - /// Sets a named property in this object. - ///

- /// The property is specified by a string name - /// as defined for get. - ///

- /// The possible values that may be passed in are as defined for - /// get. A class that implements this method may choose - /// to ignore calls to set certain properties, in which case those - /// properties are effectively read-only.

- /// For properties defined in a prototype chain, - /// use putProperty in ScriptableObject.

- /// Note that if a property a is defined in the prototype p - /// of an object o, then evaluating o.a = 23 will cause - /// set to be called on the prototype p with - /// o as the start parameter. - /// To preserve JavaScript semantics, it is the Scriptable - /// object's responsibility to modify o.

- /// This design allows properties to be defined in prototypes and implemented - /// in terms of getters and setters of Java values without consuming slots - /// in each instance.

- ///

- /// The values that may be set are limited to the following: - ///

    - ///
  • java.lang.Boolean objects
  • - ///
  • java.lang.String objects
  • - ///
  • java.lang.Number objects
  • - ///
  • EcmaScript.NET.Scriptable objects
  • - ///
  • null
  • - ///
  • The value returned by Context.getUndefinedValue()
  • - ///

- /// Arbitrary Java objects may be wrapped in a Scriptable by first calling - /// Context.toObject. This allows the property of a JavaScript - /// object to contain an arbitrary Java object as a value.

- /// Note that has will be called by the runtime first before - /// set is called to determine in which object the - /// property is defined. - /// Note that this method is not expected to traverse the prototype chain, - /// which is different from the ECMA [[Put]] operation. - ///

- /// the name of the property - /// - /// the object whose property is being set - /// - /// value to set the property to - /// - object Put (string name, IScriptable start, object value); - - /// Sets an indexed property in this object. - ///

- /// The property is specified by an integral index - /// as defined for get.

- /// - /// Identical to put(String, Scriptable, Object) except that - /// an integral index is used to select the property. - /// - ///

- /// the numeric index for the property - /// - /// the object whose property is being set - /// - /// value to set the property to - /// - object Put (int index, IScriptable start, object value); - - /// Removes a property from this object. - /// This operation corresponds to the ECMA [[Delete]] except that - /// the no result is returned. The runtime will guarantee that this - /// method is called only if the property exists. After this method - /// is called, the runtime will call Scriptable.has to see if the - /// property has been removed in order to determine the boolean - /// result of the delete operator as defined by ECMA 11.4.1. - ///

- /// A property can be made permanent by ignoring calls to remove - /// it.

- /// The property is specified by a String name - /// as defined for get. - ///

- /// To delete properties defined in a prototype chain, - /// see deleteProperty in ScriptableObject. - ///

- /// the identifier for the property - /// - void Delete (string name); - - /// Removes a property from this object. - /// - /// The property is specified by an integral index - /// as defined for get. - ///

- /// To delete properties defined in a prototype chain, - /// see deleteProperty in ScriptableObject. - /// - /// Identical to delete(String) except that - /// an integral index is used to select the property. - /// - ///

- /// the numeric index for the property - /// - void Delete (int index); - - /// Get the prototype of the object. - /// the prototype - /// - IScriptable GetPrototype (); - - /// Set the prototype of the object. - /// the prototype to set - /// - void SetPrototype (IScriptable prototype); - - /// Get an array of property ids. - /// - /// Not all property ids need be returned. Those properties - /// whose ids are not returned are considered non-enumerable. - /// - /// - /// an array of Objects. Each entry in the array is either - /// a java.lang.String or a java.lang.Number - /// - object [] GetIds (); - - /// Get the default value of the object with a given hint. - /// The hints are String.class for type String, Number.class for type - /// Number, Scriptable.class for type Object, and Boolean.class for - /// type Boolean.

- /// - /// A hint of null means "no hint". - /// - /// See ECMA 8.6.2.6. - /// - ///

- /// the type hint - /// - /// the default value - /// - object GetDefaultValue (Type hint); - - /// The instanceof operator. - /// - ///

- /// The JavaScript code "lhs instanceof rhs" causes rhs.hasInstance(lhs) to - /// be called. - /// - ///

- /// The return value is implementation dependent so that embedded host objects can - /// return an appropriate value. See the JS 1.3 language documentation for more - /// detail. - /// - ///

This operator corresponds to the proposed EMCA [[HasInstance]] operator. - /// - ///

- /// The value that appeared on the LHS of the instanceof - /// operator - /// - /// - /// an implementation dependent value - /// - bool HasInstance (IScriptable instance); - } +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +// API class +using System; + +namespace EcmaScript.NET +{ + + /// + /// This is interface that all objects in JavaScript must implement. + /// The interface provides for the management of properties and for + /// performing conversions. + ///

+ /// Host system implementors may find it easier to extend the ScriptableObject + /// class rather than implementing Scriptable when writing host objects. + ///

+ /// There are many static methods defined in ScriptableObject that perform + /// the multiple calls to the Scriptable interface needed in order to + /// manipulate properties in prototype chains. + ///

+ /// + ///

+ public interface IScriptable + { + + /// Get the name of the set of objects implemented by this Java class. + /// This corresponds to the [[Class]] operation in ECMA and is used + /// by Object.prototype.toString() in ECMA.

+ /// See ECMA 8.6.2 and 15.2.4.2. + ///

+ string ClassName + { + get; + + } + + /// Get the parent scope of the object. + /// the parent scope + /// + /// Set the parent scope of the object. + /// the parent scope to set + /// + IScriptable ParentScope + { + get; + set; + } + + /// Get a named property from the object. + /// + /// Looks property up in this object and returns the associated value + /// if found. Returns NOT_FOUND if not found. + /// Note that this method is not expected to traverse the prototype + /// chain. This is different from the ECMA [[Get]] operation. + /// + /// Depending on the property selector, the runtime will call + /// this method or the form of get that takes an + /// integer: + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + ///
JavaScript codeJava code
a.b a.get("b", a)
a["foo"] a.get("foo", a)
a[3] a.get(3, a)
a["3"] a.get(3, a)
a[3.0] a.get(3, a)
a["3.0"] a.get("3.0", a)
a[1.1] a.get("1.1", a)
a[-4] a.get(-4, a)
+ ///

+ /// The values that may be returned are limited to the following: + ///

    + ///
  • java.lang.Boolean objects
  • + ///
  • java.lang.String objects
  • + ///
  • java.lang.Number objects
  • + ///
  • EcmaScript.NET.Scriptable objects
  • + ///
  • null
  • + ///
  • The value returned by Context.getUndefinedValue()
  • + ///
  • NOT_FOUND
  • + ///
+ ///
+ /// the name of the property + /// + /// the object in which the lookup began + /// + /// the value of the property (may be null), or NOT_FOUND + /// + object Get (string name, IScriptable start); + + /// Get a property from the object selected by an integral index. + /// + /// Identical to get(String, Scriptable) except that + /// an integral index is used to select the property. + /// + /// + /// the numeric index for the property + /// + /// the object in which the lookup began + /// + /// the value of the property (may be null), or NOT_FOUND + /// + object Get (int index, IScriptable start); + + /// Indicates whether or not a named property is defined in an object. + /// + /// Does not traverse the prototype chain.

+ /// + /// The property is specified by a String name + /// as defined for the get method.

+ /// + ///

+ /// the name of the property + /// + /// the object in which the lookup began + /// + /// true if and only if the named property is found in the object + /// + bool Has (string name, IScriptable start); + + /// Indicates whether or not an indexed property is defined in an object. + /// + /// Does not traverse the prototype chain.

+ /// + /// The property is specified by an integral index + /// as defined for the get method.

+ /// + ///

+ /// the numeric index for the property + /// + /// the object in which the lookup began + /// + /// true if and only if the indexed property is found in the object + /// + bool Has (int index, IScriptable start); + + /// Sets a named property in this object. + ///

+ /// The property is specified by a string name + /// as defined for get. + ///

+ /// The possible values that may be passed in are as defined for + /// get. A class that implements this method may choose + /// to ignore calls to set certain properties, in which case those + /// properties are effectively read-only.

+ /// For properties defined in a prototype chain, + /// use putProperty in ScriptableObject.

+ /// Note that if a property a is defined in the prototype p + /// of an object o, then evaluating o.a = 23 will cause + /// set to be called on the prototype p with + /// o as the start parameter. + /// To preserve JavaScript semantics, it is the Scriptable + /// object's responsibility to modify o.

+ /// This design allows properties to be defined in prototypes and implemented + /// in terms of getters and setters of Java values without consuming slots + /// in each instance.

+ ///

+ /// The values that may be set are limited to the following: + ///

    + ///
  • java.lang.Boolean objects
  • + ///
  • java.lang.String objects
  • + ///
  • java.lang.Number objects
  • + ///
  • EcmaScript.NET.Scriptable objects
  • + ///
  • null
  • + ///
  • The value returned by Context.getUndefinedValue()
  • + ///

+ /// Arbitrary Java objects may be wrapped in a Scriptable by first calling + /// Context.toObject. This allows the property of a JavaScript + /// object to contain an arbitrary Java object as a value.

+ /// Note that has will be called by the runtime first before + /// set is called to determine in which object the + /// property is defined. + /// Note that this method is not expected to traverse the prototype chain, + /// which is different from the ECMA [[Put]] operation. + ///

+ /// the name of the property + /// + /// the object whose property is being set + /// + /// value to set the property to + /// + object Put (string name, IScriptable start, object value); + + /// Sets an indexed property in this object. + ///

+ /// The property is specified by an integral index + /// as defined for get.

+ /// + /// Identical to put(String, Scriptable, Object) except that + /// an integral index is used to select the property. + /// + ///

+ /// the numeric index for the property + /// + /// the object whose property is being set + /// + /// value to set the property to + /// + object Put (int index, IScriptable start, object value); + + /// Removes a property from this object. + /// This operation corresponds to the ECMA [[Delete]] except that + /// the no result is returned. The runtime will guarantee that this + /// method is called only if the property exists. After this method + /// is called, the runtime will call Scriptable.has to see if the + /// property has been removed in order to determine the boolean + /// result of the delete operator as defined by ECMA 11.4.1. + ///

+ /// A property can be made permanent by ignoring calls to remove + /// it.

+ /// The property is specified by a String name + /// as defined for get. + ///

+ /// To delete properties defined in a prototype chain, + /// see deleteProperty in ScriptableObject. + ///

+ /// the identifier for the property + /// + void Delete (string name); + + /// Removes a property from this object. + /// + /// The property is specified by an integral index + /// as defined for get. + ///

+ /// To delete properties defined in a prototype chain, + /// see deleteProperty in ScriptableObject. + /// + /// Identical to delete(String) except that + /// an integral index is used to select the property. + /// + ///

+ /// the numeric index for the property + /// + void Delete (int index); + + /// Get the prototype of the object. + /// the prototype + /// + IScriptable GetPrototype (); + + /// Set the prototype of the object. + /// the prototype to set + /// + void SetPrototype (IScriptable prototype); + + /// Get an array of property ids. + /// + /// Not all property ids need be returned. Those properties + /// whose ids are not returned are considered non-enumerable. + /// + /// + /// an array of Objects. Each entry in the array is either + /// a java.lang.String or a java.lang.Number + /// + object [] GetIds (); + + /// Get the default value of the object with a given hint. + /// The hints are String.class for type String, Number.class for type + /// Number, Scriptable.class for type Object, and Boolean.class for + /// type Boolean.

+ /// + /// A hint of null means "no hint". + /// + /// See ECMA 8.6.2.6. + /// + ///

+ /// the type hint + /// + /// the default value + /// + object GetDefaultValue (Type hint); + + /// The instanceof operator. + /// + ///

+ /// The JavaScript code "lhs instanceof rhs" causes rhs.hasInstance(lhs) to + /// be called. + /// + ///

+ /// The return value is implementation dependent so that embedded host objects can + /// return an appropriate value. See the JS 1.3 language documentation for more + /// detail. + /// + ///

This operator corresponds to the proposed EMCA [[HasInstance]] operator. + /// + ///

+ /// The value that appeared on the LHS of the instanceof + /// operator + /// + /// + /// an implementation dependent value + /// + bool HasInstance (IScriptable instance); + } } \ No newline at end of file diff --git a/Code/EcmaScript.NET/IdEnumeration.cs b/src/EcmaScript.NET/IdEnumeration.cs similarity index 96% rename from Code/EcmaScript.NET/IdEnumeration.cs rename to src/EcmaScript.NET/IdEnumeration.cs index e80f5e2..2f069a4 100644 --- a/Code/EcmaScript.NET/IdEnumeration.cs +++ b/src/EcmaScript.NET/IdEnumeration.cs @@ -1,144 +1,144 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ -using System; - -using EcmaScript.NET.Collections; - -namespace EcmaScript.NET -{ - - /// - /// This is the enumeration needed by the for..in statement. - /// - /// See ECMA 12.6.3. - /// - /// IdEnumeration maintains a ObjToIntMap to make sure a given - /// id is enumerated only once across multiple objects in a - /// prototype chain. - /// - /// ECMA delete doesn't hide properties in the prototype, - /// but js/ref does. This means that the js/ref for..in can - /// avoid maintaining a hash table and instead perform lookups - /// to see if a given property has already been enumerated. - /// - /// - public class IdEnumeration - { - - protected IdEnumeration () - { - ; - } - - public IdEnumeration (object value, Context cx, bool enumValues) - { - obj = ScriptConvert.ToObjectOrNull (cx, value); - if (obj != null) { - // null or undefined do not cause errors but rather lead to empty - // "for in" loop - this.enumValues = enumValues; - - // enumInit should read all initial ids before returning - // or "for (a.i in a)" would wrongly enumerate i in a as well - ChangeObject (); - } - } - - private IScriptable obj; - private object [] ids; - private int index; - private ObjToIntMap used; - private string currentId; - private bool enumValues; - - public virtual bool MoveNext () - { - // OPT this could be more efficient - bool result; - for (; ; ) { - if (obj == null) { - result = false; - break; - } - if (index == ids.Length) { - obj = obj.GetPrototype (); - this.ChangeObject (); - continue; - } - object id = ids [index++]; - if (used != null && used.has (id)) { - continue; - } - if (id is string) { - string strId = (string)id; - if (!obj.Has (strId, obj)) - continue; // must have been deleted - currentId = strId; - } - else { - int intId = Convert.ToInt32 (id); - if (!obj.Has (intId, obj)) - continue; // must have been deleted - currentId = Convert.ToString (intId); - } - result = true; - break; - } - return result; - } - - public virtual object Current (Context cx) - { - if (!enumValues) - return currentId; - - object result; - - string s = ScriptRuntime.ToStringIdOrIndex (cx, currentId); - if (s == null) { - int index = ScriptRuntime.lastIndexResult (cx); - result = obj.Get (index, obj); - } - else { - result = obj.Get (s, obj); - } - - return result; - } - - private void ChangeObject () - { - object [] ids = null; - while (obj != null) { - ids = obj.GetIds (); - if (ids.Length != 0) { - break; - } - obj = obj.GetPrototype (); - } - if (obj != null && this.ids != null) { - object [] previous = this.ids; - int L = previous.Length; - if (used == null) { - used = new ObjToIntMap (L); - } - for (int i = 0; i != L; ++i) { - used.intern (previous [i]); - } - } - this.ids = ids; - this.index = 0; - } - - } -} +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ +using System; + +using EcmaScript.NET.Collections; + +namespace EcmaScript.NET +{ + + /// + /// This is the enumeration needed by the for..in statement. + /// + /// See ECMA 12.6.3. + /// + /// IdEnumeration maintains a ObjToIntMap to make sure a given + /// id is enumerated only once across multiple objects in a + /// prototype chain. + /// + /// ECMA delete doesn't hide properties in the prototype, + /// but js/ref does. This means that the js/ref for..in can + /// avoid maintaining a hash table and instead perform lookups + /// to see if a given property has already been enumerated. + /// + /// + public class IdEnumeration + { + + protected IdEnumeration () + { + ; + } + + public IdEnumeration (object value, Context cx, bool enumValues) + { + obj = ScriptConvert.ToObjectOrNull (cx, value); + if (obj != null) { + // null or undefined do not cause errors but rather lead to empty + // "for in" loop + this.enumValues = enumValues; + + // enumInit should read all initial ids before returning + // or "for (a.i in a)" would wrongly enumerate i in a as well + ChangeObject (); + } + } + + private IScriptable obj; + private object [] ids; + private int index; + private ObjToIntMap used; + private string currentId; + private bool enumValues; + + public virtual bool MoveNext () + { + // OPT this could be more efficient + bool result; + for (; ; ) { + if (obj == null) { + result = false; + break; + } + if (index == ids.Length) { + obj = obj.GetPrototype (); + this.ChangeObject (); + continue; + } + object id = ids [index++]; + if (used != null && used.has (id)) { + continue; + } + if (id is string) { + string strId = (string)id; + if (!obj.Has (strId, obj)) + continue; // must have been deleted + currentId = strId; + } + else { + int intId = Convert.ToInt32 (id); + if (!obj.Has (intId, obj)) + continue; // must have been deleted + currentId = Convert.ToString (intId); + } + result = true; + break; + } + return result; + } + + public virtual object Current (Context cx) + { + if (!enumValues) + return currentId; + + object result; + + string s = ScriptRuntime.ToStringIdOrIndex (cx, currentId); + if (s == null) { + int index = ScriptRuntime.lastIndexResult (cx); + result = obj.Get (index, obj); + } + else { + result = obj.Get (s, obj); + } + + return result; + } + + private void ChangeObject () + { + object [] ids = null; + while (obj != null) { + ids = obj.GetIds (); + if (ids.Length != 0) { + break; + } + obj = obj.GetPrototype (); + } + if (obj != null && this.ids != null) { + object [] previous = this.ids; + int L = previous.Length; + if (used == null) { + used = new ObjToIntMap (L); + } + for (int i = 0; i != L; ++i) { + used.intern (previous [i]); + } + } + this.ids = ids; + this.index = 0; + } + + } +} diff --git a/Code/EcmaScript.NET/IdFunctionObject.cs b/src/EcmaScript.NET/IdFunctionObject.cs similarity index 94% rename from Code/EcmaScript.NET/IdFunctionObject.cs rename to src/EcmaScript.NET/IdFunctionObject.cs index 96def15..ba33259 100644 --- a/Code/EcmaScript.NET/IdFunctionObject.cs +++ b/src/EcmaScript.NET/IdFunctionObject.cs @@ -1,196 +1,196 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; - -namespace EcmaScript.NET -{ - - - public class IdFunctionObject : BaseFunction - { - - public IdFunctionObject () - { - ; - } - - override public int Arity - { - get - { - return arity; - } - - } - override public int Length - { - get - { - return Arity; - } - - } - override public string FunctionName - { - get - { - return (functionName == null) ? "" : functionName; - } - } - - public IdFunctionObject (IIdFunctionCall idcall, object tag, int id, int arity) - { - if (arity < 0) - throw new ArgumentException (); - - this.idcall = idcall; - this.tag = tag; - this.m_MethodId = id; - this.arity = arity; - if (arity < 0) - throw new ArgumentException (); - } - - public IdFunctionObject (IIdFunctionCall idcall, object tag, int id, string name, int arity, IScriptable scope) - : base (scope, null) - { - - if (arity < 0) - throw new ArgumentException (); - if (name == null) - throw new ArgumentException (); - - this.idcall = idcall; - this.tag = tag; - this.m_MethodId = id; - this.arity = arity; - this.functionName = name; - } - - public virtual void InitFunction (string name, IScriptable scope) - { - if (name == null) - throw new ArgumentException (); - if (scope == null) - throw new ArgumentException (); - this.functionName = name; - ParentScope = scope; - } - - public bool HasTag (object tag) - { - return this.tag == tag; - } - - public int MethodId - { - get - { - return m_MethodId; - } - } - - public void MarkAsConstructor (IScriptable prototypeProperty) - { - useCallAsConstructor = true; - ImmunePrototypeProperty = prototypeProperty; - } - - public void AddAsProperty (IScriptable target) - { - AddAsProperty (target, ScriptableObject.DONTENUM); - } - - public void AddAsProperty (IScriptable target, int attributes) - { - ScriptableObject.DefineProperty (target, functionName, this, attributes); - } - - public virtual void ExportAsScopeProperty () - { - AddAsProperty (ParentScope); - } - - public virtual void ExportAsScopeProperty (int attributes) - { - AddAsProperty (ParentScope, attributes); - } - - public override IScriptable GetPrototype () - { - // Lazy initialization of prototype: for native functions this - // may not be called at all - IScriptable proto = base.GetPrototype (); - if (proto == null) { - proto = GetFunctionPrototype (ParentScope); - SetPrototype (proto); - } - return proto; - } - - public override object Call (Context cx, IScriptable scope, IScriptable thisObj, object [] args) - { - return idcall.ExecIdCall (this, cx, scope, thisObj, args); - } - - public override IScriptable CreateObject (Context cx, IScriptable scope) - { - if (useCallAsConstructor) { - return null; - } - // Throw error if not explicitly coded to be used as constructor, - // to satisfy ECMAScript standard (see bugzilla 202019). - // To follow current (2003-05-01) SpiderMonkey behavior, change it to: - // return super.createObject(cx, scope); - throw ScriptRuntime.TypeErrorById ("msg.not.ctor", functionName); - } - - internal override string Decompile (int indent, int flags) - { - System.Text.StringBuilder sb = new System.Text.StringBuilder (); - bool justbody = (0 != (flags & Decompiler.ONLY_BODY_FLAG)); - if (!justbody) { - sb.Append ("function "); - sb.Append (FunctionName); - sb.Append ("() { "); - } - sb.Append ("[native code for "); - if (idcall is IScriptable) { - IScriptable sobj = (IScriptable)idcall; - sb.Append (sobj.ClassName); - sb.Append ('.'); - } - sb.Append (FunctionName); - sb.Append (", arity="); - sb.Append (Arity); - sb.Append (justbody ? "]\n" : "] }\n"); - return sb.ToString (); - } - - public ApplicationException Unknown () - { - // It is program error to call id-like methods for unknown function - return new ApplicationException ("BAD FUNCTION ID=" + m_MethodId + " MASTER=" + idcall); - } - - private IIdFunctionCall idcall; - private object tag; - private int m_MethodId; - private int arity; - private bool useCallAsConstructor; - private string functionName; - - - } +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; + +namespace EcmaScript.NET +{ + + + public class IdFunctionObject : BaseFunction + { + + public IdFunctionObject () + { + ; + } + + override public int Arity + { + get + { + return arity; + } + + } + override public int Length + { + get + { + return Arity; + } + + } + override public string FunctionName + { + get + { + return (functionName == null) ? "" : functionName; + } + } + + public IdFunctionObject (IIdFunctionCall idcall, object tag, int id, int arity) + { + if (arity < 0) + throw new ArgumentException (); + + this.idcall = idcall; + this.tag = tag; + this.m_MethodId = id; + this.arity = arity; + if (arity < 0) + throw new ArgumentException (); + } + + public IdFunctionObject (IIdFunctionCall idcall, object tag, int id, string name, int arity, IScriptable scope) + : base (scope, null) + { + + if (arity < 0) + throw new ArgumentException (); + if (name == null) + throw new ArgumentException (); + + this.idcall = idcall; + this.tag = tag; + this.m_MethodId = id; + this.arity = arity; + this.functionName = name; + } + + public virtual void InitFunction (string name, IScriptable scope) + { + if (name == null) + throw new ArgumentException (); + if (scope == null) + throw new ArgumentException (); + this.functionName = name; + ParentScope = scope; + } + + public bool HasTag (object tag) + { + return this.tag == tag; + } + + public int MethodId + { + get + { + return m_MethodId; + } + } + + public void MarkAsConstructor (IScriptable prototypeProperty) + { + useCallAsConstructor = true; + ImmunePrototypeProperty = prototypeProperty; + } + + public void AddAsProperty (IScriptable target) + { + AddAsProperty (target, ScriptableObject.DONTENUM); + } + + public void AddAsProperty (IScriptable target, int attributes) + { + ScriptableObject.DefineProperty (target, functionName, this, attributes); + } + + public virtual void ExportAsScopeProperty () + { + AddAsProperty (ParentScope); + } + + public virtual void ExportAsScopeProperty (int attributes) + { + AddAsProperty (ParentScope, attributes); + } + + public override IScriptable GetPrototype () + { + // Lazy initialization of prototype: for native functions this + // may not be called at all + IScriptable proto = base.GetPrototype (); + if (proto == null) { + proto = GetFunctionPrototype (ParentScope); + SetPrototype (proto); + } + return proto; + } + + public override object Call (Context cx, IScriptable scope, IScriptable thisObj, object [] args) + { + return idcall.ExecIdCall (this, cx, scope, thisObj, args); + } + + public override IScriptable CreateObject (Context cx, IScriptable scope) + { + if (useCallAsConstructor) { + return null; + } + // Throw error if not explicitly coded to be used as constructor, + // to satisfy ECMAScript standard (see bugzilla 202019). + // To follow current (2003-05-01) SpiderMonkey behavior, change it to: + // return super.createObject(cx, scope); + throw ScriptRuntime.TypeErrorById ("msg.not.ctor", functionName); + } + + internal override string Decompile (int indent, int flags) + { + System.Text.StringBuilder sb = new System.Text.StringBuilder (); + bool justbody = (0 != (flags & Decompiler.ONLY_BODY_FLAG)); + if (!justbody) { + sb.Append ("function "); + sb.Append (FunctionName); + sb.Append ("() { "); + } + sb.Append ("[native code for "); + if (idcall is IScriptable) { + IScriptable sobj = (IScriptable)idcall; + sb.Append (sobj.ClassName); + sb.Append ('.'); + } + sb.Append (FunctionName); + sb.Append (", arity="); + sb.Append (Arity); + sb.Append (justbody ? "]\n" : "] }\n"); + return sb.ToString (); + } + + public Exception Unknown () + { + // It is program error to call id-like methods for unknown function + return new Exception ("BAD FUNCTION ID=" + m_MethodId + " MASTER=" + idcall); + } + + private IIdFunctionCall idcall; + private object tag; + private int m_MethodId; + private int arity; + private bool useCallAsConstructor; + private string functionName; + + + } } \ No newline at end of file diff --git a/Code/EcmaScript.NET/IdScriptableObject.cs b/src/EcmaScript.NET/IdScriptableObject.cs similarity index 94% rename from Code/EcmaScript.NET/IdScriptableObject.cs rename to src/EcmaScript.NET/IdScriptableObject.cs index c4b332d..7162271 100644 --- a/Code/EcmaScript.NET/IdScriptableObject.cs +++ b/src/EcmaScript.NET/IdScriptableObject.cs @@ -1,694 +1,694 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; -using System.Runtime.InteropServices; - -namespace EcmaScript.NET -{ - - /// - /// Base class for native object implementation that uses IdFunctionObject to export its methods to script via .prototype object. - /// Any descendant should implement at least the following methods: - /// findInstanceIdInfo - /// getInstanceIdName - /// execIdCall - /// methodArity - /// To define non-function properties, the descendant should override - /// getInstanceIdValue - /// setInstanceIdValue - /// to get/set property value and provide its default attributes. - /// To customize initializition of constructor and protype objects, descendant - /// may override scopeInit or fillConstructorProperties methods. - /// - public abstract class IdScriptableObject : ScriptableObject, IIdFunctionCall - { - - /// - /// Get maximum id findInstanceIdInfo can generate. - /// - protected virtual internal int MaxInstanceId - { - get - { - return 0; - } - } - - private volatile PrototypeValues prototypeValues; - - private sealed class PrototypeValues - { - - internal int MaxId - { - get - { - return maxId; - } - - } - - private const int VALUE_SLOT = 0; - private const int NAME_SLOT = 1; - private const int SLOT_SPAN = 2; - - private IdScriptableObject obj; - private int maxId; - private volatile object [] valueArray; - private volatile short [] attributeArray; - private volatile int lastFoundId = 1; - - // The following helps to avoid creation of valueArray during runtime - // initialization for common case of "constructor" property - internal int constructorId; - private IdFunctionObject constructor; - private short constructorAttrs; - - internal PrototypeValues (IdScriptableObject obj, int maxId) - { - if (obj == null) - throw new ArgumentNullException ("obj"); - if (maxId < 1) - throw new ArgumentException ("maxId may not lower than 1"); - this.obj = obj; - this.maxId = maxId; - } - - internal void InitValue (int id, string name, object value, int attributes) - { - if (!(1 <= id && id <= maxId)) - throw new ArgumentException (); - if (name == null) - throw new ArgumentException (); - if (value == UniqueTag.NotFound) - throw new ArgumentException (); - ScriptableObject.CheckValidAttributes (attributes); - if (obj.FindPrototypeId (name) != id) - throw new ArgumentException (name); - - if (id == constructorId) { - if (!(value is IdFunctionObject)) { - throw new ArgumentException ("consructor should be initialized with IdFunctionObject"); - } - constructor = (IdFunctionObject)value; - constructorAttrs = (short)attributes; - return; - } - - InitSlot (id, name, value, attributes); - } - - private void InitSlot (int id, string name, object value, int attributes) - { - object [] array = valueArray; - if (array == null) - throw new ArgumentNullException ("array"); - - if (value == null) { - value = UniqueTag.NullValue; - } - int index = (id - 1) * SLOT_SPAN; - lock (this) { - object value2 = array [index + VALUE_SLOT]; - if (value2 == null) { - array [index + VALUE_SLOT] = value; - array [index + NAME_SLOT] = name; - attributeArray [id - 1] = (short)attributes; - } - else { - if (!name.Equals (array [index + NAME_SLOT])) - throw new ApplicationException (); - } - } - } - - internal IdFunctionObject createPrecachedConstructor () - { - if (constructorId != 0) - throw new ApplicationException (); - constructorId = obj.FindPrototypeId ("constructor"); - if (constructorId == 0) { - throw new ApplicationException ("No id for constructor property"); - } - obj.InitPrototypeId (constructorId); - if (constructor == null) { - throw new ApplicationException (obj.GetType ().FullName + ".initPrototypeId() did not " + "initialize id=" + constructorId); - } - constructor.InitFunction (obj.ClassName, ScriptableObject.GetTopLevelScope (obj)); - constructor.MarkAsConstructor (obj); - return constructor; - } - - internal int FindId (string name) - { - object [] array = valueArray; - if (array == null) { - return obj.FindPrototypeId (name); - } - int id = lastFoundId; - if ((object)name == array [(id - 1) * SLOT_SPAN + NAME_SLOT]) { - return id; - } - id = obj.FindPrototypeId (name); - if (id != 0) { - int nameSlot = (id - 1) * SLOT_SPAN + NAME_SLOT; - // Make cache to work! - array [nameSlot] = name; - lastFoundId = id; - } - return id; - } - - internal bool Has (int id) - { - object [] array = valueArray; - if (array == null) { - // Not yet initialized, assume all exists - return true; - } - int valueSlot = (id - 1) * SLOT_SPAN + VALUE_SLOT; - object value = array [valueSlot]; - if (value == null) { - // The particular entry has not been yet initialized - return true; - } - return value != UniqueTag.NotFound; - } - - internal object Get (int id) - { - object value = EnsureId (id); - if (value == UniqueTag.NullValue) { - value = null; - } - return value; - } - - internal void Set (int id, IScriptable start, object value) - { - if (value == UniqueTag.NotFound) - throw new ArgumentException (); - EnsureId (id); - int attr = attributeArray [id - 1]; - if ((attr & ScriptableObject.READONLY) == 0) { - if (start == obj) { - if (value == null) { - value = UniqueTag.NullValue; - } - int valueSlot = (id - 1) * SLOT_SPAN + VALUE_SLOT; - lock (this) { - valueArray [valueSlot] = value; - } - } - else { - int nameSlot = (id - 1) * SLOT_SPAN + NAME_SLOT; - string name = (string)valueArray [nameSlot]; - start.Put (name, start, value); - } - } - } - - internal void Delete (int id) - { - EnsureId (id); - int attr = attributeArray [id - 1]; - if ((attr & ScriptableObject.PERMANENT) == 0) { - int valueSlot = (id - 1) * SLOT_SPAN + VALUE_SLOT; - lock (this) { - valueArray [valueSlot] = UniqueTag.NotFound; - attributeArray [id - 1] = (short)(ScriptableObject.EMPTY); - } - } - } - - internal int GetAttributes (int id) - { - EnsureId (id); - return attributeArray [id - 1]; - } - - internal void SetAttributes (int id, int attributes) - { - ScriptableObject.CheckValidAttributes (attributes); - EnsureId (id); - lock (this) { - attributeArray [id - 1] = (short)attributes; - } - } - - internal object [] GetNames (bool getAll, object [] extraEntries) - { - object [] names = null; - int count = 0; - - for (int id = 1; id <= maxId; ++id) { - - object value = EnsureId (id); - if (getAll || (attributeArray [id - 1] & ScriptableObject.DONTENUM) == 0) { - if (value != UniqueTag.NotFound) { - int nameSlot = (id - 1) * SLOT_SPAN + NAME_SLOT; - string name = (string)valueArray [nameSlot]; - if (names == null) { - names = new object [maxId]; - } - names [count++] = name; - } - } - } - if (count == 0) { - return extraEntries; - } - else if (extraEntries == null || extraEntries.Length == 0) { - if (count != names.Length) { - object [] tmp = new object [count]; - Array.Copy (names, 0, tmp, 0, count); - names = tmp; - } - return names; - } - else { - int extra = extraEntries.Length; - object [] tmp = new object [extra + count]; - Array.Copy (extraEntries, 0, tmp, 0, extra); - Array.Copy (names, 0, tmp, extra, count); - return tmp; - } - } - - private object EnsureId (int id) - { - object [] array = valueArray; - if (array == null) { - lock (this) { - array = valueArray; - if (array == null) { - array = new object [maxId * SLOT_SPAN]; - valueArray = array; - attributeArray = new short [maxId]; - } - } - } - int valueSlot = (id - 1) * SLOT_SPAN + VALUE_SLOT; - object value = array [valueSlot]; - - if (value == null) { - if (id == constructorId) { - InitSlot (constructorId, "constructor", constructor, constructorAttrs); - constructor = null; // no need to refer it any longer - } - else { - obj.InitPrototypeId (id); - } - value = array [valueSlot]; - if (value == null) { - throw new ApplicationException (obj.GetType ().FullName + ".initPrototypeId(int id) " + "did not initialize id=" + id); - } - } - return value; - } - } - - public IdScriptableObject () - { - ; - } - - public IdScriptableObject (IScriptable scope, IScriptable prototype) - : base (scope, prototype) - { - ; - } - - protected internal object DefaultGet (string name) - { - return base.Get (name, this); - } - - protected internal void DefaultPut (string name, object value) - { - base.Put (name, this, value); - } - - public override bool Has (string name, IScriptable start) - { - int info = FindInstanceIdInfo (name); - if (info != 0) { - int attr = (int)((uint)info >> 16); - if ((attr & PERMANENT) != 0) { - return true; - } - int id = (info & 0xFFFF); - return UniqueTag.NotFound != GetInstanceIdValue (id); - } - if (prototypeValues != null) { - int id = prototypeValues.FindId (name); - if (id != 0) { - return prototypeValues.Has (id); - } - } - return base.Has (name, start); - } - - public override object Get (string name, IScriptable start) - { - int info = FindInstanceIdInfo (name); - if (info != 0) { - int id = (info & 0xFFFF); - return GetInstanceIdValue (id); - } - if (prototypeValues != null) { - int id = prototypeValues.FindId (name); - if (id != 0) { - return prototypeValues.Get (id); - } - } - return base.Get (name, start); - } - - public override object Put (string name, IScriptable start, object value) - { - int info = FindInstanceIdInfo (name); - if (info != 0) { - if (start == this && Sealed) { - throw Context.ReportRuntimeErrorById ("msg.modify.sealed", name); - } - int attr = (int)((uint)info >> 16); - if ((attr & READONLY) == 0) { - if (start == this) { - int id = (info & 0xFFFF); - SetInstanceIdValue (id, value); - } - else { - return start.Put (name, start, value); - } - } - return value; - } - if (prototypeValues != null) { - int id = prototypeValues.FindId (name); - if (id != 0) { - if (start == this && Sealed) { - throw Context.ReportRuntimeErrorById ("msg.modify.sealed", name); - } - prototypeValues.Set (id, start, value); - return value; - } - } - return base.Put (name, start, value); - } - - public override void Delete (string name) - { - int info = FindInstanceIdInfo (name); - if (info != 0) { - // Let the super class to throw exceptions for sealed objects - if (!Sealed) { - int attr = (int)((uint)info >> 16); - if ((attr & PERMANENT) == 0) { - int id = (info & 0xFFFF); - SetInstanceIdValue (id, UniqueTag.NotFound); - } - return; - } - } - if (prototypeValues != null) { - int id = prototypeValues.FindId (name); - if (id != 0) { - if (!Sealed) { - prototypeValues.Delete (id); - } - return; - } - } - base.Delete (name); - } - - public override int GetAttributes (string name) - { - int info = FindInstanceIdInfo (name); - if (info != 0) { - int attr = (int)((uint)info >> 16); - return attr; - } - if (prototypeValues != null) { - int id = prototypeValues.FindId (name); - if (id != 0) { - return prototypeValues.GetAttributes (id); - } - } - return base.GetAttributes (name); - } - - public override void SetAttributes (string name, int attributes) - { - ScriptableObject.CheckValidAttributes (attributes); - int info = FindInstanceIdInfo (name); - if (info != 0) { - int currentAttributes = (int)((uint)info >> 16); - if (attributes != currentAttributes) { - throw new ApplicationException ("Change of attributes for this id is not supported"); - } - return; - } - if (prototypeValues != null) { - int id = prototypeValues.FindId (name); - if (id != 0) { - prototypeValues.SetAttributes (id, attributes); - return; - } - } - base.SetAttributes (name, attributes); - } - - internal override object [] GetIds (bool getAll) - { - object [] result = base.GetIds (getAll); - - if (prototypeValues != null) { - result = prototypeValues.GetNames (getAll, result); - } - - int maxInstanceId = MaxInstanceId; - if (maxInstanceId != 0) { - object [] ids = null; - int count = 0; - - for (int id = maxInstanceId; id != 0; --id) { - string name = GetInstanceIdName (id); - int info = FindInstanceIdInfo (name); - if (info != 0) { - int attr = (int)((uint)info >> 16); - if ((attr & PERMANENT) == 0) { - if (UniqueTag.NotFound == GetInstanceIdValue (id)) { - continue; - } - } - if (getAll || (attr & DONTENUM) == 0) { - if (count == 0) { - // Need extra room for no more then [1..id] names - ids = new object [id]; - } - ids [count++] = name; - } - } - } - if (count != 0) { - if (result.Length == 0 && ids.Length == count) { - result = ids; - } - else { - object [] tmp = new object [result.Length + count]; - Array.Copy (result, 0, tmp, 0, result.Length); - Array.Copy (ids, 0, tmp, result.Length, count); - result = tmp; - } - } - } - return result; - } - - protected internal static int InstanceIdInfo (int attributes, int id) - { - return (attributes << 16) | id; - } - - /// - /// Map name to id of instance property. - /// Should return 0 if not found or the result of - /// {@link #instanceIdInfo(int, int)}. - /// - protected internal virtual int FindInstanceIdInfo (string name) - { - return 0; - } - - /// - /// Map id back to property name it defines. - /// - protected internal virtual string GetInstanceIdName (int id) - { - throw new ArgumentException (Convert.ToString (id)); - } - - /// - /// Get id value. - /// * If id value is constant, descendant can call cacheIdValue to store - /// * value in the permanent cache. - /// * Default implementation creates IdFunctionObject instance for given id - /// * and cache its value - /// - protected internal virtual object GetInstanceIdValue (int id) - { - throw new ApplicationException (Convert.ToString (id)); - } - - /// - /// Set or delete id value. If value == NOT_FOUND , the implementation - /// should make sure that the following getInstanceIdValue return NOT_FOUND. - /// - protected internal virtual void SetInstanceIdValue (int id, object value) - { - throw new ApplicationException (Convert.ToString (id)); - } - - /// 'thisObj' will be null if invoked as constructor, in which case - /// * instance of Scriptable should be returned. - /// - public virtual object ExecIdCall (IdFunctionObject f, Context cx, IScriptable scope, IScriptable thisObj, object [] args) - { - throw f.Unknown (); - } - - public IdFunctionObject ExportAsJSClass (int maxPrototypeId, IScriptable scope, bool zealed) - { - return ExportAsJSClass (maxPrototypeId, scope, zealed, ScriptableObject.DONTENUM); - } - - public IdFunctionObject ExportAsJSClass (int maxPrototypeId, IScriptable scope, bool zealed, int attributes) - { - // Set scope and prototype unless this is top level scope itself - if (scope != this && scope != null) { - ParentScope = scope; - SetPrototype (GetObjectPrototype (scope)); - } - - ActivatePrototypeMap (maxPrototypeId); - IdFunctionObject ctor = prototypeValues.createPrecachedConstructor (); - if (zealed) { - SealObject (); - } - FillConstructorProperties (ctor); - if (zealed) { - ctor.SealObject (); - } - ctor.ExportAsScopeProperty (attributes); - return ctor; - } - - public void ActivatePrototypeMap (int maxPrototypeId) - { - PrototypeValues values = new PrototypeValues (this, maxPrototypeId); - lock (this) { - if (prototypeValues != null) - throw new ApplicationException (); - prototypeValues = values; - } - } - - public void InitPrototypeMethod (object tag, int id, string name, int arity) - { - IScriptable scope = ScriptableObject.GetTopLevelScope (this); - IdFunctionObject f = NewIdFunction (tag, id, name, arity, scope); - prototypeValues.InitValue (id, name, f, DONTENUM); - } - - public void InitPrototypeConstructor (IdFunctionObject f) - { - int id = prototypeValues.constructorId; - if (id == 0) - throw new ApplicationException (); - if (f.MethodId != id) - throw new ArgumentException (); - if (Sealed) { - f.SealObject (); - } - prototypeValues.InitValue (id, "constructor", f, DONTENUM); - } - - public void InitPrototypeValue (int id, string name, object value, int attributes) - { - prototypeValues.InitValue (id, name, value, attributes); - } - - protected internal virtual void InitPrototypeId (int id) - { - throw new ApplicationException (Convert.ToString (id)); - } - - protected internal virtual int FindPrototypeId (string name) - { - throw new ApplicationException (name); - } - - protected internal virtual void FillConstructorProperties (IdFunctionObject ctor) - { - } - - protected internal virtual void AddIdFunctionProperty (IScriptable obj, object tag, int id, string name, int arity) - { - IScriptable scope = ScriptableObject.GetTopLevelScope (obj); - IdFunctionObject f = NewIdFunction (tag, id, name, arity, scope); - f.AddAsProperty (obj); - } - - /// - /// Utility method to construct type error to indicate incompatible call - /// when converting script thisObj to a particular type is not possible. - /// Possible usage would be to have a private function like realThis: - ///
-        /// private static NativeSomething realThis(Scriptable thisObj,
-        /// IdFunctionObject f)
-        /// {
-        /// if (!(thisObj instanceof NativeSomething))
-        /// throw incompatibleCallError(f);
-        /// return (NativeSomething)thisObj;
-        /// }
-        /// 
- /// Note that although such function can be implemented universally via - /// java.lang.Class.isInstance(), it would be much more slower. - ///
- /// specify if the function f does not change state of - /// object. - /// - /// Scriptable object suitable for a check by the instanceof - /// operator. - /// - /// RuntimeException if no more instanceof target can be found - protected internal static EcmaScriptError IncompatibleCallError (IdFunctionObject f) - { - throw ScriptRuntime.TypeErrorById ("msg.incompat.call", f.FunctionName); - } - - private IdFunctionObject NewIdFunction (object tag, int id, string name, int arity, IScriptable scope) - { - IdFunctionObject f = new IdFunctionObject (this, tag, id, name, arity, scope); - if (Sealed) { - f.SealObject (); - } - return f; - } - - - } +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; +using System.Runtime.InteropServices; + +namespace EcmaScript.NET +{ + + /// + /// Base class for native object implementation that uses IdFunctionObject to export its methods to script via .prototype object. + /// Any descendant should implement at least the following methods: + /// findInstanceIdInfo + /// getInstanceIdName + /// execIdCall + /// methodArity + /// To define non-function properties, the descendant should override + /// getInstanceIdValue + /// setInstanceIdValue + /// to get/set property value and provide its default attributes. + /// To customize initializition of constructor and protype objects, descendant + /// may override scopeInit or fillConstructorProperties methods. + /// + public abstract class IdScriptableObject : ScriptableObject, IIdFunctionCall + { + + /// + /// Get maximum id findInstanceIdInfo can generate. + /// + protected virtual internal int MaxInstanceId + { + get + { + return 0; + } + } + + private volatile PrototypeValues prototypeValues; + + private sealed class PrototypeValues + { + + internal int MaxId + { + get + { + return maxId; + } + + } + + private const int VALUE_SLOT = 0; + private const int NAME_SLOT = 1; + private const int SLOT_SPAN = 2; + + private IdScriptableObject obj; + private int maxId; + private volatile object [] valueArray; + private volatile short [] attributeArray; + private volatile int lastFoundId = 1; + + // The following helps to avoid creation of valueArray during runtime + // initialization for common case of "constructor" property + internal int constructorId; + private IdFunctionObject constructor; + private short constructorAttrs; + + internal PrototypeValues (IdScriptableObject obj, int maxId) + { + if (obj == null) + throw new ArgumentNullException ("obj"); + if (maxId < 1) + throw new ArgumentException ("maxId may not lower than 1"); + this.obj = obj; + this.maxId = maxId; + } + + internal void InitValue (int id, string name, object value, int attributes) + { + if (!(1 <= id && id <= maxId)) + throw new ArgumentException (); + if (name == null) + throw new ArgumentException (); + if (value == UniqueTag.NotFound) + throw new ArgumentException (); + ScriptableObject.CheckValidAttributes (attributes); + if (obj.FindPrototypeId (name) != id) + throw new ArgumentException (name); + + if (id == constructorId) { + if (!(value is IdFunctionObject)) { + throw new ArgumentException ("consructor should be initialized with IdFunctionObject"); + } + constructor = (IdFunctionObject)value; + constructorAttrs = (short)attributes; + return; + } + + InitSlot (id, name, value, attributes); + } + + private void InitSlot (int id, string name, object value, int attributes) + { + object [] array = valueArray; + if (array == null) + throw new ArgumentNullException ("array"); + + if (value == null) { + value = UniqueTag.NullValue; + } + int index = (id - 1) * SLOT_SPAN; + lock (this) { + object value2 = array [index + VALUE_SLOT]; + if (value2 == null) { + array [index + VALUE_SLOT] = value; + array [index + NAME_SLOT] = name; + attributeArray [id - 1] = (short)attributes; + } + else { + if (!name.Equals (array [index + NAME_SLOT])) + throw new Exception (); + } + } + } + + internal IdFunctionObject createPrecachedConstructor () + { + if (constructorId != 0) + throw new Exception (); + constructorId = obj.FindPrototypeId ("constructor"); + if (constructorId == 0) { + throw new Exception ("No id for constructor property"); + } + obj.InitPrototypeId (constructorId); + if (constructor == null) { + throw new Exception (obj.GetType ().FullName + ".initPrototypeId() did not " + "initialize id=" + constructorId); + } + constructor.InitFunction (obj.ClassName, ScriptableObject.GetTopLevelScope (obj)); + constructor.MarkAsConstructor (obj); + return constructor; + } + + internal int FindId (string name) + { + object [] array = valueArray; + if (array == null) { + return obj.FindPrototypeId (name); + } + int id = lastFoundId; + if ((object)name == array [(id - 1) * SLOT_SPAN + NAME_SLOT]) { + return id; + } + id = obj.FindPrototypeId (name); + if (id != 0) { + int nameSlot = (id - 1) * SLOT_SPAN + NAME_SLOT; + // Make cache to work! + array [nameSlot] = name; + lastFoundId = id; + } + return id; + } + + internal bool Has (int id) + { + object [] array = valueArray; + if (array == null) { + // Not yet initialized, assume all exists + return true; + } + int valueSlot = (id - 1) * SLOT_SPAN + VALUE_SLOT; + object value = array [valueSlot]; + if (value == null) { + // The particular entry has not been yet initialized + return true; + } + return value != UniqueTag.NotFound; + } + + internal object Get (int id) + { + object value = EnsureId (id); + if (value == UniqueTag.NullValue) { + value = null; + } + return value; + } + + internal void Set (int id, IScriptable start, object value) + { + if (value == UniqueTag.NotFound) + throw new ArgumentException (); + EnsureId (id); + int attr = attributeArray [id - 1]; + if ((attr & ScriptableObject.READONLY) == 0) { + if (start == obj) { + if (value == null) { + value = UniqueTag.NullValue; + } + int valueSlot = (id - 1) * SLOT_SPAN + VALUE_SLOT; + lock (this) { + valueArray [valueSlot] = value; + } + } + else { + int nameSlot = (id - 1) * SLOT_SPAN + NAME_SLOT; + string name = (string)valueArray [nameSlot]; + start.Put (name, start, value); + } + } + } + + internal void Delete (int id) + { + EnsureId (id); + int attr = attributeArray [id - 1]; + if ((attr & ScriptableObject.PERMANENT) == 0) { + int valueSlot = (id - 1) * SLOT_SPAN + VALUE_SLOT; + lock (this) { + valueArray [valueSlot] = UniqueTag.NotFound; + attributeArray [id - 1] = (short)(ScriptableObject.EMPTY); + } + } + } + + internal int GetAttributes (int id) + { + EnsureId (id); + return attributeArray [id - 1]; + } + + internal void SetAttributes (int id, int attributes) + { + ScriptableObject.CheckValidAttributes (attributes); + EnsureId (id); + lock (this) { + attributeArray [id - 1] = (short)attributes; + } + } + + internal object [] GetNames (bool getAll, object [] extraEntries) + { + object [] names = null; + int count = 0; + + for (int id = 1; id <= maxId; ++id) { + + object value = EnsureId (id); + if (getAll || (attributeArray [id - 1] & ScriptableObject.DONTENUM) == 0) { + if (value != UniqueTag.NotFound) { + int nameSlot = (id - 1) * SLOT_SPAN + NAME_SLOT; + string name = (string)valueArray [nameSlot]; + if (names == null) { + names = new object [maxId]; + } + names [count++] = name; + } + } + } + if (count == 0) { + return extraEntries; + } + else if (extraEntries == null || extraEntries.Length == 0) { + if (count != names.Length) { + object [] tmp = new object [count]; + Array.Copy (names, 0, tmp, 0, count); + names = tmp; + } + return names; + } + else { + int extra = extraEntries.Length; + object [] tmp = new object [extra + count]; + Array.Copy (extraEntries, 0, tmp, 0, extra); + Array.Copy (names, 0, tmp, extra, count); + return tmp; + } + } + + private object EnsureId (int id) + { + object [] array = valueArray; + if (array == null) { + lock (this) { + array = valueArray; + if (array == null) { + array = new object [maxId * SLOT_SPAN]; + valueArray = array; + attributeArray = new short [maxId]; + } + } + } + int valueSlot = (id - 1) * SLOT_SPAN + VALUE_SLOT; + object value = array [valueSlot]; + + if (value == null) { + if (id == constructorId) { + InitSlot (constructorId, "constructor", constructor, constructorAttrs); + constructor = null; // no need to refer it any longer + } + else { + obj.InitPrototypeId (id); + } + value = array [valueSlot]; + if (value == null) { + throw new Exception (obj.GetType ().FullName + ".initPrototypeId(int id) " + "did not initialize id=" + id); + } + } + return value; + } + } + + public IdScriptableObject () + { + ; + } + + public IdScriptableObject (IScriptable scope, IScriptable prototype) + : base (scope, prototype) + { + ; + } + + protected internal object DefaultGet (string name) + { + return base.Get (name, this); + } + + protected internal void DefaultPut (string name, object value) + { + base.Put (name, this, value); + } + + public override bool Has (string name, IScriptable start) + { + int info = FindInstanceIdInfo (name); + if (info != 0) { + int attr = (int)((uint)info >> 16); + if ((attr & PERMANENT) != 0) { + return true; + } + int id = (info & 0xFFFF); + return UniqueTag.NotFound != GetInstanceIdValue (id); + } + if (prototypeValues != null) { + int id = prototypeValues.FindId (name); + if (id != 0) { + return prototypeValues.Has (id); + } + } + return base.Has (name, start); + } + + public override object Get (string name, IScriptable start) + { + int info = FindInstanceIdInfo (name); + if (info != 0) { + int id = (info & 0xFFFF); + return GetInstanceIdValue (id); + } + if (prototypeValues != null) { + int id = prototypeValues.FindId (name); + if (id != 0) { + return prototypeValues.Get (id); + } + } + return base.Get (name, start); + } + + public override object Put (string name, IScriptable start, object value) + { + int info = FindInstanceIdInfo (name); + if (info != 0) { + if (start == this && Sealed) { + throw Context.ReportRuntimeErrorById ("msg.modify.sealed", name); + } + int attr = (int)((uint)info >> 16); + if ((attr & READONLY) == 0) { + if (start == this) { + int id = (info & 0xFFFF); + SetInstanceIdValue (id, value); + } + else { + return start.Put (name, start, value); + } + } + return value; + } + if (prototypeValues != null) { + int id = prototypeValues.FindId (name); + if (id != 0) { + if (start == this && Sealed) { + throw Context.ReportRuntimeErrorById ("msg.modify.sealed", name); + } + prototypeValues.Set (id, start, value); + return value; + } + } + return base.Put (name, start, value); + } + + public override void Delete (string name) + { + int info = FindInstanceIdInfo (name); + if (info != 0) { + // Let the super class to throw exceptions for sealed objects + if (!Sealed) { + int attr = (int)((uint)info >> 16); + if ((attr & PERMANENT) == 0) { + int id = (info & 0xFFFF); + SetInstanceIdValue (id, UniqueTag.NotFound); + } + return; + } + } + if (prototypeValues != null) { + int id = prototypeValues.FindId (name); + if (id != 0) { + if (!Sealed) { + prototypeValues.Delete (id); + } + return; + } + } + base.Delete (name); + } + + public override int GetAttributes (string name) + { + int info = FindInstanceIdInfo (name); + if (info != 0) { + int attr = (int)((uint)info >> 16); + return attr; + } + if (prototypeValues != null) { + int id = prototypeValues.FindId (name); + if (id != 0) { + return prototypeValues.GetAttributes (id); + } + } + return base.GetAttributes (name); + } + + public override void SetAttributes (string name, int attributes) + { + ScriptableObject.CheckValidAttributes (attributes); + int info = FindInstanceIdInfo (name); + if (info != 0) { + int currentAttributes = (int)((uint)info >> 16); + if (attributes != currentAttributes) { + throw new Exception ("Change of attributes for this id is not supported"); + } + return; + } + if (prototypeValues != null) { + int id = prototypeValues.FindId (name); + if (id != 0) { + prototypeValues.SetAttributes (id, attributes); + return; + } + } + base.SetAttributes (name, attributes); + } + + internal override object [] GetIds (bool getAll) + { + object [] result = base.GetIds (getAll); + + if (prototypeValues != null) { + result = prototypeValues.GetNames (getAll, result); + } + + int maxInstanceId = MaxInstanceId; + if (maxInstanceId != 0) { + object [] ids = null; + int count = 0; + + for (int id = maxInstanceId; id != 0; --id) { + string name = GetInstanceIdName (id); + int info = FindInstanceIdInfo (name); + if (info != 0) { + int attr = (int)((uint)info >> 16); + if ((attr & PERMANENT) == 0) { + if (UniqueTag.NotFound == GetInstanceIdValue (id)) { + continue; + } + } + if (getAll || (attr & DONTENUM) == 0) { + if (count == 0) { + // Need extra room for no more then [1..id] names + ids = new object [id]; + } + ids [count++] = name; + } + } + } + if (count != 0) { + if (result.Length == 0 && ids.Length == count) { + result = ids; + } + else { + object [] tmp = new object [result.Length + count]; + Array.Copy (result, 0, tmp, 0, result.Length); + Array.Copy (ids, 0, tmp, result.Length, count); + result = tmp; + } + } + } + return result; + } + + protected internal static int InstanceIdInfo (int attributes, int id) + { + return (attributes << 16) | id; + } + + /// + /// Map name to id of instance property. + /// Should return 0 if not found or the result of + /// {@link #instanceIdInfo(int, int)}. + /// + protected internal virtual int FindInstanceIdInfo (string name) + { + return 0; + } + + /// + /// Map id back to property name it defines. + /// + protected internal virtual string GetInstanceIdName (int id) + { + throw new ArgumentException (Convert.ToString (id)); + } + + /// + /// Get id value. + /// * If id value is constant, descendant can call cacheIdValue to store + /// * value in the permanent cache. + /// * Default implementation creates IdFunctionObject instance for given id + /// * and cache its value + /// + protected internal virtual object GetInstanceIdValue (int id) + { + throw new Exception (Convert.ToString (id)); + } + + /// + /// Set or delete id value. If value == NOT_FOUND , the implementation + /// should make sure that the following getInstanceIdValue return NOT_FOUND. + /// + protected internal virtual void SetInstanceIdValue (int id, object value) + { + throw new Exception (Convert.ToString (id)); + } + + /// 'thisObj' will be null if invoked as constructor, in which case + /// * instance of Scriptable should be returned. + /// + public virtual object ExecIdCall (IdFunctionObject f, Context cx, IScriptable scope, IScriptable thisObj, object [] args) + { + throw f.Unknown (); + } + + public IdFunctionObject ExportAsJSClass (int maxPrototypeId, IScriptable scope, bool zealed) + { + return ExportAsJSClass (maxPrototypeId, scope, zealed, ScriptableObject.DONTENUM); + } + + public IdFunctionObject ExportAsJSClass (int maxPrototypeId, IScriptable scope, bool zealed, int attributes) + { + // Set scope and prototype unless this is top level scope itself + if (scope != this && scope != null) { + ParentScope = scope; + SetPrototype (GetObjectPrototype (scope)); + } + + ActivatePrototypeMap (maxPrototypeId); + IdFunctionObject ctor = prototypeValues.createPrecachedConstructor (); + if (zealed) { + SealObject (); + } + FillConstructorProperties (ctor); + if (zealed) { + ctor.SealObject (); + } + ctor.ExportAsScopeProperty (attributes); + return ctor; + } + + public void ActivatePrototypeMap (int maxPrototypeId) + { + PrototypeValues values = new PrototypeValues (this, maxPrototypeId); + lock (this) { + if (prototypeValues != null) + throw new Exception (); + prototypeValues = values; + } + } + + public void InitPrototypeMethod (object tag, int id, string name, int arity) + { + IScriptable scope = ScriptableObject.GetTopLevelScope (this); + IdFunctionObject f = NewIdFunction (tag, id, name, arity, scope); + prototypeValues.InitValue (id, name, f, DONTENUM); + } + + public void InitPrototypeConstructor (IdFunctionObject f) + { + int id = prototypeValues.constructorId; + if (id == 0) + throw new Exception (); + if (f.MethodId != id) + throw new ArgumentException (); + if (Sealed) { + f.SealObject (); + } + prototypeValues.InitValue (id, "constructor", f, DONTENUM); + } + + public void InitPrototypeValue (int id, string name, object value, int attributes) + { + prototypeValues.InitValue (id, name, value, attributes); + } + + protected internal virtual void InitPrototypeId (int id) + { + throw new Exception (Convert.ToString (id)); + } + + protected internal virtual int FindPrototypeId (string name) + { + throw new Exception (name); + } + + protected internal virtual void FillConstructorProperties (IdFunctionObject ctor) + { + } + + protected internal virtual void AddIdFunctionProperty (IScriptable obj, object tag, int id, string name, int arity) + { + IScriptable scope = ScriptableObject.GetTopLevelScope (obj); + IdFunctionObject f = NewIdFunction (tag, id, name, arity, scope); + f.AddAsProperty (obj); + } + + /// + /// Utility method to construct type error to indicate incompatible call + /// when converting script thisObj to a particular type is not possible. + /// Possible usage would be to have a private function like realThis: + ///
+        /// private static NativeSomething realThis(Scriptable thisObj,
+        /// IdFunctionObject f)
+        /// {
+        /// if (!(thisObj instanceof NativeSomething))
+        /// throw incompatibleCallError(f);
+        /// return (NativeSomething)thisObj;
+        /// }
+        /// 
+ /// Note that although such function can be implemented universally via + /// java.lang.Class.isInstance(), it would be much more slower. + ///
+ /// specify if the function f does not change state of + /// object. + /// + /// Scriptable object suitable for a check by the instanceof + /// operator. + /// + /// RuntimeException if no more instanceof target can be found + protected internal static EcmaScriptError IncompatibleCallError (IdFunctionObject f) + { + throw ScriptRuntime.TypeErrorById ("msg.incompat.call", f.FunctionName); + } + + private IdFunctionObject NewIdFunction (object tag, int id, string name, int arity, IScriptable scope) + { + IdFunctionObject f = new IdFunctionObject (this, tag, id, name, arity, scope); + if (Sealed) { + f.SealObject (); + } + return f; + } + + + } } \ No newline at end of file diff --git a/Code/EcmaScript.NET/InterpretedFunction.cs b/src/EcmaScript.NET/InterpretedFunction.cs similarity index 96% rename from Code/EcmaScript.NET/InterpretedFunction.cs rename to src/EcmaScript.NET/InterpretedFunction.cs index f966257..f48c36a 100644 --- a/Code/EcmaScript.NET/InterpretedFunction.cs +++ b/src/EcmaScript.NET/InterpretedFunction.cs @@ -1,186 +1,186 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; - -using EcmaScript.NET.Debugging; -using EcmaScript.NET.Types; - -namespace EcmaScript.NET -{ - - - sealed class InterpretedFunction : BuiltinFunction, IScript - { - override public string FunctionName - { - get - { - return (idata.itsName == null) ? "" : idata.itsName; - } - - } - - override public string EncodedSource - { - get - { - return Interpreter.GetEncodedSource (idata); - } - - } - override public DebuggableScript DebuggableView - { - get - { - return idata; - } - - } - override protected internal Context.Versions LanguageVersion - { - get - { - return idata.languageVersion; - } - - } - override protected internal int ParamCount - { - get - { - return idata.argCount; - } - - } - override protected internal int ParamAndVarCount - { - get - { - return idata.argNames.Length; - } - - } - - internal InterpreterData idata; - internal SecurityController securityController; - internal object securityDomain; - internal IScriptable [] functionRegExps; - - private InterpretedFunction (InterpreterData idata, object staticSecurityDomain) - { - this.idata = idata; - - // Always get Context from the current thread to - // avoid security breaches via passing mangled Context instances - // with bogus SecurityController - Context cx = Context.CurrentContext; - SecurityController sc = cx.SecurityController; - object dynamicDomain; - if (sc != null) { - dynamicDomain = sc.getDynamicSecurityDomain (staticSecurityDomain); - } - else { - if (staticSecurityDomain != null) { - throw new ArgumentException (); - } - dynamicDomain = null; - } - - this.securityController = sc; - this.securityDomain = dynamicDomain; - } - - private InterpretedFunction (InterpretedFunction parent, int index) - { - this.idata = parent.idata.itsNestedFunctions [index]; - this.securityController = parent.securityController; - this.securityDomain = parent.securityDomain; - } - - /// Create script from compiled bytecode. - internal static InterpretedFunction createScript (InterpreterData idata, object staticSecurityDomain) - { - InterpretedFunction f; - f = new InterpretedFunction (idata, staticSecurityDomain); - return f; - } - - /// Create function compiled from Function(...) constructor. - internal static InterpretedFunction createFunction (Context cx, IScriptable scope, InterpreterData idata, object staticSecurityDomain) - { - InterpretedFunction f; - f = new InterpretedFunction (idata, staticSecurityDomain); - f.initInterpretedFunction (cx, scope); - return f; - } - - /// Create function embedded in script or another function. - internal static InterpretedFunction createFunction (Context cx, IScriptable scope, InterpretedFunction parent, int index) - { - InterpretedFunction f = new InterpretedFunction (parent, index); - f.initInterpretedFunction (cx, scope); - return f; - } - - internal IScriptable [] createRegExpWraps (Context cx, IScriptable scope) - { - if (idata.itsRegExpLiterals == null) - Context.CodeBug (); - - RegExpProxy rep = cx.RegExpProxy; - int N = idata.itsRegExpLiterals.Length; - IScriptable [] array = new IScriptable [N]; - for (int i = 0; i != N; ++i) { - array [i] = rep.Wrap (cx, scope, idata.itsRegExpLiterals [i]); - } - return array; - } - - private void initInterpretedFunction (Context cx, IScriptable scope) - { - initScriptFunction (cx, scope); - if (idata.itsRegExpLiterals != null) { - functionRegExps = createRegExpWraps (cx, scope); - } - } - - public override object Call (Context cx, IScriptable scope, IScriptable thisObj, object [] args) - { - if (!ScriptRuntime.hasTopCall (cx)) { - return ScriptRuntime.DoTopCall (this, cx, scope, thisObj, args); - } - return Interpreter.Interpret (this, cx, scope, thisObj, args); - } - - public object Exec (Context cx, IScriptable scope) - { - if (idata.itsFunctionType != 0) { - // Can only be applied to scripts - throw new ApplicationException (); - } - if (!ScriptRuntime.hasTopCall (cx)) { - // It will go through "call" path. but they are equivalent - return ScriptRuntime.DoTopCall (this, cx, scope, scope, ScriptRuntime.EmptyArgs); - } - return Interpreter.Interpret (this, cx, scope, scope, ScriptRuntime.EmptyArgs); - } - - protected internal override string getParamOrVarName (int index) - { - return idata.argNames [index]; - } - - - } +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; + +using EcmaScript.NET.Debugging; +using EcmaScript.NET.Types; + +namespace EcmaScript.NET +{ + + + sealed class InterpretedFunction : BuiltinFunction, IScript + { + override public string FunctionName + { + get + { + return (idata.itsName == null) ? "" : idata.itsName; + } + + } + + override public string EncodedSource + { + get + { + return Interpreter.GetEncodedSource (idata); + } + + } + override public DebuggableScript DebuggableView + { + get + { + return idata; + } + + } + override protected internal Context.Versions LanguageVersion + { + get + { + return idata.languageVersion; + } + + } + override protected internal int ParamCount + { + get + { + return idata.argCount; + } + + } + override protected internal int ParamAndVarCount + { + get + { + return idata.argNames.Length; + } + + } + + internal InterpreterData idata; + internal SecurityController securityController; + internal object securityDomain; + internal IScriptable [] functionRegExps; + + private InterpretedFunction (InterpreterData idata, object staticSecurityDomain) + { + this.idata = idata; + + // Always get Context from the current thread to + // avoid security breaches via passing mangled Context instances + // with bogus SecurityController + Context cx = Context.CurrentContext; + SecurityController sc = cx.SecurityController; + object dynamicDomain; + if (sc != null) { + dynamicDomain = sc.getDynamicSecurityDomain (staticSecurityDomain); + } + else { + if (staticSecurityDomain != null) { + throw new ArgumentException (); + } + dynamicDomain = null; + } + + this.securityController = sc; + this.securityDomain = dynamicDomain; + } + + private InterpretedFunction (InterpretedFunction parent, int index) + { + this.idata = parent.idata.itsNestedFunctions [index]; + this.securityController = parent.securityController; + this.securityDomain = parent.securityDomain; + } + + /// Create script from compiled bytecode. + internal static InterpretedFunction createScript (InterpreterData idata, object staticSecurityDomain) + { + InterpretedFunction f; + f = new InterpretedFunction (idata, staticSecurityDomain); + return f; + } + + /// Create function compiled from Function(...) constructor. + internal static InterpretedFunction createFunction (Context cx, IScriptable scope, InterpreterData idata, object staticSecurityDomain) + { + InterpretedFunction f; + f = new InterpretedFunction (idata, staticSecurityDomain); + f.initInterpretedFunction (cx, scope); + return f; + } + + /// Create function embedded in script or another function. + internal static InterpretedFunction createFunction (Context cx, IScriptable scope, InterpretedFunction parent, int index) + { + InterpretedFunction f = new InterpretedFunction (parent, index); + f.initInterpretedFunction (cx, scope); + return f; + } + + internal IScriptable [] createRegExpWraps (Context cx, IScriptable scope) + { + if (idata.itsRegExpLiterals == null) + Context.CodeBug (); + + RegExpProxy rep = cx.RegExpProxy; + int N = idata.itsRegExpLiterals.Length; + IScriptable [] array = new IScriptable [N]; + for (int i = 0; i != N; ++i) { + array [i] = rep.Wrap (cx, scope, idata.itsRegExpLiterals [i]); + } + return array; + } + + private void initInterpretedFunction (Context cx, IScriptable scope) + { + initScriptFunction (cx, scope); + if (idata.itsRegExpLiterals != null) { + functionRegExps = createRegExpWraps (cx, scope); + } + } + + public override object Call (Context cx, IScriptable scope, IScriptable thisObj, object [] args) + { + if (!ScriptRuntime.hasTopCall (cx)) { + return ScriptRuntime.DoTopCall (this, cx, scope, thisObj, args); + } + return Interpreter.Interpret (this, cx, scope, thisObj, args); + } + + public object Exec (Context cx, IScriptable scope) + { + if (idata.itsFunctionType != 0) { + // Can only be applied to scripts + throw new Exception (); + } + if (!ScriptRuntime.hasTopCall (cx)) { + // It will go through "call" path. but they are equivalent + return ScriptRuntime.DoTopCall (this, cx, scope, scope, ScriptRuntime.EmptyArgs); + } + return Interpreter.Interpret (this, cx, scope, scope, ScriptRuntime.EmptyArgs); + } + + protected internal override string getParamOrVarName (int index) + { + return idata.argNames [index]; + } + + + } } \ No newline at end of file diff --git a/Code/EcmaScript.NET/Interpreter.cs b/src/EcmaScript.NET/Interpreter.cs similarity index 97% rename from Code/EcmaScript.NET/Interpreter.cs rename to src/EcmaScript.NET/Interpreter.cs index 05cb879..ea601da 100644 --- a/Code/EcmaScript.NET/Interpreter.cs +++ b/src/EcmaScript.NET/Interpreter.cs @@ -1,4755 +1,4755 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; - -using EcmaScript.NET.Debugging; -using EcmaScript.NET.Collections; - -namespace EcmaScript.NET -{ - - public class Interpreter - { - - // Additional interpreter-specific codes - - const int Icode_DUP = -1; - const int Icode_DUP2 = -2; - const int Icode_SWAP = -3; - const int Icode_POP = -4; - const int Icode_POP_RESULT = -5; - const int Icode_IFEQ_POP = -6; - const int Icode_VAR_INC_DEC = -7; - const int Icode_NAME_INC_DEC = -8; - const int Icode_PROP_INC_DEC = -9; - const int Icode_ELEM_INC_DEC = -10; - const int Icode_REF_INC_DEC = -11; - const int Icode_SCOPE_LOAD = -12; - const int Icode_SCOPE_SAVE = -13; - const int Icode_TYPEOFNAME = -14; - const int Icode_NAME_AND_THIS = -15; - const int Icode_PROP_AND_THIS = -16; - const int Icode_ELEM_AND_THIS = -17; - const int Icode_VALUE_AND_THIS = -18; - const int Icode_CLOSURE_EXPR = -19; - const int Icode_CLOSURE_STMT = -20; - const int Icode_CALLSPECIAL = -21; - const int Icode_RETUNDEF = -22; - const int Icode_GOSUB = -23; - const int Icode_STARTSUB = -24; - const int Icode_RETSUB = -25; - const int Icode_LINE = -26; - const int Icode_SHORTNUMBER = -27; - const int Icode_INTNUMBER = -28; - const int Icode_LITERAL_NEW = -29; - const int Icode_LITERAL_SET = -30; - const int Icode_SPARE_ARRAYLIT = -31; - const int Icode_REG_IND_C0 = -32; - const int Icode_REG_IND_C1 = -33; - const int Icode_REG_IND_C2 = -34; - const int Icode_REG_IND_C3 = -35; - const int Icode_REG_IND_C4 = -36; - const int Icode_REG_IND_C5 = -37; - const int Icode_REG_IND1 = -38; - const int Icode_REG_IND2 = -39; - const int Icode_REG_IND4 = -40; - const int Icode_REG_STR_C0 = -41; - const int Icode_REG_STR_C1 = -42; - const int Icode_REG_STR_C2 = -43; - const int Icode_REG_STR_C3 = -44; - const int Icode_REG_STR1 = -45; - const int Icode_REG_STR2 = -46; - const int Icode_REG_STR4 = -47; - const int Icode_GETVAR1 = -48; - const int Icode_SETVAR1 = -49; - const int Icode_UNDEF = -50; - const int Icode_ZERO = -51; - const int Icode_ONE = -52; - const int Icode_ENTERDQ = -53; - const int Icode_LEAVEDQ = -54; - const int Icode_TAIL_CALL = -55; - const int Icode_LOCAL_CLEAR = -56; - const int Icode_DEBUGGER = -57; - - // Last icode - const int MIN_ICODE = -57; - - - // data for parsing - - CompilerEnvirons compilerEnv; - - bool itsInFunctionFlag; - - InterpreterData itsData; - ScriptOrFnNode scriptOrFn; - int itsICodeTop; - int itsStackDepth; - int itsLineNumber; - int itsDoubleTableTop; - ObjToIntMap itsStrings = new ObjToIntMap (20); - int itsLocalTop; - - const int MIN_LABEL_TABLE_SIZE = 32; - const int MIN_FIXUP_TABLE_SIZE = 40; - int [] itsLabelTable; - int itsLabelTableTop; - // itsFixupTable[i] = (label_index << 32) | fixup_site - long [] itsFixupTable; - int itsFixupTableTop; - ObjArray itsLiteralIds = new ObjArray (); - - int itsExceptionTableTop; - const int EXCEPTION_TRY_START_SLOT = 0; - const int EXCEPTION_TRY_END_SLOT = 1; - const int EXCEPTION_HANDLER_SLOT = 2; - const int EXCEPTION_TYPE_SLOT = 3; - const int EXCEPTION_LOCAL_SLOT = 4; - const int EXCEPTION_SCOPE_SLOT = 5; - // SLOT_SIZE: space for try start/end, handler, start, handler type, - // exception local and scope local - const int EXCEPTION_SLOT_SIZE = 6; - - // ECF_ or Expression Context Flags constants: for now only TAIL is available - const int ECF_TAIL = 1 << 0; - - - internal class CallFrame : System.ICloneable - { - - internal CallFrame parentFrame; - // amount of stack frames before this one on the interpretation stack - internal int frameIndex; - // If true indicates read-only frame that is a part of continuation - internal bool frozen; - - internal InterpretedFunction fnOrScript; - internal InterpreterData idata; - - // Stack structure - // stack[0 <= i < localShift]: arguments and local variables - // stack[localShift <= i <= emptyStackTop]: used for local temporaries - // stack[emptyStackTop < i < stack.length]: stack data - // sDbl[i]: if stack[i] is UniqueTag.DoubleMark, sDbl[i] holds the number value - - internal object [] stack; - internal double [] sDbl; - - internal CallFrame varSource; // defaults to this unless continuation frame - internal int localShift; - internal int emptyStackTop; - - internal DebugFrame debuggerFrame; - internal bool useActivation; - - internal IScriptable thisObj; - internal IScriptable [] scriptRegExps; - - // The values that change during interpretation - - internal object result; - internal double resultDbl; - internal int pc; - internal int pcPrevBranch; - internal int pcSourceLineStart; - internal IScriptable scope; - - internal int savedStackTop; - internal int savedCallOp; - - internal virtual CallFrame cloneFrozen () - { - if (!frozen) - Context.CodeBug (); - - CallFrame copy = (CallFrame)Clone (); - - // clone stack but keep varSource to point to values - // from this frame to share variables. - - // TODO: STACK - copy.stack = new object [stack.Length]; - stack.CopyTo (copy.stack, 0); - copy.sDbl = new double [sDbl.Length]; - sDbl.CopyTo (copy.sDbl, 0); - - copy.frozen = false; - return copy; - } - - virtual public object Clone () - { - return base.MemberwiseClone (); - } - } - - - sealed class ContinuationJump - { - - internal CallFrame capturedFrame; - internal CallFrame branchFrame; - internal object result; - internal double resultDbl; - - internal ContinuationJump (Continuation c, CallFrame current) - { - this.capturedFrame = (CallFrame)c.Implementation; - if (this.capturedFrame == null || current == null) { - // Continuation and current execution does not share - // any frames if there is nothing to capture or - // if there is no currently executed frames - this.branchFrame = null; - } - else { - // Search for branch frame where parent frame chains starting - // from captured and current meet. - CallFrame chain1 = this.capturedFrame; - CallFrame chain2 = current; - - // First work parents of chain1 or chain2 until the same - // frame depth. - int diff = chain1.frameIndex - chain2.frameIndex; - if (diff != 0) { - if (diff < 0) { - // swap to make sure that - // chain1.frameIndex > chain2.frameIndex and diff > 0 - chain1 = current; - chain2 = this.capturedFrame; - diff = -diff; - } - do { - chain1 = chain1.parentFrame; - } - while (--diff != 0); - if (chain1.frameIndex != chain2.frameIndex) - Context.CodeBug (); - } - - // Now walk parents in parallel until a shared frame is found - // or until the root is reached. - while (chain1 != chain2 && chain1 != null) { - chain1 = chain1.parentFrame; - chain2 = chain2.parentFrame; - } - - this.branchFrame = chain1; - if (this.branchFrame != null && !this.branchFrame.frozen) - Context.CodeBug (); - } - } - } - - static string bytecodeName (int bytecode) - { - if (!validBytecode (bytecode)) { - throw new ArgumentException (Convert.ToString (bytecode)); - } - - if (!Token.printICode) { - return Convert.ToString (bytecode); - } - - if (ValidTokenCode (bytecode)) { - return Token.name (bytecode); - } - - switch (bytecode) { - - case Icode_DUP: - return "DUP"; - - case Icode_DUP2: - return "DUP2"; - - case Icode_SWAP: - return "SWAP"; - - case Icode_POP: - return "POP"; - - case Icode_POP_RESULT: - return "POP_RESULT"; - - case Icode_IFEQ_POP: - return "IFEQ_POP"; - - case Icode_VAR_INC_DEC: - return "VAR_INC_DEC"; - - case Icode_NAME_INC_DEC: - return "NAME_INC_DEC"; - - case Icode_PROP_INC_DEC: - return "PROP_INC_DEC"; - - case Icode_ELEM_INC_DEC: - return "ELEM_INC_DEC"; - - case Icode_REF_INC_DEC: - return "REF_INC_DEC"; - - case Icode_SCOPE_LOAD: - return "SCOPE_LOAD"; - - case Icode_SCOPE_SAVE: - return "SCOPE_SAVE"; - - case Icode_TYPEOFNAME: - return "TYPEOFNAME"; - - case Icode_NAME_AND_THIS: - return "NAME_AND_THIS"; - - case Icode_PROP_AND_THIS: - return "PROP_AND_THIS"; - - case Icode_ELEM_AND_THIS: - return "ELEM_AND_THIS"; - - case Icode_VALUE_AND_THIS: - return "VALUE_AND_THIS"; - - case Icode_CLOSURE_EXPR: - return "CLOSURE_EXPR"; - - case Icode_CLOSURE_STMT: - return "CLOSURE_STMT"; - - case Icode_CALLSPECIAL: - return "CALLSPECIAL"; - - case Icode_RETUNDEF: - return "RETUNDEF"; - - case Icode_GOSUB: - return "GOSUB"; - - case Icode_STARTSUB: - return "STARTSUB"; - - case Icode_RETSUB: - return "RETSUB"; - - case Icode_LINE: - return "LINE"; - - case Icode_SHORTNUMBER: - return "SHORTNUMBER"; - - case Icode_INTNUMBER: - return "INTNUMBER"; - - case Icode_LITERAL_NEW: - return "LITERAL_NEW"; - - case Icode_LITERAL_SET: - return "LITERAL_SET"; - - case Icode_SPARE_ARRAYLIT: - return "SPARE_ARRAYLIT"; - - case Icode_REG_IND_C0: - return "REG_IND_C0"; - - case Icode_REG_IND_C1: - return "REG_IND_C1"; - - case Icode_REG_IND_C2: - return "REG_IND_C2"; - - case Icode_REG_IND_C3: - return "REG_IND_C3"; - - case Icode_REG_IND_C4: - return "REG_IND_C4"; - - case Icode_REG_IND_C5: - return "REG_IND_C5"; - - case Icode_REG_IND1: - return "LOAD_IND1"; - - case Icode_REG_IND2: - return "LOAD_IND2"; - - case Icode_REG_IND4: - return "LOAD_IND4"; - - case Icode_REG_STR_C0: - return "REG_STR_C0"; - - case Icode_REG_STR_C1: - return "REG_STR_C1"; - - case Icode_REG_STR_C2: - return "REG_STR_C2"; - - case Icode_REG_STR_C3: - return "REG_STR_C3"; - - case Icode_REG_STR1: - return "LOAD_STR1"; - - case Icode_REG_STR2: - return "LOAD_STR2"; - - case Icode_REG_STR4: - return "LOAD_STR4"; - - case Icode_GETVAR1: - return "GETVAR1"; - - case Icode_SETVAR1: - return "SETVAR1"; - - case Icode_UNDEF: - return "UNDEF"; - - case Icode_ZERO: - return "ZERO"; - - case Icode_ONE: - return "ONE"; - - case Icode_ENTERDQ: - return "ENTERDQ"; - - case Icode_LEAVEDQ: - return "LEAVEDQ"; - - case Icode_TAIL_CALL: - return "TAIL_CALL"; - - case Icode_LOCAL_CLEAR: - return "LOCAL_CLEAR"; - - case Icode_DEBUGGER: - return "DEBUGGER"; - } - - // icode without name - throw new ApplicationException (Convert.ToString (bytecode)); - } - - static bool validIcode (int icode) - { - return MIN_ICODE <= icode && icode <= -1; - } - - static bool ValidTokenCode (int token) - { - return Token.FIRST_BYTECODE_TOKEN <= token && token <= Token.LAST_BYTECODE_TOKEN; - } - - static bool validBytecode (int bytecode) - { - return validIcode (bytecode) || ValidTokenCode (bytecode); - } - - public virtual object Compile (CompilerEnvirons compilerEnv, ScriptOrFnNode tree, string encodedSource, bool returnFunction) - { - this.compilerEnv = compilerEnv; - new NodeTransformer ().transform (tree); - - if (Token.printTrees) { - System.Console.Out.WriteLine (tree.toStringTree (tree)); - } - - if (returnFunction) { - tree = tree.getFunctionNode (0); - } - - scriptOrFn = tree; - itsData = new InterpreterData (compilerEnv.LanguageVersion, scriptOrFn.SourceName, encodedSource); - itsData.topLevel = true; - - if (returnFunction) { - generateFunctionICode (); - } - else { - generateICodeFromTree (scriptOrFn); - } - - return itsData; - } - - public virtual IScript CreateScriptObject (object bytecode, object staticSecurityDomain) - { - InterpreterData idata = (InterpreterData)bytecode; - return InterpretedFunction.createScript (itsData, staticSecurityDomain); - } - - public virtual IFunction CreateFunctionObject (Context cx, IScriptable scope, object bytecode, object staticSecurityDomain) - { - InterpreterData idata = (InterpreterData)bytecode; - return InterpretedFunction.createFunction (cx, scope, itsData, staticSecurityDomain); - } - - void generateFunctionICode () - { - itsInFunctionFlag = true; - - FunctionNode theFunction = (FunctionNode)scriptOrFn; - - itsData.itsFunctionType = theFunction.FunctionType; - itsData.itsNeedsActivation = theFunction.RequiresActivation; - itsData.itsName = theFunction.FunctionName; - if (!theFunction.IgnoreDynamicScope) { - if (compilerEnv.UseDynamicScope) { - itsData.useDynamicScope = true; - } - } - - generateICodeFromTree (theFunction.LastChild); - } - - void generateICodeFromTree (Node tree) - { - generateNestedFunctions (); - - generateRegExpLiterals (); - - VisitStatement (tree); - fixLabelGotos (); - // add RETURN_RESULT only to scripts as function always ends with RETURN - if (itsData.itsFunctionType == 0) { - addToken (Token.RETURN_RESULT); - } - - if (itsData.itsICode.Length != itsICodeTop) { - // Make itsData.itsICode length exactly itsICodeTop to save memory - // and catch bugs with jumps beyound icode as early as possible - sbyte [] tmp = new sbyte [itsICodeTop]; - Array.Copy (itsData.itsICode, 0, tmp, 0, itsICodeTop); - itsData.itsICode = tmp; - } - if (itsStrings.size () == 0) { - itsData.itsStringTable = null; - } - else { - itsData.itsStringTable = new string [itsStrings.size ()]; - ObjToIntMap.Iterator iter = itsStrings.newIterator (); - for (iter.start (); !iter.done (); iter.next ()) { - string str = (string)iter.Key; - int index = iter.Value; - if (itsData.itsStringTable [index] != null) - Context.CodeBug (); - itsData.itsStringTable [index] = str; - } - } - if (itsDoubleTableTop == 0) { - itsData.itsDoubleTable = null; - } - else if (itsData.itsDoubleTable.Length != itsDoubleTableTop) { - double [] tmp = new double [itsDoubleTableTop]; - Array.Copy (itsData.itsDoubleTable, 0, tmp, 0, itsDoubleTableTop); - itsData.itsDoubleTable = tmp; - } - if (itsExceptionTableTop != 0 && itsData.itsExceptionTable.Length != itsExceptionTableTop) { - int [] tmp = new int [itsExceptionTableTop]; - Array.Copy (itsData.itsExceptionTable, 0, tmp, 0, itsExceptionTableTop); - itsData.itsExceptionTable = tmp; - } - - itsData.itsMaxVars = scriptOrFn.ParamAndVarCount; - // itsMaxFrameArray: interpret method needs this amount for its - // stack and sDbl arrays - itsData.itsMaxFrameArray = itsData.itsMaxVars + itsData.itsMaxLocals + itsData.itsMaxStack; - - itsData.argNames = scriptOrFn.ParamAndVarNames; - itsData.argCount = scriptOrFn.ParamCount; - - itsData.encodedSourceStart = scriptOrFn.EncodedSourceStart; - itsData.encodedSourceEnd = scriptOrFn.EncodedSourceEnd; - - if (itsLiteralIds.size () != 0) { - itsData.literalIds = itsLiteralIds.ToArray (); - } - - if (Token.printICode) - dumpICode (itsData); - } - - void generateNestedFunctions () - { - int functionCount = scriptOrFn.FunctionCount; - if (functionCount == 0) - return; - - InterpreterData [] array = new InterpreterData [functionCount]; - for (int i = 0; i != functionCount; i++) { - FunctionNode def = scriptOrFn.getFunctionNode (i); - Interpreter jsi = new Interpreter (); - jsi.compilerEnv = compilerEnv; - jsi.scriptOrFn = def; - jsi.itsData = new InterpreterData (itsData); - jsi.generateFunctionICode (); - array [i] = jsi.itsData; - } - itsData.itsNestedFunctions = array; - } - - void generateRegExpLiterals () - { - int N = scriptOrFn.RegexpCount; - if (N == 0) - return; - - Context cx = Context.CurrentContext; - RegExpProxy rep = cx.RegExpProxy; - object [] array = new object [N]; - for (int i = 0; i != N; i++) { - string str = scriptOrFn.getRegexpString (i); - string flags = scriptOrFn.getRegexpFlags (i); - array [i] = rep.Compile (cx, str, flags); - } - itsData.itsRegExpLiterals = array; - } - - void updateLineNumber (Node node) - { - int lineno = node.Lineno; - if (lineno != itsLineNumber && lineno >= 0) { - if (itsData.firstLinePC < 0) { - itsData.firstLinePC = lineno; - } - itsLineNumber = lineno; - addIcode (Icode_LINE); - addUint16 (lineno & 0xFFFF); - } - } - - ApplicationException badTree (Node node) - { - throw new ApplicationException (node.ToString ()); - } - - void VisitStatement (Node node) - { - int type = node.Type; - Node child = node.FirstChild; - switch (type) { - - - case Token.FUNCTION: { - int fnIndex = node.getExistingIntProp (Node.FUNCTION_PROP); - int fnType = scriptOrFn.getFunctionNode (fnIndex).FunctionType; - // Only function expressions or function expression - // statements needs closure code creating new function - // object on stack as function statements are initialized - // at script/function start - // In addition function expression can not present here - // at statement level, they must only present as expressions. - if (fnType == FunctionNode.FUNCTION_EXPRESSION_STATEMENT) { - addIndexOp (Icode_CLOSURE_STMT, fnIndex); - } - else { - if (fnType != FunctionNode.FUNCTION_STATEMENT) { - throw Context.CodeBug (); - } - } - } - break; - - - case Token.SCRIPT: - case Token.LABEL: - case Token.LOOP: - case Token.BLOCK: - case Token.EMPTY: - case Token.WITH: - updateLineNumber (node); - while (child != null) { - VisitStatement (child); - child = child.Next; - } - break; - - - case Token.ENTERWITH: - VisitExpression (child, 0); - addToken (Token.ENTERWITH); - stackChange (-1); - break; - - - case Token.LEAVEWITH: - addToken (Token.LEAVEWITH); - break; - - - case Token.LOCAL_BLOCK: { - int local = allocLocal (); - node.putIntProp (Node.LOCAL_PROP, local); - updateLineNumber (node); - while (child != null) { - VisitStatement (child); - child = child.Next; - } - addIndexOp (Icode_LOCAL_CLEAR, local); - releaseLocal (local); - } - break; - - case Token.DEBUGGER: - updateLineNumber (node); - addIcode (Icode_DEBUGGER); - break; - - case Token.SWITCH: - updateLineNumber (node); { - // See comments in IRFactory.createSwitch() for description - // of SWITCH node - Node switchNode = (Node.Jump)node; - VisitExpression (child, 0); - for (Node.Jump caseNode = (Node.Jump)child.Next; caseNode != null; caseNode = (Node.Jump)caseNode.Next) { - if (caseNode.Type != Token.CASE) - throw badTree (caseNode); - Node test = caseNode.FirstChild; - addIcode (Icode_DUP); - stackChange (1); - VisitExpression (test, 0); - addToken (Token.SHEQ); - stackChange (-1); - // If true, Icode_IFEQ_POP will jump and remove case - // value from stack - addGoto (caseNode.target, Icode_IFEQ_POP); - stackChange (-1); - } - addIcode (Icode_POP); - stackChange (-1); - } - break; - - - case Token.TARGET: - markTargetLabel (node); - break; - - - case Token.IFEQ: - case Token.IFNE: { - Node target = ((Node.Jump)node).target; - VisitExpression (child, 0); - addGoto (target, type); - stackChange (-1); - } - break; - - - case Token.GOTO: { - Node target = ((Node.Jump)node).target; - addGoto (target, type); - } - break; - - - case Token.JSR: { - Node target = ((Node.Jump)node).target; - addGoto (target, Icode_GOSUB); - } - break; - - - case Token.FINALLY: { - // Account for incomming GOTOSUB address - stackChange (1); - int finallyRegister = getLocalBlockRef (node); - addIndexOp (Icode_STARTSUB, finallyRegister); - stackChange (-1); - while (child != null) { - VisitStatement (child); - child = child.Next; - } - addIndexOp (Icode_RETSUB, finallyRegister); - } - break; - - - case Token.EXPR_VOID: - case Token.EXPR_RESULT: - updateLineNumber (node); - VisitExpression (child, 0); - addIcode ((type == Token.EXPR_VOID) ? Icode_POP : Icode_POP_RESULT); - stackChange (-1); - break; - - - case Token.TRY: { - Node.Jump tryNode = (Node.Jump)node; - int exceptionObjectLocal = getLocalBlockRef (tryNode); - int scopeLocal = allocLocal (); - - addIndexOp (Icode_SCOPE_SAVE, scopeLocal); - - int tryStart = itsICodeTop; - while (child != null) { - VisitStatement (child); - child = child.Next; - } - - Node catchTarget = tryNode.target; - if (catchTarget != null) { - int catchStartPC = itsLabelTable [getTargetLabel (catchTarget)]; - addExceptionHandler (tryStart, catchStartPC, catchStartPC, false, exceptionObjectLocal, scopeLocal); - } - Node finallyTarget = tryNode.Finally; - if (finallyTarget != null) { - int finallyStartPC = itsLabelTable [getTargetLabel (finallyTarget)]; - addExceptionHandler (tryStart, finallyStartPC, finallyStartPC, true, exceptionObjectLocal, scopeLocal); - } - - addIndexOp (Icode_LOCAL_CLEAR, scopeLocal); - releaseLocal (scopeLocal); - } - break; - - - case Token.CATCH_SCOPE: { - int localIndex = getLocalBlockRef (node); - int scopeIndex = node.getExistingIntProp (Node.CATCH_SCOPE_PROP); - string name = child.String; - child = child.Next; - VisitExpression (child, 0); // load expression object - addStringPrefix (name); - addIndexPrefix (localIndex); - addToken (Token.CATCH_SCOPE); - addUint8 (scopeIndex != 0 ? 1 : 0); - stackChange (-1); - } - break; - - - case Token.THROW: - updateLineNumber (node); - VisitExpression (child, 0); - addToken (Token.THROW); - addUint16 (itsLineNumber & 0xFFFF); - stackChange (-1); - break; - - - case Token.RETHROW: - updateLineNumber (node); - addIndexOp (Token.RETHROW, getLocalBlockRef (node)); - break; - - - case Token.RETURN: - updateLineNumber (node); - if (child != null) { - VisitExpression (child, ECF_TAIL); - addToken (Token.RETURN); - stackChange (-1); - } - else { - addIcode (Icode_RETUNDEF); - } - break; - - - case Token.RETURN_RESULT: - updateLineNumber (node); - addToken (Token.RETURN_RESULT); - break; - - - case Token.ENUM_INIT_KEYS: - case Token.ENUM_INIT_VALUES: - VisitExpression (child, 0); - addIndexOp (type, getLocalBlockRef (node)); - stackChange (-1); - break; - - - default: - throw badTree (node); - - } - - if (itsStackDepth != 0) { - throw Context.CodeBug (); - } - } - - bool VisitExpressionOptimized (Node node, int contextFlags) - { - return false; -#if FALKSE - if (node.Type == Token.ADD) { - Node next = node.Next; - if (next == null) - return false; - switch (next.Type) { - case Token.NAME: - case Token.STRING: - return true; - } - } - return false; -#endif - } - - void VisitExpression (Node node, int contextFlags) - { - if (VisitExpressionOptimized (node, contextFlags)) - return; - - int type = node.Type; - Node child = node.FirstChild; - int savedStackDepth = itsStackDepth; - switch (type) { - - - case Token.FUNCTION: { - int fnIndex = node.getExistingIntProp (Node.FUNCTION_PROP); - FunctionNode fn = scriptOrFn.getFunctionNode (fnIndex); - - // See comments in visitStatement for Token.FUNCTION case - switch (fn.FunctionType) { - case FunctionNode.FUNCTION_EXPRESSION: - addIndexOp (Icode_CLOSURE_EXPR, fnIndex); - break; - default: - throw Context.CodeBug (); - } - - stackChange (1); - } - break; - - - case Token.LOCAL_LOAD: { - int localIndex = getLocalBlockRef (node); - addIndexOp (Token.LOCAL_LOAD, localIndex); - stackChange (1); - } - break; - - - case Token.COMMA: { - Node lastChild = node.LastChild; - while (child != lastChild) { - VisitExpression (child, 0); - addIcode (Icode_POP); - stackChange (-1); - child = child.Next; - } - // Preserve tail context flag if any - VisitExpression (child, contextFlags & ECF_TAIL); - } - break; - - - case Token.USE_STACK: - // Indicates that stack was modified externally, - // like placed catch object - stackChange (1); - break; - - - case Token.REF_CALL: - case Token.CALL: - case Token.NEW: { - if (type == Token.NEW) { - VisitExpression (child, 0); - } - else { - generateCallFunAndThis (child); - } - int argCount = 0; - while ((child = child.Next) != null) { - VisitExpression (child, 0); - ++argCount; - } - int callType = node.getIntProp (Node.SPECIALCALL_PROP, Node.NON_SPECIALCALL); - if (callType != Node.NON_SPECIALCALL) { - // embed line number and source filename - addIndexOp (Icode_CALLSPECIAL, argCount); - addUint8 (callType); - addUint8 (type == Token.NEW ? 1 : 0); - addUint16 (itsLineNumber & 0xFFFF); - } - else { - if (type == Token.CALL) { - if ((contextFlags & ECF_TAIL) != 0) { - type = Icode_TAIL_CALL; - } - } - addIndexOp (type, argCount); - } - // adjust stack - if (type == Token.NEW) { - // new: f, args -> result - stackChange (-argCount); - } - else { - // call: f, thisObj, args -> result - // ref_call: f, thisObj, args -> ref - stackChange (-1 - argCount); - } - if (argCount > itsData.itsMaxCalleeArgs) { - itsData.itsMaxCalleeArgs = argCount; - } - } - break; - - - case Token.AND: - case Token.OR: { - VisitExpression (child, 0); - addIcode (Icode_DUP); - stackChange (1); - int afterSecondJumpStart = itsICodeTop; - int jump = (type == Token.AND) ? Token.IFNE : Token.IFEQ; - addGotoOp (jump); - stackChange (-1); - addIcode (Icode_POP); - stackChange (-1); - child = child.Next; - // Preserve tail context flag if any - VisitExpression (child, contextFlags & ECF_TAIL); - resolveForwardGoto (afterSecondJumpStart); - } - break; - - - case Token.HOOK: { - Node ifThen = child.Next; - Node ifElse = ifThen.Next; - VisitExpression (child, 0); - int elseJumpStart = itsICodeTop; - addGotoOp (Token.IFNE); - ; - stackChange (-1); - // Preserve tail context flag if any - VisitExpression (ifThen, contextFlags & ECF_TAIL); - int afterElseJumpStart = itsICodeTop; - addGotoOp (Token.GOTO); - resolveForwardGoto (elseJumpStart); - itsStackDepth = savedStackDepth; - // Preserve tail context flag if any - VisitExpression (ifElse, contextFlags & ECF_TAIL); - resolveForwardGoto (afterElseJumpStart); - } - break; - - - case Token.GETPROP: - VisitExpression (child, 0); - child = child.Next; - addStringOp (Token.GETPROP, child.String); - break; - - - case Token.ADD: - case Token.GETELEM: - case Token.DELPROP: - case Token.BITAND: - case Token.BITOR: - case Token.BITXOR: - case Token.LSH: - case Token.RSH: - case Token.URSH: - case Token.SUB: - case Token.MOD: - case Token.DIV: - case Token.MUL: - case Token.EQ: - case Token.NE: - case Token.SHEQ: - case Token.SHNE: - case Token.IN: - case Token.INSTANCEOF: - case Token.LE: - case Token.LT: - case Token.GE: - case Token.GT: - VisitExpression (child, 0); - VisitExpression (child.Next, 0); - addToken (type); - stackChange (-1); - break; - - - case Token.POS: - case Token.NEG: - case Token.NOT: - case Token.BITNOT: - case Token.TYPEOF: - case Token.VOID: - VisitExpression (child, 0); - if (type == Token.VOID) { - addIcode (Icode_POP); - addIcode (Icode_UNDEF); - } - else { - addToken (type); - } - break; - - - case Token.GET_REF: - case Token.DEL_REF: - VisitExpression (child, 0); - addToken (type); - break; - - - case Token.SETPROP: - case Token.SETPROP_OP: - case Token.SETPROP_GETTER: - case Token.SETPROP_SETTER: - { - VisitExpression (child, 0); - child = child.Next; - string property = child.String; - child = child.Next; - if (type == Token.SETPROP_OP) { - addIcode (Icode_DUP); - stackChange (1); - addStringOp (Token.GETPROP, property); - // Compensate for the following USE_STACK - stackChange (-1); - } - VisitExpression (child, 0); - addStringOp ((type == Token.SETPROP_OP) ? Token.SETPROP : type, property); - stackChange (-1); - } - break; - - - case Token.SETELEM: - case Token.SETELEM_OP: - VisitExpression (child, 0); - child = child.Next; - VisitExpression (child, 0); - child = child.Next; - if (type == Token.SETELEM_OP) { - addIcode (Icode_DUP2); - stackChange (2); - addToken (Token.GETELEM); - stackChange (-1); - // Compensate for the following USE_STACK - stackChange (-1); - } - VisitExpression (child, 0); - addToken (Token.SETELEM); - stackChange (-2); - break; - - - case Token.SET_REF: - case Token.SET_REF_OP: - VisitExpression (child, 0); - child = child.Next; - if (type == Token.SET_REF_OP) { - addIcode (Icode_DUP); - stackChange (1); - addToken (Token.GET_REF); - // Compensate for the following USE_STACK - stackChange (-1); - } - VisitExpression (child, 0); - addToken (Token.SET_REF); - stackChange (-1); - break; - - - case Token.SETNAME: { - string name = child.String; - VisitExpression (child, 0); - child = child.Next; - VisitExpression (child, 0); - addStringOp (Token.SETNAME, name); - stackChange (-1); - } - break; - - - case Token.TYPEOFNAME: { - string name = node.String; - int index = -1; - // use typeofname if an activation frame exists - // since the vars all exist there instead of in jregs - if (itsInFunctionFlag && !itsData.itsNeedsActivation) - index = scriptOrFn.getParamOrVarIndex (name); - if (index == -1) { - addStringOp (Icode_TYPEOFNAME, name); - stackChange (1); - } - else { - addVarOp (Token.GETVAR, index); - stackChange (1); - addToken (Token.TYPEOF); - } - } - break; - - - case Token.BINDNAME: - case Token.NAME: - case Token.STRING: - addStringOp (type, node.String); - stackChange (1); - break; - - - case Token.INC: - case Token.DEC: - VisitIncDec (node, child); - break; - - - case Token.NUMBER: { - double num = node.Double; - int inum = (int)num; - if (inum == num) { - if (inum == 0) { - addIcode (Icode_ZERO); - // Check for negative zero - if (1.0 / num < 0.0) { - addToken (Token.NEG); - } - } - else if (inum == 1) { - addIcode (Icode_ONE); - } - else if ((short)inum == inum) { - addIcode (Icode_SHORTNUMBER); - // write short as uin16 bit pattern - addUint16 (inum & 0xFFFF); - } - else { - addIcode (Icode_INTNUMBER); - addInt (inum); - } - } - else { - int index = GetDoubleIndex (num); - addIndexOp (Token.NUMBER, index); - } - stackChange (1); - } - break; - - - case Token.GETVAR: { - if (itsData.itsNeedsActivation) - Context.CodeBug (); - string name = node.String; - int index = scriptOrFn.getParamOrVarIndex (name); - addVarOp (Token.GETVAR, index); - stackChange (1); - } - break; - - - case Token.SETVAR: { - if (itsData.itsNeedsActivation) - Context.CodeBug (); - string name = child.String; - child = child.Next; - VisitExpression (child, 0); - int index = scriptOrFn.getParamOrVarIndex (name); - addVarOp (Token.SETVAR, index); - } - break; - - - - case Token.NULL: - case Token.THIS: - case Token.THISFN: - case Token.FALSE: - case Token.TRUE: - addToken (type); - stackChange (1); - break; - - - case Token.ENUM_NEXT: - case Token.ENUM_ID: - addIndexOp (type, getLocalBlockRef (node)); - stackChange (1); - break; - - - case Token.REGEXP: { - int index = node.getExistingIntProp (Node.REGEXP_PROP); - addIndexOp (Token.REGEXP, index); - stackChange (1); - } - break; - - - case Token.ARRAYLIT: - case Token.OBJECTLIT: - VisitLiteral (node, child); - break; - - - case Token.REF_SPECIAL: - VisitExpression (child, 0); - addStringOp (type, (string)node.getProp (Node.NAME_PROP)); - break; - - - case Token.REF_MEMBER: - case Token.REF_NS_MEMBER: - case Token.REF_NAME: - case Token.REF_NS_NAME: { - int memberTypeFlags = node.getIntProp (Node.MEMBER_TYPE_PROP, 0); - // generate possible target, possible namespace and member - int childCount = 0; - do { - VisitExpression (child, 0); - ++childCount; - child = child.Next; - } - while (child != null); - addIndexOp (type, memberTypeFlags); - stackChange (1 - childCount); - } - break; - - - case Token.DOTQUERY: { - int queryPC; - updateLineNumber (node); - VisitExpression (child, 0); - addIcode (Icode_ENTERDQ); - stackChange (-1); - queryPC = itsICodeTop; - VisitExpression (child.Next, 0); - addBackwardGoto (Icode_LEAVEDQ, queryPC); - } - break; - - - case Token.DEFAULTNAMESPACE: - case Token.ESCXMLATTR: - case Token.ESCXMLTEXT: - VisitExpression (child, 0); - addToken (type); - break; - - default: - throw badTree (node); - - } - //if (savedStackDepth + 1 != itsStackDepth) { - // EcmaScriptHelper.CodeBug(); - //} - } - - void generateCallFunAndThis (Node left) - { - // Generate code to place on stack function and thisObj - int type = left.Type; - switch (type) { - - case Token.NAME: { - string name = left.String; - // stack: ... -> ... function thisObj - addStringOp (Icode_NAME_AND_THIS, name); - stackChange (2); - break; - } - - case Token.GETPROP: - case Token.GETELEM: { - Node target = left.FirstChild; - VisitExpression (target, 0); - Node id = target.Next; - if (type == Token.GETPROP) { - string property = id.String; - // stack: ... target -> ... function thisObj - addStringOp (Icode_PROP_AND_THIS, property); - stackChange (1); - } - else { - VisitExpression (id, 0); - // stack: ... target id -> ... function thisObj - addIcode (Icode_ELEM_AND_THIS); - } - break; - } - - default: - // Including Token.GETVAR - VisitExpression (left, 0); - // stack: ... value -> ... function thisObj - addIcode (Icode_VALUE_AND_THIS); - stackChange (1); - break; - - } - } - - void VisitIncDec (Node node, Node child) - { - int incrDecrMask = node.getExistingIntProp (Node.INCRDECR_PROP); - int childType = child.Type; - switch (childType) { - - case Token.GETVAR: { - if (itsData.itsNeedsActivation) - Context.CodeBug (); - string name = child.String; - int i = scriptOrFn.getParamOrVarIndex (name); - addVarOp (Icode_VAR_INC_DEC, i); - addUint8 (incrDecrMask); - stackChange (1); - break; - } - - case Token.NAME: { - string name = child.String; - addStringOp (Icode_NAME_INC_DEC, name); - addUint8 (incrDecrMask); - stackChange (1); - break; - } - - case Token.GETPROP: { - Node obj = child.FirstChild; - VisitExpression (obj, 0); - string property = obj.Next.String; - addStringOp (Icode_PROP_INC_DEC, property); - addUint8 (incrDecrMask); - break; - } - - case Token.GETELEM: { - Node obj = child.FirstChild; - VisitExpression (obj, 0); - Node index = obj.Next; - VisitExpression (index, 0); - addIcode (Icode_ELEM_INC_DEC); - addUint8 (incrDecrMask); - stackChange (-1); - break; - } - - case Token.GET_REF: { - Node rf = child.FirstChild; - VisitExpression (rf, 0); - addIcode (Icode_REF_INC_DEC); - addUint8 (incrDecrMask); - break; - } - - default: { - throw badTree (node); - } - - } - } - - void VisitLiteral (Node node, Node child) - { - int type = node.Type; - int count; - object [] propertyIds = null; - if (type == Token.ARRAYLIT) { - count = 0; - for (Node n = child; n != null; n = n.Next) { - ++count; - } - } - else if (type == Token.OBJECTLIT) { - propertyIds = (object [])node.getProp (Node.OBJECT_IDS_PROP); - count = propertyIds.Length; - } - else { - throw badTree (node); - } - addIndexOp (Icode_LITERAL_NEW, count); - stackChange (1); - while (child != null) { - VisitExpression (child, 0); - addIcode (Icode_LITERAL_SET); - stackChange (-1); - child = child.Next; - } - if (type == Token.ARRAYLIT) { - int [] skipIndexes = (int [])node.getProp (Node.SKIP_INDEXES_PROP); - if (skipIndexes == null) { - addToken (Token.ARRAYLIT); - } - else { - int index = itsLiteralIds.size (); - itsLiteralIds.add (skipIndexes); - addIndexOp (Icode_SPARE_ARRAYLIT, index); - } - } - else { - int index = itsLiteralIds.size (); - itsLiteralIds.add (propertyIds); - addIndexOp (Token.OBJECTLIT, index); - } - } - - int getLocalBlockRef (Node node) - { - Node localBlock = (Node)node.getProp (Node.LOCAL_BLOCK_PROP); - return localBlock.getExistingIntProp (Node.LOCAL_PROP); - } - - int getTargetLabel (Node target) - { - int label = target.labelId (); - if (label != -1) { - return label; - } - label = itsLabelTableTop; - if (itsLabelTable == null || label == itsLabelTable.Length) { - if (itsLabelTable == null) { - itsLabelTable = new int [MIN_LABEL_TABLE_SIZE]; - } - else { - int [] tmp = new int [itsLabelTable.Length * 2]; - Array.Copy (itsLabelTable, 0, tmp, 0, label); - itsLabelTable = tmp; - } - } - itsLabelTableTop = label + 1; - itsLabelTable [label] = -1; - - target.labelId (label); - return label; - } - - void markTargetLabel (Node target) - { - int label = getTargetLabel (target); - if (itsLabelTable [label] != -1) { - // Can mark label only once - Context.CodeBug (); - } - itsLabelTable [label] = itsICodeTop; - } - - void addGoto (Node target, int gotoOp) - { - int label = getTargetLabel (target); - if (!(label < itsLabelTableTop)) - Context.CodeBug (); - int targetPC = itsLabelTable [label]; - - if (targetPC != -1) { - addBackwardGoto (gotoOp, targetPC); - } - else { - int gotoPC = itsICodeTop; - addGotoOp (gotoOp); - int top = itsFixupTableTop; - if (itsFixupTable == null || top == itsFixupTable.Length) { - if (itsFixupTable == null) { - itsFixupTable = new long [MIN_FIXUP_TABLE_SIZE]; - } - else { - long [] tmp = new long [itsFixupTable.Length * 2]; - Array.Copy (itsFixupTable, 0, tmp, 0, top); - itsFixupTable = tmp; - } - } - itsFixupTableTop = top + 1; - itsFixupTable [top] = ((long)label << 32) | (uint)gotoPC; - } - } - - void fixLabelGotos () - { - for (int i = 0; i < itsFixupTableTop; i++) { - long fixup = itsFixupTable [i]; - int label = (int)(fixup >> 32); - int jumpSource = (int)fixup; - int pc = itsLabelTable [label]; - if (pc == -1) { - // Unlocated label - throw Context.CodeBug (); - } - resolveGoto (jumpSource, pc); - } - itsFixupTableTop = 0; - } - - void addBackwardGoto (int gotoOp, int jumpPC) - { - int fromPC = itsICodeTop; - // Ensure that this is a jump backward - if (fromPC <= jumpPC) - throw Context.CodeBug (); - addGotoOp (gotoOp); - resolveGoto (fromPC, jumpPC); - } - - - void resolveForwardGoto (int fromPC) - { - // Ensure that forward jump skips at least self bytecode - if (itsICodeTop < fromPC + 3) - throw Context.CodeBug (); - resolveGoto (fromPC, itsICodeTop); - } - - void resolveGoto (int fromPC, int jumpPC) - { - int offset = jumpPC - fromPC; - // Ensure that jumps do not overlap - if (0 <= offset && offset <= 2) - throw Context.CodeBug (); - int offsetSite = fromPC + 1; - if (offset != (short)offset) { - if (itsData.longJumps == null) { - itsData.longJumps = new UintMap (); - } - itsData.longJumps.put (offsetSite, jumpPC); - offset = 0; - } - sbyte [] array = itsData.itsICode; - array [offsetSite] = (sbyte)(offset >> 8); - array [offsetSite + 1] = (sbyte)offset; - } - - void addToken (int token) - { - if (!ValidTokenCode (token)) - throw Context.CodeBug (); - addUint8 (token); - } - - void addIcode (int icode) - { - if (!validIcode (icode)) - throw Context.CodeBug (); - // Write negative icode as uint8 bits - addUint8 (icode & 0xFF); - } - - void addUint8 (int value) - { - if ((value & ~0xFF) != 0) - throw Context.CodeBug (); - sbyte [] array = itsData.itsICode; - int top = itsICodeTop; - if (top == array.Length) { - array = increaseICodeCapasity (1); - } - array [top] = (sbyte)value; - itsICodeTop = top + 1; - } - - void addUint16 (int value) - { - if ((value & ~0xFFFF) != 0) - throw Context.CodeBug (); - sbyte [] array = itsData.itsICode; - int top = itsICodeTop; - if (top + 2 > array.Length) { - array = increaseICodeCapasity (2); - } - array [top] = (sbyte)((uint)value >> 8); - array [top + 1] = (sbyte)value; - itsICodeTop = top + 2; - } - - void addInt (int i) - { - sbyte [] array = itsData.itsICode; - int top = itsICodeTop; - if (top + 4 > array.Length) { - array = increaseICodeCapasity (4); - } - array [top] = (sbyte)((uint)i >> 24); - array [top + 1] = (sbyte)((uint)i >> 16); - array [top + 2] = (sbyte)((uint)i >> 8); - array [top + 3] = (sbyte)i; - itsICodeTop = top + 4; - } - - int GetDoubleIndex (double num) - { - int index = itsDoubleTableTop; - if (index == 0) { - itsData.itsDoubleTable = new double [64]; - } - else if (itsData.itsDoubleTable.Length == index) { - double [] na = new double [index * 2]; - Array.Copy (itsData.itsDoubleTable, 0, na, 0, index); - itsData.itsDoubleTable = na; - } - itsData.itsDoubleTable [index] = num; - itsDoubleTableTop = index + 1; - return index; - } - - void addVarOp (int op, int varIndex) - { - switch (op) { - - case Token.GETVAR: - case Token.SETVAR: - if (varIndex < 128) { - addIcode (op == Token.GETVAR ? Icode_GETVAR1 : Icode_SETVAR1); - addUint8 (varIndex); - return; - } - // fallthrough - goto case Icode_VAR_INC_DEC; - - case Icode_VAR_INC_DEC: - addIndexOp (op, varIndex); - return; - } - throw Context.CodeBug (); - } - - void addStringOp (int op, string str) - { - addStringPrefix (str); - if (validIcode (op)) { - addIcode (op); - } - else { - addToken (op); - } - } - - void addIndexOp (int op, int index) - { - addIndexPrefix (index); - if (validIcode (op)) { - addIcode (op); - } - else { - addToken (op); - } - } - - void addStringPrefix (string str) - { - int index = itsStrings.Get (str, -1); - if (index == -1) { - index = itsStrings.size (); - itsStrings.put (str, index); - } - if (index < 4) { - addIcode (Icode_REG_STR_C0 - index); - } - else if (index <= 0xFF) { - addIcode (Icode_REG_STR1); - addUint8 (index); - } - else if (index <= 0xFFFF) { - addIcode (Icode_REG_STR2); - addUint16 (index); - } - else { - addIcode (Icode_REG_STR4); - addInt (index); - } - } - - void addIndexPrefix (int index) - { - if (index < 0) - Context.CodeBug (); - if (index < 6) { - addIcode (Icode_REG_IND_C0 - index); - } - else if (index <= 0xFF) { - addIcode (Icode_REG_IND1); - addUint8 (index); - } - else if (index <= 0xFFFF) { - addIcode (Icode_REG_IND2); - addUint16 (index); - } - else { - addIcode (Icode_REG_IND4); - addInt (index); - } - } - - void addExceptionHandler (int icodeStart, int icodeEnd, int handlerStart, bool isFinally, int exceptionObjectLocal, int scopeLocal) - { - int top = itsExceptionTableTop; - int [] table = itsData.itsExceptionTable; - if (table == null) { - if (top != 0) - Context.CodeBug (); - table = new int [EXCEPTION_SLOT_SIZE * 2]; - itsData.itsExceptionTable = table; - } - else if (table.Length == top) { - table = new int [table.Length * 2]; - Array.Copy (itsData.itsExceptionTable, 0, table, 0, top); - itsData.itsExceptionTable = table; - } - table [top + EXCEPTION_TRY_START_SLOT] = icodeStart; - table [top + EXCEPTION_TRY_END_SLOT] = icodeEnd; - table [top + EXCEPTION_HANDLER_SLOT] = handlerStart; - table [top + EXCEPTION_TYPE_SLOT] = isFinally ? 1 : 0; - table [top + EXCEPTION_LOCAL_SLOT] = exceptionObjectLocal; - table [top + EXCEPTION_SCOPE_SLOT] = scopeLocal; - - itsExceptionTableTop = top + EXCEPTION_SLOT_SIZE; - } - - sbyte [] increaseICodeCapasity (int extraSize) - { - int capacity = itsData.itsICode.Length; - int top = itsICodeTop; - if (top + extraSize <= capacity) - throw Context.CodeBug (); - capacity *= 2; - if (top + extraSize > capacity) { - capacity = top + extraSize; - } - sbyte [] array = new sbyte [capacity]; - Array.Copy (itsData.itsICode, 0, array, 0, top); - itsData.itsICode = array; - return array; - } - - void stackChange (int change) - { - if (change <= 0) { - itsStackDepth += change; - } - else { - int newDepth = itsStackDepth + change; - if (newDepth > itsData.itsMaxStack) { - itsData.itsMaxStack = newDepth; - } - itsStackDepth = newDepth; - } - } - - int allocLocal () - { - int localSlot = itsLocalTop; - ++itsLocalTop; - if (itsLocalTop > itsData.itsMaxLocals) { - itsData.itsMaxLocals = itsLocalTop; - } - return localSlot; - } - - void releaseLocal (int localSlot) - { - --itsLocalTop; - if (localSlot != itsLocalTop) - Context.CodeBug (); - } - - static int GetShort (sbyte [] iCode, int pc) - { - return (iCode [pc] << 8) | (iCode [pc + 1] & 0xFF); - } - - static int GetIndex (sbyte [] iCode, int pc) - { - return ((iCode [pc] & 0xFF) << 8) | (iCode [pc + 1] & 0xFF); - } - - static int GetInt (sbyte [] iCode, int pc) - { - return (iCode [pc] << 24) | ((iCode [pc + 1] & 0xFF) << 16) | ((iCode [pc + 2] & 0xFF) << 8) | (iCode [pc + 3] & 0xFF); - } - - static int getExceptionHandler (CallFrame frame, bool onlyFinally) - { - int [] exceptionTable = frame.idata.itsExceptionTable; - if (exceptionTable == null) { - // No exception handlers - return -1; - } - - // Icode switch in the interpreter increments PC immediately - // and it is necessary to subtract 1 from the saved PC - // to point it before the start of the next instruction. - int pc = frame.pc - 1; - - // OPT: use binary search - int best = -1, bestStart = 0, bestEnd = 0; - for (int i = 0; i != exceptionTable.Length; i += EXCEPTION_SLOT_SIZE) { - int start = exceptionTable [i + EXCEPTION_TRY_START_SLOT]; - int end = exceptionTable [i + EXCEPTION_TRY_END_SLOT]; - if (!(start <= pc && pc < end)) { - continue; - } - if (onlyFinally && exceptionTable [i + EXCEPTION_TYPE_SLOT] != 1) { - continue; - } - if (best >= 0) { - // Since handlers always nest and they never have shared end - // although they can share start it is sufficient to compare - // handlers ends - if (bestEnd < end) { - continue; - } - // Check the above assumption - if (bestStart > start) - Context.CodeBug (); // should be nested - if (bestEnd == end) - Context.CodeBug (); // no ens sharing - } - best = i; - bestStart = start; - bestEnd = end; - } - return best; - } - - static void dumpICode (InterpreterData idata) - { - if (!Token.printICode) { - return; - } - - sbyte [] iCode = idata.itsICode; - int iCodeLength = iCode.Length; - string [] strings = idata.itsStringTable; - - System.IO.TextWriter sw = Console.Out; - sw.WriteLine ("ICode dump, for " + idata.itsName + ", length = " + iCodeLength); - sw.WriteLine ("MaxStack = " + idata.itsMaxStack); - - int indexReg = 0; - for (int pc = 0; pc < iCodeLength; ) { - sw.Flush (); - sw.Write (" [" + pc + "] "); - int token = iCode [pc]; - int icodeLength = bytecodeSpan (token); - string tname = bytecodeName (token); - int old_pc = pc; - ++pc; - switch (token) { - - default: - if (icodeLength != 1) - Context.CodeBug (); - sw.WriteLine (tname); - break; - - - - case Icode_GOSUB: - case Token.GOTO: - case Token.IFEQ: - case Token.IFNE: - case Icode_IFEQ_POP: - case Icode_LEAVEDQ: { - int newPC = pc + GetShort (iCode, pc) - 1; - sw.WriteLine (tname + " " + newPC); - pc += 2; - break; - } - - case Icode_VAR_INC_DEC: - case Icode_NAME_INC_DEC: - case Icode_PROP_INC_DEC: - case Icode_ELEM_INC_DEC: - case Icode_REF_INC_DEC: { - int incrDecrType = iCode [pc]; - sw.WriteLine (tname + " " + incrDecrType); - ++pc; - break; - } - - - case Icode_CALLSPECIAL: { - int callType = iCode [pc] & 0xFF; - bool isNew = (iCode [pc + 1] != 0); - int line = GetIndex (iCode, pc + 2); - sw.WriteLine (tname + " " + callType + " " + isNew + " " + indexReg + " " + line); - pc += 4; - break; - } - - - case Token.CATCH_SCOPE: { - bool afterFisrtFlag = (iCode [pc] != 0); - sw.WriteLine (tname + " " + afterFisrtFlag); - ++pc; - } - break; - - case Token.REGEXP: - sw.WriteLine (tname + " " + idata.itsRegExpLiterals [indexReg]); - break; - - case Token.OBJECTLIT: - case Icode_SPARE_ARRAYLIT: - sw.WriteLine (tname + " " + idata.literalIds [indexReg]); - break; - - case Icode_CLOSURE_EXPR: - case Icode_CLOSURE_STMT: - sw.WriteLine (tname + " " + idata.itsNestedFunctions [indexReg]); - break; - - case Token.CALL: - case Icode_TAIL_CALL: - case Token.REF_CALL: - case Token.NEW: - sw.WriteLine (tname + ' ' + indexReg); - break; - - case Token.THROW: { - int line = GetIndex (iCode, pc); - sw.WriteLine (tname + " : " + line); - pc += 2; - break; - } - - case Icode_SHORTNUMBER: { - int value = GetShort (iCode, pc); - sw.WriteLine (tname + " " + value); - pc += 2; - break; - } - - case Icode_INTNUMBER: { - int value = GetInt (iCode, pc); - sw.WriteLine (tname + " " + value); - pc += 4; - break; - } - - case Token.NUMBER: { - double value = idata.itsDoubleTable [indexReg]; - sw.WriteLine (tname + " " + value); - pc += 2; - break; - } - - case Icode_LINE: { - int line = GetIndex (iCode, pc); - sw.WriteLine (tname + " : " + line); - pc += 2; - break; - } - - case Icode_REG_STR1: { - string str = strings [0xFF & iCode [pc]]; - sw.WriteLine (tname + " \"" + str + '"'); - ++pc; - break; - } - - case Icode_REG_STR2: { - string str = strings [GetIndex (iCode, pc)]; - sw.WriteLine (tname + " \"" + str + '"'); - pc += 2; - break; - } - - case Icode_REG_STR4: { - string str = strings [GetInt (iCode, pc)]; - sw.WriteLine (tname + " \"" + str + '"'); - pc += 4; - break; - } - - case Icode_REG_IND1: { - indexReg = 0xFF & iCode [pc]; - sw.WriteLine (tname + " " + indexReg); - ++pc; - break; - } - - case Icode_REG_IND2: { - indexReg = GetIndex (iCode, pc); - sw.WriteLine (tname + " " + indexReg); - pc += 2; - break; - } - - case Icode_REG_IND4: { - indexReg = GetInt (iCode, pc); - sw.WriteLine (tname + " " + indexReg); - pc += 4; - break; - } - - case Icode_GETVAR1: - case Icode_SETVAR1: - indexReg = iCode [pc]; - sw.WriteLine (tname + " " + indexReg); - ++pc; - break; - } - if (old_pc + icodeLength != pc) - Context.CodeBug (); - } - - int [] table = idata.itsExceptionTable; - if (table != null) { - sw.WriteLine ("Exception handlers: " + table.Length / EXCEPTION_SLOT_SIZE); - for (int i = 0; i != table.Length; i += EXCEPTION_SLOT_SIZE) { - int tryStart = table [i + EXCEPTION_TRY_START_SLOT]; - int tryEnd = table [i + EXCEPTION_TRY_END_SLOT]; - int handlerStart = table [i + EXCEPTION_HANDLER_SLOT]; - int type = table [i + EXCEPTION_TYPE_SLOT]; - int exceptionLocal = table [i + EXCEPTION_LOCAL_SLOT]; - int scopeLocal = table [i + EXCEPTION_SCOPE_SLOT]; - - sw.WriteLine (" tryStart=" + tryStart + " tryEnd=" + tryEnd + " handlerStart=" + handlerStart + " type=" + (type == 0 ? "catch" : "finally") + " exceptionLocal=" + exceptionLocal); - } - } - sw.Flush (); - } - - static int bytecodeSpan (int bytecode) - { - switch (bytecode) { - - case Token.THROW: - // source line - return 1 + 2; - - - case Icode_GOSUB: - case Token.GOTO: - case Token.IFEQ: - case Token.IFNE: - case Icode_IFEQ_POP: - case Icode_LEAVEDQ: - // target pc offset - return 1 + 2; - - - case Icode_CALLSPECIAL: - // call type - // is new - // line number - return 1 + 1 + 1 + 2; - - - case Token.CATCH_SCOPE: - // scope flag - return 1 + 1; - - - case Icode_VAR_INC_DEC: - case Icode_NAME_INC_DEC: - case Icode_PROP_INC_DEC: - case Icode_ELEM_INC_DEC: - case Icode_REF_INC_DEC: - // type of ++/-- - return 1 + 1; - - - case Icode_SHORTNUMBER: - // short number - return 1 + 2; - - - case Icode_INTNUMBER: - // int number - return 1 + 4; - - - case Icode_REG_IND1: - // ubyte index - return 1 + 1; - - - case Icode_REG_IND2: - // ushort index - return 1 + 2; - - - case Icode_REG_IND4: - // int index - return 1 + 4; - - - case Icode_REG_STR1: - // ubyte string index - return 1 + 1; - - - case Icode_REG_STR2: - // ushort string index - return 1 + 2; - - - case Icode_REG_STR4: - // int string index - return 1 + 4; - - - case Icode_GETVAR1: - case Icode_SETVAR1: - // byte var index - return 1 + 1; - - - case Icode_LINE: - // line number - return 1 + 2; - } - - if (!validBytecode (bytecode)) - throw Context.CodeBug (); - - return 1; - } - - internal static int [] getLineNumbers (InterpreterData data) - { - UintMap presentLines = new UintMap (); - - sbyte [] iCode = data.itsICode; - int iCodeLength = iCode.Length; - for (int pc = 0; pc != iCodeLength; ) { - int bytecode = iCode [pc]; - int span = bytecodeSpan (bytecode); - if (bytecode == Icode_LINE) { - if (span != 3) - Context.CodeBug (); - int line = GetIndex (iCode, pc + 1); - presentLines.put (line, 0); - } - pc += span; - } - - return presentLines.Keys; - } - - internal static void captureInterpreterStackInfo (EcmaScriptException ex) - { - Context cx = Context.CurrentContext; - if (cx == null || cx.lastInterpreterFrame == null) { - // No interpreter invocations - ex.m_InterpreterStackInfo = null; - ex.m_InterpreterLineData = null; - return; - } - // has interpreter frame on the stack - CallFrame [] array; - if (cx.previousInterpreterInvocations == null || cx.previousInterpreterInvocations.size () == 0) { - array = new CallFrame [1]; - } - else { - int previousCount = cx.previousInterpreterInvocations.size (); - if (cx.previousInterpreterInvocations.peek () == cx.lastInterpreterFrame) { - // It can happen if exception was generated after - // frame was pushed to cx.previousInterpreterInvocations - // but before assignment to cx.lastInterpreterFrame. - // In this case frames has to be ignored. - --previousCount; - } - array = new CallFrame [previousCount + 1]; - cx.previousInterpreterInvocations.ToArray (array); - } - array [array.Length - 1] = (CallFrame)cx.lastInterpreterFrame; - - int interpreterFrameCount = 0; - for (int i = 0; i != array.Length; ++i) { - interpreterFrameCount += 1 + array [i].frameIndex; - } - - int [] linePC = new int [interpreterFrameCount]; - // Fill linePC with pc positions from all interpreter frames. - // Start from the most nested frame - int linePCIndex = interpreterFrameCount; - for (int i = array.Length; i != 0; ) { - --i; - CallFrame frame = array [i]; - while (frame != null) { - --linePCIndex; - linePC [linePCIndex] = frame.pcSourceLineStart; - frame = frame.parentFrame; - } - } - if (linePCIndex != 0) - Context.CodeBug (); - - ex.m_InterpreterStackInfo = array; - ex.m_InterpreterLineData = linePC; - } - - internal static string GetSourcePositionFromStack (Context cx, int [] linep) - { - CallFrame frame = (CallFrame)cx.lastInterpreterFrame; - InterpreterData idata = frame.idata; - if (frame.pcSourceLineStart >= 0) { - linep [0] = GetIndex (idata.itsICode, frame.pcSourceLineStart); - } - else { - linep [0] = 0; - } - return idata.itsSourceFile; - } - - - internal static string GetStack (EcmaScriptException ex) - { - System.Text.StringBuilder sb = new System.Text.StringBuilder (); - - CallFrame [] array = (CallFrame [])ex.m_InterpreterStackInfo; - if (array == null) // TODO: When does this happen? - return sb.ToString (); - - int [] linePC = ex.m_InterpreterLineData; - int arrayIndex = array.Length; - int linePCIndex = linePC.Length; - - while (arrayIndex != 0) { - --arrayIndex; - - CallFrame frame = array [arrayIndex]; - while (frame != null) { - if (linePCIndex == 0) - Context.CodeBug (); - --linePCIndex; - InterpreterData idata = frame.idata; - - if (sb.Length > 0) - sb.Append (Environment.NewLine); - sb.Append ("\tat script"); - if (idata.itsName != null && idata.itsName.Length != 0) { - sb.Append ('.'); - sb.Append (idata.itsName); - } - sb.Append ('('); - sb.Append (idata.itsSourceFile); - int pc = linePC [linePCIndex]; - if (pc >= 0) { - // Include line info only if available - sb.Append (':'); - sb.Append (GetIndex (idata.itsICode, pc)); - } - sb.Append (')'); - - - frame = frame.parentFrame; - - - } - } - - return sb.ToString (); - } - - internal static string getPatchedStack (EcmaScriptException ex, string nativeStackTrace) - { - string tag = "EcmaScript.NET.Interpreter.interpretLoop"; - System.Text.StringBuilder sb = new System.Text.StringBuilder (nativeStackTrace.Length + 1000); - string lineSeparator = System.Environment.NewLine; - - CallFrame [] array = (CallFrame [])ex.m_InterpreterStackInfo; - if (array == null) // TODO: when does this happen? - return sb.ToString (); - - int [] linePC = ex.m_InterpreterLineData; - int arrayIndex = array.Length; - int linePCIndex = linePC.Length; - int offset = 0; - while (arrayIndex != 0) { - --arrayIndex; - int pos = nativeStackTrace.IndexOf (tag, offset); - if (pos < 0) { - break; - } - - // Skip tag length - pos += tag.Length; - // Skip until the end of line - for (; pos != nativeStackTrace.Length; ++pos) { - char c = nativeStackTrace [pos]; - if (c == '\n' || c == '\r') { - break; - } - } - sb.Append (nativeStackTrace.Substring (offset, (pos) - (offset))); - offset = pos; - - CallFrame frame = array [arrayIndex]; - while (frame != null) { - if (linePCIndex == 0) - Context.CodeBug (); - --linePCIndex; - InterpreterData idata = frame.idata; - sb.Append (lineSeparator); - sb.Append ("\tat script"); - if (idata.itsName != null && idata.itsName.Length != 0) { - sb.Append ('.'); - sb.Append (idata.itsName); - } - sb.Append ('('); - sb.Append (idata.itsSourceFile); - int pc = linePC [linePCIndex]; - if (pc >= 0) { - // Include line info only if available - sb.Append (':'); - sb.Append (GetIndex (idata.itsICode, pc)); - } - sb.Append (')'); - frame = frame.parentFrame; - } - } - sb.Append (nativeStackTrace.Substring (offset)); - - return sb.ToString (); - } - - internal static string GetEncodedSource (InterpreterData idata) - { - if (idata.encodedSource == null) { - return null; - } - return idata.encodedSource.Substring (idata.encodedSourceStart, (idata.encodedSourceEnd) - (idata.encodedSourceStart)); - } - - static void initFunction (Context cx, IScriptable scope, InterpretedFunction parent, int index) - { - InterpretedFunction fn; - fn = InterpretedFunction.createFunction (cx, scope, parent, index); - ScriptRuntime.initFunction (cx, scope, fn, fn.idata.itsFunctionType, parent.idata.evalScriptFlag); - } - - internal static object Interpret (InterpretedFunction ifun, Context cx, IScriptable scope, IScriptable thisObj, object [] args) - { - using (Helpers.StackOverflowVerifier sov = new Helpers.StackOverflowVerifier (128)) { - if (!ScriptRuntime.hasTopCall (cx)) - Context.CodeBug (); - - if (cx.interpreterSecurityDomain != ifun.securityDomain) { - object savedDomain = cx.interpreterSecurityDomain; - cx.interpreterSecurityDomain = ifun.securityDomain; - try { - return ifun.securityController.callWithDomain (ifun.securityDomain, cx, ifun, scope, thisObj, args); - } - finally { - cx.interpreterSecurityDomain = savedDomain; - } - } - - CallFrame frame = new CallFrame (); - initFrame (cx, scope, thisObj, args, null, 0, args.Length, ifun, null, frame); - - return InterpretLoop (cx, frame, (object)null); - } - } - - public static object restartContinuation (Continuation c, Context cx, IScriptable scope, object [] args) - { - if (!ScriptRuntime.hasTopCall (cx)) { - return ScriptRuntime.DoTopCall (c, cx, scope, null, args); - } - - object arg; - if (args.Length == 0) { - arg = Undefined.Value; - } - else { - arg = args [0]; - } - - CallFrame capturedFrame = (CallFrame)c.Implementation; - if (capturedFrame == null) { - // No frames to restart - return arg; - } - - ContinuationJump cjump = new ContinuationJump (c, null); - - cjump.result = arg; - return InterpretLoop (cx, null, cjump); - } - - static object InterpretLoop (Context cx, CallFrame frame, object throwable) - { - // throwable holds exception object to rethrow or catch - // It is also used for continuation restart in which case - // it holds ContinuationJump - - object DBL_MRK = UniqueTag.DoubleMark; - object undefined = Undefined.Value; - - bool instructionCounting = (cx.instructionThreshold != 0); - // arbitrary number to add to instructionCount when calling - // other functions - const int INVOCATION_COST = 100; - - // arbitrary exception cost for instruction counting - const int EXCEPTION_COST = 100; - - string stringReg = null; - int indexReg = -1; - - if (cx.lastInterpreterFrame != null) { - // save the top frame from the previous interpreterLoop - // invocation on the stack - if (cx.previousInterpreterInvocations == null) { - cx.previousInterpreterInvocations = new ObjArray (); - } - cx.previousInterpreterInvocations.push (cx.lastInterpreterFrame); - } - - // When restarting continuation throwable is not null and to jump - // to the code that rewind continuation state indexReg should be set - // to -1. - // With the normal call throable == null and indexReg == -1 allows to - // catch bugs with using indeReg to access array eleemnts before - // initializing indexReg. - - if (throwable != null) { - // Assert assumptions - if (!(throwable is ContinuationJump)) { - // It should be continuation - Context.CodeBug (); - } - } - - object interpreterResult = null; - double interpreterResultDbl = 0.0; - - for (; ; ) { - - try { - - if (throwable != null) { - // Recovering from exception, indexReg contains - // the index of handler - - if (indexReg >= 0) { - // Normal excepton handler, transfer - // control appropriately - - if (frame.frozen) { - // TODO: Deal with exceptios!!! - frame = frame.cloneFrozen (); - } - - int [] table = frame.idata.itsExceptionTable; - - frame.pc = table [indexReg + EXCEPTION_HANDLER_SLOT]; - if (instructionCounting) { - frame.pcPrevBranch = frame.pc; - } - - frame.savedStackTop = frame.emptyStackTop; - int scopeLocal = frame.localShift + table [indexReg + EXCEPTION_SCOPE_SLOT]; - int exLocal = frame.localShift + table [indexReg + EXCEPTION_LOCAL_SLOT]; - frame.scope = (IScriptable)frame.stack [scopeLocal]; - frame.stack [exLocal] = throwable; - - throwable = null; - } - else { - // Continuation restoration - ContinuationJump cjump = (ContinuationJump)throwable; - - // Clear throwable to indicate that execptions are OK - throwable = null; - - if (cjump.branchFrame != frame) - Context.CodeBug (); - - // Check that we have at least one frozen frame - // in the case of detached continuation restoration: - // unwind code ensure that - if (cjump.capturedFrame == null) - Context.CodeBug (); - - // Need to rewind branchFrame, capturedFrame - // and all frames in between - int rewindCount = cjump.capturedFrame.frameIndex + 1; - if (cjump.branchFrame != null) { - rewindCount -= cjump.branchFrame.frameIndex; - } - - int enterCount = 0; - CallFrame [] enterFrames = null; - - CallFrame x = cjump.capturedFrame; - for (int i = 0; i != rewindCount; ++i) { - if (!x.frozen) - Context.CodeBug (); - if (isFrameEnterExitRequired (x)) { - if (enterFrames == null) { - // Allocate enough space to store the rest - // of rewind frames in case all of them - // would require to enter - enterFrames = new CallFrame [rewindCount - i]; - } - enterFrames [enterCount] = x; - ++enterCount; - } - x = x.parentFrame; - } - - while (enterCount != 0) { - // execute enter: walk enterFrames in the reverse - // order since they were stored starting from - // the capturedFrame, not branchFrame - --enterCount; - x = enterFrames [enterCount]; - EnterFrame (cx, x, ScriptRuntime.EmptyArgs); - } - - // Continuation jump is almost done: capturedFrame - // points to the call to the function that captured - // continuation, so clone capturedFrame and - // emulate return that function with the suplied result - frame = cjump.capturedFrame.cloneFrozen (); - setCallResult (frame, cjump.result, cjump.resultDbl); - // restart the execution - } - - // Should be already cleared - if (throwable != null) - Context.CodeBug (); - } - else { - if (frame.frozen) - Context.CodeBug (); - } - - // Use local variables for constant values in frame - // for faster access - object [] stack = frame.stack; - double [] sDbl = frame.sDbl; - object [] vars = frame.varSource.stack; - double [] varDbls = frame.varSource.sDbl; - - sbyte [] iCode = frame.idata.itsICode; - string [] strings = frame.idata.itsStringTable; - - // Use local for stackTop as well. Since execption handlers - // can only exist at statement level where stack is empty, - // it is necessary to save/restore stackTop only accross - // function calls and normal returns. - int stackTop = frame.savedStackTop; - - // Store new frame in cx which is used for error reporting etc. - cx.lastInterpreterFrame = frame; - - for (; ; ) { - - // Exception handler assumes that PC is already incremented - // pass the instruction start when it searches the - // exception handler - int op = iCode [frame.pc++]; - { - switch (op) { - - case Token.THROW: { - object value = stack [stackTop]; - if (value == DBL_MRK) - value = sDbl [stackTop]; - stackTop--; - - int sourceLine = GetIndex (iCode, frame.pc); - throwable = new EcmaScriptThrow ( - value, frame.idata.itsSourceFile, sourceLine); - goto withoutExceptions_brk; - } - - case Token.RETHROW: { - indexReg += frame.localShift; - throwable = stack [indexReg]; - break; - } - - case Token.GE: - case Token.LE: - case Token.GT: - case Token.LT: { - --stackTop; - object rhs = stack [stackTop + 1]; - object lhs = stack [stackTop]; - bool valBln; - { - { - double rDbl, lDbl; - if (rhs == DBL_MRK) { - rDbl = sDbl [stackTop + 1]; - lDbl = stack_double (frame, stackTop); - } - else if (lhs == DBL_MRK) { - rDbl = ScriptConvert.ToNumber (rhs); - lDbl = sDbl [stackTop]; - } - else { - - goto number_compare_brk; - } - switch (op) { - - case Token.GE: - valBln = (lDbl >= rDbl); - - goto object_compare_brk; - - case Token.LE: - valBln = (lDbl <= rDbl); - - goto object_compare_brk; - - case Token.GT: - valBln = (lDbl > rDbl); - - goto object_compare_brk; - - case Token.LT: - valBln = (lDbl < rDbl); - - goto object_compare_brk; - - default: - throw Context.CodeBug (); - - } - } - - number_compare_brk: - ; - - switch (op) { - - case Token.GE: - valBln = ScriptRuntime.cmp_LE (rhs, lhs); - break; - - case Token.LE: - valBln = ScriptRuntime.cmp_LE (lhs, rhs); - break; - - case Token.GT: - valBln = ScriptRuntime.cmp_LT (rhs, lhs); - break; - - case Token.LT: - valBln = ScriptRuntime.cmp_LT (lhs, rhs); - break; - - default: - throw Context.CodeBug (); - - } - } - - object_compare_brk: - ; - - stack [stackTop] = valBln; - - goto Loop; - } - goto case Token.IN; - - case Token.IN: - case Token.INSTANCEOF: { - object rhs = stack [stackTop]; - if (rhs == DBL_MRK) - rhs = sDbl [stackTop]; - --stackTop; - object lhs = stack [stackTop]; - if (lhs == DBL_MRK) - lhs = sDbl [stackTop]; - bool valBln; - if (op == Token.IN) { - valBln = ScriptRuntime.In (lhs, rhs, cx); - } - else { - valBln = ScriptRuntime.InstanceOf (lhs, rhs, cx); - } - stack [stackTop] = valBln; - - goto Loop; - } - goto case Token.EQ; - - case Token.EQ: - case Token.NE: { - --stackTop; - bool valBln; - object rhs = stack [stackTop + 1]; - object lhs = stack [stackTop]; - if (rhs == DBL_MRK) { - if (lhs == DBL_MRK) { - valBln = (sDbl [stackTop] == sDbl [stackTop + 1]); - } - else { - valBln = ScriptRuntime.eqNumber (sDbl [stackTop + 1], lhs); - } - } - else { - if (lhs == DBL_MRK) { - valBln = ScriptRuntime.eqNumber (sDbl [stackTop], rhs); - } - else { - valBln = ScriptRuntime.eq (lhs, rhs); - } - } - valBln ^= (op == Token.NE); - stack [stackTop] = valBln; - - goto Loop; - } - goto case Token.SHEQ; - - case Token.SHEQ: - case Token.SHNE: { - --stackTop; - object rhs = stack [stackTop + 1]; - object lhs = stack [stackTop]; - bool valBln; - { - double rdbl, ldbl; - if (rhs == DBL_MRK) { - rdbl = sDbl [stackTop + 1]; - if (lhs == DBL_MRK) { - ldbl = sDbl [stackTop]; - } - else if (CliHelper.IsNumber (lhs)) { - ldbl = Convert.ToDouble (lhs); - } - else { - valBln = false; - - goto shallow_compare_brk; - } - } - else if (lhs == DBL_MRK) { - ldbl = sDbl [stackTop]; - if (rhs == DBL_MRK) { - rdbl = sDbl [stackTop + 1]; - } - else if (CliHelper.IsNumber (rhs)) { - rdbl = Convert.ToDouble (rhs); - } - else { - valBln = false; - - goto shallow_compare_brk; - } - } - else { - valBln = ScriptRuntime.shallowEq (lhs, rhs); - - goto shallow_compare_brk; - } - valBln = (ldbl == rdbl); - } - - shallow_compare_brk: - ; - - valBln ^= (op == Token.SHNE); - stack [stackTop] = valBln; - - goto Loop; - } - goto case Token.IFNE; - - case Token.IFNE: - if (stack_boolean (frame, stackTop--)) { - frame.pc += 2; - - goto Loop; - } - - goto jumplessRun_brk; - - case Token.IFEQ: - if (!stack_boolean (frame, stackTop--)) { - frame.pc += 2; - - goto Loop; - } - - goto jumplessRun_brk; - - case Icode_IFEQ_POP: - if (!stack_boolean (frame, stackTop--)) { - frame.pc += 2; - - goto Loop; - } - stack [stackTop--] = null; - - goto jumplessRun_brk; - - case Token.GOTO: - - goto jumplessRun_brk; - - case Icode_GOSUB: - ++stackTop; - stack [stackTop] = DBL_MRK; - sDbl [stackTop] = frame.pc + 2; - - goto jumplessRun_brk; - - case Icode_STARTSUB: - if (stackTop == frame.emptyStackTop + 1) { - // Call from Icode_GOSUB: store return PC address in the local - indexReg += frame.localShift; - stack [indexReg] = stack [stackTop]; - sDbl [indexReg] = sDbl [stackTop]; - --stackTop; - } - else { - // Call from exception handler: exception object is already stored - // in the local - if (stackTop != frame.emptyStackTop) - Context.CodeBug (); - } - - goto Loop; - goto case Icode_RETSUB; - - case Icode_RETSUB: { - // indexReg: local to store return address - if (instructionCounting) { - addInstructionCount (cx, frame, 0); - } - indexReg += frame.localShift; - object value = stack [indexReg]; - if (value != DBL_MRK) { - // Invocation from exception handler, restore object to rethrow - throwable = value; - goto withoutExceptions_brk; - } - // Normal return from GOSUB - frame.pc = (int)sDbl [indexReg]; - if (instructionCounting) { - frame.pcPrevBranch = frame.pc; - } - - goto Loop; - } - goto case Icode_POP; - - case Icode_POP: - stack [stackTop] = null; - stackTop--; - - goto Loop; - goto case Icode_POP_RESULT; - - case Icode_POP_RESULT: - frame.result = stack [stackTop]; - frame.resultDbl = sDbl [stackTop]; - stack [stackTop] = null; - --stackTop; - - goto Loop; - goto case Icode_DUP; - - case Icode_DUP: - stack [stackTop + 1] = stack [stackTop]; - sDbl [stackTop + 1] = sDbl [stackTop]; - stackTop++; - - goto Loop; - goto case Icode_DUP2; - - case Icode_DUP2: - stack [stackTop + 1] = stack [stackTop - 1]; - sDbl [stackTop + 1] = sDbl [stackTop - 1]; - stack [stackTop + 2] = stack [stackTop]; - sDbl [stackTop + 2] = sDbl [stackTop]; - stackTop += 2; - - goto Loop; - goto case Icode_SWAP; - - case Icode_SWAP: { - object o = stack [stackTop]; - stack [stackTop] = stack [stackTop - 1]; - stack [stackTop - 1] = o; - double d = sDbl [stackTop]; - sDbl [stackTop] = sDbl [stackTop - 1]; - sDbl [stackTop - 1] = d; - - goto Loop; - } - goto case Token.RETURN; - - case Token.RETURN: - frame.result = stack [stackTop]; - frame.resultDbl = sDbl [stackTop]; - --stackTop; - - goto Loop_brk; - - case Token.RETURN_RESULT: - - goto Loop_brk; - - case Icode_RETUNDEF: - frame.result = undefined; - - goto Loop_brk; - - case Token.BITNOT: { - int rIntValue = stack_int32 (frame, stackTop); - stack [stackTop] = DBL_MRK; - sDbl [stackTop] = ~rIntValue; - - goto Loop; - } - goto case Token.BITAND; - - case Token.BITAND: - case Token.BITOR: - case Token.BITXOR: - case Token.LSH: - case Token.RSH: { - int rIntValue = stack_int32 (frame, stackTop); - --stackTop; - int lIntValue = stack_int32 (frame, stackTop); - stack [stackTop] = DBL_MRK; - switch (op) { - - case Token.BITAND: - lIntValue &= rIntValue; - break; - - case Token.BITOR: - lIntValue |= rIntValue; - break; - - case Token.BITXOR: - lIntValue ^= rIntValue; - break; - - case Token.LSH: - lIntValue <<= rIntValue; - break; - - case Token.RSH: - lIntValue >>= rIntValue; - break; - } - sDbl [stackTop] = lIntValue; - - goto Loop; - } - goto case Token.URSH; - - case Token.URSH: { - int rIntValue = stack_int32 (frame, stackTop) & 0x1F; - --stackTop; - double lDbl = stack_double (frame, stackTop); - stack [stackTop] = DBL_MRK; - uint i = (uint)ScriptConvert.ToUint32 (lDbl); - sDbl [stackTop] = i >> rIntValue; - - goto Loop; - } - goto case Token.NEG; - - case Token.NEG: - case Token.POS: { - double rDbl = stack_double (frame, stackTop); - stack [stackTop] = DBL_MRK; - if (op == Token.NEG) { - rDbl = -rDbl; - } - sDbl [stackTop] = rDbl; - - goto Loop; - } - goto case Token.ADD; - - case Token.ADD: - --stackTop; - DoAdd (stack, sDbl, stackTop, cx); - - goto Loop; - goto case Token.SUB; - - case Token.SUB: - case Token.MUL: - case Token.DIV: - case Token.MOD: { - double rDbl = stack_double (frame, stackTop); - --stackTop; - double lDbl = stack_double (frame, stackTop); - stack [stackTop] = DBL_MRK; - switch (op) { - - case Token.SUB: - lDbl -= rDbl; - break; - - case Token.MUL: - lDbl *= rDbl; - break; - - case Token.DIV: - lDbl /= rDbl; - break; - - case Token.MOD: - lDbl %= rDbl; - break; - } - sDbl [stackTop] = lDbl; - - goto Loop; - } - goto case Token.NOT; - - case Token.NOT: - stack [stackTop] = !stack_boolean (frame, stackTop); - - goto Loop; - goto case Token.BINDNAME; - - case Token.BINDNAME: - stack [++stackTop] = ScriptRuntime.bind (cx, frame.scope, stringReg); - - goto Loop; - goto case Token.SETNAME; - - case Token.SETNAME: { - object rhs = stack [stackTop]; - if (rhs == DBL_MRK) - rhs = sDbl [stackTop]; - --stackTop; - IScriptable lhs = (IScriptable)stack [stackTop]; - stack [stackTop] = ScriptRuntime.setName (lhs, rhs, cx, frame.scope, stringReg); - - goto Loop; - } - goto case Token.DELPROP; - - case Token.DELPROP: { - object rhs = stack [stackTop]; - if (rhs == DBL_MRK) - rhs = sDbl [stackTop]; - --stackTop; - object lhs = stack [stackTop]; - if (lhs == DBL_MRK) - lhs = sDbl [stackTop]; - stack [stackTop] = ScriptRuntime.delete (lhs, rhs, cx); - - goto Loop; - } - goto case Token.GETPROP; - - case Token.GETPROP: { - object lhs = stack [stackTop]; - if (lhs == DBL_MRK) - lhs = sDbl [stackTop]; - stack [stackTop] = ScriptRuntime.getObjectProp (lhs, stringReg, cx); - - goto Loop; - } - goto case Token.SETPROP; - - case Token.SETPROP_GETTER: - case Token.SETPROP_SETTER: - case Token.SETPROP: { - object rhs = stack [stackTop]; - if (rhs == DBL_MRK) - rhs = sDbl [stackTop]; - --stackTop; - object lhs = stack [stackTop]; - if (lhs == DBL_MRK) - lhs = sDbl [stackTop]; - - switch (op) { - case Token.SETPROP_GETTER: - ((ScriptableObject)lhs).DefineGetter(stringReg, ((ICallable)rhs)); - stack[stackTop] = rhs; - break; - - case Token.SETPROP_SETTER: - ((ScriptableObject)lhs).DefineSetter(stringReg, ((ICallable)rhs)); - stack[stackTop] = rhs; - break; - - - default: - stack [stackTop] = ScriptRuntime.setObjectProp (lhs, stringReg, rhs, cx); - break; - } - - goto Loop; - } - goto case Icode_PROP_INC_DEC; - - case Icode_PROP_INC_DEC: { - object lhs = stack [stackTop]; - if (lhs == DBL_MRK) - lhs = sDbl [stackTop]; - stack [stackTop] = ScriptRuntime.propIncrDecr (lhs, stringReg, cx, iCode [frame.pc]); - ++frame.pc; - - goto Loop; - } - goto case Token.GETELEM; - - case Token.GETELEM: { - --stackTop; - object lhs = stack [stackTop]; - if (lhs == DBL_MRK) { - lhs = sDbl [stackTop]; - } - object value; - object id = stack [stackTop + 1]; - if (id != DBL_MRK) { - value = ScriptRuntime.getObjectElem (lhs, id, cx); - } - else { - double d = sDbl [stackTop + 1]; - value = ScriptRuntime.getObjectIndex (lhs, d, cx); - } - stack [stackTop] = value; - - goto Loop; - } - goto case Token.SETELEM; - - case Token.SETELEM: { - stackTop -= 2; - object rhs = stack [stackTop + 2]; - if (rhs == DBL_MRK) { - rhs = sDbl [stackTop + 2]; - } - object lhs = stack [stackTop]; - if (lhs == DBL_MRK) { - lhs = sDbl [stackTop]; - } - object value; - object id = stack [stackTop + 1]; - if (id != DBL_MRK) { - value = ScriptRuntime.setObjectElem (lhs, id, rhs, cx); - } - else { - double d = sDbl [stackTop + 1]; - value = ScriptRuntime.setObjectIndex (lhs, d, rhs, cx); - } - stack [stackTop] = value; - - goto Loop; - } - goto case Icode_ELEM_INC_DEC; - - case Icode_ELEM_INC_DEC: { - object rhs = stack [stackTop]; - if (rhs == DBL_MRK) - rhs = sDbl [stackTop]; - --stackTop; - object lhs = stack [stackTop]; - if (lhs == DBL_MRK) - lhs = sDbl [stackTop]; - stack [stackTop] = ScriptRuntime.elemIncrDecr (lhs, rhs, cx, iCode [frame.pc]); - ++frame.pc; - - goto Loop; - } - goto case Token.GET_REF; - - case Token.GET_REF: { - IRef rf = (IRef)stack [stackTop]; - stack [stackTop] = ScriptRuntime.refGet (rf, cx); - - goto Loop; - } - goto case Token.SET_REF; - - case Token.SET_REF: { - object value = stack [stackTop]; - if (value == DBL_MRK) - value = sDbl [stackTop]; - --stackTop; - IRef rf = (IRef)stack [stackTop]; - stack [stackTop] = ScriptRuntime.refSet (rf, value, cx); - - goto Loop; - } - goto case Token.DEL_REF; - - case Token.DEL_REF: { - IRef rf = (IRef)stack [stackTop]; - stack [stackTop] = ScriptRuntime.refDel (rf, cx); - - goto Loop; - } - goto case Icode_REF_INC_DEC; - - case Icode_REF_INC_DEC: { - IRef rf = (IRef)stack [stackTop]; - stack [stackTop] = ScriptRuntime.refIncrDecr (rf, cx, iCode [frame.pc]); - ++frame.pc; - - goto Loop; - } - goto case Token.LOCAL_LOAD; - - case Token.LOCAL_LOAD: - ++stackTop; - indexReg += frame.localShift; - stack [stackTop] = stack [indexReg]; - sDbl [stackTop] = sDbl [indexReg]; - - goto Loop; - goto case Icode_LOCAL_CLEAR; - - case Icode_LOCAL_CLEAR: - indexReg += frame.localShift; - stack [indexReg] = null; - - goto Loop; - goto case Icode_NAME_AND_THIS; - - case Icode_NAME_AND_THIS: - // stringReg: name - ++stackTop; - stack [stackTop] = ScriptRuntime.getNameFunctionAndThis (stringReg, cx, frame.scope); - ++stackTop; - stack [stackTop] = ScriptRuntime.lastStoredScriptable (cx); - - goto Loop; - goto case Icode_PROP_AND_THIS; - - case Icode_PROP_AND_THIS: { - object obj = stack [stackTop]; - if (obj == DBL_MRK) - obj = sDbl [stackTop]; - // stringReg: property - stack [stackTop] = ScriptRuntime.getPropFunctionAndThis (obj, stringReg, cx); - ++stackTop; - stack [stackTop] = ScriptRuntime.lastStoredScriptable (cx); - - goto Loop; - } - goto case Icode_ELEM_AND_THIS; - - case Icode_ELEM_AND_THIS: { - object obj = stack [stackTop - 1]; - if (obj == DBL_MRK) - obj = sDbl [stackTop - 1]; - object id = stack [stackTop]; - if (id == DBL_MRK) - id = sDbl [stackTop]; - stack [stackTop - 1] = ScriptRuntime.GetElemFunctionAndThis (obj, id, cx); - stack [stackTop] = ScriptRuntime.lastStoredScriptable (cx); - - goto Loop; - } - goto case Icode_VALUE_AND_THIS; - - case Icode_VALUE_AND_THIS: { - object value = stack [stackTop]; - if (value == DBL_MRK) - value = sDbl [stackTop]; - stack [stackTop] = ScriptRuntime.getValueFunctionAndThis (value, cx); - ++stackTop; - stack [stackTop] = ScriptRuntime.lastStoredScriptable (cx); - - goto Loop; - } - goto case Icode_CALLSPECIAL; - - case Icode_CALLSPECIAL: { - if (instructionCounting) { - cx.instructionCount += INVOCATION_COST; - } - int callType = iCode [frame.pc] & 0xFF; - bool isNew = (iCode [frame.pc + 1] != 0); - int sourceLine = GetIndex (iCode, frame.pc + 2); - - // indexReg: number of arguments - if (isNew) { - // stack change: function arg0 .. argN -> newResult - stackTop -= indexReg; - - object function = stack [stackTop]; - if (function == DBL_MRK) - function = sDbl [stackTop]; - object [] outArgs = GetArgsArray (stack, sDbl, stackTop + 1, indexReg); - stack [stackTop] = ScriptRuntime.newSpecial (cx, function, outArgs, frame.scope, callType); - } - else { - // stack change: function thisObj arg0 .. argN -> result - stackTop -= (1 + indexReg); - - // Call code generation ensure that stack here - // is ... Callable Scriptable - IScriptable functionThis = (IScriptable)stack [stackTop + 1]; - ICallable function = (ICallable)stack [stackTop]; - object [] outArgs = GetArgsArray (stack, sDbl, stackTop + 2, indexReg); - stack [stackTop] = ScriptRuntime.callSpecial (cx, function, functionThis, outArgs, frame.scope, frame.thisObj, callType, frame.idata.itsSourceFile, sourceLine); - } - frame.pc += 4; - - goto Loop; - } - goto case Token.CALL; - - case Token.CALL: - case Icode_TAIL_CALL: - case Token.REF_CALL: { - if (instructionCounting) { - cx.instructionCount += INVOCATION_COST; - } - // stack change: function thisObj arg0 .. argN -> result - // indexReg: number of arguments - stackTop -= (1 + indexReg); - - // CALL generation ensures that fun and funThisObj - // are already Scriptable and Callable objects respectively - ICallable fun = (ICallable)stack [stackTop]; - IScriptable funThisObj = (IScriptable)stack [stackTop + 1]; - if (op == Token.REF_CALL) { - object [] outArgs = GetArgsArray (stack, sDbl, stackTop + 2, indexReg); - stack [stackTop] = ScriptRuntime.callRef (fun, funThisObj, outArgs, cx); - - goto Loop; - } - IScriptable calleeScope = frame.scope; - if (frame.useActivation) { - calleeScope = ScriptableObject.GetTopLevelScope (frame.scope); - } - if (fun is InterpretedFunction) { - InterpretedFunction ifun = (InterpretedFunction)fun; - if (frame.fnOrScript.securityDomain == ifun.securityDomain) { - CallFrame callParentFrame = frame; - CallFrame calleeFrame = new CallFrame (); - if (op == Icode_TAIL_CALL) { - // In principle tail call can re-use the current - // frame and its stack arrays but it is hard to - // do properly. Any exceptions that can legally - // happen during frame re-initialization including - // StackOverflowException during innocent looking - // System.arraycopy may leave the current frame - // data corrupted leading to undefined behaviour - // in the catch code bellow that unwinds JS stack - // on exceptions. Then there is issue about frame release - // end exceptions there. - // To avoid frame allocation a released frame - // can be cached for re-use which would also benefit - // non-tail calls but it is not clear that this caching - // would gain in performance due to potentially - // bad iteraction with GC. - callParentFrame = frame.parentFrame; - } - initFrame (cx, calleeScope, funThisObj, stack, sDbl, stackTop + 2, indexReg, ifun, callParentFrame, calleeFrame); - if (op == Icode_TAIL_CALL) { - // Release the parent - ExitFrame (cx, frame, (object)null); - } - else { - frame.savedStackTop = stackTop; - frame.savedCallOp = op; - } - frame = calleeFrame; - - goto StateLoop; - } - } - - if (fun is Continuation) { - // Jump to the captured continuation - ContinuationJump cjump; - cjump = new ContinuationJump ((Continuation)fun, frame); - - // continuation result is the first argument if any - // of contination call - if (indexReg == 0) { - cjump.result = undefined; - } - else { - cjump.result = stack [stackTop + 2]; - cjump.resultDbl = sDbl [stackTop + 2]; - } - - // Start the real unwind job - throwable = cjump; - break; - } - - if (fun is IdFunctionObject) { - IdFunctionObject ifun = (IdFunctionObject)fun; - if (Continuation.IsContinuationConstructor (ifun)) { - captureContinuation (cx, frame, stackTop); - - goto Loop; - } - } - - object [] outArgs2 = GetArgsArray (stack, sDbl, stackTop + 2, indexReg); - stack [stackTop] = fun.Call (cx, calleeScope, funThisObj, outArgs2); - - - goto Loop; - } - goto case Token.NEW; - - case Token.NEW: { - if (instructionCounting) { - cx.instructionCount += INVOCATION_COST; - } - // stack change: function arg0 .. argN -> newResult - // indexReg: number of arguments - stackTop -= indexReg; - - object lhs = stack [stackTop]; - if (lhs is InterpretedFunction) { - InterpretedFunction f = (InterpretedFunction)lhs; - if (frame.fnOrScript.securityDomain == f.securityDomain) { - IScriptable newInstance = f.CreateObject (cx, frame.scope); - CallFrame calleeFrame = new CallFrame (); - initFrame (cx, frame.scope, newInstance, stack, sDbl, stackTop + 1, indexReg, f, frame, calleeFrame); - - stack [stackTop] = newInstance; - frame.savedStackTop = stackTop; - frame.savedCallOp = op; - frame = calleeFrame; - - goto StateLoop; - } - } - if (!(lhs is IFunction)) { - if (lhs == DBL_MRK) - lhs = sDbl [stackTop]; - throw ScriptRuntime.NotFunctionError (lhs); - } - IFunction fun = (IFunction)lhs; - - if (fun is IdFunctionObject) { - IdFunctionObject ifun = (IdFunctionObject)fun; - if (Continuation.IsContinuationConstructor (ifun)) { - captureContinuation (cx, frame, stackTop); - - goto Loop; - } - } - - object [] outArgs = GetArgsArray (stack, sDbl, stackTop + 1, indexReg); - stack [stackTop] = fun.Construct (cx, frame.scope, outArgs); - - goto Loop; - } - goto case Token.TYPEOF; - - case Token.TYPEOF: { - object lhs = stack [stackTop]; - if (lhs == DBL_MRK) - lhs = sDbl [stackTop]; - stack [stackTop] = ScriptRuntime.Typeof (lhs); - - goto Loop; - } - goto case Icode_TYPEOFNAME; - - case Icode_TYPEOFNAME: - stack [++stackTop] = ScriptRuntime.TypeofName (frame.scope, stringReg); - - goto Loop; - goto case Token.STRING; - - case Token.STRING: - stack [++stackTop] = stringReg; - - goto Loop; - goto case Icode_SHORTNUMBER; - - case Icode_SHORTNUMBER: - ++stackTop; - stack [stackTop] = DBL_MRK; - sDbl [stackTop] = GetShort (iCode, frame.pc); - frame.pc += 2; - - goto Loop; - goto case Icode_INTNUMBER; - - case Icode_INTNUMBER: - ++stackTop; - stack [stackTop] = DBL_MRK; - sDbl [stackTop] = GetInt (iCode, frame.pc); - frame.pc += 4; - - goto Loop; - goto case Token.NUMBER; - - case Token.NUMBER: - ++stackTop; - stack [stackTop] = DBL_MRK; - sDbl [stackTop] = frame.idata.itsDoubleTable [indexReg]; - - goto Loop; - goto case Token.NAME; - - case Token.NAME: - stack [++stackTop] = ScriptRuntime.name (cx, frame.scope, stringReg); - - goto Loop; - goto case Icode_NAME_INC_DEC; - - case Icode_NAME_INC_DEC: - stack [++stackTop] = ScriptRuntime.nameIncrDecr (frame.scope, stringReg, iCode [frame.pc]); - ++frame.pc; - - goto Loop; - goto case Icode_SETVAR1; - - case Icode_SETVAR1: - indexReg = iCode [frame.pc++]; - // fallthrough - goto case Token.SETVAR; - - case Token.SETVAR: - if (!frame.useActivation) { - vars [indexReg] = stack [stackTop]; - varDbls [indexReg] = sDbl [stackTop]; - } - else { - object val = stack [stackTop]; - if (val == DBL_MRK) - val = sDbl [stackTop]; - stringReg = frame.idata.argNames [indexReg]; - frame.scope.Put (stringReg, frame.scope, val); - } - - goto Loop; - goto case Icode_GETVAR1; - - case Icode_GETVAR1: - indexReg = iCode [frame.pc++]; - // fallthrough - goto case Token.GETVAR; - - case Token.GETVAR: - ++stackTop; - if (!frame.useActivation) { - stack [stackTop] = vars [indexReg]; - sDbl [stackTop] = varDbls [indexReg]; - } - else { - stringReg = frame.idata.argNames [indexReg]; - stack [stackTop] = frame.scope.Get (stringReg, frame.scope); - } - - goto Loop; - goto case Icode_VAR_INC_DEC; - - case Icode_VAR_INC_DEC: { - // indexReg : varindex - ++stackTop; - int incrDecrMask = iCode [frame.pc]; - if (!frame.useActivation) { - stack [stackTop] = DBL_MRK; - object varValue = vars [indexReg]; - double d; - if (varValue == DBL_MRK) { - d = varDbls [indexReg]; - } - else { - d = ScriptConvert.ToNumber (varValue); - vars [indexReg] = DBL_MRK; - } - double d2 = ((incrDecrMask & Node.DECR_FLAG) == 0) ? d + 1.0 : d - 1.0; - varDbls [indexReg] = d2; - sDbl [stackTop] = ((incrDecrMask & Node.POST_FLAG) == 0) ? d2 : d; - } - else { - string varName = frame.idata.argNames [indexReg]; - stack [stackTop] = ScriptRuntime.nameIncrDecr (frame.scope, varName, incrDecrMask); - } - ++frame.pc; - - goto Loop; - } - goto case Icode_ZERO; - - case Icode_ZERO: - ++stackTop; - stack [stackTop] = DBL_MRK; - sDbl [stackTop] = 0; - - goto Loop; - goto case Icode_ONE; - - case Icode_ONE: - ++stackTop; - stack [stackTop] = DBL_MRK; - sDbl [stackTop] = 1; - - goto Loop; - goto case Token.NULL; - - case Token.NULL: - stack [++stackTop] = null; - - goto Loop; - goto case Token.THIS; - - case Token.THIS: - stack [++stackTop] = frame.thisObj; - - goto Loop; - goto case Token.THISFN; - - case Token.THISFN: - stack [++stackTop] = frame.fnOrScript; - - goto Loop; - goto case Token.FALSE; - - case Token.FALSE: - stack [++stackTop] = false; - - goto Loop; - goto case Token.TRUE; - - case Token.TRUE: - stack [++stackTop] = true; - - goto Loop; - goto case Icode_UNDEF; - - case Icode_UNDEF: - stack [++stackTop] = undefined; - - goto Loop; - goto case Token.ENTERWITH; - - case Token.ENTERWITH: { - object lhs = stack [stackTop]; - if (lhs == DBL_MRK) - lhs = sDbl [stackTop]; - --stackTop; - frame.scope = ScriptRuntime.enterWith (lhs, cx, frame.scope); - - goto Loop; - } - goto case Token.LEAVEWITH; - - case Token.LEAVEWITH: - frame.scope = ScriptRuntime.leaveWith (frame.scope); - - goto Loop; - goto case Token.CATCH_SCOPE; - - case Token.CATCH_SCOPE: { - // stack top: exception object - // stringReg: name of exception variable - // indexReg: local for exception scope - --stackTop; - indexReg += frame.localShift; - - bool afterFirstScope = (frame.idata.itsICode [frame.pc] != 0); - - Exception caughtException = (Exception)stack [stackTop + 1]; - IScriptable lastCatchScope; - if (!afterFirstScope) { - lastCatchScope = null; - } - else { - lastCatchScope = (IScriptable)stack [indexReg]; - } - stack [indexReg] = ScriptRuntime.NewCatchScope (caughtException, lastCatchScope, stringReg, cx, frame.scope); - ++frame.pc; - - goto Loop; - } - goto case Token.ENUM_INIT_KEYS; - - case Token.ENUM_INIT_KEYS: - case Token.ENUM_INIT_VALUES: { - object lhs = stack [stackTop]; - if (lhs == DBL_MRK) - lhs = sDbl [stackTop]; - --stackTop; - indexReg += frame.localShift; - - if (lhs is IIdEnumerable) { - stack [indexReg] = ((IIdEnumerable)lhs).GetEnumeration (cx, (op == Token.ENUM_INIT_VALUES)); - } - else { - stack [indexReg] = new IdEnumeration (lhs, cx, (op == Token.ENUM_INIT_VALUES)); - } - - - goto Loop; - } - goto case Token.ENUM_NEXT; - - case Token.ENUM_NEXT: - case Token.ENUM_ID: { - indexReg += frame.localShift; - IdEnumeration val = (IdEnumeration)stack [indexReg]; - ++stackTop; - stack [stackTop] = (op == Token.ENUM_NEXT) ? val.MoveNext () : val.Current (cx); - - goto Loop; - } - goto case Token.REF_SPECIAL; - - case Token.REF_SPECIAL: { - //stringReg: name of special property - object obj = stack [stackTop]; - if (obj == DBL_MRK) - obj = sDbl [stackTop]; - stack [stackTop] = ScriptRuntime.specialRef (obj, stringReg, cx); - - goto Loop; - } - goto case Token.REF_MEMBER; - - case Token.REF_MEMBER: { - //indexReg: flags - object elem = stack [stackTop]; - if (elem == DBL_MRK) - elem = sDbl [stackTop]; - --stackTop; - object obj = stack [stackTop]; - if (obj == DBL_MRK) - obj = sDbl [stackTop]; - stack [stackTop] = ScriptRuntime.memberRef (obj, elem, cx, indexReg); - - goto Loop; - } - goto case Token.REF_NS_MEMBER; - - case Token.REF_NS_MEMBER: { - //indexReg: flags - object elem = stack [stackTop]; - if (elem == DBL_MRK) - elem = sDbl [stackTop]; - --stackTop; - object ns = stack [stackTop]; - if (ns == DBL_MRK) - ns = sDbl [stackTop]; - --stackTop; - object obj = stack [stackTop]; - if (obj == DBL_MRK) - obj = sDbl [stackTop]; - stack [stackTop] = ScriptRuntime.memberRef (obj, ns, elem, cx, indexReg); - - goto Loop; - } - goto case Token.REF_NAME; - - case Token.REF_NAME: { - //indexReg: flags - object name = stack [stackTop]; - if (name == DBL_MRK) - name = sDbl [stackTop]; - stack [stackTop] = ScriptRuntime.nameRef (name, cx, frame.scope, indexReg); - - goto Loop; - } - goto case Token.REF_NS_NAME; - - case Token.REF_NS_NAME: { - //indexReg: flags - object name = stack [stackTop]; - if (name == DBL_MRK) - name = sDbl [stackTop]; - --stackTop; - object ns = stack [stackTop]; - if (ns == DBL_MRK) - ns = sDbl [stackTop]; - stack [stackTop] = ScriptRuntime.nameRef (ns, name, cx, frame.scope, indexReg); - - goto Loop; - } - goto case Icode_SCOPE_LOAD; - - case Icode_SCOPE_LOAD: - indexReg += frame.localShift; - frame.scope = (IScriptable)stack [indexReg]; - - goto Loop; - goto case Icode_SCOPE_SAVE; - - case Icode_SCOPE_SAVE: - indexReg += frame.localShift; - stack [indexReg] = frame.scope; - - goto Loop; - goto case Icode_CLOSURE_EXPR; - - case Icode_CLOSURE_EXPR: { - InterpretedFunction fun = InterpretedFunction.createFunction (cx, frame.scope, frame.fnOrScript, indexReg); - stack [++stackTop] = fun; - } - goto Loop; - goto case Icode_CLOSURE_STMT; - - case Icode_CLOSURE_STMT: - initFunction (cx, frame.scope, frame.fnOrScript, indexReg); - - goto Loop; - goto case Token.REGEXP; - - case Token.REGEXP: - stack [++stackTop] = frame.scriptRegExps [indexReg]; - - goto Loop; - goto case Icode_LITERAL_NEW; - - case Icode_LITERAL_NEW: - // indexReg: number of values in the literal - ++stackTop; - stack [stackTop] = new object [indexReg]; - sDbl [stackTop] = 0; - - goto Loop; - goto case Icode_LITERAL_SET; - - case Icode_LITERAL_SET: { - object value = stack [stackTop]; - if (value == DBL_MRK) - value = sDbl [stackTop]; - --stackTop; - int i = (int)sDbl [stackTop]; - ((object [])stack [stackTop]) [i] = value; - sDbl [stackTop] = i + 1; - - goto Loop; - } - goto case Token.ARRAYLIT; - - case Token.ARRAYLIT: - case Icode_SPARE_ARRAYLIT: - case Token.OBJECTLIT: { - object [] data = (object [])stack [stackTop]; - object val; - if (op == Token.OBJECTLIT) { - object [] ids = (object [])frame.idata.literalIds [indexReg]; - val = ScriptRuntime.newObjectLiteral (ids, data, cx, frame.scope); - } - else { - int [] skipIndexces = null; - if (op == Icode_SPARE_ARRAYLIT) { - skipIndexces = (int [])frame.idata.literalIds [indexReg]; - } - val = ScriptRuntime.newArrayLiteral (data, skipIndexces, cx, frame.scope); - } - stack [stackTop] = val; - - goto Loop; - } - goto case Icode_ENTERDQ; - - case Icode_ENTERDQ: { - object lhs = stack [stackTop]; - if (lhs == DBL_MRK) - lhs = sDbl [stackTop]; - --stackTop; - frame.scope = ScriptRuntime.enterDotQuery (lhs, frame.scope); - - goto Loop; - } - goto case Icode_LEAVEDQ; - - case Icode_LEAVEDQ: { - bool valBln = stack_boolean (frame, stackTop); - object x = ScriptRuntime.updateDotQuery (valBln, frame.scope); - if (x != null) { - stack [stackTop] = x; - frame.scope = ScriptRuntime.leaveDotQuery (frame.scope); - frame.pc += 2; - - goto Loop; - } - // reset stack and PC to code after ENTERDQ - --stackTop; - - goto jumplessRun_brk; - } - - case Token.DEFAULTNAMESPACE: { - object value = stack [stackTop]; - if (value == DBL_MRK) - value = sDbl [stackTop]; - stack [stackTop] = ScriptRuntime.setDefaultNamespace (value, cx); - - goto Loop; - } - goto case Token.ESCXMLATTR; - - case Token.ESCXMLATTR: { - object value = stack [stackTop]; - if (value != DBL_MRK) { - stack [stackTop] = ScriptRuntime.escapeAttributeValue (value, cx); - } - - goto Loop; - } - goto case Token.ESCXMLTEXT; - - case Token.ESCXMLTEXT: { - object value = stack [stackTop]; - if (value != DBL_MRK) { - stack [stackTop] = ScriptRuntime.escapeTextValue (value, cx); - } - - goto Loop; - } - goto case Icode_LINE; - - case Icode_DEBUGGER: { - if (frame.debuggerFrame != null) { - frame.debuggerFrame.OnDebuggerStatement(cx); - } - break; - } - - case Icode_LINE: - frame.pcSourceLineStart = frame.pc; - if (frame.debuggerFrame != null) { - int line = GetIndex (iCode, frame.pc); - frame.debuggerFrame.OnLineChange (cx, line); - } - frame.pc += 2; - - goto Loop; - goto case Icode_REG_IND_C0; - - case Icode_REG_IND_C0: - indexReg = 0; - - goto Loop; - goto case Icode_REG_IND_C1; - - case Icode_REG_IND_C1: - indexReg = 1; - - goto Loop; - goto case Icode_REG_IND_C2; - - case Icode_REG_IND_C2: - indexReg = 2; - - goto Loop; - goto case Icode_REG_IND_C3; - - case Icode_REG_IND_C3: - indexReg = 3; - - goto Loop; - goto case Icode_REG_IND_C4; - - case Icode_REG_IND_C4: - indexReg = 4; - - goto Loop; - goto case Icode_REG_IND_C5; - - case Icode_REG_IND_C5: - indexReg = 5; - - goto Loop; - goto case Icode_REG_IND1; - - case Icode_REG_IND1: - indexReg = 0xFF & iCode [frame.pc]; - ++frame.pc; - - goto Loop; - goto case Icode_REG_IND2; - - case Icode_REG_IND2: - indexReg = GetIndex (iCode, frame.pc); - frame.pc += 2; - - goto Loop; - goto case Icode_REG_IND4; - - case Icode_REG_IND4: - indexReg = GetInt (iCode, frame.pc); - frame.pc += 4; - - goto Loop; - goto case Icode_REG_STR_C0; - - case Icode_REG_STR_C0: - stringReg = strings [0]; - - goto Loop; - goto case Icode_REG_STR_C1; - - case Icode_REG_STR_C1: - stringReg = strings [1]; - - goto Loop; - goto case Icode_REG_STR_C2; - - case Icode_REG_STR_C2: - stringReg = strings [2]; - - goto Loop; - goto case Icode_REG_STR_C3; - - case Icode_REG_STR_C3: - stringReg = strings [3]; - - goto Loop; - goto case Icode_REG_STR1; - - case Icode_REG_STR1: - stringReg = strings [0xFF & iCode [frame.pc]]; - ++frame.pc; - - goto Loop; - goto case Icode_REG_STR2; - - case Icode_REG_STR2: - stringReg = strings [GetIndex (iCode, frame.pc)]; - frame.pc += 2; - - goto Loop; - goto case Icode_REG_STR4; - - case Icode_REG_STR4: - stringReg = strings [GetInt (iCode, frame.pc)]; - frame.pc += 4; - - goto Loop; - goto default; - - default: - dumpICode (frame.idata); - throw new ApplicationException ("Unknown icode : " + op + " @ pc : " + (frame.pc - 1)); - - } // end of interpreter switch - } - - jumplessRun_brk: - ; - // end of jumplessRun label block - - // This should be reachable only for jump implementation - // when pc points to encoded target offset - if (instructionCounting) { - addInstructionCount (cx, frame, 2); - } - int offset = GetShort (iCode, frame.pc); - if (offset != 0) { - // -1 accounts for pc pointing to jump opcode + 1 - frame.pc += offset - 1; - } - else { - frame.pc = frame.idata.longJumps.getExistingInt (frame.pc); - } - if (instructionCounting) { - frame.pcPrevBranch = frame.pc; - } - - goto Loop; - - Loop: - ; - } - - Loop_brk: - ; - // end of Loop: for - - ExitFrame (cx, frame, (object)null); - interpreterResult = frame.result; - interpreterResultDbl = frame.resultDbl; - if (frame.parentFrame != null) { - frame = frame.parentFrame; - if (frame.frozen) { - frame = frame.cloneFrozen (); - } - setCallResult (frame, interpreterResult, interpreterResultDbl); - interpreterResult = null; // Help GC - - goto StateLoop; - } - - goto StateLoop_brk; - } - // end of interpreter withoutExceptions: try - catch (Exception ex) { - if (throwable != null) { - // This is serious bug and it is better to track it ASAP - throw new ApplicationException (); - } - throwable = ex; - } - - withoutExceptions_brk: - - // This should be reachable only after above catch or from - // finally when it needs to propagate exception or from - // explicit throw - if (throwable == null) - Context.CodeBug (); - - // Exception type - const int EX_CATCH_STATE = 2; // Can execute JS catch - const int EX_FINALLY_STATE = 1; // Can execute JS finally - const int EX_NO_JS_STATE = 0; // Terminate JS execution - - int exState; - ContinuationJump cjump2 = null; - - if (throwable is EcmaScriptThrow) { - exState = EX_CATCH_STATE; - } - else if (throwable is EcmaScriptError) { - // an offical ECMA error object, - exState = EX_CATCH_STATE; - } - else if (throwable is EcmaScriptRuntimeException) { - exState = EX_CATCH_STATE; - } - else if (throwable is EcmaScriptException) { - exState = EX_FINALLY_STATE; - } - else if (throwable is Exception) { - exState = EX_NO_JS_STATE; - } - else { - // It must be ContinuationJump - exState = EX_FINALLY_STATE; - cjump2 = (ContinuationJump)throwable; - } - - if (instructionCounting) { - try { - addInstructionCount (cx, frame, EXCEPTION_COST); - } - catch (ApplicationException ex) { - // Error from instruction counting - // => unconditionally terminate JS - throwable = ex; - cjump2 = null; - exState = EX_NO_JS_STATE; - } - } - if (frame.debuggerFrame != null && throwable is ApplicationException) { - // Call debugger only for RuntimeException - ApplicationException rex = (ApplicationException)throwable; - try { - frame.debuggerFrame.OnExceptionThrown (cx, rex); - } - catch (Exception ex) { - // Any exception from debugger - // => unconditionally terminate JS - throwable = ex; - cjump2 = null; - exState = EX_NO_JS_STATE; - } - } - - for (; ; ) { - if (exState != EX_NO_JS_STATE) { - bool onlyFinally = (exState != EX_CATCH_STATE); - indexReg = getExceptionHandler (frame, onlyFinally); - if (indexReg >= 0) { - // We caught an exception, restart the loop - // with exception pending the processing at the loop - // start - - goto StateLoop; - } - } - // No allowed execption handlers in this frame, unwind - // to parent and try to look there - - ExitFrame (cx, frame, throwable); - - frame = frame.parentFrame; - if (frame == null) { - break; - } - if (cjump2 != null && cjump2.branchFrame == frame) { - // Continuation branch point was hit, - // restart the state loop to reenter continuation - indexReg = -1; - - goto StateLoop; - } - } - - // No more frames, rethrow the exception or deal with continuation - if (cjump2 != null) { - if (cjump2.branchFrame != null) { - // The above loop should locate the top frame - Context.CodeBug (); - } - if (cjump2.capturedFrame != null) { - // Restarting detached continuation - indexReg = -1; - - goto StateLoop; - } - // Return continuation result to the caller - interpreterResult = cjump2.result; - interpreterResultDbl = cjump2.resultDbl; - throwable = null; - } - - goto StateLoop_brk; - - StateLoop: - ; - } - - StateLoop_brk: - ; - // end of StateLoop: for(;;) - - // Do cleanups/restorations before the final return or throw - - if (cx.previousInterpreterInvocations != null && cx.previousInterpreterInvocations.size () != 0) { - cx.lastInterpreterFrame = cx.previousInterpreterInvocations.pop (); - } - else { - // It was the last interpreter frame on the stack - cx.lastInterpreterFrame = null; - // Force GC of the value cx.previousInterpreterInvocations - cx.previousInterpreterInvocations = null; - } - - if (throwable != null) { - if (throwable is Helpers.StackOverflowVerifierException) { - throw Context.ReportRuntimeError ( - ScriptRuntime.GetMessage ("mag.too.deep.parser.recursion")); - } - throw (Exception)throwable; - } - - return (interpreterResult != DBL_MRK) ? interpreterResult : - interpreterResultDbl; - } - - static void initFrame (Context cx, IScriptable callerScope, IScriptable thisObj, object [] args, double [] argsDbl, int argShift, int argCount, InterpretedFunction fnOrScript, CallFrame parentFrame, CallFrame frame) - { - InterpreterData idata = fnOrScript.idata; - - bool useActivation = idata.itsNeedsActivation; - DebugFrame debuggerFrame = null; - if (cx.m_Debugger != null) { - debuggerFrame = cx.m_Debugger.GetFrame (cx, idata); - if (debuggerFrame != null) { - useActivation = true; - } - } - - if (useActivation) { - // Copy args to new array to pass to enterActivationFunction - // or debuggerFrame.onEnter - if (argsDbl != null) { - args = GetArgsArray (args, argsDbl, argShift, argCount); - } - argShift = 0; - argsDbl = null; - } - - IScriptable scope; - if (idata.itsFunctionType != 0) { - if (!idata.useDynamicScope) { - scope = fnOrScript.ParentScope; - } - else { - scope = callerScope; - } - - if (useActivation) { - scope = ScriptRuntime.createFunctionActivation (fnOrScript, scope, args); - } - } - else { - scope = callerScope; - ScriptRuntime.initScript (fnOrScript, thisObj, cx, scope, fnOrScript.idata.evalScriptFlag); - } - - if (idata.itsNestedFunctions != null) { - if (idata.itsFunctionType != 0 && !idata.itsNeedsActivation) - Context.CodeBug (); - for (int i = 0; i < idata.itsNestedFunctions.Length; i++) { - InterpreterData fdata = idata.itsNestedFunctions [i]; - if (fdata.itsFunctionType == FunctionNode.FUNCTION_STATEMENT) { - initFunction (cx, scope, fnOrScript, i); - } - } - } - - IScriptable [] scriptRegExps = null; - if (idata.itsRegExpLiterals != null) { - // Wrapped regexps for functions are stored in - // InterpretedFunction - // but for script which should not contain references to scope - // the regexps re-wrapped during each script execution - if (idata.itsFunctionType != 0) { - scriptRegExps = fnOrScript.functionRegExps; - } - else { - scriptRegExps = fnOrScript.createRegExpWraps (cx, scope); - } - } - - // Initialize args, vars, locals and stack - - int emptyStackTop = idata.itsMaxVars + idata.itsMaxLocals - 1; - int maxFrameArray = idata.itsMaxFrameArray; - if (maxFrameArray != emptyStackTop + idata.itsMaxStack + 1) - Context.CodeBug (); - - object [] stack; - double [] sDbl; - bool stackReuse; - if (frame.stack != null && maxFrameArray <= frame.stack.Length) { - // Reuse stacks from old frame - stackReuse = true; - stack = frame.stack; - sDbl = frame.sDbl; - } - else { - stackReuse = false; - stack = new object [maxFrameArray]; - sDbl = new double [maxFrameArray]; - } - - int definedArgs = idata.argCount; - if (definedArgs > argCount) { - definedArgs = argCount; - } - - // Fill the frame structure - - frame.parentFrame = parentFrame; - frame.frameIndex = (parentFrame == null) ? 0 : parentFrame.frameIndex + 1; - if (frame.frameIndex > cx.MaximumInterpreterStackDepth) - throw ScriptRuntime.TypeErrorById ("msg.stackoverflow"); - frame.frozen = false; - - frame.fnOrScript = fnOrScript; - frame.idata = idata; - - frame.stack = stack; - frame.sDbl = sDbl; - frame.varSource = frame; - frame.localShift = idata.itsMaxVars; - frame.emptyStackTop = emptyStackTop; - - frame.debuggerFrame = debuggerFrame; - frame.useActivation = useActivation; - - frame.thisObj = thisObj; - frame.scriptRegExps = scriptRegExps; - - // Initialize initial values of variables that change during - // interpretation. - frame.result = Undefined.Value; - frame.pc = 0; - frame.pcPrevBranch = 0; - frame.pcSourceLineStart = idata.firstLinePC; - frame.scope = scope; - - frame.savedStackTop = emptyStackTop; - frame.savedCallOp = 0; - - Array.Copy (args, argShift, stack, 0, definedArgs); - if (argsDbl != null) { - Array.Copy (argsDbl, argShift, sDbl, 0, definedArgs); - } - for (int i = definedArgs; i != idata.itsMaxVars; ++i) { - stack [i] = Undefined.Value; - } - if (stackReuse) { - // Clean the stack part and space beyond stack if any - // of the old array to allow to GC objects there - for (int i = emptyStackTop + 1; i != stack.Length; ++i) { - stack [i] = null; - } - } - - EnterFrame (cx, frame, args); - } - - static bool isFrameEnterExitRequired (CallFrame frame) - { - return frame.debuggerFrame != null || frame.idata.itsNeedsActivation; - } - - static void EnterFrame (Context cx, CallFrame frame, object [] args) - { - if (frame.debuggerFrame != null) { - frame.debuggerFrame.OnEnter (cx, frame.scope, frame.thisObj, args); - } - if (frame.idata.itsNeedsActivation) { - // Enter activation only when itsNeedsActivation true, not when - // useActivation holds since debugger should not interfere - // with activation chaining - ScriptRuntime.enterActivationFunction (cx, frame.scope); - } - } - - static void ExitFrame (Context cx, CallFrame frame, object throwable) - { - if (frame.idata.itsNeedsActivation) { - ScriptRuntime.exitActivationFunction (cx); - } - - if (frame.debuggerFrame != null) { - try { - if (throwable is Exception) { - frame.debuggerFrame.OnExit (cx, true, throwable); - } - else { - object result; - ContinuationJump cjump = (ContinuationJump)throwable; - if (cjump == null) { - result = frame.result; - } - else { - result = cjump.result; - } - if (result == UniqueTag.DoubleMark) { - double resultDbl; - if (cjump == null) { - resultDbl = frame.resultDbl; - } - else { - resultDbl = cjump.resultDbl; - } - result = resultDbl; - } - frame.debuggerFrame.OnExit (cx, false, result); - } - } - catch (Exception ex) { - Console.Error.WriteLine ("USAGE WARNING: onExit terminated with exception"); - Console.Error.WriteLine (ex.ToString ()); - } - } - } - - static void setCallResult (CallFrame frame, object callResult, double callResultDbl) - { - if (frame.savedCallOp == Token.CALL) { - frame.stack [frame.savedStackTop] = callResult; - frame.sDbl [frame.savedStackTop] = callResultDbl; - } - else if (frame.savedCallOp == Token.NEW) { - // If construct returns scriptable, - // then it replaces on stack top saved original instance - // of the object. - if (callResult is IScriptable) { - frame.stack [frame.savedStackTop] = callResult; - } - } - else { - Context.CodeBug (); - } - frame.savedCallOp = 0; - } - - static void captureContinuation (Context cx, CallFrame frame, int stackTop) - { - Continuation c = new Continuation (); - ScriptRuntime.setObjectProtoAndParent (c, ScriptRuntime.getTopCallScope (cx)); - - // Make sure that all frames upstack frames are frozen - CallFrame x = frame.parentFrame; - while (x != null && !x.frozen) { - x.frozen = true; - // Allow to GC unused stack space - for (int i = x.savedStackTop + 1; i != x.stack.Length; ++i) { - // Allow to GC unused stack space - x.stack [i] = null; - } - if (x.savedCallOp == Token.CALL) { - // the call will always overwrite the stack top with the result - x.stack [x.savedStackTop] = null; - } - else { - if (x.savedCallOp != Token.NEW) - Context.CodeBug (); - // the new operator uses stack top to store the constructed - // object so it shall not be cleared: see comments in - // setCallResult - } - x = x.parentFrame; - } - - c.initImplementation (frame.parentFrame); - frame.stack [stackTop] = c; - } - - static int stack_int32 (CallFrame frame, int i) - { - object x = frame.stack [i]; - double value; - if (x == UniqueTag.DoubleMark) { - value = frame.sDbl [i]; - } - else { - value = ScriptConvert.ToNumber (x); - } - return ScriptConvert.ToInt32 (value); - } - - static double stack_double (CallFrame frame, int i) - { - object x = frame.stack [i]; - if (x != UniqueTag.DoubleMark) { - return ScriptConvert.ToNumber (x); - } - else { - return frame.sDbl [i]; - } - } - - static bool stack_boolean (CallFrame frame, int i) - { - object x = frame.stack [i]; - if (x is bool) { - return (bool)x; - } - else if (x == UniqueTag.DoubleMark) { - double d = frame.sDbl [i]; - return !double.IsNaN (d) && d != 0.0; - } - else if (x == null || x == Undefined.Value) { - return false; - } - else if (CliHelper.IsNumber (x)) { - double d = Convert.ToDouble (x); - return (!double.IsNaN (d) && d != 0.0); - } - else { - return ScriptConvert.ToBoolean (x); - } - } - - static void DoAdd (object [] stack, double [] sDbl, int stackTop, Context cx) - { - object rhs = stack [stackTop + 1]; - object lhs = stack [stackTop]; - double d; - bool leftRightOrder; - if (rhs == UniqueTag.DoubleMark) { - d = sDbl [stackTop + 1]; - if (lhs == UniqueTag.DoubleMark) { - sDbl [stackTop] += d; - return; - } - leftRightOrder = true; - // fallthrough to object + number code - } - else if (lhs == UniqueTag.DoubleMark) { - d = sDbl [stackTop]; - lhs = rhs; - leftRightOrder = false; - // fallthrough to object + number code - } - else { - if (lhs is IScriptable || rhs is IScriptable) { - stack [stackTop] = ScriptRuntime.Add (lhs, rhs, cx); - } - else if (lhs is string) { - string lstr = (string)lhs; - string rstr = ScriptConvert.ToString (rhs); - stack [stackTop] = string.Concat (lstr, rstr); - } - else if (rhs is string) { - string lstr = ScriptConvert.ToString (lhs); - string rstr = (string)rhs; - stack [stackTop] = string.Concat (lstr, rstr); - } - else { - double lDbl = (CliHelper.IsNumber (lhs)) ? Convert.ToDouble (lhs) : ScriptConvert.ToNumber (lhs); - double rDbl = (CliHelper.IsNumber (rhs)) ? Convert.ToDouble (rhs) : ScriptConvert.ToNumber (rhs); - stack [stackTop] = UniqueTag.DoubleMark; - sDbl [stackTop] = lDbl + rDbl; - } - return; - } - - // handle object(lhs) + number(d) code - if (lhs is IScriptable) { - rhs = d; - if (!leftRightOrder) { - object tmp = lhs; - lhs = rhs; - rhs = tmp; - } - stack [stackTop] = ScriptRuntime.Add (lhs, rhs, cx); - } - else if (lhs is string) { - string lstr = (string)lhs; - string rstr = ScriptConvert.ToString (d); - if (leftRightOrder) { - stack [stackTop] = string.Concat (lstr, rstr); - } - else { - stack [stackTop] = string.Concat (rstr, lstr); - } - } - else { - double lDbl = (CliHelper.IsNumber (lhs)) ? Convert.ToDouble (lhs) : ScriptConvert.ToNumber (lhs); - stack [stackTop] = UniqueTag.DoubleMark; - sDbl [stackTop] = lDbl + d; - } - } - - void addGotoOp (int gotoOp) - { - sbyte [] array = itsData.itsICode; - int top = itsICodeTop; - if (top + 3 > array.Length) { - array = increaseICodeCapasity (3); - } - array [top] = (sbyte)gotoOp; - // Offset would written later - itsICodeTop = top + 1 + 2; - } - - - static object [] GetArgsArray (object [] stack, double [] sDbl, int shift, int count) - { - if (count == 0) { - return ScriptRuntime.EmptyArgs; - } - object [] args = new object [count]; - for (int i = 0; i != count; ++i, ++shift) { - object val = stack [shift]; - if (val == UniqueTag.DoubleMark) { - val = sDbl [shift]; - } - args [i] = val; - } - return args; - } - - static void addInstructionCount (Context cx, CallFrame frame, int extra) - { - cx.instructionCount += frame.pc - frame.pcPrevBranch + extra; - if (cx.instructionCount > cx.instructionThreshold) { - cx.ObserveInstructionCount (cx.instructionCount); - cx.instructionCount = 0; - } - } - - } +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; + +using EcmaScript.NET.Debugging; +using EcmaScript.NET.Collections; + +namespace EcmaScript.NET +{ + + public class Interpreter + { + + // Additional interpreter-specific codes + + const int Icode_DUP = -1; + const int Icode_DUP2 = -2; + const int Icode_SWAP = -3; + const int Icode_POP = -4; + const int Icode_POP_RESULT = -5; + const int Icode_IFEQ_POP = -6; + const int Icode_VAR_INC_DEC = -7; + const int Icode_NAME_INC_DEC = -8; + const int Icode_PROP_INC_DEC = -9; + const int Icode_ELEM_INC_DEC = -10; + const int Icode_REF_INC_DEC = -11; + const int Icode_SCOPE_LOAD = -12; + const int Icode_SCOPE_SAVE = -13; + const int Icode_TYPEOFNAME = -14; + const int Icode_NAME_AND_THIS = -15; + const int Icode_PROP_AND_THIS = -16; + const int Icode_ELEM_AND_THIS = -17; + const int Icode_VALUE_AND_THIS = -18; + const int Icode_CLOSURE_EXPR = -19; + const int Icode_CLOSURE_STMT = -20; + const int Icode_CALLSPECIAL = -21; + const int Icode_RETUNDEF = -22; + const int Icode_GOSUB = -23; + const int Icode_STARTSUB = -24; + const int Icode_RETSUB = -25; + const int Icode_LINE = -26; + const int Icode_SHORTNUMBER = -27; + const int Icode_INTNUMBER = -28; + const int Icode_LITERAL_NEW = -29; + const int Icode_LITERAL_SET = -30; + const int Icode_SPARE_ARRAYLIT = -31; + const int Icode_REG_IND_C0 = -32; + const int Icode_REG_IND_C1 = -33; + const int Icode_REG_IND_C2 = -34; + const int Icode_REG_IND_C3 = -35; + const int Icode_REG_IND_C4 = -36; + const int Icode_REG_IND_C5 = -37; + const int Icode_REG_IND1 = -38; + const int Icode_REG_IND2 = -39; + const int Icode_REG_IND4 = -40; + const int Icode_REG_STR_C0 = -41; + const int Icode_REG_STR_C1 = -42; + const int Icode_REG_STR_C2 = -43; + const int Icode_REG_STR_C3 = -44; + const int Icode_REG_STR1 = -45; + const int Icode_REG_STR2 = -46; + const int Icode_REG_STR4 = -47; + const int Icode_GETVAR1 = -48; + const int Icode_SETVAR1 = -49; + const int Icode_UNDEF = -50; + const int Icode_ZERO = -51; + const int Icode_ONE = -52; + const int Icode_ENTERDQ = -53; + const int Icode_LEAVEDQ = -54; + const int Icode_TAIL_CALL = -55; + const int Icode_LOCAL_CLEAR = -56; + const int Icode_DEBUGGER = -57; + + // Last icode + const int MIN_ICODE = -57; + + + // data for parsing + + CompilerEnvirons compilerEnv; + + bool itsInFunctionFlag; + + InterpreterData itsData; + ScriptOrFnNode scriptOrFn; + int itsICodeTop; + int itsStackDepth; + int itsLineNumber; + int itsDoubleTableTop; + ObjToIntMap itsStrings = new ObjToIntMap (20); + int itsLocalTop; + + const int MIN_LABEL_TABLE_SIZE = 32; + const int MIN_FIXUP_TABLE_SIZE = 40; + int [] itsLabelTable; + int itsLabelTableTop; + // itsFixupTable[i] = (label_index << 32) | fixup_site + long [] itsFixupTable; + int itsFixupTableTop; + ObjArray itsLiteralIds = new ObjArray (); + + int itsExceptionTableTop; + const int EXCEPTION_TRY_START_SLOT = 0; + const int EXCEPTION_TRY_END_SLOT = 1; + const int EXCEPTION_HANDLER_SLOT = 2; + const int EXCEPTION_TYPE_SLOT = 3; + const int EXCEPTION_LOCAL_SLOT = 4; + const int EXCEPTION_SCOPE_SLOT = 5; + // SLOT_SIZE: space for try start/end, handler, start, handler type, + // exception local and scope local + const int EXCEPTION_SLOT_SIZE = 6; + + // ECF_ or Expression Context Flags constants: for now only TAIL is available + const int ECF_TAIL = 1 << 0; + + + internal class CallFrame : System.ICloneable + { + + internal CallFrame parentFrame; + // amount of stack frames before this one on the interpretation stack + internal int frameIndex; + // If true indicates read-only frame that is a part of continuation + internal bool frozen; + + internal InterpretedFunction fnOrScript; + internal InterpreterData idata; + + // Stack structure + // stack[0 <= i < localShift]: arguments and local variables + // stack[localShift <= i <= emptyStackTop]: used for local temporaries + // stack[emptyStackTop < i < stack.length]: stack data + // sDbl[i]: if stack[i] is UniqueTag.DoubleMark, sDbl[i] holds the number value + + internal object [] stack; + internal double [] sDbl; + + internal CallFrame varSource; // defaults to this unless continuation frame + internal int localShift; + internal int emptyStackTop; + + internal DebugFrame debuggerFrame; + internal bool useActivation; + + internal IScriptable thisObj; + internal IScriptable [] scriptRegExps; + + // The values that change during interpretation + + internal object result; + internal double resultDbl; + internal int pc; + internal int pcPrevBranch; + internal int pcSourceLineStart; + internal IScriptable scope; + + internal int savedStackTop; + internal int savedCallOp; + + internal virtual CallFrame cloneFrozen () + { + if (!frozen) + Context.CodeBug (); + + CallFrame copy = (CallFrame)Clone (); + + // clone stack but keep varSource to point to values + // from this frame to share variables. + + // TODO: STACK + copy.stack = new object [stack.Length]; + stack.CopyTo (copy.stack, 0); + copy.sDbl = new double [sDbl.Length]; + sDbl.CopyTo (copy.sDbl, 0); + + copy.frozen = false; + return copy; + } + + virtual public object Clone () + { + return base.MemberwiseClone (); + } + } + + + sealed class ContinuationJump + { + + internal CallFrame capturedFrame; + internal CallFrame branchFrame; + internal object result; + internal double resultDbl; + + internal ContinuationJump (Continuation c, CallFrame current) + { + this.capturedFrame = (CallFrame)c.Implementation; + if (this.capturedFrame == null || current == null) { + // Continuation and current execution does not share + // any frames if there is nothing to capture or + // if there is no currently executed frames + this.branchFrame = null; + } + else { + // Search for branch frame where parent frame chains starting + // from captured and current meet. + CallFrame chain1 = this.capturedFrame; + CallFrame chain2 = current; + + // First work parents of chain1 or chain2 until the same + // frame depth. + int diff = chain1.frameIndex - chain2.frameIndex; + if (diff != 0) { + if (diff < 0) { + // swap to make sure that + // chain1.frameIndex > chain2.frameIndex and diff > 0 + chain1 = current; + chain2 = this.capturedFrame; + diff = -diff; + } + do { + chain1 = chain1.parentFrame; + } + while (--diff != 0); + if (chain1.frameIndex != chain2.frameIndex) + Context.CodeBug (); + } + + // Now walk parents in parallel until a shared frame is found + // or until the root is reached. + while (chain1 != chain2 && chain1 != null) { + chain1 = chain1.parentFrame; + chain2 = chain2.parentFrame; + } + + this.branchFrame = chain1; + if (this.branchFrame != null && !this.branchFrame.frozen) + Context.CodeBug (); + } + } + } + + static string bytecodeName (int bytecode) + { + if (!validBytecode (bytecode)) { + throw new ArgumentException (Convert.ToString (bytecode)); + } + + if (!Token.printICode) { + return Convert.ToString (bytecode); + } + + if (ValidTokenCode (bytecode)) { + return Token.name (bytecode); + } + + switch (bytecode) { + + case Icode_DUP: + return "DUP"; + + case Icode_DUP2: + return "DUP2"; + + case Icode_SWAP: + return "SWAP"; + + case Icode_POP: + return "POP"; + + case Icode_POP_RESULT: + return "POP_RESULT"; + + case Icode_IFEQ_POP: + return "IFEQ_POP"; + + case Icode_VAR_INC_DEC: + return "VAR_INC_DEC"; + + case Icode_NAME_INC_DEC: + return "NAME_INC_DEC"; + + case Icode_PROP_INC_DEC: + return "PROP_INC_DEC"; + + case Icode_ELEM_INC_DEC: + return "ELEM_INC_DEC"; + + case Icode_REF_INC_DEC: + return "REF_INC_DEC"; + + case Icode_SCOPE_LOAD: + return "SCOPE_LOAD"; + + case Icode_SCOPE_SAVE: + return "SCOPE_SAVE"; + + case Icode_TYPEOFNAME: + return "TYPEOFNAME"; + + case Icode_NAME_AND_THIS: + return "NAME_AND_THIS"; + + case Icode_PROP_AND_THIS: + return "PROP_AND_THIS"; + + case Icode_ELEM_AND_THIS: + return "ELEM_AND_THIS"; + + case Icode_VALUE_AND_THIS: + return "VALUE_AND_THIS"; + + case Icode_CLOSURE_EXPR: + return "CLOSURE_EXPR"; + + case Icode_CLOSURE_STMT: + return "CLOSURE_STMT"; + + case Icode_CALLSPECIAL: + return "CALLSPECIAL"; + + case Icode_RETUNDEF: + return "RETUNDEF"; + + case Icode_GOSUB: + return "GOSUB"; + + case Icode_STARTSUB: + return "STARTSUB"; + + case Icode_RETSUB: + return "RETSUB"; + + case Icode_LINE: + return "LINE"; + + case Icode_SHORTNUMBER: + return "SHORTNUMBER"; + + case Icode_INTNUMBER: + return "INTNUMBER"; + + case Icode_LITERAL_NEW: + return "LITERAL_NEW"; + + case Icode_LITERAL_SET: + return "LITERAL_SET"; + + case Icode_SPARE_ARRAYLIT: + return "SPARE_ARRAYLIT"; + + case Icode_REG_IND_C0: + return "REG_IND_C0"; + + case Icode_REG_IND_C1: + return "REG_IND_C1"; + + case Icode_REG_IND_C2: + return "REG_IND_C2"; + + case Icode_REG_IND_C3: + return "REG_IND_C3"; + + case Icode_REG_IND_C4: + return "REG_IND_C4"; + + case Icode_REG_IND_C5: + return "REG_IND_C5"; + + case Icode_REG_IND1: + return "LOAD_IND1"; + + case Icode_REG_IND2: + return "LOAD_IND2"; + + case Icode_REG_IND4: + return "LOAD_IND4"; + + case Icode_REG_STR_C0: + return "REG_STR_C0"; + + case Icode_REG_STR_C1: + return "REG_STR_C1"; + + case Icode_REG_STR_C2: + return "REG_STR_C2"; + + case Icode_REG_STR_C3: + return "REG_STR_C3"; + + case Icode_REG_STR1: + return "LOAD_STR1"; + + case Icode_REG_STR2: + return "LOAD_STR2"; + + case Icode_REG_STR4: + return "LOAD_STR4"; + + case Icode_GETVAR1: + return "GETVAR1"; + + case Icode_SETVAR1: + return "SETVAR1"; + + case Icode_UNDEF: + return "UNDEF"; + + case Icode_ZERO: + return "ZERO"; + + case Icode_ONE: + return "ONE"; + + case Icode_ENTERDQ: + return "ENTERDQ"; + + case Icode_LEAVEDQ: + return "LEAVEDQ"; + + case Icode_TAIL_CALL: + return "TAIL_CALL"; + + case Icode_LOCAL_CLEAR: + return "LOCAL_CLEAR"; + + case Icode_DEBUGGER: + return "DEBUGGER"; + } + + // icode without name + throw new Exception (Convert.ToString (bytecode)); + } + + static bool validIcode (int icode) + { + return MIN_ICODE <= icode && icode <= -1; + } + + static bool ValidTokenCode (int token) + { + return Token.FIRST_BYTECODE_TOKEN <= token && token <= Token.LAST_BYTECODE_TOKEN; + } + + static bool validBytecode (int bytecode) + { + return validIcode (bytecode) || ValidTokenCode (bytecode); + } + + public virtual object Compile (CompilerEnvirons compilerEnv, ScriptOrFnNode tree, string encodedSource, bool returnFunction) + { + this.compilerEnv = compilerEnv; + new NodeTransformer ().transform (tree); + + if (Token.printTrees) { + System.Console.Out.WriteLine (tree.toStringTree (tree)); + } + + if (returnFunction) { + tree = tree.getFunctionNode (0); + } + + scriptOrFn = tree; + itsData = new InterpreterData (compilerEnv.LanguageVersion, scriptOrFn.SourceName, encodedSource); + itsData.topLevel = true; + + if (returnFunction) { + generateFunctionICode (); + } + else { + generateICodeFromTree (scriptOrFn); + } + + return itsData; + } + + public virtual IScript CreateScriptObject (object bytecode, object staticSecurityDomain) + { + InterpreterData idata = (InterpreterData)bytecode; + return InterpretedFunction.createScript (itsData, staticSecurityDomain); + } + + public virtual IFunction CreateFunctionObject (Context cx, IScriptable scope, object bytecode, object staticSecurityDomain) + { + InterpreterData idata = (InterpreterData)bytecode; + return InterpretedFunction.createFunction (cx, scope, itsData, staticSecurityDomain); + } + + void generateFunctionICode () + { + itsInFunctionFlag = true; + + FunctionNode theFunction = (FunctionNode)scriptOrFn; + + itsData.itsFunctionType = theFunction.FunctionType; + itsData.itsNeedsActivation = theFunction.RequiresActivation; + itsData.itsName = theFunction.FunctionName; + if (!theFunction.IgnoreDynamicScope) { + if (compilerEnv.UseDynamicScope) { + itsData.useDynamicScope = true; + } + } + + generateICodeFromTree (theFunction.LastChild); + } + + void generateICodeFromTree (Node tree) + { + generateNestedFunctions (); + + generateRegExpLiterals (); + + VisitStatement (tree); + fixLabelGotos (); + // add RETURN_RESULT only to scripts as function always ends with RETURN + if (itsData.itsFunctionType == 0) { + addToken (Token.RETURN_RESULT); + } + + if (itsData.itsICode.Length != itsICodeTop) { + // Make itsData.itsICode length exactly itsICodeTop to save memory + // and catch bugs with jumps beyound icode as early as possible + sbyte [] tmp = new sbyte [itsICodeTop]; + Array.Copy (itsData.itsICode, 0, tmp, 0, itsICodeTop); + itsData.itsICode = tmp; + } + if (itsStrings.size () == 0) { + itsData.itsStringTable = null; + } + else { + itsData.itsStringTable = new string [itsStrings.size ()]; + ObjToIntMap.Iterator iter = itsStrings.newIterator (); + for (iter.start (); !iter.done (); iter.next ()) { + string str = (string)iter.Key; + int index = iter.Value; + if (itsData.itsStringTable [index] != null) + Context.CodeBug (); + itsData.itsStringTable [index] = str; + } + } + if (itsDoubleTableTop == 0) { + itsData.itsDoubleTable = null; + } + else if (itsData.itsDoubleTable.Length != itsDoubleTableTop) { + double [] tmp = new double [itsDoubleTableTop]; + Array.Copy (itsData.itsDoubleTable, 0, tmp, 0, itsDoubleTableTop); + itsData.itsDoubleTable = tmp; + } + if (itsExceptionTableTop != 0 && itsData.itsExceptionTable.Length != itsExceptionTableTop) { + int [] tmp = new int [itsExceptionTableTop]; + Array.Copy (itsData.itsExceptionTable, 0, tmp, 0, itsExceptionTableTop); + itsData.itsExceptionTable = tmp; + } + + itsData.itsMaxVars = scriptOrFn.ParamAndVarCount; + // itsMaxFrameArray: interpret method needs this amount for its + // stack and sDbl arrays + itsData.itsMaxFrameArray = itsData.itsMaxVars + itsData.itsMaxLocals + itsData.itsMaxStack; + + itsData.argNames = scriptOrFn.ParamAndVarNames; + itsData.argCount = scriptOrFn.ParamCount; + + itsData.encodedSourceStart = scriptOrFn.EncodedSourceStart; + itsData.encodedSourceEnd = scriptOrFn.EncodedSourceEnd; + + if (itsLiteralIds.size () != 0) { + itsData.literalIds = itsLiteralIds.ToArray (); + } + + if (Token.printICode) + dumpICode (itsData); + } + + void generateNestedFunctions () + { + int functionCount = scriptOrFn.FunctionCount; + if (functionCount == 0) + return; + + InterpreterData [] array = new InterpreterData [functionCount]; + for (int i = 0; i != functionCount; i++) { + FunctionNode def = scriptOrFn.getFunctionNode (i); + Interpreter jsi = new Interpreter (); + jsi.compilerEnv = compilerEnv; + jsi.scriptOrFn = def; + jsi.itsData = new InterpreterData (itsData); + jsi.generateFunctionICode (); + array [i] = jsi.itsData; + } + itsData.itsNestedFunctions = array; + } + + void generateRegExpLiterals () + { + int N = scriptOrFn.RegexpCount; + if (N == 0) + return; + + Context cx = Context.CurrentContext; + RegExpProxy rep = cx.RegExpProxy; + object [] array = new object [N]; + for (int i = 0; i != N; i++) { + string str = scriptOrFn.getRegexpString (i); + string flags = scriptOrFn.getRegexpFlags (i); + array [i] = rep.Compile (cx, str, flags); + } + itsData.itsRegExpLiterals = array; + } + + void updateLineNumber (Node node) + { + int lineno = node.Lineno; + if (lineno != itsLineNumber && lineno >= 0) { + if (itsData.firstLinePC < 0) { + itsData.firstLinePC = lineno; + } + itsLineNumber = lineno; + addIcode (Icode_LINE); + addUint16 (lineno & 0xFFFF); + } + } + + Exception badTree (Node node) + { + throw new Exception (node.ToString ()); + } + + void VisitStatement (Node node) + { + int type = node.Type; + Node child = node.FirstChild; + switch (type) { + + + case Token.FUNCTION: { + int fnIndex = node.getExistingIntProp (Node.FUNCTION_PROP); + int fnType = scriptOrFn.getFunctionNode (fnIndex).FunctionType; + // Only function expressions or function expression + // statements needs closure code creating new function + // object on stack as function statements are initialized + // at script/function start + // In addition function expression can not present here + // at statement level, they must only present as expressions. + if (fnType == FunctionNode.FUNCTION_EXPRESSION_STATEMENT) { + addIndexOp (Icode_CLOSURE_STMT, fnIndex); + } + else { + if (fnType != FunctionNode.FUNCTION_STATEMENT) { + throw Context.CodeBug (); + } + } + } + break; + + + case Token.SCRIPT: + case Token.LABEL: + case Token.LOOP: + case Token.BLOCK: + case Token.EMPTY: + case Token.WITH: + updateLineNumber (node); + while (child != null) { + VisitStatement (child); + child = child.Next; + } + break; + + + case Token.ENTERWITH: + VisitExpression (child, 0); + addToken (Token.ENTERWITH); + stackChange (-1); + break; + + + case Token.LEAVEWITH: + addToken (Token.LEAVEWITH); + break; + + + case Token.LOCAL_BLOCK: { + int local = allocLocal (); + node.putIntProp (Node.LOCAL_PROP, local); + updateLineNumber (node); + while (child != null) { + VisitStatement (child); + child = child.Next; + } + addIndexOp (Icode_LOCAL_CLEAR, local); + releaseLocal (local); + } + break; + + case Token.DEBUGGER: + updateLineNumber (node); + addIcode (Icode_DEBUGGER); + break; + + case Token.SWITCH: + updateLineNumber (node); { + // See comments in IRFactory.createSwitch() for description + // of SWITCH node + Node switchNode = (Node.Jump)node; + VisitExpression (child, 0); + for (Node.Jump caseNode = (Node.Jump)child.Next; caseNode != null; caseNode = (Node.Jump)caseNode.Next) { + if (caseNode.Type != Token.CASE) + throw badTree (caseNode); + Node test = caseNode.FirstChild; + addIcode (Icode_DUP); + stackChange (1); + VisitExpression (test, 0); + addToken (Token.SHEQ); + stackChange (-1); + // If true, Icode_IFEQ_POP will jump and remove case + // value from stack + addGoto (caseNode.target, Icode_IFEQ_POP); + stackChange (-1); + } + addIcode (Icode_POP); + stackChange (-1); + } + break; + + + case Token.TARGET: + markTargetLabel (node); + break; + + + case Token.IFEQ: + case Token.IFNE: { + Node target = ((Node.Jump)node).target; + VisitExpression (child, 0); + addGoto (target, type); + stackChange (-1); + } + break; + + + case Token.GOTO: { + Node target = ((Node.Jump)node).target; + addGoto (target, type); + } + break; + + + case Token.JSR: { + Node target = ((Node.Jump)node).target; + addGoto (target, Icode_GOSUB); + } + break; + + + case Token.FINALLY: { + // Account for incomming GOTOSUB address + stackChange (1); + int finallyRegister = getLocalBlockRef (node); + addIndexOp (Icode_STARTSUB, finallyRegister); + stackChange (-1); + while (child != null) { + VisitStatement (child); + child = child.Next; + } + addIndexOp (Icode_RETSUB, finallyRegister); + } + break; + + + case Token.EXPR_VOID: + case Token.EXPR_RESULT: + updateLineNumber (node); + VisitExpression (child, 0); + addIcode ((type == Token.EXPR_VOID) ? Icode_POP : Icode_POP_RESULT); + stackChange (-1); + break; + + + case Token.TRY: { + Node.Jump tryNode = (Node.Jump)node; + int exceptionObjectLocal = getLocalBlockRef (tryNode); + int scopeLocal = allocLocal (); + + addIndexOp (Icode_SCOPE_SAVE, scopeLocal); + + int tryStart = itsICodeTop; + while (child != null) { + VisitStatement (child); + child = child.Next; + } + + Node catchTarget = tryNode.target; + if (catchTarget != null) { + int catchStartPC = itsLabelTable [getTargetLabel (catchTarget)]; + addExceptionHandler (tryStart, catchStartPC, catchStartPC, false, exceptionObjectLocal, scopeLocal); + } + Node finallyTarget = tryNode.Finally; + if (finallyTarget != null) { + int finallyStartPC = itsLabelTable [getTargetLabel (finallyTarget)]; + addExceptionHandler (tryStart, finallyStartPC, finallyStartPC, true, exceptionObjectLocal, scopeLocal); + } + + addIndexOp (Icode_LOCAL_CLEAR, scopeLocal); + releaseLocal (scopeLocal); + } + break; + + + case Token.CATCH_SCOPE: { + int localIndex = getLocalBlockRef (node); + int scopeIndex = node.getExistingIntProp (Node.CATCH_SCOPE_PROP); + string name = child.String; + child = child.Next; + VisitExpression (child, 0); // load expression object + addStringPrefix (name); + addIndexPrefix (localIndex); + addToken (Token.CATCH_SCOPE); + addUint8 (scopeIndex != 0 ? 1 : 0); + stackChange (-1); + } + break; + + + case Token.THROW: + updateLineNumber (node); + VisitExpression (child, 0); + addToken (Token.THROW); + addUint16 (itsLineNumber & 0xFFFF); + stackChange (-1); + break; + + + case Token.RETHROW: + updateLineNumber (node); + addIndexOp (Token.RETHROW, getLocalBlockRef (node)); + break; + + + case Token.RETURN: + updateLineNumber (node); + if (child != null) { + VisitExpression (child, ECF_TAIL); + addToken (Token.RETURN); + stackChange (-1); + } + else { + addIcode (Icode_RETUNDEF); + } + break; + + + case Token.RETURN_RESULT: + updateLineNumber (node); + addToken (Token.RETURN_RESULT); + break; + + + case Token.ENUM_INIT_KEYS: + case Token.ENUM_INIT_VALUES: + VisitExpression (child, 0); + addIndexOp (type, getLocalBlockRef (node)); + stackChange (-1); + break; + + + default: + throw badTree (node); + + } + + if (itsStackDepth != 0) { + throw Context.CodeBug (); + } + } + + bool VisitExpressionOptimized (Node node, int contextFlags) + { + return false; +#if FALKSE + if (node.Type == Token.ADD) { + Node next = node.Next; + if (next == null) + return false; + switch (next.Type) { + case Token.NAME: + case Token.STRING: + return true; + } + } + return false; +#endif + } + + void VisitExpression (Node node, int contextFlags) + { + if (VisitExpressionOptimized (node, contextFlags)) + return; + + int type = node.Type; + Node child = node.FirstChild; + int savedStackDepth = itsStackDepth; + switch (type) { + + + case Token.FUNCTION: { + int fnIndex = node.getExistingIntProp (Node.FUNCTION_PROP); + FunctionNode fn = scriptOrFn.getFunctionNode (fnIndex); + + // See comments in visitStatement for Token.FUNCTION case + switch (fn.FunctionType) { + case FunctionNode.FUNCTION_EXPRESSION: + addIndexOp (Icode_CLOSURE_EXPR, fnIndex); + break; + default: + throw Context.CodeBug (); + } + + stackChange (1); + } + break; + + + case Token.LOCAL_LOAD: { + int localIndex = getLocalBlockRef (node); + addIndexOp (Token.LOCAL_LOAD, localIndex); + stackChange (1); + } + break; + + + case Token.COMMA: { + Node lastChild = node.LastChild; + while (child != lastChild) { + VisitExpression (child, 0); + addIcode (Icode_POP); + stackChange (-1); + child = child.Next; + } + // Preserve tail context flag if any + VisitExpression (child, contextFlags & ECF_TAIL); + } + break; + + + case Token.USE_STACK: + // Indicates that stack was modified externally, + // like placed catch object + stackChange (1); + break; + + + case Token.REF_CALL: + case Token.CALL: + case Token.NEW: { + if (type == Token.NEW) { + VisitExpression (child, 0); + } + else { + generateCallFunAndThis (child); + } + int argCount = 0; + while ((child = child.Next) != null) { + VisitExpression (child, 0); + ++argCount; + } + int callType = node.getIntProp (Node.SPECIALCALL_PROP, Node.NON_SPECIALCALL); + if (callType != Node.NON_SPECIALCALL) { + // embed line number and source filename + addIndexOp (Icode_CALLSPECIAL, argCount); + addUint8 (callType); + addUint8 (type == Token.NEW ? 1 : 0); + addUint16 (itsLineNumber & 0xFFFF); + } + else { + if (type == Token.CALL) { + if ((contextFlags & ECF_TAIL) != 0) { + type = Icode_TAIL_CALL; + } + } + addIndexOp (type, argCount); + } + // adjust stack + if (type == Token.NEW) { + // new: f, args -> result + stackChange (-argCount); + } + else { + // call: f, thisObj, args -> result + // ref_call: f, thisObj, args -> ref + stackChange (-1 - argCount); + } + if (argCount > itsData.itsMaxCalleeArgs) { + itsData.itsMaxCalleeArgs = argCount; + } + } + break; + + + case Token.AND: + case Token.OR: { + VisitExpression (child, 0); + addIcode (Icode_DUP); + stackChange (1); + int afterSecondJumpStart = itsICodeTop; + int jump = (type == Token.AND) ? Token.IFNE : Token.IFEQ; + addGotoOp (jump); + stackChange (-1); + addIcode (Icode_POP); + stackChange (-1); + child = child.Next; + // Preserve tail context flag if any + VisitExpression (child, contextFlags & ECF_TAIL); + resolveForwardGoto (afterSecondJumpStart); + } + break; + + + case Token.HOOK: { + Node ifThen = child.Next; + Node ifElse = ifThen.Next; + VisitExpression (child, 0); + int elseJumpStart = itsICodeTop; + addGotoOp (Token.IFNE); + ; + stackChange (-1); + // Preserve tail context flag if any + VisitExpression (ifThen, contextFlags & ECF_TAIL); + int afterElseJumpStart = itsICodeTop; + addGotoOp (Token.GOTO); + resolveForwardGoto (elseJumpStart); + itsStackDepth = savedStackDepth; + // Preserve tail context flag if any + VisitExpression (ifElse, contextFlags & ECF_TAIL); + resolveForwardGoto (afterElseJumpStart); + } + break; + + + case Token.GETPROP: + VisitExpression (child, 0); + child = child.Next; + addStringOp (Token.GETPROP, child.String); + break; + + + case Token.ADD: + case Token.GETELEM: + case Token.DELPROP: + case Token.BITAND: + case Token.BITOR: + case Token.BITXOR: + case Token.LSH: + case Token.RSH: + case Token.URSH: + case Token.SUB: + case Token.MOD: + case Token.DIV: + case Token.MUL: + case Token.EQ: + case Token.NE: + case Token.SHEQ: + case Token.SHNE: + case Token.IN: + case Token.INSTANCEOF: + case Token.LE: + case Token.LT: + case Token.GE: + case Token.GT: + VisitExpression (child, 0); + VisitExpression (child.Next, 0); + addToken (type); + stackChange (-1); + break; + + + case Token.POS: + case Token.NEG: + case Token.NOT: + case Token.BITNOT: + case Token.TYPEOF: + case Token.VOID: + VisitExpression (child, 0); + if (type == Token.VOID) { + addIcode (Icode_POP); + addIcode (Icode_UNDEF); + } + else { + addToken (type); + } + break; + + + case Token.GET_REF: + case Token.DEL_REF: + VisitExpression (child, 0); + addToken (type); + break; + + + case Token.SETPROP: + case Token.SETPROP_OP: + case Token.SETPROP_GETTER: + case Token.SETPROP_SETTER: + { + VisitExpression (child, 0); + child = child.Next; + string property = child.String; + child = child.Next; + if (type == Token.SETPROP_OP) { + addIcode (Icode_DUP); + stackChange (1); + addStringOp (Token.GETPROP, property); + // Compensate for the following USE_STACK + stackChange (-1); + } + VisitExpression (child, 0); + addStringOp ((type == Token.SETPROP_OP) ? Token.SETPROP : type, property); + stackChange (-1); + } + break; + + + case Token.SETELEM: + case Token.SETELEM_OP: + VisitExpression (child, 0); + child = child.Next; + VisitExpression (child, 0); + child = child.Next; + if (type == Token.SETELEM_OP) { + addIcode (Icode_DUP2); + stackChange (2); + addToken (Token.GETELEM); + stackChange (-1); + // Compensate for the following USE_STACK + stackChange (-1); + } + VisitExpression (child, 0); + addToken (Token.SETELEM); + stackChange (-2); + break; + + + case Token.SET_REF: + case Token.SET_REF_OP: + VisitExpression (child, 0); + child = child.Next; + if (type == Token.SET_REF_OP) { + addIcode (Icode_DUP); + stackChange (1); + addToken (Token.GET_REF); + // Compensate for the following USE_STACK + stackChange (-1); + } + VisitExpression (child, 0); + addToken (Token.SET_REF); + stackChange (-1); + break; + + + case Token.SETNAME: { + string name = child.String; + VisitExpression (child, 0); + child = child.Next; + VisitExpression (child, 0); + addStringOp (Token.SETNAME, name); + stackChange (-1); + } + break; + + + case Token.TYPEOFNAME: { + string name = node.String; + int index = -1; + // use typeofname if an activation frame exists + // since the vars all exist there instead of in jregs + if (itsInFunctionFlag && !itsData.itsNeedsActivation) + index = scriptOrFn.getParamOrVarIndex (name); + if (index == -1) { + addStringOp (Icode_TYPEOFNAME, name); + stackChange (1); + } + else { + addVarOp (Token.GETVAR, index); + stackChange (1); + addToken (Token.TYPEOF); + } + } + break; + + + case Token.BINDNAME: + case Token.NAME: + case Token.STRING: + addStringOp (type, node.String); + stackChange (1); + break; + + + case Token.INC: + case Token.DEC: + VisitIncDec (node, child); + break; + + + case Token.NUMBER: { + double num = node.Double; + int inum = (int)num; + if (inum == num) { + if (inum == 0) { + addIcode (Icode_ZERO); + // Check for negative zero + if (1.0 / num < 0.0) { + addToken (Token.NEG); + } + } + else if (inum == 1) { + addIcode (Icode_ONE); + } + else if ((short)inum == inum) { + addIcode (Icode_SHORTNUMBER); + // write short as uin16 bit pattern + addUint16 (inum & 0xFFFF); + } + else { + addIcode (Icode_INTNUMBER); + addInt (inum); + } + } + else { + int index = GetDoubleIndex (num); + addIndexOp (Token.NUMBER, index); + } + stackChange (1); + } + break; + + + case Token.GETVAR: { + if (itsData.itsNeedsActivation) + Context.CodeBug (); + string name = node.String; + int index = scriptOrFn.getParamOrVarIndex (name); + addVarOp (Token.GETVAR, index); + stackChange (1); + } + break; + + + case Token.SETVAR: { + if (itsData.itsNeedsActivation) + Context.CodeBug (); + string name = child.String; + child = child.Next; + VisitExpression (child, 0); + int index = scriptOrFn.getParamOrVarIndex (name); + addVarOp (Token.SETVAR, index); + } + break; + + + + case Token.NULL: + case Token.THIS: + case Token.THISFN: + case Token.FALSE: + case Token.TRUE: + addToken (type); + stackChange (1); + break; + + + case Token.ENUM_NEXT: + case Token.ENUM_ID: + addIndexOp (type, getLocalBlockRef (node)); + stackChange (1); + break; + + + case Token.REGEXP: { + int index = node.getExistingIntProp (Node.REGEXP_PROP); + addIndexOp (Token.REGEXP, index); + stackChange (1); + } + break; + + + case Token.ARRAYLIT: + case Token.OBJECTLIT: + VisitLiteral (node, child); + break; + + + case Token.REF_SPECIAL: + VisitExpression (child, 0); + addStringOp (type, (string)node.getProp (Node.NAME_PROP)); + break; + + + case Token.REF_MEMBER: + case Token.REF_NS_MEMBER: + case Token.REF_NAME: + case Token.REF_NS_NAME: { + int memberTypeFlags = node.getIntProp (Node.MEMBER_TYPE_PROP, 0); + // generate possible target, possible namespace and member + int childCount = 0; + do { + VisitExpression (child, 0); + ++childCount; + child = child.Next; + } + while (child != null); + addIndexOp (type, memberTypeFlags); + stackChange (1 - childCount); + } + break; + + + case Token.DOTQUERY: { + int queryPC; + updateLineNumber (node); + VisitExpression (child, 0); + addIcode (Icode_ENTERDQ); + stackChange (-1); + queryPC = itsICodeTop; + VisitExpression (child.Next, 0); + addBackwardGoto (Icode_LEAVEDQ, queryPC); + } + break; + + + case Token.DEFAULTNAMESPACE: + case Token.ESCXMLATTR: + case Token.ESCXMLTEXT: + VisitExpression (child, 0); + addToken (type); + break; + + default: + throw badTree (node); + + } + //if (savedStackDepth + 1 != itsStackDepth) { + // EcmaScriptHelper.CodeBug(); + //} + } + + void generateCallFunAndThis (Node left) + { + // Generate code to place on stack function and thisObj + int type = left.Type; + switch (type) { + + case Token.NAME: { + string name = left.String; + // stack: ... -> ... function thisObj + addStringOp (Icode_NAME_AND_THIS, name); + stackChange (2); + break; + } + + case Token.GETPROP: + case Token.GETELEM: { + Node target = left.FirstChild; + VisitExpression (target, 0); + Node id = target.Next; + if (type == Token.GETPROP) { + string property = id.String; + // stack: ... target -> ... function thisObj + addStringOp (Icode_PROP_AND_THIS, property); + stackChange (1); + } + else { + VisitExpression (id, 0); + // stack: ... target id -> ... function thisObj + addIcode (Icode_ELEM_AND_THIS); + } + break; + } + + default: + // Including Token.GETVAR + VisitExpression (left, 0); + // stack: ... value -> ... function thisObj + addIcode (Icode_VALUE_AND_THIS); + stackChange (1); + break; + + } + } + + void VisitIncDec (Node node, Node child) + { + int incrDecrMask = node.getExistingIntProp (Node.INCRDECR_PROP); + int childType = child.Type; + switch (childType) { + + case Token.GETVAR: { + if (itsData.itsNeedsActivation) + Context.CodeBug (); + string name = child.String; + int i = scriptOrFn.getParamOrVarIndex (name); + addVarOp (Icode_VAR_INC_DEC, i); + addUint8 (incrDecrMask); + stackChange (1); + break; + } + + case Token.NAME: { + string name = child.String; + addStringOp (Icode_NAME_INC_DEC, name); + addUint8 (incrDecrMask); + stackChange (1); + break; + } + + case Token.GETPROP: { + Node obj = child.FirstChild; + VisitExpression (obj, 0); + string property = obj.Next.String; + addStringOp (Icode_PROP_INC_DEC, property); + addUint8 (incrDecrMask); + break; + } + + case Token.GETELEM: { + Node obj = child.FirstChild; + VisitExpression (obj, 0); + Node index = obj.Next; + VisitExpression (index, 0); + addIcode (Icode_ELEM_INC_DEC); + addUint8 (incrDecrMask); + stackChange (-1); + break; + } + + case Token.GET_REF: { + Node rf = child.FirstChild; + VisitExpression (rf, 0); + addIcode (Icode_REF_INC_DEC); + addUint8 (incrDecrMask); + break; + } + + default: { + throw badTree (node); + } + + } + } + + void VisitLiteral (Node node, Node child) + { + int type = node.Type; + int count; + object [] propertyIds = null; + if (type == Token.ARRAYLIT) { + count = 0; + for (Node n = child; n != null; n = n.Next) { + ++count; + } + } + else if (type == Token.OBJECTLIT) { + propertyIds = (object [])node.getProp (Node.OBJECT_IDS_PROP); + count = propertyIds.Length; + } + else { + throw badTree (node); + } + addIndexOp (Icode_LITERAL_NEW, count); + stackChange (1); + while (child != null) { + VisitExpression (child, 0); + addIcode (Icode_LITERAL_SET); + stackChange (-1); + child = child.Next; + } + if (type == Token.ARRAYLIT) { + int [] skipIndexes = (int [])node.getProp (Node.SKIP_INDEXES_PROP); + if (skipIndexes == null) { + addToken (Token.ARRAYLIT); + } + else { + int index = itsLiteralIds.size (); + itsLiteralIds.add (skipIndexes); + addIndexOp (Icode_SPARE_ARRAYLIT, index); + } + } + else { + int index = itsLiteralIds.size (); + itsLiteralIds.add (propertyIds); + addIndexOp (Token.OBJECTLIT, index); + } + } + + int getLocalBlockRef (Node node) + { + Node localBlock = (Node)node.getProp (Node.LOCAL_BLOCK_PROP); + return localBlock.getExistingIntProp (Node.LOCAL_PROP); + } + + int getTargetLabel (Node target) + { + int label = target.labelId (); + if (label != -1) { + return label; + } + label = itsLabelTableTop; + if (itsLabelTable == null || label == itsLabelTable.Length) { + if (itsLabelTable == null) { + itsLabelTable = new int [MIN_LABEL_TABLE_SIZE]; + } + else { + int [] tmp = new int [itsLabelTable.Length * 2]; + Array.Copy (itsLabelTable, 0, tmp, 0, label); + itsLabelTable = tmp; + } + } + itsLabelTableTop = label + 1; + itsLabelTable [label] = -1; + + target.labelId (label); + return label; + } + + void markTargetLabel (Node target) + { + int label = getTargetLabel (target); + if (itsLabelTable [label] != -1) { + // Can mark label only once + Context.CodeBug (); + } + itsLabelTable [label] = itsICodeTop; + } + + void addGoto (Node target, int gotoOp) + { + int label = getTargetLabel (target); + if (!(label < itsLabelTableTop)) + Context.CodeBug (); + int targetPC = itsLabelTable [label]; + + if (targetPC != -1) { + addBackwardGoto (gotoOp, targetPC); + } + else { + int gotoPC = itsICodeTop; + addGotoOp (gotoOp); + int top = itsFixupTableTop; + if (itsFixupTable == null || top == itsFixupTable.Length) { + if (itsFixupTable == null) { + itsFixupTable = new long [MIN_FIXUP_TABLE_SIZE]; + } + else { + long [] tmp = new long [itsFixupTable.Length * 2]; + Array.Copy (itsFixupTable, 0, tmp, 0, top); + itsFixupTable = tmp; + } + } + itsFixupTableTop = top + 1; + itsFixupTable [top] = ((long)label << 32) | (uint)gotoPC; + } + } + + void fixLabelGotos () + { + for (int i = 0; i < itsFixupTableTop; i++) { + long fixup = itsFixupTable [i]; + int label = (int)(fixup >> 32); + int jumpSource = (int)fixup; + int pc = itsLabelTable [label]; + if (pc == -1) { + // Unlocated label + throw Context.CodeBug (); + } + resolveGoto (jumpSource, pc); + } + itsFixupTableTop = 0; + } + + void addBackwardGoto (int gotoOp, int jumpPC) + { + int fromPC = itsICodeTop; + // Ensure that this is a jump backward + if (fromPC <= jumpPC) + throw Context.CodeBug (); + addGotoOp (gotoOp); + resolveGoto (fromPC, jumpPC); + } + + + void resolveForwardGoto (int fromPC) + { + // Ensure that forward jump skips at least self bytecode + if (itsICodeTop < fromPC + 3) + throw Context.CodeBug (); + resolveGoto (fromPC, itsICodeTop); + } + + void resolveGoto (int fromPC, int jumpPC) + { + int offset = jumpPC - fromPC; + // Ensure that jumps do not overlap + if (0 <= offset && offset <= 2) + throw Context.CodeBug (); + int offsetSite = fromPC + 1; + if (offset != (short)offset) { + if (itsData.longJumps == null) { + itsData.longJumps = new UintMap (); + } + itsData.longJumps.put (offsetSite, jumpPC); + offset = 0; + } + sbyte [] array = itsData.itsICode; + array [offsetSite] = (sbyte)(offset >> 8); + array [offsetSite + 1] = (sbyte)offset; + } + + void addToken (int token) + { + if (!ValidTokenCode (token)) + throw Context.CodeBug (); + addUint8 (token); + } + + void addIcode (int icode) + { + if (!validIcode (icode)) + throw Context.CodeBug (); + // Write negative icode as uint8 bits + addUint8 (icode & 0xFF); + } + + void addUint8 (int value) + { + if ((value & ~0xFF) != 0) + throw Context.CodeBug (); + sbyte [] array = itsData.itsICode; + int top = itsICodeTop; + if (top == array.Length) { + array = increaseICodeCapasity (1); + } + array [top] = (sbyte)value; + itsICodeTop = top + 1; + } + + void addUint16 (int value) + { + if ((value & ~0xFFFF) != 0) + throw Context.CodeBug (); + sbyte [] array = itsData.itsICode; + int top = itsICodeTop; + if (top + 2 > array.Length) { + array = increaseICodeCapasity (2); + } + array [top] = (sbyte)((uint)value >> 8); + array [top + 1] = (sbyte)value; + itsICodeTop = top + 2; + } + + void addInt (int i) + { + sbyte [] array = itsData.itsICode; + int top = itsICodeTop; + if (top + 4 > array.Length) { + array = increaseICodeCapasity (4); + } + array [top] = (sbyte)((uint)i >> 24); + array [top + 1] = (sbyte)((uint)i >> 16); + array [top + 2] = (sbyte)((uint)i >> 8); + array [top + 3] = (sbyte)i; + itsICodeTop = top + 4; + } + + int GetDoubleIndex (double num) + { + int index = itsDoubleTableTop; + if (index == 0) { + itsData.itsDoubleTable = new double [64]; + } + else if (itsData.itsDoubleTable.Length == index) { + double [] na = new double [index * 2]; + Array.Copy (itsData.itsDoubleTable, 0, na, 0, index); + itsData.itsDoubleTable = na; + } + itsData.itsDoubleTable [index] = num; + itsDoubleTableTop = index + 1; + return index; + } + + void addVarOp (int op, int varIndex) + { + switch (op) { + + case Token.GETVAR: + case Token.SETVAR: + if (varIndex < 128) { + addIcode (op == Token.GETVAR ? Icode_GETVAR1 : Icode_SETVAR1); + addUint8 (varIndex); + return; + } + // fallthrough + goto case Icode_VAR_INC_DEC; + + case Icode_VAR_INC_DEC: + addIndexOp (op, varIndex); + return; + } + throw Context.CodeBug (); + } + + void addStringOp (int op, string str) + { + addStringPrefix (str); + if (validIcode (op)) { + addIcode (op); + } + else { + addToken (op); + } + } + + void addIndexOp (int op, int index) + { + addIndexPrefix (index); + if (validIcode (op)) { + addIcode (op); + } + else { + addToken (op); + } + } + + void addStringPrefix (string str) + { + int index = itsStrings.Get (str, -1); + if (index == -1) { + index = itsStrings.size (); + itsStrings.put (str, index); + } + if (index < 4) { + addIcode (Icode_REG_STR_C0 - index); + } + else if (index <= 0xFF) { + addIcode (Icode_REG_STR1); + addUint8 (index); + } + else if (index <= 0xFFFF) { + addIcode (Icode_REG_STR2); + addUint16 (index); + } + else { + addIcode (Icode_REG_STR4); + addInt (index); + } + } + + void addIndexPrefix (int index) + { + if (index < 0) + Context.CodeBug (); + if (index < 6) { + addIcode (Icode_REG_IND_C0 - index); + } + else if (index <= 0xFF) { + addIcode (Icode_REG_IND1); + addUint8 (index); + } + else if (index <= 0xFFFF) { + addIcode (Icode_REG_IND2); + addUint16 (index); + } + else { + addIcode (Icode_REG_IND4); + addInt (index); + } + } + + void addExceptionHandler (int icodeStart, int icodeEnd, int handlerStart, bool isFinally, int exceptionObjectLocal, int scopeLocal) + { + int top = itsExceptionTableTop; + int [] table = itsData.itsExceptionTable; + if (table == null) { + if (top != 0) + Context.CodeBug (); + table = new int [EXCEPTION_SLOT_SIZE * 2]; + itsData.itsExceptionTable = table; + } + else if (table.Length == top) { + table = new int [table.Length * 2]; + Array.Copy (itsData.itsExceptionTable, 0, table, 0, top); + itsData.itsExceptionTable = table; + } + table [top + EXCEPTION_TRY_START_SLOT] = icodeStart; + table [top + EXCEPTION_TRY_END_SLOT] = icodeEnd; + table [top + EXCEPTION_HANDLER_SLOT] = handlerStart; + table [top + EXCEPTION_TYPE_SLOT] = isFinally ? 1 : 0; + table [top + EXCEPTION_LOCAL_SLOT] = exceptionObjectLocal; + table [top + EXCEPTION_SCOPE_SLOT] = scopeLocal; + + itsExceptionTableTop = top + EXCEPTION_SLOT_SIZE; + } + + sbyte [] increaseICodeCapasity (int extraSize) + { + int capacity = itsData.itsICode.Length; + int top = itsICodeTop; + if (top + extraSize <= capacity) + throw Context.CodeBug (); + capacity *= 2; + if (top + extraSize > capacity) { + capacity = top + extraSize; + } + sbyte [] array = new sbyte [capacity]; + Array.Copy (itsData.itsICode, 0, array, 0, top); + itsData.itsICode = array; + return array; + } + + void stackChange (int change) + { + if (change <= 0) { + itsStackDepth += change; + } + else { + int newDepth = itsStackDepth + change; + if (newDepth > itsData.itsMaxStack) { + itsData.itsMaxStack = newDepth; + } + itsStackDepth = newDepth; + } + } + + int allocLocal () + { + int localSlot = itsLocalTop; + ++itsLocalTop; + if (itsLocalTop > itsData.itsMaxLocals) { + itsData.itsMaxLocals = itsLocalTop; + } + return localSlot; + } + + void releaseLocal (int localSlot) + { + --itsLocalTop; + if (localSlot != itsLocalTop) + Context.CodeBug (); + } + + static int GetShort (sbyte [] iCode, int pc) + { + return (iCode [pc] << 8) | (iCode [pc + 1] & 0xFF); + } + + static int GetIndex (sbyte [] iCode, int pc) + { + return ((iCode [pc] & 0xFF) << 8) | (iCode [pc + 1] & 0xFF); + } + + static int GetInt (sbyte [] iCode, int pc) + { + return (iCode [pc] << 24) | ((iCode [pc + 1] & 0xFF) << 16) | ((iCode [pc + 2] & 0xFF) << 8) | (iCode [pc + 3] & 0xFF); + } + + static int getExceptionHandler (CallFrame frame, bool onlyFinally) + { + int [] exceptionTable = frame.idata.itsExceptionTable; + if (exceptionTable == null) { + // No exception handlers + return -1; + } + + // Icode switch in the interpreter increments PC immediately + // and it is necessary to subtract 1 from the saved PC + // to point it before the start of the next instruction. + int pc = frame.pc - 1; + + // OPT: use binary search + int best = -1, bestStart = 0, bestEnd = 0; + for (int i = 0; i != exceptionTable.Length; i += EXCEPTION_SLOT_SIZE) { + int start = exceptionTable [i + EXCEPTION_TRY_START_SLOT]; + int end = exceptionTable [i + EXCEPTION_TRY_END_SLOT]; + if (!(start <= pc && pc < end)) { + continue; + } + if (onlyFinally && exceptionTable [i + EXCEPTION_TYPE_SLOT] != 1) { + continue; + } + if (best >= 0) { + // Since handlers always nest and they never have shared end + // although they can share start it is sufficient to compare + // handlers ends + if (bestEnd < end) { + continue; + } + // Check the above assumption + if (bestStart > start) + Context.CodeBug (); // should be nested + if (bestEnd == end) + Context.CodeBug (); // no ens sharing + } + best = i; + bestStart = start; + bestEnd = end; + } + return best; + } + + static void dumpICode (InterpreterData idata) + { + if (!Token.printICode) { + return; + } + + sbyte [] iCode = idata.itsICode; + int iCodeLength = iCode.Length; + string [] strings = idata.itsStringTable; + + System.IO.TextWriter sw = Console.Out; + sw.WriteLine ("ICode dump, for " + idata.itsName + ", length = " + iCodeLength); + sw.WriteLine ("MaxStack = " + idata.itsMaxStack); + + int indexReg = 0; + for (int pc = 0; pc < iCodeLength; ) { + sw.Flush (); + sw.Write (" [" + pc + "] "); + int token = iCode [pc]; + int icodeLength = bytecodeSpan (token); + string tname = bytecodeName (token); + int old_pc = pc; + ++pc; + switch (token) { + + default: + if (icodeLength != 1) + Context.CodeBug (); + sw.WriteLine (tname); + break; + + + + case Icode_GOSUB: + case Token.GOTO: + case Token.IFEQ: + case Token.IFNE: + case Icode_IFEQ_POP: + case Icode_LEAVEDQ: { + int newPC = pc + GetShort (iCode, pc) - 1; + sw.WriteLine (tname + " " + newPC); + pc += 2; + break; + } + + case Icode_VAR_INC_DEC: + case Icode_NAME_INC_DEC: + case Icode_PROP_INC_DEC: + case Icode_ELEM_INC_DEC: + case Icode_REF_INC_DEC: { + int incrDecrType = iCode [pc]; + sw.WriteLine (tname + " " + incrDecrType); + ++pc; + break; + } + + + case Icode_CALLSPECIAL: { + int callType = iCode [pc] & 0xFF; + bool isNew = (iCode [pc + 1] != 0); + int line = GetIndex (iCode, pc + 2); + sw.WriteLine (tname + " " + callType + " " + isNew + " " + indexReg + " " + line); + pc += 4; + break; + } + + + case Token.CATCH_SCOPE: { + bool afterFisrtFlag = (iCode [pc] != 0); + sw.WriteLine (tname + " " + afterFisrtFlag); + ++pc; + } + break; + + case Token.REGEXP: + sw.WriteLine (tname + " " + idata.itsRegExpLiterals [indexReg]); + break; + + case Token.OBJECTLIT: + case Icode_SPARE_ARRAYLIT: + sw.WriteLine (tname + " " + idata.literalIds [indexReg]); + break; + + case Icode_CLOSURE_EXPR: + case Icode_CLOSURE_STMT: + sw.WriteLine (tname + " " + idata.itsNestedFunctions [indexReg]); + break; + + case Token.CALL: + case Icode_TAIL_CALL: + case Token.REF_CALL: + case Token.NEW: + sw.WriteLine (tname + ' ' + indexReg); + break; + + case Token.THROW: { + int line = GetIndex (iCode, pc); + sw.WriteLine (tname + " : " + line); + pc += 2; + break; + } + + case Icode_SHORTNUMBER: { + int value = GetShort (iCode, pc); + sw.WriteLine (tname + " " + value); + pc += 2; + break; + } + + case Icode_INTNUMBER: { + int value = GetInt (iCode, pc); + sw.WriteLine (tname + " " + value); + pc += 4; + break; + } + + case Token.NUMBER: { + double value = idata.itsDoubleTable [indexReg]; + sw.WriteLine (tname + " " + value); + pc += 2; + break; + } + + case Icode_LINE: { + int line = GetIndex (iCode, pc); + sw.WriteLine (tname + " : " + line); + pc += 2; + break; + } + + case Icode_REG_STR1: { + string str = strings [0xFF & iCode [pc]]; + sw.WriteLine (tname + " \"" + str + '"'); + ++pc; + break; + } + + case Icode_REG_STR2: { + string str = strings [GetIndex (iCode, pc)]; + sw.WriteLine (tname + " \"" + str + '"'); + pc += 2; + break; + } + + case Icode_REG_STR4: { + string str = strings [GetInt (iCode, pc)]; + sw.WriteLine (tname + " \"" + str + '"'); + pc += 4; + break; + } + + case Icode_REG_IND1: { + indexReg = 0xFF & iCode [pc]; + sw.WriteLine (tname + " " + indexReg); + ++pc; + break; + } + + case Icode_REG_IND2: { + indexReg = GetIndex (iCode, pc); + sw.WriteLine (tname + " " + indexReg); + pc += 2; + break; + } + + case Icode_REG_IND4: { + indexReg = GetInt (iCode, pc); + sw.WriteLine (tname + " " + indexReg); + pc += 4; + break; + } + + case Icode_GETVAR1: + case Icode_SETVAR1: + indexReg = iCode [pc]; + sw.WriteLine (tname + " " + indexReg); + ++pc; + break; + } + if (old_pc + icodeLength != pc) + Context.CodeBug (); + } + + int [] table = idata.itsExceptionTable; + if (table != null) { + sw.WriteLine ("Exception handlers: " + table.Length / EXCEPTION_SLOT_SIZE); + for (int i = 0; i != table.Length; i += EXCEPTION_SLOT_SIZE) { + int tryStart = table [i + EXCEPTION_TRY_START_SLOT]; + int tryEnd = table [i + EXCEPTION_TRY_END_SLOT]; + int handlerStart = table [i + EXCEPTION_HANDLER_SLOT]; + int type = table [i + EXCEPTION_TYPE_SLOT]; + int exceptionLocal = table [i + EXCEPTION_LOCAL_SLOT]; + int scopeLocal = table [i + EXCEPTION_SCOPE_SLOT]; + + sw.WriteLine (" tryStart=" + tryStart + " tryEnd=" + tryEnd + " handlerStart=" + handlerStart + " type=" + (type == 0 ? "catch" : "finally") + " exceptionLocal=" + exceptionLocal); + } + } + sw.Flush (); + } + + static int bytecodeSpan (int bytecode) + { + switch (bytecode) { + + case Token.THROW: + // source line + return 1 + 2; + + + case Icode_GOSUB: + case Token.GOTO: + case Token.IFEQ: + case Token.IFNE: + case Icode_IFEQ_POP: + case Icode_LEAVEDQ: + // target pc offset + return 1 + 2; + + + case Icode_CALLSPECIAL: + // call type + // is new + // line number + return 1 + 1 + 1 + 2; + + + case Token.CATCH_SCOPE: + // scope flag + return 1 + 1; + + + case Icode_VAR_INC_DEC: + case Icode_NAME_INC_DEC: + case Icode_PROP_INC_DEC: + case Icode_ELEM_INC_DEC: + case Icode_REF_INC_DEC: + // type of ++/-- + return 1 + 1; + + + case Icode_SHORTNUMBER: + // short number + return 1 + 2; + + + case Icode_INTNUMBER: + // int number + return 1 + 4; + + + case Icode_REG_IND1: + // ubyte index + return 1 + 1; + + + case Icode_REG_IND2: + // ushort index + return 1 + 2; + + + case Icode_REG_IND4: + // int index + return 1 + 4; + + + case Icode_REG_STR1: + // ubyte string index + return 1 + 1; + + + case Icode_REG_STR2: + // ushort string index + return 1 + 2; + + + case Icode_REG_STR4: + // int string index + return 1 + 4; + + + case Icode_GETVAR1: + case Icode_SETVAR1: + // byte var index + return 1 + 1; + + + case Icode_LINE: + // line number + return 1 + 2; + } + + if (!validBytecode (bytecode)) + throw Context.CodeBug (); + + return 1; + } + + internal static int [] getLineNumbers (InterpreterData data) + { + UintMap presentLines = new UintMap (); + + sbyte [] iCode = data.itsICode; + int iCodeLength = iCode.Length; + for (int pc = 0; pc != iCodeLength; ) { + int bytecode = iCode [pc]; + int span = bytecodeSpan (bytecode); + if (bytecode == Icode_LINE) { + if (span != 3) + Context.CodeBug (); + int line = GetIndex (iCode, pc + 1); + presentLines.put (line, 0); + } + pc += span; + } + + return presentLines.Keys; + } + + internal static void captureInterpreterStackInfo (EcmaScriptException ex) + { + Context cx = Context.CurrentContext; + if (cx == null || cx.lastInterpreterFrame == null) { + // No interpreter invocations + ex.m_InterpreterStackInfo = null; + ex.m_InterpreterLineData = null; + return; + } + // has interpreter frame on the stack + CallFrame [] array; + if (cx.previousInterpreterInvocations == null || cx.previousInterpreterInvocations.size () == 0) { + array = new CallFrame [1]; + } + else { + int previousCount = cx.previousInterpreterInvocations.size (); + if (cx.previousInterpreterInvocations.peek () == cx.lastInterpreterFrame) { + // It can happen if exception was generated after + // frame was pushed to cx.previousInterpreterInvocations + // but before assignment to cx.lastInterpreterFrame. + // In this case frames has to be ignored. + --previousCount; + } + array = new CallFrame [previousCount + 1]; + cx.previousInterpreterInvocations.ToArray (array); + } + array [array.Length - 1] = (CallFrame)cx.lastInterpreterFrame; + + int interpreterFrameCount = 0; + for (int i = 0; i != array.Length; ++i) { + interpreterFrameCount += 1 + array [i].frameIndex; + } + + int [] linePC = new int [interpreterFrameCount]; + // Fill linePC with pc positions from all interpreter frames. + // Start from the most nested frame + int linePCIndex = interpreterFrameCount; + for (int i = array.Length; i != 0; ) { + --i; + CallFrame frame = array [i]; + while (frame != null) { + --linePCIndex; + linePC [linePCIndex] = frame.pcSourceLineStart; + frame = frame.parentFrame; + } + } + if (linePCIndex != 0) + Context.CodeBug (); + + ex.m_InterpreterStackInfo = array; + ex.m_InterpreterLineData = linePC; + } + + internal static string GetSourcePositionFromStack (Context cx, int [] linep) + { + CallFrame frame = (CallFrame)cx.lastInterpreterFrame; + InterpreterData idata = frame.idata; + if (frame.pcSourceLineStart >= 0) { + linep [0] = GetIndex (idata.itsICode, frame.pcSourceLineStart); + } + else { + linep [0] = 0; + } + return idata.itsSourceFile; + } + + + internal static string GetStack (EcmaScriptException ex) + { + System.Text.StringBuilder sb = new System.Text.StringBuilder (); + + CallFrame [] array = (CallFrame [])ex.m_InterpreterStackInfo; + if (array == null) // TODO: When does this happen? + return sb.ToString (); + + int [] linePC = ex.m_InterpreterLineData; + int arrayIndex = array.Length; + int linePCIndex = linePC.Length; + + while (arrayIndex != 0) { + --arrayIndex; + + CallFrame frame = array [arrayIndex]; + while (frame != null) { + if (linePCIndex == 0) + Context.CodeBug (); + --linePCIndex; + InterpreterData idata = frame.idata; + + if (sb.Length > 0) + sb.Append (Environment.NewLine); + sb.Append ("\tat script"); + if (idata.itsName != null && idata.itsName.Length != 0) { + sb.Append ('.'); + sb.Append (idata.itsName); + } + sb.Append ('('); + sb.Append (idata.itsSourceFile); + int pc = linePC [linePCIndex]; + if (pc >= 0) { + // Include line info only if available + sb.Append (':'); + sb.Append (GetIndex (idata.itsICode, pc)); + } + sb.Append (')'); + + + frame = frame.parentFrame; + + + } + } + + return sb.ToString (); + } + + internal static string getPatchedStack (EcmaScriptException ex, string nativeStackTrace) + { + string tag = "EcmaScript.NET.Interpreter.interpretLoop"; + System.Text.StringBuilder sb = new System.Text.StringBuilder (nativeStackTrace.Length + 1000); + string lineSeparator = System.Environment.NewLine; + + CallFrame [] array = (CallFrame [])ex.m_InterpreterStackInfo; + if (array == null) // TODO: when does this happen? + return sb.ToString (); + + int [] linePC = ex.m_InterpreterLineData; + int arrayIndex = array.Length; + int linePCIndex = linePC.Length; + int offset = 0; + while (arrayIndex != 0) { + --arrayIndex; + int pos = nativeStackTrace.IndexOf (tag, offset); + if (pos < 0) { + break; + } + + // Skip tag length + pos += tag.Length; + // Skip until the end of line + for (; pos != nativeStackTrace.Length; ++pos) { + char c = nativeStackTrace [pos]; + if (c == '\n' || c == '\r') { + break; + } + } + sb.Append (nativeStackTrace.Substring (offset, (pos) - (offset))); + offset = pos; + + CallFrame frame = array [arrayIndex]; + while (frame != null) { + if (linePCIndex == 0) + Context.CodeBug (); + --linePCIndex; + InterpreterData idata = frame.idata; + sb.Append (lineSeparator); + sb.Append ("\tat script"); + if (idata.itsName != null && idata.itsName.Length != 0) { + sb.Append ('.'); + sb.Append (idata.itsName); + } + sb.Append ('('); + sb.Append (idata.itsSourceFile); + int pc = linePC [linePCIndex]; + if (pc >= 0) { + // Include line info only if available + sb.Append (':'); + sb.Append (GetIndex (idata.itsICode, pc)); + } + sb.Append (')'); + frame = frame.parentFrame; + } + } + sb.Append (nativeStackTrace.Substring (offset)); + + return sb.ToString (); + } + + internal static string GetEncodedSource (InterpreterData idata) + { + if (idata.encodedSource == null) { + return null; + } + return idata.encodedSource.Substring (idata.encodedSourceStart, (idata.encodedSourceEnd) - (idata.encodedSourceStart)); + } + + static void initFunction (Context cx, IScriptable scope, InterpretedFunction parent, int index) + { + InterpretedFunction fn; + fn = InterpretedFunction.createFunction (cx, scope, parent, index); + ScriptRuntime.initFunction (cx, scope, fn, fn.idata.itsFunctionType, parent.idata.evalScriptFlag); + } + + internal static object Interpret (InterpretedFunction ifun, Context cx, IScriptable scope, IScriptable thisObj, object [] args) + { + using (Helpers.StackOverflowVerifier sov = new Helpers.StackOverflowVerifier (128)) { + if (!ScriptRuntime.hasTopCall (cx)) + Context.CodeBug (); + + if (cx.interpreterSecurityDomain != ifun.securityDomain) { + object savedDomain = cx.interpreterSecurityDomain; + cx.interpreterSecurityDomain = ifun.securityDomain; + try { + return ifun.securityController.callWithDomain (ifun.securityDomain, cx, ifun, scope, thisObj, args); + } + finally { + cx.interpreterSecurityDomain = savedDomain; + } + } + + CallFrame frame = new CallFrame (); + initFrame (cx, scope, thisObj, args, null, 0, args.Length, ifun, null, frame); + + return InterpretLoop (cx, frame, (object)null); + } + } + + public static object restartContinuation (Continuation c, Context cx, IScriptable scope, object [] args) + { + if (!ScriptRuntime.hasTopCall (cx)) { + return ScriptRuntime.DoTopCall (c, cx, scope, null, args); + } + + object arg; + if (args.Length == 0) { + arg = Undefined.Value; + } + else { + arg = args [0]; + } + + CallFrame capturedFrame = (CallFrame)c.Implementation; + if (capturedFrame == null) { + // No frames to restart + return arg; + } + + ContinuationJump cjump = new ContinuationJump (c, null); + + cjump.result = arg; + return InterpretLoop (cx, null, cjump); + } + + static object InterpretLoop (Context cx, CallFrame frame, object throwable) + { + // throwable holds exception object to rethrow or catch + // It is also used for continuation restart in which case + // it holds ContinuationJump + + object DBL_MRK = UniqueTag.DoubleMark; + object undefined = Undefined.Value; + + bool instructionCounting = (cx.instructionThreshold != 0); + // arbitrary number to add to instructionCount when calling + // other functions + const int INVOCATION_COST = 100; + + // arbitrary exception cost for instruction counting + const int EXCEPTION_COST = 100; + + string stringReg = null; + int indexReg = -1; + + if (cx.lastInterpreterFrame != null) { + // save the top frame from the previous interpreterLoop + // invocation on the stack + if (cx.previousInterpreterInvocations == null) { + cx.previousInterpreterInvocations = new ObjArray (); + } + cx.previousInterpreterInvocations.push (cx.lastInterpreterFrame); + } + + // When restarting continuation throwable is not null and to jump + // to the code that rewind continuation state indexReg should be set + // to -1. + // With the normal call throable == null and indexReg == -1 allows to + // catch bugs with using indeReg to access array eleemnts before + // initializing indexReg. + + if (throwable != null) { + // Assert assumptions + if (!(throwable is ContinuationJump)) { + // It should be continuation + Context.CodeBug (); + } + } + + object interpreterResult = null; + double interpreterResultDbl = 0.0; + + for (; ; ) { + + try { + + if (throwable != null) { + // Recovering from exception, indexReg contains + // the index of handler + + if (indexReg >= 0) { + // Normal excepton handler, transfer + // control appropriately + + if (frame.frozen) { + // TODO: Deal with exceptios!!! + frame = frame.cloneFrozen (); + } + + int [] table = frame.idata.itsExceptionTable; + + frame.pc = table [indexReg + EXCEPTION_HANDLER_SLOT]; + if (instructionCounting) { + frame.pcPrevBranch = frame.pc; + } + + frame.savedStackTop = frame.emptyStackTop; + int scopeLocal = frame.localShift + table [indexReg + EXCEPTION_SCOPE_SLOT]; + int exLocal = frame.localShift + table [indexReg + EXCEPTION_LOCAL_SLOT]; + frame.scope = (IScriptable)frame.stack [scopeLocal]; + frame.stack [exLocal] = throwable; + + throwable = null; + } + else { + // Continuation restoration + ContinuationJump cjump = (ContinuationJump)throwable; + + // Clear throwable to indicate that execptions are OK + throwable = null; + + if (cjump.branchFrame != frame) + Context.CodeBug (); + + // Check that we have at least one frozen frame + // in the case of detached continuation restoration: + // unwind code ensure that + if (cjump.capturedFrame == null) + Context.CodeBug (); + + // Need to rewind branchFrame, capturedFrame + // and all frames in between + int rewindCount = cjump.capturedFrame.frameIndex + 1; + if (cjump.branchFrame != null) { + rewindCount -= cjump.branchFrame.frameIndex; + } + + int enterCount = 0; + CallFrame [] enterFrames = null; + + CallFrame x = cjump.capturedFrame; + for (int i = 0; i != rewindCount; ++i) { + if (!x.frozen) + Context.CodeBug (); + if (isFrameEnterExitRequired (x)) { + if (enterFrames == null) { + // Allocate enough space to store the rest + // of rewind frames in case all of them + // would require to enter + enterFrames = new CallFrame [rewindCount - i]; + } + enterFrames [enterCount] = x; + ++enterCount; + } + x = x.parentFrame; + } + + while (enterCount != 0) { + // execute enter: walk enterFrames in the reverse + // order since they were stored starting from + // the capturedFrame, not branchFrame + --enterCount; + x = enterFrames [enterCount]; + EnterFrame (cx, x, ScriptRuntime.EmptyArgs); + } + + // Continuation jump is almost done: capturedFrame + // points to the call to the function that captured + // continuation, so clone capturedFrame and + // emulate return that function with the suplied result + frame = cjump.capturedFrame.cloneFrozen (); + setCallResult (frame, cjump.result, cjump.resultDbl); + // restart the execution + } + + // Should be already cleared + if (throwable != null) + Context.CodeBug (); + } + else { + if (frame.frozen) + Context.CodeBug (); + } + + // Use local variables for constant values in frame + // for faster access + object [] stack = frame.stack; + double [] sDbl = frame.sDbl; + object [] vars = frame.varSource.stack; + double [] varDbls = frame.varSource.sDbl; + + sbyte [] iCode = frame.idata.itsICode; + string [] strings = frame.idata.itsStringTable; + + // Use local for stackTop as well. Since execption handlers + // can only exist at statement level where stack is empty, + // it is necessary to save/restore stackTop only accross + // function calls and normal returns. + int stackTop = frame.savedStackTop; + + // Store new frame in cx which is used for error reporting etc. + cx.lastInterpreterFrame = frame; + + for (; ; ) { + + // Exception handler assumes that PC is already incremented + // pass the instruction start when it searches the + // exception handler + int op = iCode [frame.pc++]; + { + switch (op) { + + case Token.THROW: { + object value = stack [stackTop]; + if (value == DBL_MRK) + value = sDbl [stackTop]; + stackTop--; + + int sourceLine = GetIndex (iCode, frame.pc); + throwable = new EcmaScriptThrow ( + value, frame.idata.itsSourceFile, sourceLine); + goto withoutExceptions_brk; + } + + case Token.RETHROW: { + indexReg += frame.localShift; + throwable = stack [indexReg]; + break; + } + + case Token.GE: + case Token.LE: + case Token.GT: + case Token.LT: { + --stackTop; + object rhs = stack [stackTop + 1]; + object lhs = stack [stackTop]; + bool valBln; + { + { + double rDbl, lDbl; + if (rhs == DBL_MRK) { + rDbl = sDbl [stackTop + 1]; + lDbl = stack_double (frame, stackTop); + } + else if (lhs == DBL_MRK) { + rDbl = ScriptConvert.ToNumber (rhs); + lDbl = sDbl [stackTop]; + } + else { + + goto number_compare_brk; + } + switch (op) { + + case Token.GE: + valBln = (lDbl >= rDbl); + + goto object_compare_brk; + + case Token.LE: + valBln = (lDbl <= rDbl); + + goto object_compare_brk; + + case Token.GT: + valBln = (lDbl > rDbl); + + goto object_compare_brk; + + case Token.LT: + valBln = (lDbl < rDbl); + + goto object_compare_brk; + + default: + throw Context.CodeBug (); + + } + } + + number_compare_brk: + ; + + switch (op) { + + case Token.GE: + valBln = ScriptRuntime.cmp_LE (rhs, lhs); + break; + + case Token.LE: + valBln = ScriptRuntime.cmp_LE (lhs, rhs); + break; + + case Token.GT: + valBln = ScriptRuntime.cmp_LT (rhs, lhs); + break; + + case Token.LT: + valBln = ScriptRuntime.cmp_LT (lhs, rhs); + break; + + default: + throw Context.CodeBug (); + + } + } + + object_compare_brk: + ; + + stack [stackTop] = valBln; + + goto Loop; + } + goto case Token.IN; + + case Token.IN: + case Token.INSTANCEOF: { + object rhs = stack [stackTop]; + if (rhs == DBL_MRK) + rhs = sDbl [stackTop]; + --stackTop; + object lhs = stack [stackTop]; + if (lhs == DBL_MRK) + lhs = sDbl [stackTop]; + bool valBln; + if (op == Token.IN) { + valBln = ScriptRuntime.In (lhs, rhs, cx); + } + else { + valBln = ScriptRuntime.InstanceOf (lhs, rhs, cx); + } + stack [stackTop] = valBln; + + goto Loop; + } + goto case Token.EQ; + + case Token.EQ: + case Token.NE: { + --stackTop; + bool valBln; + object rhs = stack [stackTop + 1]; + object lhs = stack [stackTop]; + if (rhs == DBL_MRK) { + if (lhs == DBL_MRK) { + valBln = (sDbl [stackTop] == sDbl [stackTop + 1]); + } + else { + valBln = ScriptRuntime.eqNumber (sDbl [stackTop + 1], lhs); + } + } + else { + if (lhs == DBL_MRK) { + valBln = ScriptRuntime.eqNumber (sDbl [stackTop], rhs); + } + else { + valBln = ScriptRuntime.eq (lhs, rhs); + } + } + valBln ^= (op == Token.NE); + stack [stackTop] = valBln; + + goto Loop; + } + goto case Token.SHEQ; + + case Token.SHEQ: + case Token.SHNE: { + --stackTop; + object rhs = stack [stackTop + 1]; + object lhs = stack [stackTop]; + bool valBln; + { + double rdbl, ldbl; + if (rhs == DBL_MRK) { + rdbl = sDbl [stackTop + 1]; + if (lhs == DBL_MRK) { + ldbl = sDbl [stackTop]; + } + else if (CliHelper.IsNumber (lhs)) { + ldbl = Convert.ToDouble (lhs); + } + else { + valBln = false; + + goto shallow_compare_brk; + } + } + else if (lhs == DBL_MRK) { + ldbl = sDbl [stackTop]; + if (rhs == DBL_MRK) { + rdbl = sDbl [stackTop + 1]; + } + else if (CliHelper.IsNumber (rhs)) { + rdbl = Convert.ToDouble (rhs); + } + else { + valBln = false; + + goto shallow_compare_brk; + } + } + else { + valBln = ScriptRuntime.shallowEq (lhs, rhs); + + goto shallow_compare_brk; + } + valBln = (ldbl == rdbl); + } + + shallow_compare_brk: + ; + + valBln ^= (op == Token.SHNE); + stack [stackTop] = valBln; + + goto Loop; + } + goto case Token.IFNE; + + case Token.IFNE: + if (stack_boolean (frame, stackTop--)) { + frame.pc += 2; + + goto Loop; + } + + goto jumplessRun_brk; + + case Token.IFEQ: + if (!stack_boolean (frame, stackTop--)) { + frame.pc += 2; + + goto Loop; + } + + goto jumplessRun_brk; + + case Icode_IFEQ_POP: + if (!stack_boolean (frame, stackTop--)) { + frame.pc += 2; + + goto Loop; + } + stack [stackTop--] = null; + + goto jumplessRun_brk; + + case Token.GOTO: + + goto jumplessRun_brk; + + case Icode_GOSUB: + ++stackTop; + stack [stackTop] = DBL_MRK; + sDbl [stackTop] = frame.pc + 2; + + goto jumplessRun_brk; + + case Icode_STARTSUB: + if (stackTop == frame.emptyStackTop + 1) { + // Call from Icode_GOSUB: store return PC address in the local + indexReg += frame.localShift; + stack [indexReg] = stack [stackTop]; + sDbl [indexReg] = sDbl [stackTop]; + --stackTop; + } + else { + // Call from exception handler: exception object is already stored + // in the local + if (stackTop != frame.emptyStackTop) + Context.CodeBug (); + } + + goto Loop; + goto case Icode_RETSUB; + + case Icode_RETSUB: { + // indexReg: local to store return address + if (instructionCounting) { + addInstructionCount (cx, frame, 0); + } + indexReg += frame.localShift; + object value = stack [indexReg]; + if (value != DBL_MRK) { + // Invocation from exception handler, restore object to rethrow + throwable = value; + goto withoutExceptions_brk; + } + // Normal return from GOSUB + frame.pc = (int)sDbl [indexReg]; + if (instructionCounting) { + frame.pcPrevBranch = frame.pc; + } + + goto Loop; + } + goto case Icode_POP; + + case Icode_POP: + stack [stackTop] = null; + stackTop--; + + goto Loop; + goto case Icode_POP_RESULT; + + case Icode_POP_RESULT: + frame.result = stack [stackTop]; + frame.resultDbl = sDbl [stackTop]; + stack [stackTop] = null; + --stackTop; + + goto Loop; + goto case Icode_DUP; + + case Icode_DUP: + stack [stackTop + 1] = stack [stackTop]; + sDbl [stackTop + 1] = sDbl [stackTop]; + stackTop++; + + goto Loop; + goto case Icode_DUP2; + + case Icode_DUP2: + stack [stackTop + 1] = stack [stackTop - 1]; + sDbl [stackTop + 1] = sDbl [stackTop - 1]; + stack [stackTop + 2] = stack [stackTop]; + sDbl [stackTop + 2] = sDbl [stackTop]; + stackTop += 2; + + goto Loop; + goto case Icode_SWAP; + + case Icode_SWAP: { + object o = stack [stackTop]; + stack [stackTop] = stack [stackTop - 1]; + stack [stackTop - 1] = o; + double d = sDbl [stackTop]; + sDbl [stackTop] = sDbl [stackTop - 1]; + sDbl [stackTop - 1] = d; + + goto Loop; + } + goto case Token.RETURN; + + case Token.RETURN: + frame.result = stack [stackTop]; + frame.resultDbl = sDbl [stackTop]; + --stackTop; + + goto Loop_brk; + + case Token.RETURN_RESULT: + + goto Loop_brk; + + case Icode_RETUNDEF: + frame.result = undefined; + + goto Loop_brk; + + case Token.BITNOT: { + int rIntValue = stack_int32 (frame, stackTop); + stack [stackTop] = DBL_MRK; + sDbl [stackTop] = ~rIntValue; + + goto Loop; + } + goto case Token.BITAND; + + case Token.BITAND: + case Token.BITOR: + case Token.BITXOR: + case Token.LSH: + case Token.RSH: { + int rIntValue = stack_int32 (frame, stackTop); + --stackTop; + int lIntValue = stack_int32 (frame, stackTop); + stack [stackTop] = DBL_MRK; + switch (op) { + + case Token.BITAND: + lIntValue &= rIntValue; + break; + + case Token.BITOR: + lIntValue |= rIntValue; + break; + + case Token.BITXOR: + lIntValue ^= rIntValue; + break; + + case Token.LSH: + lIntValue <<= rIntValue; + break; + + case Token.RSH: + lIntValue >>= rIntValue; + break; + } + sDbl [stackTop] = lIntValue; + + goto Loop; + } + goto case Token.URSH; + + case Token.URSH: { + int rIntValue = stack_int32 (frame, stackTop) & 0x1F; + --stackTop; + double lDbl = stack_double (frame, stackTop); + stack [stackTop] = DBL_MRK; + uint i = (uint)ScriptConvert.ToUint32 (lDbl); + sDbl [stackTop] = i >> rIntValue; + + goto Loop; + } + goto case Token.NEG; + + case Token.NEG: + case Token.POS: { + double rDbl = stack_double (frame, stackTop); + stack [stackTop] = DBL_MRK; + if (op == Token.NEG) { + rDbl = -rDbl; + } + sDbl [stackTop] = rDbl; + + goto Loop; + } + goto case Token.ADD; + + case Token.ADD: + --stackTop; + DoAdd (stack, sDbl, stackTop, cx); + + goto Loop; + goto case Token.SUB; + + case Token.SUB: + case Token.MUL: + case Token.DIV: + case Token.MOD: { + double rDbl = stack_double (frame, stackTop); + --stackTop; + double lDbl = stack_double (frame, stackTop); + stack [stackTop] = DBL_MRK; + switch (op) { + + case Token.SUB: + lDbl -= rDbl; + break; + + case Token.MUL: + lDbl *= rDbl; + break; + + case Token.DIV: + lDbl /= rDbl; + break; + + case Token.MOD: + lDbl %= rDbl; + break; + } + sDbl [stackTop] = lDbl; + + goto Loop; + } + goto case Token.NOT; + + case Token.NOT: + stack [stackTop] = !stack_boolean (frame, stackTop); + + goto Loop; + goto case Token.BINDNAME; + + case Token.BINDNAME: + stack [++stackTop] = ScriptRuntime.bind (cx, frame.scope, stringReg); + + goto Loop; + goto case Token.SETNAME; + + case Token.SETNAME: { + object rhs = stack [stackTop]; + if (rhs == DBL_MRK) + rhs = sDbl [stackTop]; + --stackTop; + IScriptable lhs = (IScriptable)stack [stackTop]; + stack [stackTop] = ScriptRuntime.setName (lhs, rhs, cx, frame.scope, stringReg); + + goto Loop; + } + goto case Token.DELPROP; + + case Token.DELPROP: { + object rhs = stack [stackTop]; + if (rhs == DBL_MRK) + rhs = sDbl [stackTop]; + --stackTop; + object lhs = stack [stackTop]; + if (lhs == DBL_MRK) + lhs = sDbl [stackTop]; + stack [stackTop] = ScriptRuntime.delete (lhs, rhs, cx); + + goto Loop; + } + goto case Token.GETPROP; + + case Token.GETPROP: { + object lhs = stack [stackTop]; + if (lhs == DBL_MRK) + lhs = sDbl [stackTop]; + stack [stackTop] = ScriptRuntime.getObjectProp (lhs, stringReg, cx); + + goto Loop; + } + goto case Token.SETPROP; + + case Token.SETPROP_GETTER: + case Token.SETPROP_SETTER: + case Token.SETPROP: { + object rhs = stack [stackTop]; + if (rhs == DBL_MRK) + rhs = sDbl [stackTop]; + --stackTop; + object lhs = stack [stackTop]; + if (lhs == DBL_MRK) + lhs = sDbl [stackTop]; + + switch (op) { + case Token.SETPROP_GETTER: + ((ScriptableObject)lhs).DefineGetter(stringReg, ((ICallable)rhs)); + stack[stackTop] = rhs; + break; + + case Token.SETPROP_SETTER: + ((ScriptableObject)lhs).DefineSetter(stringReg, ((ICallable)rhs)); + stack[stackTop] = rhs; + break; + + + default: + stack [stackTop] = ScriptRuntime.setObjectProp (lhs, stringReg, rhs, cx); + break; + } + + goto Loop; + } + goto case Icode_PROP_INC_DEC; + + case Icode_PROP_INC_DEC: { + object lhs = stack [stackTop]; + if (lhs == DBL_MRK) + lhs = sDbl [stackTop]; + stack [stackTop] = ScriptRuntime.propIncrDecr (lhs, stringReg, cx, iCode [frame.pc]); + ++frame.pc; + + goto Loop; + } + goto case Token.GETELEM; + + case Token.GETELEM: { + --stackTop; + object lhs = stack [stackTop]; + if (lhs == DBL_MRK) { + lhs = sDbl [stackTop]; + } + object value; + object id = stack [stackTop + 1]; + if (id != DBL_MRK) { + value = ScriptRuntime.getObjectElem (lhs, id, cx); + } + else { + double d = sDbl [stackTop + 1]; + value = ScriptRuntime.getObjectIndex (lhs, d, cx); + } + stack [stackTop] = value; + + goto Loop; + } + goto case Token.SETELEM; + + case Token.SETELEM: { + stackTop -= 2; + object rhs = stack [stackTop + 2]; + if (rhs == DBL_MRK) { + rhs = sDbl [stackTop + 2]; + } + object lhs = stack [stackTop]; + if (lhs == DBL_MRK) { + lhs = sDbl [stackTop]; + } + object value; + object id = stack [stackTop + 1]; + if (id != DBL_MRK) { + value = ScriptRuntime.setObjectElem (lhs, id, rhs, cx); + } + else { + double d = sDbl [stackTop + 1]; + value = ScriptRuntime.setObjectIndex (lhs, d, rhs, cx); + } + stack [stackTop] = value; + + goto Loop; + } + goto case Icode_ELEM_INC_DEC; + + case Icode_ELEM_INC_DEC: { + object rhs = stack [stackTop]; + if (rhs == DBL_MRK) + rhs = sDbl [stackTop]; + --stackTop; + object lhs = stack [stackTop]; + if (lhs == DBL_MRK) + lhs = sDbl [stackTop]; + stack [stackTop] = ScriptRuntime.elemIncrDecr (lhs, rhs, cx, iCode [frame.pc]); + ++frame.pc; + + goto Loop; + } + goto case Token.GET_REF; + + case Token.GET_REF: { + IRef rf = (IRef)stack [stackTop]; + stack [stackTop] = ScriptRuntime.refGet (rf, cx); + + goto Loop; + } + goto case Token.SET_REF; + + case Token.SET_REF: { + object value = stack [stackTop]; + if (value == DBL_MRK) + value = sDbl [stackTop]; + --stackTop; + IRef rf = (IRef)stack [stackTop]; + stack [stackTop] = ScriptRuntime.refSet (rf, value, cx); + + goto Loop; + } + goto case Token.DEL_REF; + + case Token.DEL_REF: { + IRef rf = (IRef)stack [stackTop]; + stack [stackTop] = ScriptRuntime.refDel (rf, cx); + + goto Loop; + } + goto case Icode_REF_INC_DEC; + + case Icode_REF_INC_DEC: { + IRef rf = (IRef)stack [stackTop]; + stack [stackTop] = ScriptRuntime.refIncrDecr (rf, cx, iCode [frame.pc]); + ++frame.pc; + + goto Loop; + } + goto case Token.LOCAL_LOAD; + + case Token.LOCAL_LOAD: + ++stackTop; + indexReg += frame.localShift; + stack [stackTop] = stack [indexReg]; + sDbl [stackTop] = sDbl [indexReg]; + + goto Loop; + goto case Icode_LOCAL_CLEAR; + + case Icode_LOCAL_CLEAR: + indexReg += frame.localShift; + stack [indexReg] = null; + + goto Loop; + goto case Icode_NAME_AND_THIS; + + case Icode_NAME_AND_THIS: + // stringReg: name + ++stackTop; + stack [stackTop] = ScriptRuntime.getNameFunctionAndThis (stringReg, cx, frame.scope); + ++stackTop; + stack [stackTop] = ScriptRuntime.lastStoredScriptable (cx); + + goto Loop; + goto case Icode_PROP_AND_THIS; + + case Icode_PROP_AND_THIS: { + object obj = stack [stackTop]; + if (obj == DBL_MRK) + obj = sDbl [stackTop]; + // stringReg: property + stack [stackTop] = ScriptRuntime.getPropFunctionAndThis (obj, stringReg, cx); + ++stackTop; + stack [stackTop] = ScriptRuntime.lastStoredScriptable (cx); + + goto Loop; + } + goto case Icode_ELEM_AND_THIS; + + case Icode_ELEM_AND_THIS: { + object obj = stack [stackTop - 1]; + if (obj == DBL_MRK) + obj = sDbl [stackTop - 1]; + object id = stack [stackTop]; + if (id == DBL_MRK) + id = sDbl [stackTop]; + stack [stackTop - 1] = ScriptRuntime.GetElemFunctionAndThis (obj, id, cx); + stack [stackTop] = ScriptRuntime.lastStoredScriptable (cx); + + goto Loop; + } + goto case Icode_VALUE_AND_THIS; + + case Icode_VALUE_AND_THIS: { + object value = stack [stackTop]; + if (value == DBL_MRK) + value = sDbl [stackTop]; + stack [stackTop] = ScriptRuntime.getValueFunctionAndThis (value, cx); + ++stackTop; + stack [stackTop] = ScriptRuntime.lastStoredScriptable (cx); + + goto Loop; + } + goto case Icode_CALLSPECIAL; + + case Icode_CALLSPECIAL: { + if (instructionCounting) { + cx.instructionCount += INVOCATION_COST; + } + int callType = iCode [frame.pc] & 0xFF; + bool isNew = (iCode [frame.pc + 1] != 0); + int sourceLine = GetIndex (iCode, frame.pc + 2); + + // indexReg: number of arguments + if (isNew) { + // stack change: function arg0 .. argN -> newResult + stackTop -= indexReg; + + object function = stack [stackTop]; + if (function == DBL_MRK) + function = sDbl [stackTop]; + object [] outArgs = GetArgsArray (stack, sDbl, stackTop + 1, indexReg); + stack [stackTop] = ScriptRuntime.newSpecial (cx, function, outArgs, frame.scope, callType); + } + else { + // stack change: function thisObj arg0 .. argN -> result + stackTop -= (1 + indexReg); + + // Call code generation ensure that stack here + // is ... Callable Scriptable + IScriptable functionThis = (IScriptable)stack [stackTop + 1]; + ICallable function = (ICallable)stack [stackTop]; + object [] outArgs = GetArgsArray (stack, sDbl, stackTop + 2, indexReg); + stack [stackTop] = ScriptRuntime.callSpecial (cx, function, functionThis, outArgs, frame.scope, frame.thisObj, callType, frame.idata.itsSourceFile, sourceLine); + } + frame.pc += 4; + + goto Loop; + } + goto case Token.CALL; + + case Token.CALL: + case Icode_TAIL_CALL: + case Token.REF_CALL: { + if (instructionCounting) { + cx.instructionCount += INVOCATION_COST; + } + // stack change: function thisObj arg0 .. argN -> result + // indexReg: number of arguments + stackTop -= (1 + indexReg); + + // CALL generation ensures that fun and funThisObj + // are already Scriptable and Callable objects respectively + ICallable fun = (ICallable)stack [stackTop]; + IScriptable funThisObj = (IScriptable)stack [stackTop + 1]; + if (op == Token.REF_CALL) { + object [] outArgs = GetArgsArray (stack, sDbl, stackTop + 2, indexReg); + stack [stackTop] = ScriptRuntime.callRef (fun, funThisObj, outArgs, cx); + + goto Loop; + } + IScriptable calleeScope = frame.scope; + if (frame.useActivation) { + calleeScope = ScriptableObject.GetTopLevelScope (frame.scope); + } + if (fun is InterpretedFunction) { + InterpretedFunction ifun = (InterpretedFunction)fun; + if (frame.fnOrScript.securityDomain == ifun.securityDomain) { + CallFrame callParentFrame = frame; + CallFrame calleeFrame = new CallFrame (); + if (op == Icode_TAIL_CALL) { + // In principle tail call can re-use the current + // frame and its stack arrays but it is hard to + // do properly. Any exceptions that can legally + // happen during frame re-initialization including + // StackOverflowException during innocent looking + // System.arraycopy may leave the current frame + // data corrupted leading to undefined behaviour + // in the catch code bellow that unwinds JS stack + // on exceptions. Then there is issue about frame release + // end exceptions there. + // To avoid frame allocation a released frame + // can be cached for re-use which would also benefit + // non-tail calls but it is not clear that this caching + // would gain in performance due to potentially + // bad iteraction with GC. + callParentFrame = frame.parentFrame; + } + initFrame (cx, calleeScope, funThisObj, stack, sDbl, stackTop + 2, indexReg, ifun, callParentFrame, calleeFrame); + if (op == Icode_TAIL_CALL) { + // Release the parent + ExitFrame (cx, frame, (object)null); + } + else { + frame.savedStackTop = stackTop; + frame.savedCallOp = op; + } + frame = calleeFrame; + + goto StateLoop; + } + } + + if (fun is Continuation) { + // Jump to the captured continuation + ContinuationJump cjump; + cjump = new ContinuationJump ((Continuation)fun, frame); + + // continuation result is the first argument if any + // of contination call + if (indexReg == 0) { + cjump.result = undefined; + } + else { + cjump.result = stack [stackTop + 2]; + cjump.resultDbl = sDbl [stackTop + 2]; + } + + // Start the real unwind job + throwable = cjump; + break; + } + + if (fun is IdFunctionObject) { + IdFunctionObject ifun = (IdFunctionObject)fun; + if (Continuation.IsContinuationConstructor (ifun)) { + captureContinuation (cx, frame, stackTop); + + goto Loop; + } + } + + object [] outArgs2 = GetArgsArray (stack, sDbl, stackTop + 2, indexReg); + stack [stackTop] = fun.Call (cx, calleeScope, funThisObj, outArgs2); + + + goto Loop; + } + goto case Token.NEW; + + case Token.NEW: { + if (instructionCounting) { + cx.instructionCount += INVOCATION_COST; + } + // stack change: function arg0 .. argN -> newResult + // indexReg: number of arguments + stackTop -= indexReg; + + object lhs = stack [stackTop]; + if (lhs is InterpretedFunction) { + InterpretedFunction f = (InterpretedFunction)lhs; + if (frame.fnOrScript.securityDomain == f.securityDomain) { + IScriptable newInstance = f.CreateObject (cx, frame.scope); + CallFrame calleeFrame = new CallFrame (); + initFrame (cx, frame.scope, newInstance, stack, sDbl, stackTop + 1, indexReg, f, frame, calleeFrame); + + stack [stackTop] = newInstance; + frame.savedStackTop = stackTop; + frame.savedCallOp = op; + frame = calleeFrame; + + goto StateLoop; + } + } + if (!(lhs is IFunction)) { + if (lhs == DBL_MRK) + lhs = sDbl [stackTop]; + throw ScriptRuntime.NotFunctionError (lhs); + } + IFunction fun = (IFunction)lhs; + + if (fun is IdFunctionObject) { + IdFunctionObject ifun = (IdFunctionObject)fun; + if (Continuation.IsContinuationConstructor (ifun)) { + captureContinuation (cx, frame, stackTop); + + goto Loop; + } + } + + object [] outArgs = GetArgsArray (stack, sDbl, stackTop + 1, indexReg); + stack [stackTop] = fun.Construct (cx, frame.scope, outArgs); + + goto Loop; + } + goto case Token.TYPEOF; + + case Token.TYPEOF: { + object lhs = stack [stackTop]; + if (lhs == DBL_MRK) + lhs = sDbl [stackTop]; + stack [stackTop] = ScriptRuntime.Typeof (lhs); + + goto Loop; + } + goto case Icode_TYPEOFNAME; + + case Icode_TYPEOFNAME: + stack [++stackTop] = ScriptRuntime.TypeofName (frame.scope, stringReg); + + goto Loop; + goto case Token.STRING; + + case Token.STRING: + stack [++stackTop] = stringReg; + + goto Loop; + goto case Icode_SHORTNUMBER; + + case Icode_SHORTNUMBER: + ++stackTop; + stack [stackTop] = DBL_MRK; + sDbl [stackTop] = GetShort (iCode, frame.pc); + frame.pc += 2; + + goto Loop; + goto case Icode_INTNUMBER; + + case Icode_INTNUMBER: + ++stackTop; + stack [stackTop] = DBL_MRK; + sDbl [stackTop] = GetInt (iCode, frame.pc); + frame.pc += 4; + + goto Loop; + goto case Token.NUMBER; + + case Token.NUMBER: + ++stackTop; + stack [stackTop] = DBL_MRK; + sDbl [stackTop] = frame.idata.itsDoubleTable [indexReg]; + + goto Loop; + goto case Token.NAME; + + case Token.NAME: + stack [++stackTop] = ScriptRuntime.name (cx, frame.scope, stringReg); + + goto Loop; + goto case Icode_NAME_INC_DEC; + + case Icode_NAME_INC_DEC: + stack [++stackTop] = ScriptRuntime.nameIncrDecr (frame.scope, stringReg, iCode [frame.pc]); + ++frame.pc; + + goto Loop; + goto case Icode_SETVAR1; + + case Icode_SETVAR1: + indexReg = iCode [frame.pc++]; + // fallthrough + goto case Token.SETVAR; + + case Token.SETVAR: + if (!frame.useActivation) { + vars [indexReg] = stack [stackTop]; + varDbls [indexReg] = sDbl [stackTop]; + } + else { + object val = stack [stackTop]; + if (val == DBL_MRK) + val = sDbl [stackTop]; + stringReg = frame.idata.argNames [indexReg]; + frame.scope.Put (stringReg, frame.scope, val); + } + + goto Loop; + goto case Icode_GETVAR1; + + case Icode_GETVAR1: + indexReg = iCode [frame.pc++]; + // fallthrough + goto case Token.GETVAR; + + case Token.GETVAR: + ++stackTop; + if (!frame.useActivation) { + stack [stackTop] = vars [indexReg]; + sDbl [stackTop] = varDbls [indexReg]; + } + else { + stringReg = frame.idata.argNames [indexReg]; + stack [stackTop] = frame.scope.Get (stringReg, frame.scope); + } + + goto Loop; + goto case Icode_VAR_INC_DEC; + + case Icode_VAR_INC_DEC: { + // indexReg : varindex + ++stackTop; + int incrDecrMask = iCode [frame.pc]; + if (!frame.useActivation) { + stack [stackTop] = DBL_MRK; + object varValue = vars [indexReg]; + double d; + if (varValue == DBL_MRK) { + d = varDbls [indexReg]; + } + else { + d = ScriptConvert.ToNumber (varValue); + vars [indexReg] = DBL_MRK; + } + double d2 = ((incrDecrMask & Node.DECR_FLAG) == 0) ? d + 1.0 : d - 1.0; + varDbls [indexReg] = d2; + sDbl [stackTop] = ((incrDecrMask & Node.POST_FLAG) == 0) ? d2 : d; + } + else { + string varName = frame.idata.argNames [indexReg]; + stack [stackTop] = ScriptRuntime.nameIncrDecr (frame.scope, varName, incrDecrMask); + } + ++frame.pc; + + goto Loop; + } + goto case Icode_ZERO; + + case Icode_ZERO: + ++stackTop; + stack [stackTop] = DBL_MRK; + sDbl [stackTop] = 0; + + goto Loop; + goto case Icode_ONE; + + case Icode_ONE: + ++stackTop; + stack [stackTop] = DBL_MRK; + sDbl [stackTop] = 1; + + goto Loop; + goto case Token.NULL; + + case Token.NULL: + stack [++stackTop] = null; + + goto Loop; + goto case Token.THIS; + + case Token.THIS: + stack [++stackTop] = frame.thisObj; + + goto Loop; + goto case Token.THISFN; + + case Token.THISFN: + stack [++stackTop] = frame.fnOrScript; + + goto Loop; + goto case Token.FALSE; + + case Token.FALSE: + stack [++stackTop] = false; + + goto Loop; + goto case Token.TRUE; + + case Token.TRUE: + stack [++stackTop] = true; + + goto Loop; + goto case Icode_UNDEF; + + case Icode_UNDEF: + stack [++stackTop] = undefined; + + goto Loop; + goto case Token.ENTERWITH; + + case Token.ENTERWITH: { + object lhs = stack [stackTop]; + if (lhs == DBL_MRK) + lhs = sDbl [stackTop]; + --stackTop; + frame.scope = ScriptRuntime.enterWith (lhs, cx, frame.scope); + + goto Loop; + } + goto case Token.LEAVEWITH; + + case Token.LEAVEWITH: + frame.scope = ScriptRuntime.leaveWith (frame.scope); + + goto Loop; + goto case Token.CATCH_SCOPE; + + case Token.CATCH_SCOPE: { + // stack top: exception object + // stringReg: name of exception variable + // indexReg: local for exception scope + --stackTop; + indexReg += frame.localShift; + + bool afterFirstScope = (frame.idata.itsICode [frame.pc] != 0); + + Exception caughtException = (Exception)stack [stackTop + 1]; + IScriptable lastCatchScope; + if (!afterFirstScope) { + lastCatchScope = null; + } + else { + lastCatchScope = (IScriptable)stack [indexReg]; + } + stack [indexReg] = ScriptRuntime.NewCatchScope (caughtException, lastCatchScope, stringReg, cx, frame.scope); + ++frame.pc; + + goto Loop; + } + goto case Token.ENUM_INIT_KEYS; + + case Token.ENUM_INIT_KEYS: + case Token.ENUM_INIT_VALUES: { + object lhs = stack [stackTop]; + if (lhs == DBL_MRK) + lhs = sDbl [stackTop]; + --stackTop; + indexReg += frame.localShift; + + if (lhs is IIdEnumerable) { + stack [indexReg] = ((IIdEnumerable)lhs).GetEnumeration (cx, (op == Token.ENUM_INIT_VALUES)); + } + else { + stack [indexReg] = new IdEnumeration (lhs, cx, (op == Token.ENUM_INIT_VALUES)); + } + + + goto Loop; + } + goto case Token.ENUM_NEXT; + + case Token.ENUM_NEXT: + case Token.ENUM_ID: { + indexReg += frame.localShift; + IdEnumeration val = (IdEnumeration)stack [indexReg]; + ++stackTop; + stack [stackTop] = (op == Token.ENUM_NEXT) ? val.MoveNext () : val.Current (cx); + + goto Loop; + } + goto case Token.REF_SPECIAL; + + case Token.REF_SPECIAL: { + //stringReg: name of special property + object obj = stack [stackTop]; + if (obj == DBL_MRK) + obj = sDbl [stackTop]; + stack [stackTop] = ScriptRuntime.specialRef (obj, stringReg, cx); + + goto Loop; + } + goto case Token.REF_MEMBER; + + case Token.REF_MEMBER: { + //indexReg: flags + object elem = stack [stackTop]; + if (elem == DBL_MRK) + elem = sDbl [stackTop]; + --stackTop; + object obj = stack [stackTop]; + if (obj == DBL_MRK) + obj = sDbl [stackTop]; + stack [stackTop] = ScriptRuntime.memberRef (obj, elem, cx, indexReg); + + goto Loop; + } + goto case Token.REF_NS_MEMBER; + + case Token.REF_NS_MEMBER: { + //indexReg: flags + object elem = stack [stackTop]; + if (elem == DBL_MRK) + elem = sDbl [stackTop]; + --stackTop; + object ns = stack [stackTop]; + if (ns == DBL_MRK) + ns = sDbl [stackTop]; + --stackTop; + object obj = stack [stackTop]; + if (obj == DBL_MRK) + obj = sDbl [stackTop]; + stack [stackTop] = ScriptRuntime.memberRef (obj, ns, elem, cx, indexReg); + + goto Loop; + } + goto case Token.REF_NAME; + + case Token.REF_NAME: { + //indexReg: flags + object name = stack [stackTop]; + if (name == DBL_MRK) + name = sDbl [stackTop]; + stack [stackTop] = ScriptRuntime.nameRef (name, cx, frame.scope, indexReg); + + goto Loop; + } + goto case Token.REF_NS_NAME; + + case Token.REF_NS_NAME: { + //indexReg: flags + object name = stack [stackTop]; + if (name == DBL_MRK) + name = sDbl [stackTop]; + --stackTop; + object ns = stack [stackTop]; + if (ns == DBL_MRK) + ns = sDbl [stackTop]; + stack [stackTop] = ScriptRuntime.nameRef (ns, name, cx, frame.scope, indexReg); + + goto Loop; + } + goto case Icode_SCOPE_LOAD; + + case Icode_SCOPE_LOAD: + indexReg += frame.localShift; + frame.scope = (IScriptable)stack [indexReg]; + + goto Loop; + goto case Icode_SCOPE_SAVE; + + case Icode_SCOPE_SAVE: + indexReg += frame.localShift; + stack [indexReg] = frame.scope; + + goto Loop; + goto case Icode_CLOSURE_EXPR; + + case Icode_CLOSURE_EXPR: { + InterpretedFunction fun = InterpretedFunction.createFunction (cx, frame.scope, frame.fnOrScript, indexReg); + stack [++stackTop] = fun; + } + goto Loop; + goto case Icode_CLOSURE_STMT; + + case Icode_CLOSURE_STMT: + initFunction (cx, frame.scope, frame.fnOrScript, indexReg); + + goto Loop; + goto case Token.REGEXP; + + case Token.REGEXP: + stack [++stackTop] = frame.scriptRegExps [indexReg]; + + goto Loop; + goto case Icode_LITERAL_NEW; + + case Icode_LITERAL_NEW: + // indexReg: number of values in the literal + ++stackTop; + stack [stackTop] = new object [indexReg]; + sDbl [stackTop] = 0; + + goto Loop; + goto case Icode_LITERAL_SET; + + case Icode_LITERAL_SET: { + object value = stack [stackTop]; + if (value == DBL_MRK) + value = sDbl [stackTop]; + --stackTop; + int i = (int)sDbl [stackTop]; + ((object [])stack [stackTop]) [i] = value; + sDbl [stackTop] = i + 1; + + goto Loop; + } + goto case Token.ARRAYLIT; + + case Token.ARRAYLIT: + case Icode_SPARE_ARRAYLIT: + case Token.OBJECTLIT: { + object [] data = (object [])stack [stackTop]; + object val; + if (op == Token.OBJECTLIT) { + object [] ids = (object [])frame.idata.literalIds [indexReg]; + val = ScriptRuntime.newObjectLiteral (ids, data, cx, frame.scope); + } + else { + int [] skipIndexces = null; + if (op == Icode_SPARE_ARRAYLIT) { + skipIndexces = (int [])frame.idata.literalIds [indexReg]; + } + val = ScriptRuntime.newArrayLiteral (data, skipIndexces, cx, frame.scope); + } + stack [stackTop] = val; + + goto Loop; + } + goto case Icode_ENTERDQ; + + case Icode_ENTERDQ: { + object lhs = stack [stackTop]; + if (lhs == DBL_MRK) + lhs = sDbl [stackTop]; + --stackTop; + frame.scope = ScriptRuntime.enterDotQuery (lhs, frame.scope); + + goto Loop; + } + goto case Icode_LEAVEDQ; + + case Icode_LEAVEDQ: { + bool valBln = stack_boolean (frame, stackTop); + object x = ScriptRuntime.updateDotQuery (valBln, frame.scope); + if (x != null) { + stack [stackTop] = x; + frame.scope = ScriptRuntime.leaveDotQuery (frame.scope); + frame.pc += 2; + + goto Loop; + } + // reset stack and PC to code after ENTERDQ + --stackTop; + + goto jumplessRun_brk; + } + + case Token.DEFAULTNAMESPACE: { + object value = stack [stackTop]; + if (value == DBL_MRK) + value = sDbl [stackTop]; + stack [stackTop] = ScriptRuntime.setDefaultNamespace (value, cx); + + goto Loop; + } + goto case Token.ESCXMLATTR; + + case Token.ESCXMLATTR: { + object value = stack [stackTop]; + if (value != DBL_MRK) { + stack [stackTop] = ScriptRuntime.escapeAttributeValue (value, cx); + } + + goto Loop; + } + goto case Token.ESCXMLTEXT; + + case Token.ESCXMLTEXT: { + object value = stack [stackTop]; + if (value != DBL_MRK) { + stack [stackTop] = ScriptRuntime.escapeTextValue (value, cx); + } + + goto Loop; + } + goto case Icode_LINE; + + case Icode_DEBUGGER: { + if (frame.debuggerFrame != null) { + frame.debuggerFrame.OnDebuggerStatement(cx); + } + break; + } + + case Icode_LINE: + frame.pcSourceLineStart = frame.pc; + if (frame.debuggerFrame != null) { + int line = GetIndex (iCode, frame.pc); + frame.debuggerFrame.OnLineChange (cx, line); + } + frame.pc += 2; + + goto Loop; + goto case Icode_REG_IND_C0; + + case Icode_REG_IND_C0: + indexReg = 0; + + goto Loop; + goto case Icode_REG_IND_C1; + + case Icode_REG_IND_C1: + indexReg = 1; + + goto Loop; + goto case Icode_REG_IND_C2; + + case Icode_REG_IND_C2: + indexReg = 2; + + goto Loop; + goto case Icode_REG_IND_C3; + + case Icode_REG_IND_C3: + indexReg = 3; + + goto Loop; + goto case Icode_REG_IND_C4; + + case Icode_REG_IND_C4: + indexReg = 4; + + goto Loop; + goto case Icode_REG_IND_C5; + + case Icode_REG_IND_C5: + indexReg = 5; + + goto Loop; + goto case Icode_REG_IND1; + + case Icode_REG_IND1: + indexReg = 0xFF & iCode [frame.pc]; + ++frame.pc; + + goto Loop; + goto case Icode_REG_IND2; + + case Icode_REG_IND2: + indexReg = GetIndex (iCode, frame.pc); + frame.pc += 2; + + goto Loop; + goto case Icode_REG_IND4; + + case Icode_REG_IND4: + indexReg = GetInt (iCode, frame.pc); + frame.pc += 4; + + goto Loop; + goto case Icode_REG_STR_C0; + + case Icode_REG_STR_C0: + stringReg = strings [0]; + + goto Loop; + goto case Icode_REG_STR_C1; + + case Icode_REG_STR_C1: + stringReg = strings [1]; + + goto Loop; + goto case Icode_REG_STR_C2; + + case Icode_REG_STR_C2: + stringReg = strings [2]; + + goto Loop; + goto case Icode_REG_STR_C3; + + case Icode_REG_STR_C3: + stringReg = strings [3]; + + goto Loop; + goto case Icode_REG_STR1; + + case Icode_REG_STR1: + stringReg = strings [0xFF & iCode [frame.pc]]; + ++frame.pc; + + goto Loop; + goto case Icode_REG_STR2; + + case Icode_REG_STR2: + stringReg = strings [GetIndex (iCode, frame.pc)]; + frame.pc += 2; + + goto Loop; + goto case Icode_REG_STR4; + + case Icode_REG_STR4: + stringReg = strings [GetInt (iCode, frame.pc)]; + frame.pc += 4; + + goto Loop; + goto default; + + default: + dumpICode (frame.idata); + throw new Exception ("Unknown icode : " + op + " @ pc : " + (frame.pc - 1)); + + } // end of interpreter switch + } + + jumplessRun_brk: + ; + // end of jumplessRun label block + + // This should be reachable only for jump implementation + // when pc points to encoded target offset + if (instructionCounting) { + addInstructionCount (cx, frame, 2); + } + int offset = GetShort (iCode, frame.pc); + if (offset != 0) { + // -1 accounts for pc pointing to jump opcode + 1 + frame.pc += offset - 1; + } + else { + frame.pc = frame.idata.longJumps.getExistingInt (frame.pc); + } + if (instructionCounting) { + frame.pcPrevBranch = frame.pc; + } + + goto Loop; + + Loop: + ; + } + + Loop_brk: + ; + // end of Loop: for + + ExitFrame (cx, frame, (object)null); + interpreterResult = frame.result; + interpreterResultDbl = frame.resultDbl; + if (frame.parentFrame != null) { + frame = frame.parentFrame; + if (frame.frozen) { + frame = frame.cloneFrozen (); + } + setCallResult (frame, interpreterResult, interpreterResultDbl); + interpreterResult = null; // Help GC + + goto StateLoop; + } + + goto StateLoop_brk; + } + // end of interpreter withoutExceptions: try + catch (Exception ex) { + if (throwable != null) { + // This is serious bug and it is better to track it ASAP + throw new Exception (); + } + throwable = ex; + } + + withoutExceptions_brk: + + // This should be reachable only after above catch or from + // finally when it needs to propagate exception or from + // explicit throw + if (throwable == null) + Context.CodeBug (); + + // Exception type + const int EX_CATCH_STATE = 2; // Can execute JS catch + const int EX_FINALLY_STATE = 1; // Can execute JS finally + const int EX_NO_JS_STATE = 0; // Terminate JS execution + + int exState; + ContinuationJump cjump2 = null; + + if (throwable is EcmaScriptThrow) { + exState = EX_CATCH_STATE; + } + else if (throwable is EcmaScriptError) { + // an offical ECMA error object, + exState = EX_CATCH_STATE; + } + else if (throwable is EcmaScriptRuntimeException) { + exState = EX_CATCH_STATE; + } + else if (throwable is EcmaScriptException) { + exState = EX_FINALLY_STATE; + } + else if (throwable is Exception) { + exState = EX_NO_JS_STATE; + } + else { + // It must be ContinuationJump + exState = EX_FINALLY_STATE; + cjump2 = (ContinuationJump)throwable; + } + + if (instructionCounting) { + try { + addInstructionCount (cx, frame, EXCEPTION_COST); + } + catch (Exception ex) { + // Error from instruction counting + // => unconditionally terminate JS + throwable = ex; + cjump2 = null; + exState = EX_NO_JS_STATE; + } + } + if (frame.debuggerFrame != null && throwable is Exception) { + // Call debugger only for RuntimeException + Exception rex = (Exception)throwable; + try { + frame.debuggerFrame.OnExceptionThrown (cx, rex); + } + catch (Exception ex) { + // Any exception from debugger + // => unconditionally terminate JS + throwable = ex; + cjump2 = null; + exState = EX_NO_JS_STATE; + } + } + + for (; ; ) { + if (exState != EX_NO_JS_STATE) { + bool onlyFinally = (exState != EX_CATCH_STATE); + indexReg = getExceptionHandler (frame, onlyFinally); + if (indexReg >= 0) { + // We caught an exception, restart the loop + // with exception pending the processing at the loop + // start + + goto StateLoop; + } + } + // No allowed execption handlers in this frame, unwind + // to parent and try to look there + + ExitFrame (cx, frame, throwable); + + frame = frame.parentFrame; + if (frame == null) { + break; + } + if (cjump2 != null && cjump2.branchFrame == frame) { + // Continuation branch point was hit, + // restart the state loop to reenter continuation + indexReg = -1; + + goto StateLoop; + } + } + + // No more frames, rethrow the exception or deal with continuation + if (cjump2 != null) { + if (cjump2.branchFrame != null) { + // The above loop should locate the top frame + Context.CodeBug (); + } + if (cjump2.capturedFrame != null) { + // Restarting detached continuation + indexReg = -1; + + goto StateLoop; + } + // Return continuation result to the caller + interpreterResult = cjump2.result; + interpreterResultDbl = cjump2.resultDbl; + throwable = null; + } + + goto StateLoop_brk; + + StateLoop: + ; + } + + StateLoop_brk: + ; + // end of StateLoop: for(;;) + + // Do cleanups/restorations before the final return or throw + + if (cx.previousInterpreterInvocations != null && cx.previousInterpreterInvocations.size () != 0) { + cx.lastInterpreterFrame = cx.previousInterpreterInvocations.pop (); + } + else { + // It was the last interpreter frame on the stack + cx.lastInterpreterFrame = null; + // Force GC of the value cx.previousInterpreterInvocations + cx.previousInterpreterInvocations = null; + } + + if (throwable != null) { + if (throwable is Helpers.StackOverflowVerifierException) { + throw Context.ReportRuntimeError ( + ScriptRuntime.GetMessage ("mag.too.deep.parser.recursion")); + } + throw (Exception)throwable; + } + + return (interpreterResult != DBL_MRK) ? interpreterResult : + interpreterResultDbl; + } + + static void initFrame (Context cx, IScriptable callerScope, IScriptable thisObj, object [] args, double [] argsDbl, int argShift, int argCount, InterpretedFunction fnOrScript, CallFrame parentFrame, CallFrame frame) + { + InterpreterData idata = fnOrScript.idata; + + bool useActivation = idata.itsNeedsActivation; + DebugFrame debuggerFrame = null; + if (cx.m_Debugger != null) { + debuggerFrame = cx.m_Debugger.GetFrame (cx, idata); + if (debuggerFrame != null) { + useActivation = true; + } + } + + if (useActivation) { + // Copy args to new array to pass to enterActivationFunction + // or debuggerFrame.onEnter + if (argsDbl != null) { + args = GetArgsArray (args, argsDbl, argShift, argCount); + } + argShift = 0; + argsDbl = null; + } + + IScriptable scope; + if (idata.itsFunctionType != 0) { + if (!idata.useDynamicScope) { + scope = fnOrScript.ParentScope; + } + else { + scope = callerScope; + } + + if (useActivation) { + scope = ScriptRuntime.createFunctionActivation (fnOrScript, scope, args); + } + } + else { + scope = callerScope; + ScriptRuntime.initScript (fnOrScript, thisObj, cx, scope, fnOrScript.idata.evalScriptFlag); + } + + if (idata.itsNestedFunctions != null) { + if (idata.itsFunctionType != 0 && !idata.itsNeedsActivation) + Context.CodeBug (); + for (int i = 0; i < idata.itsNestedFunctions.Length; i++) { + InterpreterData fdata = idata.itsNestedFunctions [i]; + if (fdata.itsFunctionType == FunctionNode.FUNCTION_STATEMENT) { + initFunction (cx, scope, fnOrScript, i); + } + } + } + + IScriptable [] scriptRegExps = null; + if (idata.itsRegExpLiterals != null) { + // Wrapped regexps for functions are stored in + // InterpretedFunction + // but for script which should not contain references to scope + // the regexps re-wrapped during each script execution + if (idata.itsFunctionType != 0) { + scriptRegExps = fnOrScript.functionRegExps; + } + else { + scriptRegExps = fnOrScript.createRegExpWraps (cx, scope); + } + } + + // Initialize args, vars, locals and stack + + int emptyStackTop = idata.itsMaxVars + idata.itsMaxLocals - 1; + int maxFrameArray = idata.itsMaxFrameArray; + if (maxFrameArray != emptyStackTop + idata.itsMaxStack + 1) + Context.CodeBug (); + + object [] stack; + double [] sDbl; + bool stackReuse; + if (frame.stack != null && maxFrameArray <= frame.stack.Length) { + // Reuse stacks from old frame + stackReuse = true; + stack = frame.stack; + sDbl = frame.sDbl; + } + else { + stackReuse = false; + stack = new object [maxFrameArray]; + sDbl = new double [maxFrameArray]; + } + + int definedArgs = idata.argCount; + if (definedArgs > argCount) { + definedArgs = argCount; + } + + // Fill the frame structure + + frame.parentFrame = parentFrame; + frame.frameIndex = (parentFrame == null) ? 0 : parentFrame.frameIndex + 1; + if (frame.frameIndex > cx.MaximumInterpreterStackDepth) + throw ScriptRuntime.TypeErrorById ("msg.stackoverflow"); + frame.frozen = false; + + frame.fnOrScript = fnOrScript; + frame.idata = idata; + + frame.stack = stack; + frame.sDbl = sDbl; + frame.varSource = frame; + frame.localShift = idata.itsMaxVars; + frame.emptyStackTop = emptyStackTop; + + frame.debuggerFrame = debuggerFrame; + frame.useActivation = useActivation; + + frame.thisObj = thisObj; + frame.scriptRegExps = scriptRegExps; + + // Initialize initial values of variables that change during + // interpretation. + frame.result = Undefined.Value; + frame.pc = 0; + frame.pcPrevBranch = 0; + frame.pcSourceLineStart = idata.firstLinePC; + frame.scope = scope; + + frame.savedStackTop = emptyStackTop; + frame.savedCallOp = 0; + + Array.Copy (args, argShift, stack, 0, definedArgs); + if (argsDbl != null) { + Array.Copy (argsDbl, argShift, sDbl, 0, definedArgs); + } + for (int i = definedArgs; i != idata.itsMaxVars; ++i) { + stack [i] = Undefined.Value; + } + if (stackReuse) { + // Clean the stack part and space beyond stack if any + // of the old array to allow to GC objects there + for (int i = emptyStackTop + 1; i != stack.Length; ++i) { + stack [i] = null; + } + } + + EnterFrame (cx, frame, args); + } + + static bool isFrameEnterExitRequired (CallFrame frame) + { + return frame.debuggerFrame != null || frame.idata.itsNeedsActivation; + } + + static void EnterFrame (Context cx, CallFrame frame, object [] args) + { + if (frame.debuggerFrame != null) { + frame.debuggerFrame.OnEnter (cx, frame.scope, frame.thisObj, args); + } + if (frame.idata.itsNeedsActivation) { + // Enter activation only when itsNeedsActivation true, not when + // useActivation holds since debugger should not interfere + // with activation chaining + ScriptRuntime.enterActivationFunction (cx, frame.scope); + } + } + + static void ExitFrame (Context cx, CallFrame frame, object throwable) + { + if (frame.idata.itsNeedsActivation) { + ScriptRuntime.exitActivationFunction (cx); + } + + if (frame.debuggerFrame != null) { + try { + if (throwable is Exception) { + frame.debuggerFrame.OnExit (cx, true, throwable); + } + else { + object result; + ContinuationJump cjump = (ContinuationJump)throwable; + if (cjump == null) { + result = frame.result; + } + else { + result = cjump.result; + } + if (result == UniqueTag.DoubleMark) { + double resultDbl; + if (cjump == null) { + resultDbl = frame.resultDbl; + } + else { + resultDbl = cjump.resultDbl; + } + result = resultDbl; + } + frame.debuggerFrame.OnExit (cx, false, result); + } + } + catch (Exception ex) { + Console.Error.WriteLine ("USAGE WARNING: onExit terminated with exception"); + Console.Error.WriteLine (ex.ToString ()); + } + } + } + + static void setCallResult (CallFrame frame, object callResult, double callResultDbl) + { + if (frame.savedCallOp == Token.CALL) { + frame.stack [frame.savedStackTop] = callResult; + frame.sDbl [frame.savedStackTop] = callResultDbl; + } + else if (frame.savedCallOp == Token.NEW) { + // If construct returns scriptable, + // then it replaces on stack top saved original instance + // of the object. + if (callResult is IScriptable) { + frame.stack [frame.savedStackTop] = callResult; + } + } + else { + Context.CodeBug (); + } + frame.savedCallOp = 0; + } + + static void captureContinuation (Context cx, CallFrame frame, int stackTop) + { + Continuation c = new Continuation (); + ScriptRuntime.setObjectProtoAndParent (c, ScriptRuntime.getTopCallScope (cx)); + + // Make sure that all frames upstack frames are frozen + CallFrame x = frame.parentFrame; + while (x != null && !x.frozen) { + x.frozen = true; + // Allow to GC unused stack space + for (int i = x.savedStackTop + 1; i != x.stack.Length; ++i) { + // Allow to GC unused stack space + x.stack [i] = null; + } + if (x.savedCallOp == Token.CALL) { + // the call will always overwrite the stack top with the result + x.stack [x.savedStackTop] = null; + } + else { + if (x.savedCallOp != Token.NEW) + Context.CodeBug (); + // the new operator uses stack top to store the constructed + // object so it shall not be cleared: see comments in + // setCallResult + } + x = x.parentFrame; + } + + c.initImplementation (frame.parentFrame); + frame.stack [stackTop] = c; + } + + static int stack_int32 (CallFrame frame, int i) + { + object x = frame.stack [i]; + double value; + if (x == UniqueTag.DoubleMark) { + value = frame.sDbl [i]; + } + else { + value = ScriptConvert.ToNumber (x); + } + return ScriptConvert.ToInt32 (value); + } + + static double stack_double (CallFrame frame, int i) + { + object x = frame.stack [i]; + if (x != UniqueTag.DoubleMark) { + return ScriptConvert.ToNumber (x); + } + else { + return frame.sDbl [i]; + } + } + + static bool stack_boolean (CallFrame frame, int i) + { + object x = frame.stack [i]; + if (x is bool) { + return (bool)x; + } + else if (x == UniqueTag.DoubleMark) { + double d = frame.sDbl [i]; + return !double.IsNaN (d) && d != 0.0; + } + else if (x == null || x == Undefined.Value) { + return false; + } + else if (CliHelper.IsNumber (x)) { + double d = Convert.ToDouble (x); + return (!double.IsNaN (d) && d != 0.0); + } + else { + return ScriptConvert.ToBoolean (x); + } + } + + static void DoAdd (object [] stack, double [] sDbl, int stackTop, Context cx) + { + object rhs = stack [stackTop + 1]; + object lhs = stack [stackTop]; + double d; + bool leftRightOrder; + if (rhs == UniqueTag.DoubleMark) { + d = sDbl [stackTop + 1]; + if (lhs == UniqueTag.DoubleMark) { + sDbl [stackTop] += d; + return; + } + leftRightOrder = true; + // fallthrough to object + number code + } + else if (lhs == UniqueTag.DoubleMark) { + d = sDbl [stackTop]; + lhs = rhs; + leftRightOrder = false; + // fallthrough to object + number code + } + else { + if (lhs is IScriptable || rhs is IScriptable) { + stack [stackTop] = ScriptRuntime.Add (lhs, rhs, cx); + } + else if (lhs is string) { + string lstr = (string)lhs; + string rstr = ScriptConvert.ToString (rhs); + stack [stackTop] = string.Concat (lstr, rstr); + } + else if (rhs is string) { + string lstr = ScriptConvert.ToString (lhs); + string rstr = (string)rhs; + stack [stackTop] = string.Concat (lstr, rstr); + } + else { + double lDbl = (CliHelper.IsNumber (lhs)) ? Convert.ToDouble (lhs) : ScriptConvert.ToNumber (lhs); + double rDbl = (CliHelper.IsNumber (rhs)) ? Convert.ToDouble (rhs) : ScriptConvert.ToNumber (rhs); + stack [stackTop] = UniqueTag.DoubleMark; + sDbl [stackTop] = lDbl + rDbl; + } + return; + } + + // handle object(lhs) + number(d) code + if (lhs is IScriptable) { + rhs = d; + if (!leftRightOrder) { + object tmp = lhs; + lhs = rhs; + rhs = tmp; + } + stack [stackTop] = ScriptRuntime.Add (lhs, rhs, cx); + } + else if (lhs is string) { + string lstr = (string)lhs; + string rstr = ScriptConvert.ToString (d); + if (leftRightOrder) { + stack [stackTop] = string.Concat (lstr, rstr); + } + else { + stack [stackTop] = string.Concat (rstr, lstr); + } + } + else { + double lDbl = (CliHelper.IsNumber (lhs)) ? Convert.ToDouble (lhs) : ScriptConvert.ToNumber (lhs); + stack [stackTop] = UniqueTag.DoubleMark; + sDbl [stackTop] = lDbl + d; + } + } + + void addGotoOp (int gotoOp) + { + sbyte [] array = itsData.itsICode; + int top = itsICodeTop; + if (top + 3 > array.Length) { + array = increaseICodeCapasity (3); + } + array [top] = (sbyte)gotoOp; + // Offset would written later + itsICodeTop = top + 1 + 2; + } + + + static object [] GetArgsArray (object [] stack, double [] sDbl, int shift, int count) + { + if (count == 0) { + return ScriptRuntime.EmptyArgs; + } + object [] args = new object [count]; + for (int i = 0; i != count; ++i, ++shift) { + object val = stack [shift]; + if (val == UniqueTag.DoubleMark) { + val = sDbl [shift]; + } + args [i] = val; + } + return args; + } + + static void addInstructionCount (Context cx, CallFrame frame, int extra) + { + cx.instructionCount += frame.pc - frame.pcPrevBranch + extra; + if (cx.instructionCount > cx.instructionThreshold) { + cx.ObserveInstructionCount (cx.instructionCount); + cx.instructionCount = 0; + } + } + + } } \ No newline at end of file diff --git a/Code/EcmaScript.NET/InterpreterData.cs b/src/EcmaScript.NET/InterpreterData.cs similarity index 96% rename from Code/EcmaScript.NET/InterpreterData.cs rename to src/EcmaScript.NET/InterpreterData.cs index c610843..d75dcb8 100644 --- a/Code/EcmaScript.NET/InterpreterData.cs +++ b/src/EcmaScript.NET/InterpreterData.cs @@ -1,186 +1,186 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; - -using EcmaScript.NET.Debugging; -using EcmaScript.NET.Collections; - -namespace EcmaScript.NET -{ - - internal sealed class InterpreterData : DebuggableScript - { - public bool TopLevel - { - get - { - return topLevel; - } - - } - public string FunctionName - { - get - { - return itsName; - } - - } - public int ParamCount - { - get - { - return argCount; - } - - } - public int ParamAndVarCount - { - get - { - return argNames.Length; - } - - } - public string SourceName - { - get - { - return itsSourceFile; - } - - } - public bool GeneratedScript - { - get - { - return ScriptRuntime.isGeneratedScript (itsSourceFile); - } - - } - public int [] LineNumbers - { - get - { - return Interpreter.getLineNumbers (this); - } - - } - public int FunctionCount - { - get - { - return (itsNestedFunctions == null) ? 0 : itsNestedFunctions.Length; - } - - } - public DebuggableScript Parent - { - get - { - return parentData; - } - - } - - internal const int INITIAL_MAX_ICODE_LENGTH = 1024; - internal const int INITIAL_STRINGTABLE_SIZE = 64; - internal const int INITIAL_NUMBERTABLE_SIZE = 64; - - internal InterpreterData (Context.Versions languageVersion, string sourceFile, string encodedSource) - { - this.languageVersion = languageVersion; - this.itsSourceFile = sourceFile; - this.encodedSource = encodedSource; - - Init (); - } - - internal InterpreterData (InterpreterData parent) - { - this.parentData = parent; - this.languageVersion = parent.languageVersion; - this.itsSourceFile = parent.itsSourceFile; - this.encodedSource = parent.encodedSource; - - Init (); - } - - private void Init () - { - itsICode = new sbyte [INITIAL_MAX_ICODE_LENGTH]; - itsStringTable = new string [INITIAL_STRINGTABLE_SIZE]; - } - - internal string itsName; - internal string itsSourceFile; - internal bool itsNeedsActivation; - internal int itsFunctionType; - - internal string [] itsStringTable; - internal double [] itsDoubleTable; - internal InterpreterData [] itsNestedFunctions; - internal object [] itsRegExpLiterals; - - internal sbyte [] itsICode; - - internal int [] itsExceptionTable; - - internal int itsMaxVars; - internal int itsMaxLocals; - internal int itsMaxStack; - internal int itsMaxFrameArray; - - // see comments in NativeFuncion for definition of argNames and argCount - internal string [] argNames; - internal int argCount; - - internal int itsMaxCalleeArgs; - - internal string encodedSource; - internal int encodedSourceStart; - internal int encodedSourceEnd; - - internal Context.Versions languageVersion; - - internal bool useDynamicScope; - - internal bool topLevel; - - internal object [] literalIds; - - internal UintMap longJumps; - - internal int firstLinePC = -1; // PC for the first LINE icode - - internal InterpreterData parentData; - - internal bool evalScriptFlag; // true if script corresponds to eval() code - - public bool IsFunction () - { - return itsFunctionType != 0; - } - - public string GetParamOrVarName (int index) - { - return argNames [index]; - } - - public DebuggableScript GetFunction (int index) - { - return itsNestedFunctions [index]; - } - } +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; + +using EcmaScript.NET.Debugging; +using EcmaScript.NET.Collections; + +namespace EcmaScript.NET +{ + + internal sealed class InterpreterData : DebuggableScript + { + public bool TopLevel + { + get + { + return topLevel; + } + + } + public string FunctionName + { + get + { + return itsName; + } + + } + public int ParamCount + { + get + { + return argCount; + } + + } + public int ParamAndVarCount + { + get + { + return argNames.Length; + } + + } + public string SourceName + { + get + { + return itsSourceFile; + } + + } + public bool GeneratedScript + { + get + { + return ScriptRuntime.isGeneratedScript (itsSourceFile); + } + + } + public int [] LineNumbers + { + get + { + return Interpreter.getLineNumbers (this); + } + + } + public int FunctionCount + { + get + { + return (itsNestedFunctions == null) ? 0 : itsNestedFunctions.Length; + } + + } + public DebuggableScript Parent + { + get + { + return parentData; + } + + } + + internal const int INITIAL_MAX_ICODE_LENGTH = 1024; + internal const int INITIAL_STRINGTABLE_SIZE = 64; + internal const int INITIAL_NUMBERTABLE_SIZE = 64; + + internal InterpreterData (Context.Versions languageVersion, string sourceFile, string encodedSource) + { + this.languageVersion = languageVersion; + this.itsSourceFile = sourceFile; + this.encodedSource = encodedSource; + + Init (); + } + + internal InterpreterData (InterpreterData parent) + { + this.parentData = parent; + this.languageVersion = parent.languageVersion; + this.itsSourceFile = parent.itsSourceFile; + this.encodedSource = parent.encodedSource; + + Init (); + } + + private void Init () + { + itsICode = new sbyte [INITIAL_MAX_ICODE_LENGTH]; + itsStringTable = new string [INITIAL_STRINGTABLE_SIZE]; + } + + internal string itsName; + internal string itsSourceFile; + internal bool itsNeedsActivation; + internal int itsFunctionType; + + internal string [] itsStringTable; + internal double [] itsDoubleTable; + internal InterpreterData [] itsNestedFunctions; + internal object [] itsRegExpLiterals; + + internal sbyte [] itsICode; + + internal int [] itsExceptionTable; + + internal int itsMaxVars; + internal int itsMaxLocals; + internal int itsMaxStack; + internal int itsMaxFrameArray; + + // see comments in NativeFuncion for definition of argNames and argCount + internal string [] argNames; + internal int argCount; + + internal int itsMaxCalleeArgs; + + internal string encodedSource; + internal int encodedSourceStart; + internal int encodedSourceEnd; + + internal Context.Versions languageVersion; + + internal bool useDynamicScope; + + internal bool topLevel; + + internal object [] literalIds; + + internal UintMap longJumps; + + internal int firstLinePC = -1; // PC for the first LINE icode + + internal InterpreterData parentData; + + internal bool evalScriptFlag; // true if script corresponds to eval() code + + public bool IsFunction () + { + return itsFunctionType != 0; + } + + public string GetParamOrVarName (int index) + { + return argNames [index]; + } + + public DebuggableScript GetFunction (int index) + { + return itsNestedFunctions [index]; + } + } } \ No newline at end of file diff --git a/Code/EcmaScript.NET/Node.cs b/src/EcmaScript.NET/Node.cs similarity index 96% rename from Code/EcmaScript.NET/Node.cs rename to src/EcmaScript.NET/Node.cs index 2dd2a3e..d803cae 100644 --- a/Code/EcmaScript.NET/Node.cs +++ b/src/EcmaScript.NET/Node.cs @@ -1,919 +1,919 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; - -using EcmaScript.NET.Collections; - -namespace EcmaScript.NET -{ - - /// This class implements the root of the intermediate representation. - /// - /// - public class Node - { - - internal class GetterPropertyLiteral - { - internal object Property; - - public GetterPropertyLiteral (object property) - { - Property = property; - } - } - - internal class SetterPropertyLiteral - { - internal object Property; - - public SetterPropertyLiteral (object property) - { - Property = property; - } - } - - public Node FirstChild - { - get - { - return first; - } - - } - public Node LastChild - { - get - { - return last; - } - - } - public Node Next - { - get - { - return next; - } - - } - public Node LastSibling - { - get - { - Node n = this; - while (n.next != null) { - n = n.next; - } - return n; - } - - } - public int Lineno - { - get - { - return lineno; - } - - } - /// Can only be called when getType() == Token.NUMBER - public double Double - { - get - { - return ((NumberNode)this).number; - } - - set - { - ((NumberNode)this).number = value; - } - - } - /// Can only be called when node has String context. - public string String - { - get - { - return ((StringNode)this).str; - } - - set - { - if (value == null) - Context.CodeBug (); - ((StringNode)this).str = value; - } - - } - - public const int FUNCTION_PROP = 1; - public const int LOCAL_PROP = 2; - public const int LOCAL_BLOCK_PROP = 3; - public const int REGEXP_PROP = 4; - public const int CASEARRAY_PROP = 5; - public const int TARGETBLOCK_PROP = 6; - public const int VARIABLE_PROP = 7; - public const int ISNUMBER_PROP = 8; - public const int DIRECTCALL_PROP = 9; - public const int SPECIALCALL_PROP = 10; - public const int SKIP_INDEXES_PROP = 11; - public const int OBJECT_IDS_PROP = 12; - public const int INCRDECR_PROP = 13; - public const int CATCH_SCOPE_PROP = 14; - public const int LABEL_ID_PROP = 15; - public const int MEMBER_TYPE_PROP = 16; - public const int NAME_PROP = 17; - public const int LAST_PROP = NAME_PROP; - - // values of ISNUMBER_PROP to specify - // which of the children are Number Types - public const int BOTH = 0; - public const int LEFT = 1; - public const int RIGHT = 2; - - public const int NON_SPECIALCALL = 0; - public const int SPECIALCALL_EVAL = 1; - public const int SPECIALCALL_WITH = 2; - - public const int DECR_FLAG = 0x1; - public const int POST_FLAG = 0x2; - - public const int PROPERTY_FLAG = 0x1; - public const int ATTRIBUTE_FLAG = 0x2; - public const int DESCENDANTS_FLAG = 0x4; // x..y or x..@i - - - private class NumberNode : Node - { - internal NumberNode (double number) - : base (Token.NUMBER) - { - this.number = number; - } - - internal double number; - } - - private class StringNode : Node - { - internal StringNode (int Type, string str) - : base (Type) - { - this.str = str; - } - - internal string str; - } - - public class Jump : Node - { - public Jump JumpStatement - { - get - { - if (!(Type == Token.BREAK || Type == Token.CONTINUE)) - Context.CodeBug (); - return jumpNode; - } - - set - { - if (!(Type == Token.BREAK || Type == Token.CONTINUE)) - Context.CodeBug (); - if (value == null) - Context.CodeBug (); - if (this.jumpNode != null) - Context.CodeBug (); //only once - this.jumpNode = value; - } - - } - public Node Default - { - get - { - if (!(Type == Token.SWITCH)) - Context.CodeBug (); - return target2; - } - - set - { - if (!(Type == Token.SWITCH)) - Context.CodeBug (); - if (value.Type != Token.TARGET) - Context.CodeBug (); - if (target2 != null) - Context.CodeBug (); //only once - target2 = value; - } - - } - public Node Finally - { - get - { - if (!(Type == Token.TRY)) - Context.CodeBug (); - return target2; - } - - set - { - if (!(Type == Token.TRY)) - Context.CodeBug (); - if (value.Type != Token.TARGET) - Context.CodeBug (); - if (target2 != null) - Context.CodeBug (); //only once - target2 = value; - } - - } - public Jump Loop - { - get - { - if (!(Type == Token.LABEL)) - Context.CodeBug (); - return jumpNode; - } - - set - { - if (!(Type == Token.LABEL)) - Context.CodeBug (); - if (value == null) - Context.CodeBug (); - if (jumpNode != null) - Context.CodeBug (); //only once - jumpNode = value; - } - - } - public Node Continue - { - get - { - if (Type != Token.LOOP) - Context.CodeBug (); - return target2; - } - - set - { - if (Type != Token.LOOP) - Context.CodeBug (); - if (value.Type != Token.TARGET) - Context.CodeBug (); - if (target2 != null) - Context.CodeBug (); //only once - target2 = value; - } - - } - public Jump (int Type) - : base (Type) - { - } - - internal Jump (int Type, int lineno) - : base (Type, lineno) - { - } - - internal Jump (int Type, Node child) - : base (Type, child) - { - } - - internal Jump (int Type, Node child, int lineno) - : base (Type, child, lineno) - { - } - - public Node target; - private Node target2; - private Jump jumpNode; - } - - private class PropListItem - { - internal PropListItem next; - internal int Type; - internal int intValue; - internal object objectValue; - } - - - public Node (int nodeType) - { - Type = nodeType; - } - - public Node (int nodeType, Node child) - { - Type = nodeType; - first = last = child; - child.next = null; - } - - public Node (int nodeType, Node left, Node right) - { - Type = nodeType; - first = left; - last = right; - left.next = right; - right.next = null; - } - - public Node (int nodeType, Node left, Node mid, Node right) - { - Type = nodeType; - first = left; - last = right; - left.next = mid; - mid.next = right; - right.next = null; - } - - public Node (int nodeType, int line) - { - Type = nodeType; - lineno = line; - } - - public Node (int nodeType, Node child, int line) - : this (nodeType, child) - { - lineno = line; - } - - public Node (int nodeType, Node left, Node right, int line) - : this (nodeType, left, right) - { - lineno = line; - } - - public Node (int nodeType, Node left, Node mid, Node right, int line) - : this (nodeType, left, mid, right) - { - lineno = line; - } - - public static Node newNumber (double number) - { - return new NumberNode (number); - } - - public static Node newString (string str) - { - return new StringNode (Token.STRING, str); - } - - public static Node newString (int Type, string str) - { - return new StringNode (Type, str); - } - - public bool hasChildren () - { - return first != null; - } - - public Node getChildBefore (Node child) - { - if (child == first) - return null; - Node n = first; - while (n.next != child) { - n = n.next; - if (n == null) - throw new ApplicationException ("node is not a child"); - } - return n; - } - - public void addChildToFront (Node child) - { - child.next = first; - first = child; - if (last == null) { - last = child; - } - } - - public void addChildToBack (Node child) - { - child.next = null; - if (last == null) { - first = last = child; - return; - } - last.next = child; - last = child; - } - - public void addChildrenToFront (Node children) - { - Node lastSib = children.LastSibling; - lastSib.next = first; - first = children; - if (last == null) { - last = lastSib; - } - } - - public void addChildrenToBack (Node children) - { - if (last != null) { - last.next = children; - } - last = children.LastSibling; - if (first == null) { - first = children; - } - } - - /// Add 'child' before 'node'. - public void addChildBefore (Node newChild, Node node) - { - if (newChild.next != null) - throw new ApplicationException ("newChild had siblings in addChildBefore"); - if (first == node) { - newChild.next = first; - first = newChild; - return; - } - Node prev = getChildBefore (node); - addChildAfter (newChild, prev); - } - - /// Add 'child' after 'node'. - public void addChildAfter (Node newChild, Node node) - { - if (newChild.next != null) - throw new ApplicationException ("newChild had siblings in addChildAfter"); - newChild.next = node.next; - node.next = newChild; - if (last == node) - last = newChild; - } - - public void removeChild (Node child) - { - Node prev = getChildBefore (child); - if (prev == null) - first = first.next; - else - prev.next = child.next; - if (child == last) - last = prev; - child.next = null; - } - - public void replaceChild (Node child, Node newChild) - { - newChild.next = child.next; - if (child == first) { - first = newChild; - } - else { - Node prev = getChildBefore (child); - prev.next = newChild; - } - if (child == last) - last = newChild; - child.next = null; - } - - public void replaceChildAfter (Node prevChild, Node newChild) - { - Node child = prevChild.next; - newChild.next = child.next; - prevChild.next = newChild; - if (child == last) - last = newChild; - child.next = null; - } - - private static string propToString (int propType) - { - if (Token.printTrees) { - // If Context.printTrees is false, the compiler - // can remove all these strings. - switch (propType) { - - case FUNCTION_PROP: - return "function"; - - case LOCAL_PROP: - return "local"; - - case LOCAL_BLOCK_PROP: - return "local_block"; - - case REGEXP_PROP: - return "regexp"; - - case CASEARRAY_PROP: - return "casearray"; - - - case TARGETBLOCK_PROP: - return "targetblock"; - - case VARIABLE_PROP: - return "variable"; - - case ISNUMBER_PROP: - return "isnumber"; - - case DIRECTCALL_PROP: - return "directcall"; - - - case SPECIALCALL_PROP: - return "specialcall"; - - case SKIP_INDEXES_PROP: - return "skip_indexes"; - - case OBJECT_IDS_PROP: - return "object_ids_prop"; - - case INCRDECR_PROP: - return "incrdecr_prop"; - - case CATCH_SCOPE_PROP: - return "catch_scope_prop"; - - case LABEL_ID_PROP: - return "label_id_prop"; - - case MEMBER_TYPE_PROP: - return "member_Type_prop"; - - case NAME_PROP: - return "name_prop"; - - - default: - Context.CodeBug (); - break; - - } - } - return null; - } - - private PropListItem lookupProperty (int propType) - { - PropListItem x = propListHead; - while (x != null && propType != x.Type) { - x = x.next; - } - return x; - } - - private PropListItem ensureProperty (int propType) - { - PropListItem item = lookupProperty (propType); - if (item == null) { - item = new PropListItem (); - item.Type = propType; - item.next = propListHead; - propListHead = item; - } - return item; - } - - public void removeProp (int propType) - { - PropListItem x = propListHead; - if (x != null) { - PropListItem prev = null; - while (x.Type != propType) { - prev = x; - x = x.next; - if (x == null) { - return; - } - } - if (prev == null) { - propListHead = x.next; - } - else { - prev.next = x.next; - } - } - } - - public object getProp (int propType) - { - PropListItem item = lookupProperty (propType); - if (item == null) { - return null; - } - return item.objectValue; - } - - public int getIntProp (int propType, int defaultValue) - { - PropListItem item = lookupProperty (propType); - if (item == null) { - return defaultValue; - } - return item.intValue; - } - - public int getExistingIntProp (int propType) - { - PropListItem item = lookupProperty (propType); - if (item == null) { - Context.CodeBug (); - } - return item.intValue; - } - - public void putProp (int propType, object prop) - { - if (prop == null) { - removeProp (propType); - } - else { - PropListItem item = ensureProperty (propType); - item.objectValue = prop; - } - } - - public void putIntProp (int propType, int prop) - { - PropListItem item = ensureProperty (propType); - item.intValue = prop; - } - - public static Node newTarget () - { - return new Node (Token.TARGET); - } - - public int labelId () - { - if (Type != Token.TARGET) - Context.CodeBug (); - return getIntProp (LABEL_ID_PROP, -1); - } - - public void labelId (int labelId) - { - if (Type != Token.TARGET) - Context.CodeBug (); - putIntProp (LABEL_ID_PROP, labelId); - } - - public override string ToString () - { - if (Token.printTrees) { - System.Text.StringBuilder sb = new System.Text.StringBuilder (); - toString (new ObjToIntMap (), sb); - return sb.ToString (); - } - return Convert.ToString (Type); - } - - private void toString (ObjToIntMap printIds, System.Text.StringBuilder sb) - { - if (Token.printTrees) { - sb.Append (Token.name (this.Type)); - if (this is StringNode) { - sb.Append (' '); - sb.Append (String); - } - else if (this is ScriptOrFnNode) { - ScriptOrFnNode sof = (ScriptOrFnNode)this; - if (this is FunctionNode) { - FunctionNode fn = (FunctionNode)this; - sb.Append (' '); - sb.Append (fn.FunctionName); - } - sb.Append (" [source name: "); - sb.Append (sof.SourceName); - sb.Append ("] [encoded source length: "); - sb.Append (sof.EncodedSourceEnd - sof.EncodedSourceStart); - sb.Append ("] [base line: "); - sb.Append (sof.BaseLineno); - sb.Append ("] [end line: "); - sb.Append (sof.EndLineno); - sb.Append (']'); - } - else if (this is Jump) { - Jump jump = (Jump)this; - if (this.Type == Token.BREAK || this.Type == Token.CONTINUE) { - sb.Append (" [label: "); - appendPrintId (jump.JumpStatement, printIds, sb); - sb.Append (']'); - } - else if (this.Type == Token.TRY) { - Node catchNode = jump.target; - Node finallyTarget = jump.Finally; - if (catchNode != null) { - sb.Append (" [catch: "); - appendPrintId (catchNode, printIds, sb); - sb.Append (']'); - } - if (finallyTarget != null) { - sb.Append (" [finally: "); - appendPrintId (finallyTarget, printIds, sb); - sb.Append (']'); - } - } - else if (this.Type == Token.LABEL || this.Type == Token.LOOP || this.Type == Token.SWITCH) { - sb.Append (" [break: "); - appendPrintId (jump.target, printIds, sb); - sb.Append (']'); - if (this.Type == Token.LOOP) { - sb.Append (" [continue: "); - appendPrintId (jump.Continue, printIds, sb); - sb.Append (']'); - } - } - else { - sb.Append (" [target: "); - appendPrintId (jump.target, printIds, sb); - sb.Append (']'); - } - } - else if (this.Type == Token.NUMBER) { - sb.Append (' '); - sb.Append (Double); - } - else if (this.Type == Token.TARGET) { - sb.Append (' '); - appendPrintId (this, printIds, sb); - } - if (lineno != -1) { - sb.Append (' '); - sb.Append (lineno); - } - - for (PropListItem x = propListHead; x != null; x = x.next) { - int Type = x.Type; - sb.Append (" ["); - sb.Append (propToString (Type)); - sb.Append (": "); - string value; - switch (Type) { - - case TARGETBLOCK_PROP: // can't add this as it recurses - value = "target block property"; - break; - - case LOCAL_BLOCK_PROP: // can't add this as it is dull - value = "last local block"; - break; - - case ISNUMBER_PROP: - switch (x.intValue) { - - case BOTH: - value = "both"; - break; - - case RIGHT: - value = "right"; - break; - - case LEFT: - value = "left"; - break; - - default: - throw Context.CodeBug (); - - } - break; - - case SPECIALCALL_PROP: - switch (x.intValue) { - - case SPECIALCALL_EVAL: - value = "eval"; - break; - - case SPECIALCALL_WITH: - value = "with"; - break; - - default: - // NON_SPECIALCALL should not be stored - throw Context.CodeBug (); - - } - break; - - default: - object obj = x.objectValue; - if (obj != null) { - value = obj.ToString (); - } - else { - value = Convert.ToString (x.intValue); - } - break; - - } - sb.Append (value); - sb.Append (']'); - } - } - } - - public string toStringTree (ScriptOrFnNode treeTop) - { - if (Token.printTrees) { - System.Text.StringBuilder sb = new System.Text.StringBuilder (); - toStringTreeHelper (treeTop, this, null, 0, sb); - return sb.ToString (); - } - return null; - } - - private static void toStringTreeHelper (ScriptOrFnNode treeTop, Node n, ObjToIntMap printIds, int level, System.Text.StringBuilder sb) - { - if (Token.printTrees) { - if (printIds == null) { - printIds = new ObjToIntMap (); - generatePrintIds (treeTop, printIds); - } - for (int i = 0; i != level; ++i) { - sb.Append (" "); - } - n.toString (printIds, sb); - sb.Append ('\n'); - for (Node cursor = n.FirstChild; cursor != null; cursor = cursor.Next) { - if (cursor.Type == Token.FUNCTION) { - int fnIndex = cursor.getExistingIntProp (Node.FUNCTION_PROP); - FunctionNode fn = treeTop.getFunctionNode (fnIndex); - toStringTreeHelper (fn, fn, null, level + 1, sb); - } - else { - toStringTreeHelper (treeTop, cursor, printIds, level + 1, sb); - } - } - } - } - - private static void generatePrintIds (Node n, ObjToIntMap map) - { - if (Token.printTrees) { - map.put (n, map.size ()); - for (Node cursor = n.FirstChild; cursor != null; cursor = cursor.Next) { - generatePrintIds (cursor, map); - } - } - } - - private static void appendPrintId (Node n, ObjToIntMap printIds, System.Text.StringBuilder sb) - { - if (Token.printTrees) { - if (n != null) { - int id = printIds.Get (n, -1); - sb.Append ('#'); - if (id != -1) { - sb.Append (id + 1); - } - else { - sb.Append (""); - } - } - } - } - - internal int Type; // Type of the node; Token.NAME for example - internal Node next; // next sibling - private Node first; // first element of a linked list of children - private Node last; // last element of a linked list of children - private int lineno = -1; // encapsulated int data; depends on Type - - /// Linked list of properties. Since vast majority of nodes would have - /// no more then 2 properties, linked list saves memory and provides - /// fast lookup. If this does not holds, propListHead can be replaced - /// by UintMap. - /// - private PropListItem propListHead; - } +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; + +using EcmaScript.NET.Collections; + +namespace EcmaScript.NET +{ + + /// This class implements the root of the intermediate representation. + /// + /// + public class Node + { + + internal class GetterPropertyLiteral + { + internal object Property; + + public GetterPropertyLiteral (object property) + { + Property = property; + } + } + + internal class SetterPropertyLiteral + { + internal object Property; + + public SetterPropertyLiteral (object property) + { + Property = property; + } + } + + public Node FirstChild + { + get + { + return first; + } + + } + public Node LastChild + { + get + { + return last; + } + + } + public Node Next + { + get + { + return next; + } + + } + public Node LastSibling + { + get + { + Node n = this; + while (n.next != null) { + n = n.next; + } + return n; + } + + } + public int Lineno + { + get + { + return lineno; + } + + } + /// Can only be called when getType() == Token.NUMBER + public double Double + { + get + { + return ((NumberNode)this).number; + } + + set + { + ((NumberNode)this).number = value; + } + + } + /// Can only be called when node has String context. + public string String + { + get + { + return ((StringNode)this).str; + } + + set + { + if (value == null) + Context.CodeBug (); + ((StringNode)this).str = value; + } + + } + + public const int FUNCTION_PROP = 1; + public const int LOCAL_PROP = 2; + public const int LOCAL_BLOCK_PROP = 3; + public const int REGEXP_PROP = 4; + public const int CASEARRAY_PROP = 5; + public const int TARGETBLOCK_PROP = 6; + public const int VARIABLE_PROP = 7; + public const int ISNUMBER_PROP = 8; + public const int DIRECTCALL_PROP = 9; + public const int SPECIALCALL_PROP = 10; + public const int SKIP_INDEXES_PROP = 11; + public const int OBJECT_IDS_PROP = 12; + public const int INCRDECR_PROP = 13; + public const int CATCH_SCOPE_PROP = 14; + public const int LABEL_ID_PROP = 15; + public const int MEMBER_TYPE_PROP = 16; + public const int NAME_PROP = 17; + public const int LAST_PROP = NAME_PROP; + + // values of ISNUMBER_PROP to specify + // which of the children are Number Types + public const int BOTH = 0; + public const int LEFT = 1; + public const int RIGHT = 2; + + public const int NON_SPECIALCALL = 0; + public const int SPECIALCALL_EVAL = 1; + public const int SPECIALCALL_WITH = 2; + + public const int DECR_FLAG = 0x1; + public const int POST_FLAG = 0x2; + + public const int PROPERTY_FLAG = 0x1; + public const int ATTRIBUTE_FLAG = 0x2; + public const int DESCENDANTS_FLAG = 0x4; // x..y or x..@i + + + private class NumberNode : Node + { + internal NumberNode (double number) + : base (Token.NUMBER) + { + this.number = number; + } + + internal double number; + } + + private class StringNode : Node + { + internal StringNode (int Type, string str) + : base (Type) + { + this.str = str; + } + + internal string str; + } + + public class Jump : Node + { + public Jump JumpStatement + { + get + { + if (!(Type == Token.BREAK || Type == Token.CONTINUE)) + Context.CodeBug (); + return jumpNode; + } + + set + { + if (!(Type == Token.BREAK || Type == Token.CONTINUE)) + Context.CodeBug (); + if (value == null) + Context.CodeBug (); + if (this.jumpNode != null) + Context.CodeBug (); //only once + this.jumpNode = value; + } + + } + public Node Default + { + get + { + if (!(Type == Token.SWITCH)) + Context.CodeBug (); + return target2; + } + + set + { + if (!(Type == Token.SWITCH)) + Context.CodeBug (); + if (value.Type != Token.TARGET) + Context.CodeBug (); + if (target2 != null) + Context.CodeBug (); //only once + target2 = value; + } + + } + public Node Finally + { + get + { + if (!(Type == Token.TRY)) + Context.CodeBug (); + return target2; + } + + set + { + if (!(Type == Token.TRY)) + Context.CodeBug (); + if (value.Type != Token.TARGET) + Context.CodeBug (); + if (target2 != null) + Context.CodeBug (); //only once + target2 = value; + } + + } + public Jump Loop + { + get + { + if (!(Type == Token.LABEL)) + Context.CodeBug (); + return jumpNode; + } + + set + { + if (!(Type == Token.LABEL)) + Context.CodeBug (); + if (value == null) + Context.CodeBug (); + if (jumpNode != null) + Context.CodeBug (); //only once + jumpNode = value; + } + + } + public Node Continue + { + get + { + if (Type != Token.LOOP) + Context.CodeBug (); + return target2; + } + + set + { + if (Type != Token.LOOP) + Context.CodeBug (); + if (value.Type != Token.TARGET) + Context.CodeBug (); + if (target2 != null) + Context.CodeBug (); //only once + target2 = value; + } + + } + public Jump (int Type) + : base (Type) + { + } + + internal Jump (int Type, int lineno) + : base (Type, lineno) + { + } + + internal Jump (int Type, Node child) + : base (Type, child) + { + } + + internal Jump (int Type, Node child, int lineno) + : base (Type, child, lineno) + { + } + + public Node target; + private Node target2; + private Jump jumpNode; + } + + private class PropListItem + { + internal PropListItem next; + internal int Type; + internal int intValue; + internal object objectValue; + } + + + public Node (int nodeType) + { + Type = nodeType; + } + + public Node (int nodeType, Node child) + { + Type = nodeType; + first = last = child; + child.next = null; + } + + public Node (int nodeType, Node left, Node right) + { + Type = nodeType; + first = left; + last = right; + left.next = right; + right.next = null; + } + + public Node (int nodeType, Node left, Node mid, Node right) + { + Type = nodeType; + first = left; + last = right; + left.next = mid; + mid.next = right; + right.next = null; + } + + public Node (int nodeType, int line) + { + Type = nodeType; + lineno = line; + } + + public Node (int nodeType, Node child, int line) + : this (nodeType, child) + { + lineno = line; + } + + public Node (int nodeType, Node left, Node right, int line) + : this (nodeType, left, right) + { + lineno = line; + } + + public Node (int nodeType, Node left, Node mid, Node right, int line) + : this (nodeType, left, mid, right) + { + lineno = line; + } + + public static Node newNumber (double number) + { + return new NumberNode (number); + } + + public static Node newString (string str) + { + return new StringNode (Token.STRING, str); + } + + public static Node newString (int Type, string str) + { + return new StringNode (Type, str); + } + + public bool hasChildren () + { + return first != null; + } + + public Node getChildBefore (Node child) + { + if (child == first) + return null; + Node n = first; + while (n.next != child) { + n = n.next; + if (n == null) + throw new Exception ("node is not a child"); + } + return n; + } + + public void addChildToFront (Node child) + { + child.next = first; + first = child; + if (last == null) { + last = child; + } + } + + public void addChildToBack (Node child) + { + child.next = null; + if (last == null) { + first = last = child; + return; + } + last.next = child; + last = child; + } + + public void addChildrenToFront (Node children) + { + Node lastSib = children.LastSibling; + lastSib.next = first; + first = children; + if (last == null) { + last = lastSib; + } + } + + public void addChildrenToBack (Node children) + { + if (last != null) { + last.next = children; + } + last = children.LastSibling; + if (first == null) { + first = children; + } + } + + /// Add 'child' before 'node'. + public void addChildBefore (Node newChild, Node node) + { + if (newChild.next != null) + throw new Exception ("newChild had siblings in addChildBefore"); + if (first == node) { + newChild.next = first; + first = newChild; + return; + } + Node prev = getChildBefore (node); + addChildAfter (newChild, prev); + } + + /// Add 'child' after 'node'. + public void addChildAfter (Node newChild, Node node) + { + if (newChild.next != null) + throw new Exception ("newChild had siblings in addChildAfter"); + newChild.next = node.next; + node.next = newChild; + if (last == node) + last = newChild; + } + + public void removeChild (Node child) + { + Node prev = getChildBefore (child); + if (prev == null) + first = first.next; + else + prev.next = child.next; + if (child == last) + last = prev; + child.next = null; + } + + public void replaceChild (Node child, Node newChild) + { + newChild.next = child.next; + if (child == first) { + first = newChild; + } + else { + Node prev = getChildBefore (child); + prev.next = newChild; + } + if (child == last) + last = newChild; + child.next = null; + } + + public void replaceChildAfter (Node prevChild, Node newChild) + { + Node child = prevChild.next; + newChild.next = child.next; + prevChild.next = newChild; + if (child == last) + last = newChild; + child.next = null; + } + + private static string propToString (int propType) + { + if (Token.printTrees) { + // If Context.printTrees is false, the compiler + // can remove all these strings. + switch (propType) { + + case FUNCTION_PROP: + return "function"; + + case LOCAL_PROP: + return "local"; + + case LOCAL_BLOCK_PROP: + return "local_block"; + + case REGEXP_PROP: + return "regexp"; + + case CASEARRAY_PROP: + return "casearray"; + + + case TARGETBLOCK_PROP: + return "targetblock"; + + case VARIABLE_PROP: + return "variable"; + + case ISNUMBER_PROP: + return "isnumber"; + + case DIRECTCALL_PROP: + return "directcall"; + + + case SPECIALCALL_PROP: + return "specialcall"; + + case SKIP_INDEXES_PROP: + return "skip_indexes"; + + case OBJECT_IDS_PROP: + return "object_ids_prop"; + + case INCRDECR_PROP: + return "incrdecr_prop"; + + case CATCH_SCOPE_PROP: + return "catch_scope_prop"; + + case LABEL_ID_PROP: + return "label_id_prop"; + + case MEMBER_TYPE_PROP: + return "member_Type_prop"; + + case NAME_PROP: + return "name_prop"; + + + default: + Context.CodeBug (); + break; + + } + } + return null; + } + + private PropListItem lookupProperty (int propType) + { + PropListItem x = propListHead; + while (x != null && propType != x.Type) { + x = x.next; + } + return x; + } + + private PropListItem ensureProperty (int propType) + { + PropListItem item = lookupProperty (propType); + if (item == null) { + item = new PropListItem (); + item.Type = propType; + item.next = propListHead; + propListHead = item; + } + return item; + } + + public void removeProp (int propType) + { + PropListItem x = propListHead; + if (x != null) { + PropListItem prev = null; + while (x.Type != propType) { + prev = x; + x = x.next; + if (x == null) { + return; + } + } + if (prev == null) { + propListHead = x.next; + } + else { + prev.next = x.next; + } + } + } + + public object getProp (int propType) + { + PropListItem item = lookupProperty (propType); + if (item == null) { + return null; + } + return item.objectValue; + } + + public int getIntProp (int propType, int defaultValue) + { + PropListItem item = lookupProperty (propType); + if (item == null) { + return defaultValue; + } + return item.intValue; + } + + public int getExistingIntProp (int propType) + { + PropListItem item = lookupProperty (propType); + if (item == null) { + Context.CodeBug (); + } + return item.intValue; + } + + public void putProp (int propType, object prop) + { + if (prop == null) { + removeProp (propType); + } + else { + PropListItem item = ensureProperty (propType); + item.objectValue = prop; + } + } + + public void putIntProp (int propType, int prop) + { + PropListItem item = ensureProperty (propType); + item.intValue = prop; + } + + public static Node newTarget () + { + return new Node (Token.TARGET); + } + + public int labelId () + { + if (Type != Token.TARGET) + Context.CodeBug (); + return getIntProp (LABEL_ID_PROP, -1); + } + + public void labelId (int labelId) + { + if (Type != Token.TARGET) + Context.CodeBug (); + putIntProp (LABEL_ID_PROP, labelId); + } + + public override string ToString () + { + if (Token.printTrees) { + System.Text.StringBuilder sb = new System.Text.StringBuilder (); + toString (new ObjToIntMap (), sb); + return sb.ToString (); + } + return Convert.ToString (Type); + } + + private void toString (ObjToIntMap printIds, System.Text.StringBuilder sb) + { + if (Token.printTrees) { + sb.Append (Token.name (this.Type)); + if (this is StringNode) { + sb.Append (' '); + sb.Append (String); + } + else if (this is ScriptOrFnNode) { + ScriptOrFnNode sof = (ScriptOrFnNode)this; + if (this is FunctionNode) { + FunctionNode fn = (FunctionNode)this; + sb.Append (' '); + sb.Append (fn.FunctionName); + } + sb.Append (" [source name: "); + sb.Append (sof.SourceName); + sb.Append ("] [encoded source length: "); + sb.Append (sof.EncodedSourceEnd - sof.EncodedSourceStart); + sb.Append ("] [base line: "); + sb.Append (sof.BaseLineno); + sb.Append ("] [end line: "); + sb.Append (sof.EndLineno); + sb.Append (']'); + } + else if (this is Jump) { + Jump jump = (Jump)this; + if (this.Type == Token.BREAK || this.Type == Token.CONTINUE) { + sb.Append (" [label: "); + appendPrintId (jump.JumpStatement, printIds, sb); + sb.Append (']'); + } + else if (this.Type == Token.TRY) { + Node catchNode = jump.target; + Node finallyTarget = jump.Finally; + if (catchNode != null) { + sb.Append (" [catch: "); + appendPrintId (catchNode, printIds, sb); + sb.Append (']'); + } + if (finallyTarget != null) { + sb.Append (" [finally: "); + appendPrintId (finallyTarget, printIds, sb); + sb.Append (']'); + } + } + else if (this.Type == Token.LABEL || this.Type == Token.LOOP || this.Type == Token.SWITCH) { + sb.Append (" [break: "); + appendPrintId (jump.target, printIds, sb); + sb.Append (']'); + if (this.Type == Token.LOOP) { + sb.Append (" [continue: "); + appendPrintId (jump.Continue, printIds, sb); + sb.Append (']'); + } + } + else { + sb.Append (" [target: "); + appendPrintId (jump.target, printIds, sb); + sb.Append (']'); + } + } + else if (this.Type == Token.NUMBER) { + sb.Append (' '); + sb.Append (Double); + } + else if (this.Type == Token.TARGET) { + sb.Append (' '); + appendPrintId (this, printIds, sb); + } + if (lineno != -1) { + sb.Append (' '); + sb.Append (lineno); + } + + for (PropListItem x = propListHead; x != null; x = x.next) { + int Type = x.Type; + sb.Append (" ["); + sb.Append (propToString (Type)); + sb.Append (": "); + string value; + switch (Type) { + + case TARGETBLOCK_PROP: // can't add this as it recurses + value = "target block property"; + break; + + case LOCAL_BLOCK_PROP: // can't add this as it is dull + value = "last local block"; + break; + + case ISNUMBER_PROP: + switch (x.intValue) { + + case BOTH: + value = "both"; + break; + + case RIGHT: + value = "right"; + break; + + case LEFT: + value = "left"; + break; + + default: + throw Context.CodeBug (); + + } + break; + + case SPECIALCALL_PROP: + switch (x.intValue) { + + case SPECIALCALL_EVAL: + value = "eval"; + break; + + case SPECIALCALL_WITH: + value = "with"; + break; + + default: + // NON_SPECIALCALL should not be stored + throw Context.CodeBug (); + + } + break; + + default: + object obj = x.objectValue; + if (obj != null) { + value = obj.ToString (); + } + else { + value = Convert.ToString (x.intValue); + } + break; + + } + sb.Append (value); + sb.Append (']'); + } + } + } + + public string toStringTree (ScriptOrFnNode treeTop) + { + if (Token.printTrees) { + System.Text.StringBuilder sb = new System.Text.StringBuilder (); + toStringTreeHelper (treeTop, this, null, 0, sb); + return sb.ToString (); + } + return null; + } + + private static void toStringTreeHelper (ScriptOrFnNode treeTop, Node n, ObjToIntMap printIds, int level, System.Text.StringBuilder sb) + { + if (Token.printTrees) { + if (printIds == null) { + printIds = new ObjToIntMap (); + generatePrintIds (treeTop, printIds); + } + for (int i = 0; i != level; ++i) { + sb.Append (" "); + } + n.toString (printIds, sb); + sb.Append ('\n'); + for (Node cursor = n.FirstChild; cursor != null; cursor = cursor.Next) { + if (cursor.Type == Token.FUNCTION) { + int fnIndex = cursor.getExistingIntProp (Node.FUNCTION_PROP); + FunctionNode fn = treeTop.getFunctionNode (fnIndex); + toStringTreeHelper (fn, fn, null, level + 1, sb); + } + else { + toStringTreeHelper (treeTop, cursor, printIds, level + 1, sb); + } + } + } + } + + private static void generatePrintIds (Node n, ObjToIntMap map) + { + if (Token.printTrees) { + map.put (n, map.size ()); + for (Node cursor = n.FirstChild; cursor != null; cursor = cursor.Next) { + generatePrintIds (cursor, map); + } + } + } + + private static void appendPrintId (Node n, ObjToIntMap printIds, System.Text.StringBuilder sb) + { + if (Token.printTrees) { + if (n != null) { + int id = printIds.Get (n, -1); + sb.Append ('#'); + if (id != -1) { + sb.Append (id + 1); + } + else { + sb.Append (""); + } + } + } + } + + internal int Type; // Type of the node; Token.NAME for example + internal Node next; // next sibling + private Node first; // first element of a linked list of children + private Node last; // last element of a linked list of children + private int lineno = -1; // encapsulated int data; depends on Type + + /// Linked list of properties. Since vast majority of nodes would have + /// no more then 2 properties, linked list saves memory and provides + /// fast lookup. If this does not holds, propListHead can be replaced + /// by UintMap. + /// + private PropListItem propListHead; + } } \ No newline at end of file diff --git a/Code/EcmaScript.NET/NodeFactory.cs b/src/EcmaScript.NET/NodeFactory.cs similarity index 97% rename from Code/EcmaScript.NET/NodeFactory.cs rename to src/EcmaScript.NET/NodeFactory.cs index e0efb67..0221f88 100644 --- a/Code/EcmaScript.NET/NodeFactory.cs +++ b/src/EcmaScript.NET/NodeFactory.cs @@ -1,1429 +1,1429 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; - -using EcmaScript.NET.Collections; - -namespace EcmaScript.NET -{ - - /// This class allows the creation of nodes, and follows the Factory pattern. - /// - /// - sealed class NodeFactory - { - internal NodeFactory (Parser parser) - { - this.parser = parser; - } - - internal ScriptOrFnNode CreateScript () - { - return new ScriptOrFnNode (Token.SCRIPT); - } - - /// Script (for associating file/url names with toplevel scripts.) - internal void initScript (ScriptOrFnNode scriptNode, Node body) - { - Node children = body.FirstChild; - if (children != null) { - scriptNode.addChildrenToBack (children); - } - } - - /// Leaf - internal Node CreateLeaf (int nodeType) - { - return new Node (nodeType); - } - - internal Node CreateLeaf (int nodeType, int nodeOp) - { - return new Node (nodeType, nodeOp); - } - - /// Statement leaf nodes. - - internal Node CreateSwitch (Node expr, int lineno) - { - // - // The switch will be rewritten from: - // - // switch (expr) { - // case test1: statements1; - // ... - // default: statementsDefault; - // ... - // case testN: statementsN; - // } - // - // to: - // - // { - // switch (expr) { - // case test1: goto label1; - // ... - // case testN: goto labelN; - // } - // goto labelDefault; - // label1: - // statements1; - // ... - // labelDefault: - // statementsDefault; - // ... - // labelN: - // statementsN; - // breakLabel: - // } - // - // where inside switch each "break;" without label will be replaced - // by "goto breakLabel". - // - // If the original switch does not have the default label, then - // the transformed code would contain after the switch instead of - // goto labelDefault; - // the following goto: - // goto breakLabel; - // - - Node.Jump switchNode = new Node.Jump (Token.SWITCH, expr, lineno); - Node block = new Node (Token.BLOCK, switchNode); - return block; - } - - /// If caseExpression argument is null it indicate default label. - internal void addSwitchCase (Node switchBlock, Node caseExpression, Node statements) - { - if (switchBlock.Type != Token.BLOCK) - throw Context.CodeBug (); - Node.Jump switchNode = (Node.Jump)switchBlock.FirstChild; - if (switchNode.Type != Token.SWITCH) - throw Context.CodeBug (); - - Node gotoTarget = Node.newTarget (); - if (caseExpression != null) { - Node.Jump caseNode = new Node.Jump (Token.CASE, caseExpression); - caseNode.target = gotoTarget; - switchNode.addChildToBack (caseNode); - } - else { - switchNode.Default = gotoTarget; - } - switchBlock.addChildToBack (gotoTarget); - switchBlock.addChildToBack (statements); - } - - internal void closeSwitch (Node switchBlock) - { - if (switchBlock.Type != Token.BLOCK) - throw Context.CodeBug (); - Node.Jump switchNode = (Node.Jump)switchBlock.FirstChild; - if (switchNode.Type != Token.SWITCH) - throw Context.CodeBug (); - - Node switchBreakTarget = Node.newTarget (); - // switchNode.target is only used by NodeTransformer - // to detect switch end - switchNode.target = switchBreakTarget; - - Node defaultTarget = switchNode.Default; - if (defaultTarget == null) { - defaultTarget = switchBreakTarget; - } - - switchBlock.addChildAfter (makeJump (Token.GOTO, defaultTarget), switchNode); - switchBlock.addChildToBack (switchBreakTarget); - } - - internal Node CreateVariables (int lineno) - { - return new Node (Token.VAR, lineno); - } - - internal Node CreateExprStatement (Node expr, int lineno) - { - int type; - if (parser.insideFunction ()) { - type = Token.EXPR_VOID; - } - else { - type = Token.EXPR_RESULT; - } - return new Node (type, expr, lineno); - } - - internal Node CreateExprStatementNoReturn (Node expr, int lineno) - { - return new Node (Token.EXPR_VOID, expr, lineno); - } - - internal Node CreateDefaultNamespace (Node expr, int lineno) - { - // default xml namespace requires activation - setRequiresActivation (); - Node n = CreateUnary (Token.DEFAULTNAMESPACE, expr); - Node result = CreateExprStatement (n, lineno); - return result; - } - - /// Name - internal Node CreateName (string name) - { - checkActivationName (name, Token.NAME); - return Node.newString (Token.NAME, name); - } - - /// String (for literals) - internal Node CreateString (string str) - { - return Node.newString (str); - } - - /// Number (for literals) - internal Node CreateNumber (double number) - { - return Node.newNumber (number); - } - - /// Catch clause of try/catch/finally - /// the name of the variable to bind to the exception - /// - /// the condition under which to catch the exception. - /// May be null if no condition is given. - /// - /// the statements in the catch clause - /// - /// the starting line number of the catch clause - /// - internal Node CreateCatch (string varName, Node catchCond, Node stmts, int lineno) - { - if (catchCond == null) { - catchCond = new Node (Token.EMPTY); - } - return new Node (Token.CATCH, CreateName (varName), catchCond, stmts, lineno); - } - - /// Throw - internal Node CreateThrow (Node expr, int lineno) - { - return new Node (Token.THROW, expr, lineno); - } - - /// Return - internal Node CreateReturn (Node expr, int lineno) - { - return expr == null ? new Node (Token.RETURN, lineno) : new Node (Token.RETURN, expr, lineno); - } - - /// Debugger - internal Node CreateDebugger(int lineno) - { - return new Node(Token.DEBUGGER, lineno); - } - - /// Label - internal Node CreateLabel (int lineno) - { - return new Node.Jump (Token.LABEL, lineno); - } - - internal Node getLabelLoop (Node label) - { - return ((Node.Jump)label).Loop; - } - - /// Label - internal Node CreateLabeledStatement (Node labelArg, Node statement) - { - Node.Jump label = (Node.Jump)labelArg; - - // Make a target and put it _after_ the statement - // node. And in the LABEL node, so breaks get the - // right target. - - Node breakTarget = Node.newTarget (); - Node block = new Node (Token.BLOCK, label, statement, breakTarget); - label.target = breakTarget; - - return block; - } - - /// Break (possibly labeled) - internal Node CreateBreak (Node breakStatement, int lineno) - { - Node.Jump n = new Node.Jump (Token.BREAK, lineno); - Node.Jump jumpStatement; - int t = breakStatement.Type; - if (t == Token.LOOP || t == Token.LABEL) { - jumpStatement = (Node.Jump)breakStatement; - } - else if (t == Token.BLOCK && breakStatement.FirstChild.Type == Token.SWITCH) { - jumpStatement = (Node.Jump)breakStatement.FirstChild; - } - else { - throw Context.CodeBug (); - } - n.JumpStatement = jumpStatement; - return n; - } - - /// Continue (possibly labeled) - internal Node CreateContinue (Node loop, int lineno) - { - if (loop.Type != Token.LOOP) - Context.CodeBug (); - Node.Jump n = new Node.Jump (Token.CONTINUE, lineno); - n.JumpStatement = (Node.Jump)loop; - return n; - } - - /// Statement block - /// Creates the empty statement block - /// Must make subsequent calls to add statements to the node - /// - internal Node CreateBlock (int lineno) - { - return new Node (Token.BLOCK, lineno); - } - - internal FunctionNode CreateFunction (string name) - { - return new FunctionNode (name); - } - - internal Node initFunction (FunctionNode fnNode, int functionIndex, Node statements, int functionType) - { - fnNode.itsFunctionType = functionType; - fnNode.addChildToBack (statements); - - int functionCount = fnNode.FunctionCount; - if (functionCount != 0) { - // Functions containing other functions require activation objects - fnNode.itsNeedsActivation = true; - for (int i = 0; i != functionCount; ++i) { - FunctionNode fn = fnNode.getFunctionNode (i); - // nested function expression statements overrides var - if (fn.FunctionType == FunctionNode.FUNCTION_EXPRESSION_STATEMENT) { - string name = fn.FunctionName; - if (name != null && name.Length != 0) { - fnNode.removeParamOrVar (name); - } - } - } - } - - if (functionType == FunctionNode.FUNCTION_EXPRESSION) { - string name = fnNode.FunctionName; - if (name != null && name.Length != 0 && !fnNode.hasParamOrVar (name)) { - // A function expression needs to have its name as a - // variable (if it isn't already allocated as a variable). - // See ECMA Ch. 13. We add code to the beginning of the - // function to initialize a local variable of the - // function's name to the function value. - fnNode.addVar (name); - Node setFn = new Node (Token.EXPR_VOID, new Node (Token.SETNAME, Node.newString (Token.BINDNAME, name), new Node (Token.THISFN))); - statements.addChildrenToFront (setFn); - } - } - - // Add return to end if needed. - Node lastStmt = statements.LastChild; - if (lastStmt == null || lastStmt.Type != Token.RETURN) { - statements.addChildToBack (new Node (Token.RETURN)); - } - - Node result = Node.newString (Token.FUNCTION, fnNode.FunctionName); - result.putIntProp (Node.FUNCTION_PROP, functionIndex); - return result; - } - - /// Add a child to the back of the given node. This function - /// breaks the Factory abstraction, but it removes a requirement - /// from implementors of Node. - /// - internal void addChildToBack (Node parent, Node child) - { - parent.addChildToBack (child); - } - - /// Create loop node. The parser will later call - /// CreateWhile|CreateDoWhile|CreateFor|CreateForIn - /// to finish loop generation. - /// - internal Node CreateLoopNode (Node loopLabel, int lineno) - { - Node.Jump result = new Node.Jump (Token.LOOP, lineno); - if (loopLabel != null) { - ((Node.Jump)loopLabel).Loop = result; - } - return result; - } - - /// While - internal Node CreateWhile (Node loop, Node cond, Node body) - { - return CreateLoop ((Node.Jump)loop, LOOP_WHILE, body, cond, null, null); - } - - /// DoWhile - internal Node CreateDoWhile (Node loop, Node body, Node cond) - { - return CreateLoop ((Node.Jump)loop, LOOP_DO_WHILE, body, cond, null, null); - } - - /// For - internal Node CreateFor (Node loop, Node init, Node test, Node incr, Node body) - { - return CreateLoop ((Node.Jump)loop, LOOP_FOR, body, test, init, incr); - } - - private Node CreateLoop (Node.Jump loop, int loopType, Node body, Node cond, Node init, Node incr) - { - Node bodyTarget = Node.newTarget (); - Node condTarget = Node.newTarget (); - if (loopType == LOOP_FOR && cond.Type == Token.EMPTY) { - cond = new Node (Token.TRUE); - } - Node.Jump IFEQ = new Node.Jump (Token.IFEQ, cond); - IFEQ.target = bodyTarget; - Node breakTarget = Node.newTarget (); - - loop.addChildToBack (bodyTarget); - loop.addChildrenToBack (body); - if (loopType == LOOP_WHILE || loopType == LOOP_FOR) { - // propagate lineno to condition - loop.addChildrenToBack (new Node (Token.EMPTY, loop.Lineno)); - } - loop.addChildToBack (condTarget); - loop.addChildToBack (IFEQ); - loop.addChildToBack (breakTarget); - - loop.target = breakTarget; - Node continueTarget = condTarget; - - if (loopType == LOOP_WHILE || loopType == LOOP_FOR) { - // Just add a GOTO to the condition in the do..while - loop.addChildToFront (makeJump (Token.GOTO, condTarget)); - - if (loopType == LOOP_FOR) { - if (init.Type != Token.EMPTY) { - if (init.Type != Token.VAR) { - init = new Node (Token.EXPR_VOID, init); - } - loop.addChildToFront (init); - } - Node incrTarget = Node.newTarget (); - loop.addChildAfter (incrTarget, body); - if (incr.Type != Token.EMPTY) { - incr = new Node (Token.EXPR_VOID, incr); - loop.addChildAfter (incr, incrTarget); - } - continueTarget = incrTarget; - } - } - - loop.Continue = continueTarget; - - return loop; - } - - /// For .. In - /// - /// - internal Node CreateForIn (Node loop, Node lhs, Node obj, Node body, bool isForEach) - { - int type = lhs.Type; - - Node lvalue; - if (type == Token.VAR) { - /* - * check that there was only one variable given. - * we can't do this in the parser, because then the - * parser would have to know something about the - * 'init' node of the for-in loop. - */ - Node lastChild = lhs.LastChild; - if (lhs.FirstChild != lastChild) { - parser.ReportError ("msg.mult.index"); - } - lvalue = Node.newString (Token.NAME, lastChild.String); - } - else { - lvalue = makeReference (lhs); - if (lvalue == null) { - parser.ReportError ("msg.bad.for.in.lhs"); - return obj; - } - } - - Node localBlock = new Node (Token.LOCAL_BLOCK); - - int initType = (isForEach) ? Token.ENUM_INIT_VALUES : Token.ENUM_INIT_KEYS; - Node init = new Node (initType, obj); - init.putProp (Node.LOCAL_BLOCK_PROP, localBlock); - Node cond = new Node (Token.ENUM_NEXT); - cond.putProp (Node.LOCAL_BLOCK_PROP, localBlock); - Node id = new Node (Token.ENUM_ID); - id.putProp (Node.LOCAL_BLOCK_PROP, localBlock); - - Node newBody = new Node (Token.BLOCK); - Node assign = simpleAssignment (lvalue, id); - newBody.addChildToBack (new Node (Token.EXPR_VOID, assign)); - newBody.addChildToBack (body); - - loop = CreateWhile (loop, cond, newBody); - loop.addChildToFront (init); - if (type == Token.VAR) - loop.addChildToFront (lhs); - localBlock.addChildToBack (loop); - - return localBlock; - } - - /// Try/Catch/Finally - /// - /// The IRFactory tries to express as much as possible in the tree; - /// the responsibilities remaining for Codegen are to add the Java - /// handlers: (Either (but not both) of TARGET and FINALLY might not - /// be defined) - /// - a catch handler for javascript exceptions that unwraps the - /// exception onto the stack and GOTOes to the catch target - /// - a finally handler - /// ... and a goto to GOTO around these handlers. - /// - internal Node CreateTryCatchFinally (Node tryBlock, Node catchBlocks, Node finallyBlock, int lineno) - { - bool hasFinally = (finallyBlock != null) && (finallyBlock.Type != Token.BLOCK || finallyBlock.hasChildren ()); - - // short circuit - if (tryBlock.Type == Token.BLOCK && !tryBlock.hasChildren () && !hasFinally) { - return tryBlock; - } - - bool hasCatch = catchBlocks.hasChildren (); - - // short circuit - if (!hasFinally && !hasCatch) { - // bc finally might be an empty block... - return tryBlock; - } - - - Node handlerBlock = new Node (Token.LOCAL_BLOCK); - Node.Jump pn = new Node.Jump (Token.TRY, tryBlock, lineno); - pn.putProp (Node.LOCAL_BLOCK_PROP, handlerBlock); - - if (hasCatch) { - // jump around catch code - Node endCatch = Node.newTarget (); - pn.addChildToBack (makeJump (Token.GOTO, endCatch)); - - // make a TARGET for the catch that the tcf node knows about - Node catchTarget = Node.newTarget (); - pn.target = catchTarget; - // mark it - pn.addChildToBack (catchTarget); - - // - // Given - // - // try { - // tryBlock; - // } catch (e if condition1) { - // something1; - // ... - // - // } catch (e if conditionN) { - // somethingN; - // } catch (e) { - // somethingDefault; - // } - // - // rewrite as - // - // try { - // tryBlock; - // goto after_catch: - // } catch (x) { - // with (newCatchScope(e, x)) { - // if (condition1) { - // something1; - // goto after_catch; - // } - // } - // ... - // with (newCatchScope(e, x)) { - // if (conditionN) { - // somethingN; - // goto after_catch; - // } - // } - // with (newCatchScope(e, x)) { - // somethingDefault; - // goto after_catch; - // } - // } - // after_catch: - // - // If there is no default catch, then the last with block - // arround "somethingDefault;" is replaced by "rethrow;" - - // It is assumed that catch handler generation will store - // exeception object in handlerBlock register - - // Block with local for exception scope objects - Node catchScopeBlock = new Node (Token.LOCAL_BLOCK); - - // expects catchblocks children to be (cond block) pairs. - Node cb = catchBlocks.FirstChild; - bool hasDefault = false; - int scopeIndex = 0; - while (cb != null) { - int catchLineNo = cb.Lineno; - - Node name = cb.FirstChild; - Node cond = name.Next; - Node catchStatement = cond.Next; - cb.removeChild (name); - cb.removeChild (cond); - cb.removeChild (catchStatement); - - // Add goto to the catch statement to jump out of catch - // but prefix it with LEAVEWITH since try..catch produces - // "with"code in order to limit the scope of the exception - // object. - catchStatement.addChildToBack (new Node (Token.LEAVEWITH)); - catchStatement.addChildToBack (makeJump (Token.GOTO, endCatch)); - - // Create condition "if" when present - Node condStmt; - if (cond.Type == Token.EMPTY) { - condStmt = catchStatement; - hasDefault = true; - } - else { - condStmt = CreateIf (cond, catchStatement, null, catchLineNo); - } - - // Generate code to Create the scope object and store - // it in catchScopeBlock register - Node catchScope = new Node (Token.CATCH_SCOPE, name, CreateUseLocal (handlerBlock)); - catchScope.putProp (Node.LOCAL_BLOCK_PROP, catchScopeBlock); - catchScope.putIntProp (Node.CATCH_SCOPE_PROP, scopeIndex); - catchScopeBlock.addChildToBack (catchScope); - - // Add with statement based on catch scope object - catchScopeBlock.addChildToBack (CreateWith (CreateUseLocal (catchScopeBlock), condStmt, catchLineNo)); - - // move to next cb - cb = cb.Next; - ++scopeIndex; - } - pn.addChildToBack (catchScopeBlock); - if (!hasDefault) { - // Generate code to rethrow if no catch clause was executed - Node rethrow = new Node (Token.RETHROW); - rethrow.putProp (Node.LOCAL_BLOCK_PROP, handlerBlock); - pn.addChildToBack (rethrow); - } - - pn.addChildToBack (endCatch); - } - - if (hasFinally) { - Node finallyTarget = Node.newTarget (); - pn.Finally = finallyTarget; - - // add jsr finally to the try block - pn.addChildToBack (makeJump (Token.JSR, finallyTarget)); - - // jump around finally code - Node finallyEnd = Node.newTarget (); - pn.addChildToBack (makeJump (Token.GOTO, finallyEnd)); - - pn.addChildToBack (finallyTarget); - Node fBlock = new Node (Token.FINALLY, finallyBlock); - fBlock.putProp (Node.LOCAL_BLOCK_PROP, handlerBlock); - pn.addChildToBack (fBlock); - - pn.addChildToBack (finallyEnd); - } - handlerBlock.addChildToBack (pn); - return handlerBlock; - } - - /// Throw, Return, Label, Break and Continue are defined in ASTFactory. - - /// With - internal Node CreateWith (Node obj, Node body, int lineno) - { - setRequiresActivation (); - Node result = new Node (Token.BLOCK, lineno); - result.addChildToBack (new Node (Token.ENTERWITH, obj)); - Node bodyNode = new Node (Token.WITH, body, lineno); - result.addChildrenToBack (bodyNode); - result.addChildToBack (new Node (Token.LEAVEWITH)); - return result; - } - - /// DOTQUERY - public Node CreateDotQuery (Node obj, Node body, int lineno) - { - setRequiresActivation (); - Node result = new Node (Token.DOTQUERY, obj, body, lineno); - return result; - } - - internal Node CreateArrayLiteral (ObjArray elems, int skipCount) - { - int length = elems.size (); - int [] skipIndexes = null; - if (skipCount != 0) { - skipIndexes = new int [skipCount]; - } - Node array = new Node (Token.ARRAYLIT); - for (int i = 0, j = 0; i != length; ++i) { - Node elem = (Node)elems.Get (i); - if (elem != null) { - array.addChildToBack (elem); - } - else { - skipIndexes [j] = i; - ++j; - } - } - if (skipCount != 0) { - array.putProp (Node.SKIP_INDEXES_PROP, skipIndexes); - } - return array; - } - - /// Object Literals - ///
CreateObjectLiteral rewrites its argument as object - /// creation plus object property entries, so later compiler - /// stages don't need to know about object literals. - ///
- internal Node CreateObjectLiteral (ObjArray elems) - { - int size = elems.size () / 2; - Node obj = new Node (Token.OBJECTLIT); - object [] properties; - if (size == 0) { - properties = ScriptRuntime.EmptyArgs; - } - else { - properties = new object [size]; - for (int i = 0; i != size; ++i) { - properties [i] = elems.Get (2 * i); - Node value = (Node)elems.Get (2 * i + 1); - obj.addChildToBack (value); - } - } - obj.putProp (Node.OBJECT_IDS_PROP, properties); - return obj; - } - - /// Regular expressions - internal Node CreateRegExp (int regexpIndex) - { - Node n = new Node (Token.REGEXP); - n.putIntProp (Node.REGEXP_PROP, regexpIndex); - return n; - } - - /// If statement - internal Node CreateIf (Node cond, Node ifTrue, Node ifFalse, int lineno) - { - int condStatus = isAlwaysDefinedBoolean (cond); - if (condStatus == ALWAYS_TRUE_BOOLEAN) { - return ifTrue; - } - else if (condStatus == ALWAYS_FALSE_BOOLEAN) { - if (ifFalse != null) { - return ifFalse; - } - return new Node (Token.BLOCK, lineno); - } - - Node result = new Node (Token.BLOCK, lineno); - Node ifNotTarget = Node.newTarget (); - Node.Jump IFNE = new Node.Jump (Token.IFNE, cond); - IFNE.target = ifNotTarget; - - result.addChildToBack (IFNE); - result.addChildrenToBack (ifTrue); - - if (ifFalse != null) { - Node endTarget = Node.newTarget (); - result.addChildToBack (makeJump (Token.GOTO, endTarget)); - result.addChildToBack (ifNotTarget); - result.addChildrenToBack (ifFalse); - result.addChildToBack (endTarget); - } - else { - result.addChildToBack (ifNotTarget); - } - - return result; - } - - internal Node CreateCondExpr (Node cond, Node ifTrue, Node ifFalse) - { - int condStatus = isAlwaysDefinedBoolean (cond); - if (condStatus == ALWAYS_TRUE_BOOLEAN) { - return ifTrue; - } - else if (condStatus == ALWAYS_FALSE_BOOLEAN) { - return ifFalse; - } - return new Node (Token.HOOK, cond, ifTrue, ifFalse); - } - - /// Unary - internal Node CreateUnary (int nodeType, Node child) - { - int childType = child.Type; - switch (nodeType) { - - case Token.DELPROP: { - Node n; - if (childType == Token.NAME) { - // Transform Delete(Name "a") - // to Delete(Bind("a"), String("a")) - child.Type = Token.BINDNAME; - Node left = child; - Node right = Node.newString (child.String); - n = new Node (nodeType, left, right); - } - else if (childType == Token.GETPROP || childType == Token.GETELEM) { - Node left = child.FirstChild; - Node right = child.LastChild; - child.removeChild (left); - child.removeChild (right); - n = new Node (nodeType, left, right); - } - else if (childType == Token.GET_REF) { - Node rf = child.FirstChild; - child.removeChild (rf); - n = new Node (Token.DEL_REF, rf); - } - else { - n = new Node (Token.TRUE); - } - return n; - } - - case Token.TYPEOF: - if (childType == Token.NAME) { - child.Type = Token.TYPEOFNAME; - return child; - } - break; - - case Token.BITNOT: - if (childType == Token.NUMBER) { - int value = ScriptConvert.ToInt32 (child.Double); - child.Double = ~value; - return child; - } - break; - - case Token.NEG: - if (childType == Token.NUMBER) { - child.Double = -child.Double; - return child; - } - break; - - case Token.NOT: { - int status = isAlwaysDefinedBoolean (child); - if (status != 0) { - int type; - if (status == ALWAYS_TRUE_BOOLEAN) { - type = Token.FALSE; - } - else { - type = Token.TRUE; - } - if (childType == Token.TRUE || childType == Token.FALSE) { - child.Type = type; - return child; - } - return new Node (type); - } - break; - } - } - return new Node (nodeType, child); - } - - internal Node CreateCallOrNew (int nodeType, Node child) - { - int type = Node.NON_SPECIALCALL; - if (child.Type == Token.NAME) { - string name = child.String; - if (name.Equals ("eval")) { - type = Node.SPECIALCALL_EVAL; - } - else if (name.Equals ("With")) { - type = Node.SPECIALCALL_WITH; - } - } - else if (child.Type == Token.GETPROP) { - string name = child.LastChild.String; - if (name.Equals ("eval")) { - type = Node.SPECIALCALL_EVAL; - } - } - Node node = new Node (nodeType, child); - if (type != Node.NON_SPECIALCALL) { - // Calls to these functions require activation objects. - setRequiresActivation (); - node.putIntProp (Node.SPECIALCALL_PROP, type); - } - return node; - } - - internal Node CreateIncDec (int nodeType, bool post, Node child) - { - child = makeReference (child); - if (child == null) { - string msg; - if (nodeType == Token.DEC) { - msg = "msg.bad.decr"; - } - else { - msg = "msg.bad.incr"; - } - parser.ReportError (msg); - return null; - } - - int childType = child.Type; - - switch (childType) { - - case Token.NAME: - case Token.GETPROP: - case Token.GETELEM: - case Token.GET_REF: { - Node n = new Node (nodeType, child); - int incrDecrMask = 0; - if (nodeType == Token.DEC) { - incrDecrMask |= Node.DECR_FLAG; - } - if (post) { - incrDecrMask |= Node.POST_FLAG; - } - n.putIntProp (Node.INCRDECR_PROP, incrDecrMask); - return n; - } - } - throw Context.CodeBug (); - } - - internal Node CreatePropertyGet (Node target, string ns, string name, int memberTypeFlags) - { - if (ns == null && memberTypeFlags == 0) { - if (target == null) { - return CreateName (name); - } - checkActivationName (name, Token.GETPROP); - if (ScriptRuntime.isSpecialProperty (name)) { - Node rf = new Node (Token.REF_SPECIAL, target); - rf.putProp (Node.NAME_PROP, name); - return new Node (Token.GET_REF, rf); - } - return new Node (Token.GETPROP, target, CreateString (name)); - } - Node elem = CreateString (name); - memberTypeFlags |= Node.PROPERTY_FLAG; - return CreateMemberRefGet (target, ns, elem, memberTypeFlags); - } - - internal Node CreateElementGet (Node target, string ns, Node elem, int memberTypeFlags) - { - // OPT: could optimize to CreatePropertyGet - // iff elem is string that can not be number - if (ns == null && memberTypeFlags == 0) { - // stand-alone [aaa] as primary expression is array literal - // declaration and should not come here! - if (target == null) - throw Context.CodeBug (); - return new Node (Token.GETELEM, target, elem); - } - return CreateMemberRefGet (target, ns, elem, memberTypeFlags); - } - - private Node CreateMemberRefGet (Node target, string ns, Node elem, int memberTypeFlags) - { - Node nsNode = null; - if (ns != null) { - // See 11.1.2 in ECMA 357 - if (ns.Equals ("*")) { - nsNode = new Node (Token.NULL); - } - else { - nsNode = CreateName (ns); - } - } - Node rf; - if (target == null) { - if (ns == null) { - rf = new Node (Token.REF_NAME, elem); - } - else { - rf = new Node (Token.REF_NS_NAME, nsNode, elem); - } - } - else { - if (ns == null) { - rf = new Node (Token.REF_MEMBER, target, elem); - } - else { - rf = new Node (Token.REF_NS_MEMBER, target, nsNode, elem); - } - } - if (memberTypeFlags != 0) { - rf.putIntProp (Node.MEMBER_TYPE_PROP, memberTypeFlags); - } - return new Node (Token.GET_REF, rf); - } - - /// Binary - internal Node CreateBinary (int nodeType, Node left, Node right) - { - switch (nodeType) { - - - case Token.ADD: - // numerical addition and string concatenation - if (left.Type == Token.STRING) { - string s2; - if (right.Type == Token.STRING) { - s2 = right.String; - } - else if (right.Type == Token.NUMBER) { - s2 = ScriptConvert.ToString (right.Double, 10); - } - else { - break; - } - string s1 = left.String; - left.String = string.Concat (s1, s2); - return left; - } - else if (left.Type == Token.NUMBER) { - if (right.Type == Token.NUMBER) { - left.Double = left.Double + right.Double; - return left; - } - else if (right.Type == Token.STRING) { - string s1, s2; - s1 = ScriptConvert.ToString (left.Double, 10); - s2 = right.String; - right.String = string.Concat (s1, s2); - return right; - } - } - // can't do anything if we don't know both types - since - // 0 + object is supposed to call toString on the object and do - // string concantenation rather than addition - break; - - - case Token.SUB: - // numerical subtraction - if (left.Type == Token.NUMBER) { - double ld = left.Double; - if (right.Type == Token.NUMBER) { - //both numbers - left.Double = ld - right.Double; - return left; - } - else if (ld == 0.0) { - // first 0: 0-x -> -x - return new Node (Token.NEG, right); - } - } - else if (right.Type == Token.NUMBER) { - if (right.Double == 0.0) { - //second 0: x - 0 -> +x - // can not make simply x because x - 0 must be number - return new Node (Token.POS, left); - } - } - break; - - - case Token.MUL: - // numerical multiplication - if (left.Type == Token.NUMBER) { - double ld = left.Double; - if (right.Type == Token.NUMBER) { - //both numbers - left.Double = ld * right.Double; - return left; - } - else if (ld == 1.0) { - // first 1: 1 * x -> +x - return new Node (Token.POS, right); - } - } - else if (right.Type == Token.NUMBER) { - if (right.Double == 1.0) { - //second 1: x * 1 -> +x - // can not make simply x because x - 0 must be number - return new Node (Token.POS, left); - } - } - // can't do x*0: Infinity * 0 gives NaN, not 0 - break; - - - case Token.DIV: - // number division - if (right.Type == Token.NUMBER) { - double rd = right.Double; - if (left.Type == Token.NUMBER) { - // both constants -- just divide, trust Java to handle x/0 - left.Double = left.Double / rd; - return left; - } - else if (rd == 1.0) { - // second 1: x/1 -> +x - // not simply x to force number convertion - return new Node (Token.POS, left); - } - } - break; - - - case Token.AND: { - int leftStatus = isAlwaysDefinedBoolean (left); - if (leftStatus == ALWAYS_FALSE_BOOLEAN) { - // if the first one is false, replace with FALSE - return new Node (Token.FALSE); - } - else if (leftStatus == ALWAYS_TRUE_BOOLEAN) { - // if first is true, set to second - return right; - } - int rightStatus = isAlwaysDefinedBoolean (right); - if (rightStatus == ALWAYS_FALSE_BOOLEAN) { - // if the second one is false, replace with FALSE - if (!hasSideEffects (left)) { - return new Node (Token.FALSE); - } - } - else if (rightStatus == ALWAYS_TRUE_BOOLEAN) { - // if second is true, set to first - return left; - } - break; - } - - - case Token.OR: { - int leftStatus = isAlwaysDefinedBoolean (left); - if (leftStatus == ALWAYS_TRUE_BOOLEAN) { - // if the first one is true, replace with TRUE - return new Node (Token.TRUE); - } - else if (leftStatus == ALWAYS_FALSE_BOOLEAN) { - // if first is false, set to second - return right; - } - int rightStatus = isAlwaysDefinedBoolean (right); - if (rightStatus == ALWAYS_TRUE_BOOLEAN) { - // if the second one is true, replace with TRUE - if (!hasSideEffects (left)) { - return new Node (Token.TRUE); - } - } - else if (rightStatus == ALWAYS_FALSE_BOOLEAN) { - // if second is false, set to first - return left; - } - break; - } - } - - return new Node (nodeType, left, right); - } - - private Node simpleAssignment (Node left, Node right) - { - int nodeType = left.Type; - switch (nodeType) { - - case Token.NAME: - left.Type = Token.BINDNAME; - return new Node (Token.SETNAME, left, right); - - - case Token.GETPROP: - case Token.GETELEM: { - Node obj = left.FirstChild; - Node id = left.LastChild; - int type; - if (nodeType == Token.GETPROP) { - type = Token.SETPROP; - } - else { - type = Token.SETELEM; - } - return new Node (type, obj, id, right); - } - - case Token.GET_REF: { - Node rf = left.FirstChild; - checkMutableReference (rf); - return new Node (Token.SET_REF, rf, right); - } - } - - throw Context.CodeBug (); - } - - private void checkMutableReference (Node n) - { - int memberTypeFlags = n.getIntProp (Node.MEMBER_TYPE_PROP, 0); - if ((memberTypeFlags & Node.DESCENDANTS_FLAG) != 0) { - parser.ReportError ("msg.bad.assign.left"); - } - } - - internal Node CreateAssignment (int assignType, Node left, Node right) - { - left = makeReference (left); - if (left == null) { - parser.ReportError ("msg.bad.assign.left"); - return right; - } - - int assignOp; - switch (assignType) { - - case Token.ASSIGN: - return simpleAssignment (left, right); - - case Token.ASSIGN_BITOR: - assignOp = Token.BITOR; - break; - - case Token.ASSIGN_BITXOR: - assignOp = Token.BITXOR; - break; - - case Token.ASSIGN_BITAND: - assignOp = Token.BITAND; - break; - - case Token.ASSIGN_LSH: - assignOp = Token.LSH; - break; - - case Token.ASSIGN_RSH: - assignOp = Token.RSH; - break; - - case Token.ASSIGN_URSH: - assignOp = Token.URSH; - break; - - case Token.ASSIGN_ADD: - assignOp = Token.ADD; - break; - - case Token.ASSIGN_SUB: - assignOp = Token.SUB; - break; - - case Token.ASSIGN_MUL: - assignOp = Token.MUL; - break; - - case Token.ASSIGN_DIV: - assignOp = Token.DIV; - break; - - case Token.ASSIGN_MOD: - assignOp = Token.MOD; - break; - - default: - throw Context.CodeBug (); - - } - - int nodeType = left.Type; - switch (nodeType) { - - case Token.NAME: { - string s = left.String; - - Node opLeft = Node.newString (Token.NAME, s); - Node op = new Node (assignOp, opLeft, right); - Node lvalueLeft = Node.newString (Token.BINDNAME, s); - return new Node (Token.SETNAME, lvalueLeft, op); - } - - case Token.GETPROP: - case Token.GETELEM: { - Node obj = left.FirstChild; - Node id = left.LastChild; - - int type = nodeType == Token.GETPROP ? Token.SETPROP_OP : Token.SETELEM_OP; - - Node opLeft = new Node (Token.USE_STACK); - Node op = new Node (assignOp, opLeft, right); - return new Node (type, obj, id, op); - } - - case Token.GET_REF: { - Node rf = left.FirstChild; - checkMutableReference (rf); - Node opLeft = new Node (Token.USE_STACK); - Node op = new Node (assignOp, opLeft, right); - return new Node (Token.SET_REF_OP, rf, op); - } - } - - throw Context.CodeBug (); - } - - internal Node CreateUseLocal (Node localBlock) - { - if (Token.LOCAL_BLOCK != localBlock.Type) - throw Context.CodeBug (); - Node result = new Node (Token.LOCAL_LOAD); - result.putProp (Node.LOCAL_BLOCK_PROP, localBlock); - return result; - } - - private Node.Jump makeJump (int type, Node target) - { - Node.Jump n = new Node.Jump (type); - n.target = target; - return n; - } - - private Node makeReference (Node node) - { - int type = node.Type; - switch (type) { - - case Token.NAME: - case Token.GETPROP: - case Token.GETELEM: - case Token.GET_REF: - return node; - - case Token.CALL: - node.Type = Token.REF_CALL; - return new Node (Token.GET_REF, node); - } - // Signal caller to report error - return null; - } - - // Check if Node always mean true or false in boolean context - private static int isAlwaysDefinedBoolean (Node node) - { - switch (node.Type) { - - case Token.FALSE: - case Token.NULL: - return ALWAYS_FALSE_BOOLEAN; - - case Token.TRUE: - return ALWAYS_TRUE_BOOLEAN; - - case Token.NUMBER: { - double num = node.Double; - if (!double.IsNaN (num) && num != 0.0) { - return ALWAYS_TRUE_BOOLEAN; - } - else { - return ALWAYS_FALSE_BOOLEAN; - } - } - } - return 0; - } - - private static bool hasSideEffects (Node exprTree) - { - switch (exprTree.Type) { - - case Token.INC: - case Token.DEC: - case Token.SETPROP: - case Token.SETELEM: - case Token.SETNAME: - case Token.CALL: - case Token.NEW: - return true; - - default: - Node child = exprTree.FirstChild; - while (child != null) { - if (hasSideEffects (child)) - return true; - child = child.Next; - } - break; - - } - return false; - } - - private void checkActivationName (string name, int token) - { - if (parser.insideFunction ()) { - bool activation = false; - if ("arguments".Equals (name) || (parser.compilerEnv.activationNames != null && parser.compilerEnv.activationNames.ContainsKey (name))) { - activation = true; - } - else if ("length".Equals (name)) { - if (token == Token.GETPROP && parser.compilerEnv.LanguageVersion == Context.Versions.JS1_2) { - // Use of "length" in 1.2 requires an activation object. - activation = true; - } - } - if (activation) { - setRequiresActivation (); - } - } - } - - private void setRequiresActivation () - { - if (parser.insideFunction ()) { - ((FunctionNode)parser.currentScriptOrFn).itsNeedsActivation = true; - } - } - - private Parser parser; - - private const int LOOP_DO_WHILE = 0; - private const int LOOP_WHILE = 1; - private const int LOOP_FOR = 2; - - private const int ALWAYS_TRUE_BOOLEAN = 1; - private const int ALWAYS_FALSE_BOOLEAN = -1; - } +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; + +using EcmaScript.NET.Collections; + +namespace EcmaScript.NET +{ + + /// This class allows the creation of nodes, and follows the Factory pattern. + /// + /// + sealed class NodeFactory + { + internal NodeFactory (Parser parser) + { + this.parser = parser; + } + + internal ScriptOrFnNode CreateScript () + { + return new ScriptOrFnNode (Token.SCRIPT); + } + + /// Script (for associating file/url names with toplevel scripts.) + internal void initScript (ScriptOrFnNode scriptNode, Node body) + { + Node children = body.FirstChild; + if (children != null) { + scriptNode.addChildrenToBack (children); + } + } + + /// Leaf + internal Node CreateLeaf (int nodeType) + { + return new Node (nodeType); + } + + internal Node CreateLeaf (int nodeType, int nodeOp) + { + return new Node (nodeType, nodeOp); + } + + /// Statement leaf nodes. + + internal Node CreateSwitch (Node expr, int lineno) + { + // + // The switch will be rewritten from: + // + // switch (expr) { + // case test1: statements1; + // ... + // default: statementsDefault; + // ... + // case testN: statementsN; + // } + // + // to: + // + // { + // switch (expr) { + // case test1: goto label1; + // ... + // case testN: goto labelN; + // } + // goto labelDefault; + // label1: + // statements1; + // ... + // labelDefault: + // statementsDefault; + // ... + // labelN: + // statementsN; + // breakLabel: + // } + // + // where inside switch each "break;" without label will be replaced + // by "goto breakLabel". + // + // If the original switch does not have the default label, then + // the transformed code would contain after the switch instead of + // goto labelDefault; + // the following goto: + // goto breakLabel; + // + + Node.Jump switchNode = new Node.Jump (Token.SWITCH, expr, lineno); + Node block = new Node (Token.BLOCK, switchNode); + return block; + } + + /// If caseExpression argument is null it indicate default label. + internal void addSwitchCase (Node switchBlock, Node caseExpression, Node statements) + { + if (switchBlock.Type != Token.BLOCK) + throw Context.CodeBug (); + Node.Jump switchNode = (Node.Jump)switchBlock.FirstChild; + if (switchNode.Type != Token.SWITCH) + throw Context.CodeBug (); + + Node gotoTarget = Node.newTarget (); + if (caseExpression != null) { + Node.Jump caseNode = new Node.Jump (Token.CASE, caseExpression); + caseNode.target = gotoTarget; + switchNode.addChildToBack (caseNode); + } + else { + switchNode.Default = gotoTarget; + } + switchBlock.addChildToBack (gotoTarget); + switchBlock.addChildToBack (statements); + } + + internal void closeSwitch (Node switchBlock) + { + if (switchBlock.Type != Token.BLOCK) + throw Context.CodeBug (); + Node.Jump switchNode = (Node.Jump)switchBlock.FirstChild; + if (switchNode.Type != Token.SWITCH) + throw Context.CodeBug (); + + Node switchBreakTarget = Node.newTarget (); + // switchNode.target is only used by NodeTransformer + // to detect switch end + switchNode.target = switchBreakTarget; + + Node defaultTarget = switchNode.Default; + if (defaultTarget == null) { + defaultTarget = switchBreakTarget; + } + + switchBlock.addChildAfter (makeJump (Token.GOTO, defaultTarget), switchNode); + switchBlock.addChildToBack (switchBreakTarget); + } + + internal Node CreateVariables (int lineno) + { + return new Node (Token.VAR, lineno); + } + + internal Node CreateExprStatement (Node expr, int lineno) + { + int type; + if (parser.insideFunction ()) { + type = Token.EXPR_VOID; + } + else { + type = Token.EXPR_RESULT; + } + return new Node (type, expr, lineno); + } + + internal Node CreateExprStatementNoReturn (Node expr, int lineno) + { + return new Node (Token.EXPR_VOID, expr, lineno); + } + + internal Node CreateDefaultNamespace (Node expr, int lineno) + { + // default xml namespace requires activation + setRequiresActivation (); + Node n = CreateUnary (Token.DEFAULTNAMESPACE, expr); + Node result = CreateExprStatement (n, lineno); + return result; + } + + /// Name + internal Node CreateName (string name) + { + checkActivationName (name, Token.NAME); + return Node.newString (Token.NAME, name); + } + + /// String (for literals) + internal Node CreateString (string str) + { + return Node.newString (str); + } + + /// Number (for literals) + internal Node CreateNumber (double number) + { + return Node.newNumber (number); + } + + /// Catch clause of try/catch/finally + /// the name of the variable to bind to the exception + /// + /// the condition under which to catch the exception. + /// May be null if no condition is given. + /// + /// the statements in the catch clause + /// + /// the starting line number of the catch clause + /// + internal Node CreateCatch (string varName, Node catchCond, Node stmts, int lineno) + { + if (catchCond == null) { + catchCond = new Node (Token.EMPTY); + } + return new Node (Token.CATCH, CreateName (varName), catchCond, stmts, lineno); + } + + /// Throw + internal Node CreateThrow (Node expr, int lineno) + { + return new Node (Token.THROW, expr, lineno); + } + + /// Return + internal Node CreateReturn (Node expr, int lineno) + { + return expr == null ? new Node (Token.RETURN, lineno) : new Node (Token.RETURN, expr, lineno); + } + + /// Debugger + internal Node CreateDebugger(int lineno) + { + return new Node(Token.DEBUGGER, lineno); + } + + /// Label + internal Node CreateLabel (int lineno) + { + return new Node.Jump (Token.LABEL, lineno); + } + + internal Node getLabelLoop (Node label) + { + return ((Node.Jump)label).Loop; + } + + /// Label + internal Node CreateLabeledStatement (Node labelArg, Node statement) + { + Node.Jump label = (Node.Jump)labelArg; + + // Make a target and put it _after_ the statement + // node. And in the LABEL node, so breaks get the + // right target. + + Node breakTarget = Node.newTarget (); + Node block = new Node (Token.BLOCK, label, statement, breakTarget); + label.target = breakTarget; + + return block; + } + + /// Break (possibly labeled) + internal Node CreateBreak (Node breakStatement, int lineno) + { + Node.Jump n = new Node.Jump (Token.BREAK, lineno); + Node.Jump jumpStatement; + int t = breakStatement.Type; + if (t == Token.LOOP || t == Token.LABEL) { + jumpStatement = (Node.Jump)breakStatement; + } + else if (t == Token.BLOCK && breakStatement.FirstChild.Type == Token.SWITCH) { + jumpStatement = (Node.Jump)breakStatement.FirstChild; + } + else { + throw Context.CodeBug (); + } + n.JumpStatement = jumpStatement; + return n; + } + + /// Continue (possibly labeled) + internal Node CreateContinue (Node loop, int lineno) + { + if (loop.Type != Token.LOOP) + Context.CodeBug (); + Node.Jump n = new Node.Jump (Token.CONTINUE, lineno); + n.JumpStatement = (Node.Jump)loop; + return n; + } + + /// Statement block + /// Creates the empty statement block + /// Must make subsequent calls to add statements to the node + /// + internal Node CreateBlock (int lineno) + { + return new Node (Token.BLOCK, lineno); + } + + internal FunctionNode CreateFunction (string name) + { + return new FunctionNode (name); + } + + internal Node initFunction (FunctionNode fnNode, int functionIndex, Node statements, int functionType) + { + fnNode.itsFunctionType = functionType; + fnNode.addChildToBack (statements); + + int functionCount = fnNode.FunctionCount; + if (functionCount != 0) { + // Functions containing other functions require activation objects + fnNode.itsNeedsActivation = true; + for (int i = 0; i != functionCount; ++i) { + FunctionNode fn = fnNode.getFunctionNode (i); + // nested function expression statements overrides var + if (fn.FunctionType == FunctionNode.FUNCTION_EXPRESSION_STATEMENT) { + string name = fn.FunctionName; + if (name != null && name.Length != 0) { + fnNode.removeParamOrVar (name); + } + } + } + } + + if (functionType == FunctionNode.FUNCTION_EXPRESSION) { + string name = fnNode.FunctionName; + if (name != null && name.Length != 0 && !fnNode.hasParamOrVar (name)) { + // A function expression needs to have its name as a + // variable (if it isn't already allocated as a variable). + // See ECMA Ch. 13. We add code to the beginning of the + // function to initialize a local variable of the + // function's name to the function value. + fnNode.addVar (name); + Node setFn = new Node (Token.EXPR_VOID, new Node (Token.SETNAME, Node.newString (Token.BINDNAME, name), new Node (Token.THISFN))); + statements.addChildrenToFront (setFn); + } + } + + // Add return to end if needed. + Node lastStmt = statements.LastChild; + if (lastStmt == null || lastStmt.Type != Token.RETURN) { + statements.addChildToBack (new Node (Token.RETURN)); + } + + Node result = Node.newString (Token.FUNCTION, fnNode.FunctionName); + result.putIntProp (Node.FUNCTION_PROP, functionIndex); + return result; + } + + /// Add a child to the back of the given node. This function + /// breaks the Factory abstraction, but it removes a requirement + /// from implementors of Node. + /// + internal void addChildToBack (Node parent, Node child) + { + parent.addChildToBack (child); + } + + /// Create loop node. The parser will later call + /// CreateWhile|CreateDoWhile|CreateFor|CreateForIn + /// to finish loop generation. + /// + internal Node CreateLoopNode (Node loopLabel, int lineno) + { + Node.Jump result = new Node.Jump (Token.LOOP, lineno); + if (loopLabel != null) { + ((Node.Jump)loopLabel).Loop = result; + } + return result; + } + + /// While + internal Node CreateWhile (Node loop, Node cond, Node body) + { + return CreateLoop ((Node.Jump)loop, LOOP_WHILE, body, cond, null, null); + } + + /// DoWhile + internal Node CreateDoWhile (Node loop, Node body, Node cond) + { + return CreateLoop ((Node.Jump)loop, LOOP_DO_WHILE, body, cond, null, null); + } + + /// For + internal Node CreateFor (Node loop, Node init, Node test, Node incr, Node body) + { + return CreateLoop ((Node.Jump)loop, LOOP_FOR, body, test, init, incr); + } + + private Node CreateLoop (Node.Jump loop, int loopType, Node body, Node cond, Node init, Node incr) + { + Node bodyTarget = Node.newTarget (); + Node condTarget = Node.newTarget (); + if (loopType == LOOP_FOR && cond.Type == Token.EMPTY) { + cond = new Node (Token.TRUE); + } + Node.Jump IFEQ = new Node.Jump (Token.IFEQ, cond); + IFEQ.target = bodyTarget; + Node breakTarget = Node.newTarget (); + + loop.addChildToBack (bodyTarget); + loop.addChildrenToBack (body); + if (loopType == LOOP_WHILE || loopType == LOOP_FOR) { + // propagate lineno to condition + loop.addChildrenToBack (new Node (Token.EMPTY, loop.Lineno)); + } + loop.addChildToBack (condTarget); + loop.addChildToBack (IFEQ); + loop.addChildToBack (breakTarget); + + loop.target = breakTarget; + Node continueTarget = condTarget; + + if (loopType == LOOP_WHILE || loopType == LOOP_FOR) { + // Just add a GOTO to the condition in the do..while + loop.addChildToFront (makeJump (Token.GOTO, condTarget)); + + if (loopType == LOOP_FOR) { + if (init.Type != Token.EMPTY) { + if (init.Type != Token.VAR) { + init = new Node (Token.EXPR_VOID, init); + } + loop.addChildToFront (init); + } + Node incrTarget = Node.newTarget (); + loop.addChildAfter (incrTarget, body); + if (incr.Type != Token.EMPTY) { + incr = new Node (Token.EXPR_VOID, incr); + loop.addChildAfter (incr, incrTarget); + } + continueTarget = incrTarget; + } + } + + loop.Continue = continueTarget; + + return loop; + } + + /// For .. In + /// + /// + internal Node CreateForIn (Node loop, Node lhs, Node obj, Node body, bool isForEach) + { + int type = lhs.Type; + + Node lvalue; + if (type == Token.VAR) { + /* + * check that there was only one variable given. + * we can't do this in the parser, because then the + * parser would have to know something about the + * 'init' node of the for-in loop. + */ + Node lastChild = lhs.LastChild; + if (lhs.FirstChild != lastChild) { + parser.ReportError ("msg.mult.index"); + } + lvalue = Node.newString (Token.NAME, lastChild.String); + } + else { + lvalue = makeReference (lhs); + if (lvalue == null) { + parser.ReportError ("msg.bad.for.in.lhs"); + return obj; + } + } + + Node localBlock = new Node (Token.LOCAL_BLOCK); + + int initType = (isForEach) ? Token.ENUM_INIT_VALUES : Token.ENUM_INIT_KEYS; + Node init = new Node (initType, obj); + init.putProp (Node.LOCAL_BLOCK_PROP, localBlock); + Node cond = new Node (Token.ENUM_NEXT); + cond.putProp (Node.LOCAL_BLOCK_PROP, localBlock); + Node id = new Node (Token.ENUM_ID); + id.putProp (Node.LOCAL_BLOCK_PROP, localBlock); + + Node newBody = new Node (Token.BLOCK); + Node assign = simpleAssignment (lvalue, id); + newBody.addChildToBack (new Node (Token.EXPR_VOID, assign)); + newBody.addChildToBack (body); + + loop = CreateWhile (loop, cond, newBody); + loop.addChildToFront (init); + if (type == Token.VAR) + loop.addChildToFront (lhs); + localBlock.addChildToBack (loop); + + return localBlock; + } + + /// Try/Catch/Finally + /// + /// The IRFactory tries to express as much as possible in the tree; + /// the responsibilities remaining for Codegen are to add the Java + /// handlers: (Either (but not both) of TARGET and FINALLY might not + /// be defined) + /// - a catch handler for javascript exceptions that unwraps the + /// exception onto the stack and GOTOes to the catch target + /// - a finally handler + /// ... and a goto to GOTO around these handlers. + /// + internal Node CreateTryCatchFinally (Node tryBlock, Node catchBlocks, Node finallyBlock, int lineno) + { + bool hasFinally = (finallyBlock != null) && (finallyBlock.Type != Token.BLOCK || finallyBlock.hasChildren ()); + + // short circuit + if (tryBlock.Type == Token.BLOCK && !tryBlock.hasChildren () && !hasFinally) { + return tryBlock; + } + + bool hasCatch = catchBlocks.hasChildren (); + + // short circuit + if (!hasFinally && !hasCatch) { + // bc finally might be an empty block... + return tryBlock; + } + + + Node handlerBlock = new Node (Token.LOCAL_BLOCK); + Node.Jump pn = new Node.Jump (Token.TRY, tryBlock, lineno); + pn.putProp (Node.LOCAL_BLOCK_PROP, handlerBlock); + + if (hasCatch) { + // jump around catch code + Node endCatch = Node.newTarget (); + pn.addChildToBack (makeJump (Token.GOTO, endCatch)); + + // make a TARGET for the catch that the tcf node knows about + Node catchTarget = Node.newTarget (); + pn.target = catchTarget; + // mark it + pn.addChildToBack (catchTarget); + + // + // Given + // + // try { + // tryBlock; + // } catch (e if condition1) { + // something1; + // ... + // + // } catch (e if conditionN) { + // somethingN; + // } catch (e) { + // somethingDefault; + // } + // + // rewrite as + // + // try { + // tryBlock; + // goto after_catch: + // } catch (x) { + // with (newCatchScope(e, x)) { + // if (condition1) { + // something1; + // goto after_catch; + // } + // } + // ... + // with (newCatchScope(e, x)) { + // if (conditionN) { + // somethingN; + // goto after_catch; + // } + // } + // with (newCatchScope(e, x)) { + // somethingDefault; + // goto after_catch; + // } + // } + // after_catch: + // + // If there is no default catch, then the last with block + // arround "somethingDefault;" is replaced by "rethrow;" + + // It is assumed that catch handler generation will store + // exeception object in handlerBlock register + + // Block with local for exception scope objects + Node catchScopeBlock = new Node (Token.LOCAL_BLOCK); + + // expects catchblocks children to be (cond block) pairs. + Node cb = catchBlocks.FirstChild; + bool hasDefault = false; + int scopeIndex = 0; + while (cb != null) { + int catchLineNo = cb.Lineno; + + Node name = cb.FirstChild; + Node cond = name.Next; + Node catchStatement = cond.Next; + cb.removeChild (name); + cb.removeChild (cond); + cb.removeChild (catchStatement); + + // Add goto to the catch statement to jump out of catch + // but prefix it with LEAVEWITH since try..catch produces + // "with"code in order to limit the scope of the exception + // object. + catchStatement.addChildToBack (new Node (Token.LEAVEWITH)); + catchStatement.addChildToBack (makeJump (Token.GOTO, endCatch)); + + // Create condition "if" when present + Node condStmt; + if (cond.Type == Token.EMPTY) { + condStmt = catchStatement; + hasDefault = true; + } + else { + condStmt = CreateIf (cond, catchStatement, null, catchLineNo); + } + + // Generate code to Create the scope object and store + // it in catchScopeBlock register + Node catchScope = new Node (Token.CATCH_SCOPE, name, CreateUseLocal (handlerBlock)); + catchScope.putProp (Node.LOCAL_BLOCK_PROP, catchScopeBlock); + catchScope.putIntProp (Node.CATCH_SCOPE_PROP, scopeIndex); + catchScopeBlock.addChildToBack (catchScope); + + // Add with statement based on catch scope object + catchScopeBlock.addChildToBack (CreateWith (CreateUseLocal (catchScopeBlock), condStmt, catchLineNo)); + + // move to next cb + cb = cb.Next; + ++scopeIndex; + } + pn.addChildToBack (catchScopeBlock); + if (!hasDefault) { + // Generate code to rethrow if no catch clause was executed + Node rethrow = new Node (Token.RETHROW); + rethrow.putProp (Node.LOCAL_BLOCK_PROP, handlerBlock); + pn.addChildToBack (rethrow); + } + + pn.addChildToBack (endCatch); + } + + if (hasFinally) { + Node finallyTarget = Node.newTarget (); + pn.Finally = finallyTarget; + + // add jsr finally to the try block + pn.addChildToBack (makeJump (Token.JSR, finallyTarget)); + + // jump around finally code + Node finallyEnd = Node.newTarget (); + pn.addChildToBack (makeJump (Token.GOTO, finallyEnd)); + + pn.addChildToBack (finallyTarget); + Node fBlock = new Node (Token.FINALLY, finallyBlock); + fBlock.putProp (Node.LOCAL_BLOCK_PROP, handlerBlock); + pn.addChildToBack (fBlock); + + pn.addChildToBack (finallyEnd); + } + handlerBlock.addChildToBack (pn); + return handlerBlock; + } + + /// Throw, Return, Label, Break and Continue are defined in ASTFactory. + + /// With + internal Node CreateWith (Node obj, Node body, int lineno) + { + setRequiresActivation (); + Node result = new Node (Token.BLOCK, lineno); + result.addChildToBack (new Node (Token.ENTERWITH, obj)); + Node bodyNode = new Node (Token.WITH, body, lineno); + result.addChildrenToBack (bodyNode); + result.addChildToBack (new Node (Token.LEAVEWITH)); + return result; + } + + /// DOTQUERY + public Node CreateDotQuery (Node obj, Node body, int lineno) + { + setRequiresActivation (); + Node result = new Node (Token.DOTQUERY, obj, body, lineno); + return result; + } + + internal Node CreateArrayLiteral (ObjArray elems, int skipCount) + { + int length = elems.size (); + int [] skipIndexes = null; + if (skipCount != 0) { + skipIndexes = new int [skipCount]; + } + Node array = new Node (Token.ARRAYLIT); + for (int i = 0, j = 0; i != length; ++i) { + Node elem = (Node)elems.Get (i); + if (elem != null) { + array.addChildToBack (elem); + } + else { + skipIndexes [j] = i; + ++j; + } + } + if (skipCount != 0) { + array.putProp (Node.SKIP_INDEXES_PROP, skipIndexes); + } + return array; + } + + /// Object Literals + ///
CreateObjectLiteral rewrites its argument as object + /// creation plus object property entries, so later compiler + /// stages don't need to know about object literals. + ///
+ internal Node CreateObjectLiteral (ObjArray elems) + { + int size = elems.size () / 2; + Node obj = new Node (Token.OBJECTLIT); + object [] properties; + if (size == 0) { + properties = ScriptRuntime.EmptyArgs; + } + else { + properties = new object [size]; + for (int i = 0; i != size; ++i) { + properties [i] = elems.Get (2 * i); + Node value = (Node)elems.Get (2 * i + 1); + obj.addChildToBack (value); + } + } + obj.putProp (Node.OBJECT_IDS_PROP, properties); + return obj; + } + + /// Regular expressions + internal Node CreateRegExp (int regexpIndex) + { + Node n = new Node (Token.REGEXP); + n.putIntProp (Node.REGEXP_PROP, regexpIndex); + return n; + } + + /// If statement + internal Node CreateIf (Node cond, Node ifTrue, Node ifFalse, int lineno) + { + int condStatus = isAlwaysDefinedBoolean (cond); + if (condStatus == ALWAYS_TRUE_BOOLEAN) { + return ifTrue; + } + else if (condStatus == ALWAYS_FALSE_BOOLEAN) { + if (ifFalse != null) { + return ifFalse; + } + return new Node (Token.BLOCK, lineno); + } + + Node result = new Node (Token.BLOCK, lineno); + Node ifNotTarget = Node.newTarget (); + Node.Jump IFNE = new Node.Jump (Token.IFNE, cond); + IFNE.target = ifNotTarget; + + result.addChildToBack (IFNE); + result.addChildrenToBack (ifTrue); + + if (ifFalse != null) { + Node endTarget = Node.newTarget (); + result.addChildToBack (makeJump (Token.GOTO, endTarget)); + result.addChildToBack (ifNotTarget); + result.addChildrenToBack (ifFalse); + result.addChildToBack (endTarget); + } + else { + result.addChildToBack (ifNotTarget); + } + + return result; + } + + internal Node CreateCondExpr (Node cond, Node ifTrue, Node ifFalse) + { + int condStatus = isAlwaysDefinedBoolean (cond); + if (condStatus == ALWAYS_TRUE_BOOLEAN) { + return ifTrue; + } + else if (condStatus == ALWAYS_FALSE_BOOLEAN) { + return ifFalse; + } + return new Node (Token.HOOK, cond, ifTrue, ifFalse); + } + + /// Unary + internal Node CreateUnary (int nodeType, Node child) + { + int childType = child.Type; + switch (nodeType) { + + case Token.DELPROP: { + Node n; + if (childType == Token.NAME) { + // Transform Delete(Name "a") + // to Delete(Bind("a"), String("a")) + child.Type = Token.BINDNAME; + Node left = child; + Node right = Node.newString (child.String); + n = new Node (nodeType, left, right); + } + else if (childType == Token.GETPROP || childType == Token.GETELEM) { + Node left = child.FirstChild; + Node right = child.LastChild; + child.removeChild (left); + child.removeChild (right); + n = new Node (nodeType, left, right); + } + else if (childType == Token.GET_REF) { + Node rf = child.FirstChild; + child.removeChild (rf); + n = new Node (Token.DEL_REF, rf); + } + else { + n = new Node (Token.TRUE); + } + return n; + } + + case Token.TYPEOF: + if (childType == Token.NAME) { + child.Type = Token.TYPEOFNAME; + return child; + } + break; + + case Token.BITNOT: + if (childType == Token.NUMBER) { + int value = ScriptConvert.ToInt32 (child.Double); + child.Double = ~value; + return child; + } + break; + + case Token.NEG: + if (childType == Token.NUMBER) { + child.Double = -child.Double; + return child; + } + break; + + case Token.NOT: { + int status = isAlwaysDefinedBoolean (child); + if (status != 0) { + int type; + if (status == ALWAYS_TRUE_BOOLEAN) { + type = Token.FALSE; + } + else { + type = Token.TRUE; + } + if (childType == Token.TRUE || childType == Token.FALSE) { + child.Type = type; + return child; + } + return new Node (type); + } + break; + } + } + return new Node (nodeType, child); + } + + internal Node CreateCallOrNew (int nodeType, Node child) + { + int type = Node.NON_SPECIALCALL; + if (child.Type == Token.NAME) { + string name = child.String; + if (name.Equals ("eval")) { + type = Node.SPECIALCALL_EVAL; + } + else if (name.Equals ("With")) { + type = Node.SPECIALCALL_WITH; + } + } + else if (child.Type == Token.GETPROP) { + string name = child.LastChild.String; + if (name.Equals ("eval")) { + type = Node.SPECIALCALL_EVAL; + } + } + Node node = new Node (nodeType, child); + if (type != Node.NON_SPECIALCALL) { + // Calls to these functions require activation objects. + setRequiresActivation (); + node.putIntProp (Node.SPECIALCALL_PROP, type); + } + return node; + } + + internal Node CreateIncDec (int nodeType, bool post, Node child) + { + child = makeReference (child); + if (child == null) { + string msg; + if (nodeType == Token.DEC) { + msg = "msg.bad.decr"; + } + else { + msg = "msg.bad.incr"; + } + parser.ReportError (msg); + return null; + } + + int childType = child.Type; + + switch (childType) { + + case Token.NAME: + case Token.GETPROP: + case Token.GETELEM: + case Token.GET_REF: { + Node n = new Node (nodeType, child); + int incrDecrMask = 0; + if (nodeType == Token.DEC) { + incrDecrMask |= Node.DECR_FLAG; + } + if (post) { + incrDecrMask |= Node.POST_FLAG; + } + n.putIntProp (Node.INCRDECR_PROP, incrDecrMask); + return n; + } + } + throw Context.CodeBug (); + } + + internal Node CreatePropertyGet (Node target, string ns, string name, int memberTypeFlags) + { + if (ns == null && memberTypeFlags == 0) { + if (target == null) { + return CreateName (name); + } + checkActivationName (name, Token.GETPROP); + if (ScriptRuntime.isSpecialProperty (name)) { + Node rf = new Node (Token.REF_SPECIAL, target); + rf.putProp (Node.NAME_PROP, name); + return new Node (Token.GET_REF, rf); + } + return new Node (Token.GETPROP, target, CreateString (name)); + } + Node elem = CreateString (name); + memberTypeFlags |= Node.PROPERTY_FLAG; + return CreateMemberRefGet (target, ns, elem, memberTypeFlags); + } + + internal Node CreateElementGet (Node target, string ns, Node elem, int memberTypeFlags) + { + // OPT: could optimize to CreatePropertyGet + // iff elem is string that can not be number + if (ns == null && memberTypeFlags == 0) { + // stand-alone [aaa] as primary expression is array literal + // declaration and should not come here! + if (target == null) + throw Context.CodeBug (); + return new Node (Token.GETELEM, target, elem); + } + return CreateMemberRefGet (target, ns, elem, memberTypeFlags); + } + + private Node CreateMemberRefGet (Node target, string ns, Node elem, int memberTypeFlags) + { + Node nsNode = null; + if (ns != null) { + // See 11.1.2 in ECMA 357 + if (ns.Equals ("*")) { + nsNode = new Node (Token.NULL); + } + else { + nsNode = CreateName (ns); + } + } + Node rf; + if (target == null) { + if (ns == null) { + rf = new Node (Token.REF_NAME, elem); + } + else { + rf = new Node (Token.REF_NS_NAME, nsNode, elem); + } + } + else { + if (ns == null) { + rf = new Node (Token.REF_MEMBER, target, elem); + } + else { + rf = new Node (Token.REF_NS_MEMBER, target, nsNode, elem); + } + } + if (memberTypeFlags != 0) { + rf.putIntProp (Node.MEMBER_TYPE_PROP, memberTypeFlags); + } + return new Node (Token.GET_REF, rf); + } + + /// Binary + internal Node CreateBinary (int nodeType, Node left, Node right) + { + switch (nodeType) { + + + case Token.ADD: + // numerical addition and string concatenation + if (left.Type == Token.STRING) { + string s2; + if (right.Type == Token.STRING) { + s2 = right.String; + } + else if (right.Type == Token.NUMBER) { + s2 = ScriptConvert.ToString (right.Double, 10); + } + else { + break; + } + string s1 = left.String; + left.String = string.Concat (s1, s2); + return left; + } + else if (left.Type == Token.NUMBER) { + if (right.Type == Token.NUMBER) { + left.Double = left.Double + right.Double; + return left; + } + else if (right.Type == Token.STRING) { + string s1, s2; + s1 = ScriptConvert.ToString (left.Double, 10); + s2 = right.String; + right.String = string.Concat (s1, s2); + return right; + } + } + // can't do anything if we don't know both types - since + // 0 + object is supposed to call toString on the object and do + // string concantenation rather than addition + break; + + + case Token.SUB: + // numerical subtraction + if (left.Type == Token.NUMBER) { + double ld = left.Double; + if (right.Type == Token.NUMBER) { + //both numbers + left.Double = ld - right.Double; + return left; + } + else if (ld == 0.0) { + // first 0: 0-x -> -x + return new Node (Token.NEG, right); + } + } + else if (right.Type == Token.NUMBER) { + if (right.Double == 0.0) { + //second 0: x - 0 -> +x + // can not make simply x because x - 0 must be number + return new Node (Token.POS, left); + } + } + break; + + + case Token.MUL: + // numerical multiplication + if (left.Type == Token.NUMBER) { + double ld = left.Double; + if (right.Type == Token.NUMBER) { + //both numbers + left.Double = ld * right.Double; + return left; + } + else if (ld == 1.0) { + // first 1: 1 * x -> +x + return new Node (Token.POS, right); + } + } + else if (right.Type == Token.NUMBER) { + if (right.Double == 1.0) { + //second 1: x * 1 -> +x + // can not make simply x because x - 0 must be number + return new Node (Token.POS, left); + } + } + // can't do x*0: Infinity * 0 gives NaN, not 0 + break; + + + case Token.DIV: + // number division + if (right.Type == Token.NUMBER) { + double rd = right.Double; + if (left.Type == Token.NUMBER) { + // both constants -- just divide, trust Java to handle x/0 + left.Double = left.Double / rd; + return left; + } + else if (rd == 1.0) { + // second 1: x/1 -> +x + // not simply x to force number convertion + return new Node (Token.POS, left); + } + } + break; + + + case Token.AND: { + int leftStatus = isAlwaysDefinedBoolean (left); + if (leftStatus == ALWAYS_FALSE_BOOLEAN) { + // if the first one is false, replace with FALSE + return new Node (Token.FALSE); + } + else if (leftStatus == ALWAYS_TRUE_BOOLEAN) { + // if first is true, set to second + return right; + } + int rightStatus = isAlwaysDefinedBoolean (right); + if (rightStatus == ALWAYS_FALSE_BOOLEAN) { + // if the second one is false, replace with FALSE + if (!hasSideEffects (left)) { + return new Node (Token.FALSE); + } + } + else if (rightStatus == ALWAYS_TRUE_BOOLEAN) { + // if second is true, set to first + return left; + } + break; + } + + + case Token.OR: { + int leftStatus = isAlwaysDefinedBoolean (left); + if (leftStatus == ALWAYS_TRUE_BOOLEAN) { + // if the first one is true, replace with TRUE + return new Node (Token.TRUE); + } + else if (leftStatus == ALWAYS_FALSE_BOOLEAN) { + // if first is false, set to second + return right; + } + int rightStatus = isAlwaysDefinedBoolean (right); + if (rightStatus == ALWAYS_TRUE_BOOLEAN) { + // if the second one is true, replace with TRUE + if (!hasSideEffects (left)) { + return new Node (Token.TRUE); + } + } + else if (rightStatus == ALWAYS_FALSE_BOOLEAN) { + // if second is false, set to first + return left; + } + break; + } + } + + return new Node (nodeType, left, right); + } + + private Node simpleAssignment (Node left, Node right) + { + int nodeType = left.Type; + switch (nodeType) { + + case Token.NAME: + left.Type = Token.BINDNAME; + return new Node (Token.SETNAME, left, right); + + + case Token.GETPROP: + case Token.GETELEM: { + Node obj = left.FirstChild; + Node id = left.LastChild; + int type; + if (nodeType == Token.GETPROP) { + type = Token.SETPROP; + } + else { + type = Token.SETELEM; + } + return new Node (type, obj, id, right); + } + + case Token.GET_REF: { + Node rf = left.FirstChild; + checkMutableReference (rf); + return new Node (Token.SET_REF, rf, right); + } + } + + throw Context.CodeBug (); + } + + private void checkMutableReference (Node n) + { + int memberTypeFlags = n.getIntProp (Node.MEMBER_TYPE_PROP, 0); + if ((memberTypeFlags & Node.DESCENDANTS_FLAG) != 0) { + parser.ReportError ("msg.bad.assign.left"); + } + } + + internal Node CreateAssignment (int assignType, Node left, Node right) + { + left = makeReference (left); + if (left == null) { + parser.ReportError ("msg.bad.assign.left"); + return right; + } + + int assignOp; + switch (assignType) { + + case Token.ASSIGN: + return simpleAssignment (left, right); + + case Token.ASSIGN_BITOR: + assignOp = Token.BITOR; + break; + + case Token.ASSIGN_BITXOR: + assignOp = Token.BITXOR; + break; + + case Token.ASSIGN_BITAND: + assignOp = Token.BITAND; + break; + + case Token.ASSIGN_LSH: + assignOp = Token.LSH; + break; + + case Token.ASSIGN_RSH: + assignOp = Token.RSH; + break; + + case Token.ASSIGN_URSH: + assignOp = Token.URSH; + break; + + case Token.ASSIGN_ADD: + assignOp = Token.ADD; + break; + + case Token.ASSIGN_SUB: + assignOp = Token.SUB; + break; + + case Token.ASSIGN_MUL: + assignOp = Token.MUL; + break; + + case Token.ASSIGN_DIV: + assignOp = Token.DIV; + break; + + case Token.ASSIGN_MOD: + assignOp = Token.MOD; + break; + + default: + throw Context.CodeBug (); + + } + + int nodeType = left.Type; + switch (nodeType) { + + case Token.NAME: { + string s = left.String; + + Node opLeft = Node.newString (Token.NAME, s); + Node op = new Node (assignOp, opLeft, right); + Node lvalueLeft = Node.newString (Token.BINDNAME, s); + return new Node (Token.SETNAME, lvalueLeft, op); + } + + case Token.GETPROP: + case Token.GETELEM: { + Node obj = left.FirstChild; + Node id = left.LastChild; + + int type = nodeType == Token.GETPROP ? Token.SETPROP_OP : Token.SETELEM_OP; + + Node opLeft = new Node (Token.USE_STACK); + Node op = new Node (assignOp, opLeft, right); + return new Node (type, obj, id, op); + } + + case Token.GET_REF: { + Node rf = left.FirstChild; + checkMutableReference (rf); + Node opLeft = new Node (Token.USE_STACK); + Node op = new Node (assignOp, opLeft, right); + return new Node (Token.SET_REF_OP, rf, op); + } + } + + throw Context.CodeBug (); + } + + internal Node CreateUseLocal (Node localBlock) + { + if (Token.LOCAL_BLOCK != localBlock.Type) + throw Context.CodeBug (); + Node result = new Node (Token.LOCAL_LOAD); + result.putProp (Node.LOCAL_BLOCK_PROP, localBlock); + return result; + } + + private Node.Jump makeJump (int type, Node target) + { + Node.Jump n = new Node.Jump (type); + n.target = target; + return n; + } + + private Node makeReference (Node node) + { + int type = node.Type; + switch (type) { + + case Token.NAME: + case Token.GETPROP: + case Token.GETELEM: + case Token.GET_REF: + return node; + + case Token.CALL: + node.Type = Token.REF_CALL; + return new Node (Token.GET_REF, node); + } + // Signal caller to report error + return null; + } + + // Check if Node always mean true or false in boolean context + private static int isAlwaysDefinedBoolean (Node node) + { + switch (node.Type) { + + case Token.FALSE: + case Token.NULL: + return ALWAYS_FALSE_BOOLEAN; + + case Token.TRUE: + return ALWAYS_TRUE_BOOLEAN; + + case Token.NUMBER: { + double num = node.Double; + if (!double.IsNaN (num) && num != 0.0) { + return ALWAYS_TRUE_BOOLEAN; + } + else { + return ALWAYS_FALSE_BOOLEAN; + } + } + } + return 0; + } + + private static bool hasSideEffects (Node exprTree) + { + switch (exprTree.Type) { + + case Token.INC: + case Token.DEC: + case Token.SETPROP: + case Token.SETELEM: + case Token.SETNAME: + case Token.CALL: + case Token.NEW: + return true; + + default: + Node child = exprTree.FirstChild; + while (child != null) { + if (hasSideEffects (child)) + return true; + child = child.Next; + } + break; + + } + return false; + } + + private void checkActivationName (string name, int token) + { + if (parser.insideFunction ()) { + bool activation = false; + if ("arguments".Equals (name) || (parser.compilerEnv.activationNames != null && parser.compilerEnv.activationNames.ContainsKey (name))) { + activation = true; + } + else if ("length".Equals (name)) { + if (token == Token.GETPROP && parser.compilerEnv.LanguageVersion == Context.Versions.JS1_2) { + // Use of "length" in 1.2 requires an activation object. + activation = true; + } + } + if (activation) { + setRequiresActivation (); + } + } + } + + private void setRequiresActivation () + { + if (parser.insideFunction ()) { + ((FunctionNode)parser.currentScriptOrFn).itsNeedsActivation = true; + } + } + + private Parser parser; + + private const int LOOP_DO_WHILE = 0; + private const int LOOP_WHILE = 1; + private const int LOOP_FOR = 2; + + private const int ALWAYS_TRUE_BOOLEAN = 1; + private const int ALWAYS_FALSE_BOOLEAN = -1; + } } \ No newline at end of file diff --git a/Code/EcmaScript.NET/NodeTransformer.cs b/src/EcmaScript.NET/NodeTransformer.cs similarity index 97% rename from Code/EcmaScript.NET/NodeTransformer.cs rename to src/EcmaScript.NET/NodeTransformer.cs index ef4d0b6..4ec7562 100644 --- a/Code/EcmaScript.NET/NodeTransformer.cs +++ b/src/EcmaScript.NET/NodeTransformer.cs @@ -1,345 +1,345 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; - -using EcmaScript.NET.Collections; - -namespace EcmaScript.NET -{ - - /// This class transforms a tree to a lower-level representation for codegen. - /// - /// - public class NodeTransformer - { - - public NodeTransformer () - { - } - - public void transform (ScriptOrFnNode tree) - { - transformCompilationUnit (tree); - for (int i = 0; i != tree.FunctionCount; ++i) { - FunctionNode fn = tree.getFunctionNode (i); - transform (fn); - } - } - - private void transformCompilationUnit (ScriptOrFnNode tree) - { - loops = new ObjArray (); - loopEnds = new ObjArray (); - // to save against upchecks if no finally blocks are used. - hasFinally = false; - - try { - transformCompilationUnit_r (tree, tree); - } catch (Helpers.StackOverflowVerifierException) { - throw Context.ReportRuntimeError ( - ScriptRuntime.GetMessage ("mag.too.deep.parser.recursion")); - } - } - - private void transformCompilationUnit_r (ScriptOrFnNode tree, Node parent) - { - Node node = null; - - using (Helpers.StackOverflowVerifier sov = new Helpers.StackOverflowVerifier (1024)) { - for (; ; ) { - Node previous = null; - if (node == null) { - node = parent.FirstChild; - } - else { - previous = node; - node = node.Next; - } - if (node == null) { - break; - } - - int type = node.Type; - - switch (type) { - - - case Token.LABEL: - case Token.SWITCH: - case Token.LOOP: - loops.push (node); - loopEnds.push (((Node.Jump)node).target); - break; - - - case Token.WITH: { - loops.push (node); - Node leave = node.Next; - if (leave.Type != Token.LEAVEWITH) { - Context.CodeBug (); - } - loopEnds.push (leave); - break; - } - - - case Token.TRY: { - Node.Jump jump = (Node.Jump)node; - Node finallytarget = jump.Finally; - if (finallytarget != null) { - hasFinally = true; - loops.push (node); - loopEnds.push (finallytarget); - } - break; - } - - - case Token.TARGET: - case Token.LEAVEWITH: - if (!loopEnds.Empty && loopEnds.peek () == node) { - loopEnds.pop (); - loops.pop (); - } - break; - - - case Token.RETURN: { - /* If we didn't support try/finally, it wouldn't be - * necessary to put LEAVEWITH nodes here... but as - * we do need a series of JSR FINALLY nodes before - * each RETURN, we need to ensure that each finally - * block gets the correct scope... which could mean - * that some LEAVEWITH nodes are necessary. - */ - if (!hasFinally) - break; // skip the whole mess. - Node unwindBlock = null; - for (int i = loops.size () - 1; i >= 0; i--) { - Node n = (Node)loops.Get (i); - int elemtype = n.Type; - if (elemtype == Token.TRY || elemtype == Token.WITH) { - Node unwind; - if (elemtype == Token.TRY) { - Node.Jump jsrnode = new Node.Jump (Token.JSR); - Node jsrtarget = ((Node.Jump)n).Finally; - jsrnode.target = jsrtarget; - unwind = jsrnode; - } - else { - unwind = new Node (Token.LEAVEWITH); - } - if (unwindBlock == null) { - unwindBlock = new Node (Token.BLOCK, node.Lineno); - } - unwindBlock.addChildToBack (unwind); - } - } - if (unwindBlock != null) { - Node returnNode = node; - Node returnExpr = returnNode.FirstChild; - node = replaceCurrent (parent, previous, node, unwindBlock); - if (returnExpr == null) { - unwindBlock.addChildToBack (returnNode); - } - else { - Node store = new Node (Token.EXPR_RESULT, returnExpr); - unwindBlock.addChildToFront (store); - returnNode = new Node (Token.RETURN_RESULT); - unwindBlock.addChildToBack (returnNode); - // transform return expression - transformCompilationUnit_r (tree, store); - } - // skip transformCompilationUnit_r to avoid infinite loop - - goto siblingLoop; - } - break; - } - - - case Token.BREAK: - case Token.CONTINUE: { - Node.Jump jump = (Node.Jump)node; - Node.Jump jumpStatement = jump.JumpStatement; - if (jumpStatement == null) - Context.CodeBug (); - - for (int i = loops.size (); ; ) { - if (i == 0) { - // Parser/IRFactory ensure that break/continue - // always has a jump statement associated with it - // which should be found - throw Context.CodeBug (); - } - --i; - Node n = (Node)loops.Get (i); - if (n == jumpStatement) { - break; - } - - int elemtype = n.Type; - if (elemtype == Token.WITH) { - Node leave = new Node (Token.LEAVEWITH); - previous = addBeforeCurrent (parent, previous, node, leave); - } - else if (elemtype == Token.TRY) { - Node.Jump tryNode = (Node.Jump)n; - Node.Jump jsrFinally = new Node.Jump (Token.JSR); - jsrFinally.target = tryNode.Finally; - previous = addBeforeCurrent (parent, previous, node, jsrFinally); - } - } - - if (type == Token.BREAK) { - jump.target = jumpStatement.target; - } - else { - jump.target = jumpStatement.Continue; - } - jump.Type = Token.GOTO; - - break; - } - - - case Token.CALL: - visitCall (node, tree); - break; - - - case Token.NEW: - visitNew (node, tree); - break; - - - case Token.VAR: { - Node result = new Node (Token.BLOCK); - for (Node cursor = node.FirstChild; cursor != null; ) { - // Move cursor to next before createAssignment get chance - // to change n.next - Node n = cursor; - if (n.Type != Token.NAME) - Context.CodeBug (); - cursor = cursor.Next; - if (!n.hasChildren ()) - continue; - Node init = n.FirstChild; - n.removeChild (init); - n.Type = Token.BINDNAME; - n = new Node (Token.SETNAME, n, init); - Node pop = new Node (Token.EXPR_VOID, n, node.Lineno); - result.addChildToBack (pop); - } - node = replaceCurrent (parent, previous, node, result); - break; - } - - - case Token.NAME: - case Token.SETNAME: - case Token.DELPROP: { - // Turn name to var for faster access if possible - if (tree.Type != Token.FUNCTION || ((FunctionNode)tree).RequiresActivation) { - break; - } - Node nameSource; - if (type == Token.NAME) { - nameSource = node; - } - else { - nameSource = node.FirstChild; - if (nameSource.Type != Token.BINDNAME) { - if (type == Token.DELPROP) { - break; - } - throw Context.CodeBug (); - } - } - string name = nameSource.String; - if (tree.hasParamOrVar (name)) { - if (type == Token.NAME) { - node.Type = Token.GETVAR; - } - else if (type == Token.SETNAME) { - node.Type = Token.SETVAR; - nameSource.Type = Token.STRING; - } - else if (type == Token.DELPROP) { - // Local variables are by definition permanent - Node n = new Node (Token.FALSE); - node = replaceCurrent (parent, previous, node, n); - } - else { - throw Context.CodeBug (); - } - } - break; - } - } - - transformCompilationUnit_r (tree, node); - - siblingLoop: - ; - } - } - } - - protected internal virtual void visitNew (Node node, ScriptOrFnNode tree) - { - } - - protected internal virtual void visitCall (Node node, ScriptOrFnNode tree) - { - } - - private static Node addBeforeCurrent (Node parent, Node previous, Node current, Node toAdd) - { - if (previous == null) { - if (!(current == parent.FirstChild)) - Context.CodeBug (); - parent.addChildToFront (toAdd); - } - else { - if (!(current == previous.Next)) - Context.CodeBug (); - parent.addChildAfter (toAdd, previous); - } - return toAdd; - } - - private static Node replaceCurrent (Node parent, Node previous, Node current, Node replacement) - { - if (previous == null) { - if (!(current == parent.FirstChild)) - Context.CodeBug (); - parent.replaceChild (current, replacement); - } - else if (previous.next == current) { - // Check cachedPrev.next == current is necessary due to possible - // tree mutations - parent.replaceChildAfter (previous, replacement); - } - else { - parent.replaceChild (current, replacement); - } - return replacement; - } - - private ObjArray loops; - private ObjArray loopEnds; - private bool hasFinally; - } +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; + +using EcmaScript.NET.Collections; + +namespace EcmaScript.NET +{ + + /// This class transforms a tree to a lower-level representation for codegen. + /// + /// + public class NodeTransformer + { + + public NodeTransformer () + { + } + + public void transform (ScriptOrFnNode tree) + { + transformCompilationUnit (tree); + for (int i = 0; i != tree.FunctionCount; ++i) { + FunctionNode fn = tree.getFunctionNode (i); + transform (fn); + } + } + + private void transformCompilationUnit (ScriptOrFnNode tree) + { + loops = new ObjArray (); + loopEnds = new ObjArray (); + // to save against upchecks if no finally blocks are used. + hasFinally = false; + + try { + transformCompilationUnit_r (tree, tree); + } catch (Helpers.StackOverflowVerifierException) { + throw Context.ReportRuntimeError ( + ScriptRuntime.GetMessage ("mag.too.deep.parser.recursion")); + } + } + + private void transformCompilationUnit_r (ScriptOrFnNode tree, Node parent) + { + Node node = null; + + using (Helpers.StackOverflowVerifier sov = new Helpers.StackOverflowVerifier (1024)) { + for (; ; ) { + Node previous = null; + if (node == null) { + node = parent.FirstChild; + } + else { + previous = node; + node = node.Next; + } + if (node == null) { + break; + } + + int type = node.Type; + + switch (type) { + + + case Token.LABEL: + case Token.SWITCH: + case Token.LOOP: + loops.push (node); + loopEnds.push (((Node.Jump)node).target); + break; + + + case Token.WITH: { + loops.push (node); + Node leave = node.Next; + if (leave.Type != Token.LEAVEWITH) { + Context.CodeBug (); + } + loopEnds.push (leave); + break; + } + + + case Token.TRY: { + Node.Jump jump = (Node.Jump)node; + Node finallytarget = jump.Finally; + if (finallytarget != null) { + hasFinally = true; + loops.push (node); + loopEnds.push (finallytarget); + } + break; + } + + + case Token.TARGET: + case Token.LEAVEWITH: + if (!loopEnds.Empty && loopEnds.peek () == node) { + loopEnds.pop (); + loops.pop (); + } + break; + + + case Token.RETURN: { + /* If we didn't support try/finally, it wouldn't be + * necessary to put LEAVEWITH nodes here... but as + * we do need a series of JSR FINALLY nodes before + * each RETURN, we need to ensure that each finally + * block gets the correct scope... which could mean + * that some LEAVEWITH nodes are necessary. + */ + if (!hasFinally) + break; // skip the whole mess. + Node unwindBlock = null; + for (int i = loops.size () - 1; i >= 0; i--) { + Node n = (Node)loops.Get (i); + int elemtype = n.Type; + if (elemtype == Token.TRY || elemtype == Token.WITH) { + Node unwind; + if (elemtype == Token.TRY) { + Node.Jump jsrnode = new Node.Jump (Token.JSR); + Node jsrtarget = ((Node.Jump)n).Finally; + jsrnode.target = jsrtarget; + unwind = jsrnode; + } + else { + unwind = new Node (Token.LEAVEWITH); + } + if (unwindBlock == null) { + unwindBlock = new Node (Token.BLOCK, node.Lineno); + } + unwindBlock.addChildToBack (unwind); + } + } + if (unwindBlock != null) { + Node returnNode = node; + Node returnExpr = returnNode.FirstChild; + node = replaceCurrent (parent, previous, node, unwindBlock); + if (returnExpr == null) { + unwindBlock.addChildToBack (returnNode); + } + else { + Node store = new Node (Token.EXPR_RESULT, returnExpr); + unwindBlock.addChildToFront (store); + returnNode = new Node (Token.RETURN_RESULT); + unwindBlock.addChildToBack (returnNode); + // transform return expression + transformCompilationUnit_r (tree, store); + } + // skip transformCompilationUnit_r to avoid infinite loop + + goto siblingLoop; + } + break; + } + + + case Token.BREAK: + case Token.CONTINUE: { + Node.Jump jump = (Node.Jump)node; + Node.Jump jumpStatement = jump.JumpStatement; + if (jumpStatement == null) + Context.CodeBug (); + + for (int i = loops.size (); ; ) { + if (i == 0) { + // Parser/IRFactory ensure that break/continue + // always has a jump statement associated with it + // which should be found + throw Context.CodeBug (); + } + --i; + Node n = (Node)loops.Get (i); + if (n == jumpStatement) { + break; + } + + int elemtype = n.Type; + if (elemtype == Token.WITH) { + Node leave = new Node (Token.LEAVEWITH); + previous = addBeforeCurrent (parent, previous, node, leave); + } + else if (elemtype == Token.TRY) { + Node.Jump tryNode = (Node.Jump)n; + Node.Jump jsrFinally = new Node.Jump (Token.JSR); + jsrFinally.target = tryNode.Finally; + previous = addBeforeCurrent (parent, previous, node, jsrFinally); + } + } + + if (type == Token.BREAK) { + jump.target = jumpStatement.target; + } + else { + jump.target = jumpStatement.Continue; + } + jump.Type = Token.GOTO; + + break; + } + + + case Token.CALL: + visitCall (node, tree); + break; + + + case Token.NEW: + visitNew (node, tree); + break; + + + case Token.VAR: { + Node result = new Node (Token.BLOCK); + for (Node cursor = node.FirstChild; cursor != null; ) { + // Move cursor to next before createAssignment get chance + // to change n.next + Node n = cursor; + if (n.Type != Token.NAME) + Context.CodeBug (); + cursor = cursor.Next; + if (!n.hasChildren ()) + continue; + Node init = n.FirstChild; + n.removeChild (init); + n.Type = Token.BINDNAME; + n = new Node (Token.SETNAME, n, init); + Node pop = new Node (Token.EXPR_VOID, n, node.Lineno); + result.addChildToBack (pop); + } + node = replaceCurrent (parent, previous, node, result); + break; + } + + + case Token.NAME: + case Token.SETNAME: + case Token.DELPROP: { + // Turn name to var for faster access if possible + if (tree.Type != Token.FUNCTION || ((FunctionNode)tree).RequiresActivation) { + break; + } + Node nameSource; + if (type == Token.NAME) { + nameSource = node; + } + else { + nameSource = node.FirstChild; + if (nameSource.Type != Token.BINDNAME) { + if (type == Token.DELPROP) { + break; + } + throw Context.CodeBug (); + } + } + string name = nameSource.String; + if (tree.hasParamOrVar (name)) { + if (type == Token.NAME) { + node.Type = Token.GETVAR; + } + else if (type == Token.SETNAME) { + node.Type = Token.SETVAR; + nameSource.Type = Token.STRING; + } + else if (type == Token.DELPROP) { + // Local variables are by definition permanent + Node n = new Node (Token.FALSE); + node = replaceCurrent (parent, previous, node, n); + } + else { + throw Context.CodeBug (); + } + } + break; + } + } + + transformCompilationUnit_r (tree, node); + + siblingLoop: + ; + } + } + } + + protected internal virtual void visitNew (Node node, ScriptOrFnNode tree) + { + } + + protected internal virtual void visitCall (Node node, ScriptOrFnNode tree) + { + } + + private static Node addBeforeCurrent (Node parent, Node previous, Node current, Node toAdd) + { + if (previous == null) { + if (!(current == parent.FirstChild)) + Context.CodeBug (); + parent.addChildToFront (toAdd); + } + else { + if (!(current == previous.Next)) + Context.CodeBug (); + parent.addChildAfter (toAdd, previous); + } + return toAdd; + } + + private static Node replaceCurrent (Node parent, Node previous, Node current, Node replacement) + { + if (previous == null) { + if (!(current == parent.FirstChild)) + Context.CodeBug (); + parent.replaceChild (current, replacement); + } + else if (previous.next == current) { + // Check cachedPrev.next == current is necessary due to possible + // tree mutations + parent.replaceChildAfter (previous, replacement); + } + else { + parent.replaceChild (current, replacement); + } + return replacement; + } + + private ObjArray loops; + private ObjArray loopEnds; + private bool hasFinally; + } } \ No newline at end of file diff --git a/Code/EcmaScript.NET/Parser.cs b/src/EcmaScript.NET/Parser.cs similarity index 96% rename from Code/EcmaScript.NET/Parser.cs rename to src/EcmaScript.NET/Parser.cs index ff25eaa..dcc56a1 100644 --- a/Code/EcmaScript.NET/Parser.cs +++ b/src/EcmaScript.NET/Parser.cs @@ -1,2464 +1,2477 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; -using System.Collections; - -using EcmaScript.NET.Collections; - -namespace EcmaScript.NET -{ - - /// This class implements the JavaScript parser. - /// - /// It is based on the C source files jsparse.c and jsparse.h - /// in the jsref package. - /// - /// - public class Parser - { - public string EncodedSource - { - get - { - return encodedSource; - } - - } - // TokenInformation flags : currentFlaggedToken stores them together - // with token type - internal const int CLEAR_TI_MASK = 0xFFFF; - internal const int TI_AFTER_EOL = 1 << 16; - internal const int TI_CHECK_LABEL = 1 << 17; // indicates to check for label - - internal CompilerEnvirons compilerEnv; - ErrorReporter errorReporter; - string sourceURI; - internal bool calledByCompileFunction; - - TokenStream ts; - int currentFlaggedToken; - int syntaxErrorCount; - - NodeFactory nf; - - int nestingOfFunction; - - Decompiler decompiler; - string encodedSource; - - // The following are per function variables and should be saved/restored - // during function parsing. - // TODO: Move to separated class? - internal ScriptOrFnNode currentScriptOrFn; - int nestingOfWith; - Hashtable labelSet; // map of label names into nodes - ObjArray loopSet; - ObjArray loopAndSwitchSet; - // end of per function variables - - // Exception to unwind - - class ParserException : ApplicationException - { - - } - - public Parser(CompilerEnvirons compilerEnv, ErrorReporter errorReporter) - { - this.compilerEnv = compilerEnv; - this.errorReporter = errorReporter; - } - - Decompiler CreateDecompiler(CompilerEnvirons compilerEnv) - { - return new Decompiler(); - } - - internal void AddWarning(string messageId, string messageArg) - { - string message = ScriptRuntime.GetMessage(messageId, messageArg); - errorReporter.Warning(message, sourceURI, ts.Lineno, ts.Line, ts.Offset); - } - - internal void AddError(string messageId) - { - ++syntaxErrorCount; - string message = ScriptRuntime.GetMessage(messageId); - errorReporter.Error(message, sourceURI, ts.Lineno, ts.Line, ts.Offset); - } - - internal Exception ReportError(string messageId) - { - AddError(messageId); - - // Throw a ParserException exception to unwind the recursive descent - // parse. - throw new ParserException(); - } - - int peekToken() - { - int tt = currentFlaggedToken; - if (tt == Token.EOF) - { - - while ((tt = ts.Token) == Token.CONDCOMMENT || tt == Token.KEEPCOMMENT) - { - if (tt == Token.CONDCOMMENT) - { - /* Support for JScript conditional comments */ - decompiler.AddJScriptConditionalComment(ts.String); - } - else - { - /* Support for preserved comments */ - decompiler.AddPreservedComment(ts.String); - } - } - - if (tt == Token.EOL) - { - do - { - tt = ts.Token; - - if (tt == Token.CONDCOMMENT) - { - /* Support for JScript conditional comments */ - decompiler.AddJScriptConditionalComment(ts.String); - } - else if (tt == Token.KEEPCOMMENT) - { - /* Support for preserved comments */ - decompiler.AddPreservedComment(ts.String); - } - - } - while (tt == Token.EOL || tt == Token.CONDCOMMENT || tt == Token.KEEPCOMMENT); - tt |= TI_AFTER_EOL; - } - currentFlaggedToken = tt; - } - return tt & CLEAR_TI_MASK; - } - - int peekFlaggedToken() - { - peekToken(); - return currentFlaggedToken; - } - - void consumeToken() - { - currentFlaggedToken = Token.EOF; - } - - int nextToken() - { - int tt = peekToken(); - consumeToken(); - return tt; - } - - int nextFlaggedToken() - { - peekToken(); - int ttFlagged = currentFlaggedToken; - consumeToken(); - return ttFlagged; - } - - bool matchToken(int toMatch) - { - int tt = peekToken(); - if (tt != toMatch) - { - return false; - } - consumeToken(); - return true; - } - - int peekTokenOrEOL() - { - int tt = peekToken(); - // Check for last peeked token flags - if ((currentFlaggedToken & TI_AFTER_EOL) != 0) - { - tt = Token.EOL; - } - return tt; - } - - void setCheckForLabel() - { - if ((currentFlaggedToken & CLEAR_TI_MASK) != Token.NAME) - throw Context.CodeBug(); - currentFlaggedToken |= TI_CHECK_LABEL; - } - - void mustMatchToken(int toMatch, string messageId) - { - if (!matchToken(toMatch)) - { - ReportError(messageId); - } - } - - void mustHaveXML() - { - if (!compilerEnv.isXmlAvailable()) - { - ReportError("msg.XML.not.available"); - } - } - - public bool Eof - { - get - { - return ts.eof(); - } - } - - internal bool insideFunction() - { - return nestingOfFunction != 0; - } - - Node enterLoop(Node loopLabel) - { - Node loop = nf.CreateLoopNode(loopLabel, ts.Lineno); - if (loopSet == null) - { - loopSet = new ObjArray(); - if (loopAndSwitchSet == null) - { - loopAndSwitchSet = new ObjArray(); - } - } - loopSet.push(loop); - loopAndSwitchSet.push(loop); - return loop; - } - - void exitLoop() - { - loopSet.pop(); - loopAndSwitchSet.pop(); - } - - Node enterSwitch(Node switchSelector, int lineno, Node switchLabel) - { - Node switchNode = nf.CreateSwitch(switchSelector, lineno); - if (loopAndSwitchSet == null) - { - loopAndSwitchSet = new ObjArray(); - } - loopAndSwitchSet.push(switchNode); - return switchNode; - } - - void exitSwitch() - { - loopAndSwitchSet.pop(); - } - - /* - * Build a parse tree from the given sourceString. - * - * @return an Object representing the parsed - * program. If the parse fails, null will be returned. (The - * parse failure will result in a call to the ErrorReporter from - * CompilerEnvirons.) - */ - public ScriptOrFnNode Parse(string sourceString, string sourceURI, int lineno) - { - this.sourceURI = sourceURI; - this.ts = new TokenStream(this, null, sourceString, lineno); - try - { - return Parse(); - } - catch (System.IO.IOException) - { - // Should never happen - throw new ApplicationException(); - } - } - - /* - * Build a parse tree from the given sourceString. - * - * @return an Object representing the parsed - * program. If the parse fails, null will be returned. (The - * parse failure will result in a call to the ErrorReporter from - * CompilerEnvirons.) - */ - public ScriptOrFnNode Parse(System.IO.StreamReader sourceReader, string sourceURI, int lineno) - { - this.sourceURI = sourceURI; - this.ts = new TokenStream(this, sourceReader, null, lineno); - return Parse(); - } - - ScriptOrFnNode Parse() - { - this.decompiler = CreateDecompiler(compilerEnv); - this.nf = new NodeFactory(this); - currentScriptOrFn = nf.CreateScript(); - int sourceStartOffset = decompiler.CurrentOffset; - this.encodedSource = null; - decompiler.AddToken(Token.SCRIPT); - - this.currentFlaggedToken = Token.EOF; - this.syntaxErrorCount = 0; - - int baseLineno = ts.Lineno; // line number where source starts - - /* so we have something to add nodes to until - * we've collected all the source */ - Node pn = nf.CreateLeaf(Token.BLOCK); - - for (; ; ) - { - int tt = peekToken(); - - if (tt <= Token.EOF) - { - break; - } - - Node n; - if (tt == Token.FUNCTION) - { - consumeToken(); - try - { - n = function(calledByCompileFunction ? FunctionNode.FUNCTION_EXPRESSION : FunctionNode.FUNCTION_STATEMENT); - } - catch (ParserException) - { - break; - } - } - else - { - n = statement(); - } - nf.addChildToBack(pn, n); - } - - if (this.syntaxErrorCount != 0) - { - string msg = Convert.ToString(this.syntaxErrorCount); - msg = ScriptRuntime.GetMessage("msg.got.syntax.errors", msg); - throw errorReporter.RuntimeError(msg, sourceURI, baseLineno, null, 0); - } - - currentScriptOrFn.SourceName = sourceURI; - currentScriptOrFn.BaseLineno = baseLineno; - currentScriptOrFn.EndLineno = ts.Lineno; - - int sourceEndOffset = decompiler.CurrentOffset; - currentScriptOrFn.setEncodedSourceBounds(sourceStartOffset, sourceEndOffset); - - nf.initScript(currentScriptOrFn, pn); - - if (compilerEnv.isGeneratingSource()) - { - encodedSource = decompiler.EncodedSource; - } - this.decompiler = null; // It helps GC - - return currentScriptOrFn; - } - - /* - * The C version of this function takes an argument list, - * which doesn't seem to be needed for tree generation... - * it'd only be useful for checking argument hiding, which - * I'm not doing anyway... - */ - Node parseFunctionBody() - { - ++nestingOfFunction; - Node pn = nf.CreateBlock(ts.Lineno); - try - { - for (; ; ) - { - Node n; - int tt = peekToken(); - switch (tt) - { - - case Token.ERROR: - case Token.EOF: - case Token.RC: - - goto bodyLoop_brk; - - - case Token.FUNCTION: - consumeToken(); - n = function(FunctionNode.FUNCTION_STATEMENT); - break; - - default: - n = statement(); - break; - - } - nf.addChildToBack(pn, n); - } - - bodyLoop_brk: - ; - - } - catch (ParserException) - { - // Ignore it - } - finally - { - --nestingOfFunction; - } - - return pn; - } - - Node function(int functionType) - { - using (Helpers.StackOverflowVerifier sov = new Helpers.StackOverflowVerifier(1024)) - { - int syntheticType = functionType; - int baseLineno = ts.Lineno; // line number where source starts - - int functionSourceStart = decompiler.MarkFunctionStart(functionType); - string name; - Node memberExprNode = null; - if (matchToken(Token.NAME)) - { - name = ts.String; - decompiler.AddName(name); - if (!matchToken(Token.LP)) - { - if (compilerEnv.isAllowMemberExprAsFunctionName()) - { - // Extension to ECMA: if 'function ' does not follow - // by '(', assume starts memberExpr - Node memberExprHead = nf.CreateName(name); - name = ""; - memberExprNode = memberExprTail(false, memberExprHead); - } - mustMatchToken(Token.LP, "msg.no.paren.parms"); - } - } - else if (matchToken(Token.LP)) - { - // Anonymous function - name = ""; - } - else - { - name = ""; - if (compilerEnv.isAllowMemberExprAsFunctionName()) - { - // Note that memberExpr can not start with '(' like - // in function (1+2).toString(), because 'function (' already - // processed as anonymous function - memberExprNode = memberExpr(false); - } - mustMatchToken(Token.LP, "msg.no.paren.parms"); - } - - if (memberExprNode != null) - { - syntheticType = FunctionNode.FUNCTION_EXPRESSION; - } - - bool nested = insideFunction(); - - FunctionNode fnNode = nf.CreateFunction(name); - if (nested || nestingOfWith > 0) - { - // 1. Nested functions are not affected by the dynamic scope flag - // as dynamic scope is already a parent of their scope. - // 2. Functions defined under the with statement also immune to - // this setup, in which case dynamic scope is ignored in favor - // of with object. - fnNode.itsIgnoreDynamicScope = true; - } - - int functionIndex = currentScriptOrFn.addFunction(fnNode); - - int functionSourceEnd; - - ScriptOrFnNode savedScriptOrFn = currentScriptOrFn; - currentScriptOrFn = fnNode; - int savedNestingOfWith = nestingOfWith; - nestingOfWith = 0; - Hashtable savedLabelSet = labelSet; - labelSet = null; - ObjArray savedLoopSet = loopSet; - loopSet = null; - ObjArray savedLoopAndSwitchSet = loopAndSwitchSet; - loopAndSwitchSet = null; - - Node body; - try - { - decompiler.AddToken(Token.LP); - if (!matchToken(Token.RP)) - { - bool first = true; - do - { - if (!first) - decompiler.AddToken(Token.COMMA); - first = false; - mustMatchToken(Token.NAME, "msg.no.parm"); - string s = ts.String; - if (fnNode.hasParamOrVar(s)) - { - AddWarning("msg.dup.parms", s); - } - fnNode.addParam(s); - decompiler.AddName(s); - } - while (matchToken(Token.COMMA)); - - mustMatchToken(Token.RP, "msg.no.paren.after.parms"); - } - decompiler.AddToken(Token.RP); - - mustMatchToken(Token.LC, "msg.no.brace.body"); - decompiler.AddEol(Token.LC); - body = parseFunctionBody(); - mustMatchToken(Token.RC, "msg.no.brace.after.body"); - - decompiler.AddToken(Token.RC); - functionSourceEnd = decompiler.MarkFunctionEnd(functionSourceStart); - if (functionType != FunctionNode.FUNCTION_EXPRESSION) - { - if (compilerEnv.LanguageVersion >= Context.Versions.JS1_2) - { - // function f() {} function g() {} is not allowed in 1.2 - // or later but for compatibility with old scripts - // the check is done only if language is - // explicitly set. - // TODO: warning needed if version == VERSION_DEFAULT ? - int tt = peekTokenOrEOL(); - if (tt == Token.FUNCTION) - { - ReportError("msg.no.semi.stmt"); - } - } - // Add EOL only if function is not part of expression - // since it gets SEMI + EOL from Statement in that case - decompiler.AddToken(Token.EOL); - } - } - finally - { - loopAndSwitchSet = savedLoopAndSwitchSet; - loopSet = savedLoopSet; - labelSet = savedLabelSet; - nestingOfWith = savedNestingOfWith; - currentScriptOrFn = savedScriptOrFn; - } - - fnNode.setEncodedSourceBounds(functionSourceStart, functionSourceEnd); - fnNode.SourceName = sourceURI; - fnNode.BaseLineno = baseLineno; - fnNode.EndLineno = ts.Lineno; - - Node pn = nf.initFunction(fnNode, functionIndex, body, syntheticType); - if (memberExprNode != null) - { - pn = nf.CreateAssignment(Token.ASSIGN, memberExprNode, pn); - if (functionType != FunctionNode.FUNCTION_EXPRESSION) - { - // TOOD: check JScript behavior: should it be createExprStatement? - pn = nf.CreateExprStatementNoReturn(pn, baseLineno); - } - } - return pn; - } - } - - Node statements() - { - Node pn = nf.CreateBlock(ts.Lineno); - - int tt; - while ((tt = peekToken()) > Token.EOF && tt != Token.RC) - { - nf.addChildToBack(pn, statement()); - } - - return pn; - } - - Node condition() - { - Node pn; - mustMatchToken(Token.LP, "msg.no.paren.cond"); - decompiler.AddToken(Token.LP); - pn = expr(false); - mustMatchToken(Token.RP, "msg.no.paren.after.cond"); - decompiler.AddToken(Token.RP); - - // there's a check here in jsparse.c that corrects = to == - - return pn; - } - - // match a NAME; return null if no match. - Node matchJumpLabelName() - { - Node label = null; - - int tt = peekTokenOrEOL(); - if (tt == Token.NAME) - { - consumeToken(); - string name = ts.String; - decompiler.AddName(name); - if (labelSet != null) - { - label = (Node)labelSet[name]; - } - if (label == null) - { - ReportError("msg.undef.label"); - } - } - - return label; - } - - Node statement() - { - using (Helpers.StackOverflowVerifier sov = new Helpers.StackOverflowVerifier(512)) - { - try - { - Node pn = statementHelper(null); - if (pn != null) - { - return pn; - } - } - catch (ParserException) - { - } - } - - // skip to end of statement - int lineno = ts.Lineno; - for (; ; ) - { - int tt = peekTokenOrEOL(); - consumeToken(); - switch (tt) - { - - case Token.ERROR: - case Token.EOF: - case Token.EOL: - case Token.SEMI: - - goto guessingStatementEnd_brk; - } - } - - guessingStatementEnd_brk: - ; - - return nf.CreateExprStatement(nf.CreateName("error"), lineno); - } - - /// Whether the "catch (e: e instanceof Exception) { ... }" syntax - /// is implemented. - /// - - Node statementHelper(Node statementLabel) - { - Node pn = null; - - int tt; - - tt = peekToken(); - - switch (tt) - { - - case Token.IF: - { - consumeToken(); - - decompiler.AddToken(Token.IF); - int lineno = ts.Lineno; - Node cond = condition(); - decompiler.AddEol(Token.LC); - Node ifTrue = statement(); - Node ifFalse = null; - if (matchToken(Token.ELSE)) - { - decompiler.AddToken(Token.RC); - decompiler.AddToken(Token.ELSE); - decompiler.AddEol(Token.LC); - ifFalse = statement(); - } - decompiler.AddEol(Token.RC); - pn = nf.CreateIf(cond, ifTrue, ifFalse, lineno); - return pn; - } - - - case Token.SWITCH: - { - consumeToken(); - - decompiler.AddToken(Token.SWITCH); - int lineno = ts.Lineno; - mustMatchToken(Token.LP, "msg.no.paren.switch"); - decompiler.AddToken(Token.LP); - pn = enterSwitch(expr(false), lineno, statementLabel); - try - { - mustMatchToken(Token.RP, "msg.no.paren.after.switch"); - decompiler.AddToken(Token.RP); - mustMatchToken(Token.LC, "msg.no.brace.switch"); - decompiler.AddEol(Token.LC); - - bool hasDefault = false; - for (; ; ) - { - tt = nextToken(); - Node caseExpression; - switch (tt) - { - - case Token.RC: - - goto switchLoop_brk; - - - case Token.CASE: - decompiler.AddToken(Token.CASE); - caseExpression = expr(false); - mustMatchToken(Token.COLON, "msg.no.colon.case"); - decompiler.AddEol(Token.COLON); - break; - - - case Token.DEFAULT: - if (hasDefault) - { - ReportError("msg.double.switch.default"); - } - decompiler.AddToken(Token.DEFAULT); - hasDefault = true; - caseExpression = null; - mustMatchToken(Token.COLON, "msg.no.colon.case"); - decompiler.AddEol(Token.COLON); - break; - - - default: - ReportError("msg.bad.switch"); - - goto switchLoop_brk; - - } - - Node block = nf.CreateLeaf(Token.BLOCK); - while ((tt = peekToken()) != Token.RC && tt != Token.CASE && tt != Token.DEFAULT && tt != Token.EOF) - { - nf.addChildToBack(block, statement()); - } - - // caseExpression == null => add default lable - nf.addSwitchCase(pn, caseExpression, block); - } - - switchLoop_brk: - ; - - decompiler.AddEol(Token.RC); - nf.closeSwitch(pn); - } - finally - { - exitSwitch(); - } - return pn; - } - - - case Token.WHILE: - { - consumeToken(); - decompiler.AddToken(Token.WHILE); - - Node loop = enterLoop(statementLabel); - try - { - Node cond = condition(); - decompiler.AddEol(Token.LC); - Node body = statement(); - decompiler.AddEol(Token.RC); - pn = nf.CreateWhile(loop, cond, body); - } - finally - { - exitLoop(); - } - return pn; - } - - - case Token.DO: - { - consumeToken(); - decompiler.AddToken(Token.DO); - decompiler.AddEol(Token.LC); - - Node loop = enterLoop(statementLabel); - try - { - Node body = statement(); - decompiler.AddToken(Token.RC); - mustMatchToken(Token.WHILE, "msg.no.while.do"); - decompiler.AddToken(Token.WHILE); - Node cond = condition(); - pn = nf.CreateDoWhile(loop, body, cond); - } - finally - { - exitLoop(); - } - // Always auto-insert semicon to follow SpiderMonkey: - // It is required by EMAScript but is ignored by the rest of - // world, see bug 238945 - matchToken(Token.SEMI); - decompiler.AddEol(Token.SEMI); - return pn; - } - - - case Token.FOR: - { - consumeToken(); - bool isForEach = false; - decompiler.AddToken(Token.FOR); - - Node loop = enterLoop(statementLabel); - try - { - - Node init; // Node init is also foo in 'foo in Object' - Node cond; // Node cond is also object in 'foo in Object' - Node incr = null; // to kill warning - Node body; - - // See if this is a for each () instead of just a for () - if (matchToken(Token.NAME)) - { - decompiler.AddName(ts.String); - if (ts.String.Equals("each")) - { - isForEach = true; - } - else - { - ReportError("msg.no.paren.for"); - } - } - - mustMatchToken(Token.LP, "msg.no.paren.for"); - decompiler.AddToken(Token.LP); - tt = peekToken(); - if (tt == Token.SEMI) - { - init = nf.CreateLeaf(Token.EMPTY); - } - else - { - if (tt == Token.VAR) - { - // set init to a var list or initial - consumeToken(); // consume the 'var' token - init = variables(true); - } - else - { - init = expr(true); - } - } - - if (matchToken(Token.IN)) - { - decompiler.AddToken(Token.IN); - // 'cond' is the object over which we're iterating - cond = expr(false); - } - else - { - // ordinary for loop - mustMatchToken(Token.SEMI, "msg.no.semi.for"); - decompiler.AddToken(Token.SEMI); - if (peekToken() == Token.SEMI) - { - // no loop condition - cond = nf.CreateLeaf(Token.EMPTY); - } - else - { - cond = expr(false); - } - - mustMatchToken(Token.SEMI, "msg.no.semi.for.cond"); - decompiler.AddToken(Token.SEMI); - if (peekToken() == Token.RP) - { - incr = nf.CreateLeaf(Token.EMPTY); - } - else - { - incr = expr(false); - } - } - - mustMatchToken(Token.RP, "msg.no.paren.for.ctrl"); - decompiler.AddToken(Token.RP); - decompiler.AddEol(Token.LC); - body = statement(); - decompiler.AddEol(Token.RC); - - if (incr == null) - { - // cond could be null if 'in obj' got eaten - // by the init node. - pn = nf.CreateForIn(loop, init, cond, body, isForEach); - } - else - { - pn = nf.CreateFor(loop, init, cond, incr, body); - } - } - finally - { - exitLoop(); - } - return pn; - } - - - case Token.TRY: - { - consumeToken(); - int lineno = ts.Lineno; - - Node tryblock; - Node catchblocks = null; - Node finallyblock = null; - - decompiler.AddToken(Token.TRY); - decompiler.AddEol(Token.LC); - tryblock = statement(); - decompiler.AddEol(Token.RC); - - catchblocks = nf.CreateLeaf(Token.BLOCK); - - bool sawDefaultCatch = false; - int peek = peekToken(); - if (peek == Token.CATCH) - { - while (matchToken(Token.CATCH)) - { - if (sawDefaultCatch) - { - ReportError("msg.catch.unreachable"); - } - decompiler.AddToken(Token.CATCH); - mustMatchToken(Token.LP, "msg.no.paren.catch"); - decompiler.AddToken(Token.LP); - - mustMatchToken(Token.NAME, "msg.bad.catchcond"); - string varName = ts.String; - decompiler.AddName(varName); - - Node catchCond = null; - if (matchToken(Token.IF)) - { - decompiler.AddToken(Token.IF); - catchCond = expr(false); - } - else - { - sawDefaultCatch = true; - } - - mustMatchToken(Token.RP, "msg.bad.catchcond"); - decompiler.AddToken(Token.RP); - mustMatchToken(Token.LC, "msg.no.brace.catchblock"); - decompiler.AddEol(Token.LC); - - nf.addChildToBack(catchblocks, nf.CreateCatch(varName, catchCond, statements(), ts.Lineno)); - - mustMatchToken(Token.RC, "msg.no.brace.after.body"); - decompiler.AddEol(Token.RC); - } - } - else if (peek != Token.FINALLY) - { - mustMatchToken(Token.FINALLY, "msg.try.no.catchfinally"); - } - - if (matchToken(Token.FINALLY)) - { - decompiler.AddToken(Token.FINALLY); - decompiler.AddEol(Token.LC); - finallyblock = statement(); - decompiler.AddEol(Token.RC); - } - - pn = nf.CreateTryCatchFinally(tryblock, catchblocks, finallyblock, lineno); - - return pn; - } - - - case Token.THROW: - { - consumeToken(); - if (peekTokenOrEOL() == Token.EOL) - { - // ECMAScript does not allow new lines before throw expression, - // see bug 256617 - ReportError("msg.bad.throw.eol"); - } - - int lineno = ts.Lineno; - decompiler.AddToken(Token.THROW); - pn = nf.CreateThrow(expr(false), lineno); - break; - } - - - case Token.BREAK: - { - consumeToken(); - int lineno = ts.Lineno; - - decompiler.AddToken(Token.BREAK); - - // matchJumpLabelName only matches if there is one - Node breakStatement = matchJumpLabelName(); - if (breakStatement == null) - { - if (loopAndSwitchSet == null || loopAndSwitchSet.size() == 0) - { - ReportError("msg.bad.break"); - return null; - } - breakStatement = (Node)loopAndSwitchSet.peek(); - } - pn = nf.CreateBreak(breakStatement, lineno); - break; - } - - - case Token.CONTINUE: - { - consumeToken(); - int lineno = ts.Lineno; - - decompiler.AddToken(Token.CONTINUE); - - Node loop; - // matchJumpLabelName only matches if there is one - Node label = matchJumpLabelName(); - if (label == null) - { - if (loopSet == null || loopSet.size() == 0) - { - ReportError("msg.continue.outside"); - return null; - } - loop = (Node)loopSet.peek(); - } - else - { - loop = nf.getLabelLoop(label); - if (loop == null) - { - ReportError("msg.continue.nonloop"); - return null; - } - } - pn = nf.CreateContinue(loop, lineno); - break; - } - - - case Token.WITH: - { - consumeToken(); - - decompiler.AddToken(Token.WITH); - int lineno = ts.Lineno; - mustMatchToken(Token.LP, "msg.no.paren.with"); - decompiler.AddToken(Token.LP); - Node obj = expr(false); - mustMatchToken(Token.RP, "msg.no.paren.after.with"); - decompiler.AddToken(Token.RP); - decompiler.AddEol(Token.LC); - - ++nestingOfWith; - Node body; - try - { - body = statement(); - } - finally - { - --nestingOfWith; - } - - decompiler.AddEol(Token.RC); - - pn = nf.CreateWith(obj, body, lineno); - return pn; - } - - - case Token.VAR: - { - consumeToken(); - pn = variables(false); - break; - } - - - case Token.RETURN: - { - if (!insideFunction()) - { - ReportError("msg.bad.return"); - } - consumeToken(); - decompiler.AddToken(Token.RETURN); - int lineno = ts.Lineno; - - Node retExpr; - /* This is ugly, but we don't want to require a semicolon. */ - tt = peekTokenOrEOL(); - switch (tt) - { - - case Token.SEMI: - case Token.RC: - case Token.EOF: - case Token.EOL: - case Token.ERROR: - retExpr = null; - break; - - default: - retExpr = expr(false); - break; - - } - pn = nf.CreateReturn(retExpr, lineno); - break; - } - - case Token.DEBUGGER: - consumeToken(); - decompiler.AddToken(Token.DEBUGGER); - pn = nf.CreateDebugger(ts.Lineno); - break; - - case Token.LC: - consumeToken(); - if (statementLabel != null) - { - decompiler.AddToken(Token.LC); - } - pn = statements(); - mustMatchToken(Token.RC, "msg.no.brace.block"); - if (statementLabel != null) - { - decompiler.AddEol(Token.RC); - } - return pn; - - - case Token.ERROR: - // Fall thru, to have a node for error recovery to work on - case Token.SEMI: - consumeToken(); - pn = nf.CreateLeaf(Token.EMPTY); - return pn; - - - case Token.FUNCTION: - { - consumeToken(); - pn = function(FunctionNode.FUNCTION_EXPRESSION_STATEMENT); - return pn; - } - - - case Token.DEFAULT: - consumeToken(); - mustHaveXML(); - - decompiler.AddToken(Token.DEFAULT); - int nsLine = ts.Lineno; - - if (!(matchToken(Token.NAME) && ts.String.Equals("xml"))) - { - ReportError("msg.bad.namespace"); - } - decompiler.AddName(ts.String); - - if (!(matchToken(Token.NAME) && ts.String.Equals("namespace"))) - { - ReportError("msg.bad.namespace"); - } - decompiler.AddName(ts.String); - - if (!matchToken(Token.ASSIGN)) - { - ReportError("msg.bad.namespace"); - } - decompiler.AddToken(Token.ASSIGN); - - Node e = expr(false); - pn = nf.CreateDefaultNamespace(e, nsLine); - break; - - - case Token.NAME: - { - int lineno = ts.Lineno; - string name = ts.String; - setCheckForLabel(); - pn = expr(false); - if (pn.Type != Token.LABEL) - { - - if (compilerEnv.getterAndSetterSupport) - { - tt = peekToken(); - if (tt == Token.NAME) - { - if (ts.String == "getter" || ts.String == "setter") - { - pn.Type = (ts.String[0] == 'g' ? Token.SETPROP_GETTER - : Token.SETPROP_SETTER); - decompiler.AddName(" " + ts.String); // HACK: Hack (whitespace) for decmpiler - consumeToken(); - matchToken(Token.ASSIGN); - decompiler.AddToken(Token.ASSIGN); - matchToken(Token.FUNCTION); - Node fn = function(FunctionNode.FUNCTION_EXPRESSION); - pn.addChildToBack(fn); - } - } - } - pn = nf.CreateExprStatement(pn, lineno); - } - else - { - // Parsed the label: push back token should be - // colon that primaryExpr left untouched. - if (peekToken() != Token.COLON) - Context.CodeBug(); - consumeToken(); - // depend on decompiling lookahead to guess that that - // last name was a label. - decompiler.AddName(name); - decompiler.AddEol(Token.COLON); - - if (labelSet == null) - { - labelSet = Hashtable.Synchronized(new Hashtable()); - } - else if (labelSet.ContainsKey(name)) - { - ReportError("msg.dup.label"); - } - - bool firstLabel; - if (statementLabel == null) - { - firstLabel = true; - statementLabel = pn; - } - else - { - // Discard multiple label nodes and use only - // the first: it allows to simplify IRFactory - firstLabel = false; - } - labelSet[name] = statementLabel; - try - { - pn = statementHelper(statementLabel); - } - finally - { - labelSet.Remove(name); - } - if (firstLabel) - { - pn = nf.CreateLabeledStatement(statementLabel, pn); - } - return pn; - } - break; - } - - - default: - { - int lineno = ts.Lineno; - pn = expr(false); - pn = nf.CreateExprStatement(pn, lineno); - break; - } - - } - - // FINDME - - int ttFlagged = peekFlaggedToken(); - switch (ttFlagged & CLEAR_TI_MASK) - { - - case Token.SEMI: - // Consume ';' as a part of expression - consumeToken(); - break; - - case Token.ERROR: - case Token.EOF: - case Token.RC: - // Autoinsert ; - break; - - default: - if ((ttFlagged & TI_AFTER_EOL) == 0) - { - // Report error if no EOL or autoinsert ; otherwise - ReportError("msg.no.semi.stmt"); - } - break; - - } - decompiler.AddEol(Token.SEMI); - - return pn; - } - - Node variables(bool inForInit) - { - Node pn = nf.CreateVariables(ts.Lineno); - bool first = true; - - decompiler.AddToken(Token.VAR); - - for (; ; ) - { - Node name; - Node init; - mustMatchToken(Token.NAME, "msg.bad.var"); - string s = ts.String; - - if (!first) - decompiler.AddToken(Token.COMMA); - first = false; - - decompiler.AddName(s); - currentScriptOrFn.addVar(s); - name = nf.CreateName(s); - - // omitted check for argument hiding - - if (matchToken(Token.ASSIGN)) - { - decompiler.AddToken(Token.ASSIGN); - - init = assignExpr(inForInit); - nf.addChildToBack(name, init); - } - nf.addChildToBack(pn, name); - if (!matchToken(Token.COMMA)) - break; - } - return pn; - } - - Node expr(bool inForInit) - { - Node pn = assignExpr(inForInit); - while (matchToken(Token.COMMA)) - { - decompiler.AddToken(Token.COMMA); - pn = nf.CreateBinary(Token.COMMA, pn, assignExpr(inForInit)); - } - return pn; - } - - Node assignExpr(bool inForInit) - { - Node pn = condExpr(inForInit); - - int tt = peekToken(); - if (Token.FIRST_ASSIGN <= tt && tt <= Token.LAST_ASSIGN) - { - consumeToken(); - decompiler.AddToken(tt); - pn = nf.CreateAssignment(tt, pn, assignExpr(inForInit)); - } - - return pn; - } - - Node condExpr(bool inForInit) - { - Node ifTrue; - Node ifFalse; - - Node pn = orExpr(inForInit); - - if (matchToken(Token.HOOK)) - { - decompiler.AddToken(Token.HOOK); - ifTrue = assignExpr(false); - mustMatchToken(Token.COLON, "msg.no.colon.cond"); - decompiler.AddToken(Token.COLON); - ifFalse = assignExpr(inForInit); - return nf.CreateCondExpr(pn, ifTrue, ifFalse); - } - - return pn; - } - - Node orExpr(bool inForInit) - { - Node pn = andExpr(inForInit); - if (matchToken(Token.OR)) - { - decompiler.AddToken(Token.OR); - pn = nf.CreateBinary(Token.OR, pn, orExpr(inForInit)); - } - - return pn; - } - - Node andExpr(bool inForInit) - { - Node pn = bitOrExpr(inForInit); - if (matchToken(Token.AND)) - { - decompiler.AddToken(Token.AND); - pn = nf.CreateBinary(Token.AND, pn, andExpr(inForInit)); - } - - return pn; - } - - Node bitOrExpr(bool inForInit) - { - Node pn = bitXorExpr(inForInit); - while (matchToken(Token.BITOR)) - { - decompiler.AddToken(Token.BITOR); - pn = nf.CreateBinary(Token.BITOR, pn, bitXorExpr(inForInit)); - } - return pn; - } - - Node bitXorExpr(bool inForInit) - { - Node pn = bitAndExpr(inForInit); - while (matchToken(Token.BITXOR)) - { - decompiler.AddToken(Token.BITXOR); - pn = nf.CreateBinary(Token.BITXOR, pn, bitAndExpr(inForInit)); - } - return pn; - } - - Node bitAndExpr(bool inForInit) - { - Node pn = eqExpr(inForInit); - while (matchToken(Token.BITAND)) - { - decompiler.AddToken(Token.BITAND); - pn = nf.CreateBinary(Token.BITAND, pn, eqExpr(inForInit)); - } - return pn; - } - - Node eqExpr(bool inForInit) - { - Node pn = relExpr(inForInit); - for (; ; ) - { - int tt = peekToken(); - switch (tt) - { - - case Token.EQ: - case Token.NE: - case Token.SHEQ: - case Token.SHNE: - consumeToken(); - int decompilerToken = tt; - int parseToken = tt; - if (compilerEnv.LanguageVersion == Context.Versions.JS1_2) - { - // JavaScript 1.2 uses shallow equality for == and != . - // In addition, convert === and !== for decompiler into - // == and != since the decompiler is supposed to show - // canonical source and in 1.2 ===, !== are allowed - // only as an alias to ==, !=. - switch (tt) - { - - case Token.EQ: - parseToken = Token.SHEQ; - break; - - case Token.NE: - parseToken = Token.SHNE; - break; - - case Token.SHEQ: - decompilerToken = Token.EQ; - break; - - case Token.SHNE: - decompilerToken = Token.NE; - break; - } - } - decompiler.AddToken(decompilerToken); - pn = nf.CreateBinary(parseToken, pn, relExpr(inForInit)); - continue; - } - break; - } - return pn; - } - - Node relExpr(bool inForInit) - { - Node pn = shiftExpr(); - for (; ; ) - { - int tt = peekToken(); - switch (tt) - { - - case Token.IN: - if (inForInit) - break; - // fall through - goto case Token.INSTANCEOF; - - case Token.INSTANCEOF: - case Token.LE: - case Token.LT: - case Token.GE: - case Token.GT: - consumeToken(); - decompiler.AddToken(tt); - pn = nf.CreateBinary(tt, pn, shiftExpr()); - continue; - } - break; - } - return pn; - } - - Node shiftExpr() - { - Node pn = addExpr(); - for (; ; ) - { - int tt = peekToken(); - switch (tt) - { - - case Token.LSH: - case Token.URSH: - case Token.RSH: - consumeToken(); - decompiler.AddToken(tt); - pn = nf.CreateBinary(tt, pn, addExpr()); - continue; - } - break; - } - return pn; - } - - Node addExpr() - { - Node pn = mulExpr(); - for (; ; ) - { - int tt = peekToken(); - if (tt == Token.ADD || tt == Token.SUB) - { - consumeToken(); - decompiler.AddToken(tt); - // flushNewLines - pn = nf.CreateBinary(tt, pn, mulExpr()); - continue; - } - break; - } - - return pn; - } - - Node mulExpr() - { - Node pn = unaryExpr(); - for (; ; ) - { - int tt = peekToken(); - switch (tt) - { - - case Token.MUL: - case Token.DIV: - case Token.MOD: - consumeToken(); - decompiler.AddToken(tt); - pn = nf.CreateBinary(tt, pn, unaryExpr()); - continue; - } - break; - } - - return pn; - } - - Node unaryExpr() - { - using (Helpers.StackOverflowVerifier sov = new Helpers.StackOverflowVerifier(4096)) - { - int tt; - - tt = peekToken(); - - switch (tt) - { - - case Token.VOID: - case Token.NOT: - case Token.BITNOT: - case Token.TYPEOF: - consumeToken(); - decompiler.AddToken(tt); - return nf.CreateUnary(tt, unaryExpr()); - - - case Token.ADD: - consumeToken(); - // Convert to special POS token in decompiler and parse tree - decompiler.AddToken(Token.POS); - return nf.CreateUnary(Token.POS, unaryExpr()); - - - case Token.SUB: - consumeToken(); - // Convert to special NEG token in decompiler and parse tree - decompiler.AddToken(Token.NEG); - return nf.CreateUnary(Token.NEG, unaryExpr()); - - - case Token.INC: - case Token.DEC: - consumeToken(); - decompiler.AddToken(tt); - return nf.CreateIncDec(tt, false, memberExpr(true)); - - - case Token.DELPROP: - consumeToken(); - decompiler.AddToken(Token.DELPROP); - return nf.CreateUnary(Token.DELPROP, unaryExpr()); - - - case Token.ERROR: - consumeToken(); - break; - - // XML stream encountered in expression. - - case Token.LT: - if (compilerEnv.isXmlAvailable()) - { - consumeToken(); - Node pn = xmlInitializer(); - return memberExprTail(true, pn); - } - // Fall thru to the default handling of RELOP - goto default; - - - default: - { - Node pn = memberExpr(true); - - // Don't look across a newline boundary for a postfix incop. - tt = peekTokenOrEOL(); - if (tt == Token.INC || tt == Token.DEC) - { - consumeToken(); - decompiler.AddToken(tt); - return nf.CreateIncDec(tt, true, pn); - } - return pn; - } - - } - return nf.CreateName("err"); // Only reached on error. Try to continue. - } - } - - Node xmlInitializer() - { - int tt = ts.FirstXMLToken; - if (tt != Token.XML && tt != Token.XMLEND) - { - ReportError("msg.syntax"); - return null; - } - - /* Make a NEW node to append to. */ - Node pnXML = nf.CreateLeaf(Token.NEW); - decompiler.AddToken(Token.NEW); - decompiler.AddToken(Token.DOT); - - string xml = ts.String; - bool fAnonymous = xml.Trim().StartsWith("<>"); - - decompiler.AddName(fAnonymous ? "XMLList" : "XML"); - Node pn = nf.CreateName(fAnonymous ? "XMLList" : "XML"); - nf.addChildToBack(pnXML, pn); - - pn = null; - Node e; - for (; ; tt = ts.NextXMLToken) - { - switch (tt) - { - - case Token.XML: - xml = ts.String; - decompiler.AddString(xml); - mustMatchToken(Token.LC, "msg.syntax"); - decompiler.AddToken(Token.LC); - e = (peekToken() == Token.RC) ? nf.CreateString("") : expr(false); - mustMatchToken(Token.RC, "msg.syntax"); - decompiler.AddToken(Token.RC); - if (pn == null) - { - pn = nf.CreateString(xml); - } - else - { - pn = nf.CreateBinary(Token.ADD, pn, nf.CreateString(xml)); - } - int nodeType; - if (ts.XMLAttribute) - { - nodeType = Token.ESCXMLATTR; - } - else - { - nodeType = Token.ESCXMLTEXT; - } - e = nf.CreateUnary(nodeType, e); - pn = nf.CreateBinary(Token.ADD, pn, e); - break; - - case Token.XMLEND: - xml = ts.String; - decompiler.AddString(xml); - if (pn == null) - { - pn = nf.CreateString(xml); - } - else - { - pn = nf.CreateBinary(Token.ADD, pn, nf.CreateString(xml)); - } - - nf.addChildToBack(pnXML, pn); - return pnXML; - - default: - ReportError("msg.syntax"); - return null; - - } - } - } - - void argumentList(Node listNode) - { - bool matched; - matched = matchToken(Token.RP); - if (!matched) - { - bool first = true; - do - { - if (!first) - decompiler.AddToken(Token.COMMA); - first = false; - nf.addChildToBack(listNode, assignExpr(false)); - } - while (matchToken(Token.COMMA)); - - mustMatchToken(Token.RP, "msg.no.paren.arg"); - } - decompiler.AddToken(Token.RP); - } - - Node memberExpr(bool allowCallSyntax) - { - int tt; - - Node pn; - - /* Check for new expressions. */ - tt = peekToken(); - if (tt == Token.NEW) - { - /* Eat the NEW token. */ - consumeToken(); - decompiler.AddToken(Token.NEW); - - /* Make a NEW node to append to. */ - pn = nf.CreateCallOrNew(Token.NEW, memberExpr(false)); - - if (matchToken(Token.LP)) - { - decompiler.AddToken(Token.LP); - /* Add the arguments to pn, if any are supplied. */ - argumentList(pn); - } - - // TODO: there's a check in the C source against - // TODO: "too many constructor arguments" - how many - // TODO: do we claim to support? - - /* Experimental syntax: allow an object literal to follow a new expression, - * which will mean a kind of anonymous class built with the JavaAdapter. - * the object literal will be passed as an additional argument to the constructor. - */ - tt = peekToken(); - if (tt == Token.LC) - { - nf.addChildToBack(pn, primaryExpr()); - } - } - else - { - pn = primaryExpr(); - } - - return memberExprTail(allowCallSyntax, pn); - } - - Node memberExprTail(bool allowCallSyntax, Node pn) - { - for (; ; ) - { - int tt = peekToken(); - switch (tt) - { - - - case Token.DOT: - case Token.DOTDOT: - { - int memberTypeFlags; - string s; - - consumeToken(); - decompiler.AddToken(tt); - memberTypeFlags = 0; - if (tt == Token.DOTDOT) - { - mustHaveXML(); - memberTypeFlags = Node.DESCENDANTS_FLAG; - } - if (!compilerEnv.isXmlAvailable()) - { - mustMatchToken(Token.NAME, "msg.no.name.after.dot"); - s = ts.String; - decompiler.AddName(s); - pn = nf.CreatePropertyGet(pn, null, s, memberTypeFlags); - break; - } - - - tt = nextToken(); - - switch (tt) - { - - // handles: name, ns::name, ns::*, ns::[expr] - case Token.NAME: - s = ts.String; - decompiler.AddName(s); - pn = propertyName(pn, s, memberTypeFlags); - break; - - // handles: *, *::name, *::*, *::[expr] - - case Token.MUL: - decompiler.AddName("*"); - pn = propertyName(pn, "*", memberTypeFlags); - break; - - // handles: '@attr', '@ns::attr', '@ns::*', '@ns::*', - // '@::attr', '@::*', '@*', '@*::attr', '@*::*' - - case Token.XMLATTR: - decompiler.AddToken(Token.XMLATTR); - pn = attributeAccess(pn, memberTypeFlags); - break; - - - default: - ReportError("msg.no.name.after.dot"); - break; - - } - } - break; - - - case Token.DOTQUERY: - consumeToken(); - mustHaveXML(); - decompiler.AddToken(Token.DOTQUERY); - pn = nf.CreateDotQuery(pn, expr(false), ts.Lineno); - mustMatchToken(Token.RP, "msg.no.paren"); - decompiler.AddToken(Token.RP); - break; - - - case Token.LB: - consumeToken(); - decompiler.AddToken(Token.LB); - pn = nf.CreateElementGet(pn, null, expr(false), 0); - mustMatchToken(Token.RB, "msg.no.bracket.index"); - decompiler.AddToken(Token.RB); - break; - - - case Token.LP: - if (!allowCallSyntax) - { - - goto tailLoop_brk; - } - consumeToken(); - decompiler.AddToken(Token.LP); - pn = nf.CreateCallOrNew(Token.CALL, pn); - /* Add the arguments to pn, if any are supplied. */ - argumentList(pn); - break; - - - default: - - goto tailLoop_brk; - - } - } - - tailLoop_brk: - ; - - return pn; - } - - /* - * Xml attribute expression: - * '@attr', '@ns::attr', '@ns::*', '@ns::*', '@*', '@*::attr', '@*::*' - */ - Node attributeAccess(Node pn, int memberTypeFlags) - { - memberTypeFlags |= Node.ATTRIBUTE_FLAG; - int tt = nextToken(); - - switch (tt) - { - - // handles: @name, @ns::name, @ns::*, @ns::[expr] - case Token.NAME: - { - string s = ts.String; - decompiler.AddName(s); - pn = propertyName(pn, s, memberTypeFlags); - } - break; - - // handles: @*, @*::name, @*::*, @*::[expr] - - case Token.MUL: - decompiler.AddName("*"); - pn = propertyName(pn, "*", memberTypeFlags); - break; - - // handles @[expr] - - case Token.LB: - decompiler.AddToken(Token.LB); - pn = nf.CreateElementGet(pn, null, expr(false), memberTypeFlags); - mustMatchToken(Token.RB, "msg.no.bracket.index"); - decompiler.AddToken(Token.RB); - break; - - - default: - ReportError("msg.no.name.after.xmlAttr"); - pn = nf.CreatePropertyGet(pn, null, "?", memberTypeFlags); - break; - - } - - return pn; - } - - /// Check if :: follows name in which case it becomes qualified name - Node propertyName(Node pn, string name, int memberTypeFlags) - { - string ns = null; - if (matchToken(Token.COLONCOLON)) - { - decompiler.AddToken(Token.COLONCOLON); - ns = name; - - int tt = nextToken(); - switch (tt) - { - - // handles name::name - case Token.NAME: - name = ts.String; - decompiler.AddName(name); - break; - - // handles name::* - - case Token.MUL: - decompiler.AddName("*"); - name = "*"; - break; - - // handles name::[expr] - - case Token.LB: - decompiler.AddToken(Token.LB); - pn = nf.CreateElementGet(pn, ns, expr(false), memberTypeFlags); - mustMatchToken(Token.RB, "msg.no.bracket.index"); - decompiler.AddToken(Token.RB); - return pn; - - - default: - ReportError("msg.no.name.after.coloncolon"); - name = "?"; - break; - - } - } - - pn = nf.CreatePropertyGet(pn, ns, name, memberTypeFlags); - return pn; - } - - int currentStackIndex = 0; - - - Node primaryExpr() - { - try - { - if (currentStackIndex++ > ScriptRuntime.MAXSTACKSIZE) - { - currentStackIndex = 0; - throw Context.ReportRuntimeError( - ScriptRuntime.GetMessage("mag.too.deep.parser.recursion"), sourceURI, ts.Lineno, null, 0); - } - - Node pn; - - int ttFlagged = nextFlaggedToken(); - int tt = ttFlagged & CLEAR_TI_MASK; - - switch (tt) - { - - - case Token.FUNCTION: - return function(FunctionNode.FUNCTION_EXPRESSION); - - - case Token.LB: - { - ObjArray elems = new ObjArray(); - int skipCount = 0; - decompiler.AddToken(Token.LB); - bool after_lb_or_comma = true; - for (; ; ) - { - tt = peekToken(); - - if (tt == Token.COMMA) - { - consumeToken(); - decompiler.AddToken(Token.COMMA); - if (!after_lb_or_comma) - { - after_lb_or_comma = true; - } - else - { - elems.add((object)null); - ++skipCount; - } - } - else if (tt == Token.RB) - { - consumeToken(); - decompiler.AddToken(Token.RB); - break; - } - else - { - if (!after_lb_or_comma) - { - ReportError("msg.no.bracket.arg"); - } - elems.add(assignExpr(false)); - after_lb_or_comma = false; - } - } - return nf.CreateArrayLiteral(elems, skipCount); - } - - - case Token.LC: - { - ObjArray elems = new ObjArray(); - decompiler.AddToken(Token.LC); - if (!matchToken(Token.RC)) - { - - bool first = true; - do - { - object property; - - if (!first) - decompiler.AddToken(Token.COMMA); - else - first = false; - - tt = peekToken(); - switch (tt) - { - - case Token.NAME: - case Token.STRING: - consumeToken(); - if (compilerEnv.getterAndSetterSupport) - { - if (tt == Token.NAME) - if (CheckForGetOrSet(elems) || CheckForGetterOrSetter(elems)) - goto next_prop; - } - - - // map NAMEs to STRINGs in object literal context - // but tell the decompiler the proper type - string s = ts.String; - if (tt == Token.NAME) - { - decompiler.AddName(s); - } - else - { - decompiler.AddString(s); - } - property = ScriptRuntime.getIndexObject(s); - - break; - - - case Token.NUMBER: - consumeToken(); - double n = ts.Number; - decompiler.AddNumber(n); - property = ScriptRuntime.getIndexObject(n); - break; - - - case Token.RC: - // trailing comma is OK. - - goto commaloop_brk; - - default: - ReportError("msg.bad.prop"); - - goto commaloop_brk; - - } - mustMatchToken(Token.COLON, "msg.no.colon.prop"); - - // OBJLIT is used as ':' in object literal for - // decompilation to solve spacing ambiguity. - decompiler.AddToken(Token.OBJECTLIT); - elems.add(property); - elems.add(assignExpr(false)); - - next_prop: - ; - } - while (matchToken(Token.COMMA)); - - commaloop_brk: - ; - - - mustMatchToken(Token.RC, "msg.no.brace.prop"); - } - decompiler.AddToken(Token.RC); - return nf.CreateObjectLiteral(elems); - } - - - case Token.LP: - - /* Brendan's IR-jsparse.c makes a new node tagged with - * TOK_LP here... I'm not sure I understand why. Isn't - * the grouping already implicit in the structure of the - * parse tree? also TOK_LP is already overloaded (I - * think) in the C IR as 'function call.' */ - decompiler.AddToken(Token.LP); - pn = expr(false); - decompiler.AddToken(Token.RP); - mustMatchToken(Token.RP, "msg.no.paren"); - return pn; - - - case Token.XMLATTR: - mustHaveXML(); - decompiler.AddToken(Token.XMLATTR); - pn = attributeAccess(null, 0); - return pn; - - - case Token.NAME: - { - string name = ts.String; - if ((ttFlagged & TI_CHECK_LABEL) != 0) - { - if (peekToken() == Token.COLON) - { - // Do not consume colon, it is used as unwind indicator - // to return to statementHelper. - // TODO: Better way? - return nf.CreateLabel(ts.Lineno); - } - } - - decompiler.AddName(name); - if (compilerEnv.isXmlAvailable()) - { - pn = propertyName(null, name, 0); - } - else - { - pn = nf.CreateName(name); - } - return pn; - } - - - case Token.NUMBER: - { - double n = ts.Number; - decompiler.AddNumber(n); - return nf.CreateNumber(n); - } - - - case Token.STRING: - { - string s = ts.String; - decompiler.AddString(s); - return nf.CreateString(s); - } - - - case Token.DIV: - case Token.ASSIGN_DIV: - { - // Got / or /= which should be treated as regexp in fact - ts.readRegExp(tt); - string flags = ts.regExpFlags; - ts.regExpFlags = null; - string re = ts.String; - decompiler.AddRegexp(re, flags); - int index = currentScriptOrFn.addRegexp(re, flags); - return nf.CreateRegExp(index); - } - - - case Token.NULL: - case Token.THIS: - case Token.FALSE: - case Token.TRUE: - decompiler.AddToken(tt); - return nf.CreateLeaf(tt); - - - case Token.RESERVED: - ReportError("msg.reserved.id"); - break; - - - case Token.ERROR: - /* the scanner or one of its subroutines reported the error. */ - break; - - - case Token.EOF: - ReportError("msg.unexpected.eof"); - break; - - - default: - ReportError("msg.syntax"); - break; - - } - return null; // should never reach here - } - finally - { - currentStackIndex--; - } - } - - - /// - /// Support for non-ecma "get"/"set" spidermonkey extension. - /// - /// - /// get NAME () SCOPE - /// set NAME () SCOPE - /// - bool CheckForGetOrSet(ObjArray elems) - { - int tt; - - string type = ts.String; - if (type != "get" && type != "set") - { - return false; - } - tt = peekToken(); - if (tt != Token.NAME) - return false; - consumeToken(); - - string name = ts.String; - - decompiler.AddName(name); - - Node func = function(FunctionNode.FUNCTION_EXPRESSION); - object property = ScriptRuntime.getIndexObject(name); - - elems.add((type[0] == 'g') ? (object)new Node.GetterPropertyLiteral(property) - : (object)new Node.SetterPropertyLiteral(property)); - elems.add(func); - - return true; - } - - /// - /// Support for non-ecma "get"/"set" spidermonkey extension. - /// - /// - /// NAME getter: FUNCTION () SCOPE - /// NAME setter: FUNCTION () SCOPE - /// - bool CheckForGetterOrSetter(ObjArray elems) - { - int tt; - - string name = ts.String; - consumeToken(); - - tt = peekToken(); - if (tt != Token.NAME) - return false; - string type = ts.String; - if (type != "getter" && type != "setter") - { - return false; - } - consumeToken(); - - matchToken(Token.COLON); - matchToken(Token.FUNCTION); - - Node func = function(FunctionNode.FUNCTION_EXPRESSION); - object property = ScriptRuntime.getIndexObject(name); - - elems.add((type[0] == 'g') ? (object)new Node.GetterPropertyLiteral(property) - : (object)new Node.SetterPropertyLiteral(property)); - elems.add(func); - - return true; - } - - } +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; +using System.Collections; +using System.Text.RegularExpressions; +using EcmaScript.NET.Collections; + +namespace EcmaScript.NET +{ + + /// This class implements the JavaScript parser. + /// + /// It is based on the C source files jsparse.c and jsparse.h + /// in the jsref package. + /// + /// + public class Parser + { + public string EncodedSource + { + get + { + return encodedSource; + } + + } + // TokenInformation flags : currentFlaggedToken stores them together + // with token type + internal const int CLEAR_TI_MASK = 0xFFFF; + internal const int TI_AFTER_EOL = 1 << 16; + internal const int TI_CHECK_LABEL = 1 << 17; // indicates to check for label + internal readonly Regex SIMPLE_IDENTIFIER_NAME_PATTERN = new Regex("^[a-zA-Z_][a-zA-Z0-9_]*$", RegexOptions.Compiled); + + internal CompilerEnvirons compilerEnv; + ErrorReporter errorReporter; + string sourceURI; + internal bool calledByCompileFunction; + + TokenStream ts; + int currentFlaggedToken; + int syntaxErrorCount; + + NodeFactory nf; + + int nestingOfFunction; + + Decompiler decompiler; + string encodedSource; + + // The following are per function variables and should be saved/restored + // during function parsing. + // TODO: Move to separated class? + internal ScriptOrFnNode currentScriptOrFn; + int nestingOfWith; + Hashtable labelSet; // map of label names into nodes + ObjArray loopSet; + ObjArray loopAndSwitchSet; + // end of per function variables + + // Exception to unwind + + class ParserException : Exception + { + + } + + public Parser(CompilerEnvirons compilerEnv, ErrorReporter errorReporter) + { + this.compilerEnv = compilerEnv; + this.errorReporter = errorReporter; + } + + Decompiler CreateDecompiler(CompilerEnvirons compilerEnv) + { + return new Decompiler(); + } + + internal void AddWarning(string messageId, string messageArg) + { + string message = ScriptRuntime.GetMessage(messageId, messageArg); + errorReporter.Warning(message, sourceURI, ts.Lineno, ts.Line, ts.Offset); + } + + internal void AddError(string messageId) + { + ++syntaxErrorCount; + string message = ScriptRuntime.GetMessage(messageId); + errorReporter.Error(message, sourceURI, ts.Lineno, ts.Line, ts.Offset); + } + + internal Exception ReportError(string messageId) + { + AddError(messageId); + + // Throw a ParserException exception to unwind the recursive descent + // parse. + throw new ParserException(); + } + + int peekToken() + { + int tt = currentFlaggedToken; + if (tt == Token.EOF) + { + + while ((tt = ts.Token) == Token.CONDCOMMENT || tt == Token.KEEPCOMMENT) + { + if (tt == Token.CONDCOMMENT) + { + /* Support for JScript conditional comments */ + decompiler.AddJScriptConditionalComment(ts.String); + } + else + { + /* Support for preserved comments */ + decompiler.AddPreservedComment(ts.String); + } + } + + if (tt == Token.EOL) + { + do + { + tt = ts.Token; + + if (tt == Token.CONDCOMMENT) + { + /* Support for JScript conditional comments */ + decompiler.AddJScriptConditionalComment(ts.String); + } + else if (tt == Token.KEEPCOMMENT) + { + /* Support for preserved comments */ + decompiler.AddPreservedComment(ts.String); + } + + } + while (tt == Token.EOL || tt == Token.CONDCOMMENT || tt == Token.KEEPCOMMENT); + tt |= TI_AFTER_EOL; + } + currentFlaggedToken = tt; + } + return tt & CLEAR_TI_MASK; + } + + int peekFlaggedToken() + { + peekToken(); + return currentFlaggedToken; + } + + void consumeToken() + { + currentFlaggedToken = Token.EOF; + } + + int nextToken() + { + int tt = peekToken(); + consumeToken(); + return tt; + } + + int nextFlaggedToken() + { + peekToken(); + int ttFlagged = currentFlaggedToken; + consumeToken(); + return ttFlagged; + } + + bool matchToken(int toMatch) + { + int tt = peekToken(); + if (tt != toMatch) + { + return false; + } + consumeToken(); + return true; + } + + int peekTokenOrEOL() + { + int tt = peekToken(); + // Check for last peeked token flags + if ((currentFlaggedToken & TI_AFTER_EOL) != 0) + { + tt = Token.EOL; + } + return tt; + } + + void setCheckForLabel() + { + if ((currentFlaggedToken & CLEAR_TI_MASK) != Token.NAME) + throw Context.CodeBug(); + currentFlaggedToken |= TI_CHECK_LABEL; + } + + void mustMatchToken(int toMatch, string messageId) + { + if (!matchToken(toMatch)) + { + ReportError(messageId); + } + } + + void mustHaveXML() + { + if (!compilerEnv.isXmlAvailable()) + { + ReportError("msg.XML.not.available"); + } + } + + public bool Eof + { + get + { + return ts.eof(); + } + } + + internal bool insideFunction() + { + return nestingOfFunction != 0; + } + + Node enterLoop(Node loopLabel) + { + Node loop = nf.CreateLoopNode(loopLabel, ts.Lineno); + if (loopSet == null) + { + loopSet = new ObjArray(); + if (loopAndSwitchSet == null) + { + loopAndSwitchSet = new ObjArray(); + } + } + loopSet.push(loop); + loopAndSwitchSet.push(loop); + return loop; + } + + void exitLoop() + { + loopSet.pop(); + loopAndSwitchSet.pop(); + } + + Node enterSwitch(Node switchSelector, int lineno, Node switchLabel) + { + Node switchNode = nf.CreateSwitch(switchSelector, lineno); + if (loopAndSwitchSet == null) + { + loopAndSwitchSet = new ObjArray(); + } + loopAndSwitchSet.push(switchNode); + return switchNode; + } + + void exitSwitch() + { + loopAndSwitchSet.pop(); + } + + /* + * Build a parse tree from the given sourceString. + * + * @return an Object representing the parsed + * program. If the parse fails, null will be returned. (The + * parse failure will result in a call to the ErrorReporter from + * CompilerEnvirons.) + */ + public ScriptOrFnNode Parse(string sourceString, string sourceURI, int lineno) + { + this.sourceURI = sourceURI; + this.ts = new TokenStream(this, null, sourceString, lineno); + try + { + return Parse(); + } + catch (System.IO.IOException) + { + // Should never happen + throw new Exception(); + } + } + + /* + * Build a parse tree from the given sourceString. + * + * @return an Object representing the parsed + * program. If the parse fails, null will be returned. (The + * parse failure will result in a call to the ErrorReporter from + * CompilerEnvirons.) + */ + public ScriptOrFnNode Parse(System.IO.StreamReader sourceReader, string sourceURI, int lineno) + { + this.sourceURI = sourceURI; + this.ts = new TokenStream(this, sourceReader, null, lineno); + return Parse(); + } + + ScriptOrFnNode Parse() + { + this.decompiler = CreateDecompiler(compilerEnv); + this.nf = new NodeFactory(this); + currentScriptOrFn = nf.CreateScript(); + int sourceStartOffset = decompiler.CurrentOffset; + this.encodedSource = null; + decompiler.AddToken(Token.SCRIPT); + + this.currentFlaggedToken = Token.EOF; + this.syntaxErrorCount = 0; + + int baseLineno = ts.Lineno; // line number where source starts + + /* so we have something to add nodes to until + * we've collected all the source */ + Node pn = nf.CreateLeaf(Token.BLOCK); + + for (; ; ) + { + int tt = peekToken(); + + if (tt <= Token.EOF) + { + break; + } + + Node n; + if (tt == Token.FUNCTION) + { + consumeToken(); + try + { + n = function(calledByCompileFunction ? FunctionNode.FUNCTION_EXPRESSION : FunctionNode.FUNCTION_STATEMENT); + } + catch (ParserException) + { + break; + } + } + else + { + n = statement(); + } + nf.addChildToBack(pn, n); + } + + if (this.syntaxErrorCount != 0) + { + string msg = Convert.ToString(this.syntaxErrorCount); + msg = ScriptRuntime.GetMessage("msg.got.syntax.errors", msg); + throw errorReporter.RuntimeError(msg, sourceURI, baseLineno, null, 0); + } + + currentScriptOrFn.SourceName = sourceURI; + currentScriptOrFn.BaseLineno = baseLineno; + currentScriptOrFn.EndLineno = ts.Lineno; + + int sourceEndOffset = decompiler.CurrentOffset; + currentScriptOrFn.setEncodedSourceBounds(sourceStartOffset, sourceEndOffset); + + nf.initScript(currentScriptOrFn, pn); + + if (compilerEnv.isGeneratingSource()) + { + encodedSource = decompiler.EncodedSource; + } + this.decompiler = null; // It helps GC + + return currentScriptOrFn; + } + + /* + * The C version of this function takes an argument list, + * which doesn't seem to be needed for tree generation... + * it'd only be useful for checking argument hiding, which + * I'm not doing anyway... + */ + Node parseFunctionBody() + { + ++nestingOfFunction; + Node pn = nf.CreateBlock(ts.Lineno); + try + { + for (; ; ) + { + Node n; + int tt = peekToken(); + switch (tt) + { + + case Token.ERROR: + case Token.EOF: + case Token.RC: + + goto bodyLoop_brk; + + + case Token.FUNCTION: + consumeToken(); + n = function(FunctionNode.FUNCTION_STATEMENT); + break; + + default: + n = statement(); + break; + + } + nf.addChildToBack(pn, n); + } + + bodyLoop_brk: + ; + + } + catch (ParserException) + { + // Ignore it + } + finally + { + --nestingOfFunction; + } + + return pn; + } + + Node function(int functionType) + { + using (Helpers.StackOverflowVerifier sov = new Helpers.StackOverflowVerifier(1024)) + { + int syntheticType = functionType; + int baseLineno = ts.Lineno; // line number where source starts + + int functionSourceStart = decompiler.MarkFunctionStart(functionType); + string name; + Node memberExprNode = null; + if (matchToken(Token.NAME)) + { + name = ts.String; + decompiler.AddName(name); + if (!matchToken(Token.LP)) + { + if (compilerEnv.isAllowMemberExprAsFunctionName()) + { + // Extension to ECMA: if 'function ' does not follow + // by '(', assume starts memberExpr + Node memberExprHead = nf.CreateName(name); + name = ""; + memberExprNode = memberExprTail(false, memberExprHead); + } + mustMatchToken(Token.LP, "msg.no.paren.parms"); + } + } + else if (matchToken(Token.LP)) + { + // Anonymous function + name = ""; + } + else + { + name = ""; + if (compilerEnv.isAllowMemberExprAsFunctionName()) + { + // Note that memberExpr can not start with '(' like + // in function (1+2).toString(), because 'function (' already + // processed as anonymous function + memberExprNode = memberExpr(false); + } + mustMatchToken(Token.LP, "msg.no.paren.parms"); + } + + if (memberExprNode != null) + { + syntheticType = FunctionNode.FUNCTION_EXPRESSION; + } + + bool nested = insideFunction(); + + FunctionNode fnNode = nf.CreateFunction(name); + if (nested || nestingOfWith > 0) + { + // 1. Nested functions are not affected by the dynamic scope flag + // as dynamic scope is already a parent of their scope. + // 2. Functions defined under the with statement also immune to + // this setup, in which case dynamic scope is ignored in favor + // of with object. + fnNode.itsIgnoreDynamicScope = true; + } + + int functionIndex = currentScriptOrFn.addFunction(fnNode); + + int functionSourceEnd; + + ScriptOrFnNode savedScriptOrFn = currentScriptOrFn; + currentScriptOrFn = fnNode; + int savedNestingOfWith = nestingOfWith; + nestingOfWith = 0; + Hashtable savedLabelSet = labelSet; + labelSet = null; + ObjArray savedLoopSet = loopSet; + loopSet = null; + ObjArray savedLoopAndSwitchSet = loopAndSwitchSet; + loopAndSwitchSet = null; + + Node body; + try + { + decompiler.AddToken(Token.LP); + if (!matchToken(Token.RP)) + { + bool first = true; + do + { + if (!first) + decompiler.AddToken(Token.COMMA); + first = false; + mustMatchToken(Token.NAME, "msg.no.parm"); + string s = ts.String; + if (fnNode.hasParamOrVar(s)) + { + AddWarning("msg.dup.parms", s); + } + fnNode.addParam(s); + decompiler.AddName(s); + } + while (matchToken(Token.COMMA)); + + mustMatchToken(Token.RP, "msg.no.paren.after.parms"); + } + decompiler.AddToken(Token.RP); + + mustMatchToken(Token.LC, "msg.no.brace.body"); + decompiler.AddEol(Token.LC); + body = parseFunctionBody(); + mustMatchToken(Token.RC, "msg.no.brace.after.body"); + + decompiler.AddToken(Token.RC); + functionSourceEnd = decompiler.MarkFunctionEnd(functionSourceStart); + if (functionType != FunctionNode.FUNCTION_EXPRESSION) + { + if (compilerEnv.LanguageVersion >= Context.Versions.JS1_2) + { + // function f() {} function g() {} is not allowed in 1.2 + // or later but for compatibility with old scripts + // the check is done only if language is + // explicitly set. + // TODO: warning needed if version == VERSION_DEFAULT ? + int tt = peekTokenOrEOL(); + if (tt == Token.FUNCTION) + { + ReportError("msg.no.semi.stmt"); + } + } + // Add EOL only if function is not part of expression + // since it gets SEMI + EOL from Statement in that case + decompiler.AddToken(Token.EOL); + } + } + finally + { + loopAndSwitchSet = savedLoopAndSwitchSet; + loopSet = savedLoopSet; + labelSet = savedLabelSet; + nestingOfWith = savedNestingOfWith; + currentScriptOrFn = savedScriptOrFn; + } + + fnNode.setEncodedSourceBounds(functionSourceStart, functionSourceEnd); + fnNode.SourceName = sourceURI; + fnNode.BaseLineno = baseLineno; + fnNode.EndLineno = ts.Lineno; + + Node pn = nf.initFunction(fnNode, functionIndex, body, syntheticType); + if (memberExprNode != null) + { + pn = nf.CreateAssignment(Token.ASSIGN, memberExprNode, pn); + if (functionType != FunctionNode.FUNCTION_EXPRESSION) + { + // TOOD: check JScript behavior: should it be createExprStatement? + pn = nf.CreateExprStatementNoReturn(pn, baseLineno); + } + } + return pn; + } + } + + Node statements() + { + Node pn = nf.CreateBlock(ts.Lineno); + + int tt; + while ((tt = peekToken()) > Token.EOF && tt != Token.RC) + { + nf.addChildToBack(pn, statement()); + } + + return pn; + } + + Node condition() + { + Node pn; + mustMatchToken(Token.LP, "msg.no.paren.cond"); + decompiler.AddToken(Token.LP); + pn = expr(false); + mustMatchToken(Token.RP, "msg.no.paren.after.cond"); + decompiler.AddToken(Token.RP); + + // there's a check here in jsparse.c that corrects = to == + + return pn; + } + + // match a NAME; return null if no match. + Node matchJumpLabelName() + { + Node label = null; + + int tt = peekTokenOrEOL(); + if (tt == Token.NAME) + { + consumeToken(); + string name = ts.String; + decompiler.AddName(name); + if (labelSet != null) + { + label = (Node)labelSet[name]; + } + if (label == null) + { + ReportError("msg.undef.label"); + } + } + + return label; + } + + Node statement() + { + using (Helpers.StackOverflowVerifier sov = new Helpers.StackOverflowVerifier(512)) + { + try + { + Node pn = statementHelper(null); + if (pn != null) + { + return pn; + } + } + catch (ParserException) + { + } + } + + // skip to end of statement + int lineno = ts.Lineno; + for (; ; ) + { + int tt = peekTokenOrEOL(); + consumeToken(); + switch (tt) + { + + case Token.ERROR: + case Token.EOF: + case Token.EOL: + case Token.SEMI: + + goto guessingStatementEnd_brk; + } + } + + guessingStatementEnd_brk: + ; + + return nf.CreateExprStatement(nf.CreateName("error"), lineno); + } + + /// Whether the "catch (e: e instanceof Exception) { ... }" syntax + /// is implemented. + /// + + Node statementHelper(Node statementLabel) + { + Node pn = null; + + int tt; + + tt = peekToken(); + + switch (tt) + { + + case Token.IF: + { + consumeToken(); + + decompiler.AddToken(Token.IF); + int lineno = ts.Lineno; + Node cond = condition(); + decompiler.AddEol(Token.LC); + Node ifTrue = statement(); + Node ifFalse = null; + if (matchToken(Token.ELSE)) + { + decompiler.AddToken(Token.RC); + decompiler.AddToken(Token.ELSE); + decompiler.AddEol(Token.LC); + ifFalse = statement(); + } + decompiler.AddEol(Token.RC); + pn = nf.CreateIf(cond, ifTrue, ifFalse, lineno); + return pn; + } + + + case Token.SWITCH: + { + consumeToken(); + + decompiler.AddToken(Token.SWITCH); + int lineno = ts.Lineno; + mustMatchToken(Token.LP, "msg.no.paren.switch"); + decompiler.AddToken(Token.LP); + pn = enterSwitch(expr(false), lineno, statementLabel); + try + { + mustMatchToken(Token.RP, "msg.no.paren.after.switch"); + decompiler.AddToken(Token.RP); + mustMatchToken(Token.LC, "msg.no.brace.switch"); + decompiler.AddEol(Token.LC); + + bool hasDefault = false; + for (; ; ) + { + tt = nextToken(); + Node caseExpression; + switch (tt) + { + + case Token.RC: + + goto switchLoop_brk; + + + case Token.CASE: + decompiler.AddToken(Token.CASE); + caseExpression = expr(false); + mustMatchToken(Token.COLON, "msg.no.colon.case"); + decompiler.AddEol(Token.COLON); + break; + + + case Token.DEFAULT: + if (hasDefault) + { + ReportError("msg.double.switch.default"); + } + decompiler.AddToken(Token.DEFAULT); + hasDefault = true; + caseExpression = null; + mustMatchToken(Token.COLON, "msg.no.colon.case"); + decompiler.AddEol(Token.COLON); + break; + + + default: + ReportError("msg.bad.switch"); + + goto switchLoop_brk; + + } + + Node block = nf.CreateLeaf(Token.BLOCK); + while ((tt = peekToken()) != Token.RC && tt != Token.CASE && tt != Token.DEFAULT && tt != Token.EOF) + { + nf.addChildToBack(block, statement()); + } + + // caseExpression == null => add default lable + nf.addSwitchCase(pn, caseExpression, block); + } + + switchLoop_brk: + ; + + decompiler.AddEol(Token.RC); + nf.closeSwitch(pn); + } + finally + { + exitSwitch(); + } + return pn; + } + + + case Token.WHILE: + { + consumeToken(); + decompiler.AddToken(Token.WHILE); + + Node loop = enterLoop(statementLabel); + try + { + Node cond = condition(); + decompiler.AddEol(Token.LC); + Node body = statement(); + decompiler.AddEol(Token.RC); + pn = nf.CreateWhile(loop, cond, body); + } + finally + { + exitLoop(); + } + return pn; + } + + + case Token.DO: + { + consumeToken(); + decompiler.AddToken(Token.DO); + decompiler.AddEol(Token.LC); + + Node loop = enterLoop(statementLabel); + try + { + Node body = statement(); + decompiler.AddToken(Token.RC); + mustMatchToken(Token.WHILE, "msg.no.while.do"); + decompiler.AddToken(Token.WHILE); + Node cond = condition(); + pn = nf.CreateDoWhile(loop, body, cond); + } + finally + { + exitLoop(); + } + // Always auto-insert semicon to follow SpiderMonkey: + // It is required by EMAScript but is ignored by the rest of + // world, see bug 238945 + matchToken(Token.SEMI); + decompiler.AddEol(Token.SEMI); + return pn; + } + + + case Token.FOR: + { + consumeToken(); + bool isForEach = false; + decompiler.AddToken(Token.FOR); + + Node loop = enterLoop(statementLabel); + try + { + + Node init; // Node init is also foo in 'foo in Object' + Node cond; // Node cond is also object in 'foo in Object' + Node incr = null; // to kill warning + Node body; + + // See if this is a for each () instead of just a for () + if (matchToken(Token.NAME)) + { + decompiler.AddName(ts.String); + if (ts.String.Equals("each")) + { + isForEach = true; + } + else + { + ReportError("msg.no.paren.for"); + } + } + + mustMatchToken(Token.LP, "msg.no.paren.for"); + decompiler.AddToken(Token.LP); + tt = peekToken(); + if (tt == Token.SEMI) + { + init = nf.CreateLeaf(Token.EMPTY); + } + else + { + if (tt == Token.VAR) + { + // set init to a var list or initial + consumeToken(); // consume the 'var' token + init = variables(true); + } + else + { + init = expr(true); + } + } + + if (matchToken(Token.IN)) + { + decompiler.AddToken(Token.IN); + // 'cond' is the object over which we're iterating + cond = expr(false); + } + else + { + // ordinary for loop + mustMatchToken(Token.SEMI, "msg.no.semi.for"); + decompiler.AddToken(Token.SEMI); + if (peekToken() == Token.SEMI) + { + // no loop condition + cond = nf.CreateLeaf(Token.EMPTY); + } + else + { + cond = expr(false); + } + + mustMatchToken(Token.SEMI, "msg.no.semi.for.cond"); + decompiler.AddToken(Token.SEMI); + if (peekToken() == Token.RP) + { + incr = nf.CreateLeaf(Token.EMPTY); + } + else + { + incr = expr(false); + } + } + + mustMatchToken(Token.RP, "msg.no.paren.for.ctrl"); + decompiler.AddToken(Token.RP); + decompiler.AddEol(Token.LC); + body = statement(); + decompiler.AddEol(Token.RC); + + if (incr == null) + { + // cond could be null if 'in obj' got eaten + // by the init node. + pn = nf.CreateForIn(loop, init, cond, body, isForEach); + } + else + { + pn = nf.CreateFor(loop, init, cond, incr, body); + } + } + finally + { + exitLoop(); + } + return pn; + } + + + case Token.TRY: + { + consumeToken(); + int lineno = ts.Lineno; + + Node tryblock; + Node catchblocks = null; + Node finallyblock = null; + + decompiler.AddToken(Token.TRY); + decompiler.AddEol(Token.LC); + tryblock = statement(); + decompiler.AddEol(Token.RC); + + catchblocks = nf.CreateLeaf(Token.BLOCK); + + bool sawDefaultCatch = false; + int peek = peekToken(); + if (peek == Token.CATCH) + { + while (matchToken(Token.CATCH)) + { + if (sawDefaultCatch) + { + ReportError("msg.catch.unreachable"); + } + decompiler.AddToken(Token.CATCH); + mustMatchToken(Token.LP, "msg.no.paren.catch"); + decompiler.AddToken(Token.LP); + + mustMatchToken(Token.NAME, "msg.bad.catchcond"); + string varName = ts.String; + decompiler.AddName(varName); + + Node catchCond = null; + if (matchToken(Token.IF)) + { + decompiler.AddToken(Token.IF); + catchCond = expr(false); + } + else + { + sawDefaultCatch = true; + } + + mustMatchToken(Token.RP, "msg.bad.catchcond"); + decompiler.AddToken(Token.RP); + mustMatchToken(Token.LC, "msg.no.brace.catchblock"); + decompiler.AddEol(Token.LC); + + nf.addChildToBack(catchblocks, nf.CreateCatch(varName, catchCond, statements(), ts.Lineno)); + + mustMatchToken(Token.RC, "msg.no.brace.after.body"); + decompiler.AddEol(Token.RC); + } + } + else if (peek != Token.FINALLY) + { + mustMatchToken(Token.FINALLY, "msg.try.no.catchfinally"); + } + + if (matchToken(Token.FINALLY)) + { + decompiler.AddToken(Token.FINALLY); + decompiler.AddEol(Token.LC); + finallyblock = statement(); + decompiler.AddEol(Token.RC); + } + + pn = nf.CreateTryCatchFinally(tryblock, catchblocks, finallyblock, lineno); + + return pn; + } + + + case Token.THROW: + { + consumeToken(); + if (peekTokenOrEOL() == Token.EOL) + { + // ECMAScript does not allow new lines before throw expression, + // see bug 256617 + ReportError("msg.bad.throw.eol"); + } + + int lineno = ts.Lineno; + decompiler.AddToken(Token.THROW); + pn = nf.CreateThrow(expr(false), lineno); + break; + } + + + case Token.BREAK: + { + consumeToken(); + int lineno = ts.Lineno; + + decompiler.AddToken(Token.BREAK); + + // matchJumpLabelName only matches if there is one + Node breakStatement = matchJumpLabelName(); + if (breakStatement == null) + { + if (loopAndSwitchSet == null || loopAndSwitchSet.size() == 0) + { + ReportError("msg.bad.break"); + return null; + } + breakStatement = (Node)loopAndSwitchSet.peek(); + } + pn = nf.CreateBreak(breakStatement, lineno); + break; + } + + + case Token.CONTINUE: + { + consumeToken(); + int lineno = ts.Lineno; + + decompiler.AddToken(Token.CONTINUE); + + Node loop; + // matchJumpLabelName only matches if there is one + Node label = matchJumpLabelName(); + if (label == null) + { + if (loopSet == null || loopSet.size() == 0) + { + ReportError("msg.continue.outside"); + return null; + } + loop = (Node)loopSet.peek(); + } + else + { + loop = nf.getLabelLoop(label); + if (loop == null) + { + ReportError("msg.continue.nonloop"); + return null; + } + } + pn = nf.CreateContinue(loop, lineno); + break; + } + + + case Token.WITH: + { + consumeToken(); + + decompiler.AddToken(Token.WITH); + int lineno = ts.Lineno; + mustMatchToken(Token.LP, "msg.no.paren.with"); + decompiler.AddToken(Token.LP); + Node obj = expr(false); + mustMatchToken(Token.RP, "msg.no.paren.after.with"); + decompiler.AddToken(Token.RP); + decompiler.AddEol(Token.LC); + + ++nestingOfWith; + Node body; + try + { + body = statement(); + } + finally + { + --nestingOfWith; + } + + decompiler.AddEol(Token.RC); + + pn = nf.CreateWith(obj, body, lineno); + return pn; + } + + + case Token.VAR: + { + consumeToken(); + pn = variables(false); + break; + } + + + case Token.RETURN: + { + if (!insideFunction()) + { + ReportError("msg.bad.return"); + } + consumeToken(); + decompiler.AddToken(Token.RETURN); + int lineno = ts.Lineno; + + Node retExpr; + /* This is ugly, but we don't want to require a semicolon. */ + tt = peekTokenOrEOL(); + switch (tt) + { + + case Token.SEMI: + case Token.RC: + case Token.EOF: + case Token.EOL: + case Token.ERROR: + retExpr = null; + break; + + default: + retExpr = expr(false); + break; + + } + pn = nf.CreateReturn(retExpr, lineno); + break; + } + + case Token.DEBUGGER: + consumeToken(); + decompiler.AddToken(Token.DEBUGGER); + pn = nf.CreateDebugger(ts.Lineno); + break; + + case Token.LC: + consumeToken(); + if (statementLabel != null) + { + decompiler.AddToken(Token.LC); + } + pn = statements(); + mustMatchToken(Token.RC, "msg.no.brace.block"); + if (statementLabel != null) + { + decompiler.AddEol(Token.RC); + } + return pn; + + + case Token.ERROR: + // Fall thru, to have a node for error recovery to work on + case Token.SEMI: + consumeToken(); + pn = nf.CreateLeaf(Token.EMPTY); + return pn; + + + case Token.FUNCTION: + { + consumeToken(); + pn = function(FunctionNode.FUNCTION_EXPRESSION_STATEMENT); + return pn; + } + + + case Token.DEFAULT: + consumeToken(); + mustHaveXML(); + + decompiler.AddToken(Token.DEFAULT); + int nsLine = ts.Lineno; + + if (!(matchToken(Token.NAME) && ts.String.Equals("xml"))) + { + ReportError("msg.bad.namespace"); + } + decompiler.AddName(ts.String); + + if (!(matchToken(Token.NAME) && ts.String.Equals("namespace"))) + { + ReportError("msg.bad.namespace"); + } + decompiler.AddName(ts.String); + + if (!matchToken(Token.ASSIGN)) + { + ReportError("msg.bad.namespace"); + } + decompiler.AddToken(Token.ASSIGN); + + Node e = expr(false); + pn = nf.CreateDefaultNamespace(e, nsLine); + break; + + + case Token.NAME: + { + int lineno = ts.Lineno; + string name = ts.String; + setCheckForLabel(); + pn = expr(false); + if (pn.Type != Token.LABEL) + { + + if (compilerEnv.getterAndSetterSupport) + { + tt = peekToken(); + if (tt == Token.NAME) + { + if (ts.String == "getter" || ts.String == "setter") + { + pn.Type = (ts.String[0] == 'g' ? Token.SETPROP_GETTER + : Token.SETPROP_SETTER); + decompiler.AddName(" " + ts.String); // HACK: Hack (whitespace) for decmpiler + consumeToken(); + matchToken(Token.ASSIGN); + decompiler.AddToken(Token.ASSIGN); + matchToken(Token.FUNCTION); + Node fn = function(FunctionNode.FUNCTION_EXPRESSION); + pn.addChildToBack(fn); + } + } + } + pn = nf.CreateExprStatement(pn, lineno); + } + else + { + // Parsed the label: push back token should be + // colon that primaryExpr left untouched. + if (peekToken() != Token.COLON) + Context.CodeBug(); + consumeToken(); + // depend on decompiling lookahead to guess that that + // last name was a label. + decompiler.AddName(name); + decompiler.AddEol(Token.COLON); + + if (labelSet == null) + { + labelSet = Hashtable.Synchronized(new Hashtable()); + } + else if (labelSet.ContainsKey(name)) + { + ReportError("msg.dup.label"); + } + + bool firstLabel; + if (statementLabel == null) + { + firstLabel = true; + statementLabel = pn; + } + else + { + // Discard multiple label nodes and use only + // the first: it allows to simplify IRFactory + firstLabel = false; + } + labelSet[name] = statementLabel; + try + { + pn = statementHelper(statementLabel); + } + finally + { + labelSet.Remove(name); + } + if (firstLabel) + { + pn = nf.CreateLabeledStatement(statementLabel, pn); + } + return pn; + } + break; + } + + + default: + { + int lineno = ts.Lineno; + pn = expr(false); + pn = nf.CreateExprStatement(pn, lineno); + break; + } + + } + + // FINDME + + int ttFlagged = peekFlaggedToken(); + switch (ttFlagged & CLEAR_TI_MASK) + { + + case Token.SEMI: + // Consume ';' as a part of expression + consumeToken(); + break; + + case Token.ERROR: + case Token.EOF: + case Token.RC: + // Autoinsert ; + break; + + default: + if ((ttFlagged & TI_AFTER_EOL) == 0) + { + // Report error if no EOL or autoinsert ; otherwise + ReportError("msg.no.semi.stmt"); + } + break; + + } + decompiler.AddEol(Token.SEMI); + + return pn; + } + + Node variables(bool inForInit) + { + Node pn = nf.CreateVariables(ts.Lineno); + bool first = true; + + decompiler.AddToken(Token.VAR); + + for (; ; ) + { + Node name; + Node init; + mustMatchToken(Token.NAME, "msg.bad.var"); + string s = ts.String; + + if (!first) + decompiler.AddToken(Token.COMMA); + first = false; + + decompiler.AddName(s); + currentScriptOrFn.addVar(s); + name = nf.CreateName(s); + + // omitted check for argument hiding + + if (matchToken(Token.ASSIGN)) + { + decompiler.AddToken(Token.ASSIGN); + + init = assignExpr(inForInit); + nf.addChildToBack(name, init); + } + nf.addChildToBack(pn, name); + if (!matchToken(Token.COMMA)) + break; + } + return pn; + } + + Node expr(bool inForInit) + { + Node pn = assignExpr(inForInit); + while (matchToken(Token.COMMA)) + { + decompiler.AddToken(Token.COMMA); + pn = nf.CreateBinary(Token.COMMA, pn, assignExpr(inForInit)); + } + return pn; + } + + Node assignExpr(bool inForInit) + { + Node pn = condExpr(inForInit); + + int tt = peekToken(); + if (Token.FIRST_ASSIGN <= tt && tt <= Token.LAST_ASSIGN) + { + consumeToken(); + decompiler.AddToken(tt); + pn = nf.CreateAssignment(tt, pn, assignExpr(inForInit)); + } + + return pn; + } + + Node condExpr(bool inForInit) + { + Node ifTrue; + Node ifFalse; + + Node pn = orExpr(inForInit); + + if (matchToken(Token.HOOK)) + { + decompiler.AddToken(Token.HOOK); + ifTrue = assignExpr(false); + mustMatchToken(Token.COLON, "msg.no.colon.cond"); + decompiler.AddToken(Token.COLON); + ifFalse = assignExpr(inForInit); + return nf.CreateCondExpr(pn, ifTrue, ifFalse); + } + + return pn; + } + + Node orExpr(bool inForInit) + { + Node pn = andExpr(inForInit); + if (matchToken(Token.OR)) + { + decompiler.AddToken(Token.OR); + pn = nf.CreateBinary(Token.OR, pn, orExpr(inForInit)); + } + + return pn; + } + + Node andExpr(bool inForInit) + { + Node pn = bitOrExpr(inForInit); + if (matchToken(Token.AND)) + { + decompiler.AddToken(Token.AND); + pn = nf.CreateBinary(Token.AND, pn, andExpr(inForInit)); + } + + return pn; + } + + Node bitOrExpr(bool inForInit) + { + Node pn = bitXorExpr(inForInit); + while (matchToken(Token.BITOR)) + { + decompiler.AddToken(Token.BITOR); + pn = nf.CreateBinary(Token.BITOR, pn, bitXorExpr(inForInit)); + } + return pn; + } + + Node bitXorExpr(bool inForInit) + { + Node pn = bitAndExpr(inForInit); + while (matchToken(Token.BITXOR)) + { + decompiler.AddToken(Token.BITXOR); + pn = nf.CreateBinary(Token.BITXOR, pn, bitAndExpr(inForInit)); + } + return pn; + } + + Node bitAndExpr(bool inForInit) + { + Node pn = eqExpr(inForInit); + while (matchToken(Token.BITAND)) + { + decompiler.AddToken(Token.BITAND); + pn = nf.CreateBinary(Token.BITAND, pn, eqExpr(inForInit)); + } + return pn; + } + + Node eqExpr(bool inForInit) + { + Node pn = relExpr(inForInit); + for (; ; ) + { + int tt = peekToken(); + switch (tt) + { + + case Token.EQ: + case Token.NE: + case Token.SHEQ: + case Token.SHNE: + consumeToken(); + int decompilerToken = tt; + int parseToken = tt; + if (compilerEnv.LanguageVersion == Context.Versions.JS1_2) + { + // JavaScript 1.2 uses shallow equality for == and != . + // In addition, convert === and !== for decompiler into + // == and != since the decompiler is supposed to show + // canonical source and in 1.2 ===, !== are allowed + // only as an alias to ==, !=. + switch (tt) + { + + case Token.EQ: + parseToken = Token.SHEQ; + break; + + case Token.NE: + parseToken = Token.SHNE; + break; + + case Token.SHEQ: + decompilerToken = Token.EQ; + break; + + case Token.SHNE: + decompilerToken = Token.NE; + break; + } + } + decompiler.AddToken(decompilerToken); + pn = nf.CreateBinary(parseToken, pn, relExpr(inForInit)); + continue; + } + break; + } + return pn; + } + + Node relExpr(bool inForInit) + { + Node pn = shiftExpr(); + for (; ; ) + { + int tt = peekToken(); + switch (tt) + { + + case Token.IN: + if (inForInit) + break; + // fall through + goto case Token.INSTANCEOF; + + case Token.INSTANCEOF: + case Token.LE: + case Token.LT: + case Token.GE: + case Token.GT: + consumeToken(); + decompiler.AddToken(tt); + pn = nf.CreateBinary(tt, pn, shiftExpr()); + continue; + } + break; + } + return pn; + } + + Node shiftExpr() + { + Node pn = addExpr(); + for (; ; ) + { + int tt = peekToken(); + switch (tt) + { + + case Token.LSH: + case Token.URSH: + case Token.RSH: + consumeToken(); + decompiler.AddToken(tt); + pn = nf.CreateBinary(tt, pn, addExpr()); + continue; + } + break; + } + return pn; + } + + Node addExpr() + { + Node pn = mulExpr(); + for (; ; ) + { + int tt = peekToken(); + if (tt == Token.ADD || tt == Token.SUB) + { + consumeToken(); + decompiler.AddToken(tt); + // flushNewLines + pn = nf.CreateBinary(tt, pn, mulExpr()); + continue; + } + break; + } + + return pn; + } + + Node mulExpr() + { + Node pn = unaryExpr(); + for (; ; ) + { + int tt = peekToken(); + switch (tt) + { + + case Token.MUL: + case Token.DIV: + case Token.MOD: + consumeToken(); + decompiler.AddToken(tt); + pn = nf.CreateBinary(tt, pn, unaryExpr()); + continue; + } + break; + } + + return pn; + } + + Node unaryExpr() + { + using (Helpers.StackOverflowVerifier sov = new Helpers.StackOverflowVerifier(4096)) + { + int tt; + + tt = peekToken(); + + switch (tt) + { + + case Token.VOID: + case Token.NOT: + case Token.BITNOT: + case Token.TYPEOF: + consumeToken(); + decompiler.AddToken(tt); + return nf.CreateUnary(tt, unaryExpr()); + + + case Token.ADD: + consumeToken(); + // Convert to special POS token in decompiler and parse tree + decompiler.AddToken(Token.POS); + return nf.CreateUnary(Token.POS, unaryExpr()); + + + case Token.SUB: + consumeToken(); + // Convert to special NEG token in decompiler and parse tree + decompiler.AddToken(Token.NEG); + return nf.CreateUnary(Token.NEG, unaryExpr()); + + + case Token.INC: + case Token.DEC: + consumeToken(); + decompiler.AddToken(tt); + return nf.CreateIncDec(tt, false, memberExpr(true)); + + + case Token.DELPROP: + consumeToken(); + decompiler.AddToken(Token.DELPROP); + return nf.CreateUnary(Token.DELPROP, unaryExpr()); + + + case Token.ERROR: + consumeToken(); + break; + + // XML stream encountered in expression. + + case Token.LT: + if (compilerEnv.isXmlAvailable()) + { + consumeToken(); + Node pn = xmlInitializer(); + return memberExprTail(true, pn); + } + // Fall thru to the default handling of RELOP + goto default; + + + default: + { + Node pn = memberExpr(true); + + // Don't look across a newline boundary for a postfix incop. + tt = peekTokenOrEOL(); + if (tt == Token.INC || tt == Token.DEC) + { + consumeToken(); + decompiler.AddToken(tt); + return nf.CreateIncDec(tt, true, pn); + } + return pn; + } + + } + return nf.CreateName("err"); // Only reached on error. Try to continue. + } + } + + Node xmlInitializer() + { + int tt = ts.FirstXMLToken; + if (tt != Token.XML && tt != Token.XMLEND) + { + ReportError("msg.syntax"); + return null; + } + + /* Make a NEW node to append to. */ + Node pnXML = nf.CreateLeaf(Token.NEW); + decompiler.AddToken(Token.NEW); + decompiler.AddToken(Token.DOT); + + string xml = ts.String; + bool fAnonymous = xml.Trim().StartsWith("<>"); + + decompiler.AddName(fAnonymous ? "XMLList" : "XML"); + Node pn = nf.CreateName(fAnonymous ? "XMLList" : "XML"); + nf.addChildToBack(pnXML, pn); + + pn = null; + Node e; + for (; ; tt = ts.NextXMLToken) + { + switch (tt) + { + + case Token.XML: + xml = ts.String; + decompiler.AddString(xml); + mustMatchToken(Token.LC, "msg.syntax"); + decompiler.AddToken(Token.LC); + e = (peekToken() == Token.RC) ? nf.CreateString("") : expr(false); + mustMatchToken(Token.RC, "msg.syntax"); + decompiler.AddToken(Token.RC); + if (pn == null) + { + pn = nf.CreateString(xml); + } + else + { + pn = nf.CreateBinary(Token.ADD, pn, nf.CreateString(xml)); + } + int nodeType; + if (ts.XMLAttribute) + { + nodeType = Token.ESCXMLATTR; + } + else + { + nodeType = Token.ESCXMLTEXT; + } + e = nf.CreateUnary(nodeType, e); + pn = nf.CreateBinary(Token.ADD, pn, e); + break; + + case Token.XMLEND: + xml = ts.String; + decompiler.AddString(xml); + if (pn == null) + { + pn = nf.CreateString(xml); + } + else + { + pn = nf.CreateBinary(Token.ADD, pn, nf.CreateString(xml)); + } + + nf.addChildToBack(pnXML, pn); + return pnXML; + + default: + ReportError("msg.syntax"); + return null; + + } + } + } + + void argumentList(Node listNode) + { + bool matched; + matched = matchToken(Token.RP); + if (!matched) + { + bool first = true; + do + { + if (!first) + decompiler.AddToken(Token.COMMA); + first = false; + nf.addChildToBack(listNode, assignExpr(false)); + } + while (matchToken(Token.COMMA)); + + mustMatchToken(Token.RP, "msg.no.paren.arg"); + } + decompiler.AddToken(Token.RP); + } + + Node memberExpr(bool allowCallSyntax) + { + int tt; + + Node pn; + + /* Check for new expressions. */ + tt = peekToken(); + if (tt == Token.NEW) + { + /* Eat the NEW token. */ + consumeToken(); + decompiler.AddToken(Token.NEW); + + /* Make a NEW node to append to. */ + pn = nf.CreateCallOrNew(Token.NEW, memberExpr(false)); + + if (matchToken(Token.LP)) + { + decompiler.AddToken(Token.LP); + /* Add the arguments to pn, if any are supplied. */ + argumentList(pn); + } + + // TODO: there's a check in the C source against + // TODO: "too many constructor arguments" - how many + // TODO: do we claim to support? + + /* Experimental syntax: allow an object literal to follow a new expression, + * which will mean a kind of anonymous class built with the JavaAdapter. + * the object literal will be passed as an additional argument to the constructor. + */ + tt = peekToken(); + if (tt == Token.LC) + { + nf.addChildToBack(pn, primaryExpr()); + } + } + else + { + pn = primaryExpr(); + } + + return memberExprTail(allowCallSyntax, pn); + } + + Node memberExprTail(bool allowCallSyntax, Node pn) + { + for (; ; ) + { + int tt = peekToken(); + switch (tt) + { + + + case Token.DOT: + case Token.DOTDOT: + { + int memberTypeFlags; + string s; + Match match; + + consumeToken(); + decompiler.AddToken(tt); + memberTypeFlags = 0; + if (tt == Token.DOTDOT) + { + mustHaveXML(); + memberTypeFlags = Node.DESCENDANTS_FLAG; + } + if (!compilerEnv.isXmlAvailable()) + { + mustMatchToken(Token.NAME, "msg.no.name.after.dot"); + s = ts.String; + decompiler.AddName(s); + pn = nf.CreatePropertyGet(pn, null, s, memberTypeFlags); + break; + } + + + tt = nextToken(); + + switch (tt) + { + + // handles: name, ns::name, ns::*, ns::[expr] + case Token.NAME: + s = ts.String; + decompiler.AddName(s); + pn = propertyName(pn, s, memberTypeFlags); + break; + + // handles: *, *::name, *::*, *::[expr] + + case Token.MUL: + decompiler.AddName("*"); + pn = propertyName(pn, "*", memberTypeFlags); + break; + + // handles: '@attr', '@ns::attr', '@ns::*', '@ns::*', + // '@::attr', '@::*', '@*', '@*::attr', '@*::*' + + case Token.XMLATTR: + decompiler.AddToken(Token.XMLATTR); + pn = attributeAccess(pn, memberTypeFlags); + break; + + + default: + + + s = ts.TokenString; + match = SIMPLE_IDENTIFIER_NAME_PATTERN.Match(s); + if (match.Success) + { + decompiler.AddName(s); + pn = propertyName(pn, s, memberTypeFlags); + AddWarning("msg.reserved.keyword", s); + } else + ReportError("msg.no.name.after.dot"); + + break; + + } + } + break; + + + case Token.DOTQUERY: + consumeToken(); + mustHaveXML(); + decompiler.AddToken(Token.DOTQUERY); + pn = nf.CreateDotQuery(pn, expr(false), ts.Lineno); + mustMatchToken(Token.RP, "msg.no.paren"); + decompiler.AddToken(Token.RP); + break; + + + case Token.LB: + consumeToken(); + decompiler.AddToken(Token.LB); + pn = nf.CreateElementGet(pn, null, expr(false), 0); + mustMatchToken(Token.RB, "msg.no.bracket.index"); + decompiler.AddToken(Token.RB); + break; + + + case Token.LP: + if (!allowCallSyntax) + { + + goto tailLoop_brk; + } + consumeToken(); + decompiler.AddToken(Token.LP); + pn = nf.CreateCallOrNew(Token.CALL, pn); + /* Add the arguments to pn, if any are supplied. */ + argumentList(pn); + break; + + + default: + + goto tailLoop_brk; + + } + } + + tailLoop_brk: + ; + + return pn; + } + + /* + * Xml attribute expression: + * '@attr', '@ns::attr', '@ns::*', '@ns::*', '@*', '@*::attr', '@*::*' + */ + Node attributeAccess(Node pn, int memberTypeFlags) + { + memberTypeFlags |= Node.ATTRIBUTE_FLAG; + int tt = nextToken(); + + switch (tt) + { + + // handles: @name, @ns::name, @ns::*, @ns::[expr] + case Token.NAME: + { + string s = ts.String; + decompiler.AddName(s); + pn = propertyName(pn, s, memberTypeFlags); + } + break; + + // handles: @*, @*::name, @*::*, @*::[expr] + + case Token.MUL: + decompiler.AddName("*"); + pn = propertyName(pn, "*", memberTypeFlags); + break; + + // handles @[expr] + + case Token.LB: + decompiler.AddToken(Token.LB); + pn = nf.CreateElementGet(pn, null, expr(false), memberTypeFlags); + mustMatchToken(Token.RB, "msg.no.bracket.index"); + decompiler.AddToken(Token.RB); + break; + + + default: + ReportError("msg.no.name.after.xmlAttr"); + pn = nf.CreatePropertyGet(pn, null, "?", memberTypeFlags); + break; + + } + + return pn; + } + + /// Check if :: follows name in which case it becomes qualified name + Node propertyName(Node pn, string name, int memberTypeFlags) + { + string ns = null; + if (matchToken(Token.COLONCOLON)) + { + decompiler.AddToken(Token.COLONCOLON); + ns = name; + + int tt = nextToken(); + switch (tt) + { + + // handles name::name + case Token.NAME: + name = ts.String; + decompiler.AddName(name); + break; + + // handles name::* + + case Token.MUL: + decompiler.AddName("*"); + name = "*"; + break; + + // handles name::[expr] + + case Token.LB: + decompiler.AddToken(Token.LB); + pn = nf.CreateElementGet(pn, ns, expr(false), memberTypeFlags); + mustMatchToken(Token.RB, "msg.no.bracket.index"); + decompiler.AddToken(Token.RB); + return pn; + + + default: + ReportError("msg.no.name.after.coloncolon"); + name = "?"; + break; + + } + } + + pn = nf.CreatePropertyGet(pn, ns, name, memberTypeFlags); + return pn; + } + + int currentStackIndex = 0; + + + Node primaryExpr() + { + try + { + if (currentStackIndex++ > ScriptRuntime.MAXSTACKSIZE) + { + currentStackIndex = 0; + throw Context.ReportRuntimeError( + ScriptRuntime.GetMessage("mag.too.deep.parser.recursion"), sourceURI, ts.Lineno, null, 0); + } + + Node pn; + + int ttFlagged = nextFlaggedToken(); + int tt = ttFlagged & CLEAR_TI_MASK; + + switch (tt) + { + + + case Token.FUNCTION: + return function(FunctionNode.FUNCTION_EXPRESSION); + + + case Token.LB: + { + ObjArray elems = new ObjArray(); + int skipCount = 0; + decompiler.AddToken(Token.LB); + bool after_lb_or_comma = true; + for (; ; ) + { + tt = peekToken(); + + if (tt == Token.COMMA) + { + consumeToken(); + decompiler.AddToken(Token.COMMA); + if (!after_lb_or_comma) + { + after_lb_or_comma = true; + } + else + { + elems.add((object)null); + ++skipCount; + } + } + else if (tt == Token.RB) + { + consumeToken(); + decompiler.AddToken(Token.RB); + break; + } + else + { + if (!after_lb_or_comma) + { + ReportError("msg.no.bracket.arg"); + } + elems.add(assignExpr(false)); + after_lb_or_comma = false; + } + } + return nf.CreateArrayLiteral(elems, skipCount); + } + + + case Token.LC: + { + ObjArray elems = new ObjArray(); + decompiler.AddToken(Token.LC); + if (!matchToken(Token.RC)) + { + + bool first = true; + do + { + object property; + + if (!first) + decompiler.AddToken(Token.COMMA); + else + first = false; + + tt = peekToken(); + switch (tt) + { + + case Token.NAME: + case Token.STRING: + consumeToken(); + if (compilerEnv.getterAndSetterSupport) + { + if (tt == Token.NAME) + if (CheckForGetOrSet(elems) || CheckForGetterOrSetter(elems)) + goto next_prop; + } + + + // map NAMEs to STRINGs in object literal context + // but tell the decompiler the proper type + string s = ts.String; + if (tt == Token.NAME) + { + decompiler.AddName(s); + } + else + { + decompiler.AddString(s); + } + property = ScriptRuntime.getIndexObject(s); + + break; + + + case Token.NUMBER: + consumeToken(); + double n = ts.Number; + decompiler.AddNumber(n); + property = ScriptRuntime.getIndexObject(n); + break; + + + case Token.RC: + // trailing comma is OK. + + goto commaloop_brk; + + default: + ReportError("msg.bad.prop"); + + goto commaloop_brk; + + } + mustMatchToken(Token.COLON, "msg.no.colon.prop"); + + // OBJLIT is used as ':' in object literal for + // decompilation to solve spacing ambiguity. + decompiler.AddToken(Token.OBJECTLIT); + elems.add(property); + elems.add(assignExpr(false)); + + next_prop: + ; + } + while (matchToken(Token.COMMA)); + + commaloop_brk: + ; + + + mustMatchToken(Token.RC, "msg.no.brace.prop"); + } + decompiler.AddToken(Token.RC); + return nf.CreateObjectLiteral(elems); + } + + + case Token.LP: + + /* Brendan's IR-jsparse.c makes a new node tagged with + * TOK_LP here... I'm not sure I understand why. Isn't + * the grouping already implicit in the structure of the + * parse tree? also TOK_LP is already overloaded (I + * think) in the C IR as 'function call.' */ + decompiler.AddToken(Token.LP); + pn = expr(false); + decompiler.AddToken(Token.RP); + mustMatchToken(Token.RP, "msg.no.paren"); + return pn; + + + case Token.XMLATTR: + mustHaveXML(); + decompiler.AddToken(Token.XMLATTR); + pn = attributeAccess(null, 0); + return pn; + + + case Token.NAME: + { + string name = ts.String; + if ((ttFlagged & TI_CHECK_LABEL) != 0) + { + if (peekToken() == Token.COLON) + { + // Do not consume colon, it is used as unwind indicator + // to return to statementHelper. + // TODO: Better way? + return nf.CreateLabel(ts.Lineno); + } + } + + decompiler.AddName(name); + if (compilerEnv.isXmlAvailable()) + { + pn = propertyName(null, name, 0); + } + else + { + pn = nf.CreateName(name); + } + return pn; + } + + + case Token.NUMBER: + { + double n = ts.Number; + decompiler.AddNumber(n); + return nf.CreateNumber(n); + } + + + case Token.STRING: + { + string s = ts.String; + decompiler.AddString(s); + return nf.CreateString(s); + } + + + case Token.DIV: + case Token.ASSIGN_DIV: + { + // Got / or /= which should be treated as regexp in fact + ts.readRegExp(tt); + string flags = ts.regExpFlags; + ts.regExpFlags = null; + string re = ts.String; + decompiler.AddRegexp(re, flags); + int index = currentScriptOrFn.addRegexp(re, flags); + return nf.CreateRegExp(index); + } + + + case Token.NULL: + case Token.THIS: + case Token.FALSE: + case Token.TRUE: + decompiler.AddToken(tt); + return nf.CreateLeaf(tt); + + + case Token.RESERVED: + ReportError("msg.reserved.id"); + break; + + + case Token.ERROR: + /* the scanner or one of its subroutines reported the error. */ + break; + + + case Token.EOF: + ReportError("msg.unexpected.eof"); + break; + + + default: + ReportError("msg.syntax"); + break; + + } + return null; // should never reach here + } + finally + { + currentStackIndex--; + } + } + + + /// + /// Support for non-ecma "get"/"set" spidermonkey extension. + /// + /// + /// get NAME () SCOPE + /// set NAME () SCOPE + /// + bool CheckForGetOrSet(ObjArray elems) + { + int tt; + + string type = ts.String; + if (type != "get" && type != "set") + { + return false; + } + tt = peekToken(); + if (tt != Token.NAME) + return false; + consumeToken(); + + string name = ts.String; + + decompiler.AddName(name); + + Node func = function(FunctionNode.FUNCTION_EXPRESSION); + object property = ScriptRuntime.getIndexObject(name); + + elems.add((type[0] == 'g') ? (object)new Node.GetterPropertyLiteral(property) + : (object)new Node.SetterPropertyLiteral(property)); + elems.add(func); + + return true; + } + + /// + /// Support for non-ecma "get"/"set" spidermonkey extension. + /// + /// + /// NAME getter: FUNCTION () SCOPE + /// NAME setter: FUNCTION () SCOPE + /// + bool CheckForGetterOrSetter(ObjArray elems) + { + int tt; + + string name = ts.String; + consumeToken(); + + tt = peekToken(); + if (tt != Token.NAME) + return false; + string type = ts.String; + if (type != "getter" && type != "setter") + { + return false; + } + consumeToken(); + + matchToken(Token.COLON); + matchToken(Token.FUNCTION); + + Node func = function(FunctionNode.FUNCTION_EXPRESSION); + object property = ScriptRuntime.getIndexObject(name); + + elems.add((type[0] == 'g') ? (object)new Node.GetterPropertyLiteral(property) + : (object)new Node.SetterPropertyLiteral(property)); + elems.add(func); + + return true; + } + + } } \ No newline at end of file diff --git a/Code/EcmaScript.NET/RegExpProxy.cs b/src/EcmaScript.NET/RegExpProxy.cs similarity index 96% rename from Code/EcmaScript.NET/RegExpProxy.cs rename to src/EcmaScript.NET/RegExpProxy.cs index 9769fbf..c9b9b7a 100644 --- a/Code/EcmaScript.NET/RegExpProxy.cs +++ b/src/EcmaScript.NET/RegExpProxy.cs @@ -1,46 +1,46 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; - -namespace EcmaScript.NET -{ - - - public enum RegExpActions - { - None = 0, - Match = 1, - Replace = 2, - Search = 3 - } - - /// - /// A proxy for the regexp package, so that the regexp package can be - /// loaded optionally. - /// - public interface RegExpProxy - { - - bool IsRegExp (IScriptable obj); - - object Compile (Context cx, string source, string flags); - - IScriptable Wrap (Context cx, IScriptable scope, object compiled); - - object Perform (Context cx, IScriptable scope, IScriptable thisObj, object [] args, RegExpActions actionType); - - int FindSplit (Context cx, IScriptable scope, string target, string separator, IScriptable re, int [] ip, int [] matchlen, bool [] matched, string [] [] parensp); - - } +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; + +namespace EcmaScript.NET +{ + + + public enum RegExpActions + { + None = 0, + Match = 1, + Replace = 2, + Search = 3 + } + + /// + /// A proxy for the regexp package, so that the regexp package can be + /// loaded optionally. + /// + public interface RegExpProxy + { + + bool IsRegExp (IScriptable obj); + + object Compile (Context cx, string source, string flags); + + IScriptable Wrap (Context cx, IScriptable scope, object compiled); + + object Perform (Context cx, IScriptable scope, IScriptable thisObj, object [] args, RegExpActions actionType); + + int FindSplit (Context cx, IScriptable scope, string target, string separator, IScriptable re, int [] ip, int [] matchlen, bool [] matched, string [] [] parensp); + + } } \ No newline at end of file diff --git a/Code/EcmaScript.NET/Resources/ChangeLog b/src/EcmaScript.NET/Resources/ChangeLog similarity index 97% rename from Code/EcmaScript.NET/Resources/ChangeLog rename to src/EcmaScript.NET/Resources/ChangeLog index def77fd..58da71b 100644 --- a/Code/EcmaScript.NET/Resources/ChangeLog +++ b/src/EcmaScript.NET/Resources/ChangeLog @@ -1,35 +1,35 @@ -2007-01-01 Christian Birkl - - * Added 'it' object - * typeof /x/ must return object, not function - -2006-12-30 Christian Birkl - - * Added Helpers.StackOverflowVerifier to prevent StackOverflowExceptions - which aren't cachable in .NET 2.0 - -2006-12-30 Christian Birkl - - * Added "gc" function to NativeGlobalObject - - * Duplicate regexp quantifiers now raise a SyntaxError (e.g. /x{1}{1}/) - - * Ported bugfix for https://bugzilla.mozilla.org/show_bug.cgi?id=289628 - - * Fixed some OutOfMemoryExceptions in Array.concat/push/... - by adding OutOfRange checks. - -2006-12-29 Christian Birkl - - * First public release (based partly on Rhino 1.6R5) - - * Major Changes are: - - - Removed support for compiling javascript sources - - Removed LiveConnect and replaced it with a new .NET layer - (See EcmaScript.NET.Types.Cli) - - Rewrote E4X Support completely since it was based on - javax.beans.* framework which has no counterpart in .NET. - - Added support JavaScript 1.5 getter and setter - - .NET'ished some bits here and there (e.g. refactoring methods +2007-01-01 Christian Birkl + + * Added 'it' object + * typeof /x/ must return object, not function + +2006-12-30 Christian Birkl + + * Added Helpers.StackOverflowVerifier to prevent StackOverflowExceptions + which aren't cachable in .NET 2.0 + +2006-12-30 Christian Birkl + + * Added "gc" function to NativeGlobalObject + + * Duplicate regexp quantifiers now raise a SyntaxError (e.g. /x{1}{1}/) + + * Ported bugfix for https://bugzilla.mozilla.org/show_bug.cgi?id=289628 + + * Fixed some OutOfMemoryExceptions in Array.concat/push/... + by adding OutOfRange checks. + +2006-12-29 Christian Birkl + + * First public release (based partly on Rhino 1.6R5) + + * Major Changes are: + + - Removed support for compiling javascript sources + - Removed LiveConnect and replaced it with a new .NET layer + (See EcmaScript.NET.Types.Cli) + - Rewrote E4X Support completely since it was based on + javax.beans.* framework which has no counterpart in .NET. + - Added support JavaScript 1.5 getter and setter + - .NET'ished some bits here and there (e.g. refactoring methods into properties and events) \ No newline at end of file diff --git a/Code/EcmaScript.NET/Resources/LICENSE b/src/EcmaScript.NET/Resources/LICENSE similarity index 97% rename from Code/EcmaScript.NET/Resources/LICENSE rename to src/EcmaScript.NET/Resources/LICENSE index 9b53555..b2b3b0a 100644 --- a/Code/EcmaScript.NET/Resources/LICENSE +++ b/src/EcmaScript.NET/Resources/LICENSE @@ -1,34 +1,34 @@ -/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Rhino code, released - * May 6, 1999. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1997-1999 - * the Initial Developer. All Rights Reserved. - * - * Alternatively, the contents of this file may be used under the terms of - * the GNU General Public License Version 2 or later (the "GPL"), in which - * case the provisions of the GPL are applicable instead of those above. If - * you wish to allow use of your version of this file only under the terms of - * the GPL and not to allow others to use your version of this file under the - * MPL, indicate your decision by deleting the provisions above and replacing - * them with the notice and other provisions required by the GPL. If you do - * not delete the provisions above, a recipient may use your version of this - * file under either the MPL or the GPL. - * +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * * ***** END LICENSE BLOCK ***** */ \ No newline at end of file diff --git a/Code/EcmaScript.NET/Resources/Messages.resx b/src/EcmaScript.NET/Resources/Messages.resx similarity index 97% rename from Code/EcmaScript.NET/Resources/Messages.resx rename to src/EcmaScript.NET/Resources/Messages.resx index b8b8d4c..02a6c08 100644 --- a/Code/EcmaScript.NET/Resources/Messages.resx +++ b/src/EcmaScript.NET/Resources/Messages.resx @@ -1,675 +1,675 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - missing name after . operator - - - missing ( before function parameters. - - - no source found to decompile function reference {0} - - - missing } in compound statement - - - missing ) after formal parameters - - - missing name after .. operator - - - Program too complex: too big jump offset. - - - invalid property id - - - illegal character - - - Invalid increment operand. - - - Function {0} can not be used as the left-hand side of assignment or as an operand of ++ or -- operator. - - - unterminated string literal - - - Construction of objects of type "{0}" is not supported. - - - Internal error: type conversion of {0} to assign to {1} on {2} failed. - - - XML runtime not available - - - @IMPLEMENTATION.VERSION@ - - - missing ] in index expression - - - Java method "{0}" was invoked with {1} as "this" value that can not be converted to Java type {2}. - - - Method "{0}" must be static with the signature "(Context cx, Scriptable thisObj, Object[] args, Function funObj)" to define a variable arguments function. - - - Unterminated character class {0} - - - {0} must extend ScriptableObject in order to define property {1}. - - - missing ) after for-loop control - - - {0} is not a function, it is {1}. - - - invalid return - - - unmatched ) in regular expression. - - - In order to define a property, getter {0} must have zero parameters or a single ScriptableObject parameter. - - - Calling eval() with anything other than a primitive string value will simply return the value. Is this what you intended? - - - Function "{0}" must be called directly, and not by way of a function of another name. - - - missing ) after with-statement object - - - invalid Unicode escape sequence - - - Invalid assignment left-hand side. - - - missing '{' before catch-block body - - - no input for {0} - - - Cannot apply "with" to {0} - - - Calling eval() with anything other than a primitive string value is not allowed in the strict mode. - - - undefined labe - - - Only one argument may be specified if the first argument to RegExp.prototype.compile is a RegExp object. - - - Cannot import "{0}" since a property by that name is already defined. - - - Cyclic {0} value not allowed. - - - missing ; before statement - - - Two-parameter setter must take a ScriptableObject as its first parameter. - - - Java package names may not be numbers. - - - missing } after property list - - - Too deep recursion while parsing - - - Unterminated quantifier {0} - - - Program too complex: internal index exceeds 64K limit. - - - Unterminated parenthetical {0} - - - Attempt to assign non-existing name "{0}" in the strict mode. It could indicate a missing variable statement. - - - invalid switch statement - - - illegal octal literal digit {0}; interpreting it as a decimal digit - - - missing name after :: operator - - - missing ( after for - - - error instantiating ({0}): class {1} is interface or abstract - - - Cannot convert {0} to interface {1} with no methods - - - Function importPackage must be called with a package; had "{0}" instead. - - - Can''t find converter method "{0}" on class {1}. - - - Script objects are not constructors. - - - "{0}" may only be invoked from a "new" expression. - - - Maximum {0} less than minimum - - - "{0}" is not a constructor. - - - Array length {0} exceeds supported capacity limit. - - - Method "{0}" called on incompatible object. - - - missing : in conditional expression - - - Cannot convert undefined to an object. - - - missing ( before condition - - - Object''s getDefaultValue() method returned an object. - - - Invalid quantifier {0} - - - Regular expressions are not available. - - - Compilation produced {0} syntax errors. - - - illegally formed XML syntax - - - Overly large back reference {0} - - - Argument {0} is not Java class: {1}. - - - Cannot modify a property of a sealed object: {0}. - - - The "{0}" constructor is deprecated. - - - duplicatet label - - - missing } after function body - - - not a valid default namespace statement. Syntax is: default xml namespace = EXPRESSION; - - - Trailing \ in regular expression. - - - missing formal parameter - - - missing ) after switch expression - - - Primitive type expected (had {0} instead) - - - "{0}" is not defined. - - - missing '{' before switch body - - - invalid catch block condition - - - ''prototype'' property of {0} is not an object. - - - missing ) after condition - - - Can''t find method {0}. - - - The undefined value has no properties. - - - {0} is not a reference to read reference value. - - - Unsupported return type "{0}" in method "{1}". - - - Invalid JavaScript value of type {0} - - - missing ( before catch-block condition - - - Can''t define constructor or class {0} since more than one constructor has multiple parameters. - - - Cannot add a property to a sealed object: {0}. - - - Expected static or delegated setter {0} to take two parameters. - - - Malformed URI sequence. - - - continue can only use labeles of iteration statements - - - Namespace object expected to left of :: (found {0} instead) - - - Cannot set property "{1}" of {0} to "{2}" - - - Unsupported parameter type "{0}" in setter "{1}". - - - back-reference exceeds number of capturing parentheses. - - - Cannot convert {0} to {1} - - - Cannot remove a property from a sealed object: {0}. - - - Invalid left-hand side of for..in loop. - - - Unsupported parameter type "{0}" in method "{1}". - - - Invalid range in character class. - - - Ambiguous import: "{0}" and and "{1}". - - - Method or constructor "{0}" must be static with the signature "(Context cx, Object[] args, Function ctorObj, boolean inNewExpr)" to define a variable arguments constructor. - - - missing ; after for-loop condition - - - Expected either one or two parameters for setter. - - - Cannot delete property "{1}" of {0} - - - Function importClass must be called with a class; had "{0}" instead. - - - missing : after property id - - - Cannot find default value for object. - - - Cannot convert null to an object. - - - invalid label - - - missing variable name - - - Can''t convert to type "{0}". - - - Cannot read property "{1}" from {0} - - - Cannot convert function {0} to interface since it contains methods with different signatures - - - Method "{0}" occurs multiple times in class "{1}". - - - Cannot call method "{1}" of {0} - - - unlabelled break must be inside loop or switch - - - Getter and setter must both be static or neither be static. - - - missing ( before with-statement object - - - Only one variable allowed in for..in loop. - - - missing ; after for-loop initializer - - - identifier is a reserved word - - - Only one class may be extended by a JavaAdapter. Had {0} and {1}. - - - invalid variable initialization - - - JavaAdapter requires at least one argument. - - - illegal usage of future reserved keyword {0}; interpreting it as ordinary identifier - - - Unexpected end of file - - - missing name after .@ - - - Can''t use instanceof on a non-object. - - - missing ) after argument list - - - missing ) in parenthetical - - - missing exponent - - - Invalid decerement operand. - - - missing ( before switch expression - - - unterminated comment - - - Zero quantifier {0} - - - illegal radix {0}. - - - invalid string escape mask - - - Duplicate parameter name "{0}". - - - Java class "{0}" has no public instance field or method named "{1}". - - - Expected static or delegated getter {0} to take a ScriptableObject parameter. - - - Line terminator is not allowed between the throw keyword and throw expression. - - - Method "{0}" not found in "{1}". - - - The choice of Java constructor {0} matching JavaScript argument types ({1}) is ambiguous; candidate constructors are: {2} - - - Internal error: attempt to access private/protected field "{0}". - - - Cannot load class "{0}" which has no zero-parameter constructor. - - - invalid flag after regular expression - - - Property {0} not found. - - - Expected argument to getClass() to be a Java object. - - - number format error - - - second argument to Function.prototype.apply must be an array - - - {0} is not an xml object. - - - The choice of Java method {0}.{1} matching JavaScript argument types ({2}) is ambiguous; candidate methods are: {3} - - - Constructor for "Packages" expects argument of type java.lang.Classloader - - - missing : after case expression - - - any catch clauses following an unqualified catch are unreachable - - - double default label in the switch statement - - - unterminated regular expression literal - - - continue must be inside loop - - - function "{0}" redeclared; prior definition will be ignored - - - Expected single parameter setter for {0} - - - Constructor for "{0}" not found. - - - syntax error - - - Overly large minimum {0} - - - missing '{' before function body - - - missing ] after element list - - - Found constructor with wrong signature: {0} calling {1} with signature {2} - - - Inappropriate array length. - - - missing while after do-loop body - - - Java constructor for "{0}" with arguments "{1}" not found. - - - ''try'' without ''catch'' or ''finally'' - - - Precision {0} out of range. - - - Java method "{0}" cannot be assigned to. - - - Cannot find function {0}. - - - Overly large maximum {0} - - - {0} is not a reference to set reference value tpo {1}. - - - Exceeded maximum stack depth - - - invalid instanceof usage - - - {0} is read-only - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + missing name after . operator + + + missing ( before function parameters. + + + no source found to decompile function reference {0} + + + missing } in compound statement + + + missing ) after formal parameters + + + missing name after .. operator + + + Program too complex: too big jump offset. + + + invalid property id + + + illegal character + + + Invalid increment operand. + + + Function {0} can not be used as the left-hand side of assignment or as an operand of ++ or -- operator. + + + unterminated string literal + + + Construction of objects of type "{0}" is not supported. + + + Internal error: type conversion of {0} to assign to {1} on {2} failed. + + + XML runtime not available + + + @IMPLEMENTATION.VERSION@ + + + missing ] in index expression + + + Java method "{0}" was invoked with {1} as "this" value that can not be converted to Java type {2}. + + + Method "{0}" must be static with the signature "(Context cx, Scriptable thisObj, Object[] args, Function funObj)" to define a variable arguments function. + + + Unterminated character class {0} + + + {0} must extend ScriptableObject in order to define property {1}. + + + missing ) after for-loop control + + + {0} is not a function, it is {1}. + + + invalid return + + + unmatched ) in regular expression. + + + In order to define a property, getter {0} must have zero parameters or a single ScriptableObject parameter. + + + Calling eval() with anything other than a primitive string value will simply return the value. Is this what you intended? + + + Function "{0}" must be called directly, and not by way of a function of another name. + + + missing ) after with-statement object + + + invalid Unicode escape sequence + + + Invalid assignment left-hand side. + + + missing '{' before catch-block body + + + no input for {0} + + + Cannot apply "with" to {0} + + + Calling eval() with anything other than a primitive string value is not allowed in the strict mode. + + + undefined labe + + + Only one argument may be specified if the first argument to RegExp.prototype.compile is a RegExp object. + + + Cannot import "{0}" since a property by that name is already defined. + + + Cyclic {0} value not allowed. + + + missing ; before statement + + + Two-parameter setter must take a ScriptableObject as its first parameter. + + + Java package names may not be numbers. + + + missing } after property list + + + Too deep recursion while parsing + + + Unterminated quantifier {0} + + + Program too complex: internal index exceeds 64K limit. + + + Unterminated parenthetical {0} + + + Attempt to assign non-existing name "{0}" in the strict mode. It could indicate a missing variable statement. + + + invalid switch statement + + + illegal octal literal digit {0}; interpreting it as a decimal digit + + + missing name after :: operator + + + missing ( after for + + + error instantiating ({0}): class {1} is interface or abstract + + + Cannot convert {0} to interface {1} with no methods + + + Function importPackage must be called with a package; had "{0}" instead. + + + Can''t find converter method "{0}" on class {1}. + + + Script objects are not constructors. + + + "{0}" may only be invoked from a "new" expression. + + + Maximum {0} less than minimum + + + "{0}" is not a constructor. + + + Array length {0} exceeds supported capacity limit. + + + Method "{0}" called on incompatible object. + + + missing : in conditional expression + + + Cannot convert undefined to an object. + + + missing ( before condition + + + Object''s getDefaultValue() method returned an object. + + + Invalid quantifier {0} + + + Regular expressions are not available. + + + Compilation produced {0} syntax errors. + + + illegally formed XML syntax + + + Overly large back reference {0} + + + Argument {0} is not Java class: {1}. + + + Cannot modify a property of a sealed object: {0}. + + + The "{0}" constructor is deprecated. + + + duplicatet label + + + missing } after function body + + + not a valid default namespace statement. Syntax is: default xml namespace = EXPRESSION; + + + Trailing \ in regular expression. + + + missing formal parameter + + + missing ) after switch expression + + + Primitive type expected (had {0} instead) + + + "{0}" is not defined. + + + missing '{' before switch body + + + invalid catch block condition + + + ''prototype'' property of {0} is not an object. + + + missing ) after condition + + + Can''t find method {0}. + + + The undefined value has no properties. + + + {0} is not a reference to read reference value. + + + Unsupported return type "{0}" in method "{1}". + + + Invalid JavaScript value of type {0} + + + missing ( before catch-block condition + + + Can''t define constructor or class {0} since more than one constructor has multiple parameters. + + + Cannot add a property to a sealed object: {0}. + + + Expected static or delegated setter {0} to take two parameters. + + + Malformed URI sequence. + + + continue can only use labeles of iteration statements + + + Namespace object expected to left of :: (found {0} instead) + + + Cannot set property "{1}" of {0} to "{2}" + + + Unsupported parameter type "{0}" in setter "{1}". + + + back-reference exceeds number of capturing parentheses. + + + Cannot convert {0} to {1} + + + Cannot remove a property from a sealed object: {0}. + + + Invalid left-hand side of for..in loop. + + + Unsupported parameter type "{0}" in method "{1}". + + + Invalid range in character class. + + + Ambiguous import: "{0}" and and "{1}". + + + Method or constructor "{0}" must be static with the signature "(Context cx, Object[] args, Function ctorObj, boolean inNewExpr)" to define a variable arguments constructor. + + + missing ; after for-loop condition + + + Expected either one or two parameters for setter. + + + Cannot delete property "{1}" of {0} + + + Function importClass must be called with a class; had "{0}" instead. + + + missing : after property id + + + Cannot find default value for object. + + + Cannot convert null to an object. + + + invalid label + + + missing variable name + + + Can''t convert to type "{0}". + + + Cannot read property "{1}" from {0} + + + Cannot convert function {0} to interface since it contains methods with different signatures + + + Method "{0}" occurs multiple times in class "{1}". + + + Cannot call method "{1}" of {0} + + + unlabelled break must be inside loop or switch + + + Getter and setter must both be static or neither be static. + + + missing ( before with-statement object + + + Only one variable allowed in for..in loop. + + + missing ; after for-loop initializer + + + identifier is a reserved word + + + Only one class may be extended by a JavaAdapter. Had {0} and {1}. + + + invalid variable initialization + + + JavaAdapter requires at least one argument. + + + illegal usage of future reserved keyword {0}; interpreting it as ordinary identifier + + + Unexpected end of file + + + missing name after .@ + + + Can''t use instanceof on a non-object. + + + missing ) after argument list + + + missing ) in parenthetical + + + missing exponent + + + Invalid decerement operand. + + + missing ( before switch expression + + + unterminated comment + + + Zero quantifier {0} + + + illegal radix {0}. + + + invalid string escape mask + + + Duplicate parameter name "{0}". + + + Java class "{0}" has no public instance field or method named "{1}". + + + Expected static or delegated getter {0} to take a ScriptableObject parameter. + + + Line terminator is not allowed between the throw keyword and throw expression. + + + Method "{0}" not found in "{1}". + + + The choice of Java constructor {0} matching JavaScript argument types ({1}) is ambiguous; candidate constructors are: {2} + + + Internal error: attempt to access private/protected field "{0}". + + + Cannot load class "{0}" which has no zero-parameter constructor. + + + invalid flag after regular expression + + + Property {0} not found. + + + Expected argument to getClass() to be a Java object. + + + number format error + + + second argument to Function.prototype.apply must be an array + + + {0} is not an xml object. + + + The choice of Java method {0}.{1} matching JavaScript argument types ({2}) is ambiguous; candidate methods are: {3} + + + Constructor for "Packages" expects argument of type java.lang.Classloader + + + missing : after case expression + + + any catch clauses following an unqualified catch are unreachable + + + double default label in the switch statement + + + unterminated regular expression literal + + + continue must be inside loop + + + function "{0}" redeclared; prior definition will be ignored + + + Expected single parameter setter for {0} + + + Constructor for "{0}" not found. + + + syntax error + + + Overly large minimum {0} + + + missing '{' before function body + + + missing ] after element list + + + Found constructor with wrong signature: {0} calling {1} with signature {2} + + + Inappropriate array length. + + + missing while after do-loop body + + + Java constructor for "{0}" with arguments "{1}" not found. + + + ''try'' without ''catch'' or ''finally'' + + + Precision {0} out of range. + + + Java method "{0}" cannot be assigned to. + + + Cannot find function {0}. + + + Overly large maximum {0} + + + {0} is not a reference to set reference value tpo {1}. + + + Exceeded maximum stack depth + + + invalid instanceof usage + + + {0} is read-only + \ No newline at end of file diff --git a/Code/EcmaScript.NET/Resources/TODO b/src/EcmaScript.NET/Resources/TODO similarity index 96% rename from Code/EcmaScript.NET/Resources/TODO rename to src/EcmaScript.NET/Resources/TODO index c273548..ac550d4 100644 --- a/Code/EcmaScript.NET/Resources/TODO +++ b/src/EcmaScript.NET/Resources/TODO @@ -1,36 +1,36 @@ -* Fix "NumberToString" Conversions (my math knowledge is too basic to figure out - how to write an algorithm which generates correct toString () representation of double) - -* DateTime tests fails with wrong (?) timezone (daylight saving time). - -* Remove ErrorReporter and make events (OnError, OnWarning) - -* Way too much public methods/classes - -* Way too much virtual methods - -* Fix Date Tests - -* Fix E4X part - -* .NET'ish lower case named methods - -* .NET'ish getXXX and setXXX methods - -* Tell Rhino guys about bugfixes made (e.g. /x{1}{1}/, RangeChecks in Array, ...) - -* Implement full JS 1.6 and JS 1.7 - -* Find a better solution for handling StackOverflowExceptions than using StackOverflowVerifier. - -* Fix Interpreter.InterpreterLoop throw (Excpetion)throwable which screwes up StackTrace - -* I've broken Array.toString () (reverted to original state - see #if FALSE in BuiltinArray.cs) - -* Merge NativeGlobal and NativeGlobalObject - -* Implement __noSucheMethod__ handler (see bug #196097) - -* Implement "LongRunningScript" Handler - -* Implement "const" keyword (http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Statements:const) +* Fix "NumberToString" Conversions (my math knowledge is too basic to figure out + how to write an algorithm which generates correct toString () representation of double) + +* DateTime tests fails with wrong (?) timezone (daylight saving time). + +* Remove ErrorReporter and make events (OnError, OnWarning) + +* Way too much public methods/classes + +* Way too much virtual methods + +* Fix Date Tests + +* Fix E4X part + +* .NET'ish lower case named methods + +* .NET'ish getXXX and setXXX methods + +* Tell Rhino guys about bugfixes made (e.g. /x{1}{1}/, RangeChecks in Array, ...) + +* Implement full JS 1.6 and JS 1.7 + +* Find a better solution for handling StackOverflowExceptions than using StackOverflowVerifier. + +* Fix Interpreter.InterpreterLoop throw (Excpetion)throwable which screwes up StackTrace + +* I've broken Array.toString () (reverted to original state - see #if FALSE in BuiltinArray.cs) + +* Merge NativeGlobal and NativeGlobalObject + +* Implement __noSucheMethod__ handler (see bug #196097) + +* Implement "LongRunningScript" Handler + +* Implement "const" keyword (http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Statements:const) diff --git a/Code/EcmaScript.NET/ScriptConvert.cs b/src/EcmaScript.NET/ScriptConvert.cs similarity index 97% rename from Code/EcmaScript.NET/ScriptConvert.cs rename to src/EcmaScript.NET/ScriptConvert.cs index 642f84f..f4071a0 100644 --- a/Code/EcmaScript.NET/ScriptConvert.cs +++ b/src/EcmaScript.NET/ScriptConvert.cs @@ -1,660 +1,660 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; -using System.Text; -using System.Globalization; - -namespace EcmaScript.NET -{ - - public sealed class ScriptConvert - { - - - /// - /// If character c is a hexadecimal digit, return - /// accumulator * 16 plus corresponding - /// number. Otherise return -1. - /// - internal static int XDigitToInt (int c, int accumulator) - { - { - // Use 0..9 < A..Z < a..z - if (c <= '9') { - c -= '0'; - if (0 <= c) { - - goto check_brk; - } - } - else if (c <= 'F') { - if ('A' <= c) { - c -= ('A' - 10); - - goto check_brk; - } - } - else if (c <= 'f') { - if ('a' <= c) { - c -= ('a' - 10); - - goto check_brk; - } - } - return -1; - } - - check_brk: - ; - - return (accumulator << 4) | c; - } - - - public static IScriptable ToObject (IScriptable scope, object val) - { - if (val is IScriptable) { - return (IScriptable)val; - } - return ToObject (null, scope, val); - } - - public static IScriptable ToObjectOrNull (Context cx, object obj) - { - if (obj is IScriptable) { - return (IScriptable)obj; - } - else if (obj != null && obj != Undefined.Value) { - return ToObject (cx, ScriptRuntime.getTopCallScope (cx), obj); - } - return null; - } - - /// Convert the value to an object. - /// - /// See ECMA 9.9. - /// - public static IScriptable ToObject (Context cx, IScriptable scope, object val) - { - if (val is IScriptable) { - return (IScriptable)val; - } - if (val == null) { - throw ScriptRuntime.TypeErrorById ("msg.null.to.object"); - } - if (val == Undefined.Value) { - throw ScriptRuntime.TypeErrorById ("msg.undef.to.object"); - } - string className = val is string ? "String" : (CliHelper.IsNumber (val) ? "Number" : (val is bool ? "Boolean" : null)); - if (className != null) { - object [] args = new object [] { val }; - scope = ScriptableObject.GetTopLevelScope (scope); - return ScriptRuntime.NewObject (cx == null ? Context.CurrentContext : cx, scope, className, args); - } - - // Extension: Wrap as a LiveConnect object. - object wrapped = cx.Wrap (scope, val, null); - if (wrapped is IScriptable) - return (IScriptable)wrapped; - throw ScriptRuntime.errorWithClassName ("msg.invalid.type", val); - } - - - /// - /// See ECMA 9.4. - /// - public static double ToInteger (object val) - { - return ToInteger (ToNumber (val)); - } - - // convenience method - public static double ToInteger (double d) - { - // if it's double.NaN - if (double.IsNaN (d)) - return +0.0; - - if (d == 0.0 || d == System.Double.PositiveInfinity || d == System.Double.NegativeInfinity) - return d; - - if (d > 0.0) - return Math.Floor (d); - else - return Math.Ceiling (d); - } - - public static double ToInteger (object [] args, int index) - { - return (index < args.Length) ? ToInteger (args [index]) : +0.0; - } - - /// - /// See ECMA 9.5. - /// - public static int ToInt32 (object val) - { - // short circuit for common integer values - if (val is int) - return ((int)val); - - return ToInt32 (ToNumber (val)); - } - - public static int ToInt32 (object [] args, int index) - { - return (index < args.Length) ? ToInt32 (args [index]) : 0; - } - - public static int ToInt32 (double d) - { - int id = (int)d; - if (id == d) { - // This covers -0.0 as well - return id; - } - - if (double.IsNaN (d) || d == System.Double.PositiveInfinity || d == System.Double.NegativeInfinity) { - return 0; - } - - d = (d >= 0) ? Math.Floor (d) : Math.Ceiling (d); - - double two32 = 4294967296.0; - d = Math.IEEERemainder (d, two32); - // (double)(long)d == d should hold here - - long l = (long)d; - // returning (int)d does not work as d can be outside int range - // but the result must always be 32 lower bits of l - return (int)l; - } - - /// See ECMA 9.6. - /// long value representing 32 bits unsigned integer - /// - public static long ToUint32 (double d) - { - long l = (long)d; - if (l == d) { - // This covers -0.0 as well - return l & 0xffffffffL; - } - - if (double.IsNaN (d) || d == System.Double.PositiveInfinity || d == System.Double.NegativeInfinity) { - return 0; - } - - d = (d >= 0) ? Math.Floor (d) : Math.Ceiling (d); - - double two32 = 4294967296.0; - l = (long)Math.IEEERemainder (d, two32); - unchecked { - return l & (int)0xffffffffL; - } - } - - public static long ToUint32 (object val) - { - return ToUint32 (ToNumber (val)); - } - - - /// Convert the value to a boolean. - /// - /// See ECMA 9.2. - /// - public static bool ToBoolean (object val) - { - for (; ; ) { - if (val is bool) - return ((bool)val); - if (val == null || val == Undefined.Value) - return false; - if (val is string) - return ((string)val).Length != 0; - if (CliHelper.IsNumber (val)) { - double d = Convert.ToDouble (val); - return (!double.IsNaN (d) && d != 0.0); - } - if (val is IScriptable) { - if (Context.CurrentContext.VersionECMA1) { - // pure ECMA - return true; - } - // ECMA extension - val = ((IScriptable)val).GetDefaultValue (typeof (bool)); - if (val is IScriptable) - throw ScriptRuntime.errorWithClassName ("msg.primitive.expected", val); - continue; - } - ScriptRuntime.WarnAboutNonJSObject (val); - return true; - } - } - - public static bool ToBoolean (object [] args, int index) - { - return (index < args.Length) ? ToBoolean (args [index]) : false; - } - - /// Convert the value to a number. - /// - /// See ECMA 9.3. - /// - public static double ToNumber (object val) - { - for (; ; ) { - if (val is double) - return (double)val; - if (CliHelper.IsNumber (val)) - return Convert.ToDouble (val); - if (val == null) - return +0.0; - if (val == Undefined.Value) - return double.NaN; - if (val is string) - return ToNumber ((string)val); - if (val is bool) - return ((bool)val) ? 1 : +0.0; - if (val is IScriptable) { - val = ((IScriptable)val).GetDefaultValue (typeof (long)); - if (val is IScriptable) - throw ScriptRuntime.errorWithClassName ("msg.primitive.expected", val); - continue; - } - ScriptRuntime.WarnAboutNonJSObject (val); - return double.NaN; - } - } - - public static double ToNumber (object [] args, int index) - { - return (index < args.Length) ? ToNumber (args [index]) : double.NaN; - } - - internal static double ToNumber (string s, int start, int radix) - { - char digitMax = '9'; - char lowerCaseBound = 'a'; - char upperCaseBound = 'A'; - int len = s.Length; - if (radix < 10) { - digitMax = (char)('0' + radix - 1); - } - if (radix > 10) { - lowerCaseBound = (char)('a' + radix - 10); - upperCaseBound = (char)('A' + radix - 10); - } - int end; - double sum = 0.0; - for (end = start; end < len; end++) { - char c = s [end]; - int newDigit; - if ('0' <= c && c <= digitMax) - newDigit = c - '0'; - else if ('a' <= c && c < lowerCaseBound) - newDigit = c - 'a' + 10; - else if ('A' <= c && c < upperCaseBound) - newDigit = c - 'A' + 10; - else - break; - sum = sum * radix + newDigit; - } - if (start == end) { - return double.NaN; - } - if (sum >= 9007199254740992.0) { - if (radix == 10) { - /* If we're accumulating a decimal number and the number - * is >= 2^53, then the result from the repeated multiply-add - * above may be inaccurate. Call Java to get the correct - * answer. - */ - try { - return System.Double.Parse (s.Substring (start, (end) - (start))); - } - catch (System.FormatException) { - return double.NaN; - } - } - else if (radix == 2 || radix == 4 || radix == 8 || radix == 16 || radix == 32) { - /* The number may also be inaccurate for one of these bases. - * This happens if the addition in value*radix + digit causes - * a round-down to an even least significant mantissa bit - * when the first dropped bit is a one. If any of the - * following digits in the number (which haven't been added - * in yet) are nonzero then the correct action would have - * been to round up instead of down. An example of this - * occurs when reading the number 0x1000000000000081, which - * rounds to 0x1000000000000000 instead of 0x1000000000000100. - */ - int bitShiftInChar = 1; - int digit = 0; - - const int SKIP_LEADING_ZEROS = 0; - const int FIRST_EXACT_53_BITS = 1; - const int AFTER_BIT_53 = 2; - const int ZEROS_AFTER_54 = 3; - const int MIXED_AFTER_54 = 4; - - int state = SKIP_LEADING_ZEROS; - int exactBitsLimit = 53; - double factor = 0.0; - bool bit53 = false; - // bit54 is the 54th bit (the first dropped from the mantissa) - bool bit54 = false; - - for (; ; ) { - if (bitShiftInChar == 1) { - if (start == end) - break; - digit = s [start++]; - if ('0' <= digit && digit <= '9') - digit -= '0'; - else if ('a' <= digit && digit <= 'z') - digit -= ('a' - 10); - else - digit -= ('A' - 10); - bitShiftInChar = radix; - } - bitShiftInChar >>= 1; - bool bit = (digit & bitShiftInChar) != 0; - - switch (state) { - - case SKIP_LEADING_ZEROS: - if (bit) { - --exactBitsLimit; - sum = 1.0; - state = FIRST_EXACT_53_BITS; - } - break; - - case FIRST_EXACT_53_BITS: - sum *= 2.0; - if (bit) - sum += 1.0; - --exactBitsLimit; - if (exactBitsLimit == 0) { - bit53 = bit; - state = AFTER_BIT_53; - } - break; - - case AFTER_BIT_53: - bit54 = bit; - factor = 2.0; - state = ZEROS_AFTER_54; - break; - - case ZEROS_AFTER_54: - if (bit) { - state = MIXED_AFTER_54; - } - // fallthrough - goto case MIXED_AFTER_54; - - case MIXED_AFTER_54: - factor *= 2; - break; - } - } - switch (state) { - - case SKIP_LEADING_ZEROS: - sum = 0.0; - break; - - case FIRST_EXACT_53_BITS: - case AFTER_BIT_53: - // do nothing - break; - - case ZEROS_AFTER_54: - // x1.1 -> x1 + 1 (round up) - // x0.1 -> x0 (round down) - if (bit54 & bit53) - sum += 1.0; - sum *= factor; - break; - - case MIXED_AFTER_54: - // x.100...1.. -> x + 1 (round up) - // x.0anything -> x (round down) - if (bit54) - sum += 1.0; - sum *= factor; - break; - } - } - /* We don't worry about inaccurate numbers for any other base. */ - } - return sum; - } - - - public static string ToString (object [] args, int index) - { - return (index < args.Length) ? ToString (args [index]) : "undefined"; - } - - internal static object ToPrimitive (object val) - { - if (!(val is IScriptable)) { - return val; - } - IScriptable s = (IScriptable)val; - object result = s.GetDefaultValue (null); - if (result is IScriptable) - throw ScriptRuntime.TypeErrorById ("msg.bad.default.value"); - return result; - } - - /// Convert the value to a string. - /// - /// See ECMA 9.8. - /// - public static string ToString (object val) - { - for (; ; ) { - if (val == null) { - return "null"; - } - if (val == Undefined.Value) { - return "undefined"; - } - if (val is string) { - return (string)val; - } - if (val is Boolean) - return ((bool)val) ? "true" : "false"; - if (CliHelper.IsNumber (val)) { - // TODO: should we just teach NativeNumber.stringValue() - // TODO: about Numbers? - return ToString (Convert.ToDouble (val), 10); - } - if (val is IScriptable) { - val = ((IScriptable)val).GetDefaultValue (typeof (string)); - if (val is IScriptable) { - throw ScriptRuntime.errorWithClassName ("msg.primitive.expected", val); - } - continue; - } - return val.ToString (); - } - } - - - - /// ToNumber applied to the String type - /// - /// See ECMA 9.3.1 - /// - public static double ToNumber (string input) - { - int len = input.Length; - int start = 0; - char [] chars = input.ToCharArray (); - char startChar; - for (; ; ) { - if (start == len) { - // Empty or contains only whitespace - return +0.0; - } - startChar = chars [start]; - if (!char.IsWhiteSpace (startChar)) - break; - start++; - } - - if (startChar == '0') { - if (start + 2 < len) { - int c1 = chars [start + 1]; - if (c1 == 'x' || c1 == 'X') { - // A hexadecimal number - return ToNumber (input, start + 2, 16); - } - } - } - else if (startChar == '+' || startChar == '-') { - if (start + 3 < len && chars [start + 1] == '0') { - int c2 = chars [start + 2]; - if (c2 == 'x' || c2 == 'X') { - // A hexadecimal number with sign - double val = ToNumber (input, start + 3, 16); - return startChar == '-' ? -val : val; - } - } - } - - int end = len - 1; - char endChar; - while (char.IsWhiteSpace (endChar = chars [end])) - end--; - if (endChar == 'y') { - // check for "Infinity" - if (startChar == '+' || startChar == '-') - start++; - if (start + 7 == end && String.Compare (input, start, "Infinity", 0, 8) == 0) - return startChar == '-' ? System.Double.NegativeInfinity : System.Double.PositiveInfinity; - return double.NaN; - } - // A non-hexadecimal, non-infinity number: - // just try a normal floating point conversion - string sub = input.Substring (start, (end + 1) - (start)); - - // MS.NET will accept non-conformant strings - // rather than throwing a NumberFormatException - // as it should (like with \0). - for (int i = sub.Length - 1; i >= 0; i--) { - char c = sub [i]; - if (('0' <= c && c <= '9') || c == '.' || - c == 'e' || c == 'E' || - c == '+' || c == '-') - continue; - return double.NaN; - } - - try { - double ret = double.Parse (sub); - if (ret == 0) { - // IMHO a bug in MS.NET: double.Parse("-0.0") == 0.0 so we retard the "-" sign here - if (sub [0] == '-') - ret = -ret; - } - return ret; - } - catch (OverflowException) { - // HACK - if (sub [0] == '-') - return double.NegativeInfinity; - else - return double.PositiveInfinity; - } - catch (Exception) { - return double.NaN; - } - } - - /// - /// See ECMA 9.7. - /// - public static char ToUint16 (object val) - { - double d = ToNumber (val); - - int i = (int)d; - if (i == d) { - return (char)i; - } - - if (double.IsNaN (d) || d == System.Double.PositiveInfinity || d == System.Double.NegativeInfinity) { - return (char)(0); - } - - d = (d >= 0) ? Math.Floor (d) : Math.Ceiling (d); - - int int16 = 0x10000; - i = (int)Math.IEEERemainder (d, int16); - return (char)i; - } - - /// Optimized version of toString(Object) for numbers. - public static string ToString (double val) - { - return ToString (val, 10); - } - - /// - /// See ECMA 9.8.1 - /// - /// - /// - /// - public static string ToString (double d, int toBase) - { - if (double.IsNaN (d)) - return "NaN"; - if (d == System.Double.PositiveInfinity) - return "Infinity"; - if (d == System.Double.NegativeInfinity) - return "-Infinity"; - if (d == 0.0) - return "0"; - - if ((toBase < 2) || (toBase > 36)) { - throw Context.ReportRuntimeErrorById ("msg.bad.radix", Convert.ToString (toBase)); - } - - if (double.IsNaN (d)) - return "NaN"; - else if (Double.IsPositiveInfinity (d)) - return "Infinity"; - else if (Double.IsNegativeInfinity (d)) - return "-Infinity"; - else { - // BugFix: Item 9856 - g16 yields better results than "g". Not perfect, but better - string ret = d.ToString ("g16"); - // TODO: This is plain wrong, but as close as we can get - // without converting DtoA to C#. - return ret; - } - } - - - - } - -} +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; +using System.Text; +using System.Globalization; + +namespace EcmaScript.NET +{ + + public sealed class ScriptConvert + { + + + /// + /// If character c is a hexadecimal digit, return + /// accumulator * 16 plus corresponding + /// number. Otherise return -1. + /// + internal static int XDigitToInt (int c, int accumulator) + { + { + // Use 0..9 < A..Z < a..z + if (c <= '9') { + c -= '0'; + if (0 <= c) { + + goto check_brk; + } + } + else if (c <= 'F') { + if ('A' <= c) { + c -= ('A' - 10); + + goto check_brk; + } + } + else if (c <= 'f') { + if ('a' <= c) { + c -= ('a' - 10); + + goto check_brk; + } + } + return -1; + } + + check_brk: + ; + + return (accumulator << 4) | c; + } + + + public static IScriptable ToObject (IScriptable scope, object val) + { + if (val is IScriptable) { + return (IScriptable)val; + } + return ToObject (null, scope, val); + } + + public static IScriptable ToObjectOrNull (Context cx, object obj) + { + if (obj is IScriptable) { + return (IScriptable)obj; + } + else if (obj != null && obj != Undefined.Value) { + return ToObject (cx, ScriptRuntime.getTopCallScope (cx), obj); + } + return null; + } + + /// Convert the value to an object. + /// + /// See ECMA 9.9. + /// + public static IScriptable ToObject (Context cx, IScriptable scope, object val) + { + if (val is IScriptable) { + return (IScriptable)val; + } + if (val == null) { + throw ScriptRuntime.TypeErrorById ("msg.null.to.object"); + } + if (val == Undefined.Value) { + throw ScriptRuntime.TypeErrorById ("msg.undef.to.object"); + } + string className = val is string ? "String" : (CliHelper.IsNumber (val) ? "Number" : (val is bool ? "Boolean" : null)); + if (className != null) { + object [] args = new object [] { val }; + scope = ScriptableObject.GetTopLevelScope (scope); + return ScriptRuntime.NewObject (cx == null ? Context.CurrentContext : cx, scope, className, args); + } + + // Extension: Wrap as a LiveConnect object. + object wrapped = cx.Wrap (scope, val, null); + if (wrapped is IScriptable) + return (IScriptable)wrapped; + throw ScriptRuntime.errorWithClassName ("msg.invalid.type", val); + } + + + /// + /// See ECMA 9.4. + /// + public static double ToInteger (object val) + { + return ToInteger (ToNumber (val)); + } + + // convenience method + public static double ToInteger (double d) + { + // if it's double.NaN + if (double.IsNaN (d)) + return +0.0; + + if (d == 0.0 || d == System.Double.PositiveInfinity || d == System.Double.NegativeInfinity) + return d; + + if (d > 0.0) + return Math.Floor (d); + else + return Math.Ceiling (d); + } + + public static double ToInteger (object [] args, int index) + { + return (index < args.Length) ? ToInteger (args [index]) : +0.0; + } + + /// + /// See ECMA 9.5. + /// + public static int ToInt32 (object val) + { + // short circuit for common integer values + if (val is int) + return ((int)val); + + return ToInt32 (ToNumber (val)); + } + + public static int ToInt32 (object [] args, int index) + { + return (index < args.Length) ? ToInt32 (args [index]) : 0; + } + + public static int ToInt32 (double d) + { + int id = (int)d; + if (id == d) { + // This covers -0.0 as well + return id; + } + + if (double.IsNaN (d) || d == System.Double.PositiveInfinity || d == System.Double.NegativeInfinity) { + return 0; + } + + d = (d >= 0) ? Math.Floor (d) : Math.Ceiling (d); + + double two32 = 4294967296.0; + d = Math.IEEERemainder (d, two32); + // (double)(long)d == d should hold here + + long l = (long)d; + // returning (int)d does not work as d can be outside int range + // but the result must always be 32 lower bits of l + return (int)l; + } + + /// See ECMA 9.6. + /// long value representing 32 bits unsigned integer + /// + public static long ToUint32 (double d) + { + long l = (long)d; + if (l == d) { + // This covers -0.0 as well + return l & 0xffffffffL; + } + + if (double.IsNaN (d) || d == System.Double.PositiveInfinity || d == System.Double.NegativeInfinity) { + return 0; + } + + d = (d >= 0) ? Math.Floor (d) : Math.Ceiling (d); + + double two32 = 4294967296.0; + l = (long)Math.IEEERemainder (d, two32); + unchecked { + return l & (int)0xffffffffL; + } + } + + public static long ToUint32 (object val) + { + return ToUint32 (ToNumber (val)); + } + + + /// Convert the value to a boolean. + /// + /// See ECMA 9.2. + /// + public static bool ToBoolean (object val) + { + for (; ; ) { + if (val is bool) + return ((bool)val); + if (val == null || val == Undefined.Value) + return false; + if (val is string) + return ((string)val).Length != 0; + if (CliHelper.IsNumber (val)) { + double d = Convert.ToDouble (val); + return (!double.IsNaN (d) && d != 0.0); + } + if (val is IScriptable) { + if (Context.CurrentContext.VersionECMA1) { + // pure ECMA + return true; + } + // ECMA extension + val = ((IScriptable)val).GetDefaultValue (typeof (bool)); + if (val is IScriptable) + throw ScriptRuntime.errorWithClassName ("msg.primitive.expected", val); + continue; + } + ScriptRuntime.WarnAboutNonJSObject (val); + return true; + } + } + + public static bool ToBoolean (object [] args, int index) + { + return (index < args.Length) ? ToBoolean (args [index]) : false; + } + + /// Convert the value to a number. + /// + /// See ECMA 9.3. + /// + public static double ToNumber (object val) + { + for (; ; ) { + if (val is double) + return (double)val; + if (CliHelper.IsNumber (val)) + return Convert.ToDouble (val); + if (val == null) + return +0.0; + if (val == Undefined.Value) + return double.NaN; + if (val is string) + return ToNumber ((string)val); + if (val is bool) + return ((bool)val) ? 1 : +0.0; + if (val is IScriptable) { + val = ((IScriptable)val).GetDefaultValue (typeof (long)); + if (val is IScriptable) + throw ScriptRuntime.errorWithClassName ("msg.primitive.expected", val); + continue; + } + ScriptRuntime.WarnAboutNonJSObject (val); + return double.NaN; + } + } + + public static double ToNumber (object [] args, int index) + { + return (index < args.Length) ? ToNumber (args [index]) : double.NaN; + } + + internal static double ToNumber (string s, int start, int radix) + { + char digitMax = '9'; + char lowerCaseBound = 'a'; + char upperCaseBound = 'A'; + int len = s.Length; + if (radix < 10) { + digitMax = (char)('0' + radix - 1); + } + if (radix > 10) { + lowerCaseBound = (char)('a' + radix - 10); + upperCaseBound = (char)('A' + radix - 10); + } + int end; + double sum = 0.0; + for (end = start; end < len; end++) { + char c = s [end]; + int newDigit; + if ('0' <= c && c <= digitMax) + newDigit = c - '0'; + else if ('a' <= c && c < lowerCaseBound) + newDigit = c - 'a' + 10; + else if ('A' <= c && c < upperCaseBound) + newDigit = c - 'A' + 10; + else + break; + sum = sum * radix + newDigit; + } + if (start == end) { + return double.NaN; + } + if (sum >= 9007199254740992.0) { + if (radix == 10) { + /* If we're accumulating a decimal number and the number + * is >= 2^53, then the result from the repeated multiply-add + * above may be inaccurate. Call Java to get the correct + * answer. + */ + try { + return System.Double.Parse (s.Substring (start, (end) - (start))); + } + catch (System.FormatException) { + return double.NaN; + } + } + else if (radix == 2 || radix == 4 || radix == 8 || radix == 16 || radix == 32) { + /* The number may also be inaccurate for one of these bases. + * This happens if the addition in value*radix + digit causes + * a round-down to an even least significant mantissa bit + * when the first dropped bit is a one. If any of the + * following digits in the number (which haven't been added + * in yet) are nonzero then the correct action would have + * been to round up instead of down. An example of this + * occurs when reading the number 0x1000000000000081, which + * rounds to 0x1000000000000000 instead of 0x1000000000000100. + */ + int bitShiftInChar = 1; + int digit = 0; + + const int SKIP_LEADING_ZEROS = 0; + const int FIRST_EXACT_53_BITS = 1; + const int AFTER_BIT_53 = 2; + const int ZEROS_AFTER_54 = 3; + const int MIXED_AFTER_54 = 4; + + int state = SKIP_LEADING_ZEROS; + int exactBitsLimit = 53; + double factor = 0.0; + bool bit53 = false; + // bit54 is the 54th bit (the first dropped from the mantissa) + bool bit54 = false; + + for (; ; ) { + if (bitShiftInChar == 1) { + if (start == end) + break; + digit = s [start++]; + if ('0' <= digit && digit <= '9') + digit -= '0'; + else if ('a' <= digit && digit <= 'z') + digit -= ('a' - 10); + else + digit -= ('A' - 10); + bitShiftInChar = radix; + } + bitShiftInChar >>= 1; + bool bit = (digit & bitShiftInChar) != 0; + + switch (state) { + + case SKIP_LEADING_ZEROS: + if (bit) { + --exactBitsLimit; + sum = 1.0; + state = FIRST_EXACT_53_BITS; + } + break; + + case FIRST_EXACT_53_BITS: + sum *= 2.0; + if (bit) + sum += 1.0; + --exactBitsLimit; + if (exactBitsLimit == 0) { + bit53 = bit; + state = AFTER_BIT_53; + } + break; + + case AFTER_BIT_53: + bit54 = bit; + factor = 2.0; + state = ZEROS_AFTER_54; + break; + + case ZEROS_AFTER_54: + if (bit) { + state = MIXED_AFTER_54; + } + // fallthrough + goto case MIXED_AFTER_54; + + case MIXED_AFTER_54: + factor *= 2; + break; + } + } + switch (state) { + + case SKIP_LEADING_ZEROS: + sum = 0.0; + break; + + case FIRST_EXACT_53_BITS: + case AFTER_BIT_53: + // do nothing + break; + + case ZEROS_AFTER_54: + // x1.1 -> x1 + 1 (round up) + // x0.1 -> x0 (round down) + if (bit54 & bit53) + sum += 1.0; + sum *= factor; + break; + + case MIXED_AFTER_54: + // x.100...1.. -> x + 1 (round up) + // x.0anything -> x (round down) + if (bit54) + sum += 1.0; + sum *= factor; + break; + } + } + /* We don't worry about inaccurate numbers for any other base. */ + } + return sum; + } + + + public static string ToString (object [] args, int index) + { + return (index < args.Length) ? ToString (args [index]) : "undefined"; + } + + internal static object ToPrimitive (object val) + { + if (!(val is IScriptable)) { + return val; + } + IScriptable s = (IScriptable)val; + object result = s.GetDefaultValue (null); + if (result is IScriptable) + throw ScriptRuntime.TypeErrorById ("msg.bad.default.value"); + return result; + } + + /// Convert the value to a string. + /// + /// See ECMA 9.8. + /// + public static string ToString (object val) + { + for (; ; ) { + if (val == null) { + return "null"; + } + if (val == Undefined.Value) { + return "undefined"; + } + if (val is string) { + return (string)val; + } + if (val is Boolean) + return ((bool)val) ? "true" : "false"; + if (CliHelper.IsNumber (val)) { + // TODO: should we just teach NativeNumber.stringValue() + // TODO: about Numbers? + return ToString (Convert.ToDouble (val), 10); + } + if (val is IScriptable) { + val = ((IScriptable)val).GetDefaultValue (typeof (string)); + if (val is IScriptable) { + throw ScriptRuntime.errorWithClassName ("msg.primitive.expected", val); + } + continue; + } + return val.ToString (); + } + } + + + + /// ToNumber applied to the String type + /// + /// See ECMA 9.3.1 + /// + public static double ToNumber (string input) + { + int len = input.Length; + int start = 0; + char [] chars = input.ToCharArray (); + char startChar; + for (; ; ) { + if (start == len) { + // Empty or contains only whitespace + return +0.0; + } + startChar = chars [start]; + if (!char.IsWhiteSpace (startChar)) + break; + start++; + } + + if (startChar == '0') { + if (start + 2 < len) { + int c1 = chars [start + 1]; + if (c1 == 'x' || c1 == 'X') { + // A hexadecimal number + return ToNumber (input, start + 2, 16); + } + } + } + else if (startChar == '+' || startChar == '-') { + if (start + 3 < len && chars [start + 1] == '0') { + int c2 = chars [start + 2]; + if (c2 == 'x' || c2 == 'X') { + // A hexadecimal number with sign + double val = ToNumber (input, start + 3, 16); + return startChar == '-' ? -val : val; + } + } + } + + int end = len - 1; + char endChar; + while (char.IsWhiteSpace (endChar = chars [end])) + end--; + if (endChar == 'y') { + // check for "Infinity" + if (startChar == '+' || startChar == '-') + start++; + if (start + 7 == end && String.Compare (input, start, "Infinity", 0, 8) == 0) + return startChar == '-' ? System.Double.NegativeInfinity : System.Double.PositiveInfinity; + return double.NaN; + } + // A non-hexadecimal, non-infinity number: + // just try a normal floating point conversion + string sub = input.Substring (start, (end + 1) - (start)); + + // MS.NET will accept non-conformant strings + // rather than throwing a NumberFormatException + // as it should (like with \0). + for (int i = sub.Length - 1; i >= 0; i--) { + char c = sub [i]; + if (('0' <= c && c <= '9') || c == '.' || + c == 'e' || c == 'E' || + c == '+' || c == '-') + continue; + return double.NaN; + } + + try { + double ret = double.Parse (sub); + if (ret == 0) { + // IMHO a bug in MS.NET: double.Parse("-0.0") == 0.0 so we retard the "-" sign here + if (sub [0] == '-') + ret = -ret; + } + return ret; + } + catch (OverflowException) { + // HACK + if (sub [0] == '-') + return double.NegativeInfinity; + else + return double.PositiveInfinity; + } + catch (Exception) { + return double.NaN; + } + } + + /// + /// See ECMA 9.7. + /// + public static char ToUint16 (object val) + { + double d = ToNumber (val); + + int i = (int)d; + if (i == d) { + return (char)i; + } + + if (double.IsNaN (d) || d == System.Double.PositiveInfinity || d == System.Double.NegativeInfinity) { + return (char)(0); + } + + d = (d >= 0) ? Math.Floor (d) : Math.Ceiling (d); + + int int16 = 0x10000; + i = (int)Math.IEEERemainder (d, int16); + return (char)i; + } + + /// Optimized version of toString(Object) for numbers. + public static string ToString (double val) + { + return ToString (val, 10); + } + + /// + /// See ECMA 9.8.1 + /// + /// + /// + /// + public static string ToString (double d, int toBase) + { + if (double.IsNaN (d)) + return "NaN"; + if (d == System.Double.PositiveInfinity) + return "Infinity"; + if (d == System.Double.NegativeInfinity) + return "-Infinity"; + if (d == 0.0) + return "0"; + + if ((toBase < 2) || (toBase > 36)) { + throw Context.ReportRuntimeErrorById ("msg.bad.radix", Convert.ToString (toBase)); + } + + if (double.IsNaN (d)) + return "NaN"; + else if (Double.IsPositiveInfinity (d)) + return "Infinity"; + else if (Double.IsNegativeInfinity (d)) + return "-Infinity"; + else { + // BugFix: Item 9856 - g16 yields better results than "g". Not perfect, but better + string ret = d.ToString ("g16"); + // TODO: This is plain wrong, but as close as we can get + // without converting DtoA to C#. + return ret; + } + } + + + + } + +} diff --git a/Code/EcmaScript.NET/ScriptOrFnNode.cs b/src/EcmaScript.NET/ScriptOrFnNode.cs similarity index 95% rename from Code/EcmaScript.NET/ScriptOrFnNode.cs rename to src/EcmaScript.NET/ScriptOrFnNode.cs index 6e4e45c..323616b 100644 --- a/Code/EcmaScript.NET/ScriptOrFnNode.cs +++ b/src/EcmaScript.NET/ScriptOrFnNode.cs @@ -1,278 +1,278 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; - -using EcmaScript.NET.Collections; - -namespace EcmaScript.NET -{ - - public class ScriptOrFnNode : Node - { - virtual public string SourceName - { - get - { - return sourceName; - } - - set - { - this.sourceName = value; - } - - } - virtual public int EncodedSourceStart - { - get - { - return encodedSourceStart; - } - - } - virtual public int EncodedSourceEnd - { - get - { - return encodedSourceEnd; - } - - } - virtual public int BaseLineno - { - get - { - return baseLineno; - } - - set - { - // One time action - if (value < 0 || baseLineno >= 0) - Context.CodeBug (); - baseLineno = value; - } - - } - virtual public int EndLineno - { - get - { - return baseLineno; - } - - set - { - // One time action - if (value < 0 || endLineno >= 0) - Context.CodeBug (); - endLineno = value; - } - - } - virtual public int FunctionCount - { - get - { - if (functions == null) { - return 0; - } - return functions.size (); - } - - } - virtual public int RegexpCount - { - get - { - if (regexps == null) { - return 0; - } - return regexps.size () / 2; - } - - } - virtual public int ParamCount - { - get - { - return varStart; - } - - } - virtual public int ParamAndVarCount - { - get - { - return itsVariables.size (); - } - - } - virtual public string [] ParamAndVarNames - { - get - { - int N = itsVariables.size (); - if (N == 0) { - return ScriptRuntime.EmptyStrings; - } - string [] array = new string [N]; - itsVariables.ToArray (array); - return array; - } - - } - virtual public object CompilerData - { - get - { - return compilerData; - } - - set - { - if (value == null) - throw new ArgumentException (); - // Can only call once - if (compilerData != null) - throw new ApplicationException (); - compilerData = value; - } - - } - - public ScriptOrFnNode (int nodeType) - : base (nodeType) - { - } - - public void setEncodedSourceBounds (int start, int end) - { - this.encodedSourceStart = start; - this.encodedSourceEnd = end; - } - - public FunctionNode getFunctionNode (int i) - { - return (FunctionNode)functions.Get (i); - } - - public int addFunction (FunctionNode fnNode) - { - if (fnNode == null) - Context.CodeBug (); - if (functions == null) { - functions = new ObjArray (); - } - functions.add (fnNode); - return functions.size () - 1; - } - - public string getRegexpString (int index) - { - return (string)regexps.Get (index * 2); - } - - public string getRegexpFlags (int index) - { - return (string)regexps.Get (index * 2 + 1); - } - - public int addRegexp (string str, string flags) - { - if (str == null) - Context.CodeBug (); - if (regexps == null) { - regexps = new ObjArray (); - } - regexps.add (str); - regexps.add (flags); - return regexps.size () / 2 - 1; - } - - public bool hasParamOrVar (string name) - { - return itsVariableNames.has (name); - } - - public int getParamOrVarIndex (string name) - { - return itsVariableNames.Get (name, -1); - } - - public string getParamOrVarName (int index) - { - return (string)itsVariables.Get (index); - } - - public void addParam (string name) - { - // Check addparam is not called after addLocal - if (varStart != itsVariables.size ()) - Context.CodeBug (); - // Allow non-unique parameter names: use the last occurrence - int index = varStart++; - itsVariables.add (name); - itsVariableNames.put (name, index); - } - - public void addVar (string name) - { - int vIndex = itsVariableNames.Get (name, -1); - if (vIndex != -1) { - // There's already a variable or parameter with this name. - return; - } - int index = itsVariables.size (); - itsVariables.add (name); - itsVariableNames.put (name, index); - } - - public void removeParamOrVar (string name) - { - int i = itsVariableNames.Get (name, -1); - if (i != -1) { - itsVariables.remove (i); - itsVariableNames.remove (name); - ObjToIntMap.Iterator iter = itsVariableNames.newIterator (); - for (iter.start (); !iter.done (); iter.next ()) { - int v = iter.Value; - if (v > i) { - iter.Value = v - 1; - } - } - } - } - - private int encodedSourceStart; - private int encodedSourceEnd; - private string sourceName; - private int baseLineno = -1; - private int endLineno = -1; - - private ObjArray functions; - - private ObjArray regexps; - - // a list of the formal parameters and local variables - private ObjArray itsVariables = new ObjArray (); - - // mapping from name to index in list - private ObjToIntMap itsVariableNames = new ObjToIntMap (11); - - private int varStart; // index in list of first variable - - private object compilerData; - } +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; + +using EcmaScript.NET.Collections; + +namespace EcmaScript.NET +{ + + public class ScriptOrFnNode : Node + { + virtual public string SourceName + { + get + { + return sourceName; + } + + set + { + this.sourceName = value; + } + + } + virtual public int EncodedSourceStart + { + get + { + return encodedSourceStart; + } + + } + virtual public int EncodedSourceEnd + { + get + { + return encodedSourceEnd; + } + + } + virtual public int BaseLineno + { + get + { + return baseLineno; + } + + set + { + // One time action + if (value < 0 || baseLineno >= 0) + Context.CodeBug (); + baseLineno = value; + } + + } + virtual public int EndLineno + { + get + { + return baseLineno; + } + + set + { + // One time action + if (value < 0 || endLineno >= 0) + Context.CodeBug (); + endLineno = value; + } + + } + virtual public int FunctionCount + { + get + { + if (functions == null) { + return 0; + } + return functions.size (); + } + + } + virtual public int RegexpCount + { + get + { + if (regexps == null) { + return 0; + } + return regexps.size () / 2; + } + + } + virtual public int ParamCount + { + get + { + return varStart; + } + + } + virtual public int ParamAndVarCount + { + get + { + return itsVariables.size (); + } + + } + virtual public string [] ParamAndVarNames + { + get + { + int N = itsVariables.size (); + if (N == 0) { + return ScriptRuntime.EmptyStrings; + } + string [] array = new string [N]; + itsVariables.ToArray (array); + return array; + } + + } + virtual public object CompilerData + { + get + { + return compilerData; + } + + set + { + if (value == null) + throw new ArgumentException (); + // Can only call once + if (compilerData != null) + throw new Exception (); + compilerData = value; + } + + } + + public ScriptOrFnNode (int nodeType) + : base (nodeType) + { + } + + public void setEncodedSourceBounds (int start, int end) + { + this.encodedSourceStart = start; + this.encodedSourceEnd = end; + } + + public FunctionNode getFunctionNode (int i) + { + return (FunctionNode)functions.Get (i); + } + + public int addFunction (FunctionNode fnNode) + { + if (fnNode == null) + Context.CodeBug (); + if (functions == null) { + functions = new ObjArray (); + } + functions.add (fnNode); + return functions.size () - 1; + } + + public string getRegexpString (int index) + { + return (string)regexps.Get (index * 2); + } + + public string getRegexpFlags (int index) + { + return (string)regexps.Get (index * 2 + 1); + } + + public int addRegexp (string str, string flags) + { + if (str == null) + Context.CodeBug (); + if (regexps == null) { + regexps = new ObjArray (); + } + regexps.add (str); + regexps.add (flags); + return regexps.size () / 2 - 1; + } + + public bool hasParamOrVar (string name) + { + return itsVariableNames.has (name); + } + + public int getParamOrVarIndex (string name) + { + return itsVariableNames.Get (name, -1); + } + + public string getParamOrVarName (int index) + { + return (string)itsVariables.Get (index); + } + + public void addParam (string name) + { + // Check addparam is not called after addLocal + if (varStart != itsVariables.size ()) + Context.CodeBug (); + // Allow non-unique parameter names: use the last occurrence + int index = varStart++; + itsVariables.add (name); + itsVariableNames.put (name, index); + } + + public void addVar (string name) + { + int vIndex = itsVariableNames.Get (name, -1); + if (vIndex != -1) { + // There's already a variable or parameter with this name. + return; + } + int index = itsVariables.size (); + itsVariables.add (name); + itsVariableNames.put (name, index); + } + + public void removeParamOrVar (string name) + { + int i = itsVariableNames.Get (name, -1); + if (i != -1) { + itsVariables.remove (i); + itsVariableNames.remove (name); + ObjToIntMap.Iterator iter = itsVariableNames.newIterator (); + for (iter.start (); !iter.done (); iter.next ()) { + int v = iter.Value; + if (v > i) { + iter.Value = v - 1; + } + } + } + } + + private int encodedSourceStart; + private int encodedSourceEnd; + private string sourceName; + private int baseLineno = -1; + private int endLineno = -1; + + private ObjArray functions; + + private ObjArray regexps; + + // a list of the formal parameters and local variables + private ObjArray itsVariables = new ObjArray (); + + // mapping from name to index in list + private ObjToIntMap itsVariableNames = new ObjToIntMap (11); + + private int varStart; // index in list of first variable + + private object compilerData; + } } \ No newline at end of file diff --git a/Code/EcmaScript.NET/ScriptRuntime.cs b/src/EcmaScript.NET/ScriptRuntime.cs similarity index 96% rename from Code/EcmaScript.NET/ScriptRuntime.cs rename to src/EcmaScript.NET/ScriptRuntime.cs index 8b581be..7ef35fe 100644 --- a/Code/EcmaScript.NET/ScriptRuntime.cs +++ b/src/EcmaScript.NET/ScriptRuntime.cs @@ -1,2610 +1,2610 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; -using System.Resources; -using System.Globalization; -using System.Text; -using System.Threading; - -using EcmaScript.NET; -using EcmaScript.NET.Types; -using EcmaScript.NET.Types.RegExp; -using EcmaScript.NET.Types.E4X; -using EcmaScript.NET.Collections; - -namespace EcmaScript.NET -{ - - /// This is the class that implements the runtime. - /// - /// - public class ScriptRuntime - { - - /// No instances should be created. - protected internal ScriptRuntime () - { - } - - public const int MAXSTACKSIZE = 1000; - - private const string XML_INIT_CLASS = "EcmaScript.NET.Xml.Impl.XMLLib"; - - private static readonly object LIBRARY_SCOPE_KEY = new object (); - - public static bool IsNativeRuntimeType (Type cl) - { - if (cl.IsPrimitive) { - return (cl != typeof (char)); - } - else { - return (cl == typeof (string) || cl == typeof (bool) - || CliHelper.IsNumberType (cl) - || typeof (IScriptable).IsAssignableFrom (cl)); - } - } - - public static ScriptableObject InitStandardObjects (Context cx, ScriptableObject scope, bool zealed) - { - if (scope == null) { - scope = new BuiltinObject (); - } - scope.AssociateValue (LIBRARY_SCOPE_KEY, scope); - - BaseFunction.Init (scope, zealed); - BuiltinObject.Init (scope, zealed); - - IScriptable objectProto = ScriptableObject.GetObjectPrototype (scope); - - // Function.prototype.__proto__ should be Object.prototype - IScriptable functionProto = ScriptableObject.GetFunctionPrototype (scope); - functionProto.SetPrototype (objectProto); - - // Set the prototype of the object passed in if need be - if (scope.GetPrototype () == null) - scope.SetPrototype (objectProto); - - // must precede NativeGlobal since it's needed therein - BuiltinError.Init (scope, zealed); - BuiltinGlobal.Init (cx, scope, zealed); - - if (scope is BuiltinGlobalObject) { - ((BuiltinGlobalObject)scope).Init (scope, zealed); - } - - BuiltinArray.Init (scope, zealed); - BuiltinString.Init (scope, zealed); - BuiltinBoolean.Init (scope, zealed); - BuiltinNumber.Init (scope, zealed); - BuiltinDate.Init (scope, zealed); - BuiltinMath.Init (scope, zealed); - - BuiltinWith.Init (scope, zealed); - BuiltinCall.Init (scope, zealed); - BuiltinScript.Init (scope, zealed); - - BuiltinRegExp.Init (scope, zealed); - - if (cx.HasFeature (Context.Features.E4x)) { - Types.E4X.XMLLib.Init (scope, zealed); - } - - Continuation.Init (scope, zealed); - - if (cx.HasFeature (Context.Features.NonEcmaItObject)) { - InitItObject (cx, scope); - } - - return scope; - } - - static void InitItObject (Context cx, ScriptableObject scope) { - BuiltinObject itObj = new BuiltinObject (); - itObj.SetPrototype (scope); - itObj.DefineProperty ("color", Undefined.Value, ScriptableObject.PERMANENT); - itObj.DefineProperty ("height", Undefined.Value, ScriptableObject.PERMANENT); - itObj.DefineProperty ("width", Undefined.Value, ScriptableObject.PERMANENT); - itObj.DefineProperty ("funny", Undefined.Value, ScriptableObject.PERMANENT); - itObj.DefineProperty ("array", Undefined.Value, ScriptableObject.PERMANENT); - itObj.DefineProperty ("rdonly", Undefined.Value, ScriptableObject.READONLY); - scope.DefineProperty ("it", itObj, ScriptableObject.PERMANENT); - } - - public static ScriptableObject getLibraryScopeOrNull (IScriptable scope) - { - ScriptableObject libScope; - libScope = (ScriptableObject)ScriptableObject.GetTopScopeValue (scope, LIBRARY_SCOPE_KEY); - return libScope; - } - - // It is public so NativeRegExp can access it . - public static bool isJSLineTerminator (int c) - { - // Optimization for faster check for eol character: - // they do not have 0xDFD0 bits set - if ((c & 0xDFD0) != 0) { - return false; - } - return c == '\n' || c == '\r' || c == 0x2028 || c == 0x2029; - } - - - /// Helper function for builtin objects that use the varargs form. - /// ECMA function formal arguments are undefined if not supplied; - /// this function pads the argument array out to the expected - /// length, if necessary. - /// - public static object [] padArguments (object [] args, int count) - { - if (count < args.Length) - return args; - - int i; - object [] result = new object [count]; - for (i = 0; i < args.Length; i++) { - result [i] = args [i]; - } - - for (; i < count; i++) { - result [i] = Undefined.Value; - } - - return result; - } - - - public static string escapeString (string s) - { - return escapeString (s, '"'); - } - - /// For escaping strings printed by object and array literals; not quite - /// the same as 'escape.' - /// - public static string escapeString (string s, char escapeQuote) - { - if (!(escapeQuote == '"' || escapeQuote == '\'')) - Context.CodeBug (); - System.Text.StringBuilder sb = null; - - for (int i = 0, L = s.Length; i != L; ++i) { - int c = s [i]; - - if (' ' <= c && c <= '~' && c != escapeQuote && c != '\\') { - // an ordinary print character (like C isprint()) and not " - // or \ . - if (sb != null) { - sb.Append ((char)c); - } - continue; - } - if (sb == null) { - sb = new System.Text.StringBuilder (L + 3); - sb.Append (s); - sb.Length = i; - } - - int escape = -1; - switch (c) { - - case '\b': - escape = 'b'; - break; - - case '\f': - escape = 'f'; - break; - - case '\n': - escape = 'n'; - break; - - case '\r': - escape = 'r'; - break; - - case '\t': - escape = 't'; - break; - - case 0xb: - escape = 'v'; - break; // Java lacks \v. - - case ' ': - escape = ' '; - break; - - case '\\': - escape = '\\'; - break; - } - if (escape >= 0) { - // an \escaped sort of character - sb.Append ('\\'); - sb.Append ((char)escape); - } - else if (c == escapeQuote) { - sb.Append ('\\'); - sb.Append (escapeQuote); - } - else { - int hexSize; - if (c < 256) { - // 2-digit hex - sb.Append ("\\x"); - hexSize = 2; - } - else { - // Unicode. - sb.Append ("\\u"); - hexSize = 4; - } - // append hexadecimal form of c left-padded with 0 - for (int shift = (hexSize - 1) * 4; shift >= 0; shift -= 4) { - int digit = 0xf & (c >> shift); - int hc = (digit < 10) ? '0' + digit : 'a' - 10 + digit; - sb.Append ((char)hc); - } - } - } - return (sb == null) ? s : sb.ToString (); - } - - internal static bool isValidIdentifierName (string s) - { - int L = s.Length; - if (L == 0) - return false; - if (!(char.IsLetter (s [0]) || s [0].CompareTo ('$') == 0 || s [0].CompareTo ('_') == 0)) - return false; - for (int i = 1; i != L; ++i) { - if (!TokenStream.IsJavaIdentifierPart (s [i])) - return false; - } - return !TokenStream.isKeyword (s); - } - - - - internal static string DefaultObjectToString (IScriptable obj) - { - return "[object " + obj.ClassName + ']'; - } - - internal static string uneval (Context cx, IScriptable scope, object value) - { - if (value == null) { - return "null"; - } - if (value == Undefined.Value) { - return "undefined"; - } - if (value is string) { - string escaped = escapeString ((string)value); - System.Text.StringBuilder sb = new System.Text.StringBuilder (escaped.Length + 2); - sb.Append ('\"'); - sb.Append (escaped); - sb.Append ('\"'); - return sb.ToString (); - } - if (CliHelper.IsNumber (value)) { - double d = Convert.ToDouble (value); - if (d == 0 && 1 / d < 0) { - return "-0"; - } - return ScriptConvert.ToString (d); - } - if (value is bool) { - return ScriptConvert.ToString (value); - } - if (value is IScriptable) { - IScriptable obj = (IScriptable)value; - object v = ScriptableObject.GetProperty (obj, "toSource"); - if (v is IFunction) { - IFunction f = (IFunction)v; - return ScriptConvert.ToString (f.Call (cx, scope, obj, EmptyArgs)); - } - return ScriptConvert.ToString (value); - } - WarnAboutNonJSObject (value); - return value.ToString (); - } - - internal static string defaultObjectToSource (Context cx, IScriptable scope, IScriptable thisObj, object [] args) - { - using (Helpers.StackOverflowVerifier sov = new Helpers.StackOverflowVerifier (1024)) { - bool toplevel, iterating; - if (cx.iterating == null) { - toplevel = true; - iterating = false; - cx.iterating = new ObjToIntMap (31); - } - else { - toplevel = false; - iterating = cx.iterating.has (thisObj); - } - - System.Text.StringBuilder result = new System.Text.StringBuilder (128); - if (toplevel) { - result.Append ("("); - } - result.Append ('{'); - - // Make sure cx.iterating is set to null when done - // so we don't leak memory - try { - if (!iterating) { - cx.iterating.intern (thisObj); // stop recursion. - object [] ids = thisObj.GetIds (); - for (int i = 0; i < ids.Length; i++) { - if (i > 0) - result.Append (", "); - object id = ids [i]; - object value; - if (id is int) { - int intId = ((int)id); - value = thisObj.Get (intId, thisObj); - result.Append (intId); - } - else { - string strId = (string)id; - value = thisObj.Get (strId, thisObj); - if (ScriptRuntime.isValidIdentifierName (strId)) { - result.Append (strId); - } - else { - result.Append ('\''); - result.Append (ScriptRuntime.escapeString (strId, '\'')); - result.Append ('\''); - } - } - result.Append (':'); - result.Append (ScriptRuntime.uneval (cx, scope, value)); - } - } - } - finally { - if (toplevel) { - cx.iterating = null; - } - } - - result.Append ('}'); - if (toplevel) { - result.Append (')'); - } - return result.ToString (); - } - } - - - - - public static IScriptable NewObject (Context cx, IScriptable scope, string constructorName, object [] args) - { - scope = ScriptableObject.GetTopLevelScope (scope); - IFunction ctor = getExistingCtor (cx, scope, constructorName); - if (args == null) { - args = ScriptRuntime.EmptyArgs; - } - return ctor.Construct (cx, scope, args); - } - - - // TODO: this is until setDefaultNamespace will learn how to store NS - // TODO: properly and separates namespace form Scriptable.get etc. - private const string DEFAULT_NS_TAG = "__default_namespace__"; - - public static object setDefaultNamespace (object ns, Context cx) - { - IScriptable scope = cx.currentActivationCall; - if (scope == null) { - scope = getTopCallScope (cx); - } - - XMLLib xmlLib = CurrentXMLLib (cx); - object obj = xmlLib.ToDefaultXmlNamespace (cx, ns); - - // TODO: this should be in separated namesapce from Scriptable.get/put - if (!scope.Has (DEFAULT_NS_TAG, scope)) { - // TODO: this is racy of cause - ScriptableObject.DefineProperty (scope, DEFAULT_NS_TAG, obj, ScriptableObject.PERMANENT | ScriptableObject.DONTENUM); - } - else { - scope.Put (DEFAULT_NS_TAG, scope, obj); - } - - return Undefined.Value; - } - - public static object searchDefaultNamespace (Context cx) - { - IScriptable scope = cx.currentActivationCall; - if (scope == null) { - scope = getTopCallScope (cx); - } - object nsObject; - for (; ; ) { - IScriptable parent = scope.ParentScope; - if (parent == null) { - nsObject = ScriptableObject.GetProperty (scope, DEFAULT_NS_TAG); - if (nsObject == UniqueTag.NotFound) { - return null; - } - break; - } - nsObject = scope.Get (DEFAULT_NS_TAG, scope); - if (nsObject != UniqueTag.NotFound) { - break; - } - scope = parent; - } - return nsObject; - } - - public static object getTopLevelProp (IScriptable scope, string id) - { - scope = ScriptableObject.GetTopLevelScope (scope); - return ScriptableObject.GetProperty (scope, id); - } - - internal static IFunction getExistingCtor (Context cx, IScriptable scope, string constructorName) - { - object ctorVal = ScriptableObject.GetProperty (scope, constructorName); - if (ctorVal is IFunction) { - return (IFunction)ctorVal; - } - if (ctorVal == UniqueTag.NotFound) { - throw Context.ReportRuntimeErrorById ("msg.ctor.not.found", constructorName); - } - else { - throw Context.ReportRuntimeErrorById ("msg.not.ctor", constructorName); - } - } - - /// Return -1L if str is not an index or the index value as lower 32 - /// bits of the result. - /// - private static long indexFromString (string str) - { - // The length of the decimal string representation of - // Integer.MAX_VALUE, 2147483647 - const int MAX_VALUE_LENGTH = 10; - - int len = str.Length; - if (len > 0) { - int i = 0; - bool negate = false; - int c = str [0]; - if (c == '-') { - if (len > 1) { - c = str [1]; - i = 1; - negate = true; - } - } - c -= '0'; - if (0 <= c && c <= 9 && len <= (negate ? MAX_VALUE_LENGTH + 1 : MAX_VALUE_LENGTH)) { - // Use negative numbers to accumulate index to handle - // Integer.MIN_VALUE that is greater by 1 in absolute value - // then Integer.MAX_VALUE - int index = -c; - int oldIndex = 0; - i++; - if (index != 0) { - // Note that 00, 01, 000 etc. are not indexes - while (i != len && 0 <= (c = str [i] - '0') && c <= 9) { - oldIndex = index; - index = 10 * index - c; - i++; - } - } - // Make sure all characters were consumed and that it couldn't - // have overflowed. - if (i == len && (oldIndex > (int.MinValue / 10) || (oldIndex == (int.MinValue / 10) && c <= (negate ? -(int.MinValue % 10) : (int.MaxValue % 10))))) { - return unchecked ((int)0xFFFFFFFFL) & (negate ? index : -index); - } - } - } - return -1L; - } - - /// If str is a decimal presentation of Uint32 value, return it as long. - /// Othewise return -1L; - /// - public static long testUint32String (string str) - { - // The length of the decimal string representation of - // UINT32_MAX_VALUE, 4294967296 - const int MAX_VALUE_LENGTH = 10; - - int len = str.Length; - if (1 <= len && len <= MAX_VALUE_LENGTH) { - int c = str [0]; - c -= '0'; - if (c == 0) { - // Note that 00,01 etc. are not valid Uint32 presentations - return (len == 1) ? 0L : -1L; - } - if (1 <= c && c <= 9) { - long v = c; - for (int i = 1; i != len; ++i) { - c = str [i] - '0'; - if (!(0 <= c && c <= 9)) { - return -1; - } - v = 10 * v + c; - } - // Check for overflow - if ((ulong)v >> 32 == 0) { - return v; - } - } - } - return -1; - } - - /// If s represents index, then return index value wrapped as Integer - /// and othewise return s. - /// - internal static object getIndexObject (string s) - { - long indexTest = indexFromString (s); - if (indexTest >= 0) { - return (int)indexTest; - } - return s; - } - - /// If d is exact int value, return its value wrapped as Integer - /// and othewise return d converted to String. - /// - internal static object getIndexObject (double d) - { - int i = (int)d; - if ((double)i == d) { - return (int)i; - } - return ScriptConvert.ToString (d); - } - - /// If ScriptConvert.ToString(id) is a decimal presentation of int32 value, then id - /// is index. In this case return null and make the index available - /// as ScriptRuntime.lastIndexResult(cx). Otherwise return ScriptConvert.ToString(id). - /// - internal static string ToStringIdOrIndex (Context cx, object id) - { - if (CliHelper.IsNumber (id)) { - double d = Convert.ToDouble (id); - int index = (int)d; - if (((double)index) == d) { - storeIndexResult (cx, index); - return null; - } - return ScriptConvert.ToString (id); - } - else { - string s; - if (id is string) { - s = ((string)id); - } - else { - s = ScriptConvert.ToString (id); - } - long indexTest = indexFromString (s); - if (indexTest >= 0) { - storeIndexResult (cx, (int)indexTest); - return null; - } - return s; - } - } - - /// Call obj.[[Get]](id) - public static object getObjectElem (object obj, object elem, Context cx) - { - IScriptable sobj = ScriptConvert.ToObjectOrNull (cx, obj); - if (sobj == null) { - throw UndefReadError (obj, elem); - } - return getObjectElem (sobj, elem, cx); - } - - public static object getObjectElem (IScriptable obj, object elem, Context cx) - { - if (obj is XMLObject) { - XMLObject xmlObject = (XMLObject)obj; - return xmlObject.EcmaGet (cx, elem); - } - - object result; - - string s = ScriptRuntime.ToStringIdOrIndex (cx, elem); - if (s == null) { - int index = lastIndexResult (cx); - result = ScriptableObject.GetProperty (obj, index); - } - else { - result = ScriptableObject.GetProperty (obj, s); - } - - if (result == UniqueTag.NotFound) { - result = Undefined.Value; - } - - return result; - } - - /// Version of getObjectElem when elem is a valid JS identifier name. - public static object getObjectProp (object obj, string property, Context cx) - { - IScriptable sobj = ScriptConvert.ToObjectOrNull (cx, obj); - if (sobj == null) { - throw UndefReadError (obj, property); - } - return getObjectProp (sobj, property, cx); - } - - public static object getObjectProp (IScriptable obj, string property, Context cx) - { - if (obj is XMLObject) { - XMLObject xmlObject = (XMLObject)obj; - return xmlObject.EcmaGet (cx, property); - } - - object result = ScriptableObject.GetProperty (obj, property); - if (result == UniqueTag.NotFound) { - result = Undefined.Value; - } - - return result; - } - - /* - * A cheaper and less general version of the above for well-known argument - * types. - */ - public static object getObjectIndex (object obj, double dblIndex, Context cx) - { - IScriptable sobj = ScriptConvert.ToObjectOrNull (cx, obj); - if (sobj == null) { - throw UndefReadError (obj, ScriptConvert.ToString (dblIndex)); - } - int index = (int)dblIndex; - if ((double)index == dblIndex) { - return getObjectIndex (sobj, index, cx); - } - else { - string s = ScriptConvert.ToString (dblIndex); - return getObjectProp (sobj, s, cx); - } - } - - public static object getObjectIndex (IScriptable obj, int index, Context cx) - { - if (obj is XMLObject) { - XMLObject xmlObject = (XMLObject)obj; - return xmlObject.EcmaGet (cx, (object)index); - } - - object result = ScriptableObject.GetProperty (obj, index); - if (result == UniqueTag.NotFound) { - result = Undefined.Value; - } - - return result; - } - - /* - * Call obj.[[Put]](id, value) - */ - public static object setObjectElem (object obj, object elem, object value, Context cx) - { - IScriptable sobj = ScriptConvert.ToObjectOrNull (cx, obj); - if (sobj == null) { - throw UndefWriteError (obj, elem, value); - } - return setObjectElem (sobj, elem, value, cx); - } - - public static object setObjectElem (IScriptable obj, object elem, object value, Context cx) - { - if (obj is XMLObject) { - XMLObject xmlObject = (XMLObject)obj; - xmlObject.EcmaPut (cx, elem, value); - return value; - } - - string s = ScriptRuntime.ToStringIdOrIndex (cx, elem); - if (s == null) { - int index = lastIndexResult (cx); - ScriptableObject.PutProperty (obj, index, value); - } - else { - ScriptableObject.PutProperty (obj, s, value); - } - - return value; - } - - /// Version of setObjectElem when elem is a valid JS identifier name. - public static object setObjectProp (object obj, string property, object value, Context cx) - { - IScriptable sobj = ScriptConvert.ToObjectOrNull (cx, obj); - if (sobj == null) { - throw UndefWriteError (obj, property, value); - } - return setObjectProp (sobj, property, value, cx); - } - - public static object setObjectProp (IScriptable obj, string property, object value, Context cx) - { - if (obj is XMLObject) { - XMLObject xmlObject = (XMLObject)obj; - xmlObject.EcmaPut (cx, property, value); - } - else { - return ScriptableObject.PutProperty (obj, property, value); - } - return value; - } - - /* - * A cheaper and less general version of the above for well-known argument - * types. - */ - public static object setObjectIndex (object obj, double dblIndex, object value, Context cx) - { - IScriptable sobj = ScriptConvert.ToObjectOrNull (cx, obj); - if (sobj == null) { - throw UndefWriteError (obj, Convert.ToString (dblIndex), value); - } - int index = (int)dblIndex; - if ((double)index == dblIndex) { - return setObjectIndex (sobj, index, value, cx); - } - else { - string s = ScriptConvert.ToString (dblIndex); - return setObjectProp (sobj, s, value, cx); - } - } - - public static object setObjectIndex (IScriptable obj, int index, object value, Context cx) - { - if (obj is XMLObject) { - XMLObject xmlObject = (XMLObject)obj; - xmlObject.EcmaPut (cx, (object)index, value); - } - else { - return ScriptableObject.PutProperty (obj, index, value); - } - return value; - } - - public static bool deleteObjectElem (IScriptable target, object elem, Context cx) - { - bool result; - if (target is XMLObject) { - XMLObject xmlObject = (XMLObject)target; - result = xmlObject.EcmaDelete (cx, elem); - } - else { - string s = ScriptRuntime.ToStringIdOrIndex (cx, elem); - if (s == null) { - int index = lastIndexResult (cx); - result = ScriptableObject.DeleteProperty (target, index); - } - else { - result = ScriptableObject.DeleteProperty (target, s); - } - } - return result; - } - - public static bool hasObjectElem (IScriptable target, object elem, Context cx) - { - bool result; - - if (target is XMLObject) { - XMLObject xmlObject = (XMLObject)target; - result = xmlObject.EcmaHas (cx, elem); - } - else { - string s = ScriptRuntime.ToStringIdOrIndex (cx, elem); - if (s == null) { - int index = lastIndexResult (cx); - result = ScriptableObject.HasProperty (target, index); - } - else { - result = ScriptableObject.HasProperty (target, s); - } - } - - return result; - } - - public static object refGet (IRef rf, Context cx) - { - return rf.Get (cx); - } - - public static object refSet (IRef rf, object value, Context cx) - { - return rf.Set (cx, value); - } - - public static object refDel (IRef rf, Context cx) - { - return rf.Delete (cx); - } - - internal static bool isSpecialProperty (string s) - { - return s.Equals ("__proto__") || s.Equals ("__parent__"); - } - - public static IRef specialRef (object obj, string specialProperty, Context cx) - { - return SpecialRef.createSpecial (cx, obj, specialProperty); - } - - /// The delete operator - /// - /// See ECMA 11.4.1 - /// - /// In ECMA 0.19, the description of the delete operator (11.4.1) - /// assumes that the [[Delete]] method returns a value. However, - /// the definition of the [[Delete]] operator (8.6.2.5) does not - /// define a return value. Here we assume that the [[Delete]] - /// method doesn't return a value. - /// - public static object delete (object obj, object id, Context cx) - { - IScriptable sobj = ScriptConvert.ToObjectOrNull (cx, obj); - if (sobj == null) { - string idStr = (id == null) ? "null" : id.ToString (); - throw TypeErrorById ("msg.undef.prop.delete", ScriptConvert.ToString (obj), idStr); - } - bool result = deleteObjectElem (sobj, id, cx); - return result; - } - - /// Looks up a name in the scope chain and returns its value. - public static object name (Context cx, IScriptable scope, string name) - { - IScriptable parent = scope.ParentScope; - if (parent == null) { - object result = topScopeName (cx, scope, name); - if (result == UniqueTag.NotFound) { - throw NotFoundError (scope, name); - } - return result; - } - - return nameOrFunction (cx, scope, parent, name, false); - } - - private static object nameOrFunction (Context cx, IScriptable scope, IScriptable parentScope, string name, bool asFunctionCall) - { - object result; - IScriptable thisObj = scope; // It is used only if asFunctionCall==true. - - XMLObject firstXMLObject = null; - for (; ; ) { - if (scope is BuiltinWith) { - IScriptable withObj = scope.GetPrototype (); - if (withObj is XMLObject) { - XMLObject xmlObj = (XMLObject)withObj; - if (xmlObj.EcmaHas (cx, name)) { - // function this should be the target object of with - thisObj = xmlObj; - result = xmlObj.EcmaGet (cx, name); - break; - } - if (firstXMLObject == null) { - firstXMLObject = xmlObj; - } - } - else { - result = ScriptableObject.GetProperty (withObj, name); - if (result != UniqueTag.NotFound) { - // function this should be the target object of with - thisObj = withObj; - break; - } - } - } - else if (scope is BuiltinCall) { - // NativeCall does not prototype chain and Scriptable.get - // can be called directly. - result = scope.Get (name, scope); - if (result != UniqueTag.NotFound) { - if (asFunctionCall) { - // ECMA 262 requires that this for nested funtions - // should be top scope - thisObj = ScriptableObject.GetTopLevelScope (parentScope); - } - break; - } - } - else { - // Can happen if embedding decided that nested - // scopes are useful for what ever reasons. - result = ScriptableObject.GetProperty (scope, name); - if (result != UniqueTag.NotFound) { - thisObj = scope; - break; - } - } - scope = parentScope; - parentScope = parentScope.ParentScope; - if (parentScope == null) { - result = topScopeName (cx, scope, name); - if (result == UniqueTag.NotFound) { - if (firstXMLObject == null || asFunctionCall) { - throw NotFoundError (scope, name); - } - // The name was not found, but we did find an XML - // object in the scope chain and we are looking for name, - // not function. The result should be an empty XMLList - // in name context. - result = firstXMLObject.EcmaGet (cx, name); - } - // For top scope thisObj for functions is always scope itself. - thisObj = scope; - break; - } - } - - if (asFunctionCall) { - if (!(result is ICallable)) { - throw NotFunctionError (result, name); - } - storeScriptable (cx, thisObj); - } - - return result; - } - - private static object topScopeName (Context cx, IScriptable scope, string name) - { - if (cx.useDynamicScope) { - scope = checkDynamicScope (cx.topCallScope, scope); - } - return ScriptableObject.GetProperty (scope, name); - } - - - /// Returns the object in the scope chain that has a given property. - /// - /// The order of evaluation of an assignment expression involves - /// evaluating the lhs to a reference, evaluating the rhs, and then - /// modifying the reference with the rhs value. This method is used - /// to 'bind' the given name to an object containing that property - /// so that the side effects of evaluating the rhs do not affect - /// which property is modified. - /// Typically used in conjunction with setName. - /// - /// See ECMA 10.1.4 - /// - public static IScriptable bind (Context cx, IScriptable scope, string id) - { - IScriptable firstXMLObject = null; - IScriptable parent = scope.ParentScope; - if (parent != null) { - // Check for possibly nested "with" scopes first - while (scope is BuiltinWith) { - IScriptable withObj = scope.GetPrototype (); - if (withObj is XMLObject) { - XMLObject xmlObject = (XMLObject)withObj; - if (xmlObject.EcmaHas (cx, id)) { - return xmlObject; - } - if (firstXMLObject == null) { - firstXMLObject = xmlObject; - } - } - else { - if (ScriptableObject.HasProperty (withObj, id)) { - return withObj; - } - } - scope = parent; - parent = parent.ParentScope; - if (parent == null) { - - goto childScopesChecks_brk; - } - } - for (; ; ) { - if (ScriptableObject.HasProperty (scope, id)) { - return scope; - } - scope = parent; - parent = parent.ParentScope; - if (parent == null) { - - goto childScopesChecks_brk; - } - } - } - - childScopesChecks_brk: - ; - - // scope here is top scope - if (cx.useDynamicScope) { - scope = checkDynamicScope (cx.topCallScope, scope); - } - if (ScriptableObject.HasProperty (scope, id)) { - return scope; - } - // Nothing was found, but since XML objects always bind - // return one if found - return firstXMLObject; - } - - public static object setName (IScriptable bound, object value, Context cx, IScriptable scope, string id) - { - if (bound != null) { - if (bound is XMLObject) { - XMLObject xmlObject = (XMLObject)bound; - xmlObject.EcmaPut (cx, id, value); - } - else { - ScriptableObject.PutProperty (bound, id, value); - } - } - else { - // "newname = 7;", where 'newname' has not yet - // been defined, creates a new property in the - // top scope unless strict mode is specified. - if (cx.HasFeature (Context.Features.StrictVars)) { - throw Context.ReportRuntimeErrorById ("msg.assn.create.strict", id); - } - // Find the top scope by walking up the scope chain. - bound = ScriptableObject.GetTopLevelScope (scope); - if (cx.useDynamicScope) { - bound = checkDynamicScope (cx.topCallScope, bound); - } - bound.Put (id, bound, value); - } - return value; - } - - - - - - - /// Prepare for calling name(...): return function corresponding to - /// name and make current top scope available - /// as ScriptRuntime.lastStoredScriptable() for consumption as thisObj. - /// The caller must call ScriptRuntime.lastStoredScriptable() immediately - /// after calling this method. - /// - public static ICallable getNameFunctionAndThis (string name, Context cx, IScriptable scope) - { - IScriptable parent = scope.ParentScope; - if (parent == null) { - object result = topScopeName (cx, scope, name); - if (!(result is ICallable)) { - if (result == UniqueTag.NotFound) { - throw NotFoundError (scope, name); - } - else { - throw NotFunctionError (result, name); - } - } - // Top scope is not NativeWith or NativeCall => thisObj == scope - IScriptable thisObj = scope; - storeScriptable (cx, thisObj); - return (ICallable)result; - } - - // name will call storeScriptable(cx, thisObj); - return (ICallable)nameOrFunction (cx, scope, parent, name, true); - } - - /// Prepare for calling obj[id](...): return function corresponding to - /// obj[id] and make obj properly converted to Scriptable available - /// as ScriptRuntime.lastStoredScriptable() for consumption as thisObj. - /// The caller must call ScriptRuntime.lastStoredScriptable() immediately - /// after calling this method. - /// - public static ICallable GetElemFunctionAndThis (object obj, object elem, Context cx) - { - string s = ScriptRuntime.ToStringIdOrIndex (cx, elem); - if (s != null) { - return getPropFunctionAndThis (obj, s, cx); - } - int index = lastIndexResult (cx); - - IScriptable thisObj = ScriptConvert.ToObjectOrNull (cx, obj); - if (thisObj == null) { - throw UndefCallError (obj, Convert.ToString (index)); - } - - object value; - for (; ; ) { - // Ignore XML lookup as requred by ECMA 357, 11.2.2.1 - value = ScriptableObject.GetProperty (thisObj, index); - if (value != UniqueTag.NotFound) { - break; - } - if (!(thisObj is XMLObject)) { - break; - } - XMLObject xmlObject = (XMLObject)thisObj; - IScriptable extra = xmlObject.GetExtraMethodSource (cx); - if (extra == null) { - break; - } - thisObj = extra; - } - if (!(value is ICallable)) { - throw NotFunctionError (value, elem); - } - - storeScriptable (cx, thisObj); - return (ICallable)value; - } - - /// Prepare for calling obj.property(...): return function corresponding to - /// obj.property and make obj properly converted to Scriptable available - /// as ScriptRuntime.lastStoredScriptable() for consumption as thisObj. - /// The caller must call ScriptRuntime.lastStoredScriptable() immediately - /// after calling this method. - /// - public static ICallable getPropFunctionAndThis (object obj, string property, Context cx) - { - IScriptable thisObj = ScriptConvert.ToObjectOrNull (cx, obj); - if (thisObj == null) { - throw UndefCallError (obj, property); - } - - object value; - for (; ; ) { - // Ignore XML lookup as requred by ECMA 357, 11.2.2.1 - value = ScriptableObject.GetProperty (thisObj, property); - if (value != UniqueTag.NotFound) { - break; - } - if (!(thisObj is XMLObject)) { - break; - } - XMLObject xmlObject = (XMLObject)thisObj; - IScriptable extra = xmlObject.GetExtraMethodSource (cx); - if (extra == null) { - break; - } - thisObj = extra; - } - - if (value == UniqueTag.NotFound) { - //if (thisObj.Get ("__noSuchMethod__", thisObj) as ICallable != null) { - // return UniqueTag.NoSuchMethodMark; - //} - } - - if (!(value is ICallable)) { - throw NotFunctionError (value, property); - } - - storeScriptable (cx, thisObj); - return (ICallable)value; - } - - /// Prepare for calling (...): return function corresponding to - /// and make parent scope of the function available - /// as ScriptRuntime.lastStoredScriptable() for consumption as thisObj. - /// The caller must call ScriptRuntime.lastStoredScriptable() immediately - /// after calling this method. - /// - public static ICallable getValueFunctionAndThis (object value, Context cx) - { - if (!(value is ICallable)) { - throw NotFunctionError (value); - } - - ICallable f = (ICallable)value; - IScriptable thisObj; - if (f is IScriptable) { - thisObj = ((IScriptable)f).ParentScope; - } - else { - if (cx.topCallScope == null) - throw new Exception (); - thisObj = cx.topCallScope; - } - if (thisObj.ParentScope != null) { - if (thisObj is BuiltinWith) { - // functions defined inside with should have with target - // as their thisObj - } - else if (thisObj is BuiltinCall) { - // nested functions should have top scope as their thisObj - thisObj = ScriptableObject.GetTopLevelScope (thisObj); - } - } - storeScriptable (cx, thisObj); - return f; - } - - /// Perform function call in reference context. Should always - /// return value that can be passed to - /// {@link #refGet(Object)} or @link #refSet(Object, Object)} - /// arbitrary number of times. - /// The args array reference should not be stored in any object that is - /// can be GC-reachable after this method returns. If this is necessary, - /// store args.clone(), not args array itself. - /// - public static IRef callRef (ICallable function, IScriptable thisObj, object [] args, Context cx) - { - if (function is IRefCallable) { - IRefCallable rfunction = (IRefCallable)function; - IRef rf = rfunction.RefCall (cx, thisObj, args); - if (rf == null) { - throw new ApplicationException (rfunction.GetType ().FullName + ".refCall() returned null"); - } - return rf; - } - // No runtime support for now - string msg = GetMessage ("msg.no.ref.from.function", ScriptConvert.ToString (function)); - throw ConstructError ("ReferenceError", msg); - } - - /// Operator new. - /// - /// See ECMA 11.2.2 - /// - public static IScriptable NewObject (object fun, Context cx, IScriptable scope, object [] args) - { - if (!(fun is IFunction)) { - throw NotFunctionError (fun); - } - IFunction function = (IFunction)fun; - return function.Construct (cx, scope, args); - } - - public static object callSpecial (Context cx, ICallable fun, IScriptable thisObj, object [] args, IScriptable scope, IScriptable callerThis, int callType, string filename, int lineNumber) - { - if (callType == Node.SPECIALCALL_EVAL) { - if (BuiltinGlobal.isEvalFunction (fun)) { - return evalSpecial (cx, scope, callerThis, args, filename, lineNumber); - } - } - else if (callType == Node.SPECIALCALL_WITH) { - if (BuiltinWith.IsWithFunction (fun)) { - throw Context.ReportRuntimeErrorById ("msg.only.from.new", "With"); - } - } - else { - throw Context.CodeBug (); - } - - return fun.Call (cx, scope, thisObj, args); - } - - public static object newSpecial (Context cx, object fun, object [] args, IScriptable scope, int callType) - { - if (callType == Node.SPECIALCALL_EVAL) { - if (BuiltinGlobal.isEvalFunction (fun)) { - throw TypeErrorById ("msg.not.ctor", "eval"); - } - } - else if (callType == Node.SPECIALCALL_WITH) { - if (BuiltinWith.IsWithFunction (fun)) { - return BuiltinWith.NewWithSpecial (cx, scope, args); - } - } - else { - throw Context.CodeBug (); - } - - return NewObject (fun, cx, scope, args); - } - - /// Function.prototype.apply and Function.prototype.call - /// - /// See Ecma 15.3.4.[34] - /// - public static object applyOrCall (bool isApply, Context cx, IScriptable scope, IScriptable thisObj, object [] args) - { - int L = args.Length; - ICallable function; - if (thisObj is ICallable) { - function = (ICallable)thisObj; - } - else { - object value = thisObj.GetDefaultValue (typeof (IFunction)); - if (!(value is ICallable)) { - throw ScriptRuntime.NotFunctionError (value, thisObj); - } - function = (ICallable)value; - } - - IScriptable callThis = null; - if (L != 0) { - callThis = ScriptConvert.ToObjectOrNull (cx, args [0]); - } - if (callThis == null) { - // This covers the case of args[0] == (null|undefined) as well. - callThis = getTopCallScope (cx); - } - - object [] callArgs; - if (isApply) { - // Follow Ecma 15.3.4.3 - if (L <= 1) { - callArgs = ScriptRuntime.EmptyArgs; - } - else { - object arg1 = args [1]; - if (arg1 == null || arg1 == Undefined.Value) { - callArgs = ScriptRuntime.EmptyArgs; - } - else if (arg1 is BuiltinArray || arg1 is Arguments) { - callArgs = cx.GetElements ((IScriptable)arg1); - } - else { - throw ScriptRuntime.TypeErrorById ("msg.arg.isnt.array"); - } - } - } - else { - // Follow Ecma 15.3.4.4 - if (L <= 1) { - callArgs = ScriptRuntime.EmptyArgs; - } - else { - callArgs = new object [L - 1]; - Array.Copy (args, 1, callArgs, 0, L - 1); - } - } - - return function.Call (cx, scope, callThis, callArgs); - } - - /// The eval function property of the global object. - /// - /// See ECMA 15.1.2.1 - /// - public static object evalSpecial (Context cx, IScriptable scope, object thisArg, object [] args, string filename, int lineNumber) - { - if (args.Length < 1) - return Undefined.Value; - object x = args [0]; - if (!(x is string)) { - if (cx.HasFeature (Context.Features.StrictEval)) { - throw Context.ReportRuntimeErrorById ("msg.eval.nonstring.strict"); - } - string message = ScriptRuntime.GetMessage ("msg.eval.nonstring"); - Context.ReportWarning (message); - return x; - } - if (filename == null) { - int [] linep = new int [1]; - filename = Context.GetSourcePositionFromStack (linep); - if (filename != null) { - lineNumber = linep [0]; - } - else { - filename = ""; - } - } - string sourceName = ScriptRuntime.makeUrlForGeneratedScript (true, filename, lineNumber); - - ErrorReporter reporter; - reporter = DefaultErrorReporter.ForEval (cx.ErrorReporter); - - // Compile with explicit interpreter instance to force interpreter - // mode. - IScript script = cx.CompileString ((string)x, new Interpreter (), reporter, sourceName, 1, (object)null); - ((InterpretedFunction)script).idata.evalScriptFlag = true; - ICallable c = (ICallable)script; - return c.Call (cx, scope, (IScriptable)thisArg, ScriptRuntime.EmptyArgs); - } - - /// The typeof operator - public static string Typeof (object value) - { - if (value == null) - return "object"; - if (value == Undefined.Value) - return "undefined"; - if (value is IScriptable) { - if (value is XMLObject) - return "xml"; - - return (value is ICallable && !(value is BuiltinRegExp)) ? "function" : "object"; - } - if (value is string) - return "string"; - if (value is char || CliHelper.IsNumber (value)) - return "number"; - if (value is bool) - return "boolean"; - throw errorWithClassName ("msg.invalid.type", value); - } - - - internal static ApplicationException errorWithClassName (string msg, object val) - { - return Context.ReportRuntimeErrorById (msg, val.GetType ().FullName); - } - - /// The typeof operator that correctly handles the undefined case - public static string TypeofName (IScriptable scope, string id) - { - Context cx = Context.CurrentContext; - IScriptable val = bind (cx, scope, id); - if (val == null) - return "undefined"; - return Typeof (getObjectProp (val, id, cx)); - } - - // neg: - // implement the '-' operator inline in the caller - // as "-ScriptConvert.ToNumber(val)" - - // not: - // implement the '!' operator inline in the caller - // as "!toBoolean(val)" - - // bitnot: - // implement the '~' operator inline in the caller - // as "~toInt32(val)" - - public static object Add (object val1, object val2, Context cx) - { - if (CliHelper.IsNumber (val1) && CliHelper.IsNumber (val2)) { - return (double)val1 + (double)val2; - } - if (val1 is XMLObject) { - object test = ((XMLObject)val1).AddValues (cx, true, val2); - if (test != UniqueTag.NotFound) { - return test; - } - } - if (val2 is XMLObject) { - object test = ((XMLObject)val2).AddValues (cx, false, val1); - if (test != UniqueTag.NotFound) { - return test; - } - } - if (val1 is EcmaScript.NET.Types.Cli.CliEventInfo) { - return ((EcmaScript.NET.Types.Cli.CliEventInfo)val1).Add (val2, cx); - } - if (val1 is IScriptable) - val1 = ((IScriptable)val1).GetDefaultValue (null); - if (val2 is IScriptable) - val2 = ((IScriptable)val2).GetDefaultValue (null); - if (!(val1 is string) && !(val2 is string)) - if ((CliHelper.IsNumber (val1)) && (CliHelper.IsNumber (val2))) - return (double)val1 + (double)val2; - else - return ScriptConvert.ToNumber (val1) + ScriptConvert.ToNumber (val2); - return string.Concat (ScriptConvert.ToString (val1), ScriptConvert.ToString (val2)); - } - - public static object nameIncrDecr (IScriptable scopeChain, string id, int incrDecrMask) - { - IScriptable target; - object value; - { - do { - target = scopeChain; - do { - value = target.Get (id, scopeChain); - if (value != UniqueTag.NotFound) { - - goto search_brk; - } - target = target.GetPrototype (); - } - while (target != null); - scopeChain = scopeChain.ParentScope; - } - while (scopeChain != null); - throw NotFoundError (scopeChain, id); - } - - search_brk: - ; - - return doScriptableIncrDecr (target, id, scopeChain, value, incrDecrMask); - } - - public static object propIncrDecr (object obj, string id, Context cx, int incrDecrMask) - { - IScriptable start = ScriptConvert.ToObjectOrNull (cx, obj); - if (start == null) { - throw UndefReadError (obj, id); - } - - IScriptable target = start; - object value; - { - do { - value = target.Get (id, start); - if (value != UniqueTag.NotFound) { - - goto search1_brk; - } - target = target.GetPrototype (); - } - while (target != null); - start.Put (id, start, double.NaN); - return double.NaN; - } - - search1_brk: - ; - - return doScriptableIncrDecr (target, id, start, value, incrDecrMask); - } - - private static object doScriptableIncrDecr (IScriptable target, string id, IScriptable protoChainStart, object value, int incrDecrMask) - { - bool post = ((incrDecrMask & Node.POST_FLAG) != 0); - double number; - if (CliHelper.IsNumber (value)) { - number = Convert.ToDouble (value); - } - else { - number = ScriptConvert.ToNumber (value); - if (post) { - // convert result to number - value = number; - } - } - if ((incrDecrMask & Node.DECR_FLAG) == 0) { - ++number; - } - else { - --number; - } - object result = number; - target.Put (id, protoChainStart, result); - if (post) { - return value; - } - else { - return result; - } - } - - public static object elemIncrDecr (object obj, object index, Context cx, int incrDecrMask) - { - object value = getObjectElem (obj, index, cx); - bool post = ((incrDecrMask & Node.POST_FLAG) != 0); - double number; - if (CliHelper.IsNumber (value)) { - number = Convert.ToDouble (value); - } - else { - number = ScriptConvert.ToNumber (value); - if (post) { - // convert result to number - value = number; - } - } - if ((incrDecrMask & Node.DECR_FLAG) == 0) { - ++number; - } - else { - --number; - } - object result = number; - setObjectElem (obj, index, result, cx); - if (post) { - return value; - } - else { - return result; - } - } - - public static object refIncrDecr (IRef rf, Context cx, int incrDecrMask) - { - object value = rf.Get (cx); - bool post = ((incrDecrMask & Node.POST_FLAG) != 0); - double number; - if (CliHelper.IsNumber (value)) { - number = Convert.ToDouble (value); - } - else { - number = ScriptConvert.ToNumber (value); - if (post) { - // convert result to number - value = number; - } - } - if ((incrDecrMask & Node.DECR_FLAG) == 0) { - ++number; - } - else { - --number; - } - rf.Set (cx, number); - if (post) { - return value; - } - else { - return number; - } - } - - - /// Equality - /// - /// See ECMA 11.9 - /// - public static bool eq (object x, object y) - { - if (x == null || x == Undefined.Value) { - if (y == null || y == Undefined.Value) { - return true; - } - if (y is ScriptableObject) { - object test = ((ScriptableObject)y).EquivalentValues (x); - if (test != UniqueTag.NotFound) { - return ((bool)test); - } - } - return false; - } - else if (CliHelper.IsNumber (x)) { - return eqNumber (Convert.ToDouble (x), y); - } - else if (x is string) { - return eqString ((string)x, y); - } - else if (x is bool) { - bool b = ((bool)x); - if (y is bool) { - return b == ((bool)y); - } - if (y is ScriptableObject) { - object test = ((ScriptableObject)y).EquivalentValues (x); - if (test != UniqueTag.NotFound) { - return ((bool)test); - } - } - return eqNumber (b ? 1.0 : 0.0, y); - } - else if (x is IScriptable) { - if (y is IScriptable) { - if (x == y) { - return true; - } - if (x is ScriptableObject) { - object test = ((ScriptableObject)x).EquivalentValues (y); - if (test != UniqueTag.NotFound) { - return ((bool)test); - } - } - if (y is ScriptableObject) { - object test = ((ScriptableObject)y).EquivalentValues (x); - if (test != UniqueTag.NotFound) { - return ((bool)test); - } - } - if (x is Wrapper && y is Wrapper) { - return ((Wrapper)x).Unwrap () == ((Wrapper)y).Unwrap (); - } - return false; - } - else if (y is bool) { - if (x is ScriptableObject) { - object test = ((ScriptableObject)x).EquivalentValues (y); - if (test != UniqueTag.NotFound) { - return ((bool)test); - } - } - double d = ((bool)y) ? 1.0 : 0.0; - return eqNumber (d, x); - } - else if (CliHelper.IsNumber (y)) { - return eqNumber (Convert.ToDouble (y), x); - } - else if (y is string) { - return eqString ((string)y, x); - } - // covers the case when y == Undefined.instance as well - return false; - } - else { - WarnAboutNonJSObject (x); - return x == y; - } - } - - internal static bool eqNumber (double x, object y) - { - for (; ; ) { - if (y == null || y == Undefined.Value) { - return false; - } - else if (CliHelper.IsNumber (y)) { - return x == Convert.ToDouble (y); - } - else if (y is string) { - return x == ScriptConvert.ToNumber (y); - } - else if (y is bool) { - return x == (((bool)y) ? 1.0 : +0.0); - } - else if (y is IScriptable) { - if (y is ScriptableObject) { - object xval = x; - object test = ((ScriptableObject)y).EquivalentValues (xval); - if (test != UniqueTag.NotFound) { - return ((bool)test); - } - } - y = ScriptConvert.ToPrimitive (y); - } - else { - WarnAboutNonJSObject (y); - return false; - } - } - } - - private static bool eqString (string x, object y) - { - for (; ; ) { - if (y == null || y == Undefined.Value) { - return false; - } - else if (y is string) { - return x.Equals (y); - } - else if (CliHelper.IsNumber (y)) { - return ScriptConvert.ToNumber (x) == Convert.ToDouble (y); - } - else if (y is bool) { - return ScriptConvert.ToNumber (x) == (((bool)y) ? 1.0 : 0.0); - } - else if (y is IScriptable) { - if (y is ScriptableObject) { - object test = ((ScriptableObject)y).EquivalentValues (x); - if (test != UniqueTag.NotFound) { - return ((bool)test); - } - } - y = ScriptConvert.ToPrimitive (y); - continue; - } - else { - WarnAboutNonJSObject (y); - return false; - } - } - } - public static bool shallowEq (object x, object y) - { - if (x == y) { - if (!(CliHelper.IsNumber (x))) { - return true; - } - // double.NaN check - double d = Convert.ToDouble (x); - return !double.IsNaN (d); - } - if (x == null || x == Undefined.Value) { - return false; - } - else if (CliHelper.IsNumber (x)) { - if (CliHelper.IsNumber (y)) { - return Convert.ToDouble (x) == Convert.ToDouble (y); - } - } - else if (x is string) { - if (y is string) { - return x.Equals (y); - } - } - else if (x is bool) { - if (y is bool) { - return x.Equals (y); - } - } - else if (x is IScriptable) { - if (x is Wrapper && y is Wrapper) { - return ((Wrapper)x).Unwrap () == ((Wrapper)y).Unwrap (); - } - } - else { - WarnAboutNonJSObject (x); - return x == y; - } - return false; - } - - /// The instanceof operator. - /// - /// - /// a instanceof b - /// - public static bool InstanceOf (object a, object b, Context cx) - { - IScriptable sB = (b as IScriptable); - - // Check RHS is an object - if (sB == null) { - throw TypeErrorById ("msg.instanceof.not.object"); - } - - IScriptable sA = (a as IScriptable); - - // for primitive values on LHS, return false - // TODO we may want to change this so that 5 instanceof Number == true - if (sA == null) { - return false; - } - - - return sB.HasInstance (sA); - } - - /// Delegates to - /// - /// - /// true iff rhs appears in lhs' proto chain - /// - protected internal static bool jsDelegatesTo (IScriptable lhs, IScriptable rhs) - { - IScriptable proto = lhs.GetPrototype (); - - while (proto != null) { - if (proto.Equals (rhs)) - return true; - proto = proto.GetPrototype (); - } - - return false; - } - - /// The in operator. - /// - /// This is a new JS 1.3 language feature. The in operator mirrors - /// the operation of the for .. in construct, and tests whether the - /// rhs has the property given by the lhs. It is different from the - /// for .. in construct in that: - ///
- it doesn't perform ToObject on the right hand side - ///
- it returns true for DontEnum properties. - ///
- /// the left hand operand - /// - /// the right hand operand - /// - /// - /// true if property name or element number a is a property of b - /// - public static bool In (object a, object b, Context cx) - { - if (!(b is IScriptable)) { - throw TypeErrorById ("msg.instanceof.not.object"); - } - - return hasObjectElem ((IScriptable)b, a, cx); - } - - public static bool cmp_LT (object val1, object val2) - { - double d1, d2; - if (CliHelper.IsNumber (val1) && CliHelper.IsNumber (val2)) { - d1 = Convert.ToDouble (val1); - d2 = Convert.ToDouble (val2); - } - else { - if (val1 is IScriptable) - val1 = ((IScriptable)val1).GetDefaultValue (typeof (long)); - if (val2 is IScriptable) - val2 = ((IScriptable)val2).GetDefaultValue (typeof (long)); - if (val1 is string && val2 is string) { - return String.CompareOrdinal (((string)val1), (string)val2) < 0; - } - d1 = ScriptConvert.ToNumber (val1); - d2 = ScriptConvert.ToNumber (val2); - } - return d1 < d2; - } - - public static bool cmp_LE (object val1, object val2) - { - double d1, d2; - if (CliHelper.IsNumber (val1) && CliHelper.IsNumber (val2)) { - d1 = Convert.ToDouble (val1); - d2 = Convert.ToDouble (val2); - } - else { - if (val1 is IScriptable) - val1 = ((IScriptable)val1).GetDefaultValue (typeof (long)); - if (val2 is IScriptable) - val2 = ((IScriptable)val2).GetDefaultValue (typeof (long)); - if (val1 is string && val2 is string) { - return String.CompareOrdinal (((string)val1), (string)val2) <= 0; - } - d1 = ScriptConvert.ToNumber (val1); - d2 = ScriptConvert.ToNumber (val2); - } - return d1 <= d2; - } - - - public static bool hasTopCall (Context cx) - { - return (cx.topCallScope != null); - } - - public static IScriptable getTopCallScope (Context cx) - { - IScriptable scope = cx.topCallScope; - if (scope == null) { - throw new ApplicationException (); - } - return scope; - } - - public static object DoTopCall (ICallable callable, Context cx, IScriptable scope, IScriptable thisObj, object [] args) - { - if (scope == null) - throw new ArgumentException (); - if (cx.topCallScope != null) - throw new ApplicationException (); - - object result; - cx.topCallScope = ScriptableObject.GetTopLevelScope (scope); - cx.useDynamicScope = cx.HasFeature (Context.Features.DynamicScope); - ContextFactory f = cx.Factory; - try { - result = f.DoTopCall (callable, cx, scope, thisObj, args); - } - finally { - cx.topCallScope = null; - // Cleanup cached references - cx.cachedXMLLib = null; - - if (cx.currentActivationCall != null) { - // Function should always call exitActivationFunction - // if it creates activation record - throw new ApplicationException ( - "ActivationCall without exitActivationFunction() invokation." - ); - } - } - return result; - } - - /// Return possibleDynamicScope if staticTopScope - /// is present on its prototype chain and return staticTopScope - /// otherwise. - /// Should only be called when staticTopScope is top scope. - /// - internal static IScriptable checkDynamicScope (IScriptable possibleDynamicScope, IScriptable staticTopScope) - { - // Return cx.topCallScope if scope - if (possibleDynamicScope == staticTopScope) { - return possibleDynamicScope; - } - IScriptable proto = possibleDynamicScope; - for (; ; ) { - proto = proto.GetPrototype (); - if (proto == staticTopScope) { - return possibleDynamicScope; - } - if (proto == null) { - return staticTopScope; - } - } - } - - public static void initScript (BuiltinFunction funObj, IScriptable thisObj, Context cx, IScriptable scope, bool evalScript) - { - if (cx.topCallScope == null) - throw new ApplicationException (); - - int varCount = funObj.ParamAndVarCount; - if (varCount != 0) { - - IScriptable varScope = scope; - // Never define any variables from var statements inside with - // object. See bug 38590. - while (varScope is BuiltinWith) { - varScope = varScope.ParentScope; - } - - for (int i = varCount; i-- != 0; ) { - string name = funObj.getParamOrVarName (i); - // Don't overwrite existing def if already defined in object - // or prototypes of object. - if (!ScriptableObject.HasProperty (scope, name)) { - if (!evalScript) { - // Global var definitions are supposed to be DONTDELETE - ScriptableObject.DefineProperty (varScope, name, Undefined.Value, ScriptableObject.PERMANENT); - } - else { - varScope.Put (name, varScope, Undefined.Value); - } - } - } - } - } - - public static IScriptable createFunctionActivation (BuiltinFunction funObj, IScriptable scope, object [] args) - { - return new BuiltinCall (funObj, scope, args); - } - - - public static void enterActivationFunction (Context cx, IScriptable activation) - { - if (cx.topCallScope == null) - throw new ApplicationException (); - - BuiltinCall call = (BuiltinCall)activation; - call.parentActivationCall = cx.currentActivationCall; - cx.currentActivationCall = call; - } - - public static void exitActivationFunction (Context cx) - { - BuiltinCall call = cx.currentActivationCall; - cx.currentActivationCall = call.parentActivationCall; - call.parentActivationCall = null; - } - - internal static BuiltinCall findFunctionActivation (Context cx, IFunction f) - { - BuiltinCall call = cx.currentActivationCall; - while (call != null) { - if (call.function == f) - return call; - call = call.parentActivationCall; - } - return null; - } - - public static IScriptable NewCatchScope (Exception t, IScriptable lastCatchScope, string exceptionName, Context cx, IScriptable scope) - { - object obj; - bool cacheObj; - - if (t is EcmaScriptThrow) { - cacheObj = false; - obj = ((EcmaScriptThrow)t).Value; - } - else { - cacheObj = true; - - // Create wrapper object unless it was associated with - // the previous scope object - - if (lastCatchScope != null) { - BuiltinObject last = (BuiltinObject)lastCatchScope; - obj = last.GetAssociatedValue (t); - if (obj == null) - Context.CodeBug (); - - goto getObj_brk; - } - - EcmaScriptException re; - string errorName; - string errorMsg; - - Exception javaException = null; - - if (t is EcmaScriptError) { - EcmaScriptError ee = (EcmaScriptError)t; - re = ee; - errorName = ee.Name; - errorMsg = ee.ErrorMessage; - } - else if (t is EcmaScriptRuntimeException) { - re = (EcmaScriptRuntimeException)t; - if (t.InnerException != null) { - javaException = t.InnerException; - errorName = "JavaException"; - errorMsg = javaException.GetType ().FullName + ": " + javaException.Message; - } - else { - errorName = "InternalError"; - errorMsg = re.Message; - } - } - else { - // Script can catch only instances of JavaScriptException, - // EcmaError and EvaluatorException - throw Context.CodeBug (); - } - - string sourceUri = re.SourceName; - if (sourceUri == null) { - sourceUri = ""; - } - int line = re.LineNumber; - object [] args; - if (line > 0) { - args = new object [] { errorMsg, sourceUri, (int)line }; - } - else { - args = new object [] { errorMsg, sourceUri }; - } - - IScriptable errorObject = cx.NewObject (scope, errorName, args); - ScriptableObject.PutProperty (errorObject, "name", errorName); - - if (javaException != null) { - object wrap = cx.Wrap (scope, javaException, null); - ScriptableObject.DefineProperty (errorObject, "javaException", wrap, ScriptableObject.PERMANENT | ScriptableObject.READONLY); - } - if (re != null) { - object wrap = cx.Wrap (scope, re, null); - ScriptableObject.DefineProperty (errorObject, "rhinoException", wrap, ScriptableObject.PERMANENT | ScriptableObject.READONLY); - } - - obj = errorObject; - } - - getObj_brk: - ; - - - - BuiltinObject catchScopeObject = new BuiltinObject (); - // See ECMA 12.4 - catchScopeObject.DefineProperty (exceptionName, obj, ScriptableObject.PERMANENT); - if (cacheObj) { - catchScopeObject.AssociateValue (t, obj); - } - return catchScopeObject; - } - - public static IScriptable enterWith (object obj, Context cx, IScriptable scope) - { - IScriptable sobj = ScriptConvert.ToObjectOrNull (cx, obj); - if (sobj == null) { - throw TypeErrorById ("msg.undef.with", ScriptConvert.ToString (obj)); - } - if (sobj is XMLObject) { - XMLObject xmlObject = (XMLObject)sobj; - return xmlObject.EnterWith (scope); - } - return new BuiltinWith (scope, sobj); - } - - public static IScriptable leaveWith (IScriptable scope) - { - BuiltinWith nw = (BuiltinWith)scope; - return nw.ParentScope; - } - - public static IScriptable enterDotQuery (object value, IScriptable scope) - { - if (!(value is XMLObject)) { - throw NotXmlError (value); - } - XMLObject obj = (XMLObject)value; - return obj.EnterDotQuery (scope); - } - - public static object updateDotQuery (bool value, IScriptable scope) - { - // Return null to continue looping - BuiltinWith nw = (BuiltinWith)scope; - return nw.UpdateDotQuery (value); - } - - public static IScriptable leaveDotQuery (IScriptable scope) - { - BuiltinWith nw = (BuiltinWith)scope; - return nw.ParentScope; - } - - public static void setFunctionProtoAndParent (BaseFunction fn, IScriptable scope) - { - fn.ParentScope = scope; - fn.SetPrototype (ScriptableObject.GetFunctionPrototype (scope)); - } - - public static void setObjectProtoAndParent (ScriptableObject obj, IScriptable scope) - { - // Compared with function it always sets the scope to top scope - scope = ScriptableObject.GetTopLevelScope (scope); - obj.ParentScope = scope; - IScriptable proto = ScriptableObject.getClassPrototype (scope, obj.ClassName); - obj.SetPrototype (proto); - } - - public static void initFunction (Context cx, IScriptable scope, BuiltinFunction function, int type, bool fromEvalCode) - { - if (type == FunctionNode.FUNCTION_STATEMENT) { - string name = function.FunctionName; - if (name != null && name.Length != 0) { - if (!fromEvalCode) { - // ECMA specifies that functions defined in global and - // function scope outside eval should have DONTDELETE set. - ScriptableObject.DefineProperty (scope, name, function, ScriptableObject.PERMANENT); - } - else { - scope.Put (name, scope, function); - } - } - } - else if (type == FunctionNode.FUNCTION_EXPRESSION_STATEMENT) { - string name = function.FunctionName; - if (name != null && name.Length != 0) { - // Always put function expression statements into initial - // activation object ignoring the with statement to follow - // SpiderMonkey - while (scope is BuiltinWith) { - scope = scope.ParentScope; - } - scope.Put (name, scope, function); - } - } - else { - throw Context.CodeBug (); - } - } - - public static IScriptable newArrayLiteral (object [] objects, int [] skipIndexces, Context cx, IScriptable scope) - { - int count = objects.Length; - int skipCount = 0; - if (skipIndexces != null) { - skipCount = skipIndexces.Length; - } - int length = count + skipCount; - int lengthObj = (int)length; - IScriptable arrayObj; - /* - * If the version is 120, then new Array(4) means create a new - * array with 4 as the first element. In this case, we have to - * set length property manually. - */ - if (cx.Version == Context.Versions.JS1_2) { - arrayObj = cx.NewObject (scope, "Array", ScriptRuntime.EmptyArgs); - ScriptableObject.PutProperty (arrayObj, "length", (object)lengthObj); - } - else { - arrayObj = cx.NewObject (scope, "Array", new object [] { lengthObj }); - } - int skip = 0; - for (int i = 0, j = 0; i != length; ++i) { - if (skip != skipCount && skipIndexces [skip] == i) { - ++skip; - continue; - } - ScriptableObject.PutProperty (arrayObj, i, objects [j]); - ++j; - } - return arrayObj; - } - - public static IScriptable newObjectLiteral (object [] propertyIds, object [] propertyValues, Context cx, IScriptable scope) - { - IScriptable obj = cx.NewObject (scope); - for (int i = 0, end = propertyIds.Length; i != end; ++i) { - object id = propertyIds [i]; - object value = propertyValues [i]; - - if (id is Node.GetterPropertyLiteral) { - BuiltinObject nativeObj = (BuiltinObject)obj; - InterpretedFunction fun = (InterpretedFunction)value; - nativeObj.DefineGetter ((string)((Node.GetterPropertyLiteral)id).Property, fun); - } - else if (id is Node.SetterPropertyLiteral) { - BuiltinObject nativeObj = (BuiltinObject)obj; - InterpretedFunction fun = (InterpretedFunction)value; - nativeObj.DefineSetter ((string)((Node.SetterPropertyLiteral)id).Property, fun); - } - else if (id is string) { - ScriptableObject.PutProperty (obj, (string)id, value); - } - else { - ScriptableObject.PutProperty (obj, (int)id, value); - } - } - return obj; - } - - public static bool isArrayObject (object obj) - { - return obj is BuiltinArray || obj is Arguments; - } - - public static object [] getArrayElements (IScriptable obj) - { - Context cx = Context.CurrentContext; - long longLen = BuiltinArray.getLengthProperty (cx, obj); - if (longLen > int.MaxValue) { - // arrays beyond MAX_INT is not in Java in any case - throw new ArgumentException (); - } - int len = (int)longLen; - if (len == 0) { - return ScriptRuntime.EmptyArgs; - } - else { - object [] result = new object [len]; - for (int i = 0; i < len; i++) { - object elem = ScriptableObject.GetProperty (obj, i); - result [i] = (elem == UniqueTag.NotFound) ? Undefined.Value : elem; - } - return result; - } - } - - internal static void checkDeprecated (Context cx, string name) - { - Context.Versions version = cx.Version; - if (version >= Context.Versions.JS1_4 || version == Context.Versions.Default) { - string msg = GetMessage ("msg.deprec.ctor", name); - if (version == Context.Versions.Default) - Context.ReportWarning (msg); - else - throw Context.ReportRuntimeError (msg); - } - } - - - private static ResourceManager m_ResourceManager = null; - - public static string GetMessage (string messageId, params object [] arguments) - { - Context cx = Context.CurrentContext; - - // Get current culture - CultureInfo culture = null; - if (cx != null) - culture = cx.CurrentCulture; - - if (m_ResourceManager == null) { - m_ResourceManager = new ResourceManager ( - "EcmaScript.NET.Resources.Messages", typeof (ScriptRuntime).Assembly); - } - - string formatString = m_ResourceManager.GetString (messageId, culture); - if (formatString == null) - throw new ApplicationException ("Missing no message resource found for message property " + messageId); - - if (arguments == null) - arguments = new object [0]; - if (arguments.Length == 0) - return formatString; - return string.Format (formatString, arguments); - } - - public static EcmaScriptError ConstructError (string error, string message) - { - int [] linep = new int [1]; - string filename = Context.GetSourcePositionFromStack (linep); - return ConstructError (error, message, filename, linep [0], null, 0); - } - - public static EcmaScriptError ConstructError (string error, string message, string sourceName, int lineNumber, string lineSource, int columnNumber) - { - return new EcmaScriptError (error, message, sourceName, lineNumber, lineSource, columnNumber); - } - - public static EcmaScriptError TypeError (string message) - { - return ConstructError ("TypeError", message); - } - - public static EcmaScriptError TypeErrorById (string messageId, params string [] args) - { - return TypeError (GetMessage (messageId, args)); - } - - public static ApplicationException UndefReadError (object obj, object id) - { - string idStr = (id == null) ? "null" : id.ToString (); - return TypeErrorById ("msg.undef.prop.read", ScriptConvert.ToString (obj), idStr); - } - - public static ApplicationException UndefCallError (object obj, object id) - { - string idStr = (id == null) ? "null" : id.ToString (); - return TypeErrorById ("msg.undef.method.call", ScriptConvert.ToString (obj), idStr); - } - - public static ApplicationException UndefWriteError (object obj, object id, object value) - { - string idStr = (id == null) ? "null" : id.ToString (); - string valueStr = (value is IScriptable) ? value.ToString () : ScriptConvert.ToString (value); - return TypeErrorById ("msg.undef.prop.write", ScriptConvert.ToString (obj), idStr, valueStr); - } - - public static ApplicationException NotFoundError (IScriptable obj, string property) - { - // TODO: use object to improve the error message - string msg = GetMessage ("msg.is.not.defined", property); - throw ConstructError ("ReferenceError", msg); - } - - public static ApplicationException NotFunctionError (object value) - { - return NotFunctionError (value, value); - } - - public static ApplicationException NotFunctionError (object value, object messageHelper) - { - // TODO: Use value for better error reporting - string msg = (messageHelper == null) ? "null" : messageHelper.ToString (); - if (value == UniqueTag.NotFound) { - return TypeErrorById ("msg.function.not.found", msg); - } - return TypeErrorById ("msg.isnt.function", msg, value == null ? "null" : value.GetType ().FullName); - } - - private static ApplicationException NotXmlError (object value) - { - throw TypeErrorById ("msg.isnt.xml.object", ScriptConvert.ToString (value)); - } - - internal static void WarnAboutNonJSObject (object nonJSObject) - { - string message = "+++ USAGE WARNING: Missed Context.Wrap() conversion:\n" - + "Runtime detected object " + nonJSObject + " of class " + nonJSObject.GetType ().FullName + " where it expected String, Number, Boolean or Scriptable instance. " - + "Please check your code for missig Context.Wrap() call."; - - Context.ReportWarning (message); - Console.Error.WriteLine (message); - } - - - private static XMLLib CurrentXMLLib (Context cx) - { - // Scripts should be running to access this - if (cx.topCallScope == null) - throw new ApplicationException (); - - XMLLib xmlLib = cx.cachedXMLLib; - if (xmlLib == null) { - xmlLib = XMLLib.ExtractFromScope (cx.topCallScope); - if (xmlLib == null) - throw new ApplicationException (); - cx.cachedXMLLib = xmlLib; - } - - return xmlLib; - } - - /// Escapes the reserved characters in a value of an attribute - /// - /// - /// Unescaped text - /// - /// The escaped text - /// - public static string escapeAttributeValue (object value, Context cx) - { - XMLLib xmlLib = CurrentXMLLib (cx); - return xmlLib.EscapeAttributeValue (value); - } - - /// Escapes the reserved characters in a value of a text node - /// - /// - /// Unescaped text - /// - /// The escaped text - /// - public static string escapeTextValue (object value, Context cx) - { - XMLLib xmlLib = CurrentXMLLib (cx); - return xmlLib.EscapeTextValue (value); - } - - public static IRef memberRef (object obj, object elem, Context cx, int memberTypeFlags) - { - if (!(obj is XMLObject)) { - throw NotXmlError (obj); - } - XMLObject xmlObject = (XMLObject)obj; - return xmlObject.MemberRef (cx, elem, memberTypeFlags); - } - - public static IRef memberRef (object obj, object ns, object elem, Context cx, int memberTypeFlags) - { - if (!(obj is XMLObject)) { - throw NotXmlError (obj); - } - XMLObject xmlObject = (XMLObject)obj; - return xmlObject.MemberRef (cx, ns, elem, memberTypeFlags); - } - - public static IRef nameRef (object name, Context cx, IScriptable scope, int memberTypeFlags) - { - XMLLib xmlLib = CurrentXMLLib (cx); - return xmlLib.NameRef (cx, name, scope, memberTypeFlags); - } - - public static IRef nameRef (object ns, object name, Context cx, IScriptable scope, int memberTypeFlags) - { - XMLLib xmlLib = CurrentXMLLib (cx); - return xmlLib.NameRef (cx, ns, name, scope, memberTypeFlags); - } - - private static void storeIndexResult (Context cx, int index) - { - cx.scratchIndex = index; - } - - internal static int lastIndexResult (Context cx) - { - return cx.scratchIndex; - } - - public static void storeUint32Result (Context cx, long value) - { - if (((ulong)value >> 32) != 0) - throw new ArgumentException (); - cx.scratchUint32 = value; - } - - public static long lastUint32Result (Context cx) - { - long value = cx.scratchUint32; - if ((ulong)value >> 32 != 0) - throw new ApplicationException (); - return value; - } - - private static void storeScriptable (Context cx, IScriptable value) - { - // The previosly stored scratchScriptable should be consumed - if (cx.scratchScriptable != null) - throw new ApplicationException (); - cx.scratchScriptable = value; - } - - public static IScriptable lastStoredScriptable (Context cx) - { - IScriptable result = cx.scratchScriptable; - cx.scratchScriptable = null; - return result; - } - - internal static string makeUrlForGeneratedScript (bool isEval, string masterScriptUrl, int masterScriptLine) - { - if (isEval) { - return masterScriptUrl + '#' + masterScriptLine + "(eval)"; - } - else { - return masterScriptUrl + '#' + masterScriptLine + "(Function)"; - } - } - - internal static bool isGeneratedScript (string sourceUrl) - { - // ALERT: this may clash with a valid URL containing (eval) or - // (Function) - return sourceUrl.IndexOf ("(eval)") >= 0 || sourceUrl.IndexOf ("(Function)") >= 0; - } - - - public static readonly object [] EmptyArgs = new object [0]; - public static readonly string [] EmptyStrings = new string [0]; - - } +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; +using System.Resources; +using System.Globalization; +using System.Text; +using System.Threading; + +using EcmaScript.NET; +using EcmaScript.NET.Types; +using EcmaScript.NET.Types.RegExp; +using EcmaScript.NET.Types.E4X; +using EcmaScript.NET.Collections; + +namespace EcmaScript.NET +{ + + /// This is the class that implements the runtime. + /// + /// + public class ScriptRuntime + { + + /// No instances should be created. + protected internal ScriptRuntime () + { + } + + public const int MAXSTACKSIZE = 1000; + + private const string XML_INIT_CLASS = "EcmaScript.NET.Xml.Impl.XMLLib"; + + private static readonly object LIBRARY_SCOPE_KEY = new object (); + + public static bool IsNativeRuntimeType (Type cl) + { + if (cl.IsPrimitive) { + return (cl != typeof (char)); + } + else { + return (cl == typeof (string) || cl == typeof (bool) + || CliHelper.IsNumberType (cl) + || typeof (IScriptable).IsAssignableFrom (cl)); + } + } + + public static ScriptableObject InitStandardObjects (Context cx, ScriptableObject scope, bool zealed) + { + if (scope == null) { + scope = new BuiltinObject (); + } + scope.AssociateValue (LIBRARY_SCOPE_KEY, scope); + + BaseFunction.Init (scope, zealed); + BuiltinObject.Init (scope, zealed); + + IScriptable objectProto = ScriptableObject.GetObjectPrototype (scope); + + // Function.prototype.__proto__ should be Object.prototype + IScriptable functionProto = ScriptableObject.GetFunctionPrototype (scope); + functionProto.SetPrototype (objectProto); + + // Set the prototype of the object passed in if need be + if (scope.GetPrototype () == null) + scope.SetPrototype (objectProto); + + // must precede NativeGlobal since it's needed therein + BuiltinError.Init (scope, zealed); + BuiltinGlobal.Init (cx, scope, zealed); + + if (scope is BuiltinGlobalObject) { + ((BuiltinGlobalObject)scope).Init (scope, zealed); + } + + BuiltinArray.Init (scope, zealed); + BuiltinString.Init (scope, zealed); + BuiltinBoolean.Init (scope, zealed); + BuiltinNumber.Init (scope, zealed); + BuiltinDate.Init (scope, zealed); + BuiltinMath.Init (scope, zealed); + + BuiltinWith.Init (scope, zealed); + BuiltinCall.Init (scope, zealed); + BuiltinScript.Init (scope, zealed); + + BuiltinRegExp.Init (scope, zealed); + + if (cx.HasFeature (Context.Features.E4x)) { + Types.E4X.XMLLib.Init (scope, zealed); + } + + Continuation.Init (scope, zealed); + + if (cx.HasFeature (Context.Features.NonEcmaItObject)) { + InitItObject (cx, scope); + } + + return scope; + } + + static void InitItObject (Context cx, ScriptableObject scope) { + BuiltinObject itObj = new BuiltinObject (); + itObj.SetPrototype (scope); + itObj.DefineProperty ("color", Undefined.Value, ScriptableObject.PERMANENT); + itObj.DefineProperty ("height", Undefined.Value, ScriptableObject.PERMANENT); + itObj.DefineProperty ("width", Undefined.Value, ScriptableObject.PERMANENT); + itObj.DefineProperty ("funny", Undefined.Value, ScriptableObject.PERMANENT); + itObj.DefineProperty ("array", Undefined.Value, ScriptableObject.PERMANENT); + itObj.DefineProperty ("rdonly", Undefined.Value, ScriptableObject.READONLY); + scope.DefineProperty ("it", itObj, ScriptableObject.PERMANENT); + } + + public static ScriptableObject getLibraryScopeOrNull (IScriptable scope) + { + ScriptableObject libScope; + libScope = (ScriptableObject)ScriptableObject.GetTopScopeValue (scope, LIBRARY_SCOPE_KEY); + return libScope; + } + + // It is public so NativeRegExp can access it . + public static bool isJSLineTerminator (int c) + { + // Optimization for faster check for eol character: + // they do not have 0xDFD0 bits set + if ((c & 0xDFD0) != 0) { + return false; + } + return c == '\n' || c == '\r' || c == 0x2028 || c == 0x2029; + } + + + /// Helper function for builtin objects that use the varargs form. + /// ECMA function formal arguments are undefined if not supplied; + /// this function pads the argument array out to the expected + /// length, if necessary. + /// + public static object [] padArguments (object [] args, int count) + { + if (count < args.Length) + return args; + + int i; + object [] result = new object [count]; + for (i = 0; i < args.Length; i++) { + result [i] = args [i]; + } + + for (; i < count; i++) { + result [i] = Undefined.Value; + } + + return result; + } + + + public static string escapeString (string s) + { + return escapeString (s, '"'); + } + + /// For escaping strings printed by object and array literals; not quite + /// the same as 'escape.' + /// + public static string escapeString (string s, char escapeQuote) + { + if (!(escapeQuote == '"' || escapeQuote == '\'')) + Context.CodeBug (); + System.Text.StringBuilder sb = null; + + for (int i = 0, L = s.Length; i != L; ++i) { + int c = s [i]; + + if (' ' <= c && c <= '~' && c != escapeQuote && c != '\\') { + // an ordinary print character (like C isprint()) and not " + // or \ . + if (sb != null) { + sb.Append ((char)c); + } + continue; + } + if (sb == null) { + sb = new System.Text.StringBuilder (L + 3); + sb.Append (s); + sb.Length = i; + } + + int escape = -1; + switch (c) { + + case '\b': + escape = 'b'; + break; + + case '\f': + escape = 'f'; + break; + + case '\n': + escape = 'n'; + break; + + case '\r': + escape = 'r'; + break; + + case '\t': + escape = 't'; + break; + + case 0xb: + escape = 'v'; + break; // Java lacks \v. + + case ' ': + escape = ' '; + break; + + case '\\': + escape = '\\'; + break; + } + if (escape >= 0) { + // an \escaped sort of character + sb.Append ('\\'); + sb.Append ((char)escape); + } + else if (c == escapeQuote) { + sb.Append ('\\'); + sb.Append (escapeQuote); + } + else { + int hexSize; + if (c < 256) { + // 2-digit hex + sb.Append ("\\x"); + hexSize = 2; + } + else { + // Unicode. + sb.Append ("\\u"); + hexSize = 4; + } + // append hexadecimal form of c left-padded with 0 + for (int shift = (hexSize - 1) * 4; shift >= 0; shift -= 4) { + int digit = 0xf & (c >> shift); + int hc = (digit < 10) ? '0' + digit : 'a' - 10 + digit; + sb.Append ((char)hc); + } + } + } + return (sb == null) ? s : sb.ToString (); + } + + internal static bool isValidIdentifierName (string s) + { + int L = s.Length; + if (L == 0) + return false; + if (!(char.IsLetter (s [0]) || s [0].CompareTo ('$') == 0 || s [0].CompareTo ('_') == 0)) + return false; + for (int i = 1; i != L; ++i) { + if (!TokenStream.IsJavaIdentifierPart (s [i])) + return false; + } + return !TokenStream.isKeyword (s); + } + + + + internal static string DefaultObjectToString (IScriptable obj) + { + return "[object " + obj.ClassName + ']'; + } + + internal static string uneval (Context cx, IScriptable scope, object value) + { + if (value == null) { + return "null"; + } + if (value == Undefined.Value) { + return "undefined"; + } + if (value is string) { + string escaped = escapeString ((string)value); + System.Text.StringBuilder sb = new System.Text.StringBuilder (escaped.Length + 2); + sb.Append ('\"'); + sb.Append (escaped); + sb.Append ('\"'); + return sb.ToString (); + } + if (CliHelper.IsNumber (value)) { + double d = Convert.ToDouble (value); + if (d == 0 && 1 / d < 0) { + return "-0"; + } + return ScriptConvert.ToString (d); + } + if (value is bool) { + return ScriptConvert.ToString (value); + } + if (value is IScriptable) { + IScriptable obj = (IScriptable)value; + object v = ScriptableObject.GetProperty (obj, "toSource"); + if (v is IFunction) { + IFunction f = (IFunction)v; + return ScriptConvert.ToString (f.Call (cx, scope, obj, EmptyArgs)); + } + return ScriptConvert.ToString (value); + } + WarnAboutNonJSObject (value); + return value.ToString (); + } + + internal static string defaultObjectToSource (Context cx, IScriptable scope, IScriptable thisObj, object [] args) + { + using (Helpers.StackOverflowVerifier sov = new Helpers.StackOverflowVerifier (1024)) { + bool toplevel, iterating; + if (cx.iterating == null) { + toplevel = true; + iterating = false; + cx.iterating = new ObjToIntMap (31); + } + else { + toplevel = false; + iterating = cx.iterating.has (thisObj); + } + + System.Text.StringBuilder result = new System.Text.StringBuilder (128); + if (toplevel) { + result.Append ("("); + } + result.Append ('{'); + + // Make sure cx.iterating is set to null when done + // so we don't leak memory + try { + if (!iterating) { + cx.iterating.intern (thisObj); // stop recursion. + object [] ids = thisObj.GetIds (); + for (int i = 0; i < ids.Length; i++) { + if (i > 0) + result.Append (", "); + object id = ids [i]; + object value; + if (id is int) { + int intId = ((int)id); + value = thisObj.Get (intId, thisObj); + result.Append (intId); + } + else { + string strId = (string)id; + value = thisObj.Get (strId, thisObj); + if (ScriptRuntime.isValidIdentifierName (strId)) { + result.Append (strId); + } + else { + result.Append ('\''); + result.Append (ScriptRuntime.escapeString (strId, '\'')); + result.Append ('\''); + } + } + result.Append (':'); + result.Append (ScriptRuntime.uneval (cx, scope, value)); + } + } + } + finally { + if (toplevel) { + cx.iterating = null; + } + } + + result.Append ('}'); + if (toplevel) { + result.Append (')'); + } + return result.ToString (); + } + } + + + + + public static IScriptable NewObject (Context cx, IScriptable scope, string constructorName, object [] args) + { + scope = ScriptableObject.GetTopLevelScope (scope); + IFunction ctor = getExistingCtor (cx, scope, constructorName); + if (args == null) { + args = ScriptRuntime.EmptyArgs; + } + return ctor.Construct (cx, scope, args); + } + + + // TODO: this is until setDefaultNamespace will learn how to store NS + // TODO: properly and separates namespace form Scriptable.get etc. + private const string DEFAULT_NS_TAG = "__default_namespace__"; + + public static object setDefaultNamespace (object ns, Context cx) + { + IScriptable scope = cx.currentActivationCall; + if (scope == null) { + scope = getTopCallScope (cx); + } + + XMLLib xmlLib = CurrentXMLLib (cx); + object obj = xmlLib.ToDefaultXmlNamespace (cx, ns); + + // TODO: this should be in separated namesapce from Scriptable.get/put + if (!scope.Has (DEFAULT_NS_TAG, scope)) { + // TODO: this is racy of cause + ScriptableObject.DefineProperty (scope, DEFAULT_NS_TAG, obj, ScriptableObject.PERMANENT | ScriptableObject.DONTENUM); + } + else { + scope.Put (DEFAULT_NS_TAG, scope, obj); + } + + return Undefined.Value; + } + + public static object searchDefaultNamespace (Context cx) + { + IScriptable scope = cx.currentActivationCall; + if (scope == null) { + scope = getTopCallScope (cx); + } + object nsObject; + for (; ; ) { + IScriptable parent = scope.ParentScope; + if (parent == null) { + nsObject = ScriptableObject.GetProperty (scope, DEFAULT_NS_TAG); + if (nsObject == UniqueTag.NotFound) { + return null; + } + break; + } + nsObject = scope.Get (DEFAULT_NS_TAG, scope); + if (nsObject != UniqueTag.NotFound) { + break; + } + scope = parent; + } + return nsObject; + } + + public static object getTopLevelProp (IScriptable scope, string id) + { + scope = ScriptableObject.GetTopLevelScope (scope); + return ScriptableObject.GetProperty (scope, id); + } + + internal static IFunction getExistingCtor (Context cx, IScriptable scope, string constructorName) + { + object ctorVal = ScriptableObject.GetProperty (scope, constructorName); + if (ctorVal is IFunction) { + return (IFunction)ctorVal; + } + if (ctorVal == UniqueTag.NotFound) { + throw Context.ReportRuntimeErrorById ("msg.ctor.not.found", constructorName); + } + else { + throw Context.ReportRuntimeErrorById ("msg.not.ctor", constructorName); + } + } + + /// Return -1L if str is not an index or the index value as lower 32 + /// bits of the result. + /// + private static long indexFromString (string str) + { + // The length of the decimal string representation of + // Integer.MAX_VALUE, 2147483647 + const int MAX_VALUE_LENGTH = 10; + + int len = str.Length; + if (len > 0) { + int i = 0; + bool negate = false; + int c = str [0]; + if (c == '-') { + if (len > 1) { + c = str [1]; + i = 1; + negate = true; + } + } + c -= '0'; + if (0 <= c && c <= 9 && len <= (negate ? MAX_VALUE_LENGTH + 1 : MAX_VALUE_LENGTH)) { + // Use negative numbers to accumulate index to handle + // Integer.MIN_VALUE that is greater by 1 in absolute value + // then Integer.MAX_VALUE + int index = -c; + int oldIndex = 0; + i++; + if (index != 0) { + // Note that 00, 01, 000 etc. are not indexes + while (i != len && 0 <= (c = str [i] - '0') && c <= 9) { + oldIndex = index; + index = 10 * index - c; + i++; + } + } + // Make sure all characters were consumed and that it couldn't + // have overflowed. + if (i == len && (oldIndex > (int.MinValue / 10) || (oldIndex == (int.MinValue / 10) && c <= (negate ? -(int.MinValue % 10) : (int.MaxValue % 10))))) { + return unchecked ((int)0xFFFFFFFFL) & (negate ? index : -index); + } + } + } + return -1L; + } + + /// If str is a decimal presentation of Uint32 value, return it as long. + /// Othewise return -1L; + /// + public static long testUint32String (string str) + { + // The length of the decimal string representation of + // UINT32_MAX_VALUE, 4294967296 + const int MAX_VALUE_LENGTH = 10; + + int len = str.Length; + if (1 <= len && len <= MAX_VALUE_LENGTH) { + int c = str [0]; + c -= '0'; + if (c == 0) { + // Note that 00,01 etc. are not valid Uint32 presentations + return (len == 1) ? 0L : -1L; + } + if (1 <= c && c <= 9) { + long v = c; + for (int i = 1; i != len; ++i) { + c = str [i] - '0'; + if (!(0 <= c && c <= 9)) { + return -1; + } + v = 10 * v + c; + } + // Check for overflow + if ((ulong)v >> 32 == 0) { + return v; + } + } + } + return -1; + } + + /// If s represents index, then return index value wrapped as Integer + /// and othewise return s. + /// + internal static object getIndexObject (string s) + { + long indexTest = indexFromString (s); + if (indexTest >= 0) { + return (int)indexTest; + } + return s; + } + + /// If d is exact int value, return its value wrapped as Integer + /// and othewise return d converted to String. + /// + internal static object getIndexObject (double d) + { + int i = (int)d; + if ((double)i == d) { + return (int)i; + } + return ScriptConvert.ToString (d); + } + + /// If ScriptConvert.ToString(id) is a decimal presentation of int32 value, then id + /// is index. In this case return null and make the index available + /// as ScriptRuntime.lastIndexResult(cx). Otherwise return ScriptConvert.ToString(id). + /// + internal static string ToStringIdOrIndex (Context cx, object id) + { + if (CliHelper.IsNumber (id)) { + double d = Convert.ToDouble (id); + int index = (int)d; + if (((double)index) == d) { + storeIndexResult (cx, index); + return null; + } + return ScriptConvert.ToString (id); + } + else { + string s; + if (id is string) { + s = ((string)id); + } + else { + s = ScriptConvert.ToString (id); + } + long indexTest = indexFromString (s); + if (indexTest >= 0) { + storeIndexResult (cx, (int)indexTest); + return null; + } + return s; + } + } + + /// Call obj.[[Get]](id) + public static object getObjectElem (object obj, object elem, Context cx) + { + IScriptable sobj = ScriptConvert.ToObjectOrNull (cx, obj); + if (sobj == null) { + throw UndefReadError (obj, elem); + } + return getObjectElem (sobj, elem, cx); + } + + public static object getObjectElem (IScriptable obj, object elem, Context cx) + { + if (obj is XMLObject) { + XMLObject xmlObject = (XMLObject)obj; + return xmlObject.EcmaGet (cx, elem); + } + + object result; + + string s = ScriptRuntime.ToStringIdOrIndex (cx, elem); + if (s == null) { + int index = lastIndexResult (cx); + result = ScriptableObject.GetProperty (obj, index); + } + else { + result = ScriptableObject.GetProperty (obj, s); + } + + if (result == UniqueTag.NotFound) { + result = Undefined.Value; + } + + return result; + } + + /// Version of getObjectElem when elem is a valid JS identifier name. + public static object getObjectProp (object obj, string property, Context cx) + { + IScriptable sobj = ScriptConvert.ToObjectOrNull (cx, obj); + if (sobj == null) { + throw UndefReadError (obj, property); + } + return getObjectProp (sobj, property, cx); + } + + public static object getObjectProp (IScriptable obj, string property, Context cx) + { + if (obj is XMLObject) { + XMLObject xmlObject = (XMLObject)obj; + return xmlObject.EcmaGet (cx, property); + } + + object result = ScriptableObject.GetProperty (obj, property); + if (result == UniqueTag.NotFound) { + result = Undefined.Value; + } + + return result; + } + + /* + * A cheaper and less general version of the above for well-known argument + * types. + */ + public static object getObjectIndex (object obj, double dblIndex, Context cx) + { + IScriptable sobj = ScriptConvert.ToObjectOrNull (cx, obj); + if (sobj == null) { + throw UndefReadError (obj, ScriptConvert.ToString (dblIndex)); + } + int index = (int)dblIndex; + if ((double)index == dblIndex) { + return getObjectIndex (sobj, index, cx); + } + else { + string s = ScriptConvert.ToString (dblIndex); + return getObjectProp (sobj, s, cx); + } + } + + public static object getObjectIndex (IScriptable obj, int index, Context cx) + { + if (obj is XMLObject) { + XMLObject xmlObject = (XMLObject)obj; + return xmlObject.EcmaGet (cx, (object)index); + } + + object result = ScriptableObject.GetProperty (obj, index); + if (result == UniqueTag.NotFound) { + result = Undefined.Value; + } + + return result; + } + + /* + * Call obj.[[Put]](id, value) + */ + public static object setObjectElem (object obj, object elem, object value, Context cx) + { + IScriptable sobj = ScriptConvert.ToObjectOrNull (cx, obj); + if (sobj == null) { + throw UndefWriteError (obj, elem, value); + } + return setObjectElem (sobj, elem, value, cx); + } + + public static object setObjectElem (IScriptable obj, object elem, object value, Context cx) + { + if (obj is XMLObject) { + XMLObject xmlObject = (XMLObject)obj; + xmlObject.EcmaPut (cx, elem, value); + return value; + } + + string s = ScriptRuntime.ToStringIdOrIndex (cx, elem); + if (s == null) { + int index = lastIndexResult (cx); + ScriptableObject.PutProperty (obj, index, value); + } + else { + ScriptableObject.PutProperty (obj, s, value); + } + + return value; + } + + /// Version of setObjectElem when elem is a valid JS identifier name. + public static object setObjectProp (object obj, string property, object value, Context cx) + { + IScriptable sobj = ScriptConvert.ToObjectOrNull (cx, obj); + if (sobj == null) { + throw UndefWriteError (obj, property, value); + } + return setObjectProp (sobj, property, value, cx); + } + + public static object setObjectProp (IScriptable obj, string property, object value, Context cx) + { + if (obj is XMLObject) { + XMLObject xmlObject = (XMLObject)obj; + xmlObject.EcmaPut (cx, property, value); + } + else { + return ScriptableObject.PutProperty (obj, property, value); + } + return value; + } + + /* + * A cheaper and less general version of the above for well-known argument + * types. + */ + public static object setObjectIndex (object obj, double dblIndex, object value, Context cx) + { + IScriptable sobj = ScriptConvert.ToObjectOrNull (cx, obj); + if (sobj == null) { + throw UndefWriteError (obj, Convert.ToString (dblIndex), value); + } + int index = (int)dblIndex; + if ((double)index == dblIndex) { + return setObjectIndex (sobj, index, value, cx); + } + else { + string s = ScriptConvert.ToString (dblIndex); + return setObjectProp (sobj, s, value, cx); + } + } + + public static object setObjectIndex (IScriptable obj, int index, object value, Context cx) + { + if (obj is XMLObject) { + XMLObject xmlObject = (XMLObject)obj; + xmlObject.EcmaPut (cx, (object)index, value); + } + else { + return ScriptableObject.PutProperty (obj, index, value); + } + return value; + } + + public static bool deleteObjectElem (IScriptable target, object elem, Context cx) + { + bool result; + if (target is XMLObject) { + XMLObject xmlObject = (XMLObject)target; + result = xmlObject.EcmaDelete (cx, elem); + } + else { + string s = ScriptRuntime.ToStringIdOrIndex (cx, elem); + if (s == null) { + int index = lastIndexResult (cx); + result = ScriptableObject.DeleteProperty (target, index); + } + else { + result = ScriptableObject.DeleteProperty (target, s); + } + } + return result; + } + + public static bool hasObjectElem (IScriptable target, object elem, Context cx) + { + bool result; + + if (target is XMLObject) { + XMLObject xmlObject = (XMLObject)target; + result = xmlObject.EcmaHas (cx, elem); + } + else { + string s = ScriptRuntime.ToStringIdOrIndex (cx, elem); + if (s == null) { + int index = lastIndexResult (cx); + result = ScriptableObject.HasProperty (target, index); + } + else { + result = ScriptableObject.HasProperty (target, s); + } + } + + return result; + } + + public static object refGet (IRef rf, Context cx) + { + return rf.Get (cx); + } + + public static object refSet (IRef rf, object value, Context cx) + { + return rf.Set (cx, value); + } + + public static object refDel (IRef rf, Context cx) + { + return rf.Delete (cx); + } + + internal static bool isSpecialProperty (string s) + { + return s.Equals ("__proto__") || s.Equals ("__parent__"); + } + + public static IRef specialRef (object obj, string specialProperty, Context cx) + { + return SpecialRef.createSpecial (cx, obj, specialProperty); + } + + /// The delete operator + /// + /// See ECMA 11.4.1 + /// + /// In ECMA 0.19, the description of the delete operator (11.4.1) + /// assumes that the [[Delete]] method returns a value. However, + /// the definition of the [[Delete]] operator (8.6.2.5) does not + /// define a return value. Here we assume that the [[Delete]] + /// method doesn't return a value. + /// + public static object delete (object obj, object id, Context cx) + { + IScriptable sobj = ScriptConvert.ToObjectOrNull (cx, obj); + if (sobj == null) { + string idStr = (id == null) ? "null" : id.ToString (); + throw TypeErrorById ("msg.undef.prop.delete", ScriptConvert.ToString (obj), idStr); + } + bool result = deleteObjectElem (sobj, id, cx); + return result; + } + + /// Looks up a name in the scope chain and returns its value. + public static object name (Context cx, IScriptable scope, string name) + { + IScriptable parent = scope.ParentScope; + if (parent == null) { + object result = topScopeName (cx, scope, name); + if (result == UniqueTag.NotFound) { + throw NotFoundError (scope, name); + } + return result; + } + + return nameOrFunction (cx, scope, parent, name, false); + } + + private static object nameOrFunction (Context cx, IScriptable scope, IScriptable parentScope, string name, bool asFunctionCall) + { + object result; + IScriptable thisObj = scope; // It is used only if asFunctionCall==true. + + XMLObject firstXMLObject = null; + for (; ; ) { + if (scope is BuiltinWith) { + IScriptable withObj = scope.GetPrototype (); + if (withObj is XMLObject) { + XMLObject xmlObj = (XMLObject)withObj; + if (xmlObj.EcmaHas (cx, name)) { + // function this should be the target object of with + thisObj = xmlObj; + result = xmlObj.EcmaGet (cx, name); + break; + } + if (firstXMLObject == null) { + firstXMLObject = xmlObj; + } + } + else { + result = ScriptableObject.GetProperty (withObj, name); + if (result != UniqueTag.NotFound) { + // function this should be the target object of with + thisObj = withObj; + break; + } + } + } + else if (scope is BuiltinCall) { + // NativeCall does not prototype chain and Scriptable.get + // can be called directly. + result = scope.Get (name, scope); + if (result != UniqueTag.NotFound) { + if (asFunctionCall) { + // ECMA 262 requires that this for nested funtions + // should be top scope + thisObj = ScriptableObject.GetTopLevelScope (parentScope); + } + break; + } + } + else { + // Can happen if embedding decided that nested + // scopes are useful for what ever reasons. + result = ScriptableObject.GetProperty (scope, name); + if (result != UniqueTag.NotFound) { + thisObj = scope; + break; + } + } + scope = parentScope; + parentScope = parentScope.ParentScope; + if (parentScope == null) { + result = topScopeName (cx, scope, name); + if (result == UniqueTag.NotFound) { + if (firstXMLObject == null || asFunctionCall) { + throw NotFoundError (scope, name); + } + // The name was not found, but we did find an XML + // object in the scope chain and we are looking for name, + // not function. The result should be an empty XMLList + // in name context. + result = firstXMLObject.EcmaGet (cx, name); + } + // For top scope thisObj for functions is always scope itself. + thisObj = scope; + break; + } + } + + if (asFunctionCall) { + if (!(result is ICallable)) { + throw NotFunctionError (result, name); + } + storeScriptable (cx, thisObj); + } + + return result; + } + + private static object topScopeName (Context cx, IScriptable scope, string name) + { + if (cx.useDynamicScope) { + scope = checkDynamicScope (cx.topCallScope, scope); + } + return ScriptableObject.GetProperty (scope, name); + } + + + /// Returns the object in the scope chain that has a given property. + /// + /// The order of evaluation of an assignment expression involves + /// evaluating the lhs to a reference, evaluating the rhs, and then + /// modifying the reference with the rhs value. This method is used + /// to 'bind' the given name to an object containing that property + /// so that the side effects of evaluating the rhs do not affect + /// which property is modified. + /// Typically used in conjunction with setName. + /// + /// See ECMA 10.1.4 + /// + public static IScriptable bind (Context cx, IScriptable scope, string id) + { + IScriptable firstXMLObject = null; + IScriptable parent = scope.ParentScope; + if (parent != null) { + // Check for possibly nested "with" scopes first + while (scope is BuiltinWith) { + IScriptable withObj = scope.GetPrototype (); + if (withObj is XMLObject) { + XMLObject xmlObject = (XMLObject)withObj; + if (xmlObject.EcmaHas (cx, id)) { + return xmlObject; + } + if (firstXMLObject == null) { + firstXMLObject = xmlObject; + } + } + else { + if (ScriptableObject.HasProperty (withObj, id)) { + return withObj; + } + } + scope = parent; + parent = parent.ParentScope; + if (parent == null) { + + goto childScopesChecks_brk; + } + } + for (; ; ) { + if (ScriptableObject.HasProperty (scope, id)) { + return scope; + } + scope = parent; + parent = parent.ParentScope; + if (parent == null) { + + goto childScopesChecks_brk; + } + } + } + + childScopesChecks_brk: + ; + + // scope here is top scope + if (cx.useDynamicScope) { + scope = checkDynamicScope (cx.topCallScope, scope); + } + if (ScriptableObject.HasProperty (scope, id)) { + return scope; + } + // Nothing was found, but since XML objects always bind + // return one if found + return firstXMLObject; + } + + public static object setName (IScriptable bound, object value, Context cx, IScriptable scope, string id) + { + if (bound != null) { + if (bound is XMLObject) { + XMLObject xmlObject = (XMLObject)bound; + xmlObject.EcmaPut (cx, id, value); + } + else { + ScriptableObject.PutProperty (bound, id, value); + } + } + else { + // "newname = 7;", where 'newname' has not yet + // been defined, creates a new property in the + // top scope unless strict mode is specified. + if (cx.HasFeature (Context.Features.StrictVars)) { + throw Context.ReportRuntimeErrorById ("msg.assn.create.strict", id); + } + // Find the top scope by walking up the scope chain. + bound = ScriptableObject.GetTopLevelScope (scope); + if (cx.useDynamicScope) { + bound = checkDynamicScope (cx.topCallScope, bound); + } + bound.Put (id, bound, value); + } + return value; + } + + + + + + + /// Prepare for calling name(...): return function corresponding to + /// name and make current top scope available + /// as ScriptRuntime.lastStoredScriptable() for consumption as thisObj. + /// The caller must call ScriptRuntime.lastStoredScriptable() immediately + /// after calling this method. + /// + public static ICallable getNameFunctionAndThis (string name, Context cx, IScriptable scope) + { + IScriptable parent = scope.ParentScope; + if (parent == null) { + object result = topScopeName (cx, scope, name); + if (!(result is ICallable)) { + if (result == UniqueTag.NotFound) { + throw NotFoundError (scope, name); + } + else { + throw NotFunctionError (result, name); + } + } + // Top scope is not NativeWith or NativeCall => thisObj == scope + IScriptable thisObj = scope; + storeScriptable (cx, thisObj); + return (ICallable)result; + } + + // name will call storeScriptable(cx, thisObj); + return (ICallable)nameOrFunction (cx, scope, parent, name, true); + } + + /// Prepare for calling obj[id](...): return function corresponding to + /// obj[id] and make obj properly converted to Scriptable available + /// as ScriptRuntime.lastStoredScriptable() for consumption as thisObj. + /// The caller must call ScriptRuntime.lastStoredScriptable() immediately + /// after calling this method. + /// + public static ICallable GetElemFunctionAndThis (object obj, object elem, Context cx) + { + string s = ScriptRuntime.ToStringIdOrIndex (cx, elem); + if (s != null) { + return getPropFunctionAndThis (obj, s, cx); + } + int index = lastIndexResult (cx); + + IScriptable thisObj = ScriptConvert.ToObjectOrNull (cx, obj); + if (thisObj == null) { + throw UndefCallError (obj, Convert.ToString (index)); + } + + object value; + for (; ; ) { + // Ignore XML lookup as requred by ECMA 357, 11.2.2.1 + value = ScriptableObject.GetProperty (thisObj, index); + if (value != UniqueTag.NotFound) { + break; + } + if (!(thisObj is XMLObject)) { + break; + } + XMLObject xmlObject = (XMLObject)thisObj; + IScriptable extra = xmlObject.GetExtraMethodSource (cx); + if (extra == null) { + break; + } + thisObj = extra; + } + if (!(value is ICallable)) { + throw NotFunctionError (value, elem); + } + + storeScriptable (cx, thisObj); + return (ICallable)value; + } + + /// Prepare for calling obj.property(...): return function corresponding to + /// obj.property and make obj properly converted to Scriptable available + /// as ScriptRuntime.lastStoredScriptable() for consumption as thisObj. + /// The caller must call ScriptRuntime.lastStoredScriptable() immediately + /// after calling this method. + /// + public static ICallable getPropFunctionAndThis (object obj, string property, Context cx) + { + IScriptable thisObj = ScriptConvert.ToObjectOrNull (cx, obj); + if (thisObj == null) { + throw UndefCallError (obj, property); + } + + object value; + for (; ; ) { + // Ignore XML lookup as requred by ECMA 357, 11.2.2.1 + value = ScriptableObject.GetProperty (thisObj, property); + if (value != UniqueTag.NotFound) { + break; + } + if (!(thisObj is XMLObject)) { + break; + } + XMLObject xmlObject = (XMLObject)thisObj; + IScriptable extra = xmlObject.GetExtraMethodSource (cx); + if (extra == null) { + break; + } + thisObj = extra; + } + + if (value == UniqueTag.NotFound) { + //if (thisObj.Get ("__noSuchMethod__", thisObj) as ICallable != null) { + // return UniqueTag.NoSuchMethodMark; + //} + } + + if (!(value is ICallable)) { + throw NotFunctionError (value, property); + } + + storeScriptable (cx, thisObj); + return (ICallable)value; + } + + /// Prepare for calling (...): return function corresponding to + /// and make parent scope of the function available + /// as ScriptRuntime.lastStoredScriptable() for consumption as thisObj. + /// The caller must call ScriptRuntime.lastStoredScriptable() immediately + /// after calling this method. + /// + public static ICallable getValueFunctionAndThis (object value, Context cx) + { + if (!(value is ICallable)) { + throw NotFunctionError (value); + } + + ICallable f = (ICallable)value; + IScriptable thisObj; + if (f is IScriptable) { + thisObj = ((IScriptable)f).ParentScope; + } + else { + if (cx.topCallScope == null) + throw new Exception (); + thisObj = cx.topCallScope; + } + if (thisObj.ParentScope != null) { + if (thisObj is BuiltinWith) { + // functions defined inside with should have with target + // as their thisObj + } + else if (thisObj is BuiltinCall) { + // nested functions should have top scope as their thisObj + thisObj = ScriptableObject.GetTopLevelScope (thisObj); + } + } + storeScriptable (cx, thisObj); + return f; + } + + /// Perform function call in reference context. Should always + /// return value that can be passed to + /// {@link #refGet(Object)} or @link #refSet(Object, Object)} + /// arbitrary number of times. + /// The args array reference should not be stored in any object that is + /// can be GC-reachable after this method returns. If this is necessary, + /// store args.clone(), not args array itself. + /// + public static IRef callRef (ICallable function, IScriptable thisObj, object [] args, Context cx) + { + if (function is IRefCallable) { + IRefCallable rfunction = (IRefCallable)function; + IRef rf = rfunction.RefCall (cx, thisObj, args); + if (rf == null) { + throw new Exception (rfunction.GetType ().FullName + ".refCall() returned null"); + } + return rf; + } + // No runtime support for now + string msg = GetMessage ("msg.no.ref.from.function", ScriptConvert.ToString (function)); + throw ConstructError ("ReferenceError", msg); + } + + /// Operator new. + /// + /// See ECMA 11.2.2 + /// + public static IScriptable NewObject (object fun, Context cx, IScriptable scope, object [] args) + { + if (!(fun is IFunction)) { + throw NotFunctionError (fun); + } + IFunction function = (IFunction)fun; + return function.Construct (cx, scope, args); + } + + public static object callSpecial (Context cx, ICallable fun, IScriptable thisObj, object [] args, IScriptable scope, IScriptable callerThis, int callType, string filename, int lineNumber) + { + if (callType == Node.SPECIALCALL_EVAL) { + if (BuiltinGlobal.isEvalFunction (fun)) { + return evalSpecial (cx, scope, callerThis, args, filename, lineNumber); + } + } + else if (callType == Node.SPECIALCALL_WITH) { + if (BuiltinWith.IsWithFunction (fun)) { + throw Context.ReportRuntimeErrorById ("msg.only.from.new", "With"); + } + } + else { + throw Context.CodeBug (); + } + + return fun.Call (cx, scope, thisObj, args); + } + + public static object newSpecial (Context cx, object fun, object [] args, IScriptable scope, int callType) + { + if (callType == Node.SPECIALCALL_EVAL) { + if (BuiltinGlobal.isEvalFunction (fun)) { + throw TypeErrorById ("msg.not.ctor", "eval"); + } + } + else if (callType == Node.SPECIALCALL_WITH) { + if (BuiltinWith.IsWithFunction (fun)) { + return BuiltinWith.NewWithSpecial (cx, scope, args); + } + } + else { + throw Context.CodeBug (); + } + + return NewObject (fun, cx, scope, args); + } + + /// Function.prototype.apply and Function.prototype.call + /// + /// See Ecma 15.3.4.[34] + /// + public static object applyOrCall (bool isApply, Context cx, IScriptable scope, IScriptable thisObj, object [] args) + { + int L = args.Length; + ICallable function; + if (thisObj is ICallable) { + function = (ICallable)thisObj; + } + else { + object value = thisObj.GetDefaultValue (typeof (IFunction)); + if (!(value is ICallable)) { + throw ScriptRuntime.NotFunctionError (value, thisObj); + } + function = (ICallable)value; + } + + IScriptable callThis = null; + if (L != 0) { + callThis = ScriptConvert.ToObjectOrNull (cx, args [0]); + } + if (callThis == null) { + // This covers the case of args[0] == (null|undefined) as well. + callThis = getTopCallScope (cx); + } + + object [] callArgs; + if (isApply) { + // Follow Ecma 15.3.4.3 + if (L <= 1) { + callArgs = ScriptRuntime.EmptyArgs; + } + else { + object arg1 = args [1]; + if (arg1 == null || arg1 == Undefined.Value) { + callArgs = ScriptRuntime.EmptyArgs; + } + else if (arg1 is BuiltinArray || arg1 is Arguments) { + callArgs = cx.GetElements ((IScriptable)arg1); + } + else { + throw ScriptRuntime.TypeErrorById ("msg.arg.isnt.array"); + } + } + } + else { + // Follow Ecma 15.3.4.4 + if (L <= 1) { + callArgs = ScriptRuntime.EmptyArgs; + } + else { + callArgs = new object [L - 1]; + Array.Copy (args, 1, callArgs, 0, L - 1); + } + } + + return function.Call (cx, scope, callThis, callArgs); + } + + /// The eval function property of the global object. + /// + /// See ECMA 15.1.2.1 + /// + public static object evalSpecial (Context cx, IScriptable scope, object thisArg, object [] args, string filename, int lineNumber) + { + if (args.Length < 1) + return Undefined.Value; + object x = args [0]; + if (!(x is string)) { + if (cx.HasFeature (Context.Features.StrictEval)) { + throw Context.ReportRuntimeErrorById ("msg.eval.nonstring.strict"); + } + string message = ScriptRuntime.GetMessage ("msg.eval.nonstring"); + Context.ReportWarning (message); + return x; + } + if (filename == null) { + int [] linep = new int [1]; + filename = Context.GetSourcePositionFromStack (linep); + if (filename != null) { + lineNumber = linep [0]; + } + else { + filename = ""; + } + } + string sourceName = ScriptRuntime.makeUrlForGeneratedScript (true, filename, lineNumber); + + ErrorReporter reporter; + reporter = DefaultErrorReporter.ForEval (cx.ErrorReporter); + + // Compile with explicit interpreter instance to force interpreter + // mode. + IScript script = cx.CompileString ((string)x, new Interpreter (), reporter, sourceName, 1, (object)null); + ((InterpretedFunction)script).idata.evalScriptFlag = true; + ICallable c = (ICallable)script; + return c.Call (cx, scope, (IScriptable)thisArg, ScriptRuntime.EmptyArgs); + } + + /// The typeof operator + public static string Typeof (object value) + { + if (value == null) + return "object"; + if (value == Undefined.Value) + return "undefined"; + if (value is IScriptable) { + if (value is XMLObject) + return "xml"; + + return (value is ICallable && !(value is BuiltinRegExp)) ? "function" : "object"; + } + if (value is string) + return "string"; + if (value is char || CliHelper.IsNumber (value)) + return "number"; + if (value is bool) + return "boolean"; + throw errorWithClassName ("msg.invalid.type", value); + } + + + internal static Exception errorWithClassName (string msg, object val) + { + return Context.ReportRuntimeErrorById (msg, val.GetType ().FullName); + } + + /// The typeof operator that correctly handles the undefined case + public static string TypeofName (IScriptable scope, string id) + { + Context cx = Context.CurrentContext; + IScriptable val = bind (cx, scope, id); + if (val == null) + return "undefined"; + return Typeof (getObjectProp (val, id, cx)); + } + + // neg: + // implement the '-' operator inline in the caller + // as "-ScriptConvert.ToNumber(val)" + + // not: + // implement the '!' operator inline in the caller + // as "!toBoolean(val)" + + // bitnot: + // implement the '~' operator inline in the caller + // as "~toInt32(val)" + + public static object Add (object val1, object val2, Context cx) + { + if (CliHelper.IsNumber (val1) && CliHelper.IsNumber (val2)) { + return (double)val1 + (double)val2; + } + if (val1 is XMLObject) { + object test = ((XMLObject)val1).AddValues (cx, true, val2); + if (test != UniqueTag.NotFound) { + return test; + } + } + if (val2 is XMLObject) { + object test = ((XMLObject)val2).AddValues (cx, false, val1); + if (test != UniqueTag.NotFound) { + return test; + } + } + if (val1 is EcmaScript.NET.Types.Cli.CliEventInfo) { + return ((EcmaScript.NET.Types.Cli.CliEventInfo)val1).Add (val2, cx); + } + if (val1 is IScriptable) + val1 = ((IScriptable)val1).GetDefaultValue (null); + if (val2 is IScriptable) + val2 = ((IScriptable)val2).GetDefaultValue (null); + if (!(val1 is string) && !(val2 is string)) + if ((CliHelper.IsNumber (val1)) && (CliHelper.IsNumber (val2))) + return (double)val1 + (double)val2; + else + return ScriptConvert.ToNumber (val1) + ScriptConvert.ToNumber (val2); + return string.Concat (ScriptConvert.ToString (val1), ScriptConvert.ToString (val2)); + } + + public static object nameIncrDecr (IScriptable scopeChain, string id, int incrDecrMask) + { + IScriptable target; + object value; + { + do { + target = scopeChain; + do { + value = target.Get (id, scopeChain); + if (value != UniqueTag.NotFound) { + + goto search_brk; + } + target = target.GetPrototype (); + } + while (target != null); + scopeChain = scopeChain.ParentScope; + } + while (scopeChain != null); + throw NotFoundError (scopeChain, id); + } + + search_brk: + ; + + return doScriptableIncrDecr (target, id, scopeChain, value, incrDecrMask); + } + + public static object propIncrDecr (object obj, string id, Context cx, int incrDecrMask) + { + IScriptable start = ScriptConvert.ToObjectOrNull (cx, obj); + if (start == null) { + throw UndefReadError (obj, id); + } + + IScriptable target = start; + object value; + { + do { + value = target.Get (id, start); + if (value != UniqueTag.NotFound) { + + goto search1_brk; + } + target = target.GetPrototype (); + } + while (target != null); + start.Put (id, start, double.NaN); + return double.NaN; + } + + search1_brk: + ; + + return doScriptableIncrDecr (target, id, start, value, incrDecrMask); + } + + private static object doScriptableIncrDecr (IScriptable target, string id, IScriptable protoChainStart, object value, int incrDecrMask) + { + bool post = ((incrDecrMask & Node.POST_FLAG) != 0); + double number; + if (CliHelper.IsNumber (value)) { + number = Convert.ToDouble (value); + } + else { + number = ScriptConvert.ToNumber (value); + if (post) { + // convert result to number + value = number; + } + } + if ((incrDecrMask & Node.DECR_FLAG) == 0) { + ++number; + } + else { + --number; + } + object result = number; + target.Put (id, protoChainStart, result); + if (post) { + return value; + } + else { + return result; + } + } + + public static object elemIncrDecr (object obj, object index, Context cx, int incrDecrMask) + { + object value = getObjectElem (obj, index, cx); + bool post = ((incrDecrMask & Node.POST_FLAG) != 0); + double number; + if (CliHelper.IsNumber (value)) { + number = Convert.ToDouble (value); + } + else { + number = ScriptConvert.ToNumber (value); + if (post) { + // convert result to number + value = number; + } + } + if ((incrDecrMask & Node.DECR_FLAG) == 0) { + ++number; + } + else { + --number; + } + object result = number; + setObjectElem (obj, index, result, cx); + if (post) { + return value; + } + else { + return result; + } + } + + public static object refIncrDecr (IRef rf, Context cx, int incrDecrMask) + { + object value = rf.Get (cx); + bool post = ((incrDecrMask & Node.POST_FLAG) != 0); + double number; + if (CliHelper.IsNumber (value)) { + number = Convert.ToDouble (value); + } + else { + number = ScriptConvert.ToNumber (value); + if (post) { + // convert result to number + value = number; + } + } + if ((incrDecrMask & Node.DECR_FLAG) == 0) { + ++number; + } + else { + --number; + } + rf.Set (cx, number); + if (post) { + return value; + } + else { + return number; + } + } + + + /// Equality + /// + /// See ECMA 11.9 + /// + public static bool eq (object x, object y) + { + if (x == null || x == Undefined.Value) { + if (y == null || y == Undefined.Value) { + return true; + } + if (y is ScriptableObject) { + object test = ((ScriptableObject)y).EquivalentValues (x); + if (test != UniqueTag.NotFound) { + return ((bool)test); + } + } + return false; + } + else if (CliHelper.IsNumber (x)) { + return eqNumber (Convert.ToDouble (x), y); + } + else if (x is string) { + return eqString ((string)x, y); + } + else if (x is bool) { + bool b = ((bool)x); + if (y is bool) { + return b == ((bool)y); + } + if (y is ScriptableObject) { + object test = ((ScriptableObject)y).EquivalentValues (x); + if (test != UniqueTag.NotFound) { + return ((bool)test); + } + } + return eqNumber (b ? 1.0 : 0.0, y); + } + else if (x is IScriptable) { + if (y is IScriptable) { + if (x == y) { + return true; + } + if (x is ScriptableObject) { + object test = ((ScriptableObject)x).EquivalentValues (y); + if (test != UniqueTag.NotFound) { + return ((bool)test); + } + } + if (y is ScriptableObject) { + object test = ((ScriptableObject)y).EquivalentValues (x); + if (test != UniqueTag.NotFound) { + return ((bool)test); + } + } + if (x is Wrapper && y is Wrapper) { + return ((Wrapper)x).Unwrap () == ((Wrapper)y).Unwrap (); + } + return false; + } + else if (y is bool) { + if (x is ScriptableObject) { + object test = ((ScriptableObject)x).EquivalentValues (y); + if (test != UniqueTag.NotFound) { + return ((bool)test); + } + } + double d = ((bool)y) ? 1.0 : 0.0; + return eqNumber (d, x); + } + else if (CliHelper.IsNumber (y)) { + return eqNumber (Convert.ToDouble (y), x); + } + else if (y is string) { + return eqString ((string)y, x); + } + // covers the case when y == Undefined.instance as well + return false; + } + else { + WarnAboutNonJSObject (x); + return x == y; + } + } + + internal static bool eqNumber (double x, object y) + { + for (; ; ) { + if (y == null || y == Undefined.Value) { + return false; + } + else if (CliHelper.IsNumber (y)) { + return x == Convert.ToDouble (y); + } + else if (y is string) { + return x == ScriptConvert.ToNumber (y); + } + else if (y is bool) { + return x == (((bool)y) ? 1.0 : +0.0); + } + else if (y is IScriptable) { + if (y is ScriptableObject) { + object xval = x; + object test = ((ScriptableObject)y).EquivalentValues (xval); + if (test != UniqueTag.NotFound) { + return ((bool)test); + } + } + y = ScriptConvert.ToPrimitive (y); + } + else { + WarnAboutNonJSObject (y); + return false; + } + } + } + + private static bool eqString (string x, object y) + { + for (; ; ) { + if (y == null || y == Undefined.Value) { + return false; + } + else if (y is string) { + return x.Equals (y); + } + else if (CliHelper.IsNumber (y)) { + return ScriptConvert.ToNumber (x) == Convert.ToDouble (y); + } + else if (y is bool) { + return ScriptConvert.ToNumber (x) == (((bool)y) ? 1.0 : 0.0); + } + else if (y is IScriptable) { + if (y is ScriptableObject) { + object test = ((ScriptableObject)y).EquivalentValues (x); + if (test != UniqueTag.NotFound) { + return ((bool)test); + } + } + y = ScriptConvert.ToPrimitive (y); + continue; + } + else { + WarnAboutNonJSObject (y); + return false; + } + } + } + public static bool shallowEq (object x, object y) + { + if (x == y) { + if (!(CliHelper.IsNumber (x))) { + return true; + } + // double.NaN check + double d = Convert.ToDouble (x); + return !double.IsNaN (d); + } + if (x == null || x == Undefined.Value) { + return false; + } + else if (CliHelper.IsNumber (x)) { + if (CliHelper.IsNumber (y)) { + return Convert.ToDouble (x) == Convert.ToDouble (y); + } + } + else if (x is string) { + if (y is string) { + return x.Equals (y); + } + } + else if (x is bool) { + if (y is bool) { + return x.Equals (y); + } + } + else if (x is IScriptable) { + if (x is Wrapper && y is Wrapper) { + return ((Wrapper)x).Unwrap () == ((Wrapper)y).Unwrap (); + } + } + else { + WarnAboutNonJSObject (x); + return x == y; + } + return false; + } + + /// The instanceof operator. + /// + /// + /// a instanceof b + /// + public static bool InstanceOf (object a, object b, Context cx) + { + IScriptable sB = (b as IScriptable); + + // Check RHS is an object + if (sB == null) { + throw TypeErrorById ("msg.instanceof.not.object"); + } + + IScriptable sA = (a as IScriptable); + + // for primitive values on LHS, return false + // TODO we may want to change this so that 5 instanceof Number == true + if (sA == null) { + return false; + } + + + return sB.HasInstance (sA); + } + + /// Delegates to + /// + /// + /// true iff rhs appears in lhs' proto chain + /// + protected internal static bool jsDelegatesTo (IScriptable lhs, IScriptable rhs) + { + IScriptable proto = lhs.GetPrototype (); + + while (proto != null) { + if (proto.Equals (rhs)) + return true; + proto = proto.GetPrototype (); + } + + return false; + } + + /// The in operator. + /// + /// This is a new JS 1.3 language feature. The in operator mirrors + /// the operation of the for .. in construct, and tests whether the + /// rhs has the property given by the lhs. It is different from the + /// for .. in construct in that: + ///
- it doesn't perform ToObject on the right hand side + ///
- it returns true for DontEnum properties. + ///
+ /// the left hand operand + /// + /// the right hand operand + /// + /// + /// true if property name or element number a is a property of b + /// + public static bool In (object a, object b, Context cx) + { + if (!(b is IScriptable)) { + throw TypeErrorById ("msg.instanceof.not.object"); + } + + return hasObjectElem ((IScriptable)b, a, cx); + } + + public static bool cmp_LT (object val1, object val2) + { + double d1, d2; + if (CliHelper.IsNumber (val1) && CliHelper.IsNumber (val2)) { + d1 = Convert.ToDouble (val1); + d2 = Convert.ToDouble (val2); + } + else { + if (val1 is IScriptable) + val1 = ((IScriptable)val1).GetDefaultValue (typeof (long)); + if (val2 is IScriptable) + val2 = ((IScriptable)val2).GetDefaultValue (typeof (long)); + if (val1 is string && val2 is string) { + return String.CompareOrdinal (((string)val1), (string)val2) < 0; + } + d1 = ScriptConvert.ToNumber (val1); + d2 = ScriptConvert.ToNumber (val2); + } + return d1 < d2; + } + + public static bool cmp_LE (object val1, object val2) + { + double d1, d2; + if (CliHelper.IsNumber (val1) && CliHelper.IsNumber (val2)) { + d1 = Convert.ToDouble (val1); + d2 = Convert.ToDouble (val2); + } + else { + if (val1 is IScriptable) + val1 = ((IScriptable)val1).GetDefaultValue (typeof (long)); + if (val2 is IScriptable) + val2 = ((IScriptable)val2).GetDefaultValue (typeof (long)); + if (val1 is string && val2 is string) { + return String.CompareOrdinal (((string)val1), (string)val2) <= 0; + } + d1 = ScriptConvert.ToNumber (val1); + d2 = ScriptConvert.ToNumber (val2); + } + return d1 <= d2; + } + + + public static bool hasTopCall (Context cx) + { + return (cx.topCallScope != null); + } + + public static IScriptable getTopCallScope (Context cx) + { + IScriptable scope = cx.topCallScope; + if (scope == null) { + throw new Exception (); + } + return scope; + } + + public static object DoTopCall (ICallable callable, Context cx, IScriptable scope, IScriptable thisObj, object [] args) + { + if (scope == null) + throw new ArgumentException (); + if (cx.topCallScope != null) + throw new Exception (); + + object result; + cx.topCallScope = ScriptableObject.GetTopLevelScope (scope); + cx.useDynamicScope = cx.HasFeature (Context.Features.DynamicScope); + ContextFactory f = cx.Factory; + try { + result = f.DoTopCall (callable, cx, scope, thisObj, args); + } + finally { + cx.topCallScope = null; + // Cleanup cached references + cx.cachedXMLLib = null; + + if (cx.currentActivationCall != null) { + // Function should always call exitActivationFunction + // if it creates activation record + throw new Exception ( + "ActivationCall without exitActivationFunction() invokation." + ); + } + } + return result; + } + + /// Return possibleDynamicScope if staticTopScope + /// is present on its prototype chain and return staticTopScope + /// otherwise. + /// Should only be called when staticTopScope is top scope. + /// + internal static IScriptable checkDynamicScope (IScriptable possibleDynamicScope, IScriptable staticTopScope) + { + // Return cx.topCallScope if scope + if (possibleDynamicScope == staticTopScope) { + return possibleDynamicScope; + } + IScriptable proto = possibleDynamicScope; + for (; ; ) { + proto = proto.GetPrototype (); + if (proto == staticTopScope) { + return possibleDynamicScope; + } + if (proto == null) { + return staticTopScope; + } + } + } + + public static void initScript (BuiltinFunction funObj, IScriptable thisObj, Context cx, IScriptable scope, bool evalScript) + { + if (cx.topCallScope == null) + throw new Exception (); + + int varCount = funObj.ParamAndVarCount; + if (varCount != 0) { + + IScriptable varScope = scope; + // Never define any variables from var statements inside with + // object. See bug 38590. + while (varScope is BuiltinWith) { + varScope = varScope.ParentScope; + } + + for (int i = varCount; i-- != 0; ) { + string name = funObj.getParamOrVarName (i); + // Don't overwrite existing def if already defined in object + // or prototypes of object. + if (!ScriptableObject.HasProperty (scope, name)) { + if (!evalScript) { + // Global var definitions are supposed to be DONTDELETE + ScriptableObject.DefineProperty (varScope, name, Undefined.Value, ScriptableObject.PERMANENT); + } + else { + varScope.Put (name, varScope, Undefined.Value); + } + } + } + } + } + + public static IScriptable createFunctionActivation (BuiltinFunction funObj, IScriptable scope, object [] args) + { + return new BuiltinCall (funObj, scope, args); + } + + + public static void enterActivationFunction (Context cx, IScriptable activation) + { + if (cx.topCallScope == null) + throw new Exception (); + + BuiltinCall call = (BuiltinCall)activation; + call.parentActivationCall = cx.currentActivationCall; + cx.currentActivationCall = call; + } + + public static void exitActivationFunction (Context cx) + { + BuiltinCall call = cx.currentActivationCall; + cx.currentActivationCall = call.parentActivationCall; + call.parentActivationCall = null; + } + + internal static BuiltinCall findFunctionActivation (Context cx, IFunction f) + { + BuiltinCall call = cx.currentActivationCall; + while (call != null) { + if (call.function == f) + return call; + call = call.parentActivationCall; + } + return null; + } + + public static IScriptable NewCatchScope (Exception t, IScriptable lastCatchScope, string exceptionName, Context cx, IScriptable scope) + { + object obj; + bool cacheObj; + + if (t is EcmaScriptThrow) { + cacheObj = false; + obj = ((EcmaScriptThrow)t).Value; + } + else { + cacheObj = true; + + // Create wrapper object unless it was associated with + // the previous scope object + + if (lastCatchScope != null) { + BuiltinObject last = (BuiltinObject)lastCatchScope; + obj = last.GetAssociatedValue (t); + if (obj == null) + Context.CodeBug (); + + goto getObj_brk; + } + + EcmaScriptException re; + string errorName; + string errorMsg; + + Exception javaException = null; + + if (t is EcmaScriptError) { + EcmaScriptError ee = (EcmaScriptError)t; + re = ee; + errorName = ee.Name; + errorMsg = ee.ErrorMessage; + } + else if (t is EcmaScriptRuntimeException) { + re = (EcmaScriptRuntimeException)t; + if (t.InnerException != null) { + javaException = t.InnerException; + errorName = "JavaException"; + errorMsg = javaException.GetType ().FullName + ": " + javaException.Message; + } + else { + errorName = "InternalError"; + errorMsg = re.Message; + } + } + else { + // Script can catch only instances of JavaScriptException, + // EcmaError and EvaluatorException + throw Context.CodeBug (); + } + + string sourceUri = re.SourceName; + if (sourceUri == null) { + sourceUri = ""; + } + int line = re.LineNumber; + object [] args; + if (line > 0) { + args = new object [] { errorMsg, sourceUri, (int)line }; + } + else { + args = new object [] { errorMsg, sourceUri }; + } + + IScriptable errorObject = cx.NewObject (scope, errorName, args); + ScriptableObject.PutProperty (errorObject, "name", errorName); + + if (javaException != null) { + object wrap = cx.Wrap (scope, javaException, null); + ScriptableObject.DefineProperty (errorObject, "javaException", wrap, ScriptableObject.PERMANENT | ScriptableObject.READONLY); + } + if (re != null) { + object wrap = cx.Wrap (scope, re, null); + ScriptableObject.DefineProperty (errorObject, "rhinoException", wrap, ScriptableObject.PERMANENT | ScriptableObject.READONLY); + } + + obj = errorObject; + } + + getObj_brk: + ; + + + + BuiltinObject catchScopeObject = new BuiltinObject (); + // See ECMA 12.4 + catchScopeObject.DefineProperty (exceptionName, obj, ScriptableObject.PERMANENT); + if (cacheObj) { + catchScopeObject.AssociateValue (t, obj); + } + return catchScopeObject; + } + + public static IScriptable enterWith (object obj, Context cx, IScriptable scope) + { + IScriptable sobj = ScriptConvert.ToObjectOrNull (cx, obj); + if (sobj == null) { + throw TypeErrorById ("msg.undef.with", ScriptConvert.ToString (obj)); + } + if (sobj is XMLObject) { + XMLObject xmlObject = (XMLObject)sobj; + return xmlObject.EnterWith (scope); + } + return new BuiltinWith (scope, sobj); + } + + public static IScriptable leaveWith (IScriptable scope) + { + BuiltinWith nw = (BuiltinWith)scope; + return nw.ParentScope; + } + + public static IScriptable enterDotQuery (object value, IScriptable scope) + { + if (!(value is XMLObject)) { + throw NotXmlError (value); + } + XMLObject obj = (XMLObject)value; + return obj.EnterDotQuery (scope); + } + + public static object updateDotQuery (bool value, IScriptable scope) + { + // Return null to continue looping + BuiltinWith nw = (BuiltinWith)scope; + return nw.UpdateDotQuery (value); + } + + public static IScriptable leaveDotQuery (IScriptable scope) + { + BuiltinWith nw = (BuiltinWith)scope; + return nw.ParentScope; + } + + public static void setFunctionProtoAndParent (BaseFunction fn, IScriptable scope) + { + fn.ParentScope = scope; + fn.SetPrototype (ScriptableObject.GetFunctionPrototype (scope)); + } + + public static void setObjectProtoAndParent (ScriptableObject obj, IScriptable scope) + { + // Compared with function it always sets the scope to top scope + scope = ScriptableObject.GetTopLevelScope (scope); + obj.ParentScope = scope; + IScriptable proto = ScriptableObject.getClassPrototype (scope, obj.ClassName); + obj.SetPrototype (proto); + } + + public static void initFunction (Context cx, IScriptable scope, BuiltinFunction function, int type, bool fromEvalCode) + { + if (type == FunctionNode.FUNCTION_STATEMENT) { + string name = function.FunctionName; + if (name != null && name.Length != 0) { + if (!fromEvalCode) { + // ECMA specifies that functions defined in global and + // function scope outside eval should have DONTDELETE set. + ScriptableObject.DefineProperty (scope, name, function, ScriptableObject.PERMANENT); + } + else { + scope.Put (name, scope, function); + } + } + } + else if (type == FunctionNode.FUNCTION_EXPRESSION_STATEMENT) { + string name = function.FunctionName; + if (name != null && name.Length != 0) { + // Always put function expression statements into initial + // activation object ignoring the with statement to follow + // SpiderMonkey + while (scope is BuiltinWith) { + scope = scope.ParentScope; + } + scope.Put (name, scope, function); + } + } + else { + throw Context.CodeBug (); + } + } + + public static IScriptable newArrayLiteral (object [] objects, int [] skipIndexces, Context cx, IScriptable scope) + { + int count = objects.Length; + int skipCount = 0; + if (skipIndexces != null) { + skipCount = skipIndexces.Length; + } + int length = count + skipCount; + int lengthObj = (int)length; + IScriptable arrayObj; + /* + * If the version is 120, then new Array(4) means create a new + * array with 4 as the first element. In this case, we have to + * set length property manually. + */ + if (cx.Version == Context.Versions.JS1_2) { + arrayObj = cx.NewObject (scope, "Array", ScriptRuntime.EmptyArgs); + ScriptableObject.PutProperty (arrayObj, "length", (object)lengthObj); + } + else { + arrayObj = cx.NewObject (scope, "Array", new object [] { lengthObj }); + } + int skip = 0; + for (int i = 0, j = 0; i != length; ++i) { + if (skip != skipCount && skipIndexces [skip] == i) { + ++skip; + continue; + } + ScriptableObject.PutProperty (arrayObj, i, objects [j]); + ++j; + } + return arrayObj; + } + + public static IScriptable newObjectLiteral (object [] propertyIds, object [] propertyValues, Context cx, IScriptable scope) + { + IScriptable obj = cx.NewObject (scope); + for (int i = 0, end = propertyIds.Length; i != end; ++i) { + object id = propertyIds [i]; + object value = propertyValues [i]; + + if (id is Node.GetterPropertyLiteral) { + BuiltinObject nativeObj = (BuiltinObject)obj; + InterpretedFunction fun = (InterpretedFunction)value; + nativeObj.DefineGetter ((string)((Node.GetterPropertyLiteral)id).Property, fun); + } + else if (id is Node.SetterPropertyLiteral) { + BuiltinObject nativeObj = (BuiltinObject)obj; + InterpretedFunction fun = (InterpretedFunction)value; + nativeObj.DefineSetter ((string)((Node.SetterPropertyLiteral)id).Property, fun); + } + else if (id is string) { + ScriptableObject.PutProperty (obj, (string)id, value); + } + else { + ScriptableObject.PutProperty (obj, (int)id, value); + } + } + return obj; + } + + public static bool isArrayObject (object obj) + { + return obj is BuiltinArray || obj is Arguments; + } + + public static object [] getArrayElements (IScriptable obj) + { + Context cx = Context.CurrentContext; + long longLen = BuiltinArray.getLengthProperty (cx, obj); + if (longLen > int.MaxValue) { + // arrays beyond MAX_INT is not in Java in any case + throw new ArgumentException (); + } + int len = (int)longLen; + if (len == 0) { + return ScriptRuntime.EmptyArgs; + } + else { + object [] result = new object [len]; + for (int i = 0; i < len; i++) { + object elem = ScriptableObject.GetProperty (obj, i); + result [i] = (elem == UniqueTag.NotFound) ? Undefined.Value : elem; + } + return result; + } + } + + internal static void checkDeprecated (Context cx, string name) + { + Context.Versions version = cx.Version; + if (version >= Context.Versions.JS1_4 || version == Context.Versions.Default) { + string msg = GetMessage ("msg.deprec.ctor", name); + if (version == Context.Versions.Default) + Context.ReportWarning (msg); + else + throw Context.ReportRuntimeError (msg); + } + } + + + private static ResourceManager m_ResourceManager = null; + + public static string GetMessage (string messageId, params object [] arguments) + { + Context cx = Context.CurrentContext; + + // Get current culture + CultureInfo culture = null; + if (cx != null) + culture = cx.CurrentCulture; + + if (m_ResourceManager == null) { + m_ResourceManager = new ResourceManager ( + "EcmaScript.NET.Resources.Messages", typeof (ScriptRuntime).Assembly); + } + + string formatString = m_ResourceManager.GetString (messageId, culture); + if (formatString == null) + throw new Exception ("Missing no message resource found for message property " + messageId); + + if (arguments == null) + arguments = new object [0]; + if (arguments.Length == 0) + return formatString; + return string.Format (formatString, arguments); + } + + public static EcmaScriptError ConstructError (string error, string message) + { + int [] linep = new int [1]; + string filename = Context.GetSourcePositionFromStack (linep); + return ConstructError (error, message, filename, linep [0], null, 0); + } + + public static EcmaScriptError ConstructError (string error, string message, string sourceName, int lineNumber, string lineSource, int columnNumber) + { + return new EcmaScriptError (error, message, sourceName, lineNumber, lineSource, columnNumber); + } + + public static EcmaScriptError TypeError (string message) + { + return ConstructError ("TypeError", message); + } + + public static EcmaScriptError TypeErrorById (string messageId, params string [] args) + { + return TypeError (GetMessage (messageId, args)); + } + + public static Exception UndefReadError (object obj, object id) + { + string idStr = (id == null) ? "null" : id.ToString (); + return TypeErrorById ("msg.undef.prop.read", ScriptConvert.ToString (obj), idStr); + } + + public static Exception UndefCallError (object obj, object id) + { + string idStr = (id == null) ? "null" : id.ToString (); + return TypeErrorById ("msg.undef.method.call", ScriptConvert.ToString (obj), idStr); + } + + public static Exception UndefWriteError (object obj, object id, object value) + { + string idStr = (id == null) ? "null" : id.ToString (); + string valueStr = (value is IScriptable) ? value.ToString () : ScriptConvert.ToString (value); + return TypeErrorById ("msg.undef.prop.write", ScriptConvert.ToString (obj), idStr, valueStr); + } + + public static Exception NotFoundError (IScriptable obj, string property) + { + // TODO: use object to improve the error message + string msg = GetMessage ("msg.is.not.defined", property); + throw ConstructError ("ReferenceError", msg); + } + + public static Exception NotFunctionError (object value) + { + return NotFunctionError (value, value); + } + + public static Exception NotFunctionError (object value, object messageHelper) + { + // TODO: Use value for better error reporting + string msg = (messageHelper == null) ? "null" : messageHelper.ToString (); + if (value == UniqueTag.NotFound) { + return TypeErrorById ("msg.function.not.found", msg); + } + return TypeErrorById ("msg.isnt.function", msg, value == null ? "null" : value.GetType ().FullName); + } + + private static Exception NotXmlError (object value) + { + throw TypeErrorById ("msg.isnt.xml.object", ScriptConvert.ToString (value)); + } + + internal static void WarnAboutNonJSObject (object nonJSObject) + { + string message = "+++ USAGE WARNING: Missed Context.Wrap() conversion:\n" + + "Runtime detected object " + nonJSObject + " of class " + nonJSObject.GetType ().FullName + " where it expected String, Number, Boolean or Scriptable instance. " + + "Please check your code for missig Context.Wrap() call."; + + Context.ReportWarning (message); + Console.Error.WriteLine (message); + } + + + private static XMLLib CurrentXMLLib (Context cx) + { + // Scripts should be running to access this + if (cx.topCallScope == null) + throw new Exception (); + + XMLLib xmlLib = cx.cachedXMLLib; + if (xmlLib == null) { + xmlLib = XMLLib.ExtractFromScope (cx.topCallScope); + if (xmlLib == null) + throw new Exception (); + cx.cachedXMLLib = xmlLib; + } + + return xmlLib; + } + + /// Escapes the reserved characters in a value of an attribute + /// + /// + /// Unescaped text + /// + /// The escaped text + /// + public static string escapeAttributeValue (object value, Context cx) + { + XMLLib xmlLib = CurrentXMLLib (cx); + return xmlLib.EscapeAttributeValue (value); + } + + /// Escapes the reserved characters in a value of a text node + /// + /// + /// Unescaped text + /// + /// The escaped text + /// + public static string escapeTextValue (object value, Context cx) + { + XMLLib xmlLib = CurrentXMLLib (cx); + return xmlLib.EscapeTextValue (value); + } + + public static IRef memberRef (object obj, object elem, Context cx, int memberTypeFlags) + { + if (!(obj is XMLObject)) { + throw NotXmlError (obj); + } + XMLObject xmlObject = (XMLObject)obj; + return xmlObject.MemberRef (cx, elem, memberTypeFlags); + } + + public static IRef memberRef (object obj, object ns, object elem, Context cx, int memberTypeFlags) + { + if (!(obj is XMLObject)) { + throw NotXmlError (obj); + } + XMLObject xmlObject = (XMLObject)obj; + return xmlObject.MemberRef (cx, ns, elem, memberTypeFlags); + } + + public static IRef nameRef (object name, Context cx, IScriptable scope, int memberTypeFlags) + { + XMLLib xmlLib = CurrentXMLLib (cx); + return xmlLib.NameRef (cx, name, scope, memberTypeFlags); + } + + public static IRef nameRef (object ns, object name, Context cx, IScriptable scope, int memberTypeFlags) + { + XMLLib xmlLib = CurrentXMLLib (cx); + return xmlLib.NameRef (cx, ns, name, scope, memberTypeFlags); + } + + private static void storeIndexResult (Context cx, int index) + { + cx.scratchIndex = index; + } + + internal static int lastIndexResult (Context cx) + { + return cx.scratchIndex; + } + + public static void storeUint32Result (Context cx, long value) + { + if (((ulong)value >> 32) != 0) + throw new ArgumentException (); + cx.scratchUint32 = value; + } + + public static long lastUint32Result (Context cx) + { + long value = cx.scratchUint32; + if ((ulong)value >> 32 != 0) + throw new Exception (); + return value; + } + + private static void storeScriptable (Context cx, IScriptable value) + { + // The previosly stored scratchScriptable should be consumed + if (cx.scratchScriptable != null) + throw new Exception (); + cx.scratchScriptable = value; + } + + public static IScriptable lastStoredScriptable (Context cx) + { + IScriptable result = cx.scratchScriptable; + cx.scratchScriptable = null; + return result; + } + + internal static string makeUrlForGeneratedScript (bool isEval, string masterScriptUrl, int masterScriptLine) + { + if (isEval) { + return masterScriptUrl + '#' + masterScriptLine + "(eval)"; + } + else { + return masterScriptUrl + '#' + masterScriptLine + "(Function)"; + } + } + + internal static bool isGeneratedScript (string sourceUrl) + { + // ALERT: this may clash with a valid URL containing (eval) or + // (Function) + return sourceUrl.IndexOf ("(eval)") >= 0 || sourceUrl.IndexOf ("(Function)") >= 0; + } + + + public static readonly object [] EmptyArgs = new object [0]; + public static readonly string [] EmptyStrings = new string [0]; + + } } \ No newline at end of file diff --git a/Code/EcmaScript.NET/ScriptableObject.cs b/src/EcmaScript.NET/ScriptableObject.cs similarity index 97% rename from Code/EcmaScript.NET/ScriptableObject.cs rename to src/EcmaScript.NET/ScriptableObject.cs index dda5f37..edd256b 100644 --- a/Code/EcmaScript.NET/ScriptableObject.cs +++ b/src/EcmaScript.NET/ScriptableObject.cs @@ -1,1410 +1,1410 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -// API class -using System; -using System.Reflection; -using System.Runtime.InteropServices; -using System.Collections; - -using EcmaScript.NET.Debugging; -using EcmaScript.NET.Attributes; -using EcmaScript.NET.Collections; - -namespace EcmaScript.NET -{ - - /// This is the default implementation of the Scriptable interface. This - /// class provides convenient default behavior that makes it easier to - /// define host objects. - ///

- /// Various properties and methods of JavaScript objects can be conveniently - /// defined using methods of ScriptableObject. - ///

- /// Classes extending ScriptableObject must define the getClassName method. - /// - ///

- public abstract class ScriptableObject : IScriptable, DebuggableObject - { - /// Return the name of the class. - /// - /// This is typically the same name as the constructor. - /// Classes extending ScriptableObject must implement this abstract - /// method. - /// - public abstract string ClassName { get;} - - - /// Returns the parent (enclosing) scope of the object. - /// Sets the parent (enclosing) scope of the object. - public IScriptable ParentScope - { - get - { - return parentScopeObject; - } - - set - { - parentScopeObject = value; - } - - } - /// Returns an array of ids for the properties of the object. - /// - ///

All properties, even those with attribute DONTENUM, are listed.

- /// - ///

- /// an array of java.lang.Objects with an entry for every - /// listed property. Properties accessed via an integer index will - /// have a corresponding - /// Integer entry in the returned array. Properties accessed by - /// a String will have a String entry in the returned array. - /// - virtual public object [] AllIds - { - get - { - return GetIds (true); - } - - } - /// Return true if this object is sealed. - /// - /// It is an error to attempt to add or remove properties to - /// a sealed object. - /// - /// - /// true if sealed, false otherwise. - /// - public bool Sealed - { - get - { - return count < 0; - } - - } - - /// The empty property attribute. - /// - /// Used by getAttributes() and setAttributes(). - /// - /// - public const int EMPTY = 0x00; - - /// Property attribute indicating assignment to this property is ignored. - /// - /// - public const int READONLY = 0x01; - - /// Property attribute indicating property is not enumerated. - /// - /// Only enumerated properties will be returned by getIds(). - /// - /// - public const int DONTENUM = 0x02; - - /// Property attribute indicating property cannot be deleted. - /// - /// - public const int PERMANENT = 0x04; - - internal static void CheckValidAttributes (int attributes) - { - const int mask = READONLY | DONTENUM | PERMANENT; - if ((attributes & ~mask) != 0) { - throw new ArgumentException (Convert.ToString (attributes)); - } - } - - public ScriptableObject () - { - } - - public ScriptableObject (IScriptable scope, IScriptable prototype) - { - if (scope == null) - throw new ArgumentException (); - - parentScopeObject = scope; - prototypeObject = prototype; - } - - /// Returns true if the named property is defined. - /// - /// - /// the name of the property - /// - /// the object in which the lookup began - /// - /// true if and only if the property was found in the object - /// - public virtual bool Has (string name, IScriptable start) - { - return null != GetNamedSlot (name); - } - - /// Returns true if the property index is defined. - /// - /// - /// the numeric index for the property - /// - /// the object in which the lookup began - /// - /// true if and only if the property was found in the object - /// - public virtual bool Has (int index, IScriptable start) - { - return null != GetSlot (null, index); - } - - /// Returns the value of the named property or NOT_FOUND. - /// - /// If the property was created using defineProperty, the - /// appropriate getter method is called. - /// - /// - /// the name of the property - /// - /// the object in which the lookup began - /// - /// the value of the property (may be null), or NOT_FOUND - /// - public virtual object Get (string name, IScriptable start) - { - Slot slot = GetNamedSlot (name); - if (slot == null) { - return UniqueTag.NotFound; - } - return slot.GetValue (null, start, start); - } - - /// Returns the value of the indexed property or NOT_FOUND. - /// - /// - /// the numeric index for the property - /// - /// the object in which the lookup began - /// - /// the value of the property (may be null), or NOT_FOUND - /// - public virtual object Get (int index, IScriptable start) - { - Slot slot = GetSlot (null, index); - if (slot == null) { - return UniqueTag.NotFound; - } - return slot.GetValue (null, start, start); - } - - /// Sets the value of the named property, creating it if need be. - /// - /// If the property was created using defineProperty, the - /// appropriate setter method is called.

- /// - /// If the property's attributes include READONLY, no action is - /// taken. - /// This method will actually set the property in the start - /// object. - /// - ///

- /// the name of the property - /// - /// the object whose property is being set - /// - /// value to set the property to - /// - public virtual object Put (string name, IScriptable start, object value) - { - Slot slot = lastAccess; // Get local copy - if ((object)name != (object)slot.stringKey || slot.wasDeleted != 0) { - int hash = name.GetHashCode (); - slot = GetSlot (name, hash); - if (slot == null) { - if (start != this) { - start.Put (name, start, value); - return value; - } - slot = AddSlot (name, hash, null); - } - // Note: cache is not updated in put - } - if (start == this && Sealed) { - throw Context.ReportRuntimeErrorById ("msg.modify.sealed", name); - } - if ((slot.attributes & ScriptableObject.READONLY) != 0) { - // FINDME - Context cx = Context.CurrentContext; - if (cx.Version == Context.Versions.JS1_2) { - throw Context.ReportRuntimeErrorById ("msg.read-only", name); - } else { - if (cx.HasFeature (Context.Features.Strict)) { - Context.ReportWarningById ("msg.read-only", name); - } - } - return value; - } - if (this == start) { - return slot.SetValue (null, start, start, value); - } - else { - if (slot.setter != null) { - Slot newSlot = (Slot)slot.Clone (); - ((ScriptableObject)start).AddSlotImpl (newSlot.stringKey, newSlot.intKey, newSlot); - return newSlot.SetValue (null, start, start, value); - } - else { - return start.Put (name, start, value); - } - } - return value; - } - - /// Sets the value of the indexed property, creating it if need be. - /// - /// - /// the numeric index for the property - /// - /// the object whose property is being set - /// - /// value to set the property to - /// - public virtual object Put (int index, IScriptable start, object value) - { - Slot slot = GetSlot (null, index); - if (slot == null) { - if (start != this) { - return start.Put (index, start, value); - } - slot = AddSlot (null, index, null); - } - if (start == this && Sealed) { - throw Context.ReportRuntimeErrorById ("msg.modify.sealed", Convert.ToString (index)); - } - if ((slot.attributes & ScriptableObject.READONLY) != 0) { - return slot.GetValue (null, start, start); // TODO: ??? - } - if (this == start) { - return slot.SetValue (null, start, start, value); - } - else { - return start.Put (index, start, value); - } - } - - /// Removes a named property from the object. - /// - /// If the property is not found, or it has the PERMANENT attribute, - /// no action is taken. - /// - /// - /// the name of the property - /// - public virtual void Delete (string name) - { - RemoveSlot (name, name.GetHashCode ()); - } - - /// Removes the indexed property from the object. - /// - /// If the property is not found, or it has the PERMANENT attribute, - /// no action is taken. - /// - /// - /// the numeric index for the property - /// - public virtual void Delete (int index) - { - RemoveSlot (null, index); - } - - - - - - /// Get the attributes of a named property. - /// - /// The property is specified by name - /// as defined for has.

- /// - ///

- /// the identifier for the property - /// - /// the bitset of attributes - /// - /// EvaluatorException if the named property is not found - /// - public virtual int GetAttributes (string name) - { - Slot slot = GetNamedSlot (name); - if (slot == null) { - throw Context.ReportRuntimeErrorById ("msg.prop.not.found", name); - } - return slot.attributes; - } - - /// Get the attributes of an indexed property. - /// - /// - /// the numeric index for the property - /// - /// EvaluatorException if the named property is not found - /// is not found - /// - /// the bitset of attributes - /// - public virtual int GetAttributes (int index) - { - Slot slot = GetSlot (null, index); - if (slot == null) { - throw Context.ReportRuntimeErrorById ("msg.prop.not.found", Convert.ToString (index)); - } - return slot.attributes; - } - - /// Set the attributes of a named property. - /// - /// The property is specified by name - /// as defined for has.

- /// - /// The possible attributes are READONLY, DONTENUM, - /// and PERMANENT. Combinations of attributes - /// are expressed by the bitwise OR of attributes. - /// EMPTY is the state of no attributes set. Any unused - /// bits are reserved for future use. - /// - ///

- /// the name of the property - /// - /// the bitset of attributes - /// - /// EvaluatorException if the named property is not found - /// - public virtual void SetAttributes (string name, int attributes) - { - CheckValidAttributes (attributes); - Slot slot = GetNamedSlot (name); - if (slot == null) { - throw Context.ReportRuntimeErrorById ("msg.prop.not.found", name); - } - slot.attributes = (short)attributes; - } - - /// Set the attributes of an indexed property. - /// - /// - /// the numeric index for the property - /// - /// the bitset of attributes - /// - /// EvaluatorException if the named property is not found - /// - public virtual void SetAttributes (int index, int attributes) - { - CheckValidAttributes (attributes); - Slot slot = GetSlot (null, index); - if (slot == null) { - throw Context.ReportRuntimeErrorById ("msg.prop.not.found", Convert.ToString (index)); - } - slot.attributes = (short)attributes; - } - - /// Returns the prototype of the object. - public virtual IScriptable GetPrototype () - { - return prototypeObject; - } - - /// Sets the prototype of the object. - public virtual void SetPrototype (IScriptable m) - { - prototypeObject = m; - } - - /// Returns an array of ids for the properties of the object. - /// - ///

Any properties with the attribute DONTENUM are not listed.

- /// - ///

- /// an array of java.lang.Objects with an entry for every - /// listed property. Properties accessed via an integer index will - /// have a corresponding - /// Integer entry in the returned array. Properties accessed by - /// a String will have a String entry in the returned array. - /// - public virtual object [] GetIds () - { - return GetIds (false); - } - - /// Implements the [[DefaultValue]] internal method. - /// - ///

Note that the toPrimitive conversion is a no-op for - /// every type other than Object, for which [[DefaultValue]] - /// is called. See ECMA 9.1.

- /// - /// A hint of null means "no hint". - /// - ///

- /// the type hint - /// - /// the default value for the object - /// - /// See ECMA 8.6.2.6. - /// - public virtual object GetDefaultValue (Type typeHint) - { - Context cx = null; - for (int i = 0; i < 2; i++) { - bool tryToString; - if (typeHint == typeof (string)) { - tryToString = (i == 0); - } - else { - tryToString = (i == 1); - } - - string methodName; - object [] args; - if (tryToString) { - methodName = "toString"; - args = ScriptRuntime.EmptyArgs; - } - else { - methodName = "valueOf"; - args = new object [1]; - string hint; - if (typeHint == null) { - hint = "undefined"; - } - else if (typeHint == typeof (string)) { - hint = "string"; - } - else if (typeHint == typeof (IScriptable)) { - hint = "object"; - } - else if (typeHint == typeof (IFunction)) { - hint = "function"; - } - else if (typeHint == typeof (bool) || typeHint == typeof (bool)) { - hint = "boolean"; - } - else if (CliHelper.IsNumberType (typeHint) || typeHint == typeof (byte) || typeHint == typeof (sbyte)) { - hint = "number"; - } - else { - throw Context.ReportRuntimeErrorById ("msg.invalid.type", typeHint.ToString ()); - } - args [0] = hint; - } - object v = GetProperty (this, methodName); - if (!(v is IFunction)) - continue; - IFunction fun = (IFunction)v; - if (cx == null) - cx = Context.CurrentContext; - v = fun.Call (cx, fun.ParentScope, this, args); - if (v != null) { - if (!(v is IScriptable)) { - return v; - } - if (typeHint == typeof (IScriptable) || typeHint == typeof (IFunction)) { - return v; - } - if (tryToString && v is Wrapper) { - // Let a wrapped java.lang.String pass for a primitive - // string. - object u = ((Wrapper)v).Unwrap (); - if (u is string) - return u; - } - } - } - // fall through to error - string arg = (typeHint == null) ? "undefined" : typeHint.FullName; - throw ScriptRuntime.TypeErrorById ("msg.default.value", arg); - } - - /// Implements the instanceof operator. - /// - ///

This operator has been proposed to ECMA. - /// - ///

- /// The value that appeared on the LHS of the instanceof - /// operator - /// - /// true if "this" appears in value's prototype chain - /// - /// - public virtual bool HasInstance (IScriptable instance) - { - - // According to specs -- section 11.8.6 of ECMA-262 -- instanceof operator - // on objects NOT implementing [[HasInstance]] internal method should - // throw a TypeError exception. Hence, in the following script the - // catch block must get executed, (since Math object does not implement - // [[HasInstance]] method). - throw ScriptRuntime.TypeError ("msg.bad.instanceof.rhs"); - - - // Default for JS objects (other than Function) is to do prototype - // chasing. This will be overridden in NativeFunction and non-JS - // objects. - //return ScriptRuntime.jsDelegatesTo(instance, this); - } - - /// Custom == operator. - /// Must return {@link Scriptable#NOT_FOUND} if this object does not - /// have custom equality operator for the given value, - /// Boolean.TRUE if this object is equivalent to value, - /// Boolean.FALSE if this object is not equivalent to - /// value. - ///

- /// The default implementation returns Boolean.TRUE - /// if this == value or {@link Scriptable#NOT_FOUND} otherwise. - /// It indicates that by default custom equality is available only if - /// value is this in which case true is returned. - ///

- protected internal virtual object EquivalentValues (object value) - { - return (this == value) ? (object)true : UniqueTag.NotFound; - } - - /// Define a JavaScript property. - /// - /// Creates the property with an initial value and sets its attributes. - /// - /// - /// the name of the property to define. - /// - /// the initial value of the property - /// - /// the attributes of the JavaScript property - /// - public virtual void DefineProperty (string propertyName, object value, int attributes) - { - Put (propertyName, this, value); - SetAttributes (propertyName, attributes); - } - - /// Utility method to add properties to arbitrary Scriptable object. - /// If destination is instance of ScriptableObject, calls - /// defineProperty there, otherwise calls put in destination - /// ignoring attributes - /// - public static void DefineProperty (IScriptable destination, string propertyName, object value, int attributes) - { - if (!(destination is ScriptableObject)) { - destination.Put (propertyName, destination, value); - return; - } - ScriptableObject so = (ScriptableObject)destination; - so.DefineProperty (propertyName, value, attributes); - } - - - - /// Get the Object.prototype property. - /// See ECMA 15.2.4. - /// - public static IScriptable GetObjectPrototype (IScriptable scope) - { - return getClassPrototype (scope, "Object"); - } - - /// Get the Function.prototype property. - /// See ECMA 15.3.4. - /// - public static IScriptable GetFunctionPrototype (IScriptable scope) - { - return getClassPrototype (scope, "Function"); - } - - /// Get the prototype for the named class. - /// - /// For example, getClassPrototype(s, "Date") will first - /// walk up the parent chain to find the outermost scope, then will - /// search that scope for the Date constructor, and then will - /// return Date.prototype. If any of the lookups fail, or - /// the prototype is not a JavaScript object, then null will - /// be returned. - /// - /// - /// an object in the scope chain - /// - /// the name of the constructor - /// - /// the prototype for the named class, or null if it - /// cannot be found. - /// - public static IScriptable getClassPrototype (IScriptable scope, string className) - { - scope = GetTopLevelScope (scope); - object ctor = GetProperty (scope, className); - object proto; - if (ctor is BaseFunction) { - proto = ((BaseFunction)ctor).PrototypeProperty; - } - else if (ctor is IScriptable) { - IScriptable ctorObj = (IScriptable)ctor; - proto = ctorObj.Get ("prototype", ctorObj); - } - else { - return null; - } - if (proto is IScriptable) { - return (IScriptable)proto; - } - return null; - } - - /// Get the global scope. - /// - ///

Walks the parent scope chain to find an object with a null - /// parent scope (the global object). - /// - ///

- /// a JavaScript object - /// - /// the corresponding global scope - /// - public static IScriptable GetTopLevelScope (IScriptable obj) - { - for (; ; ) { - IScriptable parent = obj.ParentScope; - if (parent == null) { - return obj; - } - obj = parent; - } - } - - /// Seal this object. - /// - /// A sealed object may not have properties added or removed. Once - /// an object is sealed it may not be unsealed. - /// - /// - public virtual void SealObject () - { - lock (this) { - if (count >= 0) { - count = -1 - count; - } - } - } - - /// Gets a named property from an object or any object in its prototype chain. - ///

- /// Searches the prototype chain for a property named name. - ///

- ///

- /// a JavaScript object - /// - /// a property name - /// - /// the value of a property with name name found in - /// obj or any object in its prototype chain, or - /// Scriptable.NOT_FOUND if not found - /// - public static object GetProperty (IScriptable obj, string name) - { - IScriptable start = obj; - object result; - do { - result = obj.Get (name, start); - if (result != UniqueTag.NotFound) - break; - obj = obj.GetPrototype (); - } - while (obj != null); - return result; - } - - /// Gets an indexed property from an object or any object in its prototype chain. - ///

- /// Searches the prototype chain for a property with integral index - /// index. Note that if you wish to look for properties with numerical - /// but non-integral indicies, you should use getProperty(Scriptable,String) with - /// the string value of the index. - ///

- ///

- /// a JavaScript object - /// - /// an integral index - /// - /// the value of a property with index index found in - /// obj or any object in its prototype chain, or - /// Scriptable.NOT_FOUND if not found - /// - public static object GetProperty (IScriptable obj, int index) - { - IScriptable start = obj; - object result; - do { - result = obj.Get (index, start); - if (result != UniqueTag.NotFound) - break; - obj = obj.GetPrototype (); - } - while (obj != null); - return result; - } - - /// Returns whether a named property is defined in an object or any object - /// in its prototype chain. - ///

- /// Searches the prototype chain for a property named name. - ///

- ///

- /// a JavaScript object - /// - /// a property name - /// - /// the true if property was found - /// - public static bool HasProperty (IScriptable obj, string name) - { - return null != GetBase (obj, name); - } - - /// Returns whether an indexed property is defined in an object or any object - /// in its prototype chain. - ///

- /// Searches the prototype chain for a property with index index. - ///

- ///

- /// a JavaScript object - /// - /// a property index - /// - /// the true if property was found - /// - public static bool HasProperty (IScriptable obj, int index) - { - return null != GetBase (obj, index); - } - - /// Puts a named property in an object or in an object in its prototype chain. - ///

- /// Seaches for the named property in the prototype chain. If it is found, - /// the value of the property is changed. If it is not found, a new - /// property is added in obj. - ///

- /// a JavaScript object - /// - /// a property name - /// - /// any JavaScript value accepted by Scriptable.put - /// - public static object PutProperty (IScriptable obj, string name, object value) - { - IScriptable toBase = GetBase (obj, name); - if (toBase == null) - toBase = obj; - return toBase.Put (name, obj, value); - } - - /// Puts an indexed property in an object or in an object in its prototype chain. - ///

- /// Seaches for the indexed property in the prototype chain. If it is found, - /// the value of the property is changed. If it is not found, a new - /// property is added in obj. - ///

- /// a JavaScript object - /// - /// a property index - /// - /// any JavaScript value accepted by Scriptable.put - /// - public static object PutProperty (IScriptable obj, int index, object value) - { - IScriptable toBase = GetBase (obj, index); - if (toBase == null) - toBase = obj; - return toBase.Put (index, obj, value); - } - - /// Removes the property from an object or its prototype chain. - ///

- /// Searches for a property with name in obj or - /// its prototype chain. If it is found, the object's delete - /// method is called. - ///

- /// a JavaScript object - /// - /// a property name - /// - /// true if the property doesn't exist or was successfully removed - /// - public static bool DeleteProperty (IScriptable obj, string name) - { - IScriptable toBase = GetBase (obj, name); - if (toBase == null) - return true; - toBase.Delete (name); - return !toBase.Has (name, obj); - } - - /// Removes the property from an object or its prototype chain. - ///

- /// Searches for a property with index in obj or - /// its prototype chain. If it is found, the object's delete - /// method is called. - ///

- /// a JavaScript object - /// - /// a property index - /// - /// true if the property doesn't exist or was successfully removed - /// - public static bool DeleteProperty (IScriptable obj, int index) - { - IScriptable toBase = GetBase (obj, index); - if (toBase == null) - return true; - toBase.Delete (index); - return !toBase.Has (index, obj); - } - - /// Returns an array of all ids from an object and its prototypes. - ///

- ///

- /// a JavaScript object - /// - /// an array of all ids from all object in the prototype chain. - /// If a given id occurs multiple times in the prototype chain, - /// it will occur only once in this list. - /// - public static object [] GetPropertyIds (IScriptable obj) - { - if (obj == null) { - return ScriptRuntime.EmptyArgs; - } - object [] result = obj.GetIds (); - ObjToIntMap map = null; - for (; ; ) { - obj = obj.GetPrototype (); - if (obj == null) { - break; - } - object [] ids = obj.GetIds (); - if (ids.Length == 0) { - continue; - } - if (map == null) { - if (result.Length == 0) { - result = ids; - continue; - } - map = new ObjToIntMap (result.Length + ids.Length); - for (int i = 0; i != result.Length; ++i) { - map.intern (result [i]); - } - result = null; // Allow to GC the result - } - for (int i = 0; i != ids.Length; ++i) { - map.intern (ids [i]); - } - } - if (map != null) { - result = map.getKeys (); - } - return result; - } - - /// Call a method of an object. - /// the JavaScript object - /// - /// the name of the function property - /// - /// the arguments for the call - /// - /// - public static object CallMethod (IScriptable obj, string methodName, object [] args) - { - return CallMethod (null, obj, methodName, args); - } - - /// Call a method of an object. - /// the Context object associated with the current thread. - /// - /// the JavaScript object - /// - /// the name of the function property - /// - /// the arguments for the call - /// - public static object CallMethod (Context cx, IScriptable obj, string methodName, object [] args) - { - object funObj = GetProperty (obj, methodName); - if (!(funObj is IFunction)) { - throw ScriptRuntime.NotFunctionError (obj, methodName); - } - IFunction fun = (IFunction)funObj; - // TODO: What should be the scope when calling funObj? - // The following favor scope stored in the object on the assumption - // that is more useful especially under dynamic scope setup. - // An alternative is to check for dynamic scope flag - // and use ScriptableObject.getTopLevelScope(fun) if the flag is not - // set. But that require access to Context and messy code - // so for now it is not checked. - IScriptable scope = ScriptableObject.GetTopLevelScope (obj); - if (cx != null) { - return fun.Call (cx, scope, obj, args); - } - else { - return Context.Call (null, fun, scope, obj, args); - } - } - - private static IScriptable GetBase (IScriptable obj, string name) - { - do { - if (obj.Has (name, obj)) - break; - obj = obj.GetPrototype (); - } - while (obj != null); - return obj; - } - - private static IScriptable GetBase (IScriptable obj, int index) - { - do { - if (obj.Has (index, obj)) - break; - obj = obj.GetPrototype (); - } - while (obj != null); - return obj; - } - - /// Get arbitrary application-specific value associated with this object. - /// key object to select particular value. - /// - public object GetAssociatedValue (object key) - { - Hashtable h = associatedValues; - if (h == null) - return null; - return h [key]; - } - - /// Get arbitrary application-specific value associated with the top scope - /// of the given scope. - /// The method first calls {@link #getTopLevelScope(Scriptable scope)} - /// and then searches the prototype chain of the top scope for the first - /// object containing the associated value with the given key. - /// - /// - /// the starting scope. - /// - /// key object to select particular value. - /// - public static object GetTopScopeValue (IScriptable scope, object key) - { - scope = ScriptableObject.GetTopLevelScope (scope); - for (; ; ) { - if (scope is ScriptableObject) { - ScriptableObject so = (ScriptableObject)scope; - object value = so.GetAssociatedValue (key); - if (value != null) { - return value; - } - } - scope = scope.GetPrototype (); - if (scope == null) { - return null; - } - } - } - - /// Associate arbitrary application-specific value with this object. - /// Value can only be associated with the given object and key only once. - /// The method ignores any subsequent attempts to change the already - /// associated value. - ///

The associated values are not serilized. - ///

- /// key object to select particular value. - /// - /// the value to associate - /// - /// the passed value if the method is called first time for the - /// given key or old value for any subsequent calls. - /// - public object AssociateValue (object key, object value) - { - if (value == null) - throw new ArgumentException (); - Hashtable h = associatedValues; - if (h == null) { - lock (this) { - h = associatedValues; - if (h == null) { - h = Hashtable.Synchronized (new Hashtable ()); - associatedValues = h; - } - } - } - return InitHash (h, key, value); - } - - private object InitHash (Hashtable h, object key, object initialValue) - { - lock (h.SyncRoot) { - object current = h [key]; - if (current == null) { - h [key] = initialValue; - } - else { - initialValue = current; - } - } - return initialValue; - } - - private Slot GetNamedSlot (string name) - { - // Query last access cache and check that it was not deleted - Slot slot = lastAccess; - if ((object)name == (object)slot.stringKey && slot.wasDeleted == 0) { - return slot; - } - int hash = name.GetHashCode (); - Slot [] slots = this.slots; // Get stable local reference - int i = GetSlotPosition (slots, name, hash); - if (i < 0) { - return null; - } - slot = slots [i]; - // Update cache - here stringKey.equals(name) holds, but it can be - // that slot.stringKey != name. To make last name cache work, need - // to change the key - slot.stringKey = name; - lastAccess = slot; - return slot; - } - - private Slot GetSlot (string id, int index) - { - Slot [] slots = this.slots; // Get local copy - int i = GetSlotPosition (slots, id, index); - return (i < 0) ? null : slots [i]; - } - - private static int GetSlotPosition (Slot [] slots, string id, int index) - { - if (slots != null) { - int start = (index & 0x7fffffff) % slots.Length; - int i = start; - do { - Slot slot = slots [i]; - if (slot == null) - break; - if (slot != REMOVED && slot.intKey == index && ((object)slot.stringKey == (object)id || (id != null && id.Equals (slot.stringKey)))) { - return i; - } - if (++i == slots.Length) - i = 0; - } - while (i != start); - } - return -1; - } - - /// Add a new slot to the hash table. - /// - /// This method must be synchronized since it is altering the hash - /// table itself. Note that we search again for the slot to set - /// since another thread could have added the given property or - /// caused the table to grow while this thread was searching. - /// - private Slot AddSlot (string id, int index, Slot newSlot) - { - lock (this) { - if (Sealed) { - string str = (id != null) ? id : Convert.ToString (index); - throw Context.ReportRuntimeErrorById ("msg.add.sealed", str); - } - - - - return AddSlotImpl (id, index, newSlot); - } - } - - // Must be inside synchronized (this) - private Slot AddSlotImpl (string id, int index, Slot newSlot) - { - if (slots == null) { - slots = new Slot [5]; - } - int start = (index & 0x7fffffff) % slots.Length; - int i = start; - for (; ; ) { - Slot slot = slots [i]; - if (slot == null || slot == REMOVED) { - if ((4 * (count + 1)) > (3 * slots.Length)) { - Grow (); - return AddSlotImpl (id, index, newSlot); - } - slot = (newSlot == null) ? new Slot () : newSlot; - slot.stringKey = id; - slot.intKey = index; - slots [i] = slot; - count++; - return slot; - } - if (slot.intKey == index && ((object)slot.stringKey == (object)id || (id != null && id.Equals (slot.stringKey)))) { - return slot; - } - if (++i == slots.Length) - i = 0; - if (i == start) { - // slots should never be full or bug in grow code - throw new ApplicationException (); - } - } - } - - /// Remove a slot from the hash table. - /// - /// This method must be synchronized since it is altering the hash - /// table itself. We might be able to optimize this more, but - /// deletes are not common. - /// - private void RemoveSlot (string name, int index) - { - lock (this) { - if (Sealed) { - string str = (name != null) ? name : Convert.ToString (index); - throw Context.ReportRuntimeErrorById ("msg.remove.sealed", str); - } - - int i = GetSlotPosition (slots, name, index); - if (i >= 0) { - Slot slot = slots [i]; - if ((slot.attributes & PERMANENT) == 0) { - // Mark the slot as removed to handle a case when - // another thread manages to put just removed slot - // into lastAccess cache. - slot.wasDeleted = (sbyte)1; - if (slot == lastAccess) { - lastAccess = REMOVED; - } - count--; - if (count != 0) { - slots [i] = REMOVED; - } - else { - // With no slots it is OK to mark with null. - slots [i] = null; - } - } - } - } - } - - // Grow the hash table to accommodate new entries. - // - // Note that by assigning the new array back at the end we - // can continue reading the array from other threads. - // Must be inside synchronized (this) - private void Grow () - { - Slot [] newSlots = new Slot [slots.Length * 2 + 1]; - for (int j = slots.Length - 1; j >= 0; j--) { - Slot slot = slots [j]; - if (slot == null || slot == REMOVED) - continue; - int k = (slot.intKey & 0x7fffffff) % newSlots.Length; - while (newSlots [k] != null) - if (++k == newSlots.Length) - k = 0; - // The end of the "synchronized" statement will cause the memory - // writes to be propagated on a multiprocessor machine. We want - // to make sure that the new table is prepared to be read. - // TODO: causes the 'this' pointer to be null in calling stack frames - // on the MS JVM - //synchronized (slot) { } - newSlots [k] = slot; - } - slots = newSlots; - } - - internal virtual object [] GetIds (bool getAll) - { - Slot [] s = slots; - object [] a = ScriptRuntime.EmptyArgs; - if (s == null) - return a; - int c = 0; - for (int i = 0; i < s.Length; i++) { - Slot slot = s [i]; - if (slot == null || slot == REMOVED) - continue; - if (getAll || (slot.attributes & DONTENUM) == 0) { - if (c == 0) - a = new object [s.Length - i]; - a [c++] = slot.stringKey != null ? (object)slot.stringKey : (int)slot.intKey; - } - } - if (c == a.Length) - return a; - object [] result = new object [c]; - Array.Copy (a, 0, result, 0, c); - return result; - } - - - /// The prototype of this object. - private IScriptable prototypeObject; - - /// The parent scope of this object. - private IScriptable parentScopeObject; - - private static readonly object HAS_STATIC_ACCESSORS = typeof (void); - private static readonly Slot REMOVED = new Slot (); - - - private Slot [] slots; - // If count >= 0, it gives number of keys or if count < 0, - // it indicates sealed object where -1 - count gives number of keys - private int count; - - // cache; may be removed for smaller memory footprint - - private Slot lastAccess = REMOVED; - - // associated values are not serialized - - private volatile Hashtable associatedValues; - - public virtual void DefineSetter (string name, ICallable setter) - { - Slot slot = GetSlot (name, name.GetHashCode ()); - if (slot == null) { - slot = new Slot (); - AddSlot (name, name.GetHashCode (), slot); - } - slot.setter = setter; - } - - public virtual void DefineSetter (int index, ICallable setter) - { - Slot slot = GetSlot (null, index); - if (slot == null) { - slot = new Slot (); - AddSlot (null, index, slot); - } - slot.setter = setter; - } - - public virtual void DefineGetter (int index, ICallable getter) - { - Slot slot = GetSlot (null, index); - if (slot == null) { - slot = new Slot (); - AddSlot (null, index, slot); - } - slot.getter = getter; - } - - public virtual void DefineGetter (string name, ICallable getter) - { - Slot slot = GetSlot (name, name.GetHashCode ()); - if (slot == null) { - slot = new Slot (); - AddSlot (name, name.GetHashCode (), slot); - } - slot.getter = getter; - } - - public virtual object LookupGetter (string name) - { - Slot slot = GetSlot (name, name.GetHashCode ()); - if (slot == null || slot.getter == null) - return Undefined.Value; - return slot.getter; - } - - public virtual object LookupSetter (string name) - { - Slot slot = GetSlot (name, name.GetHashCode ()); - if (slot == null || slot.setter == null) - return Undefined.Value; - return slot.setter; - } - - private class Slot : ICloneable - { - - internal int intKey; - internal string stringKey; - internal object value; - internal short attributes; - - internal sbyte wasDeleted; - - internal ICallable getter; - internal ICallable setter; - - public object Clone () - { - Slot clone = new Slot (); - clone.intKey = intKey; - clone.stringKey = stringKey; - clone.value = value; - clone.attributes = attributes; - clone.wasDeleted = wasDeleted; - clone.getter = getter; - clone.setter = setter; - return clone; - } - - internal object GetValue (Context cx, IScriptable scope, IScriptable thisObj) - { - if (getter == null) { - return value; - } - else { - if (cx == null) - cx = Context.CurrentContext; - return getter.Call (cx, scope, thisObj, - ScriptRuntime.EmptyArgs); - } - } - - internal object SetValue (Context cx, IScriptable scope, IScriptable thisObj, object value) - { - if (setter == null) { - if (getter == null) { - return (this.value = value); - } - else { - throw ScriptRuntime.TypeError ("setting a property that has only a getter"); - } - } - else { - if (cx == null) - cx = Context.CurrentContext; - return setter.Call (cx, scope, thisObj, new object [] { value }); - } - } - - } - - } +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +// API class +using System; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Collections; + +using EcmaScript.NET.Debugging; +using EcmaScript.NET.Attributes; +using EcmaScript.NET.Collections; + +namespace EcmaScript.NET +{ + + /// This is the default implementation of the Scriptable interface. This + /// class provides convenient default behavior that makes it easier to + /// define host objects. + ///

+ /// Various properties and methods of JavaScript objects can be conveniently + /// defined using methods of ScriptableObject. + ///

+ /// Classes extending ScriptableObject must define the getClassName method. + /// + ///

+ public abstract class ScriptableObject : IScriptable, DebuggableObject + { + /// Return the name of the class. + /// + /// This is typically the same name as the constructor. + /// Classes extending ScriptableObject must implement this abstract + /// method. + /// + public abstract string ClassName { get;} + + + /// Returns the parent (enclosing) scope of the object. + /// Sets the parent (enclosing) scope of the object. + public IScriptable ParentScope + { + get + { + return parentScopeObject; + } + + set + { + parentScopeObject = value; + } + + } + /// Returns an array of ids for the properties of the object. + /// + ///

All properties, even those with attribute DONTENUM, are listed.

+ /// + ///

+ /// an array of java.lang.Objects with an entry for every + /// listed property. Properties accessed via an integer index will + /// have a corresponding + /// Integer entry in the returned array. Properties accessed by + /// a String will have a String entry in the returned array. + /// + virtual public object [] AllIds + { + get + { + return GetIds (true); + } + + } + /// Return true if this object is sealed. + /// + /// It is an error to attempt to add or remove properties to + /// a sealed object. + /// + /// + /// true if sealed, false otherwise. + /// + public bool Sealed + { + get + { + return count < 0; + } + + } + + /// The empty property attribute. + /// + /// Used by getAttributes() and setAttributes(). + /// + /// + public const int EMPTY = 0x00; + + /// Property attribute indicating assignment to this property is ignored. + /// + /// + public const int READONLY = 0x01; + + /// Property attribute indicating property is not enumerated. + /// + /// Only enumerated properties will be returned by getIds(). + /// + /// + public const int DONTENUM = 0x02; + + /// Property attribute indicating property cannot be deleted. + /// + /// + public const int PERMANENT = 0x04; + + internal static void CheckValidAttributes (int attributes) + { + const int mask = READONLY | DONTENUM | PERMANENT; + if ((attributes & ~mask) != 0) { + throw new ArgumentException (Convert.ToString (attributes)); + } + } + + public ScriptableObject () + { + } + + public ScriptableObject (IScriptable scope, IScriptable prototype) + { + if (scope == null) + throw new ArgumentException (); + + parentScopeObject = scope; + prototypeObject = prototype; + } + + /// Returns true if the named property is defined. + /// + /// + /// the name of the property + /// + /// the object in which the lookup began + /// + /// true if and only if the property was found in the object + /// + public virtual bool Has (string name, IScriptable start) + { + return null != GetNamedSlot (name); + } + + /// Returns true if the property index is defined. + /// + /// + /// the numeric index for the property + /// + /// the object in which the lookup began + /// + /// true if and only if the property was found in the object + /// + public virtual bool Has (int index, IScriptable start) + { + return null != GetSlot (null, index); + } + + /// Returns the value of the named property or NOT_FOUND. + /// + /// If the property was created using defineProperty, the + /// appropriate getter method is called. + /// + /// + /// the name of the property + /// + /// the object in which the lookup began + /// + /// the value of the property (may be null), or NOT_FOUND + /// + public virtual object Get (string name, IScriptable start) + { + Slot slot = GetNamedSlot (name); + if (slot == null) { + return UniqueTag.NotFound; + } + return slot.GetValue (null, start, start); + } + + /// Returns the value of the indexed property or NOT_FOUND. + /// + /// + /// the numeric index for the property + /// + /// the object in which the lookup began + /// + /// the value of the property (may be null), or NOT_FOUND + /// + public virtual object Get (int index, IScriptable start) + { + Slot slot = GetSlot (null, index); + if (slot == null) { + return UniqueTag.NotFound; + } + return slot.GetValue (null, start, start); + } + + /// Sets the value of the named property, creating it if need be. + /// + /// If the property was created using defineProperty, the + /// appropriate setter method is called.

+ /// + /// If the property's attributes include READONLY, no action is + /// taken. + /// This method will actually set the property in the start + /// object. + /// + ///

+ /// the name of the property + /// + /// the object whose property is being set + /// + /// value to set the property to + /// + public virtual object Put (string name, IScriptable start, object value) + { + Slot slot = lastAccess; // Get local copy + if ((object)name != (object)slot.stringKey || slot.wasDeleted != 0) { + int hash = name.GetHashCode (); + slot = GetSlot (name, hash); + if (slot == null) { + if (start != this) { + start.Put (name, start, value); + return value; + } + slot = AddSlot (name, hash, null); + } + // Note: cache is not updated in put + } + if (start == this && Sealed) { + throw Context.ReportRuntimeErrorById ("msg.modify.sealed", name); + } + if ((slot.attributes & ScriptableObject.READONLY) != 0) { + // FINDME + Context cx = Context.CurrentContext; + if (cx.Version == Context.Versions.JS1_2) { + throw Context.ReportRuntimeErrorById ("msg.read-only", name); + } else { + if (cx.HasFeature (Context.Features.Strict)) { + Context.ReportWarningById ("msg.read-only", name); + } + } + return value; + } + if (this == start) { + return slot.SetValue (null, start, start, value); + } + else { + if (slot.setter != null) { + Slot newSlot = (Slot)slot.Clone (); + ((ScriptableObject)start).AddSlotImpl (newSlot.stringKey, newSlot.intKey, newSlot); + return newSlot.SetValue (null, start, start, value); + } + else { + return start.Put (name, start, value); + } + } + return value; + } + + /// Sets the value of the indexed property, creating it if need be. + /// + /// + /// the numeric index for the property + /// + /// the object whose property is being set + /// + /// value to set the property to + /// + public virtual object Put (int index, IScriptable start, object value) + { + Slot slot = GetSlot (null, index); + if (slot == null) { + if (start != this) { + return start.Put (index, start, value); + } + slot = AddSlot (null, index, null); + } + if (start == this && Sealed) { + throw Context.ReportRuntimeErrorById ("msg.modify.sealed", Convert.ToString (index)); + } + if ((slot.attributes & ScriptableObject.READONLY) != 0) { + return slot.GetValue (null, start, start); // TODO: ??? + } + if (this == start) { + return slot.SetValue (null, start, start, value); + } + else { + return start.Put (index, start, value); + } + } + + /// Removes a named property from the object. + /// + /// If the property is not found, or it has the PERMANENT attribute, + /// no action is taken. + /// + /// + /// the name of the property + /// + public virtual void Delete (string name) + { + RemoveSlot (name, name.GetHashCode ()); + } + + /// Removes the indexed property from the object. + /// + /// If the property is not found, or it has the PERMANENT attribute, + /// no action is taken. + /// + /// + /// the numeric index for the property + /// + public virtual void Delete (int index) + { + RemoveSlot (null, index); + } + + + + + + /// Get the attributes of a named property. + /// + /// The property is specified by name + /// as defined for has.

+ /// + ///

+ /// the identifier for the property + /// + /// the bitset of attributes + /// + /// EvaluatorException if the named property is not found + /// + public virtual int GetAttributes (string name) + { + Slot slot = GetNamedSlot (name); + if (slot == null) { + throw Context.ReportRuntimeErrorById ("msg.prop.not.found", name); + } + return slot.attributes; + } + + /// Get the attributes of an indexed property. + /// + /// + /// the numeric index for the property + /// + /// EvaluatorException if the named property is not found + /// is not found + /// + /// the bitset of attributes + /// + public virtual int GetAttributes (int index) + { + Slot slot = GetSlot (null, index); + if (slot == null) { + throw Context.ReportRuntimeErrorById ("msg.prop.not.found", Convert.ToString (index)); + } + return slot.attributes; + } + + /// Set the attributes of a named property. + /// + /// The property is specified by name + /// as defined for has.

+ /// + /// The possible attributes are READONLY, DONTENUM, + /// and PERMANENT. Combinations of attributes + /// are expressed by the bitwise OR of attributes. + /// EMPTY is the state of no attributes set. Any unused + /// bits are reserved for future use. + /// + ///

+ /// the name of the property + /// + /// the bitset of attributes + /// + /// EvaluatorException if the named property is not found + /// + public virtual void SetAttributes (string name, int attributes) + { + CheckValidAttributes (attributes); + Slot slot = GetNamedSlot (name); + if (slot == null) { + throw Context.ReportRuntimeErrorById ("msg.prop.not.found", name); + } + slot.attributes = (short)attributes; + } + + /// Set the attributes of an indexed property. + /// + /// + /// the numeric index for the property + /// + /// the bitset of attributes + /// + /// EvaluatorException if the named property is not found + /// + public virtual void SetAttributes (int index, int attributes) + { + CheckValidAttributes (attributes); + Slot slot = GetSlot (null, index); + if (slot == null) { + throw Context.ReportRuntimeErrorById ("msg.prop.not.found", Convert.ToString (index)); + } + slot.attributes = (short)attributes; + } + + /// Returns the prototype of the object. + public virtual IScriptable GetPrototype () + { + return prototypeObject; + } + + /// Sets the prototype of the object. + public virtual void SetPrototype (IScriptable m) + { + prototypeObject = m; + } + + /// Returns an array of ids for the properties of the object. + /// + ///

Any properties with the attribute DONTENUM are not listed.

+ /// + ///

+ /// an array of java.lang.Objects with an entry for every + /// listed property. Properties accessed via an integer index will + /// have a corresponding + /// Integer entry in the returned array. Properties accessed by + /// a String will have a String entry in the returned array. + /// + public virtual object [] GetIds () + { + return GetIds (false); + } + + /// Implements the [[DefaultValue]] internal method. + /// + ///

Note that the toPrimitive conversion is a no-op for + /// every type other than Object, for which [[DefaultValue]] + /// is called. See ECMA 9.1.

+ /// + /// A hint of null means "no hint". + /// + ///

+ /// the type hint + /// + /// the default value for the object + /// + /// See ECMA 8.6.2.6. + /// + public virtual object GetDefaultValue (Type typeHint) + { + Context cx = null; + for (int i = 0; i < 2; i++) { + bool tryToString; + if (typeHint == typeof (string)) { + tryToString = (i == 0); + } + else { + tryToString = (i == 1); + } + + string methodName; + object [] args; + if (tryToString) { + methodName = "toString"; + args = ScriptRuntime.EmptyArgs; + } + else { + methodName = "valueOf"; + args = new object [1]; + string hint; + if (typeHint == null) { + hint = "undefined"; + } + else if (typeHint == typeof (string)) { + hint = "string"; + } + else if (typeHint == typeof (IScriptable)) { + hint = "object"; + } + else if (typeHint == typeof (IFunction)) { + hint = "function"; + } + else if (typeHint == typeof (bool) || typeHint == typeof (bool)) { + hint = "boolean"; + } + else if (CliHelper.IsNumberType (typeHint) || typeHint == typeof (byte) || typeHint == typeof (sbyte)) { + hint = "number"; + } + else { + throw Context.ReportRuntimeErrorById ("msg.invalid.type", typeHint.ToString ()); + } + args [0] = hint; + } + object v = GetProperty (this, methodName); + if (!(v is IFunction)) + continue; + IFunction fun = (IFunction)v; + if (cx == null) + cx = Context.CurrentContext; + v = fun.Call (cx, fun.ParentScope, this, args); + if (v != null) { + if (!(v is IScriptable)) { + return v; + } + if (typeHint == typeof (IScriptable) || typeHint == typeof (IFunction)) { + return v; + } + if (tryToString && v is Wrapper) { + // Let a wrapped java.lang.String pass for a primitive + // string. + object u = ((Wrapper)v).Unwrap (); + if (u is string) + return u; + } + } + } + // fall through to error + string arg = (typeHint == null) ? "undefined" : typeHint.FullName; + throw ScriptRuntime.TypeErrorById ("msg.default.value", arg); + } + + /// Implements the instanceof operator. + /// + ///

This operator has been proposed to ECMA. + /// + ///

+ /// The value that appeared on the LHS of the instanceof + /// operator + /// + /// true if "this" appears in value's prototype chain + /// + /// + public virtual bool HasInstance (IScriptable instance) + { + + // According to specs -- section 11.8.6 of ECMA-262 -- instanceof operator + // on objects NOT implementing [[HasInstance]] internal method should + // throw a TypeError exception. Hence, in the following script the + // catch block must get executed, (since Math object does not implement + // [[HasInstance]] method). + throw ScriptRuntime.TypeError ("msg.bad.instanceof.rhs"); + + + // Default for JS objects (other than Function) is to do prototype + // chasing. This will be overridden in NativeFunction and non-JS + // objects. + //return ScriptRuntime.jsDelegatesTo(instance, this); + } + + /// Custom == operator. + /// Must return {@link Scriptable#NOT_FOUND} if this object does not + /// have custom equality operator for the given value, + /// Boolean.TRUE if this object is equivalent to value, + /// Boolean.FALSE if this object is not equivalent to + /// value. + ///

+ /// The default implementation returns Boolean.TRUE + /// if this == value or {@link Scriptable#NOT_FOUND} otherwise. + /// It indicates that by default custom equality is available only if + /// value is this in which case true is returned. + ///

+ protected internal virtual object EquivalentValues (object value) + { + return (this == value) ? (object)true : UniqueTag.NotFound; + } + + /// Define a JavaScript property. + /// + /// Creates the property with an initial value and sets its attributes. + /// + /// + /// the name of the property to define. + /// + /// the initial value of the property + /// + /// the attributes of the JavaScript property + /// + public virtual void DefineProperty (string propertyName, object value, int attributes) + { + Put (propertyName, this, value); + SetAttributes (propertyName, attributes); + } + + /// Utility method to add properties to arbitrary Scriptable object. + /// If destination is instance of ScriptableObject, calls + /// defineProperty there, otherwise calls put in destination + /// ignoring attributes + /// + public static void DefineProperty (IScriptable destination, string propertyName, object value, int attributes) + { + if (!(destination is ScriptableObject)) { + destination.Put (propertyName, destination, value); + return; + } + ScriptableObject so = (ScriptableObject)destination; + so.DefineProperty (propertyName, value, attributes); + } + + + + /// Get the Object.prototype property. + /// See ECMA 15.2.4. + /// + public static IScriptable GetObjectPrototype (IScriptable scope) + { + return getClassPrototype (scope, "Object"); + } + + /// Get the Function.prototype property. + /// See ECMA 15.3.4. + /// + public static IScriptable GetFunctionPrototype (IScriptable scope) + { + return getClassPrototype (scope, "Function"); + } + + /// Get the prototype for the named class. + /// + /// For example, getClassPrototype(s, "Date") will first + /// walk up the parent chain to find the outermost scope, then will + /// search that scope for the Date constructor, and then will + /// return Date.prototype. If any of the lookups fail, or + /// the prototype is not a JavaScript object, then null will + /// be returned. + /// + /// + /// an object in the scope chain + /// + /// the name of the constructor + /// + /// the prototype for the named class, or null if it + /// cannot be found. + /// + public static IScriptable getClassPrototype (IScriptable scope, string className) + { + scope = GetTopLevelScope (scope); + object ctor = GetProperty (scope, className); + object proto; + if (ctor is BaseFunction) { + proto = ((BaseFunction)ctor).PrototypeProperty; + } + else if (ctor is IScriptable) { + IScriptable ctorObj = (IScriptable)ctor; + proto = ctorObj.Get ("prototype", ctorObj); + } + else { + return null; + } + if (proto is IScriptable) { + return (IScriptable)proto; + } + return null; + } + + /// Get the global scope. + /// + ///

Walks the parent scope chain to find an object with a null + /// parent scope (the global object). + /// + ///

+ /// a JavaScript object + /// + /// the corresponding global scope + /// + public static IScriptable GetTopLevelScope (IScriptable obj) + { + for (; ; ) { + IScriptable parent = obj.ParentScope; + if (parent == null) { + return obj; + } + obj = parent; + } + } + + /// Seal this object. + /// + /// A sealed object may not have properties added or removed. Once + /// an object is sealed it may not be unsealed. + /// + /// + public virtual void SealObject () + { + lock (this) { + if (count >= 0) { + count = -1 - count; + } + } + } + + /// Gets a named property from an object or any object in its prototype chain. + ///

+ /// Searches the prototype chain for a property named name. + ///

+ ///

+ /// a JavaScript object + /// + /// a property name + /// + /// the value of a property with name name found in + /// obj or any object in its prototype chain, or + /// Scriptable.NOT_FOUND if not found + /// + public static object GetProperty (IScriptable obj, string name) + { + IScriptable start = obj; + object result; + do { + result = obj.Get (name, start); + if (result != UniqueTag.NotFound) + break; + obj = obj.GetPrototype (); + } + while (obj != null); + return result; + } + + /// Gets an indexed property from an object or any object in its prototype chain. + ///

+ /// Searches the prototype chain for a property with integral index + /// index. Note that if you wish to look for properties with numerical + /// but non-integral indicies, you should use getProperty(Scriptable,String) with + /// the string value of the index. + ///

+ ///

+ /// a JavaScript object + /// + /// an integral index + /// + /// the value of a property with index index found in + /// obj or any object in its prototype chain, or + /// Scriptable.NOT_FOUND if not found + /// + public static object GetProperty (IScriptable obj, int index) + { + IScriptable start = obj; + object result; + do { + result = obj.Get (index, start); + if (result != UniqueTag.NotFound) + break; + obj = obj.GetPrototype (); + } + while (obj != null); + return result; + } + + /// Returns whether a named property is defined in an object or any object + /// in its prototype chain. + ///

+ /// Searches the prototype chain for a property named name. + ///

+ ///

+ /// a JavaScript object + /// + /// a property name + /// + /// the true if property was found + /// + public static bool HasProperty (IScriptable obj, string name) + { + return null != GetBase (obj, name); + } + + /// Returns whether an indexed property is defined in an object or any object + /// in its prototype chain. + ///

+ /// Searches the prototype chain for a property with index index. + ///

+ ///

+ /// a JavaScript object + /// + /// a property index + /// + /// the true if property was found + /// + public static bool HasProperty (IScriptable obj, int index) + { + return null != GetBase (obj, index); + } + + /// Puts a named property in an object or in an object in its prototype chain. + ///

+ /// Seaches for the named property in the prototype chain. If it is found, + /// the value of the property is changed. If it is not found, a new + /// property is added in obj. + ///

+ /// a JavaScript object + /// + /// a property name + /// + /// any JavaScript value accepted by Scriptable.put + /// + public static object PutProperty (IScriptable obj, string name, object value) + { + IScriptable toBase = GetBase (obj, name); + if (toBase == null) + toBase = obj; + return toBase.Put (name, obj, value); + } + + /// Puts an indexed property in an object or in an object in its prototype chain. + ///

+ /// Seaches for the indexed property in the prototype chain. If it is found, + /// the value of the property is changed. If it is not found, a new + /// property is added in obj. + ///

+ /// a JavaScript object + /// + /// a property index + /// + /// any JavaScript value accepted by Scriptable.put + /// + public static object PutProperty (IScriptable obj, int index, object value) + { + IScriptable toBase = GetBase (obj, index); + if (toBase == null) + toBase = obj; + return toBase.Put (index, obj, value); + } + + /// Removes the property from an object or its prototype chain. + ///

+ /// Searches for a property with name in obj or + /// its prototype chain. If it is found, the object's delete + /// method is called. + ///

+ /// a JavaScript object + /// + /// a property name + /// + /// true if the property doesn't exist or was successfully removed + /// + public static bool DeleteProperty (IScriptable obj, string name) + { + IScriptable toBase = GetBase (obj, name); + if (toBase == null) + return true; + toBase.Delete (name); + return !toBase.Has (name, obj); + } + + /// Removes the property from an object or its prototype chain. + ///

+ /// Searches for a property with index in obj or + /// its prototype chain. If it is found, the object's delete + /// method is called. + ///

+ /// a JavaScript object + /// + /// a property index + /// + /// true if the property doesn't exist or was successfully removed + /// + public static bool DeleteProperty (IScriptable obj, int index) + { + IScriptable toBase = GetBase (obj, index); + if (toBase == null) + return true; + toBase.Delete (index); + return !toBase.Has (index, obj); + } + + /// Returns an array of all ids from an object and its prototypes. + ///

+ ///

+ /// a JavaScript object + /// + /// an array of all ids from all object in the prototype chain. + /// If a given id occurs multiple times in the prototype chain, + /// it will occur only once in this list. + /// + public static object [] GetPropertyIds (IScriptable obj) + { + if (obj == null) { + return ScriptRuntime.EmptyArgs; + } + object [] result = obj.GetIds (); + ObjToIntMap map = null; + for (; ; ) { + obj = obj.GetPrototype (); + if (obj == null) { + break; + } + object [] ids = obj.GetIds (); + if (ids.Length == 0) { + continue; + } + if (map == null) { + if (result.Length == 0) { + result = ids; + continue; + } + map = new ObjToIntMap (result.Length + ids.Length); + for (int i = 0; i != result.Length; ++i) { + map.intern (result [i]); + } + result = null; // Allow to GC the result + } + for (int i = 0; i != ids.Length; ++i) { + map.intern (ids [i]); + } + } + if (map != null) { + result = map.getKeys (); + } + return result; + } + + /// Call a method of an object. + /// the JavaScript object + /// + /// the name of the function property + /// + /// the arguments for the call + /// + /// + public static object CallMethod (IScriptable obj, string methodName, object [] args) + { + return CallMethod (null, obj, methodName, args); + } + + /// Call a method of an object. + /// the Context object associated with the current thread. + /// + /// the JavaScript object + /// + /// the name of the function property + /// + /// the arguments for the call + /// + public static object CallMethod (Context cx, IScriptable obj, string methodName, object [] args) + { + object funObj = GetProperty (obj, methodName); + if (!(funObj is IFunction)) { + throw ScriptRuntime.NotFunctionError (obj, methodName); + } + IFunction fun = (IFunction)funObj; + // TODO: What should be the scope when calling funObj? + // The following favor scope stored in the object on the assumption + // that is more useful especially under dynamic scope setup. + // An alternative is to check for dynamic scope flag + // and use ScriptableObject.getTopLevelScope(fun) if the flag is not + // set. But that require access to Context and messy code + // so for now it is not checked. + IScriptable scope = ScriptableObject.GetTopLevelScope (obj); + if (cx != null) { + return fun.Call (cx, scope, obj, args); + } + else { + return Context.Call (null, fun, scope, obj, args); + } + } + + private static IScriptable GetBase (IScriptable obj, string name) + { + do { + if (obj.Has (name, obj)) + break; + obj = obj.GetPrototype (); + } + while (obj != null); + return obj; + } + + private static IScriptable GetBase (IScriptable obj, int index) + { + do { + if (obj.Has (index, obj)) + break; + obj = obj.GetPrototype (); + } + while (obj != null); + return obj; + } + + /// Get arbitrary application-specific value associated with this object. + /// key object to select particular value. + /// + public object GetAssociatedValue (object key) + { + Hashtable h = associatedValues; + if (h == null) + return null; + return h [key]; + } + + /// Get arbitrary application-specific value associated with the top scope + /// of the given scope. + /// The method first calls {@link #getTopLevelScope(Scriptable scope)} + /// and then searches the prototype chain of the top scope for the first + /// object containing the associated value with the given key. + /// + /// + /// the starting scope. + /// + /// key object to select particular value. + /// + public static object GetTopScopeValue (IScriptable scope, object key) + { + scope = ScriptableObject.GetTopLevelScope (scope); + for (; ; ) { + if (scope is ScriptableObject) { + ScriptableObject so = (ScriptableObject)scope; + object value = so.GetAssociatedValue (key); + if (value != null) { + return value; + } + } + scope = scope.GetPrototype (); + if (scope == null) { + return null; + } + } + } + + /// Associate arbitrary application-specific value with this object. + /// Value can only be associated with the given object and key only once. + /// The method ignores any subsequent attempts to change the already + /// associated value. + ///

The associated values are not serilized. + ///

+ /// key object to select particular value. + /// + /// the value to associate + /// + /// the passed value if the method is called first time for the + /// given key or old value for any subsequent calls. + /// + public object AssociateValue (object key, object value) + { + if (value == null) + throw new ArgumentException (); + Hashtable h = associatedValues; + if (h == null) { + lock (this) { + h = associatedValues; + if (h == null) { + h = Hashtable.Synchronized (new Hashtable ()); + associatedValues = h; + } + } + } + return InitHash (h, key, value); + } + + private object InitHash (Hashtable h, object key, object initialValue) + { + lock (h.SyncRoot) { + object current = h [key]; + if (current == null) { + h [key] = initialValue; + } + else { + initialValue = current; + } + } + return initialValue; + } + + private Slot GetNamedSlot (string name) + { + // Query last access cache and check that it was not deleted + Slot slot = lastAccess; + if ((object)name == (object)slot.stringKey && slot.wasDeleted == 0) { + return slot; + } + int hash = name.GetHashCode (); + Slot [] slots = this.slots; // Get stable local reference + int i = GetSlotPosition (slots, name, hash); + if (i < 0) { + return null; + } + slot = slots [i]; + // Update cache - here stringKey.equals(name) holds, but it can be + // that slot.stringKey != name. To make last name cache work, need + // to change the key + slot.stringKey = name; + lastAccess = slot; + return slot; + } + + private Slot GetSlot (string id, int index) + { + Slot [] slots = this.slots; // Get local copy + int i = GetSlotPosition (slots, id, index); + return (i < 0) ? null : slots [i]; + } + + private static int GetSlotPosition (Slot [] slots, string id, int index) + { + if (slots != null) { + int start = (index & 0x7fffffff) % slots.Length; + int i = start; + do { + Slot slot = slots [i]; + if (slot == null) + break; + if (slot != REMOVED && slot.intKey == index && ((object)slot.stringKey == (object)id || (id != null && id.Equals (slot.stringKey)))) { + return i; + } + if (++i == slots.Length) + i = 0; + } + while (i != start); + } + return -1; + } + + /// Add a new slot to the hash table. + /// + /// This method must be synchronized since it is altering the hash + /// table itself. Note that we search again for the slot to set + /// since another thread could have added the given property or + /// caused the table to grow while this thread was searching. + /// + private Slot AddSlot (string id, int index, Slot newSlot) + { + lock (this) { + if (Sealed) { + string str = (id != null) ? id : Convert.ToString (index); + throw Context.ReportRuntimeErrorById ("msg.add.sealed", str); + } + + + + return AddSlotImpl (id, index, newSlot); + } + } + + // Must be inside synchronized (this) + private Slot AddSlotImpl (string id, int index, Slot newSlot) + { + if (slots == null) { + slots = new Slot [5]; + } + int start = (index & 0x7fffffff) % slots.Length; + int i = start; + for (; ; ) { + Slot slot = slots [i]; + if (slot == null || slot == REMOVED) { + if ((4 * (count + 1)) > (3 * slots.Length)) { + Grow (); + return AddSlotImpl (id, index, newSlot); + } + slot = (newSlot == null) ? new Slot () : newSlot; + slot.stringKey = id; + slot.intKey = index; + slots [i] = slot; + count++; + return slot; + } + if (slot.intKey == index && ((object)slot.stringKey == (object)id || (id != null && id.Equals (slot.stringKey)))) { + return slot; + } + if (++i == slots.Length) + i = 0; + if (i == start) { + // slots should never be full or bug in grow code + throw new Exception (); + } + } + } + + /// Remove a slot from the hash table. + /// + /// This method must be synchronized since it is altering the hash + /// table itself. We might be able to optimize this more, but + /// deletes are not common. + /// + private void RemoveSlot (string name, int index) + { + lock (this) { + if (Sealed) { + string str = (name != null) ? name : Convert.ToString (index); + throw Context.ReportRuntimeErrorById ("msg.remove.sealed", str); + } + + int i = GetSlotPosition (slots, name, index); + if (i >= 0) { + Slot slot = slots [i]; + if ((slot.attributes & PERMANENT) == 0) { + // Mark the slot as removed to handle a case when + // another thread manages to put just removed slot + // into lastAccess cache. + slot.wasDeleted = (sbyte)1; + if (slot == lastAccess) { + lastAccess = REMOVED; + } + count--; + if (count != 0) { + slots [i] = REMOVED; + } + else { + // With no slots it is OK to mark with null. + slots [i] = null; + } + } + } + } + } + + // Grow the hash table to accommodate new entries. + // + // Note that by assigning the new array back at the end we + // can continue reading the array from other threads. + // Must be inside synchronized (this) + private void Grow () + { + Slot [] newSlots = new Slot [slots.Length * 2 + 1]; + for (int j = slots.Length - 1; j >= 0; j--) { + Slot slot = slots [j]; + if (slot == null || slot == REMOVED) + continue; + int k = (slot.intKey & 0x7fffffff) % newSlots.Length; + while (newSlots [k] != null) + if (++k == newSlots.Length) + k = 0; + // The end of the "synchronized" statement will cause the memory + // writes to be propagated on a multiprocessor machine. We want + // to make sure that the new table is prepared to be read. + // TODO: causes the 'this' pointer to be null in calling stack frames + // on the MS JVM + //synchronized (slot) { } + newSlots [k] = slot; + } + slots = newSlots; + } + + internal virtual object [] GetIds (bool getAll) + { + Slot [] s = slots; + object [] a = ScriptRuntime.EmptyArgs; + if (s == null) + return a; + int c = 0; + for (int i = 0; i < s.Length; i++) { + Slot slot = s [i]; + if (slot == null || slot == REMOVED) + continue; + if (getAll || (slot.attributes & DONTENUM) == 0) { + if (c == 0) + a = new object [s.Length - i]; + a [c++] = slot.stringKey != null ? (object)slot.stringKey : (int)slot.intKey; + } + } + if (c == a.Length) + return a; + object [] result = new object [c]; + Array.Copy (a, 0, result, 0, c); + return result; + } + + + /// The prototype of this object. + private IScriptable prototypeObject; + + /// The parent scope of this object. + private IScriptable parentScopeObject; + + private static readonly object HAS_STATIC_ACCESSORS = typeof (void); + private static readonly Slot REMOVED = new Slot (); + + + private Slot [] slots; + // If count >= 0, it gives number of keys or if count < 0, + // it indicates sealed object where -1 - count gives number of keys + private int count; + + // cache; may be removed for smaller memory footprint + + private Slot lastAccess = REMOVED; + + // associated values are not serialized + + private volatile Hashtable associatedValues; + + public virtual void DefineSetter (string name, ICallable setter) + { + Slot slot = GetSlot (name, name.GetHashCode ()); + if (slot == null) { + slot = new Slot (); + AddSlot (name, name.GetHashCode (), slot); + } + slot.setter = setter; + } + + public virtual void DefineSetter (int index, ICallable setter) + { + Slot slot = GetSlot (null, index); + if (slot == null) { + slot = new Slot (); + AddSlot (null, index, slot); + } + slot.setter = setter; + } + + public virtual void DefineGetter (int index, ICallable getter) + { + Slot slot = GetSlot (null, index); + if (slot == null) { + slot = new Slot (); + AddSlot (null, index, slot); + } + slot.getter = getter; + } + + public virtual void DefineGetter (string name, ICallable getter) + { + Slot slot = GetSlot (name, name.GetHashCode ()); + if (slot == null) { + slot = new Slot (); + AddSlot (name, name.GetHashCode (), slot); + } + slot.getter = getter; + } + + public virtual object LookupGetter (string name) + { + Slot slot = GetSlot (name, name.GetHashCode ()); + if (slot == null || slot.getter == null) + return Undefined.Value; + return slot.getter; + } + + public virtual object LookupSetter (string name) + { + Slot slot = GetSlot (name, name.GetHashCode ()); + if (slot == null || slot.setter == null) + return Undefined.Value; + return slot.setter; + } + + private class Slot : ICloneable + { + + internal int intKey; + internal string stringKey; + internal object value; + internal short attributes; + + internal sbyte wasDeleted; + + internal ICallable getter; + internal ICallable setter; + + public object Clone () + { + Slot clone = new Slot (); + clone.intKey = intKey; + clone.stringKey = stringKey; + clone.value = value; + clone.attributes = attributes; + clone.wasDeleted = wasDeleted; + clone.getter = getter; + clone.setter = setter; + return clone; + } + + internal object GetValue (Context cx, IScriptable scope, IScriptable thisObj) + { + if (getter == null) { + return value; + } + else { + if (cx == null) + cx = Context.CurrentContext; + return getter.Call (cx, scope, thisObj, + ScriptRuntime.EmptyArgs); + } + } + + internal object SetValue (Context cx, IScriptable scope, IScriptable thisObj, object value) + { + if (setter == null) { + if (getter == null) { + return (this.value = value); + } + else { + throw ScriptRuntime.TypeError ("setting a property that has only a getter"); + } + } + else { + if (cx == null) + cx = Context.CurrentContext; + return setter.Call (cx, scope, thisObj, new object [] { value }); + } + } + + } + + } } \ No newline at end of file diff --git a/Code/EcmaScript.NET/SecurityController.cs b/src/EcmaScript.NET/SecurityController.cs similarity index 97% rename from Code/EcmaScript.NET/SecurityController.cs rename to src/EcmaScript.NET/SecurityController.cs index ad30598..0653587 100644 --- a/Code/EcmaScript.NET/SecurityController.cs +++ b/src/EcmaScript.NET/SecurityController.cs @@ -1,137 +1,137 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; - -namespace EcmaScript.NET -{ - - /// This class describes the support needed to implement security. - ///

- /// Three main pieces of functionality are required to implement - /// security for JavaScript. First, it must be possible to define - /// classes with an associated security domain. (This security - /// domain may be any object incorporating notion of access - /// restrictions that has meaning to an embedding; for a client-side - /// JavaScript embedding this would typically be - /// java.security.ProtectionDomain or similar object depending on an - /// origin URL and/or a digital certificate.) - /// Next it must be possible to get a security domain object that - /// allows a particular action only if all security domains - /// associated with code on the current Java stack allows it. And - /// finally, it must be possible to execute script code with - /// associated security domain injected into Java stack. - ///

- /// These three pieces of functionality are encapsulated in the - /// SecurityController class. - /// - ///

- public abstract class SecurityController - { - - private class AnonymousClassScript : IScript - { - public AnonymousClassScript (EcmaScript.NET.ICallable callable, EcmaScript.NET.IScriptable thisObj, object [] args, SecurityController enclosingInstance) - { - InitBlock (callable, thisObj, args, enclosingInstance); - } - private void InitBlock (EcmaScript.NET.ICallable callable, EcmaScript.NET.IScriptable thisObj, object [] args, SecurityController enclosingInstance) - { - this.callable = callable; - this.thisObj = thisObj; - this.args = args; - this.enclosingInstance = enclosingInstance; - } - - private EcmaScript.NET.ICallable callable; - - private EcmaScript.NET.IScriptable thisObj; - - private object [] args; - private SecurityController enclosingInstance; - public SecurityController Enclosing_Instance - { - get - { - return enclosingInstance; - } - - } - public virtual object Exec (Context cx, IScriptable scope) - { - return callable.Call (cx, scope, thisObj, args); - } - } - private static SecurityController m_Global; - - // The method must NOT be public or protected - internal static SecurityController Global - { - get - { - return m_Global; - } - } - - /// Check if global {@link SecurityController} was already installed. - public static bool HasGlobal () - { - return m_Global != null; - } - - /// Initialize global controller that will be used for all - /// security-related operations. The global controller takes precedence - /// over already installed {@link Context}-specific controllers and cause - /// any subsequent call to - /// {@link Context#setSecurityController(SecurityController)} - /// to throw an exception. - ///

- /// The method can only be called once. - /// - ///

- public static void initGlobal (SecurityController controller) - { - if (controller == null) - throw new ArgumentException (); - if (m_Global != null) { - throw new System.Security.SecurityException ("Cannot overwrite already installed global SecurityController"); - } - m_Global = controller; - } - - - /// Get dynamic security domain that allows an action only if it is allowed - /// by the current Java stack and securityDomain. If - /// securityDomain is null, return domain representing permissions - /// allowed by the current stack. - /// - public abstract object getDynamicSecurityDomain (object securityDomain); - - /// Call {@link - /// Callable#call(Context cx, Scriptable scope, Scriptable thisObj, - /// Object[] args)} - /// of callable under restricted security domain where an action is - /// allowed only if it is allowed according to the Java stack on the - /// moment of the execWithDomain call and securityDomain. - /// Any call to {@link #getDynamicSecurityDomain(Object)} during - /// execution of callable.call(cx, scope, thisObj, args) - /// should return a domain incorporate restrictions imposed by - /// securityDomain and Java stack on the moment of callWithDomain - /// invocation. - ///

- ///

- public abstract object callWithDomain (object securityDomain, Context cx, ICallable callable, IScriptable scope, IScriptable thisObj, object [] args); - - - } +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; + +namespace EcmaScript.NET +{ + + /// This class describes the support needed to implement security. + ///

+ /// Three main pieces of functionality are required to implement + /// security for JavaScript. First, it must be possible to define + /// classes with an associated security domain. (This security + /// domain may be any object incorporating notion of access + /// restrictions that has meaning to an embedding; for a client-side + /// JavaScript embedding this would typically be + /// java.security.ProtectionDomain or similar object depending on an + /// origin URL and/or a digital certificate.) + /// Next it must be possible to get a security domain object that + /// allows a particular action only if all security domains + /// associated with code on the current Java stack allows it. And + /// finally, it must be possible to execute script code with + /// associated security domain injected into Java stack. + ///

+ /// These three pieces of functionality are encapsulated in the + /// SecurityController class. + /// + ///

+ public abstract class SecurityController + { + + private class AnonymousClassScript : IScript + { + public AnonymousClassScript (EcmaScript.NET.ICallable callable, EcmaScript.NET.IScriptable thisObj, object [] args, SecurityController enclosingInstance) + { + InitBlock (callable, thisObj, args, enclosingInstance); + } + private void InitBlock (EcmaScript.NET.ICallable callable, EcmaScript.NET.IScriptable thisObj, object [] args, SecurityController enclosingInstance) + { + this.callable = callable; + this.thisObj = thisObj; + this.args = args; + this.enclosingInstance = enclosingInstance; + } + + private EcmaScript.NET.ICallable callable; + + private EcmaScript.NET.IScriptable thisObj; + + private object [] args; + private SecurityController enclosingInstance; + public SecurityController Enclosing_Instance + { + get + { + return enclosingInstance; + } + + } + public virtual object Exec (Context cx, IScriptable scope) + { + return callable.Call (cx, scope, thisObj, args); + } + } + private static SecurityController m_Global; + + // The method must NOT be public or protected + internal static SecurityController Global + { + get + { + return m_Global; + } + } + + /// Check if global {@link SecurityController} was already installed. + public static bool HasGlobal () + { + return m_Global != null; + } + + /// Initialize global controller that will be used for all + /// security-related operations. The global controller takes precedence + /// over already installed {@link Context}-specific controllers and cause + /// any subsequent call to + /// {@link Context#setSecurityController(SecurityController)} + /// to throw an exception. + ///

+ /// The method can only be called once. + /// + ///

+ public static void initGlobal (SecurityController controller) + { + if (controller == null) + throw new ArgumentException (); + if (m_Global != null) { + throw new System.Security.SecurityException ("Cannot overwrite already installed global SecurityController"); + } + m_Global = controller; + } + + + /// Get dynamic security domain that allows an action only if it is allowed + /// by the current Java stack and securityDomain. If + /// securityDomain is null, return domain representing permissions + /// allowed by the current stack. + /// + public abstract object getDynamicSecurityDomain (object securityDomain); + + /// Call {@link + /// Callable#call(Context cx, Scriptable scope, Scriptable thisObj, + /// Object[] args)} + /// of callable under restricted security domain where an action is + /// allowed only if it is allowed according to the Java stack on the + /// moment of the execWithDomain call and securityDomain. + /// Any call to {@link #getDynamicSecurityDomain(Object)} during + /// execution of callable.call(cx, scope, thisObj, args) + /// should return a domain incorporate restrictions imposed by + /// securityDomain and Java stack on the moment of callWithDomain + /// invocation. + ///

+ ///

+ public abstract object callWithDomain (object securityDomain, Context cx, ICallable callable, IScriptable scope, IScriptable thisObj, object [] args); + + + } } \ No newline at end of file diff --git a/Code/EcmaScript.NET/SpecialRef.cs b/src/EcmaScript.NET/SpecialRef.cs similarity index 96% rename from Code/EcmaScript.NET/SpecialRef.cs rename to src/EcmaScript.NET/SpecialRef.cs index e75be75..5809841 100644 --- a/Code/EcmaScript.NET/SpecialRef.cs +++ b/src/EcmaScript.NET/SpecialRef.cs @@ -1,145 +1,145 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; - -namespace EcmaScript.NET -{ - - class SpecialRef : IRef - { - - public enum Types - { - None = 0, - Proto = 1, - Parent = 2 - } - - private IScriptable target; - private Types type; - private string name; - - private SpecialRef (IScriptable target, Types type, string name) - { - this.target = target; - this.type = type; - this.name = name; - } - - internal static IRef createSpecial (Context cx, object obj, string name) - { - IScriptable target = ScriptConvert.ToObjectOrNull (cx, obj); - if (target == null) { - throw ScriptRuntime.UndefReadError (obj, name); - } - - Types type; - if (name.Equals ("__proto__")) { - type = Types.Proto; - } - else if (name.Equals ("__parent__")) { - type = Types.Parent; - } - else { - throw new ArgumentException (name); - } - - if (!cx.HasFeature (Context.Features.ParentProtoProperties)) { - // Clear special after checking for valid name! - type = Types.None; - } - - return new SpecialRef (target, type, name); - } - - public object Get (Context cx) - { - switch (type) { - - case Types.None: - return ScriptRuntime.getObjectProp (target, name, cx); - - case Types.Proto: - return target.GetPrototype (); - - case Types.Parent: - return target.ParentScope; - - default: - throw Context.CodeBug (); - - } - } - - public object Set (Context cx, object value) - { - switch (type) { - - case Types.None: - return ScriptRuntime.setObjectProp (target, name, value, cx); - - case Types.Proto: - case Types.Parent: { - IScriptable obj = ScriptConvert.ToObjectOrNull (cx, value); - if (obj != null) { - // Check that obj does not contain on its prototype/scope - // chain to prevent cycles - IScriptable search = obj; - do { - if (search == target) { - throw Context.ReportRuntimeErrorById ("msg.cyclic.value", name); - } - if (type == Types.Proto) { - search = search.GetPrototype (); - } - else { - search = search.ParentScope; - } - } - while (search != null); - } - if (type == Types.Proto) { - target.SetPrototype (obj); - } - else { - target.ParentScope = obj; - } - return obj; - } - - default: - throw Context.CodeBug (); - - } - } - - public bool Has (Context cx) - { - if (type == Types.None) { - return ScriptRuntime.hasObjectElem (target, name, cx); - } - return true; - } - - public bool Delete (Context cx) - { - if (type == Types.None) { - return ScriptRuntime.deleteObjectElem (target, name, cx); - } - return false; - } - - } - +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; + +namespace EcmaScript.NET +{ + + class SpecialRef : IRef + { + + public enum Types + { + None = 0, + Proto = 1, + Parent = 2 + } + + private IScriptable target; + private Types type; + private string name; + + private SpecialRef (IScriptable target, Types type, string name) + { + this.target = target; + this.type = type; + this.name = name; + } + + internal static IRef createSpecial (Context cx, object obj, string name) + { + IScriptable target = ScriptConvert.ToObjectOrNull (cx, obj); + if (target == null) { + throw ScriptRuntime.UndefReadError (obj, name); + } + + Types type; + if (name.Equals ("__proto__")) { + type = Types.Proto; + } + else if (name.Equals ("__parent__")) { + type = Types.Parent; + } + else { + throw new ArgumentException (name); + } + + if (!cx.HasFeature (Context.Features.ParentProtoProperties)) { + // Clear special after checking for valid name! + type = Types.None; + } + + return new SpecialRef (target, type, name); + } + + public object Get (Context cx) + { + switch (type) { + + case Types.None: + return ScriptRuntime.getObjectProp (target, name, cx); + + case Types.Proto: + return target.GetPrototype (); + + case Types.Parent: + return target.ParentScope; + + default: + throw Context.CodeBug (); + + } + } + + public object Set (Context cx, object value) + { + switch (type) { + + case Types.None: + return ScriptRuntime.setObjectProp (target, name, value, cx); + + case Types.Proto: + case Types.Parent: { + IScriptable obj = ScriptConvert.ToObjectOrNull (cx, value); + if (obj != null) { + // Check that obj does not contain on its prototype/scope + // chain to prevent cycles + IScriptable search = obj; + do { + if (search == target) { + throw Context.ReportRuntimeErrorById ("msg.cyclic.value", name); + } + if (type == Types.Proto) { + search = search.GetPrototype (); + } + else { + search = search.ParentScope; + } + } + while (search != null); + } + if (type == Types.Proto) { + target.SetPrototype (obj); + } + else { + target.ParentScope = obj; + } + return obj; + } + + default: + throw Context.CodeBug (); + + } + } + + public bool Has (Context cx) + { + if (type == Types.None) { + return ScriptRuntime.hasObjectElem (target, name, cx); + } + return true; + } + + public bool Delete (Context cx) + { + if (type == Types.None) { + return ScriptRuntime.deleteObjectElem (target, name, cx); + } + return false; + } + + } + } \ No newline at end of file diff --git a/Code/EcmaScript.NET/Token.cs b/src/EcmaScript.NET/Token.cs similarity index 96% rename from Code/EcmaScript.NET/Token.cs rename to src/EcmaScript.NET/Token.cs index 2c6732e..0a4b5b2 100644 --- a/Code/EcmaScript.NET/Token.cs +++ b/src/EcmaScript.NET/Token.cs @@ -1,669 +1,669 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; - -namespace EcmaScript.NET -{ - - /// This class implements the JavaScript scanner. - /// - /// It is based on the C source files jsscan.c and jsscan.h - /// in the jsref package. - /// - /// - public class Token - { - - // debug flags - internal static readonly bool printTrees = false; // TODO: make me a preprocessor directive - - internal static readonly bool printICode = false; // TODO: make me a preprocessor directive - - internal static readonly bool printNames = printTrees || printICode; - - /// - /// Token types. - /// - /// These values correspond to JSTokenType values in - /// jsscan.c. - /// - public const int ERROR = -1; /* well-known as the only code < EOF */ - public const int EOF = 0; /* end of file */ - public const int EOL = 1; /* end of line */ - public const int FIRST_BYTECODE_TOKEN = 2; - public const int ENTERWITH = 2; - public const int LEAVEWITH = 3; - public const int RETURN = 4; - public const int GOTO = 5; - public const int IFEQ = 6; - public const int IFNE = 7; - public const int SETNAME = 8; - public const int BITOR = 9; - public const int BITXOR = 10; - public const int BITAND = 11; - public const int EQ = 12; - public const int NE = 13; - public const int LT = 14; - public const int LE = 15; - public const int GT = 16; - public const int GE = 17; - public const int LSH = 18; - public const int RSH = 19; - public const int URSH = 20; - public const int ADD = 21; - public const int SUB = 22; - public const int MUL = 23; - public const int DIV = 24; - public const int MOD = 25; - public const int NOT = 26; - public const int BITNOT = 27; - public const int POS = 28; - public const int NEG = 29; - public const int NEW = 30; - public const int DELPROP = 31; - public const int TYPEOF = 32; - public const int GETPROP = 33; - public const int SETPROP = 34; - public const int GETELEM = 35; - public const int SETELEM = 36; - public const int CALL = 37; - public const int NAME = 38; - public const int NUMBER = 39; - public const int STRING = 40; - public const int NULL = 41; - public const int THIS = 42; - public const int FALSE = 43; - public const int TRUE = 44; - public const int SHEQ = 45; - public const int SHNE = 46; - public const int REGEXP = 47; - public const int BINDNAME = 48; - public const int THROW = 49; - public const int RETHROW = 50; - public const int IN = 51; - public const int INSTANCEOF = 52; - public const int LOCAL_LOAD = 53; - public const int GETVAR = 54; - public const int SETVAR = 55; - public const int CATCH_SCOPE = 56; - public const int ENUM_INIT_KEYS = 57; - public const int ENUM_INIT_VALUES = 58; - public const int ENUM_NEXT = 59; - public const int ENUM_ID = 60; - public const int THISFN = 61; - public const int RETURN_RESULT = 62; - public const int ARRAYLIT = 63; - public const int OBJECTLIT = 64; - public const int GET_REF = 65; - public const int SET_REF = 66; - public const int DEL_REF = 67; - public const int REF_CALL = 68; - public const int REF_SPECIAL = 69; - public const int DEFAULTNAMESPACE = 70; - public const int ESCXMLATTR = 71; - public const int ESCXMLTEXT = 72; - public const int REF_MEMBER = 73; - public const int REF_NS_MEMBER = 74; - public const int REF_NAME = 75; - public const int REF_NS_NAME = 76; // Reference for ns::y, @ns::y@[y] etc. - public const int SETPROP_GETTER = 77; - public const int SETPROP_SETTER = 78; - - // End of interpreter bytecodes - public const int LAST_BYTECODE_TOKEN = SETPROP_SETTER; - - public const int TRY = LAST_BYTECODE_TOKEN + 1; - public const int SEMI = LAST_BYTECODE_TOKEN + 2; - public const int LB = LAST_BYTECODE_TOKEN + 3; - public const int RB = LAST_BYTECODE_TOKEN + 4; - public const int LC = LAST_BYTECODE_TOKEN + 5; - public const int RC = LAST_BYTECODE_TOKEN + 6; - public const int LP = LAST_BYTECODE_TOKEN + 7; - public const int RP = LAST_BYTECODE_TOKEN + 8; - public const int COMMA = LAST_BYTECODE_TOKEN + 9; - public const int ASSIGN = LAST_BYTECODE_TOKEN + 10; - public const int ASSIGN_BITOR = LAST_BYTECODE_TOKEN + 11; - public const int ASSIGN_BITXOR = LAST_BYTECODE_TOKEN + 12; - public const int ASSIGN_BITAND = LAST_BYTECODE_TOKEN + 13; - public const int ASSIGN_LSH = LAST_BYTECODE_TOKEN + 14; - public const int ASSIGN_RSH = LAST_BYTECODE_TOKEN + 15; - public const int ASSIGN_URSH = LAST_BYTECODE_TOKEN + 16; - public const int ASSIGN_ADD = LAST_BYTECODE_TOKEN + 17; - public const int ASSIGN_SUB = LAST_BYTECODE_TOKEN + 18; - public const int ASSIGN_MUL = LAST_BYTECODE_TOKEN + 19; - public const int ASSIGN_DIV = LAST_BYTECODE_TOKEN + 20; - public const int ASSIGN_MOD = LAST_BYTECODE_TOKEN + 21; // %= - - public const int FIRST_ASSIGN = ASSIGN; - public const int LAST_ASSIGN = ASSIGN_MOD; - - public const int HOOK = LAST_BYTECODE_TOKEN + 22; - public const int COLON = LAST_BYTECODE_TOKEN + 23; - public const int OR = LAST_BYTECODE_TOKEN + 24; - public const int AND = LAST_BYTECODE_TOKEN + 25; - public const int INC = LAST_BYTECODE_TOKEN + 26; - public const int DEC = LAST_BYTECODE_TOKEN + 27; - public const int DOT = LAST_BYTECODE_TOKEN + 28; - public const int FUNCTION = LAST_BYTECODE_TOKEN + 29; - public const int EXPORT = LAST_BYTECODE_TOKEN + 30; - public const int IMPORT = LAST_BYTECODE_TOKEN + 31; - public const int IF = LAST_BYTECODE_TOKEN + 32; - public const int ELSE = LAST_BYTECODE_TOKEN + 33; - public const int SWITCH = LAST_BYTECODE_TOKEN + 34; - public const int CASE = LAST_BYTECODE_TOKEN + 35; - public const int DEFAULT = LAST_BYTECODE_TOKEN + 36; - public const int WHILE = LAST_BYTECODE_TOKEN + 37; - public const int DO = LAST_BYTECODE_TOKEN + 38; - public const int FOR = LAST_BYTECODE_TOKEN + 39; - public const int BREAK = LAST_BYTECODE_TOKEN + 40; - public const int CONTINUE = LAST_BYTECODE_TOKEN + 41; - public const int VAR = LAST_BYTECODE_TOKEN + 42; - public const int WITH = LAST_BYTECODE_TOKEN + 43; - public const int CATCH = LAST_BYTECODE_TOKEN + 44; - public const int FINALLY = LAST_BYTECODE_TOKEN + 45; - public const int VOID = LAST_BYTECODE_TOKEN + 46; - public const int RESERVED = LAST_BYTECODE_TOKEN + 47; - public const int EMPTY = LAST_BYTECODE_TOKEN + 48; - public const int BLOCK = LAST_BYTECODE_TOKEN + 49; - public const int LABEL = LAST_BYTECODE_TOKEN + 50; - public const int TARGET = LAST_BYTECODE_TOKEN + 51; - public const int LOOP = LAST_BYTECODE_TOKEN + 52; - public const int EXPR_VOID = LAST_BYTECODE_TOKEN + 53; - public const int EXPR_RESULT = LAST_BYTECODE_TOKEN + 54; - public const int JSR = LAST_BYTECODE_TOKEN + 55; - public const int SCRIPT = LAST_BYTECODE_TOKEN + 56; - public const int TYPEOFNAME = LAST_BYTECODE_TOKEN + 57; - public const int USE_STACK = LAST_BYTECODE_TOKEN + 58; - public const int SETPROP_OP = LAST_BYTECODE_TOKEN + 59; - public const int SETELEM_OP = LAST_BYTECODE_TOKEN + 60; - public const int LOCAL_BLOCK = LAST_BYTECODE_TOKEN + 61; - public const int SET_REF_OP = LAST_BYTECODE_TOKEN + 62; - public const int DOTDOT = LAST_BYTECODE_TOKEN + 63; - public const int COLONCOLON = LAST_BYTECODE_TOKEN + 64; - public const int XML = LAST_BYTECODE_TOKEN + 65; - public const int DOTQUERY = LAST_BYTECODE_TOKEN + 66; - public const int XMLATTR = LAST_BYTECODE_TOKEN + 67; - public const int XMLEND = LAST_BYTECODE_TOKEN + 68; - public const int TO_OBJECT = LAST_BYTECODE_TOKEN + 69; - public const int TO_DOUBLE = LAST_BYTECODE_TOKEN + 70; - - public const int GET = LAST_BYTECODE_TOKEN + 71; // JS 1.5 get pseudo keyword - public const int SET = LAST_BYTECODE_TOKEN + 72; // JS 1.5 set pseudo keyword - public const int CONST = LAST_BYTECODE_TOKEN + 73; - public const int SETCONST = LAST_BYTECODE_TOKEN + 74; - public const int SETCONSTVAR = LAST_BYTECODE_TOKEN + 75; - public const int CONDCOMMENT = LAST_BYTECODE_TOKEN + 76; // JScript conditional comment - public const int KEEPCOMMENT = LAST_BYTECODE_TOKEN + 77; // /*! ... */ comment - public const int DEBUGGER = LAST_BYTECODE_TOKEN + 78; - - public const int LAST_TOKEN = LAST_BYTECODE_TOKEN + 79; - - public static string name (int token) - { - //if (!printNames) { - // return Convert.ToString (token); - //} - switch (token) { - - case ERROR: - return "ERROR"; - case EOF: - return "EOF"; - case EOL: - return "EOL"; - - case ENTERWITH: - return "ENTERWITH"; - - case LEAVEWITH: - return "LEAVEWITH"; - - case RETURN: - return "RETURN"; - - case GOTO: - return "GOTO"; - - case IFEQ: - return "IFEQ"; - - case IFNE: - return "IFNE"; - - case SETNAME: - return "SETNAME"; - - case BITOR: - return "BITOR"; - - case BITXOR: - return "BITXOR"; - - case BITAND: - return "BITAND"; - - case EQ: - return "EQ"; - - case NE: - return "NE"; - - case LT: - return "LT"; - - case LE: - return "LE"; - - case GT: - return "GT"; - - case GE: - return "GE"; - - case LSH: - return "LSH"; - - case RSH: - return "RSH"; - - case URSH: - return "URSH"; - - case ADD: - return "ADD"; - - case SUB: - return "SUB"; - - case MUL: - return "MUL"; - - case DIV: - return "DIV"; - - case MOD: - return "MOD"; - - case NOT: - return "NOT"; - - case BITNOT: - return "BITNOT"; - - case POS: - return "POS"; - - case NEG: - return "NEG"; - - case NEW: - return "NEW"; - - case DELPROP: - return "DELPROP"; - - case TYPEOF: - return "TYPEOF"; - - case GETPROP: - return "GETPROP"; - - case SETPROP: - return "SETPROP"; - - case GETELEM: - return "GETELEM"; - - case SETELEM: - return "SETELEM"; - - case CALL: - return "CALL"; - - case NAME: - return "NAME"; - - case NUMBER: - return "NUMBER"; - - case STRING: - return "STRING"; - - case NULL: - return "NULL"; - - case THIS: - return "THIS"; - - case FALSE: - return "FALSE"; - - case TRUE: - return "TRUE"; - - case SHEQ: - return "SHEQ"; - - case SHNE: - return "SHNE"; - - case REGEXP: - return "OBJECT"; - - case BINDNAME: - return "BINDNAME"; - - case THROW: - return "THROW"; - - case RETHROW: - return "RETHROW"; - - case IN: - return "IN"; - - case INSTANCEOF: - return "INSTANCEOF"; - - case LOCAL_LOAD: - return "LOCAL_LOAD"; - - case GETVAR: - return "GETVAR"; - - case SETVAR: - return "SETVAR"; - - case CATCH_SCOPE: - return "CATCH_SCOPE"; - - case ENUM_INIT_KEYS: - return "ENUM_INIT_KEYS"; - - case ENUM_INIT_VALUES: - return "ENUM_INIT_VALUES"; - - case ENUM_NEXT: - return "ENUM_NEXT"; - - case ENUM_ID: - return "ENUM_ID"; - - case THISFN: - return "THISFN"; - - case RETURN_RESULT: - return "RETURN_RESULT"; - - case ARRAYLIT: - return "ARRAYLIT"; - - case OBJECTLIT: - return "OBJECTLIT"; - - case GET_REF: - return "GET_REF"; - - case SET_REF: - return "SET_REF"; - - case DEL_REF: - return "DEL_REF"; - - case REF_CALL: - return "REF_CALL"; - - case REF_SPECIAL: - return "REF_SPECIAL"; - - case DEFAULTNAMESPACE: - return "DEFAULTNAMESPACE"; - - case ESCXMLTEXT: - return "ESCXMLTEXT"; - - case ESCXMLATTR: - return "ESCXMLATTR"; - - case REF_MEMBER: - return "REF_MEMBER"; - - case REF_NS_MEMBER: - return "REF_NS_MEMBER"; - - case REF_NAME: - return "REF_NAME"; - - case REF_NS_NAME: - return "REF_NS_NAME"; - - case TRY: - return "TRY"; - - case SEMI: - return "SEMI"; - - case LB: - return "LB"; - - case RB: - return "RB"; - - case LC: - return "LC"; - - case RC: - return "RC"; - - case LP: - return "LP"; - - case RP: - return "RP"; - - case COMMA: - return "COMMA"; - - case ASSIGN: - return "ASSIGN"; - - case ASSIGN_BITOR: - return "ASSIGN_BITOR"; - - case ASSIGN_BITXOR: - return "ASSIGN_BITXOR"; - - case ASSIGN_BITAND: - return "ASSIGN_BITAND"; - - case ASSIGN_LSH: - return "ASSIGN_LSH"; - - case ASSIGN_RSH: - return "ASSIGN_RSH"; - - case ASSIGN_URSH: - return "ASSIGN_URSH"; - - case ASSIGN_ADD: - return "ASSIGN_ADD"; - - case ASSIGN_SUB: - return "ASSIGN_SUB"; - - case ASSIGN_MUL: - return "ASSIGN_MUL"; - - case ASSIGN_DIV: - return "ASSIGN_DIV"; - - case ASSIGN_MOD: - return "ASSIGN_MOD"; - - case HOOK: - return "HOOK"; - - case COLON: - return "COLON"; - - case OR: - return "OR"; - - case AND: - return "AND"; - - case INC: - return "INC"; - - case DEC: - return "DEC"; - - case DOT: - return "DOT"; - - case FUNCTION: - return "FUNCTION"; - - case EXPORT: - return "EXPORT"; - - case IMPORT: - return "IMPORT"; - - case IF: - return "IF"; - - case ELSE: - return "ELSE"; - - case SWITCH: - return "SWITCH"; - - case CASE: - return "CASE"; - - case DEFAULT: - return "DEFAULT"; - - case WHILE: - return "WHILE"; - - case DO: - return "DO"; - - case FOR: - return "FOR"; - - case BREAK: - return "BREAK"; - - case CONTINUE: - return "CONTINUE"; - - case VAR: - return "VAR"; - - case WITH: - return "WITH"; - - case CATCH: - return "CATCH"; - - case FINALLY: - return "FINALLY"; - - case RESERVED: - return "RESERVED"; - - case EMPTY: - return "EMPTY"; - - case BLOCK: - return "BLOCK"; - - case LABEL: - return "LABEL"; - - case TARGET: - return "TARGET"; - - case LOOP: - return "LOOP"; - - case EXPR_VOID: - return "EXPR_VOID"; - - case EXPR_RESULT: - return "EXPR_RESULT"; - - case JSR: - return "JSR"; - - case SCRIPT: - return "SCRIPT"; - - case TYPEOFNAME: - return "TYPEOFNAME"; - - case USE_STACK: - return "USE_STACK"; - - case SETPROP_OP: - return "SETPROP_OP"; - - case SETELEM_OP: - return "SETELEM_OP"; - - case LOCAL_BLOCK: - return "LOCAL_BLOCK"; - - case SET_REF_OP: - return "SET_REF_OP"; - - case DOTDOT: - return "DOTDOT"; - - case COLONCOLON: - return "COLONCOLON"; - - case XML: - return "XML"; - - case DOTQUERY: - return "DOTQUERY"; - - case XMLATTR: - return "XMLATTR"; - - case XMLEND: - return "XMLEND"; - - case TO_OBJECT: return "TO_OBJECT"; - case TO_DOUBLE: return "TO_DOUBLE"; - case GET: return "GET"; - case SET: return "SET"; - case CONST: return "CONST"; - case SETCONST: return "SETCONST"; - case SETCONSTVAR: return "SETCONSTVAR"; - case CONDCOMMENT: return "CONDCOMMENT"; - case KEEPCOMMENT: return "KEEPCOMMENT"; - case DEBUGGER: return "DEBUGGER"; - default: return "UNKNOWN Token Type"; - } - - // Token without name - throw new ApplicationException ("Unknown token: " + Convert.ToString (token)); - } - } +//------------------------------------------------------------------------------ +// +// +// The use and distribution terms for this software are contained in the file +// named 'LICENSE', which can be found in the resources directory of this +// distribution. +// +// By using this software in any fashion, you are agreeing to be bound by the +// terms of this license. +// +// +//------------------------------------------------------------------------------ + +using System; + +namespace EcmaScript.NET +{ + + /// This class implements the JavaScript scanner. + /// + /// It is based on the C source files jsscan.c and jsscan.h + /// in the jsref package. + /// + /// + public class Token + { + + // debug flags + internal static readonly bool printTrees = false; // TODO: make me a preprocessor directive + + internal static readonly bool printICode = false; // TODO: make me a preprocessor directive + + internal static readonly bool printNames = printTrees || printICode; + + /// + /// Token types. + /// + /// These values correspond to JSTokenType values in + /// jsscan.c. + /// + public const int ERROR = -1; /* well-known as the only code < EOF */ + public const int EOF = 0; /* end of file */ + public const int EOL = 1; /* end of line */ + public const int FIRST_BYTECODE_TOKEN = 2; + public const int ENTERWITH = 2; + public const int LEAVEWITH = 3; + public const int RETURN = 4; + public const int GOTO = 5; + public const int IFEQ = 6; + public const int IFNE = 7; + public const int SETNAME = 8; + public const int BITOR = 9; + public const int BITXOR = 10; + public const int BITAND = 11; + public const int EQ = 12; + public const int NE = 13; + public const int LT = 14; + public const int LE = 15; + public const int GT = 16; + public const int GE = 17; + public const int LSH = 18; + public const int RSH = 19; + public const int URSH = 20; + public const int ADD = 21; + public const int SUB = 22; + public const int MUL = 23; + public const int DIV = 24; + public const int MOD = 25; + public const int NOT = 26; + public const int BITNOT = 27; + public const int POS = 28; + public const int NEG = 29; + public const int NEW = 30; + public const int DELPROP = 31; + public const int TYPEOF = 32; + public const int GETPROP = 33; + public const int SETPROP = 34; + public const int GETELEM = 35; + public const int SETELEM = 36; + public const int CALL = 37; + public const int NAME = 38; + public const int NUMBER = 39; + public const int STRING = 40; + public const int NULL = 41; + public const int THIS = 42; + public const int FALSE = 43; + public const int TRUE = 44; + public const int SHEQ = 45; + public const int SHNE = 46; + public const int REGEXP = 47; + public const int BINDNAME = 48; + public const int THROW = 49; + public const int RETHROW = 50; + public const int IN = 51; + public const int INSTANCEOF = 52; + public const int LOCAL_LOAD = 53; + public const int GETVAR = 54; + public const int SETVAR = 55; + public const int CATCH_SCOPE = 56; + public const int ENUM_INIT_KEYS = 57; + public const int ENUM_INIT_VALUES = 58; + public const int ENUM_NEXT = 59; + public const int ENUM_ID = 60; + public const int THISFN = 61; + public const int RETURN_RESULT = 62; + public const int ARRAYLIT = 63; + public const int OBJECTLIT = 64; + public const int GET_REF = 65; + public const int SET_REF = 66; + public const int DEL_REF = 67; + public const int REF_CALL = 68; + public const int REF_SPECIAL = 69; + public const int DEFAULTNAMESPACE = 70; + public const int ESCXMLATTR = 71; + public const int ESCXMLTEXT = 72; + public const int REF_MEMBER = 73; + public const int REF_NS_MEMBER = 74; + public const int REF_NAME = 75; + public const int REF_NS_NAME = 76; // Reference for ns::y, @ns::y@[y] etc. + public const int SETPROP_GETTER = 77; + public const int SETPROP_SETTER = 78; + + // End of interpreter bytecodes + public const int LAST_BYTECODE_TOKEN = SETPROP_SETTER; + + public const int TRY = LAST_BYTECODE_TOKEN + 1; + public const int SEMI = LAST_BYTECODE_TOKEN + 2; + public const int LB = LAST_BYTECODE_TOKEN + 3; + public const int RB = LAST_BYTECODE_TOKEN + 4; + public const int LC = LAST_BYTECODE_TOKEN + 5; + public const int RC = LAST_BYTECODE_TOKEN + 6; + public const int LP = LAST_BYTECODE_TOKEN + 7; + public const int RP = LAST_BYTECODE_TOKEN + 8; + public const int COMMA = LAST_BYTECODE_TOKEN + 9; + public const int ASSIGN = LAST_BYTECODE_TOKEN + 10; + public const int ASSIGN_BITOR = LAST_BYTECODE_TOKEN + 11; + public const int ASSIGN_BITXOR = LAST_BYTECODE_TOKEN + 12; + public const int ASSIGN_BITAND = LAST_BYTECODE_TOKEN + 13; + public const int ASSIGN_LSH = LAST_BYTECODE_TOKEN + 14; + public const int ASSIGN_RSH = LAST_BYTECODE_TOKEN + 15; + public const int ASSIGN_URSH = LAST_BYTECODE_TOKEN + 16; + public const int ASSIGN_ADD = LAST_BYTECODE_TOKEN + 17; + public const int ASSIGN_SUB = LAST_BYTECODE_TOKEN + 18; + public const int ASSIGN_MUL = LAST_BYTECODE_TOKEN + 19; + public const int ASSIGN_DIV = LAST_BYTECODE_TOKEN + 20; + public const int ASSIGN_MOD = LAST_BYTECODE_TOKEN + 21; // %= + + public const int FIRST_ASSIGN = ASSIGN; + public const int LAST_ASSIGN = ASSIGN_MOD; + + public const int HOOK = LAST_BYTECODE_TOKEN + 22; + public const int COLON = LAST_BYTECODE_TOKEN + 23; + public const int OR = LAST_BYTECODE_TOKEN + 24; + public const int AND = LAST_BYTECODE_TOKEN + 25; + public const int INC = LAST_BYTECODE_TOKEN + 26; + public const int DEC = LAST_BYTECODE_TOKEN + 27; + public const int DOT = LAST_BYTECODE_TOKEN + 28; + public const int FUNCTION = LAST_BYTECODE_TOKEN + 29; + public const int EXPORT = LAST_BYTECODE_TOKEN + 30; + public const int IMPORT = LAST_BYTECODE_TOKEN + 31; + public const int IF = LAST_BYTECODE_TOKEN + 32; + public const int ELSE = LAST_BYTECODE_TOKEN + 33; + public const int SWITCH = LAST_BYTECODE_TOKEN + 34; + public const int CASE = LAST_BYTECODE_TOKEN + 35; + public const int DEFAULT = LAST_BYTECODE_TOKEN + 36; + public const int WHILE = LAST_BYTECODE_TOKEN + 37; + public const int DO = LAST_BYTECODE_TOKEN + 38; + public const int FOR = LAST_BYTECODE_TOKEN + 39; + public const int BREAK = LAST_BYTECODE_TOKEN + 40; + public const int CONTINUE = LAST_BYTECODE_TOKEN + 41; + public const int VAR = LAST_BYTECODE_TOKEN + 42; + public const int WITH = LAST_BYTECODE_TOKEN + 43; + public const int CATCH = LAST_BYTECODE_TOKEN + 44; + public const int FINALLY = LAST_BYTECODE_TOKEN + 45; + public const int VOID = LAST_BYTECODE_TOKEN + 46; + public const int RESERVED = LAST_BYTECODE_TOKEN + 47; + public const int EMPTY = LAST_BYTECODE_TOKEN + 48; + public const int BLOCK = LAST_BYTECODE_TOKEN + 49; + public const int LABEL = LAST_BYTECODE_TOKEN + 50; + public const int TARGET = LAST_BYTECODE_TOKEN + 51; + public const int LOOP = LAST_BYTECODE_TOKEN + 52; + public const int EXPR_VOID = LAST_BYTECODE_TOKEN + 53; + public const int EXPR_RESULT = LAST_BYTECODE_TOKEN + 54; + public const int JSR = LAST_BYTECODE_TOKEN + 55; + public const int SCRIPT = LAST_BYTECODE_TOKEN + 56; + public const int TYPEOFNAME = LAST_BYTECODE_TOKEN + 57; + public const int USE_STACK = LAST_BYTECODE_TOKEN + 58; + public const int SETPROP_OP = LAST_BYTECODE_TOKEN + 59; + public const int SETELEM_OP = LAST_BYTECODE_TOKEN + 60; + public const int LOCAL_BLOCK = LAST_BYTECODE_TOKEN + 61; + public const int SET_REF_OP = LAST_BYTECODE_TOKEN + 62; + public const int DOTDOT = LAST_BYTECODE_TOKEN + 63; + public const int COLONCOLON = LAST_BYTECODE_TOKEN + 64; + public const int XML = LAST_BYTECODE_TOKEN + 65; + public const int DOTQUERY = LAST_BYTECODE_TOKEN + 66; + public const int XMLATTR = LAST_BYTECODE_TOKEN + 67; + public const int XMLEND = LAST_BYTECODE_TOKEN + 68; + public const int TO_OBJECT = LAST_BYTECODE_TOKEN + 69; + public const int TO_DOUBLE = LAST_BYTECODE_TOKEN + 70; + + public const int GET = LAST_BYTECODE_TOKEN + 71; // JS 1.5 get pseudo keyword + public const int SET = LAST_BYTECODE_TOKEN + 72; // JS 1.5 set pseudo keyword + public const int CONST = LAST_BYTECODE_TOKEN + 73; + public const int SETCONST = LAST_BYTECODE_TOKEN + 74; + public const int SETCONSTVAR = LAST_BYTECODE_TOKEN + 75; + public const int CONDCOMMENT = LAST_BYTECODE_TOKEN + 76; // JScript conditional comment + public const int KEEPCOMMENT = LAST_BYTECODE_TOKEN + 77; // /*! ... */ comment + public const int DEBUGGER = LAST_BYTECODE_TOKEN + 78; + + public const int LAST_TOKEN = LAST_BYTECODE_TOKEN + 79; + + public static string name (int token) + { + //if (!printNames) { + // return Convert.ToString (token); + //} + switch (token) { + + case ERROR: + return "ERROR"; + case EOF: + return "EOF"; + case EOL: + return "EOL"; + + case ENTERWITH: + return "ENTERWITH"; + + case LEAVEWITH: + return "LEAVEWITH"; + + case RETURN: + return "RETURN"; + + case GOTO: + return "GOTO"; + + case IFEQ: + return "IFEQ"; + + case IFNE: + return "IFNE"; + + case SETNAME: + return "SETNAME"; + + case BITOR: + return "BITOR"; + + case BITXOR: + return "BITXOR"; + + case BITAND: + return "BITAND"; + + case EQ: + return "EQ"; + + case NE: + return "NE"; + + case LT: + return "LT"; + + case LE: + return "LE"; + + case GT: + return "GT"; + + case GE: + return "GE"; + + case LSH: + return "LSH"; + + case RSH: + return "RSH"; + + case URSH: + return "URSH"; + + case ADD: + return "ADD"; + + case SUB: + return "SUB"; + + case MUL: + return "MUL"; + + case DIV: + return "DIV"; + + case MOD: + return "MOD"; + + case NOT: + return "NOT"; + + case BITNOT: + return "BITNOT"; + + case POS: + return "POS"; + + case NEG: + return "NEG"; + + case NEW: + return "NEW"; + + case DELPROP: + return "DELPROP"; + + case TYPEOF: + return "TYPEOF"; + + case GETPROP: + return "GETPROP"; + + case SETPROP: + return "SETPROP"; + + case GETELEM: + return "GETELEM"; + + case SETELEM: + return "SETELEM"; + + case CALL: + return "CALL"; + + case NAME: + return "NAME"; + + case NUMBER: + return "NUMBER"; + + case STRING: + return "STRING"; + + case NULL: + return "NULL"; + + case THIS: + return "THIS"; + + case FALSE: + return "FALSE"; + + case TRUE: + return "TRUE"; + + case SHEQ: + return "SHEQ"; + + case SHNE: + return "SHNE"; + + case REGEXP: + return "OBJECT"; + + case BINDNAME: + return "BINDNAME"; + + case THROW: + return "THROW"; + + case RETHROW: + return "RETHROW"; + + case IN: + return "IN"; + + case INSTANCEOF: + return "INSTANCEOF"; + + case LOCAL_LOAD: + return "LOCAL_LOAD"; + + case GETVAR: + return "GETVAR"; + + case SETVAR: + return "SETVAR"; + + case CATCH_SCOPE: + return "CATCH_SCOPE"; + + case ENUM_INIT_KEYS: + return "ENUM_INIT_KEYS"; + + case ENUM_INIT_VALUES: + return "ENUM_INIT_VALUES"; + + case ENUM_NEXT: + return "ENUM_NEXT"; + + case ENUM_ID: + return "ENUM_ID"; + + case THISFN: + return "THISFN"; + + case RETURN_RESULT: + return "RETURN_RESULT"; + + case ARRAYLIT: + return "ARRAYLIT"; + + case OBJECTLIT: + return "OBJECTLIT"; + + case GET_REF: + return "GET_REF"; + + case SET_REF: + return "SET_REF"; + + case DEL_REF: + return "DEL_REF"; + + case REF_CALL: + return "REF_CALL"; + + case REF_SPECIAL: + return "REF_SPECIAL"; + + case DEFAULTNAMESPACE: + return "DEFAULTNAMESPACE"; + + case ESCXMLTEXT: + return "ESCXMLTEXT"; + + case ESCXMLATTR: + return "ESCXMLATTR"; + + case REF_MEMBER: + return "REF_MEMBER"; + + case REF_NS_MEMBER: + return "REF_NS_MEMBER"; + + case REF_NAME: + return "REF_NAME"; + + case REF_NS_NAME: + return "REF_NS_NAME"; + + case TRY: + return "TRY"; + + case SEMI: + return "SEMI"; + + case LB: + return "LB"; + + case RB: + return "RB"; + + case LC: + return "LC"; + + case RC: + return "RC"; + + case LP: + return "LP"; + + case RP: + return "RP"; + + case COMMA: + return "COMMA"; + + case ASSIGN: + return "ASSIGN"; + + case ASSIGN_BITOR: + return "ASSIGN_BITOR"; + + case ASSIGN_BITXOR: + return "ASSIGN_BITXOR"; + + case ASSIGN_BITAND: + return "ASSIGN_BITAND"; + + case ASSIGN_LSH: + return "ASSIGN_LSH"; + + case ASSIGN_RSH: + return "ASSIGN_RSH"; + + case ASSIGN_URSH: + return "ASSIGN_URSH"; + + case ASSIGN_ADD: + return "ASSIGN_ADD"; + + case ASSIGN_SUB: + return "ASSIGN_SUB"; + + case ASSIGN_MUL: + return "ASSIGN_MUL"; + + case ASSIGN_DIV: + return "ASSIGN_DIV"; + + case ASSIGN_MOD: + return "ASSIGN_MOD"; + + case HOOK: + return "HOOK"; + + case COLON: + return "COLON"; + + case OR: + return "OR"; + + case AND: + return "AND"; + + case INC: + return "INC"; + + case DEC: + return "DEC"; + + case DOT: + return "DOT"; + + case FUNCTION: + return "FUNCTION"; + + case EXPORT: + return "EXPORT"; + + case IMPORT: + return "IMPORT"; + + case IF: + return "IF"; + + case ELSE: + return "ELSE"; + + case SWITCH: + return "SWITCH"; + + case CASE: + return "CASE"; + + case DEFAULT: + return "DEFAULT"; + + case WHILE: + return "WHILE"; + + case DO: + return "DO"; + + case FOR: + return "FOR"; + + case BREAK: + return "BREAK"; + + case CONTINUE: + return "CONTINUE"; + + case VAR: + return "VAR"; + + case WITH: + return "WITH"; + + case CATCH: + return "CATCH"; + + case FINALLY: + return "FINALLY"; + + case RESERVED: + return "RESERVED"; + + case EMPTY: + return "EMPTY"; + + case BLOCK: + return "BLOCK"; + + case LABEL: + return "LABEL"; + + case TARGET: + return "TARGET"; + + case LOOP: + return "LOOP"; + + case EXPR_VOID: + return "EXPR_VOID"; + + case EXPR_RESULT: + return "EXPR_RESULT"; + + case JSR: + return "JSR"; + + case SCRIPT: + return "SCRIPT"; + + case TYPEOFNAME: + return "TYPEOFNAME"; + + case USE_STACK: + return "USE_STACK"; + + case SETPROP_OP: + return "SETPROP_OP"; + + case SETELEM_OP: + return "SETELEM_OP"; + + case LOCAL_BLOCK: + return "LOCAL_BLOCK"; + + case SET_REF_OP: + return "SET_REF_OP"; + + case DOTDOT: + return "DOTDOT"; + + case COLONCOLON: + return "COLONCOLON"; + + case XML: + return "XML"; + + case DOTQUERY: + return "DOTQUERY"; + + case XMLATTR: + return "XMLATTR"; + + case XMLEND: + return "XMLEND"; + + case TO_OBJECT: return "TO_OBJECT"; + case TO_DOUBLE: return "TO_DOUBLE"; + case GET: return "GET"; + case SET: return "SET"; + case CONST: return "CONST"; + case SETCONST: return "SETCONST"; + case SETCONSTVAR: return "SETCONSTVAR"; + case CONDCOMMENT: return "CONDCOMMENT"; + case KEEPCOMMENT: return "KEEPCOMMENT"; + case DEBUGGER: return "DEBUGGER"; + default: return "UNKNOWN Token Type"; + } + + // Token without name + throw new Exception ("Unknown token: " + Convert.ToString (token)); + } + } } \ No newline at end of file diff --git a/Code/EcmaScript.NET/TokenStream.cs b/src/EcmaScript.NET/TokenStream.cs similarity index 97% rename from Code/EcmaScript.NET/TokenStream.cs rename to src/EcmaScript.NET/TokenStream.cs index a61694b..1688acd 100644 --- a/Code/EcmaScript.NET/TokenStream.cs +++ b/src/EcmaScript.NET/TokenStream.cs @@ -1,1909 +1,1920 @@ -//------------------------------------------------------------------------------ -// -// -// The use and distribution terms for this software are contained in the file -// named 'LICENSE', which can be found in the resources directory of this -// distribution. -// -// By using this software in any fashion, you are agreeing to be bound by the -// terms of this license. -// -// -//------------------------------------------------------------------------------ - -using System; -using System.Text; -using System.Globalization; - -using EcmaScript.NET.Types; -using EcmaScript.NET.Collections; - -namespace EcmaScript.NET -{ - - /// This class implements the JavaScript scanner. - /// - /// It is based on the C source files jsscan.c and jsscan.h - /// in the jsref package. - /// - /// - internal class TokenStream - { - internal int Lineno - { - get - { - return lineno; - } - - } - internal string String - { - get - { - return str; - } - - } - internal double Number - { - get - { - return dNumber; - } - - } - internal int Token - { - get - { - int c; - - - for (; ; ) - { - // Eat whitespace, possibly sensitive to newlines. - for (; ; ) - { - c = Char; - if (c == EOF_CHAR) - { - return EcmaScript.NET.Token.EOF; - } - else if (c == '\n') - { - dirtyLine = false; - return EcmaScript.NET.Token.EOL; - } - else if (!isJSSpace(c)) - { - if (c != '-') - { - dirtyLine = true; - } - break; - } - } - - if (c == '@') - return EcmaScript.NET.Token.XMLATTR; - - // identifier/keyword/instanceof? - // watch out for starting with a - bool identifierStart; - bool isUnicodeEscapeStart = false; - if (c == '\\') - { - c = Char; - if (c == 'u') - { - identifierStart = true; - isUnicodeEscapeStart = true; - stringBufferTop = 0; - } - else - { - identifierStart = false; - ungetChar(c); - c = '\\'; - } - } - else - { - char ch = (char)c; - identifierStart = char.IsLetter(ch) - || ch == '$' - || ch == '_'; - if (identifierStart) - { - stringBufferTop = 0; - addToString(c); - } - } - - if (identifierStart) - { - bool containsEscape = isUnicodeEscapeStart; - for (; ; ) - { - if (isUnicodeEscapeStart) - { - // strictly speaking we should probably push-back - // all the bad characters if the u - // sequence is malformed. But since there isn't a - // correct context(is there?) for a bad Unicode - // escape sequence in an identifier, we can report - // an error here. - int escapeVal = 0; - for (int i = 0; i != 4; ++i) - { - c = Char; - escapeVal = ScriptConvert.XDigitToInt(c, escapeVal); - // Next check takes care about c < 0 and bad escape - if (escapeVal < 0) - { - break; - } - } - if (escapeVal < 0) - { - parser.AddError("msg.invalid.escape"); - return EcmaScript.NET.Token.ERROR; - } - addToString(escapeVal); - isUnicodeEscapeStart = false; - } - else - { - c = Char; - if (c == '\\') - { - c = Char; - if (c == 'u') - { - isUnicodeEscapeStart = true; - containsEscape = true; - } - else - { - parser.AddError("msg.illegal.character"); - return EcmaScript.NET.Token.ERROR; - } - } - else - { - if (c == EOF_CHAR || !IsJavaIdentifierPart((char)c)) - { - break; - } - addToString(c); - } - } - } - ungetChar(c); - - string str = StringFromBuffer; - if (!containsEscape) - { - // OPT we shouldn't have to make a string (object!) to - // check if it's a keyword. - - // Return the corresponding token if it's a keyword - int result = stringToKeyword(str); - if (result != EcmaScript.NET.Token.EOF) - { - if (result != EcmaScript.NET.Token.RESERVED) - { - return result; - } - else if (!parser.compilerEnv.isReservedKeywordAsIdentifier()) - { - return result; - } - else - { - // If implementation permits to use future reserved - // keywords in violation with the EcmaScript, - // treat it as name but issue warning - parser.AddWarning("msg.reserved.keyword", str); - } - } - } - this.str = ((string)allStrings.intern(str)); - return EcmaScript.NET.Token.NAME; - } - - // is it a number? - if (isDigit(c) || (c == '.' && isDigit(peekChar()))) - { - - stringBufferTop = 0; - int toBase = 10; - - if (c == '0') - { - c = Char; - if (c == 'x' || c == 'X') - { - toBase = 16; - c = Char; - } - else if (isDigit(c)) - { - toBase = 8; - } - else - { - addToString('0'); - } - } - - if (toBase == 16) - { - while (0 <= ScriptConvert.XDigitToInt(c, 0)) - { - addToString(c); - c = Char; - } - } - else - { - while ('0' <= c && c <= '9') - { - /* - * We permit 08 and 09 as decimal numbers, which - * makes our behavior a superset of the ECMA - * numeric grammar. We might not always be so - * permissive, so we warn about it. - */ - if (toBase == 8 && c >= '8') - { - parser.AddWarning("msg.bad.octal.literal", c == '8' ? "8" : "9"); - toBase = 10; - } - addToString(c); - c = Char; - } - } - - bool isInteger = true; - - if (toBase == 10 && (c == '.' || c == 'e' || c == 'E')) - { - isInteger = false; - if (c == '.') - { - do - { - addToString(c); - c = Char; - } - while (isDigit(c)); - } - if (c == 'e' || c == 'E') - { - addToString(c); - c = Char; - if (c == '+' || c == '-') - { - addToString(c); - c = Char; - } - if (!isDigit(c)) - { - parser.AddError("msg.missing.exponent"); - return EcmaScript.NET.Token.ERROR; - } - do - { - addToString(c); - c = Char; - } - while (isDigit(c)); - } - } - ungetChar(c); - string numString = StringFromBuffer; - - double dval; - if (toBase == 10 && !isInteger) - { - try - { - // Use Java conversion to number from string... - dval = System.Double.Parse(numString, BuiltinNumber.NumberFormatter); - } - catch (OverflowException) - { - // HACK - if (numString[0] == '-') - dval = double.NegativeInfinity; - else - dval = double.PositiveInfinity; - } - catch (Exception) - { - parser.AddError("msg.caught.nfe"); - return EcmaScript.NET.Token.ERROR; - } - } - else - { - dval = ScriptConvert.ToNumber(numString, 0, toBase); - } - - this.dNumber = dval; - return EcmaScript.NET.Token.NUMBER; - } - - // is it a string? - if (c == '"' || c == '\'') - { - // We attempt to accumulate a string the fast way, by - // building it directly out of the reader. But if there - // are any escaped characters in the string, we revert to - // building it out of a StringBuffer. - - int quoteChar = c; - stringBufferTop = 0; - - c = Char; - - while (c != quoteChar) - { - if (c == '\n' || c == EOF_CHAR) - { - ungetChar(c); - parser.AddError("msg.unterminated.string.lit"); - return EcmaScript.NET.Token.ERROR; - } - - if (c == '\\') - { - // We've hit an escaped character - - c = Char; - switch (c) - { - - case '\\': // backslash - case 'b': // backspace - case 'f': // form feed - case 'n': // line feed - case 'r': // carriage return - case 't': // horizontal tab - case 'v': // vertical tab - case 'd': // octal sequence - case 'u': // unicode sequence - case 'x': // hexadecimal sequence - // Only keep the '\' character for those - // characters that need to be escaped... - // Don't escape quoting characters... - addToString('\\'); - addToString(c); - break; - - case '\n': - // Remove line terminator after escape - break; - - - default: - if (isDigit(c)) - { - // Octal representation of a character. - // Preserve the escaping (see Y! bug #1637286) - addToString('\\'); - } - addToString(c); - break; - break; - - } - } - else - { - addToString(c); - } - - c = Char; - } - - string str = StringFromBuffer; - this.str = ((string)allStrings.intern(str)); - return EcmaScript.NET.Token.STRING; - } - - switch (c) - { - - case ';': - return EcmaScript.NET.Token.SEMI; - - case '[': - return EcmaScript.NET.Token.LB; - - case ']': - return EcmaScript.NET.Token.RB; - - case '{': - return EcmaScript.NET.Token.LC; - - case '}': - return EcmaScript.NET.Token.RC; - - case '(': - return EcmaScript.NET.Token.LP; - - case ')': - return EcmaScript.NET.Token.RP; - - case ',': - return EcmaScript.NET.Token.COMMA; - - case '?': - return EcmaScript.NET.Token.HOOK; - - case ':': - if (matchChar(':')) - { - return EcmaScript.NET.Token.COLONCOLON; - } - else - { - return EcmaScript.NET.Token.COLON; - } - - case '.': - if (matchChar('.')) - { - return EcmaScript.NET.Token.DOTDOT; - } - else if (matchChar('(')) - { - return EcmaScript.NET.Token.DOTQUERY; - } - else - { - return EcmaScript.NET.Token.DOT; - } - - case '|': - if (matchChar('|')) - { - return EcmaScript.NET.Token.OR; - } - else if (matchChar('=')) - { - return EcmaScript.NET.Token.ASSIGN_BITOR; - } - else - { - return EcmaScript.NET.Token.BITOR; - } - - - case '^': - if (matchChar('=')) - { - return EcmaScript.NET.Token.ASSIGN_BITXOR; - } - else - { - return EcmaScript.NET.Token.BITXOR; - } - - - case '&': - if (matchChar('&')) - { - return EcmaScript.NET.Token.AND; - } - else if (matchChar('=')) - { - return EcmaScript.NET.Token.ASSIGN_BITAND; - } - else - { - return EcmaScript.NET.Token.BITAND; - } - - - case '=': - if (matchChar('=')) - { - if (matchChar('=')) - return EcmaScript.NET.Token.SHEQ; - else - return EcmaScript.NET.Token.EQ; - } - else - { - return EcmaScript.NET.Token.ASSIGN; - } - - - case '!': - if (matchChar('=')) - { - if (matchChar('=')) - return EcmaScript.NET.Token.SHNE; - else - return EcmaScript.NET.Token.NE; - } - else - { - return EcmaScript.NET.Token.NOT; - } - - - case '<': - /* NB:treat HTML begin-comment as comment-till-eol */ - if (matchChar('!')) - { - if (matchChar('-')) - { - if (matchChar('-')) - { - skipLine(); - - goto retry; - } - ungetChar('-'); - } - ungetChar('!'); - } - if (matchChar('<')) - { - if (matchChar('=')) - { - return EcmaScript.NET.Token.ASSIGN_LSH; - } - else - { - return EcmaScript.NET.Token.LSH; - } - } - else - { - if (matchChar('=')) - { - return EcmaScript.NET.Token.LE; - } - else - { - return EcmaScript.NET.Token.LT; - } - } - - - case '>': - if (matchChar('>')) - { - if (matchChar('>')) - { - if (matchChar('=')) - { - return EcmaScript.NET.Token.ASSIGN_URSH; - } - else - { - return EcmaScript.NET.Token.URSH; - } - } - else - { - if (matchChar('=')) - { - return EcmaScript.NET.Token.ASSIGN_RSH; - } - else - { - return EcmaScript.NET.Token.RSH; - } - } - } - else - { - if (matchChar('=')) - { - return EcmaScript.NET.Token.GE; - } - else - { - return EcmaScript.NET.Token.GT; - } - } - - - case '*': - if (matchChar('=')) - { - return EcmaScript.NET.Token.ASSIGN_MUL; - } - else - { - return EcmaScript.NET.Token.MUL; - } - - - case '/': - // is it a // comment? - if (matchChar('/')) - { - skipLine(); - - goto retry; - } - if (matchChar('*')) - { - bool lookForSlash = false; - StringBuilder sb = new StringBuilder(); - for (; ; ) - { - c = Char; - if (c == EOF_CHAR) - { - parser.AddError("msg.unterminated.comment"); - return EcmaScript.NET.Token.ERROR; - } - sb.Append((char)c); - if (c == '*') - { - lookForSlash = true; - } - else if (c == '/') - { - if (lookForSlash) - { - sb.Remove(sb.Length - 2, 2); - string s1 = sb.ToString(); - string s2 = s1.Trim(); - if (s1.StartsWith("!")) - { - // Remove the leading '!' - this.str = s1.Substring(1); - return NET.Token.KEEPCOMMENT; - } - else if (s2.StartsWith("@cc_on") || - s2.StartsWith("@if") || - s2.StartsWith("@elif") || - s2.StartsWith("@else") || - s2.StartsWith("@end")) - { - this.str = s1; - return NET.Token.CONDCOMMENT; - } - else - { - goto retry; - } - } - } - else - { - lookForSlash = false; - } - } - } - - if (matchChar('=')) - { - return EcmaScript.NET.Token.ASSIGN_DIV; - } - else - { - return EcmaScript.NET.Token.DIV; - } - - - case '%': - if (matchChar('=')) - { - return EcmaScript.NET.Token.ASSIGN_MOD; - } - else - { - return EcmaScript.NET.Token.MOD; - } - - - case '~': - return EcmaScript.NET.Token.BITNOT; - - - case '+': - if (matchChar('=')) - { - return EcmaScript.NET.Token.ASSIGN_ADD; - } - else if (matchChar('+')) - { - return EcmaScript.NET.Token.INC; - } - else - { - return EcmaScript.NET.Token.ADD; - } - - - case '-': - if (matchChar('=')) - { - c = EcmaScript.NET.Token.ASSIGN_SUB; - } - else if (matchChar('-')) - { - if (!dirtyLine) - { - // treat HTML end-comment after possible whitespace - // after line start as comment-utill-eol - if (matchChar('>')) - { - skipLine(); - - goto retry; - } - } - c = EcmaScript.NET.Token.DEC; - } - else - { - c = EcmaScript.NET.Token.SUB; - } - dirtyLine = true; - return c; - - - default: - parser.AddError("msg.illegal.character"); - return EcmaScript.NET.Token.ERROR; - - } - - retry: - ; - } - } - - } - internal bool XMLAttribute - { - get - { - return xmlIsAttribute; - } - - } - internal int FirstXMLToken - { - get - { - xmlOpenTagsCount = 0; - xmlIsAttribute = false; - xmlIsTagContent = false; - ungetChar('<'); - return NextXMLToken; - } - - } - internal int NextXMLToken - { - get - { - stringBufferTop = 0; // remember the XML - - for (int c = Char; c != EOF_CHAR; c = Char) - { - if (xmlIsTagContent) - { - switch (c) - { - - case '>': - addToString(c); - xmlIsTagContent = false; - xmlIsAttribute = false; - break; - - case '/': - addToString(c); - if (peekChar() == '>') - { - c = Char; - addToString(c); - xmlIsTagContent = false; - xmlOpenTagsCount--; - } - break; - - case '{': - ungetChar(c); - this.str = StringFromBuffer; - return EcmaScript.NET.Token.XML; - - case '\'': - case '"': - addToString(c); - if (!readQuotedString(c)) - return EcmaScript.NET.Token.ERROR; - break; - - case '=': - addToString(c); - xmlIsAttribute = true; - break; - - case ' ': - case '\t': - case '\r': - case '\n': - addToString(c); - break; - - default: - addToString(c); - xmlIsAttribute = false; - break; - - } - - if (!xmlIsTagContent && xmlOpenTagsCount == 0) - { - this.str = StringFromBuffer; - return EcmaScript.NET.Token.XMLEND; - } - } - else - { - switch (c) - { - - case '<': - addToString(c); - c = peekChar(); - switch (c) - { - - case '!': - c = Char; // Skip ! - addToString(c); - c = peekChar(); - switch (c) - { - - case '-': - c = Char; // Skip - - addToString(c); - c = Char; - if (c == '-') - { - addToString(c); - if (!readXmlComment()) - return EcmaScript.NET.Token.ERROR; - } - else - { - // throw away the string in progress - stringBufferTop = 0; - this.str = null; - parser.AddError("msg.XML.bad.form"); - return EcmaScript.NET.Token.ERROR; - } - break; - - case '[': - c = Char; // Skip [ - addToString(c); - if (Char == 'C' && Char == 'D' && Char == 'A' && Char == 'T' && Char == 'A' && Char == '[') - { - addToString('C'); - addToString('D'); - addToString('A'); - addToString('T'); - addToString('A'); - addToString('['); - if (!readCDATA()) - return EcmaScript.NET.Token.ERROR; - } - else - { - // throw away the string in progress - stringBufferTop = 0; - this.str = null; - parser.AddError("msg.XML.bad.form"); - return EcmaScript.NET.Token.ERROR; - } - break; - - default: - if (!readEntity()) - return EcmaScript.NET.Token.ERROR; - break; - - } - break; - - case '?': - c = Char; // Skip ? - addToString(c); - if (!readPI()) - return EcmaScript.NET.Token.ERROR; - break; - - case '/': - // End tag - c = Char; // Skip / - addToString(c); - if (xmlOpenTagsCount == 0) - { - // throw away the string in progress - stringBufferTop = 0; - this.str = null; - parser.AddError("msg.XML.bad.form"); - return EcmaScript.NET.Token.ERROR; - } - xmlIsTagContent = true; - xmlOpenTagsCount--; - break; - - default: - // Start tag - xmlIsTagContent = true; - xmlOpenTagsCount++; - break; - - } - break; - - case '{': - ungetChar(c); - this.str = StringFromBuffer; - return EcmaScript.NET.Token.XML; - - default: - addToString(c); - break; - - } - } - } - - - stringBufferTop = 0; // throw away the string in progress - this.str = null; - parser.AddError("msg.XML.bad.form"); - return EcmaScript.NET.Token.ERROR; - } - - } - private string StringFromBuffer - { - get - { - return new string(stringBuffer, 0, stringBufferTop); - } - - } - private int Char - { - get - { - if (ungetCursor != 0) - { - return ungetBuffer[--ungetCursor]; - } - - for (; ; ) - { - int c; - if (sourceString != null) - { - if (sourceCursor == sourceEnd) - { - hitEOF = true; - return EOF_CHAR; - } - c = sourceString[sourceCursor++]; - } - else - { - if (sourceCursor == sourceEnd) - { - if (!fillSourceBuffer()) - { - hitEOF = true; - return EOF_CHAR; - } - } - c = sourceBuffer[sourceCursor++]; - } - - if (lineEndChar >= 0) - { - if (lineEndChar == '\r' && c == '\n') - { - lineEndChar = '\n'; - continue; - } - lineEndChar = -1; - lineStart = sourceCursor - 1; - lineno++; - } - - if (c <= 127) - { - if (c == '\n' || c == '\r') - { - lineEndChar = c; - c = '\n'; - } - } - else - { - if (isJSFormatChar(c)) - { - continue; - } - if (ScriptRuntime.isJSLineTerminator(c)) - { - lineEndChar = c; - c = '\n'; - } - } - return c; - } - } - - } - internal int Offset - { - get - { - int n = sourceCursor - lineStart; - if (lineEndChar >= 0) - { - --n; - } - return n; - } - - } - internal string Line - { - get - { - if (sourceString != null) - { - // String case - int lineEnd = sourceCursor; - if (lineEndChar >= 0) - { - --lineEnd; - } - else - { - for (; lineEnd != sourceEnd; ++lineEnd) - { - int c = sourceString[lineEnd]; - if (ScriptRuntime.isJSLineTerminator(c)) - { - break; - } - } - } - return sourceString.Substring(lineStart, (lineEnd) - (lineStart)); - } - else - { - // Reader case - int lineLength = sourceCursor - lineStart; - if (lineEndChar >= 0) - { - --lineLength; - } - else - { - // Read until the end of line - for (; ; ++lineLength) - { - int i = lineStart + lineLength; - if (i == sourceEnd) - { - try - { - if (!fillSourceBuffer()) - { - break; - } - } - catch (System.IO.IOException) - { - // ignore it, we're already displaying an error... - break; - } - // i recalculuation as fillSourceBuffer can move saved - // line buffer and change lineStart - i = lineStart + lineLength; - } - int c = sourceBuffer[i]; - if (ScriptRuntime.isJSLineTerminator(c)) - { - break; - } - } - } - return new string(sourceBuffer, lineStart, lineLength); - } - } - - } - /* - * For chars - because we need something out-of-range - * to check. (And checking EOF by exception is annoying.) - * Note distinction from EOF token type! - */ - private const int EOF_CHAR = -1; - - - internal TokenStream(Parser parser, System.IO.StreamReader sourceReader, string sourceString, int lineno) - { - this.parser = parser; - this.lineno = lineno; - if (sourceReader != null) - { - if (sourceString != null) - Context.CodeBug(); - this.sourceReader = sourceReader; - this.sourceBuffer = new char[512]; - this.sourceEnd = 0; - } - else - { - if (sourceString == null) - Context.CodeBug(); - this.sourceString = sourceString; - this.sourceEnd = sourceString.Length; - } - this.sourceCursor = 0; - } - - /* This function uses the cached op, string and number fields in - * TokenStream; if getToken has been called since the passed token - * was scanned, the op or string printed may be incorrect. - */ - internal string tokenToString(int token) - { - if (EcmaScript.NET.Token.printTrees) - { - string name = EcmaScript.NET.Token.name(token); - - switch (token) - { - - case EcmaScript.NET.Token.STRING: - case EcmaScript.NET.Token.REGEXP: - case EcmaScript.NET.Token.NAME: - return name + " `" + this.str + "'"; - - - case EcmaScript.NET.Token.NUMBER: - return "NUMBER " + this.dNumber; - } - - return name; - } - return ""; - } - - internal static bool isKeyword(string s) - { - return EcmaScript.NET.Token.EOF != stringToKeyword(s); - } - - #region Ids - private const int Id_break = EcmaScript.NET.Token.BREAK; - private const int Id_case = EcmaScript.NET.Token.CASE; - private const int Id_continue = EcmaScript.NET.Token.CONTINUE; - private const int Id_default = EcmaScript.NET.Token.DEFAULT; - private const int Id_delete = EcmaScript.NET.Token.DELPROP; - private const int Id_do = EcmaScript.NET.Token.DO; - private const int Id_else = EcmaScript.NET.Token.ELSE; - private const int Id_export = EcmaScript.NET.Token.EXPORT; - private const int Id_false = EcmaScript.NET.Token.FALSE; - private const int Id_for = EcmaScript.NET.Token.FOR; - private const int Id_function = EcmaScript.NET.Token.FUNCTION; - private const int Id_if = EcmaScript.NET.Token.IF; - private const int Id_in = EcmaScript.NET.Token.IN; - private const int Id_new = EcmaScript.NET.Token.NEW; - private const int Id_null = EcmaScript.NET.Token.NULL; - private const int Id_return = EcmaScript.NET.Token.RETURN; - private const int Id_switch = EcmaScript.NET.Token.SWITCH; - private const int Id_this = EcmaScript.NET.Token.THIS; - private const int Id_true = EcmaScript.NET.Token.TRUE; - private const int Id_typeof = EcmaScript.NET.Token.TYPEOF; - private const int Id_var = EcmaScript.NET.Token.VAR; - private const int Id_void = EcmaScript.NET.Token.VOID; - private const int Id_while = EcmaScript.NET.Token.WHILE; - private const int Id_with = EcmaScript.NET.Token.WITH; - private const int Id_abstract = EcmaScript.NET.Token.RESERVED; - private const int Id_boolean = EcmaScript.NET.Token.RESERVED; - private const int Id_byte = EcmaScript.NET.Token.RESERVED; - private const int Id_catch = EcmaScript.NET.Token.CATCH; - private const int Id_char = EcmaScript.NET.Token.RESERVED; - private const int Id_class = EcmaScript.NET.Token.RESERVED; - private const int Id_const = EcmaScript.NET.Token.RESERVED; - private const int Id_debugger = EcmaScript.NET.Token.DEBUGGER; - private const int Id_double = EcmaScript.NET.Token.RESERVED; - private const int Id_enum = EcmaScript.NET.Token.RESERVED; - private const int Id_extends = EcmaScript.NET.Token.RESERVED; - private const int Id_final = EcmaScript.NET.Token.RESERVED; - private const int Id_finally = EcmaScript.NET.Token.FINALLY; - private const int Id_float = EcmaScript.NET.Token.RESERVED; - private const int Id_goto = EcmaScript.NET.Token.RESERVED; - private const int Id_implements = EcmaScript.NET.Token.RESERVED; - private const int Id_import = EcmaScript.NET.Token.IMPORT; - private const int Id_instanceof = EcmaScript.NET.Token.INSTANCEOF; - private const int Id_int = EcmaScript.NET.Token.RESERVED; - private const int Id_interface = EcmaScript.NET.Token.RESERVED; - private const int Id_long = EcmaScript.NET.Token.RESERVED; - private const int Id_native = EcmaScript.NET.Token.RESERVED; - private const int Id_package = EcmaScript.NET.Token.RESERVED; - private const int Id_private = EcmaScript.NET.Token.RESERVED; - private const int Id_protected = EcmaScript.NET.Token.RESERVED; - private const int Id_public = EcmaScript.NET.Token.RESERVED; - private const int Id_short = EcmaScript.NET.Token.RESERVED; - private const int Id_static = EcmaScript.NET.Token.RESERVED; - private const int Id_super = EcmaScript.NET.Token.RESERVED; - private const int Id_synchronized = EcmaScript.NET.Token.RESERVED; - private const int Id_throw = EcmaScript.NET.Token.THROW; - private const int Id_throws = EcmaScript.NET.Token.RESERVED; - private const int Id_transient = EcmaScript.NET.Token.RESERVED; - private const int Id_try = EcmaScript.NET.Token.TRY; - private const int Id_volatile = EcmaScript.NET.Token.RESERVED; - #endregion - - private static int stringToKeyword(string name) - { - // The following assumes that EcmaScript.NET.Token.EOF == 0 - int id; - string s = name; - #region Generated Id Switch - L0: - { - id = 0; - string X = null; - int c; - L: - switch (s.Length) - { - case 2: - c = s[1]; - if (c == 'f') { if (s[0] == 'i') { id = Id_if; goto EL0; } } - else if (c == 'n') { if (s[0] == 'i') { id = Id_in; goto EL0; } } - else if (c == 'o') { if (s[0] == 'd') { id = Id_do; goto EL0; } } - break; - case 3: - switch (s[0]) - { - case 'f': - if (s[2] == 'r' && s[1] == 'o') { id = Id_for; goto EL0; } - break; - case 'i': - if (s[2] == 't' && s[1] == 'n') { id = Id_int; goto EL0; } - break; - case 'n': - if (s[2] == 'w' && s[1] == 'e') { id = Id_new; goto EL0; } - break; - case 't': - if (s[2] == 'y' && s[1] == 'r') { id = Id_try; goto EL0; } - break; - case 'v': - if (s[2] == 'r' && s[1] == 'a') { id = Id_var; goto EL0; } - break; - } - break; - case 4: - switch (s[0]) - { - case 'b': - X = "byte"; - id = Id_byte; - break; - case 'c': - c = s[3]; - if (c == 'e') { if (s[2] == 's' && s[1] == 'a') { id = Id_case; goto EL0; } } - else if (c == 'r') { if (s[2] == 'a' && s[1] == 'h') { id = Id_char; goto EL0; } } - break; - case 'e': - c = s[3]; - if (c == 'e') { if (s[2] == 's' && s[1] == 'l') { id = Id_else; goto EL0; } } - else if (c == 'm') { if (s[2] == 'u' && s[1] == 'n') { id = Id_enum; goto EL0; } } - break; - case 'g': - X = "goto"; - id = Id_goto; - break; - case 'l': - X = "long"; - id = Id_long; - break; - case 'n': - X = "null"; - id = Id_null; - break; - case 't': - c = s[3]; - if (c == 'e') { if (s[2] == 'u' && s[1] == 'r') { id = Id_true; goto EL0; } } - else if (c == 's') { if (s[2] == 'i' && s[1] == 'h') { id = Id_this; goto EL0; } } - break; - case 'v': - X = "void"; - id = Id_void; - break; - case 'w': - X = "with"; - id = Id_with; - break; - } - break; - case 5: - switch (s[2]) - { - case 'a': - X = "class"; - id = Id_class; - break; - case 'e': - X = "break"; - id = Id_break; - break; - case 'i': - X = "while"; - id = Id_while; - break; - case 'l': - X = "false"; - id = Id_false; - break; - case 'n': - c = s[0]; - if (c == 'c') { X = "const"; id = Id_const; } - else if (c == 'f') { X = "final"; id = Id_final; } - break; - case 'o': - c = s[0]; - if (c == 'f') { X = "float"; id = Id_float; } - else if (c == 's') { X = "short"; id = Id_short; } - break; - case 'p': - X = "super"; - id = Id_super; - break; - case 'r': - X = "throw"; - id = Id_throw; - break; - case 't': - X = "catch"; - id = Id_catch; - break; - } - break; - case 6: - switch (s[1]) - { - case 'a': - X = "native"; - id = Id_native; - break; - case 'e': - c = s[0]; - if (c == 'd') { X = "delete"; id = Id_delete; } - else if (c == 'r') { X = "return"; id = Id_return; } - break; - case 'h': - X = "throws"; - id = Id_throws; - break; - case 'm': - X = "import"; - id = Id_import; - break; - case 'o': - X = "double"; - id = Id_double; - break; - case 't': - X = "static"; - id = Id_static; - break; - case 'u': - X = "public"; - id = Id_public; - break; - case 'w': - X = "switch"; - id = Id_switch; - break; - case 'x': - X = "export"; - id = Id_export; - break; - case 'y': - X = "typeof"; - id = Id_typeof; - break; - } - break; - case 7: - switch (s[1]) - { - case 'a': - X = "package"; - id = Id_package; - break; - case 'e': - X = "default"; - id = Id_default; - break; - case 'i': - X = "finally"; - id = Id_finally; - break; - case 'o': - X = "boolean"; - id = Id_boolean; - break; - case 'r': - X = "private"; - id = Id_private; - break; - case 'x': - X = "extends"; - id = Id_extends; - break; - } - break; - case 8: - switch (s[0]) - { - case 'a': - X = "abstract"; - id = Id_abstract; - break; - case 'c': - X = "continue"; - id = Id_continue; - break; - case 'd': - X = "debugger"; - id = Id_debugger; - break; - case 'f': - X = "function"; - id = Id_function; - break; - case 'v': - X = "volatile"; - id = Id_volatile; - break; - } - break; - case 9: - c = s[0]; - if (c == 'i') { X = "interface"; id = Id_interface; } - else if (c == 'p') { X = "protected"; id = Id_protected; } - else if (c == 't') { X = "transient"; id = Id_transient; } - break; - case 10: - c = s[1]; - if (c == 'm') { X = "implements"; id = Id_implements; } - else if (c == 'n') { X = "instanceof"; id = Id_instanceof; } - break; - case 12: - X = "synchronized"; - id = Id_synchronized; - break; - } - if (X != null && X != s && !X.Equals(s)) - id = 0; - } - EL0: - - #endregion - if (id == 0) - { - return EcmaScript.NET.Token.EOF; - } - return id & 0xff; - } - - internal bool eof() - { - return hitEOF; - } - - private static bool isAlpha(int c) - { - // Use 'Z' < 'a' - if (c <= 'Z') - { - return 'A' <= c; - } - else - { - return 'a' <= c && c <= 'z'; - } - } - - internal static bool isDigit(int c) - { - return '0' <= c && c <= '9'; - } - - /* As defined in ECMA. jsscan.c uses C isspace() (which allows - * \v, I think.) note that code in getChar() implicitly accepts - * '\r' == - as well. - */ - internal static bool isJSSpace(int c) - { - if (c <= 127) - { - return c == 0x20 || c == 0x9 || c == 0xC || c == 0xB; - } - else - { - return c == 0xA0 || (int)char.GetUnicodeCategory((char)c) == (sbyte)System.Globalization.UnicodeCategory.SpaceSeparator; - } - } - - private static bool isJSFormatChar(int c) - { - return c > 127 && (int)char.GetUnicodeCategory((char)c) == (sbyte)System.Globalization.UnicodeCategory.Format; - } - - /// Parser calls the method when it gets / or /= in literal context. - internal void readRegExp(int startToken) - { - stringBufferTop = 0; - if (startToken == EcmaScript.NET.Token.ASSIGN_DIV) - { - // Miss-scanned /= - addToString('='); - } - else - { - if (startToken != EcmaScript.NET.Token.DIV) - Context.CodeBug(); - } - - int c; - bool inClass = false; - while ((c = Char) != '/' || inClass) - { - if (c == '\n' || c == EOF_CHAR) - { - ungetChar(c); - throw parser.ReportError("msg.unterminated.re.lit"); - } - if (c == '\\') - { - addToString(c); - c = Char; - } - else if (c == '[') - { - inClass = true; - } - else if (c == ']') - { - inClass = false; - } - - addToString(c); - } - - int reEnd = stringBufferTop; - - while (true) - { - if (matchChar('g')) - addToString('g'); - else if (matchChar('i')) - addToString('i'); - else if (matchChar('m')) - addToString('m'); - else - break; - } - - if (isAlpha(peekChar())) - { - throw parser.ReportError("msg.invalid.re.flag"); - } - - this.str = new string(stringBuffer, 0, reEnd); - this.regExpFlags = new string(stringBuffer, reEnd, stringBufferTop - reEnd); - } - - /// - private bool readQuotedString(int quote) - { - for (int c = Char; c != EOF_CHAR; c = Char) - { - addToString(c); - if (c == quote) - return true; - } - - stringBufferTop = 0; // throw away the string in progress - this.str = null; - parser.AddError("msg.XML.bad.form"); - return false; - } - - /// - private bool readXmlComment() - { - for (int c = Char; c != EOF_CHAR; ) - { - addToString(c); - if (c == '-' && peekChar() == '-') - { - c = Char; - addToString(c); - if (peekChar() == '>') - { - c = Char; // Skip > - addToString(c); - return true; - } - else - { - continue; - } - } - c = Char; - } - - stringBufferTop = 0; // throw away the string in progress - this.str = null; - parser.AddError("msg.XML.bad.form"); - return false; - } - - /// - private bool readCDATA() - { - for (int c = Char; c != EOF_CHAR; ) - { - addToString(c); - if (c == ']' && peekChar() == ']') - { - c = Char; - addToString(c); - if (peekChar() == '>') - { - c = Char; // Skip > - addToString(c); - return true; - } - else - { - continue; - } - } - c = Char; - } - - stringBufferTop = 0; // throw away the string in progress - this.str = null; - parser.AddError("msg.XML.bad.form"); - return false; - } - - /// - private bool readEntity() - { - int declTags = 1; - for (int c = Char; c != EOF_CHAR; c = Char) - { - addToString(c); - switch (c) - { - - case '<': - declTags++; - break; - - case '>': - declTags--; - if (declTags == 0) - return true; - break; - } - } - - stringBufferTop = 0; // throw away the string in progress - this.str = null; - parser.AddError("msg.XML.bad.form"); - return false; - } - - /// - private bool readPI() - { - for (int c = Char; c != EOF_CHAR; c = Char) - { - addToString(c); - if (c == '?' && peekChar() == '>') - { - c = Char; // Skip > - addToString(c); - return true; - } - } - - stringBufferTop = 0; // throw away the string in progress - this.str = null; - parser.AddError("msg.XML.bad.form"); - return false; - } - - private void addToString(int c) - { - int N = stringBufferTop; - if (N == stringBuffer.Length) - { - char[] tmp = new char[stringBuffer.Length * 4]; - Array.Copy(stringBuffer, 0, tmp, 0, N); - stringBuffer = tmp; - } - stringBuffer[N] = (char)c; - stringBufferTop = N + 1; - } - - private void ungetChar(int c) - { - // can not unread past across line boundary - if (ungetCursor != 0 && ungetBuffer[ungetCursor - 1] == '\n') - Context.CodeBug(); - ungetBuffer[ungetCursor++] = c; - } - - private bool matchChar(int test) - { - int c = Char; - if (c == test) - { - return true; - } - else - { - ungetChar(c); - return false; - } - } - - private int peekChar() - { - int c = Char; - ungetChar(c); - return c; - } - - private void skipLine() - { - // skip to end of line - int c; - while ((c = Char) != EOF_CHAR && c != '\n') - { - } - ungetChar(c); - } - - private bool fillSourceBuffer() - { - if (sourceString != null) - Context.CodeBug(); - if (sourceEnd == sourceBuffer.Length) - { - if (lineStart != 0) - { - Array.Copy(sourceBuffer, lineStart, sourceBuffer, 0, sourceEnd - lineStart); - sourceEnd -= lineStart; - sourceCursor -= lineStart; - lineStart = 0; - } - else - { - char[] tmp = new char[sourceBuffer.Length * 2]; - Array.Copy(sourceBuffer, 0, tmp, 0, sourceEnd); - sourceBuffer = tmp; - } - } - int n = sourceReader.Read(sourceBuffer, sourceEnd, sourceBuffer.Length - sourceEnd); - if (n <= 0) - { - return false; - } - sourceEnd += n; - return true; - } - - // stuff other than whitespace since start of line - private bool dirtyLine; - - internal string regExpFlags; - - - // Set this to an inital non-null value so that the Parser has - // something to retrieve even if an error has occured and no - // string is found. Fosters one class of error, but saves lots of - // code. - private string str = ""; - private double dNumber; - - - private char[] stringBuffer = new char[128]; - private int stringBufferTop; - private ObjToIntMap allStrings = new ObjToIntMap(50); - - // Room to backtrace from to < on failed match of the last - in