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/object_factory-inl.h"
20 #include "ecmascript/module/module_manager_helper.h"
21 #include "ecmascript/module/module_path_helper.h"
22 #include "ecmascript/module/js_module_deregister.h"
23 #include "ecmascript/module/js_shared_module_manager.h"
24 #include "ecmascript/shared_objects/js_shared_array.h"
25
26 namespace panda::ecmascript {
CreateSortedExports(JSThread * thread,const JSHandle<TaggedArray> & exports)27 JSHandle<JSTaggedValue> ModuleNamespace::CreateSortedExports(JSThread *thread, const JSHandle<TaggedArray> &exports)
28 {
29 auto globalConst = thread->GlobalConstants();
30 // 7. Let sortedExports be a new List containing the same values as the list exports where the values
31 // are ordered as if an Array of the same values had been sorted using
32 // Array.prototype.sort using undefined as comparefn.
33 JSHandle<JSArray> exportsArray = JSArray::CreateArrayFromList(thread, exports);
34 JSHandle<JSTaggedValue> sortedExports = JSHandle<JSTaggedValue>::Cast(exportsArray);
35 JSHandle<JSTaggedValue> fn = globalConst->GetHandledUndefined();
36 JSArray::Sort(thread, sortedExports, fn);
37 return sortedExports;
38 }
39
ModuleNamespaceCreate(JSThread * thread,const JSHandle<JSTaggedValue> & module,const JSHandle<TaggedArray> & exports)40 JSHandle<ModuleNamespace> ModuleNamespace::ModuleNamespaceCreate(JSThread *thread,
41 const JSHandle<JSTaggedValue> &module,
42 const JSHandle<TaggedArray> &exports)
43 {
44 auto globalConst = thread->GlobalConstants();
45 // 1. Assert: module is a Module Record.
46 ASSERT(module->IsModuleRecord());
47 if (SourceTextModule::IsSharedModule(JSHandle<SourceTextModule>::Cast(module))) {
48 return SharedModuleManager::GetInstance()->SModuleNamespaceCreate(thread, module, exports);
49 }
50
51 // 2. Assert: module.[[Namespace]] is undefined.
52 // 3. Assert: exports is a List of String values.
53 // 4. Let M be a newly created object.
54 // 5. Set M's essential internal methods to the definitions specified in 9.4.6.
55 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
56 JSHandle<ModuleNamespace> mNp = factory->NewModuleNamespace();
57 // 6. Set M.[[Module]] to module.
58 mNp->SetModule(thread, module);
59
60 JSHandle<JSTaggedValue> sortedExports = CreateSortedExports(thread, exports);
61 // 8. Set M.[[Exports]] to sortedExports.
62 mNp->SetExports(thread, sortedExports);
63 // 9. Create own properties of M corresponding to the definitions in 26.3.
64
65 JSHandle<JSTaggedValue> toStringTag = thread->GetEcmaVM()->GetGlobalEnv()->GetToStringTagSymbol();
66 JSHandle<JSTaggedValue> moduleString = globalConst->GetHandledModuleString();
67 PropertyDescriptor des(thread, moduleString, false, false, false);
68 JSHandle<JSObject> mNpObj = JSHandle<JSObject>::Cast(mNp);
69 JSObject::DefineOwnProperty(thread, mNpObj, toStringTag, des);
70 // 10. Set module.[[Namespace]] to M.
71 SetModuleDeregisterProcession(thread, mNp, ModuleDeregister::FreeModuleRecord);
72 JSHandle<ModuleRecord> moduleRecord = JSHandle<ModuleRecord>::Cast(module);
73 SourceTextModule::Cast(moduleRecord.GetTaggedValue().GetTaggedObject())->SetNamespace(thread,
74 mNp.GetTaggedValue());
75 mNp->GetJSHClass()->SetExtensible(false);
76 return mNp;
77 }
78
GetProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key)79 OperationResult ModuleNamespace::GetProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
80 const JSHandle<JSTaggedValue> &key)
81 {
82 // 1. Assert: IsPropertyKey(P) is true.
83 ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
84 // 2. If Type(P) is Symbol, then
85 // a. Return ? OrdinaryGet(O, P, Receiver).
86 if (key->IsSymbol()) {
87 return JSObject::GetProperty(thread, obj, key);
88 }
89 JSHandle<ModuleNamespace> moduleNamespace = JSHandle<ModuleNamespace>::Cast(obj);
90 // 3. Let exports be O.[[Exports]].
91 JSHandle<JSTaggedValue> exports(thread, moduleNamespace->GetExports());
92 // 4. If P is not an element of exports, return undefined.
93 if (exports->IsUndefined()) {
94 return OperationResult(thread, thread->GlobalConstants()->GetUndefined(), PropertyMetaData(false));
95 }
96 if (exports->IsJSArray()) {
97 if (!JSArray::IncludeInSortedValue(thread, exports, key))
98 return OperationResult(thread, thread->GlobalConstants()->GetUndefined(), PropertyMetaData(false));
99 } else if (exports->IsJSSharedArray() && !JSSharedArray::IncludeInSortedValue(thread, exports, key)) {
100 return OperationResult(thread, thread->GlobalConstants()->GetUndefined(), PropertyMetaData(false));
101 }
102 // 5. Let m be O.[[Module]].
103 JSHandle<SourceTextModule> mm(thread, moduleNamespace->GetModule());
104 // 6. Let binding be ! m.ResolveExport(P, « »).
105 CVector<std::pair<JSHandle<SourceTextModule>, JSHandle<JSTaggedValue>>> resolveSet;
106 JSHandle<JSTaggedValue> binding = SourceTextModule::ResolveExport(thread, mm, key, resolveSet);
107 RETURN_VALUE_IF_ABRUPT_COMPLETION(
108 thread, OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false)));
109 // 7. Assert: binding is a ResolvedBinding Record.
110 // If resolution is null or "ambiguous", throw a SyntaxError exception.
111 if (binding->IsNull() || binding->IsString()) { // LCOV_EXCL_BR_LINE
112 CString requestMod = ModulePathHelper::ReformatPath(mm->GetEcmaModuleFilenameString());
113 LOG_FULL(FATAL) << "Module: '" << requestMod << SourceTextModule::GetResolveErrorReason(binding) <<
114 ConvertToString(key.GetTaggedValue()) << ".";
115 }
116 JSTaggedValue result;
117 // 8. Let targetModule be binding.[[Module]].
118 JSType type = binding->GetTaggedObject()->GetClass()->GetObjectType();
119 switch (type) {
120 case JSType::RESOLVEDBINDING_RECORD: {
121 JSHandle<ResolvedBinding> resolvedBind = JSHandle<ResolvedBinding>::Cast(binding);
122 JSTaggedValue targetModule = resolvedBind->GetModule();
123 // 9. Assert: targetModule is not undefined.
124 ASSERT(!targetModule.IsUndefined());
125 JSHandle<SourceTextModule> module(thread, targetModule);
126 // DFX: make sure lazy module is already evaluated.
127 if (module->GetStatus() == ModuleStatus::INSTANTIATED) {
128 LOG_FULL(ERROR) << "Module is not evaluated, module is :" << module->GetEcmaModuleRecordNameString();
129 }
130 ModuleTypes moduleType = module->GetTypes();
131 if (UNLIKELY(SourceTextModule::IsNativeModule(moduleType))) {
132 result = ModuleManagerHelper::GetModuleValue(thread, module, resolvedBind->GetBindingName());
133 RETURN_VALUE_IF_ABRUPT_COMPLETION(
134 thread, OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false)));
135 } else {
136 result = module->GetModuleValue(thread, resolvedBind->GetBindingName(), true);
137 }
138 break;
139 }
140 case JSType::RESOLVEDINDEXBINDING_RECORD: {
141 JSHandle<ResolvedIndexBinding> resolvedBind = JSHandle<ResolvedIndexBinding>::Cast(binding);
142 JSTaggedValue targetModule = resolvedBind->GetModule();
143 // 9. Assert: targetModule is not undefined.
144 ASSERT(!targetModule.IsUndefined());
145 JSHandle<SourceTextModule> module(thread, targetModule);
146 // DFX: make sure lazy module is already evaluated.
147 if (module->GetStatus() == ModuleStatus::INSTANTIATED) {
148 LOG_FULL(ERROR) << "Module is not evaluated, module is :" << module->GetEcmaModuleRecordNameString();
149 }
150 ModuleTypes moduleType = module->GetTypes();
151 if (UNLIKELY(SourceTextModule::IsNativeModule(moduleType))) {
152 result = ModuleManagerHelper::GetNativeOrCjsModuleValue(
153 thread, targetModule, resolvedBind->GetIndex());
154 RETURN_VALUE_IF_ABRUPT_COMPLETION(
155 thread, OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false)));
156 } else {
157 result = module->GetModuleValue(thread, resolvedBind->GetIndex(), true);
158 }
159 break;
160 }
161 case JSType::RESOLVEDRECORDINDEXBINDING_RECORD:
162 LOG_FULL(INFO) << "RESOLVEDRECORDINDEXBINDING_RECORD";
163 break;
164 case JSType::RESOLVEDRECORDBINDING_RECORD:
165 LOG_FULL(INFO) << "RESOLVEDRECORDINDEXBINDING_RECORD";
166 break;
167 default:
168 LOG_FULL(FATAL) << "UNREACHABLE";
169 }
170 return OperationResult(thread, result, PropertyMetaData(true));
171 }
172
OwnPropertyKeys(JSThread * thread,const JSHandle<JSTaggedValue> & obj)173 JSHandle<TaggedArray> ModuleNamespace::OwnPropertyKeys(JSThread *thread, const JSHandle<JSTaggedValue> &obj)
174 {
175 ASSERT(obj->IsModuleNamespace());
176 // 1. Let exports be a copy of O.[[Exports]].
177 JSHandle<ModuleNamespace> moduleNamespace = JSHandle<ModuleNamespace>::Cast(obj);
178 JSHandle<JSTaggedValue> exports(thread, moduleNamespace->GetExports());
179 JSHandle<TaggedArray> exportsArray = JSArray::ToTaggedArray(thread, exports);
180 if (!ModuleNamespace::ValidateKeysAvailable(thread, moduleNamespace, exportsArray)) {
181 return exportsArray;
182 }
183
184 // 2. Let symbolKeys be ! OrdinaryOwnPropertyKeys(O).
185 JSHandle<TaggedArray> symbolKeys = JSObject::GetOwnPropertyKeys(thread, JSHandle<JSObject>(obj));
186 // 3. Append all the entries of symbolKeys to the end of exports.
187 JSHandle<TaggedArray> result = TaggedArray::Append(thread, exportsArray, symbolKeys);
188 // 4. Return exports.
189 return result;
190 }
191
OwnEnumPropertyKeys(JSThread * thread,const JSHandle<JSTaggedValue> & obj)192 JSHandle<TaggedArray> ModuleNamespace::OwnEnumPropertyKeys(JSThread *thread, const JSHandle<JSTaggedValue> &obj)
193 {
194 ASSERT(obj->IsModuleNamespace());
195 // 1. Let exports be a copy of O.[[Exports]].
196 JSHandle<ModuleNamespace> moduleNamespace = JSHandle<ModuleNamespace>::Cast(obj);
197 JSHandle<JSTaggedValue> exports(thread, moduleNamespace->GetExports());
198 JSHandle<TaggedArray> exportsArray = JSArray::ToTaggedArray(thread, exports);
199 if (!ModuleNamespace::ValidateKeysAvailable(thread, moduleNamespace, exportsArray)) {
200 return exportsArray;
201 }
202
203 // 2. Let symbolKeys be ! OrdinaryOwnPropertyKeys(O).
204 JSHandle<TaggedArray> symbolKeys = JSObject::GetOwnEnumPropertyKeys(thread, JSHandle<JSObject>(obj));
205 // 3. Append all the entries of symbolKeys to the end of exports.
206 JSHandle<TaggedArray> result = TaggedArray::Append(thread, exportsArray, symbolKeys);
207 // 4. Return exports.
208 return result;
209 }
210
PreventExtensions()211 bool ModuleNamespace::PreventExtensions()
212 {
213 return true;
214 }
215
DefineOwnProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key,PropertyDescriptor desc)216 bool ModuleNamespace::DefineOwnProperty(JSThread *thread,
217 const JSHandle<JSTaggedValue> &obj,
218 const JSHandle<JSTaggedValue> &key,
219 PropertyDescriptor desc)
220 {
221 ASSERT(obj->IsModuleNamespace());
222 // 1. If Type(P) is Symbol, return ! OrdinaryDefineOwnProperty(O, P, Desc).
223 if (key->IsSymbol()) {
224 bool res = JSObject::OrdinaryDefineOwnProperty(thread, JSHandle<JSObject>(obj), key, desc);
225 return res;
226 }
227
228 // 2. Let current be ? O.[[GetOwnProperty]](P).
229 PropertyDescriptor current(thread);
230 // 3. If current is undefined, return false.
231 if (!GetOwnProperty(thread, obj, key, current)) {
232 return false;
233 }
234 // 4. If Desc has a [[Configurable]] field and Desc.[[Configurable]] is true, return false.
235 // 5. If Desc has an [[Enumerable]] field and Desc.[[Enumerable]] is false, return false.
236 // 6. If IsAccessorDescriptor(Desc) is true, return false.
237 // 7. If Desc has a [[Writable]] field and Desc.[[Writable]] is false, return false.
238 if (desc.IsAccessorDescriptor()) {
239 return false;
240 }
241 if (desc.HasConfigurable() && desc.IsConfigurable()) {
242 return false;
243 }
244 if (desc.HasEnumerable() && !desc.IsEnumerable()) {
245 return false;
246 }
247 if (desc.HasWritable() && !desc.IsWritable()) {
248 return false;
249 }
250
251 // 8. If Desc has a [[Value]] field, return SameValue(Desc.[[Value]], current.[[Value]]).
252 if (desc.HasValue()) {
253 JSHandle<JSTaggedValue> descValue = desc.GetValue();
254 JSHandle<JSTaggedValue> currentValue = current.GetValue();
255 return JSTaggedValue::SameValue(descValue, currentValue);
256 }
257
258 // 9. Return true.
259 return true;
260 }
261
HasProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key)262 bool ModuleNamespace::HasProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
263 const JSHandle<JSTaggedValue> &key)
264 {
265 ASSERT(obj->IsModuleNamespace());
266 // 1. If Type(P) is Symbol, return OrdinaryHasProperty(O, P).
267 if (key->IsSymbol()) {
268 return JSObject::HasProperty(thread, JSHandle<JSObject>(obj), key);
269 }
270 // 2. Let exports be O.[[Exports]].
271 JSHandle<ModuleNamespace> moduleNamespace = JSHandle<ModuleNamespace>::Cast(obj);
272 JSHandle<JSTaggedValue> exports(thread, moduleNamespace->GetExports());
273 // 3. If P is an element of exports, return true.
274 if (exports->IsUndefined()) {
275 return false;
276 }
277 if (JSArray::IncludeInSortedValue(thread, exports, key)) {
278 return true;
279 }
280 // 4. Return false.
281 return false;
282 }
283
SetPrototype(const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & proto)284 bool ModuleNamespace::SetPrototype([[maybe_unused]] const JSHandle<JSTaggedValue> &obj,
285 const JSHandle<JSTaggedValue> &proto)
286 {
287 ASSERT(obj->IsModuleNamespace());
288 // 1. Assert: Either Type(V) is Object or Type(V) is Null.
289 ASSERT(proto->IsECMAObject() || proto->IsNull());
290 return proto->IsNull();
291 }
292
GetOwnProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key,PropertyDescriptor & desc)293 bool ModuleNamespace::GetOwnProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
294 const JSHandle<JSTaggedValue> &key, PropertyDescriptor &desc)
295 {
296 // 1. If Type(P) is Symbol, return OrdinaryGetOwnProperty(O, P).
297 if (key->IsSymbol()) {
298 return JSObject::GetOwnProperty(thread, JSHandle<JSObject>(obj), key, desc);
299 }
300 // 2. Let exports be O.[[Exports]].
301 JSHandle<ModuleNamespace> moduleNamespace = JSHandle<ModuleNamespace>::Cast(obj);
302 JSHandle<JSTaggedValue> exports(thread, moduleNamespace->GetExports());
303 // 3. If P is not an element of exports, return undefined.
304 if (exports->IsUndefined()) {
305 return false;
306 }
307 if (!JSArray::IncludeInSortedValue(thread, exports, key)) {
308 return false;
309 }
310 // 4. Let value be ? O.[[Get]](P, O).
311 JSHandle<JSTaggedValue> value = ModuleNamespace::GetProperty(thread, obj, key).GetValue();
312 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
313 // 5. Return PropertyDescriptor {
314 // [[Value]]: value, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: false }.
315 desc.SetValue(value);
316 desc.SetEnumerable(true);
317 desc.SetWritable(true);
318 desc.SetConfigurable(false);
319 return true;
320 }
321
SetProperty(JSThread * thread,bool mayThrow)322 bool ModuleNamespace::SetProperty(JSThread *thread, bool mayThrow)
323 {
324 if (mayThrow) {
325 THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot assign to read only property of Object Module", false);
326 }
327 return false;
328 }
329
DeleteProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key)330 bool ModuleNamespace::DeleteProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
331 const JSHandle<JSTaggedValue> &key)
332 {
333 ASSERT(obj->IsModuleNamespace());
334 // 1. Assert: IsPropertyKey(P) is true.
335 ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
336 // 2. If Type(P) is Symbol, then
337 // Return ? OrdinaryDelete(O, P).
338 if (key->IsSymbol()) {
339 return JSObject::DeleteProperty(thread, JSHandle<JSObject>(obj), key);
340 }
341 // 3. Let exports be O.[[Exports]].
342 JSHandle<ModuleNamespace> moduleNamespace = JSHandle<ModuleNamespace>::Cast(obj);
343 JSHandle<JSTaggedValue> exports(thread, moduleNamespace->GetExports());
344 // 4. If P is an element of exports, return false.
345 if (exports->IsUndefined()) {
346 return true;
347 }
348 if (JSArray::IncludeInSortedValue(thread, exports, key)) {
349 return false;
350 }
351 return true;
352 }
353
354 // static
ValidateKeysAvailable(JSThread * thread,const JSHandle<ModuleNamespace> & moduleNamespace,const JSHandle<TaggedArray> & exports)355 bool ModuleNamespace::ValidateKeysAvailable(JSThread *thread, const JSHandle<ModuleNamespace> &moduleNamespace,
356 const JSHandle<TaggedArray> &exports)
357 {
358 JSHandle<SourceTextModule> mm(thread, moduleNamespace->GetModule());
359 uint32_t exportsLength = exports->GetLength();
360 for (uint32_t idx = 0; idx < exportsLength; idx++) {
361 JSHandle<JSTaggedValue> key(thread, exports->Get(idx));
362 CVector<std::pair<JSHandle<SourceTextModule>, JSHandle<JSTaggedValue>>> resolveSet;
363 JSHandle<JSTaggedValue> binding = SourceTextModule::ResolveExport(thread, mm, key, resolveSet);
364 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
365 // Adapter new module
366 ASSERT(binding->IsResolvedBinding() || binding->IsResolvedIndexBinding());
367 JSTaggedValue targetModule = JSTaggedValue::Undefined();
368 if (binding->IsResolvedBinding()) {
369 targetModule = JSHandle<ResolvedBinding>::Cast(binding)->GetModule();
370 } else if (binding->IsResolvedIndexBinding()) {
371 targetModule = JSHandle<ResolvedIndexBinding>::Cast(binding)->GetModule();
372 }
373 ASSERT(!targetModule.IsUndefined());
374 JSTaggedValue dictionary = SourceTextModule::Cast(targetModule.GetTaggedObject())->GetNameDictionary();
375 if (dictionary.IsUndefined()) {
376 THROW_REFERENCE_ERROR_AND_RETURN(thread, "module environment is undefined", false);
377 }
378 }
379 return true;
380 }
381
SetModuleDeregisterProcession(JSThread * thread,const JSHandle<ModuleNamespace> & nameSpace,const NativePointerCallback & callback)382 void ModuleNamespace::SetModuleDeregisterProcession(JSThread *thread, const JSHandle<ModuleNamespace> &nameSpace,
383 const NativePointerCallback &callback)
384 {
385 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
386
387 JSHandle<SourceTextModule> module(thread, nameSpace->GetModule());
388 CString moduleStr = SourceTextModule::GetModuleName(module.GetTaggedValue());
389 int srcLength = strlen(moduleStr.c_str()) + 1;
390 auto moduleNameData = thread->GetEcmaVM()->GetNativeAreaAllocator()->AllocateBuffer(srcLength);
391 if (memcpy_s(moduleNameData, srcLength, moduleStr.c_str(), srcLength) != EOK) { // LCOV_EXCL_BR_LINE
392 LOG_ECMA(FATAL) << "Failed to copy module name's data.";
393 UNREACHABLE();
394 }
395 char *tmpData = reinterpret_cast<char *>(moduleNameData);
396 tmpData[srcLength - 1] = '\0';
397 ASSERT(nameSpace->GetDeregisterProcession().IsUndefined());
398 JSHandle<JSNativePointer> registerPointer = factory->NewJSNativePointer(
399 reinterpret_cast<void *>(moduleNameData), callback, reinterpret_cast<void *>(thread), false, srcLength);
400 nameSpace->SetDeregisterProcession(thread, registerPointer.GetTaggedValue());
401 }
402 } // namespace panda::ecmascript
403