From 4aea2e9c43fe7316e9c1f4a98e7f0d300024432a Mon Sep 17 00:00:00 2001 From: Andrey Efremov Date: Mon, 27 Feb 2023 18:35:55 +0300 Subject: [PATCH] Support inlining TryLdGlobalByName Signed-off-by: Andrey Efremov --- .../ecmascript_compiler_interface.h | 21 ++ .../optimizations/expand_intrinsics.cpp | 69 ++++++- .../optimizations/expand_intrinsics.h | 4 + .../compiler/ecmascript_runtime_interface.cpp | 127 +++++++++--- .../compiler/ecmascript_runtime_interface.h | 5 + runtime/global_dictionary-inl.h | 14 +- runtime/ic/profile_type_info.cpp | 4 +- runtime/object_operator.cpp | 8 +- tests/checked/CMakeLists.txt | 1 + tests/checked/try_load_global_by_name.js | 186 ++++++++++++++++++ 10 files changed, 405 insertions(+), 34 deletions(-) create mode 100644 tests/checked/try_load_global_by_name.js diff --git a/compiler/ecmascript_extensions/ecmascript_compiler_interface.h b/compiler/ecmascript_extensions/ecmascript_compiler_interface.h index 3056f919a..7455e2571 100644 --- a/compiler/ecmascript_extensions/ecmascript_compiler_interface.h +++ b/compiler/ecmascript_extensions/ecmascript_compiler_interface.h @@ -67,4 +67,25 @@ virtual size_t GetTaggedArrayElementSize() const { return 0; } + +struct GlobalVarInfo { + enum class Type { + DEFAULT = 0, // do not inline intrinsic + CONSTANT, // writable = false, configurable = false, not heap object. Inline as constant + NON_CONFIGURABLE, // writable = true, configurable = false. Can safely read from the same PropertyBox + FIELD // configurable = true, only simple own property reads seen in IC, + // read from PropertyBox and check not hole + }; + + Type type {Type::DEFAULT}; + // value for CONSTANT, address of PropertyBox for NON_CONFIGURABLE or FIELD + uint64_t value {0}; +}; + +virtual GlobalVarInfo GetGlobalVarInfo([[maybe_unused]] MethodPtr method, [[maybe_unused]] size_t id, + [[maybe_unused]] uintptr_t slot_id) const +{ + return {GlobalVarInfo::Type::DEFAULT, 0}; +} + #endif // PANDA_COMPILER_ECMASCRIPT_COMPILER_INTERFACE_H \ No newline at end of file diff --git a/compiler/optimizer/optimizations/expand_intrinsics.cpp b/compiler/optimizer/optimizations/expand_intrinsics.cpp index a690020d6..674d6e4b8 100644 --- a/compiler/optimizer/optimizations/expand_intrinsics.cpp +++ b/compiler/optimizer/optimizations/expand_intrinsics.cpp @@ -54,6 +54,8 @@ bool ExpandIntrinsics::Expand(IntrinsicInst *inst) return ExpandLdLexVarDyn(inst); case RuntimeInterface::IntrinsicId::INTRINSIC_LDLEXENV_DYN: return ExpandLdlexenvDyn(inst); + case RuntimeInterface::IntrinsicId::INTRINSIC_TRY_LD_GLOBAL_BY_NAME: + return ExpandTryLdGlobalByName(inst); default: return false; } @@ -447,4 +449,69 @@ bool ExpandIntrinsics::ExpandLdlexenvDyn(IntrinsicInst *intrinsic) return MoveInliningDepthToImms(intrinsic); } -} // namespace panda::compiler \ No newline at end of file +bool ExpandIntrinsics::ExpandTryLdGlobalByName(IntrinsicInst *inst) +{ + if (GetGraph()->IsAotMode()) { + return false; + } + ASSERT(inst->GetSaveState() != nullptr); + auto runtime = GetGraph()->GetRuntime(); + auto type_id = inst->GetImms()[0]; + auto pc = inst->GetPc(); + auto inline_info = runtime->GetGlobalVarInfo(GetGraph()->GetMethod(), type_id, pc); + switch (inline_info.type) { + case RuntimeInterface::GlobalVarInfo::Type::CONSTANT: { + auto constant = GetGraph()->FindOrCreateConstant(DataType::Any(inline_info.value)); + inst->ReplaceUsers(constant); + inst->GetBasicBlock()->RemoveInst(inst); + return true; + } + case RuntimeInterface::GlobalVarInfo::Type::NON_CONFIGURABLE: + ExpandTryLdGlobalField(inst, static_cast(inline_info.value)); + return true; + case RuntimeInterface::GlobalVarInfo::Type::FIELD: + ExpandTryLdGlobalField(inst, static_cast(inline_info.value)); + return true; + default: + return false; + } +} + +template +void ExpandIntrinsics::ExpandTryLdGlobalField(IntrinsicInst *inst, uintptr_t address) +{ + auto pc = inst->GetPc(); + auto save_state = inst->GetSaveState(); + auto type_id = inst->GetImms()[0]; + + auto get_address = GetGraph()->CreateInstGetGlobalVarAddress(DataType::REFERENCE, pc, address); + get_address->SetTypeId(type_id); + get_address->SetMethod(GetGraph()->GetMethod()); + get_address->SetInput(0, save_state); + inst->InsertBefore(get_address); + + auto load_object = GetGraph()->CreateInstLoadObject(DataType::ANY, pc); + load_object->SetTypeId(TypeIdMixin::MEM_DYN_GLOBAL_ID); + load_object->SetMethod(GetGraph()->GetMethod()); + load_object->SetObjField(nullptr); + load_object->SetObjectType(ObjectType::MEM_DYN_GLOBAL); + load_object->SetInput(0, get_address); + inst->InsertBefore(load_object); + + if constexpr (NEED_GUARD) { + auto cmp = GetGraph()->CreateInstCompareAnyType(DataType::BOOL, pc); + cmp->SetAnyType(AnyBaseType::ECMASCRIPT_HOLE_TYPE); + cmp->SetInput(0, load_object); + auto deopt = GetGraph()->CreateInstDeoptimizeIf(DataType::BOOL, pc); + deopt->SetInput(0, cmp); + deopt->SetInput(1, save_state); + deopt->SetDeoptimizeType(DeoptimizeType::INLINE_IC); + inst->InsertBefore(cmp); + inst->InsertBefore(deopt); + } + + inst->ReplaceUsers(load_object); + inst->GetBasicBlock()->RemoveInst(inst); +} + +} // namespace panda::compiler diff --git a/compiler/optimizer/optimizations/expand_intrinsics.h b/compiler/optimizer/optimizations/expand_intrinsics.h index 74e03389f..93ee637f7 100644 --- a/compiler/optimizer/optimizations/expand_intrinsics.h +++ b/compiler/optimizer/optimizations/expand_intrinsics.h @@ -77,6 +77,10 @@ private: bool ExpandLdLexVarDyn(IntrinsicInst *intrinsic); bool ExpandLdlexenvDyn(IntrinsicInst *intrinsic); + bool ExpandTryLdGlobalByName(IntrinsicInst *inst); + template + void ExpandTryLdGlobalField(IntrinsicInst *inst, uintptr_t address); + ArenaVector functions_; }; diff --git a/runtime/compiler/ecmascript_runtime_interface.cpp b/runtime/compiler/ecmascript_runtime_interface.cpp index 1c370bd6e..be2405361 100644 --- a/runtime/compiler/ecmascript_runtime_interface.cpp +++ b/runtime/compiler/ecmascript_runtime_interface.cpp @@ -71,38 +71,15 @@ uint32_t EcmaRuntimeInterface::GetFunctionTargetOffset([[maybe_unused]] Arch arc uintptr_t EcmaRuntimeInterface::GetGlobalVarAddress(MethodPtr method, size_t id) { - if (ecma_vm_ == nullptr) { - return 0; - } - auto thread = ecma_vm_->GetJSThread(); + auto thread = TryGetJSThread(); // Thread can be removed during destroy runtime - if (thread == nullptr || !thread->IsThreadAlive()) { + if (thread == nullptr) { return 0; } ScopedMutatorLock lock; - auto panda_file = JsMethodCast(method)->GetPandaFile(); - JSTaggedValue constant_pool(JSTaggedValue::VALUE_HOLE); - - auto func = [&](Program *p) { constant_pool = p->GetConstantPool(); }; - ecma_vm_->EnumerateProgram(func, panda_file->GetFilename()); - if (constant_pool.IsUndefined()) { - return 0; - } - - ASSERT(!constant_pool.IsHole()); - - JSTaggedValue key = ConstantPool::Cast(constant_pool.GetHeapObject())->GetObjectFromCache(id); - auto global_obj = thread->GetGlobalObject(); - auto js_obj = JSObject::Cast(global_obj.GetTaggedObject()); - TaggedArray *array = TaggedArray::Cast(js_obj->GetProperties().GetTaggedObject()); - if (array->GetLength() == 0) { - return 0; - } - - GlobalDictionary *dict = GlobalDictionary::Cast(array); - int entry = dict->FindEntry(key); + auto [dict, entry] = GetGlobalDictionaryEntry(thread, method, id); if (entry == -1) { return 0; } @@ -528,4 +505,102 @@ uintptr_t EcmaRuntimeInterface::AddFixedObjectHandle(Method *method, ObjectHeade vector->push_back(func_ref); return gos->GetAddressForRef(func_ref); } + +PandaRuntimeInterface::GlobalVarInfo EcmaRuntimeInterface::GetGlobalVarInfo(PandaRuntimeInterface::MethodPtr method, + size_t id, uintptr_t slot_id) const +{ + auto thread = TryGetJSThread(); + if (thread == nullptr) { + return {}; + } + ScopedMutatorLock lock; + auto [dict, entry] = GetGlobalDictionaryEntry(thread, method, id); + if (entry == -1) { + return {}; + } + PropertyAttributes attr = dict->GetAttributes(entry); + if (attr.IsAccessor()) { + return {}; + } + if (!attr.IsConfigurable()) { + if (!attr.IsWritable()) { + auto value = dict->GetValue(entry); + if (!value.IsHeapObject()) { + return {GlobalVarInfo::Type::CONSTANT, value.GetRawData()}; + } + } + JSTaggedValue res(dict->GetSafeBox(entry)); + if (!res.IsHeapObject()) { + return {}; + } + + ASSERT(res.IsPropertyBox()); + auto address = reinterpret_cast(res.GetHeapObject()); + return {GlobalVarInfo::Type::NON_CONFIGURABLE, address}; + } + + os::memory::LockHolder lock_m(mutex_); + uint8_t *mapping = JsMethodCast(method)->GetICMapping(); + if (mapping == nullptr) { + return {}; + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + auto slot = mapping[slot_id]; + auto it = js_function_table_.find(MethodCast(method)); + if (it == js_function_table_.end()) { + return {}; + } + + auto func = JSFunction::Cast(ecma_vm_->GetGlobalObjectStorage()->Get(it->second)); + ProfileTypeInfo *profile_type_info = ProfileTypeInfo::Cast(func->GetProfileTypeInfo().GetTaggedObject()); + + JSTaggedValue handler = profile_type_info->Get(slot); + if (!handler.IsHeapObject()) { + return {}; + } + if (PropertyBox::Cast(handler.GetHeapObject())->GetValue().IsHole()) { + // box was invalidated + return {}; + } + auto address = reinterpret_cast(handler.GetHeapObject()); + return {GlobalVarInfo::Type::FIELD, address}; +} + +JSThread *EcmaRuntimeInterface::TryGetJSThread() const +{ + if (ecma_vm_ == nullptr) { + return nullptr; + } + auto thread = ecma_vm_->GetJSThread(); + if (thread == nullptr || !thread->IsThreadAlive()) { + return nullptr; + } + return thread; +} + +std::pair EcmaRuntimeInterface::GetGlobalDictionaryEntry(JSThread *thread, MethodPtr method, + size_t id) const +{ + auto panda_file = JsMethodCast(method)->GetPandaFile(); + JSTaggedValue constant_pool(JSTaggedValue::VALUE_HOLE); + + auto func = [&](Program *p) { constant_pool = p->GetConstantPool(); }; + ecma_vm_->EnumerateProgram(func, panda_file->GetFilename()); + if (constant_pool.IsUndefined()) { + return {nullptr, -1}; + } + + ASSERT(!constant_pool.IsHole()); + + JSTaggedValue key = ConstantPool::Cast(constant_pool.GetHeapObject())->GetObjectFromCache(id); + auto global_obj = thread->GetGlobalObject(); + auto js_obj = JSObject::Cast(global_obj.GetTaggedObject()); + TaggedArray *array = TaggedArray::Cast(js_obj->GetProperties().GetTaggedObject()); + if (array->GetLength() == 0) { + return {nullptr, -1}; + } + + auto dict = GlobalDictionary::Cast(array); + return {dict, dict->FindEntry(key)}; +} } // namespace panda::ecmascript diff --git a/runtime/compiler/ecmascript_runtime_interface.h b/runtime/compiler/ecmascript_runtime_interface.h index 2e9134b44..25764763f 100644 --- a/runtime/compiler/ecmascript_runtime_interface.h +++ b/runtime/compiler/ecmascript_runtime_interface.h @@ -26,6 +26,7 @@ namespace panda::ecmascript { class EcmaVM; class ProfileTypeInfo; +class GlobalDictionary; enum class ObjectFieldType : uint8_t { INVALID = 0, ELEMENTS, COUNT }; @@ -176,6 +177,10 @@ private: size_t GetTaggedArrayElementSize() const override; + GlobalVarInfo GetGlobalVarInfo(MethodPtr method, uintptr_t id, uintptr_t slot_id) const override; + JSThread *TryGetJSThread() const; + std::pair GetGlobalDictionaryEntry(JSThread *thread, MethodPtr method, size_t id) const; + private: const EcmaVM *ecma_vm_ {nullptr}; mem::InternalAllocatorPtr internal_allocator_; diff --git a/runtime/global_dictionary-inl.h b/runtime/global_dictionary-inl.h index 566c062c2..84f9d1a34 100644 --- a/runtime/global_dictionary-inl.h +++ b/runtime/global_dictionary-inl.h @@ -17,6 +17,7 @@ #define ECMASCRIPT_GLOBAL_DICTIONARY_INL_H #include "plugins/ecmascript/runtime/global_dictionary.h" +#include "plugins/ecmascript/runtime/ecma_global_storage-inl.h" #include "plugins/ecmascript/runtime/ic/property_box.h" #include "plugins/ecmascript/runtime/tagged_hash_table-inl.h" @@ -79,6 +80,13 @@ void GlobalDictionary::SetEntry(const JSThread *thread, int entry, const JSTagge void GlobalDictionary::ClearEntry(const JSThread *thread, int entry) { +#if ECMASCRIPT_ENABLE_IC + auto box = GetBox(entry); + // The PropertyBox can be referenced from compiled code for TryLdGlobalByName + // TODO(aefremov): use handle instead + thread->GetEcmaGlobalStorage()->NewGlobalHandle(JSTaggedValue(box).GetRawData()); + box->Clear(thread); +#endif JSTaggedValue hole = JSTaggedValue::Hole(); PropertyAttributes meta_data; SetEntry(thread, entry, hole, hole, meta_data); @@ -162,14 +170,12 @@ void GlobalDictionary::InvalidatePropertyBox(JSThread *thread, const JSHandleGetBox(entry); - PropertyAttributes origin_attr = dict_handle->GetAttributes(entry); PropertyAttributes new_attr(meta_data); ASSERT(!box->GetValue().IsHole()); - int index = origin_attr.GetDictionaryOrder(); JSHandle old_value(thread, box->GetValue()); - GlobalDictionary::InvalidateAndReplaceEntry(thread, dict_handle, index, old_value); dict_handle->SetAttributes(thread, entry, new_attr); + GlobalDictionary::InvalidateAndReplaceEntry(thread, dict_handle, entry, old_value); } void GlobalDictionary::InvalidateAndReplaceEntry(JSThread *thread, const JSHandle &dict_handle, @@ -185,8 +191,8 @@ void GlobalDictionary::InvalidateAndReplaceEntry(JSThread *thread, const JSHandl // Cell is officially mutable henceforth. attr.SetBoxType(PropertyBoxType::MUTABLE); dict_handle->SetAttributes(thread, entry, attr); - dict_handle->UpdateValue(thread, entry, new_box.GetTaggedValue()); dict_handle->GetBox(entry)->Clear(thread); + dict_handle->UpdateValue(thread, entry, new_box.GetTaggedValue()); } } // namespace panda::ecmascript diff --git a/runtime/ic/profile_type_info.cpp b/runtime/ic/profile_type_info.cpp index 2968e4116..4d372371f 100644 --- a/runtime/ic/profile_type_info.cpp +++ b/runtime/ic/profile_type_info.cpp @@ -109,7 +109,9 @@ void ProfileTypeAccessor::AddGlobalHandlerKey(JSHandle key, JSHan void ProfileTypeAccessor::SetAsMega() const { profile_type_info_->Set(thread_, slot_id_, JSTaggedValue::Hole()); - profile_type_info_->Set(thread_, slot_id_ + 1, JSTaggedValue::Hole()); + if (!IsNamedGlobalIC(GetKind())) { + profile_type_info_->Set(thread_, slot_id_ + 1, JSTaggedValue::Hole()); + } } std::string ICKindToString(ICKind kind) diff --git a/runtime/object_operator.cpp b/runtime/object_operator.cpp index 061a7d97d..e1d54bba6 100644 --- a/runtime/object_operator.cpp +++ b/runtime/object_operator.cpp @@ -311,8 +311,8 @@ void ObjectOperator::LookupPropertyInlinedProps(const JSHandle &obj) } JSTaggedValue value(dict->GetBox(entry)); - uint32_t attr = dict->GetAttributes(entry).GetValue(); - SetFound(entry, value, attr, true); + PropertyAttributes attr = dict->GetAttributes(entry); + SetFound(entry, value, attr.GetValue(), !attr.IsAccessor()); return; } @@ -457,6 +457,10 @@ bool ObjectOperator::WriteDataProperty(const JSHandle &receiver, const attr_changed = true; } + if (IsAccessorDescriptor() && value_->IsPropertyBox()) { + value_.Update(PropertyBox::Cast(value_->GetTaggedObject())->GetValue()); + } + if (!desc.IsAccessorDescriptor()) { if (desc.HasWritable() && attr.IsWritable() != desc.IsWritable()) { attr.SetWritable(desc.IsWritable()); diff --git a/tests/checked/CMakeLists.txt b/tests/checked/CMakeLists.txt index d2ee7e1c8..54c018493 100644 --- a/tests/checked/CMakeLists.txt +++ b/tests/checked/CMakeLists.txt @@ -94,6 +94,7 @@ if (NOT PANDA_TARGET_ARM32) if (PANDA_TARGET_AMD64 OR NOT PANDA_ARM64_TESTS_WITH_SANITIZER) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/type_resolving.js SUPPORT_RELEASE true) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/global_var.js SUPPORT_RELEASE true) + panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/try_load_global_by_name.js SUPPORT_RELEASE true) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/ecma_profiling.js SUPPORT_RELEASE true) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/recompile_not_smi.js SUPPORT_RELEASE true) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/recompile_double_with_int.js SUPPORT_RELEASE true) diff --git a/tests/checked/try_load_global_by_name.js b/tests/checked/try_load_global_by_name.js new file mode 100644 index 000000000..ca1f791d6 --- /dev/null +++ b/tests/checked/try_load_global_by_name.js @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2021-2022 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. + */ + + +//! CHECKER Inline global property based on IC, deoptimize if it is deleted or becomes accessor +//! RUN options: "--no-async-jit --compiler-hotness-threshold=10 --compiler-regex _GLOBAL::get_.*", entry: "_GLOBAL::func_main_0" +//! EVENT /Deoptimization,_GLOBAL::func_get_x/ +//! EVENT /Deoptimization,_GLOBAL::func_get_y/ +//! EVENT /Deoptimization,_GLOBAL::func_get_may_throw/ +//! METHOD "get_x" +//! PASS_AFTER "IrBuilder" +//! INST "Intrinsic.TryLdGlobalByName" +//! PASS_AFTER "ExpandIntrinsics" +//! INST_NOT "Intrinsic.TryLdGlobalByName" +//! INST "GetGlobalVarAddress" +//! INST_NEXT "LoadObject" +//! INST_NEXT "Compare" +//! INST_NEXT "DeoptimizeIf" +//! METHOD "get_y" +//! PASS_AFTER "IrBuilder" +//! INST "Intrinsic.TryLdGlobalByName" +//! PASS_AFTER "ExpandIntrinsics" +//! INST_NOT "Intrinsic.TryLdGlobalByName" +//! INST "GetGlobalVarAddress" +//! INST_NEXT "LoadObject" +//! INST_NEXT "Compare" +//! INST_NEXT "DeoptimizeIf" +//! METHOD "get_non_conf" +//! PASS_AFTER "IrBuilder" +//! INST "Intrinsic.TryLdGlobalByName" +//! PASS_AFTER "ExpandIntrinsics" +//! INST_NOT "Intrinsic.TryLdGlobalByName" +//! INST_NOT "DeoptimizeIf" +//! INST "GetGlobalVarAddress" +//! INST_NEXT "LoadObject" +//! METHOD "get_const" +//! PASS_AFTER "IrBuilder" +//! INST "Intrinsic.TryLdGlobalByName" +//! INST_COUNT "Constant", 1 +//! PASS_AFTER "ExpandIntrinsics" +//! INST_NOT "Intrinsic.TryLdGlobalByName" +//! INST_NOT "DeoptimizeIf" +//! INST_NOT "GetGlobalVarAddress" +//! INST_COUNT "Constant", 2 +//! METHOD "get_always_throw" +//! PASS_AFTER "Codegen" +//! INST "Intrinsic.TryLdGlobalByName" + +function get_x() { + return x; +} +function get_y() { + return y; +} +function get_non_conf() { + return non_conf_var; +} +function get_const() { + return const_var; +} +function get_may_throw() { + return may_throw; +} +function get_always_throw() { + return always_throw; +} + +Object.defineProperty(globalThis, "x", {value: 1, writable: true, configurable: true}); +var sum = 0; +for (var i = 0; i < 20; i++) { + sum += get_x(); +} +if (sum != 20) { + throw "Wrong result for global field: " + sum; +} +var getter_calls = 0; +Object.defineProperty(globalThis, "x", {get: function() { getter_calls++; return 2; }}); +for (var i = 0; i < 20; i++) { + sum += get_x(); +} +if (getter_calls != 20) { + throw "Wrong number of getter calls: " + getter_calls; +} +if (sum != 60) { + throw "Wrong result after transition to accessor: " + sum; +} + +delete globalThis.x; +try { + get_x(); + throw "did't get reference error for deleted property"; +} catch (e) { + if (!(e instanceof ReferenceError)) { + throw e; + } +} + +globalThis.y = 1; +sum = 0; +for (var i = 0; i < 20; i++) { + sum += get_y(); +} +delete globalThis.y; +try { + get_y(); + throw "did't get reference error for deleted property"; +} catch (e) { + if (!(e instanceof ReferenceError)) { + throw e; + } +} +Object.defineProperty(globalThis, "y", {value: 2, writable: true, configurable: true}); +for (var i = 0; i < 20; i++) { + sum += get_y(); +} +if (sum != 60) { + throw "Wrong result for deleted and redefined property: " + sum; +} + +Object.defineProperty(globalThis, "non_conf_var", {value: 1, writable: true, configurable: false}); +sum = 0; +for (var i = 0; i < 20; i++) { + sum += get_non_conf(); +} +if (sum != 20) { + throw "Wrong result for non-configurable global var: " + sum; +} +non_conf_var = -1; +for (var i = 0; i < 20; i++) { + sum += get_non_conf(); +} +if (sum != 0) { + throw "Wrong result after write to non-configurable global var: " + sum; +} + +Object.defineProperty(globalThis, "const_var", {value: 4, writable: false, configurable: false}); +sum = 0; +for (var i = 0; i < 20; i++) { + sum += get_const(); +} +if (sum != 80) { + throw "Wrong result for non-writable global var: " + sum; +} + +Object.defineProperty(globalThis, "may_throw", {value: 5, configurable: true}); +sum = 0; +for (var i = 0; i < 20; i++) { + sum += get_may_throw(); +} +if (sum != 100) { + throw "Wrong result for configurable global var: " + sum; +} +Object.defineProperty(globalThis, "may_throw", {get: function() { throw 11; }, configurable: true}); +try { + get_may_throw(); + throw "Did't get exception from getter"; +} catch (e) { + if (e != 11) { + throw "Getter has thrown wrong exception: " + e; + } +} + +Object.defineProperty(globalThis, "always_throw", {get: function() { throw 1; }, configurable: false}); +sum = 0; +for (var i = 0; i < 15; i++) { + try { + sum -= get_always_throw(); + } catch (e) { + sum += e; + } +} +if (sum != 15) { + throw "Wrong result from non-configurable global property with throwing getter: " + sum; +} \ No newline at end of file -- Gitee