1/*
2 * Copyright (C) 2015-2017 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(ExecState* exec, JSGlobalObject*, AbstractModuleRecord* moduleRecord, Vector<std::pair<Identifier, AbstractModuleRecord::Resolution>>&& resolutions)
45{
46 VM& vm = exec->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, exec);
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(ExecState* exec, PropertyName propertyName, PropertySlot& slot)
117{
118 VM& vm = exec->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, exec, 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(exec, scope, createTDZError(exec));
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, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
169{
170 JSModuleNamespaceObject* thisObject = jsCast<JSModuleNamespaceObject*>(cell);
171 return thisObject->getOwnPropertySlotCommon(exec, propertyName, slot);
172}
173
174bool JSModuleNamespaceObject::getOwnPropertySlotByIndex(JSObject* cell, ExecState* exec, unsigned propertyName, PropertySlot& slot)
175{
176 JSModuleNamespaceObject* thisObject = jsCast<JSModuleNamespaceObject*>(cell);
177 return thisObject->getOwnPropertySlotCommon(exec, Identifier::from(exec, propertyName), slot);
178}
179
180bool JSModuleNamespaceObject::put(JSCell*, ExecState* exec, PropertyName, JSValue, PutPropertySlot& slot)
181{
182 VM& vm = exec->vm();
183 auto scope = DECLARE_THROW_SCOPE(vm);
184
185 // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects-set-p-v-receiver
186 if (slot.isStrictMode())
187 throwTypeError(exec, scope, ReadonlyPropertyWriteError);
188 return false;
189}
190
191bool JSModuleNamespaceObject::putByIndex(JSCell*, ExecState* exec, unsigned, JSValue, bool shouldThrow)
192{
193 VM& vm = exec->vm();
194 auto scope = DECLARE_THROW_SCOPE(vm);
195
196 if (shouldThrow)
197 throwTypeError(exec, scope, ReadonlyPropertyWriteError);
198 return false;
199}
200
201bool JSModuleNamespaceObject::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
202{
203 // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects-delete-p
204 JSModuleNamespaceObject* thisObject = jsCast<JSModuleNamespaceObject*>(cell);
205 if (propertyName.isSymbol())
206 return JSObject::deleteProperty(thisObject, exec, propertyName);
207
208 return !thisObject->m_exports.contains(propertyName.uid());
209}
210
211void JSModuleNamespaceObject::getOwnPropertyNames(JSObject* cell, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
212{
213 // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects-ownpropertykeys
214 JSModuleNamespaceObject* thisObject = jsCast<JSModuleNamespaceObject*>(cell);
215 for (const auto& name : thisObject->m_names)
216 propertyNames.add(name.impl());
217 return JSObject::getOwnPropertyNames(thisObject, exec, propertyNames, mode);
218}
219
220bool JSModuleNamespaceObject::defineOwnProperty(JSObject*, ExecState* exec, PropertyName, const PropertyDescriptor&, bool shouldThrow)
221{
222 VM& vm = exec->vm();
223 auto scope = DECLARE_THROW_SCOPE(vm);
224
225 // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects-defineownproperty-p-desc
226 if (shouldThrow)
227 throwTypeError(exec, scope, NonExtensibleObjectPropertyDefineError);
228 return false;
229}
230
231} // namespace JSC
232