Bläddra i källkod

Improve class constructor error reporting (#1169)

Marko Lahma 3 år sedan
förälder
incheckning
d4621d6760

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

@@ -2852,6 +2852,16 @@ x.test = {
             Assert.Equal("Delete of an unqualified identifier in strict mode.", ex.Message);
         }
 
+        [Fact]
+        public void ShouldSupportThisInSubclass()
+        {
+            var engine = new Engine();
+            var script = "class MyClass1 { } class MyClass2 extends MyClass1 { constructor() { } } const x = new MyClass2();";
+
+            var ex = Assert.Throws<JavaScriptException>(() => engine.Evaluate(script));
+            Assert.Equal("Must call super constructor in derived class before accessing 'this' or returning from derived constructor", ex.Message);
+        }
+        
         private class Wrapper
         {
             public Testificate Test { get; set; }

+ 1 - 1
Jint/Native/Function/ScriptFunctionInstance.cs

@@ -11,7 +11,7 @@ namespace Jint.Native.Function
 {
     public sealed class ScriptFunctionInstance : FunctionInstance, IConstructor
     {
-        private bool _isClassConstructor;
+        internal bool _isClassConstructor;
 
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-13.2

+ 1 - 1
Jint/Runtime/Environments/DeclarativeEnvironmentRecord.cs

@@ -79,7 +79,7 @@ namespace Jint.Runtime.Environments
             {
                 if (strict)
                 {
-                    ExceptionHelper.ThrowReferenceError(_engine.Realm, key);
+                    ExceptionHelper.ThrowReferenceNameError(_engine.Realm, key);
                 }
                 CreateMutableBindingAndInitialize(key, canBeDeleted: true, value);
                 return;

+ 10 - 3
Jint/Runtime/Environments/FunctionEnvironmentRecord.cs

@@ -54,7 +54,7 @@ namespace Jint.Runtime.Environments
         {
             if (_thisBindingStatus == ThisBindingStatus.Initialized)
             {
-                ExceptionHelper.ThrowReferenceError(_functionObject._realm, (string) null);
+                ExceptionHelper.ThrowReferenceError(_functionObject._realm, "'this' has already been bound");
             }
             _thisValue = value;
             _thisBindingStatus = ThisBindingStatus.Initialized;
@@ -68,7 +68,14 @@ namespace Jint.Runtime.Environments
                 return _thisValue;
             }
 
-            ExceptionHelper.ThrowReferenceError(_engine.ExecutionContext.Realm, (string) null);
+            var message = "Cannot access uninitialized 'this'";
+            if (NewTarget is ScriptFunctionInstance { _isClassConstructor: true, _constructorKind: ConstructorKind.Derived })
+            {
+                // help with better error message
+                message = "Must call super constructor in derived class before accessing 'this' or returning from derived constructor";
+            }
+
+            ExceptionHelper.ThrowReferenceError(_engine.ExecutionContext.Realm, message);
             return null;
         }
 
@@ -334,7 +341,7 @@ namespace Jint.Runtime.Environments
                 && right is Identifier idRight
                 && idLeft.Name == idRight.Name)
             {
-                ExceptionHelper.ThrowReferenceError(_functionObject._realm, idRight.Name);
+                ExceptionHelper.ThrowReferenceNameError(_functionObject._realm, idRight.Name);
             }
 
             if (argument.IsUndefined())

+ 2 - 2
Jint/Runtime/Environments/GlobalEnvironmentRecord.cs

@@ -134,7 +134,7 @@ namespace Jint.Runtime.Environments
                 var property = JsString.Create(name);
                 if (strict && !_global.HasProperty(property))
                 {
-                    ExceptionHelper.ThrowReferenceError(_engine.Realm, name);
+                    ExceptionHelper.ThrowReferenceNameError(_engine.Realm, name);
                 }
 
                 _global.Set(property, value);
@@ -318,4 +318,4 @@ namespace Jint.Runtime.Environments
             return ReferenceEquals(_objectRecord, other);
         }
     }
-}
+}

+ 2 - 2
Jint/Runtime/Environments/ObjectEnvironmentRecord.cs

@@ -140,7 +140,7 @@ namespace Jint.Runtime.Environments
         {
             if (strict && !_bindingObject.HasProperty(name.StringValue))
             {
-                ExceptionHelper.ThrowReferenceError(_engine.Realm, name.Key);
+                ExceptionHelper.ThrowReferenceNameError(_engine.Realm, name.Key);
             }
                 
             _bindingObject.Set(name.StringValue, value);
@@ -151,7 +151,7 @@ namespace Jint.Runtime.Environments
             var desc = _bindingObject.GetProperty(name);
             if (strict && desc == PropertyDescriptor.Undefined)
             {
-                ExceptionHelper.ThrowReferenceError(_engine.Realm, name);
+                ExceptionHelper.ThrowReferenceNameError(_engine.Realm, name);
             }
 
             return ObjectInstance.UnwrapJsValue(desc, _bindingObject);

+ 8 - 2
Jint/Runtime/ExceptionHelper.cs

@@ -39,13 +39,19 @@ namespace Jint.Runtime
         [DoesNotReturn]
         public static void ThrowReferenceError(Realm realm, Reference reference)
         {
-            ThrowReferenceError(realm, reference?.GetReferencedName()?.ToString());
+            ThrowReferenceNameError(realm, reference?.GetReferencedName()?.ToString());
         }
 
         [DoesNotReturn]
-        public static void ThrowReferenceError(Realm realm, string name)
+        public static void ThrowReferenceNameError(Realm realm, string name)
         {
             var message = name != null ? name + " is not defined" : null;
+            ThrowReferenceError(realm, message);
+        }
+
+        [DoesNotReturn]
+        public static void ThrowReferenceError(Realm realm, string message)
+        {
             throw new JavaScriptException(realm.Intrinsics.ReferenceError, message);
         }
 

+ 2 - 2
Jint/Runtime/Modules/ModuleNamespace.cs

@@ -169,7 +169,7 @@ internal sealed class ModuleNamespace : ObjectInstance
         var targetEnv = targetModule._environment;
         if (targetEnv is null)
         {
-            ExceptionHelper.ThrowReferenceError(_engine.Realm, "environment");
+            ExceptionHelper.ThrowReferenceError(_engine.Realm, "Module's environment hasn't been initialized");
         }
 
         return targetEnv.GetBindingValue(binding.BindingName, true);
@@ -217,4 +217,4 @@ internal sealed class ModuleNamespace : ObjectInstance
 
         return result;
     }
-}
+}