Переглянути джерело

Add fast case for single statement in a block (#1730)

* Add fast case for single statement in a block

* improve assignment fast case checks

* update benchmark results
Marko Lahma 1 рік тому
батько
коміт
1de233467a

+ 4 - 3
Directory.Packages.props

@@ -4,11 +4,12 @@
     <CentralPackageTransitivePinningEnabled>false</CentralPackageTransitivePinningEnabled>
   </PropertyGroup>
   <ItemGroup>
-    <PackageVersion Include="BenchmarkDotNet" Version="0.13.11" />
+    <PackageVersion Include="BenchmarkDotNet" Version="0.13.12" />
+    <PackageVersion Include="BenchmarkDotNet.TestAdapter" Version="0.13.12" />
     <PackageVersion Include="Esprima" Version="3.0.4" />
     <PackageVersion Include="Flurl.Http.Signed" Version="3.2.4" />
     <PackageVersion Include="Jurassic" Version="3.2.7" />
-    <PackageVersion Include="Meziantou.Analyzer" Version="2.0.132" />
+    <PackageVersion Include="Meziantou.Analyzer" Version="2.0.135" />
     <PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
     <PackageVersion Include="Microsoft.Extensions.TimeProvider.Testing" Version="8.0.0" />
     <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
@@ -22,7 +23,7 @@
     <PackageVersion Include="Spectre.Console.Cli" Version="0.45.0" />
     <PackageVersion Include="System.Text.Json" Version="6.0.8" />
     <PackageVersion Include="Test262Harness" Version="0.0.23" />
-    <PackageVersion Include="xunit" Version="2.6.4" />
+    <PackageVersion Include="xunit" Version="2.6.5" />
     <PackageVersion Include="xunit.runner.visualstudio" Version="2.5.6" PrivateAssets="all" />
     <PackageVersion Include="YantraJS.Core" Version="1.2.206" />
   </ItemGroup>

+ 70 - 70
Jint.Benchmark/README.md

@@ -9,16 +9,16 @@ dotnet run -c Release --allCategories EngineComparison
 * tests are run in global engine strict mode, as YantraJS always uses strict mode which improves performance
 * `Jint` and `Jint_ParsedScript` shows the difference between always parsing the script source file and reusing parsed `Script` instance.
 
-Last updated 2023-11-24
+Last updated 2024-01-07
 
 * Jint main
 * Jurassic 3.2.7
-* NiL.JS 2.5.1674
+* NiL.JS 2.5.1677
 * YantraJS.Core 1.2.206
 
 ```
 
-BenchmarkDotNet v0.13.10, Windows 11 (10.0.23590.1000)
+BenchmarkDotNet v0.13.12, Windows 11 (10.0.23612.1000)
 AMD Ryzen 9 5950X, 1 CPU, 32 logical and 16 physical cores
 .NET SDK 8.0.100
   [Host]     : .NET 8.0.0 (8.0.23.53103), X64 RyuJIT AVX2
@@ -26,70 +26,70 @@ AMD Ryzen 9 5950X, 1 CPU, 32 logical and 16 physical cores
 
 
 ```
-| Method            | FileName             | Mean             | StdDev         | Median           | Rank | Allocated     |
-|------------------ |--------------------- |-----------------:|---------------:|-----------------:|-----:|--------------:|
-| Jint_ParsedScript | array-stress         |     5,743.534 μs |      7.3172 μs |     5,745.281 μs |    1 |    6994.68 KB |
-| Jint              | array-stress         |     6,057.026 μs |     20.8293 μs |     6,053.305 μs |    2 |    7015.86 KB |
-| NilJS             | array-stress         |     6,439.544 μs |     51.8156 μs |     6,411.337 μs |    3 |    4533.76 KB |
-| YantraJS          | array-stress         |     7,788.618 μs |     90.7911 μs |     7,762.502 μs |    4 |     8073.6 KB |
-| Jurassic          | array-stress         |    11,360.976 μs |     35.3027 μs |    11,350.166 μs |    5 |   11647.14 KB |
-|                   |                      |                  |                |                  |      |               |
-| YantraJS          | dromaeo-3d-cube      |     5,193.282 μs |     14.8710 μs |     5,194.805 μs |    1 |   11412.17 KB |
-| NilJS             | dromaeo-3d-cube      |     7,770.177 μs |      6.6077 μs |     7,769.502 μs |    2 |    4693.22 KB |
-| Jint              | dromaeo-3d-cube      |    21,327.081 μs |     64.2045 μs |    21,320.428 μs |    3 |    6221.67 KB |
-| Jint_ParsedScript | dromaeo-3d-cube      |    22,275.834 μs |     48.3428 μs |    22,297.088 μs |    4 |    5964.35 KB |
-| Jurassic          | dromaeo-3d-cube      |    53,870.773 μs |     75.4553 μs |    53,884.650 μs |    5 |   10671.29 KB |
-|                   |                      |                  |                |                  |      |               |
-| NilJS             | dromaeo-core-eval    |     1,576.585 μs |      6.4653 μs |     1,573.083 μs |    1 |    1598.62 KB |
-| Jint_ParsedScript | dromaeo-core-eval    |     3,678.113 μs |     10.0864 μs |     3,675.353 μs |    2 |     332.16 KB |
-| Jint              | dromaeo-core-eval    |     3,681.029 μs |     14.6248 μs |     3,679.859 μs |    2 |     349.21 KB |
-| YantraJS          | dromaeo-core-eval    |     6,366.414 μs |     57.4589 μs |     6,366.488 μs |    3 |   36528.17 KB |
-| Jurassic          | dromaeo-core-eval    |    10,197.244 μs |     41.9562 μs |    10,196.675 μs |    4 |    2883.96 KB |
-|                   |                      |                  |                |                  |      |               |
-| Jint              | dromaeo-object-array |    41,313.413 μs |     55.8197 μs |    41,316.171 μs |    1 |  100409.09 KB |
-| Jint_ParsedScript | dromaeo-object-array |    43,203.196 μs |    411.2464 μs |    43,182.583 μs |    2 |   100368.9 KB |
-| Jurassic          | dromaeo-object-array |    43,495.683 μs |    252.0098 μs |    43,432.125 μs |    2 |   25812.61 KB |
-| YantraJS          | dromaeo-object-array |    61,354.141 μs |    482.3533 μs |    61,399.233 μs |    3 |    29478.4 KB |
-| NilJS             | dromaeo-object-array |    69,506.941 μs |    353.6867 μs |    69,678.650 μs |    4 |   17697.94 KB |
-|                   |                      |                  |                |                  |      |               |
-| Jint_ParsedScript | droma(...)egexp [21] |   161,815.086 μs |  2,069.4489 μs |   161,312.500 μs |    1 |  170563.44 KB |
-| Jint              | droma(...)egexp [21] |   178,719.583 μs |  3,296.4427 μs |   178,220.700 μs |    2 |  166374.76 KB |
-| NilJS             | droma(...)egexp [21] |   682,594.680 μs | 12,026.0148 μs |   680,779.900 μs |    3 |  767253.88 KB |
-| Jurassic          | droma(...)egexp [21] |   769,271.481 μs | 13,898.9774 μs |   768,696.800 μs |    4 |  822217.41 KB |
-| YantraJS          | droma(...)egexp [21] | 1,222,190.085 μs | 14,714.3112 μs | 1,226,884.600 μs |    5 | 1156481.56 KB |
-|                   |                      |                  |                |                  |      |               |
-| Jint              | droma(...)tring [21] |   281,911.294 μs | 11,500.7817 μs |   275,857.700 μs |    1 | 1322546.26 KB |
-| Jint_ParsedScript | droma(...)tring [21] |   283,360.189 μs | 13,196.6995 μs |   289,888.500 μs |    1 | 1322259.13 KB |
-| NilJS             | droma(...)tring [21] |   293,451.562 μs |  9,299.1813 μs |   292,627.800 μs |    2 | 1378002.01 KB |
-| Jurassic          | droma(...)tring [21] |   294,483.175 μs |  5,379.5117 μs |   292,301.200 μs |    2 | 1458171.62 KB |
-| YantraJS          | droma(...)tring [21] |   998,437.664 μs | 11,452.5169 μs |   996,547.500 μs |    3 | 15730070.8 KB |
-|                   |                      |                  |                |                  |      |               |
-| NilJS             | droma(...)ase64 [21] |    33,371.183 μs |    600.4889 μs |    33,236.238 μs |    1 |   19604.34 KB |
-| YantraJS          | droma(...)ase64 [21] |    46,639.004 μs |    560.7072 μs |    46,511.677 μs |    2 |  760382.48 KB |
-| Jint_ParsedScript | droma(...)ase64 [21] |    49,046.433 μs |    159.2847 μs |    49,076.973 μs |    3 |    6720.18 KB |
-| Jint              | droma(...)ase64 [21] |    53,332.563 μs |     86.6296 μs |    53,336.920 μs |    4 |    6804.01 KB |
-| Jurassic          | droma(...)ase64 [21] |    72,535.076 μs |    149.4006 μs |    72,556.157 μs |    5 |    73295.9 KB |
-|                   |                      |                  |                |                  |      |               |
-| Jint_ParsedScript | evaluation           |        10.408 μs |      0.0391 μs |        10.407 μs |    1 |      27.39 KB |
-| Jint              | evaluation           |        24.182 μs |      0.1142 μs |        24.183 μs |    2 |      35.96 KB |
-| NilJS             | evaluation           |        37.611 μs |      0.0868 μs |        37.595 μs |    3 |      23.47 KB |
-| YantraJS          | evaluation           |       148.389 μs |      2.2671 μs |       148.973 μs |    4 |     923.46 KB |
-| Jurassic          | evaluation           |     1,419.913 μs |      5.1742 μs |     1,416.831 μs |    5 |     420.34 KB |
-|                   |                      |                  |                |                  |      |               |
-| Jint_ParsedScript | linq-js              |        86.047 μs |      0.1106 μs |        86.063 μs |    1 |     225.04 KB |
-| YantraJS          | linq-js              |       447.064 μs |      2.5265 μs |       447.306 μs |    2 |    1443.82 KB |
-| Jint              | linq-js              |     1,657.159 μs |      5.9514 μs |     1,655.432 μs |    3 |    1273.81 KB |
-| NilJS             | linq-js              |     6,464.907 μs |     24.8496 μs |     6,454.286 μs |    4 |     4121.1 KB |
-| Jurassic          | linq-js              |    34,559.331 μs |    107.6734 μs |    34,576.087 μs |    5 |    9252.69 KB |
-|                   |                      |                  |                |                  |      |               |
-| Jint_ParsedScript | minimal              |         2.823 μs |      0.0219 μs |         2.825 μs |    1 |      12.95 KB |
-| NilJS             | minimal              |         3.865 μs |      0.0120 μs |         3.866 μs |    2 |       4.81 KB |
-| Jint              | minimal              |         4.026 μs |      0.0270 μs |         4.021 μs |    3 |      14.34 KB |
-| YantraJS          | minimal              |       141.911 μs |      2.0400 μs |       142.566 μs |    4 |     918.04 KB |
-| Jurassic          | minimal              |       257.933 μs |      0.9659 μs |       257.808 μs |    5 |     386.21 KB |
-|                   |                      |                  |                |                  |      |               |
-| YantraJS          | stopwatch            |    86,849.358 μs |    572.5135 μs |    86,760.983 μs |    1 |  224269.43 KB |
-| NilJS             | stopwatch            |   178,338.578 μs |  1,337.6320 μs |   178,821.267 μs |    2 |    97360.8 KB |
-| Jurassic          | stopwatch            |   189,956.560 μs |    315.0817 μs |   189,865.650 μs |    3 |  156935.97 KB |
-| Jint              | stopwatch            |   279,388.597 μs |  1,913.7346 μs |   279,178.250 μs |    4 |   53039.87 KB |
-| Jint_ParsedScript | stopwatch            |   288,838.154 μs |    851.9774 μs |   288,601.800 μs |    5 |   53015.34 KB |
+| Method            | FileName             | Mean             | StdDev         | Median           | Rank | Allocated      |
+|------------------ |--------------------- |-----------------:|---------------:|-----------------:|-----:|---------------:|
+| Jint              | array-stress         |     5,701.482 μs |     51.0031 μs |     5,679.317 μs |    1 |     7013.19 KB |
+| Jint_ParsedScript | array-stress         |     5,769.485 μs |     18.1258 μs |     5,773.903 μs |    2 |     6992.01 KB |
+| NilJS             | array-stress         |     6,734.868 μs |     32.5135 μs |     6,736.069 μs |    3 |     4533.76 KB |
+| YantraJS          | array-stress         |     7,306.284 μs |    262.7479 μs |     7,307.334 μs |    4 |     8073.61 KB |
+| Jurassic          | array-stress         |    11,089.906 μs |     71.6310 μs |    11,067.920 μs |    5 |    11647.13 KB |
+|                   |                      |                  |                |                  |      |                |
+| YantraJS          | dromaeo-3d-cube      |     5,297.465 μs |     43.8914 μs |     5,316.661 μs |    1 |    11412.16 KB |
+| NilJS             | dromaeo-3d-cube      |     7,853.636 μs |     19.0195 μs |     7,859.741 μs |    2 |     4693.22 KB |
+| Jint_ParsedScript | dromaeo-3d-cube      |    19,773.251 μs |     43.0614 μs |    19,783.850 μs |    3 |     5951.47 KB |
+| Jint              | dromaeo-3d-cube      |    20,295.461 μs |     20.8509 μs |    20,298.377 μs |    4 |     6208.78 KB |
+| Jurassic          | dromaeo-3d-cube      |    54,596.781 μs |    286.3963 μs |    54,645.300 μs |    5 |    10668.95 KB |
+|                   |                      |                  |                |                  |      |                |
+| NilJS             | dromaeo-core-eval    |     1,610.734 μs |      9.8954 μs |     1,608.199 μs |    1 |     1598.62 KB |
+| Jint              | dromaeo-core-eval    |     3,408.444 μs |      5.5157 μs |     3,408.544 μs |    2 |      340.16 KB |
+| Jint_ParsedScript | dromaeo-core-eval    |     3,447.974 μs |    110.0602 μs |     3,510.933 μs |    3 |      323.11 KB |
+| YantraJS          | dromaeo-core-eval    |     5,710.298 μs |     30.4418 μs |     5,719.807 μs |    4 |    36528.17 KB |
+| Jurassic          | dromaeo-core-eval    |    10,190.579 μs |     45.9655 μs |    10,182.594 μs |    5 |     2883.96 KB |
+|                   |                      |                  |                |                  |      |                |
+| Jint_ParsedScript | dromaeo-object-array |    41,591.401 μs |    178.5376 μs |    41,647.465 μs |    1 |   100366.56 KB |
+| Jint              | dromaeo-object-array |    41,745.082 μs |    604.3720 μs |    41,368.333 μs |    1 |   100406.57 KB |
+| Jurassic          | dromaeo-object-array |    43,073.872 μs |    181.7241 μs |    43,032.475 μs |    2 |    25812.61 KB |
+| YantraJS          | dromaeo-object-array |    59,837.230 μs |    375.7357 μs |    59,707.694 μs |    3 |     29477.8 KB |
+| NilJS             | dromaeo-object-array |    68,668.962 μs |    145.9207 μs |    68,675.675 μs |    4 |    17697.94 KB |
+|                   |                      |                  |                |                  |      |                |
+| Jint_ParsedScript | droma(...)egexp [21] |   160,631.005 μs |  3,835.9723 μs |   159,112.100 μs |    1 |   163386.28 KB |
+| Jint              | droma(...)egexp [21] |   170,538.964 μs |  2,311.2516 μs |   169,839.850 μs |    2 |   161772.38 KB |
+| NilJS             | droma(...)egexp [21] |   678,785.547 μs |  5,091.0200 μs |   680,536.600 μs |    3 |   767311.77 KB |
+| Jurassic          | droma(...)egexp [21] |   750,543.741 μs | 20,457.1115 μs |   747,805.000 μs |    4 |   824711.31 KB |
+| YantraJS          | droma(...)egexp [21] | 1,204,735.240 μs | 27,385.4498 μs | 1,211,328.950 μs |    5 |   1153992.6 KB |
+|                   |                      |                  |                |                  |      |                |
+| Jint_ParsedScript | droma(...)tring [21] |   264,195.604 μs | 16,545.2879 μs |   260,601.000 μs |    1 |  1321631.38 KB |
+| Jint              | droma(...)tring [21] |   272,963.545 μs | 15,656.3798 μs |   275,780.400 μs |    1 |  1321635.98 KB |
+| NilJS             | droma(...)tring [21] |   277,067.087 μs |  7,920.0419 μs |   275,379.550 μs |    1 |  1378224.85 KB |
+| Jurassic          | droma(...)tring [21] |   291,717.260 μs |  2,856.5265 μs |   292,119.900 μs |    2 |  1458185.09 KB |
+| YantraJS          | droma(...)tring [21] |   963,754.713 μs | 13,326.8728 μs |   964,274.200 μs |    3 | 15730168.65 KB |
+|                   |                      |                  |                |                  |      |                |
+| NilJS             | droma(...)ase64 [21] |    33,150.312 μs |    470.2301 μs |    33,029.025 μs |    1 |    19604.02 KB |
+| YantraJS          | droma(...)ase64 [21] |    46,806.801 μs |    620.2937 μs |    46,589.700 μs |    2 |   760382.51 KB |
+| Jint_ParsedScript | droma(...)ase64 [21] |    50,864.849 μs |    150.8649 μs |    50,804.680 μs |    3 |      6032.5 KB |
+| Jint              | droma(...)ase64 [21] |    51,077.330 μs |    108.1142 μs |    51,066.010 μs |    3 |     6116.48 KB |
+| Jurassic          | droma(...)ase64 [21] |    77,242.467 μs |    380.2579 μs |    77,224.329 μs |    4 |    73294.74 KB |
+|                   |                      |                  |                |                  |      |                |
+| Jint_ParsedScript | evaluation           |        10.778 μs |      0.0597 μs |        10.785 μs |    1 |       26.96 KB |
+| Jint              | evaluation           |        24.090 μs |      0.0847 μs |        24.096 μs |    2 |       35.53 KB |
+| NilJS             | evaluation           |        39.711 μs |      0.0998 μs |        39.727 μs |    3 |       23.47 KB |
+| YantraJS          | evaluation           |       144.919 μs |      2.3529 μs |       146.034 μs |    4 |      923.46 KB |
+| Jurassic          | evaluation           |     1,416.071 μs |      5.0698 μs |     1,417.150 μs |    5 |      420.34 KB |
+|                   |                      |                  |                |                  |      |                |
+| Jint_ParsedScript | linq-js              |        89.984 μs |      0.3819 μs |        89.854 μs |    1 |      217.66 KB |
+| YantraJS          | linq-js              |       420.317 μs |      2.8181 μs |       419.227 μs |    2 |     1443.82 KB |
+| Jint              | linq-js              |     1,678.234 μs |      5.1131 μs |     1,679.083 μs |    3 |     1266.43 KB |
+| NilJS             | linq-js              |     6,592.095 μs |     15.3867 μs |     6,589.037 μs |    4 |      4121.1 KB |
+| Jurassic          | linq-js              |    34,391.184 μs |    131.4197 μs |    34,396.960 μs |    5 |     9252.67 KB |
+|                   |                      |                  |                |                  |      |                |
+| Jint_ParsedScript | minimal              |         2.672 μs |      0.0170 μs |         2.676 μs |    1 |       13.09 KB |
+| NilJS             | minimal              |         4.038 μs |      0.0117 μs |         4.041 μs |    2 |        4.81 KB |
+| Jint              | minimal              |         4.270 μs |      0.0241 μs |         4.279 μs |    3 |       14.48 KB |
+| YantraJS          | minimal              |       138.742 μs |      2.5846 μs |       139.684 μs |    4 |      918.04 KB |
+| Jurassic          | minimal              |       256.402 μs |      2.0078 μs |       255.556 μs |    5 |      386.21 KB |
+|                   |                      |                  |                |                  |      |                |
+| YantraJS          | stopwatch            |    87,634.606 μs |    543.9371 μs |    87,538.233 μs |    1 |   224269.43 KB |
+| NilJS             | stopwatch            |   175,626.098 μs |    668.8330 μs |   175,822.317 μs |    2 |     97360.8 KB |
+| Jurassic          | stopwatch            |   187,896.208 μs |    651.0930 μs |   188,090.833 μs |    3 |   156936.57 KB |
+| Jint_ParsedScript | stopwatch            |   281,773.983 μs |    468.8012 μs |   281,660.500 μs |    4 |    53013.96 KB |
+| Jint              | stopwatch            |   291,598.703 μs |    599.9161 μs |   291,632.900 μs |    5 |    53038.47 KB |

+ 1 - 1
Jint.Tests/Runtime/EngineLimitTests.cs

@@ -9,7 +9,7 @@ public class EngineLimitTests
 {
 
 #if RELEASE
-    const int FunctionNestingCount = 1000;
+    const int FunctionNestingCount = 990;
 #else
     const int FunctionNestingCount = 495;
 #endif

+ 85 - 57
Jint/Runtime/Interpreter/Expressions/JintAssignmentExpression.cs

@@ -11,12 +11,16 @@ namespace Jint.Runtime.Interpreter.Expressions
     internal sealed class JintAssignmentExpression : JintExpression
     {
         private readonly JintExpression _left;
+        private readonly JintIdentifierExpression? _leftIdentifier;
+
         private readonly JintExpression _right;
         private readonly AssignmentOperator _operator;
 
         private JintAssignmentExpression(AssignmentExpression expression) : base(expression)
         {
             _left = Build((Expression) expression.Left);
+            _leftIdentifier = _left as JintIdentifierExpression;
+
             _right = Build(expression.Right);
             _operator = expression.Operator;
         }
@@ -38,70 +42,37 @@ namespace Jint.Runtime.Interpreter.Expressions
 
         protected override object EvaluateInternal(EvaluationContext context)
         {
-            var lref = _left.Evaluate(context) as Reference;
-            if (lref is null)
+            var engine = context.Engine;
+
+            JsValue originalLeftValue;
+            Reference lref;
+            if (_leftIdentifier is not null && JintEnvironment.TryGetIdentifierEnvironmentWithBindingValue(
+                    engine.ExecutionContext.LexicalEnvironment,
+                    _leftIdentifier.Identifier,
+                    StrictModeScope.IsStrictModeCode,
+                    out var identifierEnvironment,
+                    out var temp))
             {
-                ExceptionHelper.ThrowReferenceError(context.Engine.Realm, "not a valid reference");
+                originalLeftValue = temp;
+                lref = engine._referencePool.Rent(identifierEnvironment, _leftIdentifier.Identifier.Value, StrictModeScope.IsStrictModeCode, thisValue: null);
+            }
+            else
+            {
+                // fast lookup with binding name failed, we need to go through the reference
+                lref = (_left.Evaluate(context) as Reference)!;
+                if (lref is null)
+                {
+                    ExceptionHelper.ThrowReferenceError(context.Engine.Realm, "not a valid reference");
+                }
+                originalLeftValue = context.Engine.GetValue(lref, returnReferenceToPool: false);
             }
 
-            var engine = context.Engine;
-            var originalLeftValue = context.Engine.GetValue(lref, false);
             var handledByOverload = false;
             JsValue? newLeftValue = null;
 
             if (context.OperatorOverloadingAllowed)
             {
-                string? operatorClrName = null;
-                switch (_operator)
-                {
-                    case AssignmentOperator.PlusAssign:
-                        operatorClrName = "op_Addition";
-                        break;
-                    case AssignmentOperator.MinusAssign:
-                        operatorClrName = "op_Subtraction";
-                        break;
-                    case AssignmentOperator.TimesAssign:
-                        operatorClrName = "op_Multiply";
-                        break;
-                    case AssignmentOperator.DivideAssign:
-                        operatorClrName = "op_Division";
-                        break;
-                    case AssignmentOperator.ModuloAssign:
-                        operatorClrName = "op_Modulus";
-                        break;
-                    case AssignmentOperator.BitwiseAndAssign:
-                        operatorClrName = "op_BitwiseAnd";
-                        break;
-                    case AssignmentOperator.BitwiseOrAssign:
-                        operatorClrName = "op_BitwiseOr";
-                        break;
-                    case AssignmentOperator.BitwiseXorAssign:
-                        operatorClrName = "op_ExclusiveOr";
-                        break;
-                    case AssignmentOperator.LeftShiftAssign:
-                        operatorClrName = "op_LeftShift";
-                        break;
-                    case AssignmentOperator.RightShiftAssign:
-                        operatorClrName = "op_RightShift";
-                        break;
-                    case AssignmentOperator.UnsignedRightShiftAssign:
-                        operatorClrName = "op_UnsignedRightShift";
-                        break;
-                    case AssignmentOperator.ExponentiationAssign:
-                    case AssignmentOperator.Assign:
-                    default:
-                        break;
-                }
-
-                if (operatorClrName != null)
-                {
-                    var rval = _right.GetValue(context);
-                    if (JintBinaryExpression.TryOperatorOverloading(context, originalLeftValue, rval, operatorClrName, out var result))
-                    {
-                        newLeftValue = JsValue.FromObject(context.Engine, result);
-                        handledByOverload = true;
-                    }
-                }
+                newLeftValue = EvaluateOperatorOverloading(context, originalLeftValue, newLeftValue, ref handledByOverload);
             }
 
             var wasMutatedInPlace = false;
@@ -298,7 +269,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                             var exponent = TypeConverter.ToBigInt(rval);
                             if (exponent > int.MaxValue || exponent < int.MinValue)
                             {
-                                ExceptionHelper.ThrowTypeError(context.Engine.Realm, "Cannot do exponentation with exponent not fitting int32");
+                                ExceptionHelper.ThrowTypeError(context.Engine.Realm, "Cannot do exponentiation with exponent not fitting int32");
                             }
                             newLeftValue = JsBigInt.Create(BigInteger.Pow(TypeConverter.ToBigInt(originalLeftValue), (int) exponent));
                         }
@@ -322,6 +293,63 @@ namespace Jint.Runtime.Interpreter.Expressions
             return newLeftValue!;
         }
 
+        private JsValue? EvaluateOperatorOverloading(EvaluationContext context, JsValue originalLeftValue, JsValue? newLeftValue, ref bool handledByOverload)
+        {
+            string? operatorClrName = null;
+            switch (_operator)
+            {
+                case AssignmentOperator.PlusAssign:
+                    operatorClrName = "op_Addition";
+                    break;
+                case AssignmentOperator.MinusAssign:
+                    operatorClrName = "op_Subtraction";
+                    break;
+                case AssignmentOperator.TimesAssign:
+                    operatorClrName = "op_Multiply";
+                    break;
+                case AssignmentOperator.DivideAssign:
+                    operatorClrName = "op_Division";
+                    break;
+                case AssignmentOperator.ModuloAssign:
+                    operatorClrName = "op_Modulus";
+                    break;
+                case AssignmentOperator.BitwiseAndAssign:
+                    operatorClrName = "op_BitwiseAnd";
+                    break;
+                case AssignmentOperator.BitwiseOrAssign:
+                    operatorClrName = "op_BitwiseOr";
+                    break;
+                case AssignmentOperator.BitwiseXorAssign:
+                    operatorClrName = "op_ExclusiveOr";
+                    break;
+                case AssignmentOperator.LeftShiftAssign:
+                    operatorClrName = "op_LeftShift";
+                    break;
+                case AssignmentOperator.RightShiftAssign:
+                    operatorClrName = "op_RightShift";
+                    break;
+                case AssignmentOperator.UnsignedRightShiftAssign:
+                    operatorClrName = "op_UnsignedRightShift";
+                    break;
+                case AssignmentOperator.ExponentiationAssign:
+                case AssignmentOperator.Assign:
+                default:
+                    break;
+            }
+
+            if (operatorClrName != null)
+            {
+                var rval = _right.GetValue(context);
+                if (JintBinaryExpression.TryOperatorOverloading(context, originalLeftValue, rval, operatorClrName, out var result))
+                {
+                    newLeftValue = JsValue.FromObject(context.Engine, result);
+                    handledByOverload = true;
+                }
+            }
+
+            return newLeftValue;
+        }
+
         private JsValue NamedEvaluation(EvaluationContext context, JintExpression expression)
         {
             var rval = expression.GetValue(context);

+ 2 - 2
Jint/Runtime/Interpreter/JintStatementList.cs

@@ -126,7 +126,7 @@ namespace Jint.Runtime.Interpreter
             return c.UpdateEmpty(lastValue).UpdateEmpty(JsValue.Undefined);
         }
 
-        private static Completion HandleException(EvaluationContext context, Exception exception, JintStatement? s)
+        internal static Completion HandleException(EvaluationContext context, Exception exception, JintStatement? s)
         {
             if (exception is JavaScriptException javaScriptException)
             {
@@ -148,7 +148,7 @@ namespace Jint.Runtime.Interpreter
             throw exception;
         }
 
-        private static Completion HandleError(Engine engine, JintStatement? s)
+        internal static Completion HandleError(Engine engine, JintStatement? s)
         {
             var error = engine._error!;
             engine._error = null;

+ 47 - 6
Jint/Runtime/Interpreter/Statements/JintBlockStatement.cs

@@ -6,7 +6,8 @@ namespace Jint.Runtime.Interpreter.Statements
 {
     internal sealed class JintBlockStatement : JintStatement<BlockStatement>
     {
-        private JintStatementList _statementList = null!;
+        private JintStatementList? _statementList;
+        private JintStatement? _singleStatement;
         private List<Declaration>? _lexicalDeclarations;
 
         public JintBlockStatement(BlockStatement blockStatement) : base(blockStatement)
@@ -15,8 +16,15 @@ namespace Jint.Runtime.Interpreter.Statements
 
         protected override void Initialize(EvaluationContext context)
         {
-            _statementList = new JintStatementList(_statement, _statement.Body);
             _lexicalDeclarations = HoistingScope.GetLexicalDeclarations(_statement);
+            if (_statement.Body.Count == 1)
+            {
+                _singleStatement = Build(_statement.Body[0]);
+            }
+            else
+            {
+                _statementList = new JintStatementList(_statement, _statement.Body);
+            }
         }
 
         /// <summary>
@@ -24,10 +32,9 @@ namespace Jint.Runtime.Interpreter.Statements
         /// </summary>
         public Completion ExecuteBlock(EvaluationContext context)
         {
-            if (_statementList is null)
+            if (_statementList is null && _singleStatement is null)
             {
-                _statementList = new JintStatementList(_statement, _statement.Body);
-                _lexicalDeclarations = HoistingScope.GetLexicalDeclarations(_statement);
+                Initialize(context);
             }
 
             Environment? oldEnv = null;
@@ -40,7 +47,15 @@ namespace Jint.Runtime.Interpreter.Statements
                 engine.UpdateLexicalEnvironment(blockEnv);
             }
 
-            var blockValue = _statementList.Execute(context);
+            Completion blockValue;
+            if (_singleStatement is not null)
+            {
+                blockValue = ExecuteSingle(context);
+            }
+            else
+            {
+                blockValue = _statementList!.Execute(context);
+            }
 
             if (oldEnv is not null)
             {
@@ -50,6 +65,32 @@ namespace Jint.Runtime.Interpreter.Statements
             return blockValue;
         }
 
+        private Completion ExecuteSingle(EvaluationContext context)
+        {
+            Completion blockValue;
+            try
+            {
+                blockValue = _singleStatement!.Execute(context);
+                if (context.Engine._error is not null)
+                {
+                    blockValue = JintStatementList.HandleError(context.Engine, _singleStatement);
+                }
+            }
+            catch (Exception ex)
+            {
+                if (ex is JintException)
+                {
+                    blockValue = JintStatementList.HandleException(context, ex, _singleStatement);
+                }
+                else
+                {
+                    throw;
+                }
+            }
+
+            return blockValue;
+        }
+
         protected override Completion ExecuteInternal(EvaluationContext context)
         {
             return ExecuteBlock(context);