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