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"
22namespace panda::ecmascript::builtins {
23JSTaggedValue 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
70JSTaggedValue 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
92JSTaggedValue 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
113JSTaggedValue 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
134JSTaggedValue 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