1/*
2 * Copyright (C) 2013 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "SetPrototype.h"
28
29#include "CachedCall.h"
30#include "Error.h"
31#include "ExceptionHelpers.h"
32#include "GetterSetter.h"
33#include "JSCJSValueInlines.h"
34#include "JSFunctionInlines.h"
35#include "JSSet.h"
36#include "JSSetIterator.h"
37#include "MapData.h"
38#include "StructureInlines.h"
39
40namespace JSC {
41
42const ClassInfo SetPrototype::s_info = { "Set", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(SetPrototype) };
43
44static EncodedJSValue JSC_HOST_CALL setProtoFuncAdd(ExecState*);
45static EncodedJSValue JSC_HOST_CALL setProtoFuncClear(ExecState*);
46static EncodedJSValue JSC_HOST_CALL setProtoFuncDelete(ExecState*);
47static EncodedJSValue JSC_HOST_CALL setProtoFuncForEach(ExecState*);
48static EncodedJSValue JSC_HOST_CALL setProtoFuncHas(ExecState*);
49static EncodedJSValue JSC_HOST_CALL setProtoFuncKeys(ExecState*);
50static EncodedJSValue JSC_HOST_CALL setProtoFuncValues(ExecState*);
51static EncodedJSValue JSC_HOST_CALL setProtoFuncEntries(ExecState*);
52
53
54static EncodedJSValue JSC_HOST_CALL setProtoFuncSize(ExecState*);
55
56void SetPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
57{
58    Base::finishCreation(vm);
59    ASSERT(inherits(info()));
60    vm.prototypeMap.addPrototype(this);
61
62    JSC_NATIVE_FUNCTION(vm.propertyNames->add, setProtoFuncAdd, DontEnum, 1);
63    JSC_NATIVE_FUNCTION(vm.propertyNames->clear, setProtoFuncClear, DontEnum, 0);
64    JSC_NATIVE_FUNCTION(vm.propertyNames->deleteKeyword, setProtoFuncDelete, DontEnum, 1);
65    JSC_NATIVE_FUNCTION(vm.propertyNames->forEach, setProtoFuncForEach, DontEnum, 1);
66    JSC_NATIVE_FUNCTION(vm.propertyNames->has, setProtoFuncHas, DontEnum, 1);
67    JSC_NATIVE_FUNCTION(vm.propertyNames->keys, setProtoFuncKeys, DontEnum, 0);
68    JSC_NATIVE_FUNCTION(vm.propertyNames->values, setProtoFuncValues, DontEnum, 0);
69    JSC_NATIVE_FUNCTION(vm.propertyNames->entries, setProtoFuncEntries, DontEnum, 0);
70    JSC_NATIVE_FUNCTION(vm.propertyNames->iteratorPrivateName, setProtoFuncKeys, DontEnum, 0);
71
72    GetterSetter* accessor = GetterSetter::create(vm);
73    JSFunction* function = JSFunction::create(vm, globalObject, 0, vm.propertyNames->size.string(), setProtoFuncSize);
74    accessor->setGetter(vm, function);
75    putDirectNonIndexAccessor(vm, vm.propertyNames->size, accessor, DontEnum | Accessor);
76}
77
78ALWAYS_INLINE static MapData* getMapData(CallFrame* callFrame, JSValue thisValue)
79{
80    if (!thisValue.isObject()) {
81        throwVMError(callFrame, createNotAnObjectError(callFrame, thisValue));
82        return 0;
83    }
84    JSSet* set = jsDynamicCast<JSSet*>(thisValue);
85    if (!set) {
86        throwTypeError(callFrame, ASCIILiteral("Set operation called on non-Set object"));
87        return 0;
88    }
89    return set->mapData();
90}
91
92EncodedJSValue JSC_HOST_CALL setProtoFuncAdd(CallFrame* callFrame)
93{
94    MapData* data = getMapData(callFrame, callFrame->thisValue());
95    if (!data)
96        return JSValue::encode(jsUndefined());
97    data->set(callFrame, callFrame->argument(0), callFrame->argument(0));
98    return JSValue::encode(callFrame->thisValue());
99}
100
101EncodedJSValue JSC_HOST_CALL setProtoFuncClear(CallFrame* callFrame)
102{
103    MapData* data = getMapData(callFrame, callFrame->thisValue());
104    if (!data)
105        return JSValue::encode(jsUndefined());
106    data->clear();
107    return JSValue::encode(jsUndefined());
108}
109
110EncodedJSValue JSC_HOST_CALL setProtoFuncDelete(CallFrame* callFrame)
111{
112    MapData* data = getMapData(callFrame, callFrame->thisValue());
113    if (!data)
114        return JSValue::encode(jsUndefined());
115    return JSValue::encode(jsBoolean(data->remove(callFrame, callFrame->argument(0))));
116}
117
118EncodedJSValue JSC_HOST_CALL setProtoFuncForEach(CallFrame* callFrame)
119{
120    MapData* data = getMapData(callFrame, callFrame->thisValue());
121    if (!data)
122        return JSValue::encode(jsUndefined());
123    JSValue callBack = callFrame->argument(0);
124    CallData callData;
125    CallType callType = getCallData(callBack, callData);
126    if (callType == CallTypeNone)
127        return JSValue::encode(throwTypeError(callFrame, WTF::ASCIILiteral("Set.prototype.forEach called without callback")));
128    JSValue thisValue = callFrame->argument(1);
129    VM* vm = &callFrame->vm();
130    if (callType == CallTypeJS) {
131        JSFunction* function = jsCast<JSFunction*>(callBack);
132        CachedCall cachedCall(callFrame, function, 1);
133        for (auto ptr = data->begin(), end = data->end(); ptr != end && !vm->exception(); ++ptr) {
134            cachedCall.setThis(thisValue);
135            cachedCall.setArgument(0, ptr.key());
136            cachedCall.call();
137        }
138    } else {
139        for (auto ptr = data->begin(), end = data->end(); ptr != end && !vm->exception(); ++ptr) {
140            MarkedArgumentBuffer args;
141            args.append(ptr.key());
142            JSC::call(callFrame, callBack, callType, callData, thisValue, args);
143        }
144    }
145    return JSValue::encode(jsUndefined());
146}
147
148EncodedJSValue JSC_HOST_CALL setProtoFuncHas(CallFrame* callFrame)
149{
150    MapData* data = getMapData(callFrame, callFrame->thisValue());
151    if (!data)
152        return JSValue::encode(jsUndefined());
153    return JSValue::encode(jsBoolean(data->contains(callFrame, callFrame->argument(0))));
154}
155
156EncodedJSValue JSC_HOST_CALL setProtoFuncSize(CallFrame* callFrame)
157{
158    MapData* data = getMapData(callFrame, callFrame->thisValue());
159    if (!data)
160        return JSValue::encode(jsUndefined());
161    return JSValue::encode(jsNumber(data->size(callFrame)));
162}
163
164EncodedJSValue JSC_HOST_CALL setProtoFuncValues(CallFrame* callFrame)
165{
166    JSSet* thisObj = jsDynamicCast<JSSet*>(callFrame->thisValue());
167    if (!thisObj)
168        return JSValue::encode(throwTypeError(callFrame, ASCIILiteral("Cannot create a Map value iterator for a non-Map object.")));
169    return JSValue::encode(JSSetIterator::create(callFrame->vm(), callFrame->callee()->globalObject()->setIteratorStructure(), thisObj, SetIterateValue));
170}
171
172EncodedJSValue JSC_HOST_CALL setProtoFuncEntries(CallFrame* callFrame)
173{
174    JSSet* thisObj = jsDynamicCast<JSSet*>(callFrame->thisValue());
175    if (!thisObj)
176        return JSValue::encode(throwTypeError(callFrame, ASCIILiteral("Cannot create a Map key iterator for a non-Map object.")));
177    return JSValue::encode(JSSetIterator::create(callFrame->vm(), callFrame->callee()->globalObject()->setIteratorStructure(), thisObj, SetIterateKeyValue));
178}
179
180EncodedJSValue JSC_HOST_CALL setProtoFuncKeys(CallFrame* callFrame)
181{
182    JSSet* thisObj = jsDynamicCast<JSSet*>(callFrame->thisValue());
183    if (!thisObj)
184        return JSValue::encode(throwTypeError(callFrame, ASCIILiteral("Cannot create a Map entry iterator for a non-Map object.")));
185    return JSValue::encode(JSSetIterator::create(callFrame->vm(), callFrame->callee()->globalObject()->setIteratorStructure(), thisObj, SetIterateKey));
186}
187
188}
189