1/*
2 * Copyright (C) 2015-2019 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 "AbstractModuleRecord.h"
30#include "Error.h"
31#include "JSCInlines.h"
32#include "JSModuleEnvironment.h"
33
34namespace JSC {
35
36const ClassInfo JSModuleNamespaceObject::s_info = { "ModuleNamespaceObject", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSModuleNamespaceObject) };
37
38JSModuleNamespaceObject::JSModuleNamespaceObject(VM& vm, Structure* structure)
39 : Base(vm, structure)
40 , m_exports()
41{
42}
43
44void JSModuleNamespaceObject::finishCreation(JSGlobalObject* globalObject, AbstractModuleRecord* moduleRecord, Vector<std::pair<Identifier, AbstractModuleRecord::Resolution>>&& resolutions)
45{
46 VM& vm = globalObject->vm();
47 auto scope = DECLARE_THROW_SCOPE(vm);
48 Base::finishCreation(vm);
49 ASSERT(inherits(vm, info()));
50
51 // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects
52 // Quoted from the spec:
53 // A List containing the String values of the exported names exposed as own properties of this object.
54 // The list is ordered as if an Array of those String values had been sorted using Array.prototype.sort using SortCompare as comparefn.
55 //
56 // Sort the exported names by the code point order.
57 std::sort(resolutions.begin(), resolutions.end(), [] (const auto& lhs, const auto& rhs) {
58 return codePointCompare(lhs.first.impl(), rhs.first.impl()) < 0;
59 });
60
61 m_moduleRecord.set(vm, this, moduleRecord);
62 {
63 unsigned moduleRecordOffset = 0;
64 m_names.reserveCapacity(resolutions.size());
65 for (const auto& pair : resolutions) {
66 moduleRecordAt(moduleRecordOffset).set(vm, this, pair.second.moduleRecord);
67 m_names.append(pair.first);
68 m_exports.add(pair.first.impl(), ExportEntry {
69 pair.second.localName,
70 moduleRecordOffset
71 });
72 ++moduleRecordOffset;
73 }
74 }
75
76 putDirect(vm, vm.propertyNames->toStringTagSymbol, jsString(vm, "Module"), PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
77
78 // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects-getprototypeof
79 // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects-setprototypeof-v
80 // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects-isextensible
81 // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects-preventextensions
82 methodTable(vm)->preventExtensions(this, globalObject);
83 scope.assertNoException();
84}
85
86void JSModuleNamespaceObject::destroy(JSCell* cell)
87{
88 JSModuleNamespaceObject* thisObject = static_cast<JSModuleNamespaceObject*>(cell);
89 thisObject->JSModuleNamespaceObject::~JSModuleNamespaceObject();
90}
91
92void JSModuleNamespaceObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
93{
94 JSModuleNamespaceObject* thisObject = jsCast<JSModuleNamespaceObject*>(cell);
95 ASSERT_GC_OBJECT_INHERITS(thisObject, info());
96 Base::visitChildren(thisObject, visitor);
97 visitor.append(thisObject->m_moduleRecord);
98 for (unsigned i = 0; i < thisObject->m_names.size(); ++i)
99 visitor.appendHidden(thisObject->moduleRecordAt(i));
100}
101
102static JSValue getValue(JSModuleEnvironment* environment, PropertyName localName, ScopeOffset& scopeOffset)
103{
104 SymbolTable* symbolTable = environment->symbolTable();
105 {
106 ConcurrentJSLocker locker(symbolTable->m_lock);
107 auto iter = symbolTable->find(locker, localName.uid());
108 ASSERT(iter != symbolTable->end(locker));
109 SymbolTableEntry& entry = iter->value;
110 ASSERT(!entry.isNull());
111 scopeOffset = entry.scopeOffset();
112 }
113 return environment->variableAt(scopeOffset).get();
114}
115
116bool JSModuleNamespaceObject::getOwnPropertySlotCommon(JSGlobalObject* globalObject, PropertyName propertyName, PropertySlot& slot)
117{
118 VM& vm = globalObject->vm();
119 auto scope = DECLARE_THROW_SCOPE(vm);
120
121 // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects-getownproperty-p
122
123 // step 1.
124 // If the property name is a symbol, we don't look into the imported bindings.
125 // It may return the descriptor with writable: true, but namespace objects does not allow it in [[Set]] / [[DefineOwnProperty]] side.
126 if (propertyName.isSymbol())
127 return JSObject::getOwnPropertySlot(this, globalObject, propertyName, slot);
128
129 slot.setIsTaintedByOpaqueObject();
130
131 auto iterator = m_exports.find(propertyName.uid());
132 if (iterator == m_exports.end())
133 return false;
134 ExportEntry& exportEntry = iterator->value;
135
136 switch (slot.internalMethodType()) {
137 case PropertySlot::InternalMethodType::GetOwnProperty:
138 case PropertySlot::InternalMethodType::Get: {
139 JSModuleEnvironment* environment = moduleRecordAt(exportEntry.moduleRecordOffset)->moduleEnvironment();
140 ScopeOffset scopeOffset;
141 JSValue value = getValue(environment, exportEntry.localName, scopeOffset);
142 // If the value is filled with TDZ value, throw a reference error.
143 if (!value) {
144 throwVMError(globalObject, scope, createTDZError(globalObject));
145 return false;
146 }
147
148 slot.setValueModuleNamespace(this, static_cast<unsigned>(PropertyAttribute::DontDelete), value, environment, scopeOffset);
149 return true;
150 }
151
152 case PropertySlot::InternalMethodType::HasProperty: {
153 // Do not perform [[Get]] for [[HasProperty]].
154 // [[Get]] / [[GetOwnProperty]] onto namespace object could throw an error while [[HasProperty]] just returns true here.
155 // https://tc39.github.io/ecma262/#sec-module-namespace-exotic-objects-hasproperty-p
156 slot.setValue(this, static_cast<unsigned>(PropertyAttribute::DontDelete), jsUndefined());
157 return true;
158 }
159
160 case PropertySlot::InternalMethodType::VMInquiry:
161 return false;
162 }
163
164 RELEASE_ASSERT_NOT_REACHED();
165 return false;
166}
167
168bool JSModuleNamespaceObject::getOwnPropertySlot(JSObject* cell, JSGlobalObject* globalObject, PropertyName propertyName, PropertySlot& slot)
169{
170 JSModuleNamespaceObject* thisObject = jsCast<JSModuleNamespaceObject*>(cell);
171 return thisObject->getOwnPropertySlotCommon(globalObject, propertyName, slot);
172}
173
174bool JSModuleNamespaceObject::getOwnPropertySlotByIndex(JSObject* cell, JSGlobalObject* globalObject, unsigned propertyName, PropertySlot& slot)
175{
176 VM& vm = globalObject->vm();
177 JSModuleNamespaceObject* thisObject = jsCast<JSModuleNamespaceObject*>(cell);
178 return thisObject->getOwnPropertySlotCommon(globalObject, Identifier::from(vm, propertyName), slot);
179}
180
181bool JSModuleNamespaceObject::put(JSCell*, JSGlobalObject* globalObject, PropertyName, JSValue, PutPropertySlot& slot)
182{
183 VM& vm = globalObject->vm();
184 auto scope = DECLARE_THROW_SCOPE(vm);
185
186 // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects-set-p-v-receiver
187 if (slot.isStrictMode())
188 throwTypeError(globalObject, scope, ReadonlyPropertyWriteError);
189 return false;
190}
191
192bool JSModuleNamespaceObject::putByIndex(JSCell*, JSGlobalObject* globalObject, unsigned, JSValue, bool shouldThrow)
193{
194 VM& vm = globalObject->vm();
195 auto scope = DECLARE_THROW_SCOPE(vm);
196
197 if (shouldThrow)
198 throwTypeError(globalObject, scope, ReadonlyPropertyWriteError);
199 return false;
200}
201
202bool JSModuleNamespaceObject::deleteProperty(JSCell* cell, JSGlobalObject* globalObject, PropertyName propertyName)
203{
204 // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects-delete-p
205 JSModuleNamespaceObject* thisObject = jsCast<JSModuleNamespaceObject*>(cell);
206 if (propertyName.isSymbol())
207 return JSObject::deleteProperty(thisObject, globalObject, propertyName);
208
209 return !thisObject->m_exports.contains(propertyName.uid());
210}
211
212void JSModuleNamespaceObject::getOwnPropertyNames(JSObject* cell, JSGlobalObject* globalObject, PropertyNameArray& propertyNames, EnumerationMode mode)
213{
214 // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects-ownpropertykeys
215 JSModuleNamespaceObject* thisObject = jsCast<JSModuleNamespaceObject*>(cell);
216 for (const auto& name : thisObject->m_names)
217 propertyNames.add(name.impl());
218 return JSObject::getOwnPropertyNames(thisObject, globalObject, propertyNames, mode);
219}
220
221bool JSModuleNamespaceObject::defineOwnProperty(JSObject*, JSGlobalObject* globalObject, PropertyName, const PropertyDescriptor&, bool shouldThrow)
222{
223 VM& vm = globalObject->vm();
224 auto scope = DECLARE_THROW_SCOPE(vm);
225
226 // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects-defineownproperty-p-desc
227 if (shouldThrow)
228 throwTypeError(globalObject, scope, NonExtensibleObjectPropertyDefineError);
229 return false;
230}
231
232} // namespace JSC
233