1d487e30fe056be190c918c15fcbd2c4af3981d0
[WebKit-https.git] / Source / JavaScriptCore / runtime / JSModuleNamespaceObject.cpp
1 /*
2  * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "JSModuleNamespaceObject.h"
28
29 #include "Error.h"
30 #include "JSCInlines.h"
31 #include "JSModuleEnvironment.h"
32 #include "JSModuleRecord.h"
33 #include "JSPropertyNameIterator.h"
34
35 namespace JSC {
36
37 static EncodedJSValue JSC_HOST_CALL moduleNamespaceObjectSymbolIterator(ExecState*);
38
39 const ClassInfo JSModuleNamespaceObject::s_info = { "ModuleNamespaceObject", &Base::s_info, nullptr, CREATE_METHOD_TABLE(JSModuleNamespaceObject) };
40
41
42 JSModuleNamespaceObject::JSModuleNamespaceObject(VM& vm, Structure* structure)
43     : Base(vm, structure)
44     , m_exports()
45 {
46 }
47
48 void JSModuleNamespaceObject::finishCreation(ExecState* exec, JSGlobalObject* globalObject, JSModuleRecord* moduleRecord, const IdentifierSet& exports)
49 {
50     VM& vm = exec->vm();
51     auto scope = DECLARE_THROW_SCOPE(vm);
52     Base::finishCreation(vm);
53     ASSERT(inherits(info()));
54
55     // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects
56     // Quoted from the spec:
57     //     A List containing the String values of the exported names exposed as own properties of this object.
58     //     The list is ordered as if an Array of those String values had been sorted using Array.prototype.sort using SortCompare as comparefn.
59     //
60     // Sort the exported names by the code point order.
61     Vector<UniquedStringImpl*> temporaryVector(exports.size(), nullptr);
62     std::transform(exports.begin(), exports.end(), temporaryVector.begin(), [](const RefPtr<WTF::UniquedStringImpl>& ref) {
63         return ref.get();
64     });
65     std::sort(temporaryVector.begin(), temporaryVector.end(), [] (UniquedStringImpl* lhs, UniquedStringImpl* rhs) {
66         return codePointCompare(lhs, rhs) < 0;
67     });
68     for (auto* identifier : temporaryVector)
69         m_exports.add(identifier);
70
71     m_moduleRecord.set(vm, this, moduleRecord);
72     JSFunction* iteratorFunction = JSFunction::create(vm, globalObject, 0, ASCIILiteral("[Symbol.iterator]"), moduleNamespaceObjectSymbolIterator, NoIntrinsic);
73     putDirect(vm, vm.propertyNames->iteratorSymbol, iteratorFunction, DontEnum);
74     putDirect(vm, vm.propertyNames->toStringTagSymbol, jsString(&vm, "Module"), DontEnum | ReadOnly);
75
76     // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects-getprototypeof
77     // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects-setprototypeof-v
78     // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects-isextensible
79     // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects-preventextensions
80     methodTable(vm)->preventExtensions(this, exec);
81     ASSERT_UNUSED(scope, !scope.exception());
82 }
83
84 void JSModuleNamespaceObject::destroy(JSCell* cell)
85 {
86     JSModuleNamespaceObject* thisObject = jsCast<JSModuleNamespaceObject*>(cell);
87     thisObject->JSModuleNamespaceObject::~JSModuleNamespaceObject();
88 }
89
90 void JSModuleNamespaceObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
91 {
92     JSModuleNamespaceObject* thisObject = jsCast<JSModuleNamespaceObject*>(cell);
93     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
94     Base::visitChildren(thisObject, visitor);
95     visitor.append(&thisObject->m_moduleRecord);
96 }
97
98 bool JSModuleNamespaceObject::getOwnPropertySlot(JSObject* cell, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
99 {
100     VM& vm = exec->vm();
101     auto scope = DECLARE_THROW_SCOPE(vm);
102
103     // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects-getownproperty-p
104
105     JSModuleNamespaceObject* thisObject = jsCast<JSModuleNamespaceObject*>(cell);
106
107     // step 1.
108     // If the property name is a symbol, we don't look into the imported bindings.
109     // It may return the descriptor with writable: true, but namespace objects does not allow it in [[Set]] / [[DefineOwnProperty]] side.
110     if (propertyName.isSymbol())
111         return JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot);
112
113     // FIXME: Add IC for module namespace object.
114     // https://bugs.webkit.org/show_bug.cgi?id=160590
115     slot.disableCaching();
116     slot.setIsTaintedByOpaqueObject();
117     if (!thisObject->m_exports.contains(propertyName.uid()))
118         return false;
119
120     switch (slot.internalMethodType()) {
121     case PropertySlot::InternalMethodType::Get:
122     case PropertySlot::InternalMethodType::GetOwnProperty: {
123         JSModuleRecord* moduleRecord = thisObject->moduleRecord();
124
125         JSModuleRecord::Resolution resolution = moduleRecord->resolveExport(exec, Identifier::fromUid(exec, propertyName.uid()));
126         ASSERT(resolution.type != JSModuleRecord::Resolution::Type::NotFound && resolution.type != JSModuleRecord::Resolution::Type::Ambiguous);
127
128         JSModuleRecord* targetModule = resolution.moduleRecord;
129         JSModuleEnvironment* targetEnvironment = targetModule->moduleEnvironment();
130
131         PropertySlot trampolineSlot(targetEnvironment, PropertySlot::InternalMethodType::Get);
132         bool found = targetEnvironment->methodTable(vm)->getOwnPropertySlot(targetEnvironment, exec, resolution.localName, trampolineSlot);
133         ASSERT_UNUSED(found, found);
134
135         JSValue value = trampolineSlot.getValue(exec, propertyName);
136         ASSERT(!scope.exception());
137
138         // If the value is filled with TDZ value, throw a reference error.
139         if (!value) {
140             throwVMError(exec, scope, createTDZError(exec));
141             return false;
142         }
143
144         slot.setValue(thisObject, DontDelete, value);
145         return true;
146     }
147     case PropertySlot::InternalMethodType::HasProperty: {
148         // Do not perform [[Get]] for [[HasProperty]].
149         // [[Get]] / [[GetOwnProperty]] onto namespace object could throw an error while [[HasProperty]] just returns true here.
150         // https://tc39.github.io/ecma262/#sec-module-namespace-exotic-objects-hasproperty-p
151         slot.setValue(thisObject, DontDelete, jsUndefined());
152         return true;
153     }
154
155     case PropertySlot::InternalMethodType::VMInquiry:
156         return false;
157     }
158
159     RELEASE_ASSERT_NOT_REACHED();
160     return false;
161 }
162
163 bool JSModuleNamespaceObject::put(JSCell*, ExecState* exec, PropertyName, JSValue, PutPropertySlot& slot)
164 {
165     VM& vm = exec->vm();
166     auto scope = DECLARE_THROW_SCOPE(vm);
167
168     // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects-set-p-v-receiver
169     if (slot.isStrictMode())
170         throwTypeError(exec, scope, ASCIILiteral(ReadonlyPropertyWriteError));
171     return false;
172 }
173
174 bool JSModuleNamespaceObject::putByIndex(JSCell*, ExecState* exec, unsigned, JSValue, bool shouldThrow)
175 {
176     VM& vm = exec->vm();
177     auto scope = DECLARE_THROW_SCOPE(vm);
178
179     if (shouldThrow)
180         throwTypeError(exec, scope, ASCIILiteral(ReadonlyPropertyWriteError));
181     return false;
182 }
183
184 bool JSModuleNamespaceObject::deleteProperty(JSCell* cell, ExecState*, PropertyName propertyName)
185 {
186     // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects-delete-p
187     JSModuleNamespaceObject* thisObject = jsCast<JSModuleNamespaceObject*>(cell);
188     return !thisObject->m_exports.contains(propertyName.uid());
189 }
190
191 void JSModuleNamespaceObject::getOwnPropertyNames(JSObject* cell, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
192 {
193     // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects-ownpropertykeys
194     JSModuleNamespaceObject* thisObject = jsCast<JSModuleNamespaceObject*>(cell);
195     for (const auto& name : thisObject->m_exports)
196         propertyNames.add(name.get());
197     return JSObject::getOwnPropertyNames(thisObject, exec, propertyNames, mode);
198 }
199
200 bool JSModuleNamespaceObject::defineOwnProperty(JSObject*, ExecState* exec, PropertyName, const PropertyDescriptor&, bool shouldThrow)
201 {
202     VM& vm = exec->vm();
203     auto scope = DECLARE_THROW_SCOPE(vm);
204
205     // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects-defineownproperty-p-desc
206     if (shouldThrow)
207         throwTypeError(exec, scope, ASCIILiteral("Attempting to define property on object that is not extensible."));
208     return false;
209 }
210
211 EncodedJSValue JSC_HOST_CALL moduleNamespaceObjectSymbolIterator(ExecState* exec)
212 {
213     VM& vm = exec->vm();
214     auto scope = DECLARE_THROW_SCOPE(vm);
215
216     JSModuleNamespaceObject* object = jsDynamicCast<JSModuleNamespaceObject*>(exec->thisValue());
217     if (!object)
218         return throwVMTypeError(exec, scope, ASCIILiteral("|this| should be a module namespace object"));
219     return JSValue::encode(JSPropertyNameIterator::create(exec, exec->lexicalGlobalObject()->propertyNameIteratorStructure(), object));
220 }
221
222 } // namespace JSC