/* * Copyright (c) 2021 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ecmascript/module/js_module_namespace.h" #include "ecmascript/global_env.h" #include "ecmascript/js_array.h" #include "ecmascript/module/js_module_record.h" #include "ecmascript/module/js_module_source_text.h" #include "ecmascript/base/string_helper.h" namespace panda::ecmascript { JSHandle ModuleNamespace::ModuleNamespaceCreate(JSThread *thread, const JSHandle &module, const JSHandle &exports) { auto globalConst = thread->GlobalConstants(); // 1. Assert: module is a Module Record. ASSERT(module->IsModuleRecord()); // 2. Assert: module.[[Namespace]] is undefined. JSHandle moduleRecord = JSHandle::Cast(module); ASSERT(ModuleRecord::GetNamespace(moduleRecord.GetTaggedValue()).IsUndefined()); // 3. Assert: exports is a List of String values. // 4. Let M be a newly created object. // 5. Set M's essential internal methods to the definitions specified in 9.4.6. ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle mNp = factory->NewModuleNamespace(); // 6. Set M.[[Module]] to module. mNp->SetModule(thread, module); // 7. Let sortedExports be a new List containing the same values as the list exports where the values // are ordered as if an Array of the same values had been sorted using // Array.prototype.sort using undefined as comparefn. JSHandle exportsArray = JSArray::CreateArrayFromList(thread, exports); JSHandle sortedExports = JSHandle::Cast(exportsArray); JSHandle fn = globalConst->GetHandledUndefined(); JSArray::Sort(thread, sortedExports, fn); // 8. Set M.[[Exports]] to sortedExports. mNp->SetExports(thread, sortedExports); // 9. Create own properties of M corresponding to the definitions in 26.3. JSHandle toStringTag = thread->GetEcmaVM()->GetGlobalEnv()->GetToStringTagSymbol(); JSHandle moduleString = globalConst->GetHandledModuleString(); PropertyDescriptor des(thread, moduleString, false, false, false); JSHandle mNpObj = JSHandle::Cast(mNp); JSObject::DefineOwnProperty(thread, mNpObj, toStringTag, des); // 10. Set module.[[Namespace]] to M. ModuleRecord::SetNamespace(thread, moduleRecord.GetTaggedValue(), mNp.GetTaggedValue()); mNp->GetJSHClass()->SetExtensible(false); return mNp; } OperationResult ModuleNamespace::GetProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key) { // 1. Assert: IsPropertyKey(P) is true. ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); // 2. If Type(P) is Symbol, then // a. Return ? OrdinaryGet(O, P, Receiver). if (key->IsSymbol()) { return JSObject::GetProperty(thread, obj, key); } JSHandle moduleNamespace = JSHandle::Cast(obj); // 3. Let exports be O.[[Exports]]. JSHandle exports(thread, moduleNamespace->GetExports()); // 4. If P is not an element of exports, return undefined. if (exports->IsUndefined()) { return OperationResult(thread, thread->GlobalConstants()->GetUndefined(), PropertyMetaData(false)); } if (!JSArray::IncludeInSortedValue(thread, exports, key)) { return OperationResult(thread, thread->GlobalConstants()->GetUndefined(), PropertyMetaData(false)); } // 5. Let m be O.[[Module]]. JSHandle mm(thread, moduleNamespace->GetModule()); // 6. Let binding be ! m.ResolveExport(P, « »). CVector, JSHandle>> resolveSet; JSHandle binding = SourceTextModule::ResolveExport(thread, mm, key, resolveSet); // 7. Assert: binding is a ResolvedBinding Record. // Adapter new module ASSERT(binding->IsResolvedBinding() || binding->IsResolvedIndexBinding()); JSTaggedValue result; // 8. Let targetModule be binding.[[Module]]. if (binding->IsResolvedBinding()) { JSHandle resolvedBind = JSHandle::Cast(binding); JSTaggedValue targetModule = resolvedBind->GetModule(); // 9. Assert: targetModule is not undefined. ASSERT(!targetModule.IsUndefined()); result = SourceTextModule::Cast(targetModule.GetTaggedObject())-> GetModuleValue(thread, resolvedBind->GetBindingName(), true); } else { JSHandle resolvedBind = JSHandle::Cast(binding); JSTaggedValue targetModule = resolvedBind->GetModule(); // 9. Assert: targetModule is not undefined. ASSERT(!targetModule.IsUndefined()); result = SourceTextModule::Cast(targetModule.GetTaggedObject())-> GetModuleValue(thread, resolvedBind->GetIndex(), true); } return OperationResult(thread, result, PropertyMetaData(true)); } JSHandle ModuleNamespace::OwnPropertyKeys(JSThread *thread, const JSHandle &obj) { ASSERT(obj->IsModuleNamespace()); // 1. Let exports be a copy of O.[[Exports]]. JSHandle moduleNamespace = JSHandle::Cast(obj); JSHandle exports(thread, moduleNamespace->GetExports()); JSHandle exportsArray = JSArray::ToTaggedArray(thread, exports); if (!moduleNamespace->ValidateKeysAvailable(thread, exportsArray)) { return exportsArray; } // 2. Let symbolKeys be ! OrdinaryOwnPropertyKeys(O). JSHandle symbolKeys = JSObject::GetOwnPropertyKeys(thread, JSHandle(obj)); // 3. Append all the entries of symbolKeys to the end of exports. JSHandle result = TaggedArray::Append(thread, exportsArray, symbolKeys); // 4. Return exports. return result; } bool ModuleNamespace::PreventExtensions() { return true; } bool ModuleNamespace::DefineOwnProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, PropertyDescriptor desc) { ASSERT(obj->IsModuleNamespace()); // 1. If Type(P) is Symbol, return ! OrdinaryDefineOwnProperty(O, P, Desc). if (key->IsSymbol()) { bool res = JSObject::OrdinaryDefineOwnProperty(thread, JSHandle(obj), key, desc); return res; } // 2. Let current be ? O.[[GetOwnProperty]](P). PropertyDescriptor current(thread); // 3. If current is undefined, return false. if (!GetOwnProperty(thread, obj, key, current)) { return false; } // 4. If Desc has a [[Configurable]] field and Desc.[[Configurable]] is true, return false. // 5. If Desc has an [[Enumerable]] field and Desc.[[Enumerable]] is false, return false. // 6. If IsAccessorDescriptor(Desc) is true, return false. // 7. If Desc has a [[Writable]] field and Desc.[[Writable]] is false, return false. if (desc.IsAccessorDescriptor()) { return false; } if (desc.HasConfigurable() && desc.IsConfigurable()) { return false; } if (desc.HasEnumerable() && !desc.IsEnumerable()) { return false; } if (desc.HasWritable() && !desc.IsWritable()) { return false; } // 8. If Desc has a [[Value]] field, return SameValue(Desc.[[Value]], current.[[Value]]). if (desc.HasValue()) { JSHandle descValue = desc.GetValue(); JSHandle currentValue = current.GetValue(); return JSTaggedValue::SameValue(descValue, currentValue); } // 9. Return true. return true; } bool ModuleNamespace::HasProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key) { ASSERT(obj->IsModuleNamespace()); // 1. If Type(P) is Symbol, return OrdinaryHasProperty(O, P). if (key->IsSymbol()) { return JSObject::HasProperty(thread, JSHandle(obj), key); } // 2. Let exports be O.[[Exports]]. JSHandle moduleNamespace = JSHandle::Cast(obj); JSHandle exports(thread, moduleNamespace->GetExports()); // 3. If P is an element of exports, return true. if (exports->IsUndefined()) { return false; } if (JSArray::IncludeInSortedValue(thread, exports, key)) { return true; } // 4. Return false. return false; } bool ModuleNamespace::SetPrototype([[maybe_unused]]const JSHandle &obj, const JSHandle &proto) { ASSERT(obj->IsModuleNamespace()); // 1. Assert: Either Type(V) is Object or Type(V) is Null. ASSERT(proto->IsECMAObject() || proto->IsNull()); return proto->IsNull(); } bool ModuleNamespace::GetOwnProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, PropertyDescriptor &desc) { // 1. If Type(P) is Symbol, return OrdinaryGetOwnProperty(O, P). if (key->IsSymbol()) { return JSObject::GetOwnProperty(thread, JSHandle(obj), key, desc); } // 2. Let exports be O.[[Exports]]. JSHandle moduleNamespace = JSHandle::Cast(obj); JSHandle exports(thread, moduleNamespace->GetExports()); // 3. If P is not an element of exports, return undefined. if (exports->IsUndefined()) { return false; } if (!JSArray::IncludeInSortedValue(thread, exports, key)) { return false; } // 4. Let value be ? O.[[Get]](P, O). JSHandle value = ModuleNamespace::GetProperty(thread, obj, key).GetValue(); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); // 5. Return PropertyDescriptor { // [[Value]]: value, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: false }. desc.SetValue(value); desc.SetEnumerable(true); desc.SetWritable(true); desc.SetConfigurable(false); return true; } bool ModuleNamespace::SetProperty(JSThread *thread, bool mayThrow) { if (mayThrow) { THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot assign to read only property of Object Module", false); } return false; } bool ModuleNamespace::DeleteProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key) { ASSERT(obj->IsModuleNamespace()); // 1. Assert: IsPropertyKey(P) is true. ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); // 2. If Type(P) is Symbol, then // Return ? OrdinaryDelete(O, P). if (key->IsSymbol()) { return JSObject::DeleteProperty(thread, JSHandle(obj), key); } // 3. Let exports be O.[[Exports]]. JSHandle moduleNamespace = JSHandle::Cast(obj); JSHandle exports(thread, moduleNamespace->GetExports()); // 4. If P is an element of exports, return false. if (exports->IsUndefined()) { return true; } if (JSArray::IncludeInSortedValue(thread, exports, key)) { return false; } return true; } bool ModuleNamespace::ValidateKeysAvailable(JSThread *thread, const JSHandle &exports) { JSHandle moduleNamespace(thread, this); JSHandle mm(thread, moduleNamespace->GetModule()); uint32_t exportsLength = exports->GetLength(); for (uint32_t idx = 0; idx < exportsLength; idx++) { JSHandle key(thread, exports->Get(idx)); CVector, JSHandle>> resolveSet; JSHandle binding = SourceTextModule::ResolveExport(thread, mm, key, resolveSet); // Adapter new module ASSERT(binding->IsResolvedBinding() || binding->IsResolvedIndexBinding()); JSTaggedValue targetModule = JSTaggedValue::Undefined(); if (binding->IsResolvedBinding()) { targetModule = JSHandle::Cast(binding)->GetModule(); } else if (binding->IsResolvedIndexBinding()) { targetModule = JSHandle::Cast(binding)->GetModule(); } ASSERT(!targetModule.IsUndefined()); JSTaggedValue dictionary = SourceTextModule::Cast(targetModule.GetTaggedObject())->GetNameDictionary(); if (dictionary.IsUndefined()) { THROW_REFERENCE_ERROR_AND_RETURN(thread, "module environment is undefined", false); } } return true; } } // namespace panda::ecmascript