Explorar el Código

Fix script-level strict mode handling (#1043)

Marko Lahma hace 3 años
padre
commit
a6c09a95c8

+ 13 - 0
Jint.Tests/Runtime/EngineTests.cs

@@ -2790,6 +2790,19 @@ x.test = {
             Assert.Equal(1, result);
         }
 
+        [Fact]
+        public void ShouldObeyScriptLevelStrictModeInFunctions()
+        {
+            var engine = new Engine();
+            const string source = "'use strict'; var x = () => { delete Boolean.prototype; }; x();";
+            var ex = Assert.Throws<JavaScriptException>(() => engine.Evaluate(source));
+            Assert.Equal("Cannot delete property 'prototype' of function Boolean() { [native code] }", ex.Message);
+
+            const string source2 = "'use strict'; delete foobar;";
+            ex = Assert.Throws<JavaScriptException>(() => engine.Evaluate(source2));
+            Assert.Equal("Delete of an unqualified identifier in strict mode.", ex.Message);
+        }
+
         private class Wrapper
         {
             public Testificate Test { get; set; }

+ 37 - 33
Jint/Engine.cs

@@ -44,7 +44,7 @@ namespace Jint
         internal readonly IObjectConverter[] _objectConverters;
         private readonly IConstraint[] _constraints;
         internal readonly bool _isDebugMode;
-        internal readonly bool _isStrict;
+        internal bool _isStrict;
         internal readonly IReferenceResolver _referenceResolver;
         internal readonly ReferencePool _referencePool;
         internal readonly ArgumentsInstancePool _argumentsInstancePool;
@@ -268,43 +268,41 @@ namespace Jint
         {
             Engine DoInvoke()
             {
-                using (new StrictModeScope(_isStrict || script.Strict))
-                {
-                    GlobalDeclarationInstantiation(
-                        script,
-                        Realm.GlobalEnv);
+                GlobalDeclarationInstantiation(
+                    script,
+                    Realm.GlobalEnv);
 
-                    var list = new JintStatementList(null, script.Body);
+                var list = new JintStatementList(null, script.Body);
 
-                    Completion result;
-                    try
-                    {
-                        result = list.Execute(_activeEvaluationContext);
-                    }
-                    catch
-                    {
-                        // unhandled exception
-                        ResetCallStack();
-                        throw;
-                    }
+                Completion result;
+                try
+                {
+                    result = list.Execute(_activeEvaluationContext);
+                }
+                catch
+                {
+                    // unhandled exception
+                    ResetCallStack();
+                    throw;
+                }
 
-                    if (result.Type == CompletionType.Throw)
-                    {
-                        var ex = new JavaScriptException(result.GetValueOrDefault()).SetCallstack(this, result.Location);
-                        ResetCallStack();
-                        throw ex;
-                    }
+                if (result.Type == CompletionType.Throw)
+                {
+                    var ex = new JavaScriptException(result.GetValueOrDefault()).SetCallstack(this, result.Location);
+                    ResetCallStack();
+                    throw ex;
+                }
 
-                    // TODO what about callstack and thrown exceptions?
-                    RunAvailableContinuations(_eventLoop);
+                // TODO what about callstack and thrown exceptions?
+                RunAvailableContinuations(_eventLoop);
 
-                    _completionValue = result.GetValueOrDefault();
+                _completionValue = result.GetValueOrDefault();
 
-                    return this;
-                }
+                return this;
             }
 
-            ExecuteWithConstraints(DoInvoke);
+            var strict = _isStrict | script.Strict;
+            ExecuteWithConstraints(strict, DoInvoke);
 
             return this;
         }
@@ -630,19 +628,24 @@ namespace Jint
                 return result;
             }
 
-            return ExecuteWithConstraints(DoInvoke);
+            return ExecuteWithConstraints(Options.Strict, DoInvoke);
         }
 
-        private T ExecuteWithConstraints<T>(Func<T> callback)
+        private T ExecuteWithConstraints<T>(bool strict, Func<T> callback)
         {
             ResetConstraints();
 
             var ownsContext = _activeEvaluationContext is null;
             _activeEvaluationContext ??= new EvaluationContext(this);
 
+            var oldStrict = _isStrict;
             try
             {
-                return callback();
+                _isStrict = strict;
+                using (new StrictModeScope(_isStrict))
+                {
+                    return callback();
+                }
             }
             finally
             {
@@ -650,6 +653,7 @@ namespace Jint
                 {
                     _activeEvaluationContext = null;
                 }
+                _isStrict = oldStrict;
                 ResetConstraints();
             }
         }

+ 3 - 4
Jint/Runtime/Interpreter/Expressions/JintUnaryExpression.cs

@@ -111,8 +111,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 }
 
                 case UnaryOperator.Delete:
-                    var r = _argument.Evaluate(context).Value as Reference;
-                    if (r == null)
+                    if (_argument.Evaluate(context).Value is not Reference r)
                     {
                         return JsBoolean.True;
                     }
@@ -121,7 +120,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                     {
                         if (r.IsStrictReference())
                         {
-                            ExceptionHelper.ThrowSyntaxError(engine.Realm);
+                            ExceptionHelper.ThrowSyntaxError(engine.Realm, "Delete of an unqualified identifier in strict mode.");
                         }
 
                         engine._referencePool.Return(r);
@@ -139,7 +138,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                         var deleteStatus = o.Delete(r.GetReferencedName());
                         if (!deleteStatus && r.IsStrictReference())
                         {
-                            ExceptionHelper.ThrowTypeError(engine.Realm);
+                            ExceptionHelper.ThrowTypeError(engine.Realm, $"Cannot delete property '{r.GetReferencedName()}' of {o}");
                         }
 
                         engine._referencePool.Return(r);