1// Copyright 2012 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6//     * Redistributions of source code must retain the above copyright
7//       notice, this list of conditions and the following disclaimer.
8//     * Redistributions in binary form must reproduce the above
9//       copyright notice, this list of conditions and the following
10//       disclaimer in the documentation and/or other materials provided
11//       with the distribution.
12//     * Neither the name of Google Inc. nor the names of its
13//       contributors may be used to endorse or promote products derived
14//       from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28"use strict";
29
30// This file relies on the fact that the following declaration has been made
31// in runtime.js:
32// var $Array = global.Array;
33
34var $Set = global.Set;
35var $Map = global.Map;
36var $WeakMap = global.WeakMap;
37var $WeakSet = global.WeakSet;
38
39// Global sentinel to be used instead of undefined keys, which are not
40// supported internally but required for Harmony sets and maps.
41var undefined_sentinel = {};
42
43// -------------------------------------------------------------------
44// Harmony Set
45
46function SetConstructor() {
47  if (%_IsConstructCall()) {
48    %SetInitialize(this);
49  } else {
50    throw MakeTypeError('constructor_not_function', ['Set']);
51  }
52}
53
54
55function SetAdd(key) {
56  if (!IS_SET(this)) {
57    throw MakeTypeError('incompatible_method_receiver',
58                        ['Set.prototype.add', this]);
59  }
60  if (IS_UNDEFINED(key)) {
61    key = undefined_sentinel;
62  }
63  return %SetAdd(this, key);
64}
65
66
67function SetHas(key) {
68  if (!IS_SET(this)) {
69    throw MakeTypeError('incompatible_method_receiver',
70                        ['Set.prototype.has', this]);
71  }
72  if (IS_UNDEFINED(key)) {
73    key = undefined_sentinel;
74  }
75  return %SetHas(this, key);
76}
77
78
79function SetDelete(key) {
80  if (!IS_SET(this)) {
81    throw MakeTypeError('incompatible_method_receiver',
82                        ['Set.prototype.delete', this]);
83  }
84  if (IS_UNDEFINED(key)) {
85    key = undefined_sentinel;
86  }
87  if (%SetHas(this, key)) {
88    %SetDelete(this, key);
89    return true;
90  } else {
91    return false;
92  }
93}
94
95
96function SetGetSize() {
97  if (!IS_SET(this)) {
98    throw MakeTypeError('incompatible_method_receiver',
99                        ['Set.prototype.size', this]);
100  }
101  return %SetGetSize(this);
102}
103
104
105function SetClear() {
106  if (!IS_SET(this)) {
107    throw MakeTypeError('incompatible_method_receiver',
108                        ['Set.prototype.clear', this]);
109  }
110  // Replace the internal table with a new empty table.
111  %SetInitialize(this);
112}
113
114
115// -------------------------------------------------------------------
116
117function SetUpSet() {
118  %CheckIsBootstrapping();
119
120  %SetCode($Set, SetConstructor);
121  %FunctionSetPrototype($Set, new $Object());
122  %SetProperty($Set.prototype, "constructor", $Set, DONT_ENUM);
123
124  // Set up the non-enumerable functions on the Set prototype object.
125  InstallGetter($Set.prototype, "size", SetGetSize);
126  InstallFunctions($Set.prototype, DONT_ENUM, $Array(
127    "add", SetAdd,
128    "has", SetHas,
129    "delete", SetDelete,
130    "clear", SetClear
131  ));
132}
133
134SetUpSet();
135
136
137// -------------------------------------------------------------------
138// Harmony Map
139
140function MapConstructor() {
141  if (%_IsConstructCall()) {
142    %MapInitialize(this);
143  } else {
144    throw MakeTypeError('constructor_not_function', ['Map']);
145  }
146}
147
148
149function MapGet(key) {
150  if (!IS_MAP(this)) {
151    throw MakeTypeError('incompatible_method_receiver',
152                        ['Map.prototype.get', this]);
153  }
154  if (IS_UNDEFINED(key)) {
155    key = undefined_sentinel;
156  }
157  return %MapGet(this, key);
158}
159
160
161function MapSet(key, value) {
162  if (!IS_MAP(this)) {
163    throw MakeTypeError('incompatible_method_receiver',
164                        ['Map.prototype.set', this]);
165  }
166  if (IS_UNDEFINED(key)) {
167    key = undefined_sentinel;
168  }
169  return %MapSet(this, key, value);
170}
171
172
173function MapHas(key) {
174  if (!IS_MAP(this)) {
175    throw MakeTypeError('incompatible_method_receiver',
176                        ['Map.prototype.has', this]);
177  }
178  if (IS_UNDEFINED(key)) {
179    key = undefined_sentinel;
180  }
181  return %MapHas(this, key);
182}
183
184
185function MapDelete(key) {
186  if (!IS_MAP(this)) {
187    throw MakeTypeError('incompatible_method_receiver',
188                        ['Map.prototype.delete', this]);
189  }
190  if (IS_UNDEFINED(key)) {
191    key = undefined_sentinel;
192  }
193  return %MapDelete(this, key);
194}
195
196
197function MapGetSize() {
198  if (!IS_MAP(this)) {
199    throw MakeTypeError('incompatible_method_receiver',
200                        ['Map.prototype.size', this]);
201  }
202  return %MapGetSize(this);
203}
204
205
206function MapClear() {
207  if (!IS_MAP(this)) {
208    throw MakeTypeError('incompatible_method_receiver',
209                        ['Map.prototype.clear', this]);
210  }
211  // Replace the internal table with a new empty table.
212  %MapInitialize(this);
213}
214
215
216// -------------------------------------------------------------------
217
218function SetUpMap() {
219  %CheckIsBootstrapping();
220
221  %SetCode($Map, MapConstructor);
222  %FunctionSetPrototype($Map, new $Object());
223  %SetProperty($Map.prototype, "constructor", $Map, DONT_ENUM);
224
225  // Set up the non-enumerable functions on the Map prototype object.
226  InstallGetter($Map.prototype, "size", MapGetSize);
227  InstallFunctions($Map.prototype, DONT_ENUM, $Array(
228    "get", MapGet,
229    "set", MapSet,
230    "has", MapHas,
231    "delete", MapDelete,
232    "clear", MapClear
233  ));
234}
235
236SetUpMap();
237
238
239// -------------------------------------------------------------------
240// Harmony WeakMap
241
242function WeakMapConstructor() {
243  if (%_IsConstructCall()) {
244    %WeakCollectionInitialize(this);
245  } else {
246    throw MakeTypeError('constructor_not_function', ['WeakMap']);
247  }
248}
249
250
251function WeakMapGet(key) {
252  if (!IS_WEAKMAP(this)) {
253    throw MakeTypeError('incompatible_method_receiver',
254                        ['WeakMap.prototype.get', this]);
255  }
256  if (!(IS_SPEC_OBJECT(key) || IS_SYMBOL(key))) {
257    throw %MakeTypeError('invalid_weakmap_key', [this, key]);
258  }
259  return %WeakCollectionGet(this, key);
260}
261
262
263function WeakMapSet(key, value) {
264  if (!IS_WEAKMAP(this)) {
265    throw MakeTypeError('incompatible_method_receiver',
266                        ['WeakMap.prototype.set', this]);
267  }
268  if (!(IS_SPEC_OBJECT(key) || IS_SYMBOL(key))) {
269    throw %MakeTypeError('invalid_weakmap_key', [this, key]);
270  }
271  return %WeakCollectionSet(this, key, value);
272}
273
274
275function WeakMapHas(key) {
276  if (!IS_WEAKMAP(this)) {
277    throw MakeTypeError('incompatible_method_receiver',
278                        ['WeakMap.prototype.has', this]);
279  }
280  if (!(IS_SPEC_OBJECT(key) || IS_SYMBOL(key))) {
281    throw %MakeTypeError('invalid_weakmap_key', [this, key]);
282  }
283  return %WeakCollectionHas(this, key);
284}
285
286
287function WeakMapDelete(key) {
288  if (!IS_WEAKMAP(this)) {
289    throw MakeTypeError('incompatible_method_receiver',
290                        ['WeakMap.prototype.delete', this]);
291  }
292  if (!(IS_SPEC_OBJECT(key) || IS_SYMBOL(key))) {
293    throw %MakeTypeError('invalid_weakmap_key', [this, key]);
294  }
295  return %WeakCollectionDelete(this, key);
296}
297
298
299function WeakMapClear() {
300  if (!IS_WEAKMAP(this)) {
301    throw MakeTypeError('incompatible_method_receiver',
302                        ['WeakMap.prototype.clear', this]);
303  }
304  // Replace the internal table with a new empty table.
305  %WeakCollectionInitialize(this);
306}
307
308
309// -------------------------------------------------------------------
310
311function SetUpWeakMap() {
312  %CheckIsBootstrapping();
313
314  %SetCode($WeakMap, WeakMapConstructor);
315  %FunctionSetPrototype($WeakMap, new $Object());
316  %SetProperty($WeakMap.prototype, "constructor", $WeakMap, DONT_ENUM);
317
318  // Set up the non-enumerable functions on the WeakMap prototype object.
319  InstallFunctions($WeakMap.prototype, DONT_ENUM, $Array(
320    "get", WeakMapGet,
321    "set", WeakMapSet,
322    "has", WeakMapHas,
323    "delete", WeakMapDelete,
324    "clear", WeakMapClear
325  ));
326}
327
328SetUpWeakMap();
329
330
331// -------------------------------------------------------------------
332// Harmony WeakSet
333
334function WeakSetConstructor() {
335  if (%_IsConstructCall()) {
336    %WeakCollectionInitialize(this);
337  } else {
338    throw MakeTypeError('constructor_not_function', ['WeakSet']);
339  }
340}
341
342
343function WeakSetAdd(value) {
344  if (!IS_WEAKSET(this)) {
345    throw MakeTypeError('incompatible_method_receiver',
346                        ['WeakSet.prototype.add', this]);
347  }
348  if (!(IS_SPEC_OBJECT(value) || IS_SYMBOL(value))) {
349    throw %MakeTypeError('invalid_weakset_value', [this, value]);
350  }
351  return %WeakCollectionSet(this, value, true);
352}
353
354
355function WeakSetHas(value) {
356  if (!IS_WEAKSET(this)) {
357    throw MakeTypeError('incompatible_method_receiver',
358                        ['WeakSet.prototype.has', this]);
359  }
360  if (!(IS_SPEC_OBJECT(value) || IS_SYMBOL(value))) {
361    throw %MakeTypeError('invalid_weakset_value', [this, value]);
362  }
363  return %WeakCollectionHas(this, value);
364}
365
366
367function WeakSetDelete(value) {
368  if (!IS_WEAKSET(this)) {
369    throw MakeTypeError('incompatible_method_receiver',
370                        ['WeakSet.prototype.delete', this]);
371  }
372  if (!(IS_SPEC_OBJECT(value) || IS_SYMBOL(value))) {
373    throw %MakeTypeError('invalid_weakset_value', [this, value]);
374  }
375  return %WeakCollectionDelete(this, value);
376}
377
378
379function WeakSetClear() {
380  if (!IS_WEAKSET(this)) {
381    throw MakeTypeError('incompatible_method_receiver',
382                        ['WeakSet.prototype.clear', this]);
383  }
384  // Replace the internal table with a new empty table.
385  %WeakCollectionInitialize(this);
386}
387
388
389// -------------------------------------------------------------------
390
391function SetUpWeakSet() {
392  %CheckIsBootstrapping();
393
394  %SetCode($WeakSet, WeakSetConstructor);
395  %FunctionSetPrototype($WeakSet, new $Object());
396  %SetProperty($WeakSet.prototype, "constructor", $WeakSet, DONT_ENUM);
397
398  // Set up the non-enumerable functions on the WeakSet prototype object.
399  InstallFunctions($WeakSet.prototype, DONT_ENUM, $Array(
400    "add", WeakSetAdd,
401    "has", WeakSetHas,
402    "delete", WeakSetDelete,
403    "clear", WeakSetClear
404  ));
405}
406
407SetUpWeakSet();
408