1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "ecmascript/module/js_module_namespace.h"
17
18 #include "ecmascript/global_env.h"
19 #include "ecmascript/js_array.h"
20 #include "ecmascript/module/js_module_record.h"
21 #include "ecmascript/module/js_module_source_text.h"
22 #include "ecmascript/base/string_helper.h"
23
24 namespace panda::ecmascript {
ModuleNamespaceCreate(JSThread * thread,const JSHandle<JSTaggedValue> & module,const JSHandle<TaggedArray> & exports)25 JSHandle<ModuleNamespace> ModuleNamespace::ModuleNamespaceCreate(JSThread *thread,
26 const JSHandle<JSTaggedValue> &module,
27 const JSHandle<TaggedArray> &exports)
28 {
29 auto globalConst = thread->GlobalConstants();
30 // 1. Assert: module is a Module Record.
31 ASSERT(module->IsModuleRecord());
32 // 2. Assert: module.[[Namespace]] is undefined.
33 JSHandle<ModuleRecord> moduleRecord = JSHandle<ModuleRecord>::Cast(module);
34 ASSERT(ModuleRecord::GetNamespace(moduleRecord.GetTaggedValue()).IsUndefined());
35 // 3. Assert: exports is a List of String values.
36 // 4. Let M be a newly created object.
37 // 5. Set M's essential internal methods to the definitions specified in 9.4.6.
38 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
39 JSHandle<ModuleNamespace> mNp = factory->NewModuleNamespace();
40 // 6. Set M.[[Module]] to module.
41 mNp->SetModule(thread, module);
42 // 7. Let sortedExports be a new List containing the same values as the list exports where the values
43 // are ordered as if an Array of the same values had been sorted using
44 // Array.prototype.sort using undefined as comparefn.
45 JSHandle<JSArray> exportsArray = JSArray::CreateArrayFromList(thread, exports);
46 JSHandle<JSObject> sortedExports = JSHandle<JSObject>::Cast(exportsArray);
47 JSHandle<JSTaggedValue> fn = globalConst->GetHandledUndefined();
48 JSArray::Sort(thread, sortedExports, fn);
49 // 8. Set M.[[Exports]] to sortedExports.
50 mNp->SetExports(thread, sortedExports);
51 // 9. Create own properties of M corresponding to the definitions in 26.3.
52
53 JSHandle<JSTaggedValue> toStringTag = thread->GetEcmaVM()->GetGlobalEnv()->GetToStringTagSymbol();
54 JSHandle<JSTaggedValue> moduleString = globalConst->GetHandledModuleString();
55 PropertyDescriptor des(thread, moduleString, false, false, false);
56 JSHandle<JSObject> mNpObj = JSHandle<JSObject>::Cast(mNp);
57 JSObject::DefineOwnProperty(thread, mNpObj, toStringTag, des);
58 // 10. Set module.[[Namespace]] to M.
59 ModuleRecord::SetNamespace(thread, moduleRecord.GetTaggedValue(), mNp.GetTaggedValue());
60 mNp->GetJSHClass()->SetExtensible(false);
61 return mNp;
62 }
63
GetProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key)64 OperationResult ModuleNamespace::GetProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
65 const JSHandle<JSTaggedValue> &key)
66 {
67 // 1. Assert: IsPropertyKey(P) is true.
68 ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
69 // 2. If Type(P) is Symbol, then
70 // a. Return ? OrdinaryGet(O, P, Receiver).
71 if (key->IsSymbol()) {
72 return JSObject::GetProperty(thread, obj, key);
73 }
74 JSHandle<ModuleNamespace> moduleNamespace = JSHandle<ModuleNamespace>::Cast(obj);
75 // 3. Let exports be O.[[Exports]].
76 JSHandle<JSTaggedValue> exports(thread, moduleNamespace->GetExports());
77 // 4. If P is not an element of exports, return undefined.
78 if (exports->IsUndefined()) {
79 return OperationResult(thread, thread->GlobalConstants()->GetUndefined(), PropertyMetaData(false));
80 }
81 if (!JSArray::IncludeInSortedValue(thread, exports, key)) {
82 return OperationResult(thread, thread->GlobalConstants()->GetUndefined(), PropertyMetaData(false));
83 }
84 // 5. Let m be O.[[Module]].
85 JSHandle<SourceTextModule> mm(thread, moduleNamespace->GetModule());
86 // 6. Let binding be ! m.ResolveExport(P, « »).
87 CVector<std::pair<JSHandle<SourceTextModule>, JSHandle<JSTaggedValue>>> resolveSet;
88 JSHandle<JSTaggedValue> binding = SourceTextModule::ResolveExport(thread, mm, key, resolveSet);
89 // 7. Assert: binding is a ResolvedBinding Record.
90 // Adapter new module
91 ASSERT(binding->IsResolvedBinding() || binding->IsResolvedIndexBinding());
92 JSTaggedValue result;
93 // 8. Let targetModule be binding.[[Module]].
94 if (binding->IsResolvedBinding()) {
95 JSHandle<ResolvedBinding> resolvedBind = JSHandle<ResolvedBinding>::Cast(binding);
96 JSTaggedValue targetModule = resolvedBind->GetModule();
97 // 9. Assert: targetModule is not undefined.
98 ASSERT(!targetModule.IsUndefined());
99 result = SourceTextModule::Cast(targetModule.GetTaggedObject())->
100 GetModuleValue(thread, resolvedBind->GetBindingName(), true);
101 } else {
102 JSHandle<ResolvedIndexBinding> resolvedBind = JSHandle<ResolvedIndexBinding>::Cast(binding);
103 JSTaggedValue targetModule = resolvedBind->GetModule();
104 // 9. Assert: targetModule is not undefined.
105 ASSERT(!targetModule.IsUndefined());
106 result = SourceTextModule::Cast(targetModule.GetTaggedObject())->
107 GetModuleValue(thread, resolvedBind->GetIndex(), true);
108 }
109 return OperationResult(thread, result, PropertyMetaData(true));
110 }
111
OwnPropertyKeys(JSThread * thread,const JSHandle<JSTaggedValue> & obj)112 JSHandle<TaggedArray> ModuleNamespace::OwnPropertyKeys(JSThread *thread, const JSHandle<JSTaggedValue> &obj)
113 {
114 ASSERT(obj->IsModuleNamespace());
115 // 1. Let exports be a copy of O.[[Exports]].
116 JSHandle<ModuleNamespace> moduleNamespace = JSHandle<ModuleNamespace>::Cast(obj);
117 JSHandle<JSTaggedValue> exports(thread, moduleNamespace->GetExports());
118 JSHandle<TaggedArray> exportsArray = JSArray::ToTaggedArray(thread, exports);
119 if (!moduleNamespace->ValidateKeysAvailable(thread, exportsArray)) {
120 return exportsArray;
121 }
122
123 // 2. Let symbolKeys be ! OrdinaryOwnPropertyKeys(O).
124 JSHandle<TaggedArray> symbolKeys = JSObject::GetOwnPropertyKeys(thread, JSHandle<JSObject>(obj));
125 // 3. Append all the entries of symbolKeys to the end of exports.
126 JSHandle<TaggedArray> result = TaggedArray::Append(thread, exportsArray, symbolKeys);
127 // 4. Return exports.
128 return result;
129 }
130
PreventExtensions()131 bool ModuleNamespace::PreventExtensions()
132 {
133 return true;
134 }
135
DefineOwnProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key,PropertyDescriptor desc)136 bool ModuleNamespace::DefineOwnProperty(JSThread *thread,
137 const JSHandle<JSTaggedValue> &obj,
138 const JSHandle<JSTaggedValue> &key,
139 PropertyDescriptor desc)
140 {
141 ASSERT(obj->IsModuleNamespace());
142 // 1. If Type(P) is Symbol, return ! OrdinaryDefineOwnProperty(O, P, Desc).
143 if (key->IsSymbol()) {
144 bool res = JSObject::OrdinaryDefineOwnProperty(thread, JSHandle<JSObject>(obj), key, desc);
145 return res;
146 }
147
148 // 2. Let current be ? O.[[GetOwnProperty]](P).
149 PropertyDescriptor current(thread);
150 // 3. If current is undefined, return false.
151 if (!GetOwnProperty(thread, obj, key, current)) {
152 return false;
153 }
154 // 4. If Desc has a [[Configurable]] field and Desc.[[Configurable]] is true, return false.
155 // 5. If Desc has an [[Enumerable]] field and Desc.[[Enumerable]] is false, return false.
156 // 6. If IsAccessorDescriptor(Desc) is true, return false.
157 // 7. If Desc has a [[Writable]] field and Desc.[[Writable]] is false, return false.
158 if (desc.IsAccessorDescriptor()) {
159 return false;
160 }
161 if (desc.HasConfigurable() && desc.IsConfigurable()) {
162 return false;
163 }
164 if (desc.HasEnumerable() && !desc.IsEnumerable()) {
165 return false;
166 }
167 if (desc.HasWritable() && !desc.IsWritable()) {
168 return false;
169 }
170
171 // 8. If Desc has a [[Value]] field, return SameValue(Desc.[[Value]], current.[[Value]]).
172 if (desc.HasValue()) {
173 JSHandle<JSTaggedValue> descValue = desc.GetValue();
174 JSHandle<JSTaggedValue> currentValue = current.GetValue();
175 return JSTaggedValue::SameValue(descValue, currentValue);
176 }
177
178 // 9. Return true.
179 return true;
180 }
181
HasProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key)182 bool ModuleNamespace::HasProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
183 const JSHandle<JSTaggedValue> &key)
184 {
185 ASSERT(obj->IsModuleNamespace());
186 // 1. If Type(P) is Symbol, return OrdinaryHasProperty(O, P).
187 if (key->IsSymbol()) {
188 return JSObject::HasProperty(thread, JSHandle<JSObject>(obj), key);
189 }
190 // 2. Let exports be O.[[Exports]].
191 JSHandle<ModuleNamespace> moduleNamespace = JSHandle<ModuleNamespace>::Cast(obj);
192 JSHandle<JSTaggedValue> exports(thread, moduleNamespace->GetExports());
193 // 3. If P is an element of exports, return true.
194 if (exports->IsUndefined()) {
195 return false;
196 }
197 if (JSArray::IncludeInSortedValue(thread, exports, key)) {
198 return true;
199 }
200 // 4. Return false.
201 return false;
202 }
203
SetPrototype(const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & proto)204 bool ModuleNamespace::SetPrototype([[maybe_unused]]const JSHandle<JSTaggedValue> &obj,
205 const JSHandle<JSTaggedValue> &proto)
206 {
207 ASSERT(obj->IsModuleNamespace());
208 // 1. Assert: Either Type(V) is Object or Type(V) is Null.
209 ASSERT(proto->IsECMAObject() || proto->IsNull());
210 return proto->IsNull();
211 }
212
GetOwnProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key,PropertyDescriptor & desc)213 bool ModuleNamespace::GetOwnProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
214 const JSHandle<JSTaggedValue> &key, PropertyDescriptor &desc)
215 {
216 // 1. If Type(P) is Symbol, return OrdinaryGetOwnProperty(O, P).
217 if (key->IsSymbol()) {
218 return JSObject::GetOwnProperty(thread, JSHandle<JSObject>(obj), key, desc);
219 }
220 // 2. Let exports be O.[[Exports]].
221 JSHandle<ModuleNamespace> moduleNamespace = JSHandle<ModuleNamespace>::Cast(obj);
222 JSHandle<JSTaggedValue> exports(thread, moduleNamespace->GetExports());
223 // 3. If P is not an element of exports, return undefined.
224 if (exports->IsUndefined()) {
225 return false;
226 }
227 if (!JSArray::IncludeInSortedValue(thread, exports, key)) {
228 return false;
229 }
230 // 4. Let value be ? O.[[Get]](P, O).
231 JSHandle<JSTaggedValue> value = ModuleNamespace::GetProperty(thread, obj, key).GetValue();
232 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
233 // 5. Return PropertyDescriptor {
234 // [[Value]]: value, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: false }.
235 desc.SetValue(value);
236 desc.SetEnumerable(true);
237 desc.SetWritable(true);
238 desc.SetConfigurable(false);
239 return true;
240 }
241
SetProperty(JSThread * thread,bool mayThrow)242 bool ModuleNamespace::SetProperty(JSThread *thread, bool mayThrow)
243 {
244 if (mayThrow) {
245 THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot assign to read only property of Object Module", false);
246 }
247 return false;
248 }
249
DeleteProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key)250 bool ModuleNamespace::DeleteProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
251 const JSHandle<JSTaggedValue> &key)
252 {
253 ASSERT(obj->IsModuleNamespace());
254 // 1. Assert: IsPropertyKey(P) is true.
255 ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
256 // 2. If Type(P) is Symbol, then
257 // Return ? OrdinaryDelete(O, P).
258 if (key->IsSymbol()) {
259 return JSObject::DeleteProperty(thread, JSHandle<JSObject>(obj), key);
260 }
261 // 3. Let exports be O.[[Exports]].
262 JSHandle<ModuleNamespace> moduleNamespace = JSHandle<ModuleNamespace>::Cast(obj);
263 JSHandle<JSTaggedValue> exports(thread, moduleNamespace->GetExports());
264 // 4. If P is an element of exports, return false.
265 if (exports->IsUndefined()) {
266 return true;
267 }
268 if (JSArray::IncludeInSortedValue(thread, exports, key)) {
269 return false;
270 }
271 return true;
272 }
273
ValidateKeysAvailable(JSThread * thread,const JSHandle<TaggedArray> & exports)274 bool ModuleNamespace::ValidateKeysAvailable(JSThread *thread, const JSHandle<TaggedArray> &exports)
275 {
276 JSHandle<ModuleNamespace> moduleNamespace(thread, this);
277 JSHandle<SourceTextModule> mm(thread, moduleNamespace->GetModule());
278 uint32_t exportsLength = exports->GetLength();
279 for (uint32_t idx = 0; idx < exportsLength; idx++) {
280 JSHandle<JSTaggedValue> key(thread, exports->Get(idx));
281 CVector<std::pair<JSHandle<SourceTextModule>, JSHandle<JSTaggedValue>>> resolveSet;
282 JSHandle<JSTaggedValue> binding = SourceTextModule::ResolveExport(thread, mm, key, resolveSet);
283 // Adapter new module
284 ASSERT(binding->IsResolvedBinding() || binding->IsResolvedIndexBinding());
285 JSTaggedValue targetModule = JSTaggedValue::Undefined();
286 if (binding->IsResolvedBinding()) {
287 targetModule = JSHandle<ResolvedBinding>::Cast(binding)->GetModule();
288 } else if (binding->IsResolvedIndexBinding()) {
289 targetModule = JSHandle<ResolvedIndexBinding>::Cast(binding)->GetModule();
290 }
291 ASSERT(!targetModule.IsUndefined());
292 JSTaggedValue dictionary = SourceTextModule::Cast(targetModule.GetTaggedObject())->GetNameDictionary();
293 if (dictionary.IsUndefined()) {
294 THROW_REFERENCE_ERROR_AND_RETURN(thread, "module environment is undefined", false);
295 }
296 }
297 return true;
298 }
299 } // namespace panda::ecmascript
300