Jelajahi Sumber

[MERGE #3377 @suwc] Fix module namespace property attributes

Merge pull request #3377 from suwc:build/suwc/Issue3249

9.4.6.
   5. Return PropertyDescriptor{[[Value]]: value, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: false }.

26.3.1 @@toStringTag
The initial value of the @@toStringTag property is the String value "Module".
This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }.
Suwei Chen 8 tahun lalu
induk
melakukan
80939f7d3a

+ 79 - 1
lib/Runtime/Language/ModuleNamespace.cpp

@@ -50,7 +50,7 @@ namespace Js
         if (scriptContext->GetConfig()->IsES6ToStringTagEnabled())
         {
             DynamicObject::SetPropertyWithAttributes(PropertyIds::_symbolToStringTag, library->GetModuleTypeDisplayString(),
-                PropertyConfigurable, nullptr);
+                PropertyNone, nullptr);
         }
 
         ModuleImportOrExportEntryList* localExportList = sourceTextModuleRecord->GetLocalExportEntryList();
@@ -170,6 +170,84 @@ namespace Js
         return HasProperty(propertyId);
     }
 
+    BOOL ModuleNamespace::IsConfigurable(PropertyId propertyId)
+    {
+        SimpleDictionaryPropertyDescriptor<BigPropertyIndex> propertyDescriptor;
+        const Js::PropertyRecord* propertyRecord = GetScriptContext()->GetThreadContext()->GetPropertyName(propertyId);
+        if (propertyRecord->IsSymbol())
+        {
+            return this->DynamicObject::IsConfigurable(propertyId);
+        }
+
+        if (propertyMap != nullptr && propertyMap->TryGetValue(propertyRecord, &propertyDescriptor))
+        {
+            return !!(propertyDescriptor.Attributes & PropertyConfigurable);
+        }
+
+        if (unambiguousNonLocalExports != nullptr)
+        {
+            ModuleNameRecord moduleNameRecord;
+            if (unambiguousNonLocalExports->TryGetValue(propertyId, &moduleNameRecord))
+            {
+                return !!(PropertyModuleNamespaceDefault & PropertyConfigurable);
+            }
+        }
+
+        return DynamicObject::IsConfigurable(propertyId);
+    }
+
+    BOOL ModuleNamespace::IsEnumerable(PropertyId propertyId)
+    {
+        SimpleDictionaryPropertyDescriptor<BigPropertyIndex> propertyDescriptor;
+        const Js::PropertyRecord* propertyRecord = GetScriptContext()->GetThreadContext()->GetPropertyName(propertyId);
+        if (propertyRecord->IsSymbol())
+        {
+            return this->DynamicObject::IsEnumerable(propertyId);
+        }
+
+        if (propertyMap != nullptr && propertyMap->TryGetValue(propertyRecord, &propertyDescriptor))
+        {
+            return !!(propertyDescriptor.Attributes & PropertyEnumerable);
+        }
+
+        if (unambiguousNonLocalExports != nullptr)
+        {
+            ModuleNameRecord moduleNameRecord;
+            if (unambiguousNonLocalExports->TryGetValue(propertyId, &moduleNameRecord))
+            {
+                return !!(PropertyModuleNamespaceDefault & PropertyEnumerable);
+            }
+        }
+
+        return DynamicObject::IsEnumerable(propertyId);
+    }
+
+    BOOL ModuleNamespace::IsWritable(PropertyId propertyId)
+    {
+        SimpleDictionaryPropertyDescriptor<BigPropertyIndex> propertyDescriptor;
+        const Js::PropertyRecord* propertyRecord = GetScriptContext()->GetThreadContext()->GetPropertyName(propertyId);
+        if (propertyRecord->IsSymbol())
+        {
+            return this->DynamicObject::IsWritable(propertyId);
+        }
+
+        if (propertyMap != nullptr && propertyMap->TryGetValue(propertyRecord, &propertyDescriptor))
+        {
+            return !!(propertyDescriptor.Attributes & PropertyWritable);
+        }
+
+        if (unambiguousNonLocalExports != nullptr)
+        {
+            ModuleNameRecord moduleNameRecord;
+            if (unambiguousNonLocalExports->TryGetValue(propertyId, &moduleNameRecord))
+            {
+                return !!(PropertyModuleNamespaceDefault & PropertyWritable);
+            }
+        }
+
+        return DynamicObject::IsWritable(propertyId);
+    }
+
     PropertyQueryFlags ModuleNamespace::GetPropertyQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
     {
         SimpleDictionaryPropertyDescriptor<BigPropertyIndex> propertyDescriptor;

+ 3 - 3
lib/Runtime/Language/ModuleNamespace.h

@@ -60,9 +60,9 @@ namespace Js
         virtual BOOL GetEnumerator(JavascriptStaticEnumerator * enumerator, EnumeratorFlags flags, ScriptContext* requestContext, ForInCache * forInCache = nullptr);
         virtual BOOL SetAccessors(PropertyId propertyId, Var getter, Var setter, PropertyOperationFlags flags = PropertyOperation_None) override { return false; }
         virtual BOOL GetAccessors(PropertyId propertyId, Var *getter, Var *setter, ScriptContext * requestContext) override { return false; }
-        virtual BOOL IsWritable(PropertyId propertyId) override { return false; }
-        virtual BOOL IsConfigurable(PropertyId propertyId) override { return false; }
-        virtual BOOL IsEnumerable(PropertyId propertyId) override { return false; }
+        virtual BOOL IsWritable(PropertyId propertyId) override;
+        virtual BOOL IsConfigurable(PropertyId propertyId) override;
+        virtual BOOL IsEnumerable(PropertyId propertyId) override;
         virtual BOOL SetEnumerable(PropertyId propertyId, BOOL value) override { return false; }
         virtual BOOL SetWritable(PropertyId propertyId, BOOL value) override { return false; }
         virtual BOOL IsProtoImmutable() const { return true; }

+ 62 - 7
test/es6/module-namespace.js

@@ -32,14 +32,70 @@ function testModuleScript(source, message, shouldFail = false) {
 
 var tests = [
     {
-        name: "Issue3249: Namespace object's Symbol.toStringTag",
+        name: "Issue3249: Namespace object's property attributes",
         body: function () {
 			testModuleScript(`
+            function verifyPropertyDesc(obj, prop, desc, propName) {
+                var actualDesc = Object.getOwnPropertyDescriptor(obj, prop);
+                if (typeof propName === "undefined") { propName = prop; }
+                assert.areEqual(desc.configurable, actualDesc.configurable, propName+"'s attribute: configurable");
+                assert.areEqual(desc.enumerable, actualDesc.enumerable, propName+"'s attribute: enumerable");
+                assert.areEqual(desc.writable, actualDesc.writable, propName+"'s attribute: writable");
+            }
+
 			import * as foo from "ValidExportStatements.js";
-            var desc = Object.getOwnPropertyDescriptor(foo, Symbol.toStringTag);
-            assert.isFalse(desc.configurable, "configurable: false");
-            assert.isFalse(desc.enumerable, "enumerable: false");
-            assert.isFalse(desc.writable, "writable: false");
+            assert.areEqual("Module", foo[Symbol.toStringTag], "@@toStringTag is the String value'Module'");
+            verifyPropertyDesc(foo, Symbol.toStringTag, {configurable:false, enumerable: false, writable: false}, "Symbol.toStringTag");
+            verifyPropertyDesc(foo, "default", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo, "var7", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo, "var6", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo, "var4", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo, "var3", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo, "var2", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo, "var1", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo, "foo4", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo, "bar2", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo, "foobar", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo, "foo3", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo, "baz2", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo, "foo2", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo, "baz", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo, "bar", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo, "foo", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo, "const6", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo, "const5", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo, "const4", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo, "const3", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo, "const2", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo, "let7", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo, "let6", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo, "let5", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo, "let4", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo, "let2", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo, "let1", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo, "cl2", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo, "cl1", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo, "gn2", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo, "gn1", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo, "fn2", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo, "fn1", {configurable:false, enumerable: true, writable: true});
+
+            import * as foo1 from "ValidReExportStatements.js";
+            verifyPropertyDesc(foo1, "foo", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo1, "bar", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo1, "baz", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo1, "foo2", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo1, "bar2", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo1, "foo3", {configurable:false, enumerable: true, writable: true});
+
+            import * as foo2 from "ModuleComplexReexports.js";
+            verifyPropertyDesc(foo2, "ModuleComplexReexports_foo", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo2, "bar2", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo2, "localfoo2", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo2, "bar", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo2, "localfoo", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo2, "baz", {configurable:false, enumerable: true, writable: true});
+            verifyPropertyDesc(foo2, "foo", {configurable:false, enumerable: true, writable: true});
 		    `, '', false);
         }
     },
@@ -135,9 +191,8 @@ var tests = [
     {
         name: "reexport only",
         body: function () {
-            //testModuleScript('import * as foo from "ValidReExportStatements.js"; for (var i in foo) helpers.writeln(i + "=" + foo[i]);', '', false);
             testModuleScript(`
-			import * as foo from "ValidReExportStatements.js";
+            import * as foo from "ValidReExportStatements.js";
 			assert.areEqual("function foo() { }", foo.foo.toString(), "foo");
 			assert.areEqual("class bar { }", foo.bar.toString(), "bar");
 			assert.areEqual("function* baz() { }", foo.baz.toString(), "baz");