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