1 /*
2  * Copyright (c) 2021-2024 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/builtins/builtins_weak_map.h"
17 
18 #include "ecmascript/builtins/builtins_map.h"
19 #include "ecmascript/js_tagged_value-inl.h"
20 #include "ecmascript/js_weak_container.h"
21 #include "ecmascript/linked_hash_table.h"
22 namespace panda::ecmascript::builtins {
WeakMapConstructor(EcmaRuntimeCallInfo *argv)23 JSTaggedValue BuiltinsWeakMap::WeakMapConstructor(EcmaRuntimeCallInfo *argv)
24 {
25     ASSERT(argv);
26     BUILTINS_API_TRACE(argv->GetThread(), WeakMap, Constructor);
27     JSThread *thread = argv->GetThread();
28     [[maybe_unused]] EcmaHandleScope handleScope(thread);
29     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
30     // 1.If NewTarget is undefined, throw a TypeError exception
31     JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv);
32     if (newTarget->IsUndefined()) {
33         // throw type error
34         THROW_TYPE_ERROR_AND_RETURN(thread, "new target can't be undefined", JSTaggedValue::Exception());
35     }
36     // 2.Let WeakMap be OrdinaryCreateFromConstructor(NewTarget, "%WeakMapPrototype%", «‍[[WeakMapData]]» ).
37     JSHandle<JSTaggedValue> constructor = GetConstructor(argv);
38     JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), newTarget);
39     // 3.returnIfAbrupt()
40     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
41     JSHandle<JSWeakMap> weakMap = JSHandle<JSWeakMap>::Cast(obj);
42 
43     // 4.Set weakmap’s [[WeakMapData]] internal slot to a new empty List.
44     JSHandle<LinkedHashMap> linkedMap = LinkedHashMap::Create(thread);
45     weakMap->SetLinkedMap(thread, linkedMap);
46     // add data into set from iterable
47     // 5.If iterable is not present, let iterable be undefined.
48     // 6.If iterable is either undefined or null, let iter be undefined.
49     JSHandle<JSTaggedValue> iterable = GetCallArg(argv, 0);
50     // 8.If iter is undefined, return set
51     if (iterable->IsUndefined() || iterable->IsNull()) {
52         return weakMap.GetTaggedValue();
53     }
54     if (!iterable->IsECMAObject()) {
55         THROW_TYPE_ERROR_AND_RETURN(thread, "iterable is not object", JSTaggedValue::Exception());
56     }
57     // Let adder be Get(weakMap, "set").
58     JSHandle<JSTaggedValue> adderKey = thread->GlobalConstants()->GetHandledSetString();
59     JSHandle<JSTaggedValue> adder =
60         JSObject::GetProperty(thread, JSHandle<JSTaggedValue>(weakMap), adderKey).GetValue();
61     // ReturnIfAbrupt(adder).
62     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, adder.GetTaggedValue());
63     // If IsCallable(adder) is false, throw a TypeError exception
64     if (!adder->IsCallable()) {
65         THROW_TYPE_ERROR_AND_RETURN(thread, "adder is not callable", adder.GetTaggedValue());
66     }
67     return BuiltinsMap::AddEntriesFromIterable(thread, obj, iterable, adder, factory);
68 }
69 
Delete(EcmaRuntimeCallInfo *argv)70 JSTaggedValue BuiltinsWeakMap::Delete(EcmaRuntimeCallInfo *argv)
71 {
72     ASSERT(argv);
73     BUILTINS_API_TRACE(argv->GetThread(), WeakMap, Delete);
74     JSThread *thread = argv->GetThread();
75     [[maybe_unused]] EcmaHandleScope handleScope(thread);
76     JSHandle<JSTaggedValue> self = GetThis(argv);
77     // 2.If Type(S) is not Object, throw a TypeError exception.
78     // 3.If S does not have a [[WeakMapData]] internal slot, throw a TypeError exception.
79     if (!self->IsJSWeakMap()) {
80         THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSWeakMap.", JSTaggedValue::Exception());
81     }
82 
83     JSHandle<JSWeakMap> weakMap(self);
84     JSHandle<JSTaggedValue> key = GetCallArg(argv, 0);
85     // 5.If CanBeHeldWeakly(key) is false, return false.
86     if (!JSTaggedValue::CanBeHeldWeakly(thread, key)) {
87         return GetTaggedBoolean(false);
88     }
89     return GetTaggedBoolean(JSWeakMap::Delete(thread, weakMap, key));
90 }
91 
Has(EcmaRuntimeCallInfo *argv)92 JSTaggedValue BuiltinsWeakMap::Has(EcmaRuntimeCallInfo *argv)
93 {
94     ASSERT(argv);
95     BUILTINS_API_TRACE(argv->GetThread(), WeakMap, Has);
96     JSThread *thread = argv->GetThread();
97     [[maybe_unused]] EcmaHandleScope handleScope(thread);
98     JSHandle<JSTaggedValue> self(GetThis(argv));
99     // 2.If Type(S) is not Object, throw a TypeError exception.
100     // 3.If S does not have a [[WeakMapData]] internal slot, throw a TypeError exception.
101     if (!self->IsJSWeakMap()) {
102         THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSWeakMap.", JSTaggedValue::Exception());
103     }
104     JSWeakMap *jsWeakMap = JSWeakMap::Cast(self.GetTaggedValue().GetTaggedObject());
105     JSHandle<JSTaggedValue> key = GetCallArg(argv, 0);
106     // 5.If CanBeHeldWeakly(key) is false, return false.
107     if (!JSTaggedValue::CanBeHeldWeakly(thread, key)) {
108         return GetTaggedBoolean(false);
109     }
110     return GetTaggedBoolean(jsWeakMap->Has(thread, key.GetTaggedValue()));
111 }
112 
Get(EcmaRuntimeCallInfo *argv)113 JSTaggedValue BuiltinsWeakMap::Get(EcmaRuntimeCallInfo *argv)
114 {
115     ASSERT(argv);
116     BUILTINS_API_TRACE(argv->GetThread(), WeakMap, Get);
117     JSThread *thread = argv->GetThread();
118     [[maybe_unused]] EcmaHandleScope handleScope(thread);
119     JSHandle<JSTaggedValue> self(GetThis(argv));
120     // 2.If Type(S) is not Object, throw a TypeError exception.
121     // 3.If S does not have a [[WeakMapData]] internal slot, throw a TypeError exception.
122     if (!self->IsJSWeakMap()) {
123         THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSWeakMap.", JSTaggedValue::Exception());
124     }
125     JSWeakMap *jsWeakMap = JSWeakMap::Cast(self.GetTaggedValue().GetTaggedObject());
126     JSHandle<JSTaggedValue> key = GetCallArg(argv, 0);
127     // 4.If CanBeHeldWeakly(key) is false, return undefined.
128     if (!JSTaggedValue::CanBeHeldWeakly(thread, key)) {
129         return JSTaggedValue::Undefined();
130     }
131     return jsWeakMap->Get(thread, key.GetTaggedValue());
132 }
133 
Set(EcmaRuntimeCallInfo *argv)134 JSTaggedValue BuiltinsWeakMap::Set(EcmaRuntimeCallInfo *argv)
135 {
136     ASSERT(argv);
137     BUILTINS_API_TRACE(argv->GetThread(), WeakMap, Set);
138     JSThread *thread = argv->GetThread();
139     [[maybe_unused]] EcmaHandleScope handleScope(thread);
140     JSHandle<JSTaggedValue> self = GetThis(argv);
141 
142     // 2.If Type(S) is not Object, throw a TypeError exception.
143     // 3.If S does not have a [[WeakMapData]] internal slot, throw a TypeError exception.
144     if (!self->IsJSWeakMap()) {
145         THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSWeakMap.", JSTaggedValue::Exception());
146     }
147 
148     JSHandle<JSTaggedValue> key = GetCallArg(argv, 0);
149     // 4.If CanBeHeldWeakly(key) is false, throw a TypeError exception.
150     if (!JSTaggedValue::CanBeHeldWeakly(thread, key)) {
151         THROW_TYPE_ERROR_AND_RETURN(thread, "invalid value used as weak map key.", JSTaggedValue::Exception());
152     }
153 
154     JSHandle<JSTaggedValue> value = GetCallArg(argv, 1);
155 
156     JSHandle<JSWeakMap> map(self);
157     JSWeakMap::Set(thread, map, key, value);
158     return map.GetTaggedValue();
159 }
160 }  // namespace panda::ecmascript::builtins
161