• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_value_accessor.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(thread));
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(thread));
104     // 6. Let binding be ! m.ResolveExport(P, « »).
105     ResolvedMultiMap resolvedMap;
106     JSHandle<JSTaggedValue> binding = SourceTextModule::ResolveExport(thread, mm, key, resolvedMap);
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(thread, 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(thread);
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 = ModuleValueAccessor::GetNativeOrCjsModuleValue(thread, module,
133                     resolvedBind->GetBindingName(thread));
134                 RETURN_VALUE_IF_ABRUPT_COMPLETION(
135                     thread, OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false)));
136             } else {
137                 result = module->GetModuleValue(thread, resolvedBind->GetBindingName(thread), true);
138             }
139             break;
140         }
141         case JSType::RESOLVEDINDEXBINDING_RECORD: {
142             JSHandle<ResolvedIndexBinding> resolvedBind = JSHandle<ResolvedIndexBinding>::Cast(binding);
143             JSTaggedValue targetModule = resolvedBind->GetModule(thread);
144             // 9. Assert: targetModule is not undefined.
145             ASSERT(!targetModule.IsUndefined());
146             JSHandle<SourceTextModule> module(thread, targetModule);
147             // DFX: make sure lazy module is already evaluated.
148             if (module->GetStatus() == ModuleStatus::INSTANTIATED) {
149                 LOG_FULL(ERROR) << "Module is not evaluated, module is :" << module->GetEcmaModuleRecordNameString();
150             }
151             ModuleTypes moduleType = module->GetTypes();
152             if (UNLIKELY(SourceTextModule::IsNativeModule(moduleType))) {
153                 result = ModuleValueAccessor::GetNativeOrCjsModuleValue(thread, module, resolvedBind->GetIndex());
154                 RETURN_VALUE_IF_ABRUPT_COMPLETION(
155                     thread, OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false)));
156             } else {
157                 if (SourceTextModule::IsSharedModule(module)) {
158                     JSHandle<SourceTextModule> sharedModule = SharedModuleManager::GetInstance()->GetSModule(
159                         thread, module->GetEcmaModuleRecordNameString());
160                     if (sharedModule.GetTaggedValue().IsSourceTextModule()) {
161                         module = sharedModule;
162                     }
163                 }
164                 result = module->GetModuleValue(thread, resolvedBind->GetIndex(), true);
165             }
166             break;
167         }
168         case JSType::RESOLVEDRECORDINDEXBINDING_RECORD:
169             LOG_FULL(INFO) << "RESOLVEDRECORDINDEXBINDING_RECORD";
170             break;
171         case JSType::RESOLVEDRECORDBINDING_RECORD:
172             LOG_FULL(INFO) << "RESOLVEDRECORDINDEXBINDING_RECORD";
173             break;
174         default:
175             LOG_FULL(FATAL) << "UNREACHABLE";
176     }
177     return OperationResult(thread, result, PropertyMetaData(true));
178 }
179 
OwnPropertyKeys(JSThread * thread,const JSHandle<JSTaggedValue> & obj)180 JSHandle<TaggedArray> ModuleNamespace::OwnPropertyKeys(JSThread *thread, const JSHandle<JSTaggedValue> &obj)
181 {
182     ASSERT(obj->IsModuleNamespace());
183     // 1. Let exports be a copy of O.[[Exports]].
184     JSHandle<ModuleNamespace> moduleNamespace = JSHandle<ModuleNamespace>::Cast(obj);
185     JSHandle<JSTaggedValue> exports(thread, moduleNamespace->GetExports(thread));
186     JSHandle<TaggedArray> exportsArray = JSArray::ToTaggedArray(thread, exports);
187     if (!ModuleNamespace::ValidateKeysAvailable(thread, moduleNamespace, exportsArray)) {
188         return exportsArray;
189     }
190 
191     // 2. Let symbolKeys be ! OrdinaryOwnPropertyKeys(O).
192     JSHandle<TaggedArray> symbolKeys = JSObject::GetOwnPropertyKeys(thread, JSHandle<JSObject>(obj));
193     // 3. Append all the entries of symbolKeys to the end of exports.
194     JSHandle<TaggedArray> result = TaggedArray::Append(thread, exportsArray, symbolKeys);
195     // 4. Return exports.
196     return result;
197 }
198 
OwnEnumPropertyKeys(JSThread * thread,const JSHandle<JSTaggedValue> & obj)199 JSHandle<TaggedArray> ModuleNamespace::OwnEnumPropertyKeys(JSThread *thread, const JSHandle<JSTaggedValue> &obj)
200 {
201     ASSERT(obj->IsModuleNamespace());
202     // 1. Let exports be a copy of O.[[Exports]].
203     JSHandle<ModuleNamespace> moduleNamespace = JSHandle<ModuleNamespace>::Cast(obj);
204     JSHandle<JSTaggedValue> exports(thread, moduleNamespace->GetExports(thread));
205     JSHandle<TaggedArray> exportsArray = JSArray::ToTaggedArray(thread, exports);
206     if (!ModuleNamespace::ValidateKeysAvailable(thread, moduleNamespace, exportsArray)) {
207         return exportsArray;
208     }
209 
210     // 2. Let symbolKeys be ! OrdinaryOwnPropertyKeys(O).
211     JSHandle<TaggedArray> symbolKeys = JSObject::GetOwnEnumPropertyKeys(thread, JSHandle<JSObject>(obj));
212     // 3. Append all the entries of symbolKeys to the end of exports.
213     JSHandle<TaggedArray> result = TaggedArray::Append(thread, exportsArray, symbolKeys);
214     // 4. Return exports.
215     return result;
216 }
217 
PreventExtensions()218 bool ModuleNamespace::PreventExtensions()
219 {
220     return true;
221 }
222 
DefineOwnProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key,PropertyDescriptor desc)223 bool ModuleNamespace::DefineOwnProperty(JSThread *thread,
224                                         const JSHandle<JSTaggedValue> &obj,
225                                         const JSHandle<JSTaggedValue> &key,
226                                         PropertyDescriptor desc)
227 {
228     ASSERT(obj->IsModuleNamespace());
229     // 1. If Type(P) is Symbol, return ! OrdinaryDefineOwnProperty(O, P, Desc).
230     if (key->IsSymbol()) {
231         bool res = JSObject::OrdinaryDefineOwnProperty(thread, JSHandle<JSObject>(obj), key, desc);
232         return res;
233     }
234 
235     // 2. Let current be ? O.[[GetOwnProperty]](P).
236     PropertyDescriptor current(thread);
237     // 3. If current is undefined, return false.
238     if (!GetOwnProperty(thread, obj, key, current)) {
239         return false;
240     }
241     // 4. If Desc has a [[Configurable]] field and Desc.[[Configurable]] is true, return false.
242     // 5. If Desc has an [[Enumerable]] field and Desc.[[Enumerable]] is false, return false.
243     // 6. If IsAccessorDescriptor(Desc) is true, return false.
244     // 7. If Desc has a [[Writable]] field and Desc.[[Writable]] is false, return false.
245     if (desc.IsAccessorDescriptor()) {
246         return false;
247     }
248     if (desc.HasConfigurable() && desc.IsConfigurable()) {
249         return false;
250     }
251     if (desc.HasEnumerable() && !desc.IsEnumerable()) {
252         return false;
253     }
254     if (desc.HasWritable() && !desc.IsWritable()) {
255         return false;
256     }
257 
258     // 8. If Desc has a [[Value]] field, return SameValue(Desc.[[Value]], current.[[Value]]).
259     if (desc.HasValue()) {
260         JSHandle<JSTaggedValue> descValue = desc.GetValue();
261         JSHandle<JSTaggedValue> currentValue = current.GetValue();
262         return JSTaggedValue::SameValue(thread, descValue, currentValue);
263     }
264 
265     // 9. Return true.
266     return true;
267 }
268 
HasProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key)269 bool ModuleNamespace::HasProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
270                                   const JSHandle<JSTaggedValue> &key)
271 {
272     ASSERT(obj->IsModuleNamespace());
273     // 1. If Type(P) is Symbol, return OrdinaryHasProperty(O, P).
274     if (key->IsSymbol()) {
275         return JSObject::HasProperty(thread, JSHandle<JSObject>(obj), key);
276     }
277     // 2. Let exports be O.[[Exports]].
278     JSHandle<ModuleNamespace> moduleNamespace = JSHandle<ModuleNamespace>::Cast(obj);
279     JSHandle<JSTaggedValue> exports(thread, moduleNamespace->GetExports(thread));
280     // 3. If P is an element of exports, return true.
281     if (exports->IsUndefined()) {
282         return false;
283     }
284     if (JSArray::IncludeInSortedValue(thread, exports, key)) {
285         return true;
286     }
287     // 4. Return false.
288     return false;
289 }
290 
SetPrototype(const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & proto)291 bool ModuleNamespace::SetPrototype([[maybe_unused]] const JSHandle<JSTaggedValue> &obj,
292                                    const JSHandle<JSTaggedValue> &proto)
293 {
294     ASSERT(obj->IsModuleNamespace());
295     // 1. Assert: Either Type(V) is Object or Type(V) is Null.
296     ASSERT(proto->IsECMAObject() || proto->IsNull());
297     return proto->IsNull();
298 }
299 
GetOwnProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key,PropertyDescriptor & desc)300 bool ModuleNamespace::GetOwnProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
301                                      const JSHandle<JSTaggedValue> &key, PropertyDescriptor &desc)
302 {
303     // 1. If Type(P) is Symbol, return OrdinaryGetOwnProperty(O, P).
304     if (key->IsSymbol()) {
305         return JSObject::GetOwnProperty(thread, JSHandle<JSObject>(obj), key, desc);
306     }
307     // 2. Let exports be O.[[Exports]].
308     JSHandle<ModuleNamespace> moduleNamespace = JSHandle<ModuleNamespace>::Cast(obj);
309     JSHandle<JSTaggedValue> exports(thread, moduleNamespace->GetExports(thread));
310     // 3. If P is not an element of exports, return undefined.
311     if (exports->IsUndefined()) {
312         return false;
313     }
314     if (!JSArray::IncludeInSortedValue(thread, exports, key)) {
315         return false;
316     }
317     // 4. Let value be ? O.[[Get]](P, O).
318     JSHandle<JSTaggedValue> value = ModuleNamespace::GetProperty(thread, obj, key).GetValue();
319     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
320     // 5. Return PropertyDescriptor {
321     //    [[Value]]: value, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: false }.
322     desc.SetValue(value);
323     desc.SetEnumerable(true);
324     desc.SetWritable(true);
325     desc.SetConfigurable(false);
326     return true;
327 }
328 
SetProperty(JSThread * thread,bool mayThrow)329 bool ModuleNamespace::SetProperty(JSThread *thread, bool mayThrow)
330 {
331     if (mayThrow) {
332         THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot assign to read only property of Object Module", false);
333     }
334     return false;
335 }
336 
DeleteProperty(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key)337 bool ModuleNamespace::DeleteProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
338                                      const JSHandle<JSTaggedValue> &key)
339 {
340     ASSERT(obj->IsModuleNamespace());
341     // 1. Assert: IsPropertyKey(P) is true.
342     ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
343     // 2. If Type(P) is Symbol, then
344     //    Return ? OrdinaryDelete(O, P).
345     if (key->IsSymbol()) {
346         return JSObject::DeleteProperty(thread, JSHandle<JSObject>(obj), key);
347     }
348     // 3. Let exports be O.[[Exports]].
349     JSHandle<ModuleNamespace> moduleNamespace = JSHandle<ModuleNamespace>::Cast(obj);
350     JSHandle<JSTaggedValue> exports(thread, moduleNamespace->GetExports(thread));
351     // 4. If P is an element of exports, return false.
352     if (exports->IsUndefined()) {
353         return true;
354     }
355     if (JSArray::IncludeInSortedValue(thread, exports, key)) {
356         return false;
357     }
358     return true;
359 }
360 
361 // static
ValidateKeysAvailable(JSThread * thread,const JSHandle<ModuleNamespace> & moduleNamespace,const JSHandle<TaggedArray> & exports)362 bool ModuleNamespace::ValidateKeysAvailable(JSThread *thread, const JSHandle<ModuleNamespace> &moduleNamespace,
363                                             const JSHandle<TaggedArray> &exports)
364 {
365     JSHandle<SourceTextModule> mm(thread, moduleNamespace->GetModule(thread));
366     uint32_t exportsLength = exports->GetLength();
367     for (uint32_t idx = 0; idx < exportsLength; idx++) {
368         JSHandle<JSTaggedValue> key(thread, exports->Get(thread, idx));
369         ResolvedMultiMap resolvedMap;
370         JSHandle<JSTaggedValue> binding = SourceTextModule::ResolveExport(thread, mm, key, resolvedMap);
371         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
372         // Adapter new module
373         ASSERT(binding->IsResolvedBinding() || binding->IsResolvedIndexBinding());
374         JSTaggedValue targetModule = JSTaggedValue::Undefined();
375         if (binding->IsResolvedBinding()) {
376             targetModule = JSHandle<ResolvedBinding>::Cast(binding)->GetModule(thread);
377         } else if (binding->IsResolvedIndexBinding()) {
378             targetModule = JSHandle<ResolvedIndexBinding>::Cast(binding)->GetModule(thread);
379         }
380         ASSERT(!targetModule.IsUndefined());
381         JSTaggedValue dictionary = SourceTextModule::Cast(targetModule.GetTaggedObject())->GetNameDictionary(thread);
382         if (dictionary.IsUndefined()) {
383             THROW_REFERENCE_ERROR_AND_RETURN(thread, "module environment is undefined", false);
384         }
385     }
386     return true;
387 }
388 
SetModuleDeregisterProcession(JSThread * thread,const JSHandle<ModuleNamespace> & nameSpace,const NativePointerCallback & callback)389 void ModuleNamespace::SetModuleDeregisterProcession(JSThread *thread, const JSHandle<ModuleNamespace> &nameSpace,
390     const NativePointerCallback &callback)
391 {
392     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
393 
394     JSHandle<SourceTextModule> module(thread, nameSpace->GetModule(thread));
395     CString moduleStr = SourceTextModule::GetModuleName(module.GetTaggedValue());
396     int srcLength = strlen(moduleStr.c_str()) + 1;
397     auto moduleNameData = thread->GetEcmaVM()->GetNativeAreaAllocator()->AllocateBuffer(srcLength);
398     if (memcpy_s(moduleNameData, srcLength, moduleStr.c_str(), srcLength) != EOK) { // LCOV_EXCL_BR_LINE
399         LOG_ECMA(FATAL) << "Failed to copy module name's data.";
400         UNREACHABLE();
401     }
402     char *tmpData = reinterpret_cast<char *>(moduleNameData);
403     tmpData[srcLength - 1] = '\0';
404     ASSERT(nameSpace->GetDeregisterProcession(thread).IsUndefined());
405     JSHandle<JSNativePointer> registerPointer = factory->NewJSNativePointer(
406         reinterpret_cast<void *>(moduleNameData), callback, reinterpret_cast<void *>(thread), false, srcLength);
407     nameSpace->SetDeregisterProcession(thread, registerPointer.GetTaggedValue());
408 }
409 }  // namespace panda::ecmascript
410