1// Copyright 2012 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5"use strict";
6
7// This file relies on the fact that the following declaration has been made
8// in runtime.js:
9// var $Array = global.Array;
10
11var $Set = global.Set;
12var $Map = global.Map;
13
14
15// -------------------------------------------------------------------
16// Harmony Set
17
18function SetConstructor(iterable) {
19  if (!%_IsConstructCall()) {
20    throw MakeTypeError('constructor_not_function', ['Set']);
21  }
22
23  var iter, adder;
24
25  if (!IS_NULL_OR_UNDEFINED(iterable)) {
26    iter = GetIterator(ToObject(iterable));
27    adder = this.add;
28    if (!IS_SPEC_FUNCTION(adder)) {
29      throw MakeTypeError('property_not_function', ['add', this]);
30    }
31  }
32
33  %SetInitialize(this);
34
35  if (IS_UNDEFINED(iter)) return;
36
37  var next, done;
38  while (!(next = iter.next()).done) {
39    if (!IS_SPEC_OBJECT(next)) {
40      throw MakeTypeError('iterator_result_not_an_object', [next]);
41    }
42    %_CallFunction(this, next.value, adder);
43  }
44}
45
46
47function SetAddJS(key) {
48  if (!IS_SET(this)) {
49    throw MakeTypeError('incompatible_method_receiver',
50                        ['Set.prototype.add', this]);
51  }
52  // Normalize -0 to +0 as required by the spec.
53  // Even though we use SameValueZero as the comparison for the keys we don't
54  // want to ever store -0 as the key since the key is directly exposed when
55  // doing iteration.
56  if (key === 0) {
57    key = 0;
58  }
59  return %SetAdd(this, key);
60}
61
62
63function SetHasJS(key) {
64  if (!IS_SET(this)) {
65    throw MakeTypeError('incompatible_method_receiver',
66                        ['Set.prototype.has', this]);
67  }
68  return %SetHas(this, key);
69}
70
71
72function SetDeleteJS(key) {
73  if (!IS_SET(this)) {
74    throw MakeTypeError('incompatible_method_receiver',
75                        ['Set.prototype.delete', this]);
76  }
77  return %SetDelete(this, key);
78}
79
80
81function SetGetSizeJS() {
82  if (!IS_SET(this)) {
83    throw MakeTypeError('incompatible_method_receiver',
84                        ['Set.prototype.size', this]);
85  }
86  return %SetGetSize(this);
87}
88
89
90function SetClearJS() {
91  if (!IS_SET(this)) {
92    throw MakeTypeError('incompatible_method_receiver',
93                        ['Set.prototype.clear', this]);
94  }
95  %SetClear(this);
96}
97
98
99function SetForEach(f, receiver) {
100  if (!IS_SET(this)) {
101    throw MakeTypeError('incompatible_method_receiver',
102                        ['Set.prototype.forEach', this]);
103  }
104
105  if (!IS_SPEC_FUNCTION(f)) {
106    throw MakeTypeError('called_non_callable', [f]);
107  }
108
109  var iterator = new SetIterator(this, ITERATOR_KIND_VALUES);
110  var key;
111  var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f);
112  var value_array = [UNDEFINED];
113  while (%SetIteratorNext(iterator, value_array)) {
114    if (stepping) %DebugPrepareStepInIfStepping(f);
115    key = value_array[0];
116    %_CallFunction(receiver, key, key, this, f);
117  }
118}
119
120
121// -------------------------------------------------------------------
122
123function SetUpSet() {
124  %CheckIsBootstrapping();
125
126  %SetCode($Set, SetConstructor);
127  %FunctionSetPrototype($Set, new $Object());
128  %AddNamedProperty($Set.prototype, "constructor", $Set, DONT_ENUM);
129
130  %FunctionSetLength(SetForEach, 1);
131
132  // Set up the non-enumerable functions on the Set prototype object.
133  InstallGetter($Set.prototype, "size", SetGetSizeJS);
134  InstallFunctions($Set.prototype, DONT_ENUM, $Array(
135    "add", SetAddJS,
136    "has", SetHasJS,
137    "delete", SetDeleteJS,
138    "clear", SetClearJS,
139    "forEach", SetForEach
140  ));
141}
142
143SetUpSet();
144
145
146// -------------------------------------------------------------------
147// Harmony Map
148
149function MapConstructor(iterable) {
150  if (!%_IsConstructCall()) {
151    throw MakeTypeError('constructor_not_function', ['Map']);
152  }
153
154  var iter, adder;
155
156  if (!IS_NULL_OR_UNDEFINED(iterable)) {
157    iter = GetIterator(ToObject(iterable));
158    adder = this.set;
159    if (!IS_SPEC_FUNCTION(adder)) {
160      throw MakeTypeError('property_not_function', ['set', this]);
161    }
162  }
163
164  %MapInitialize(this);
165
166  if (IS_UNDEFINED(iter)) return;
167
168  var next, done, nextItem;
169  while (!(next = iter.next()).done) {
170    if (!IS_SPEC_OBJECT(next)) {
171      throw MakeTypeError('iterator_result_not_an_object', [next]);
172    }
173    nextItem = next.value;
174    if (!IS_SPEC_OBJECT(nextItem)) {
175      throw MakeTypeError('iterator_value_not_an_object', [nextItem]);
176    }
177    %_CallFunction(this, nextItem[0], nextItem[1], adder);
178  }
179}
180
181
182function MapGetJS(key) {
183  if (!IS_MAP(this)) {
184    throw MakeTypeError('incompatible_method_receiver',
185                        ['Map.prototype.get', this]);
186  }
187  return %MapGet(this, key);
188}
189
190
191function MapSetJS(key, value) {
192  if (!IS_MAP(this)) {
193    throw MakeTypeError('incompatible_method_receiver',
194                        ['Map.prototype.set', this]);
195  }
196  // Normalize -0 to +0 as required by the spec.
197  // Even though we use SameValueZero as the comparison for the keys we don't
198  // want to ever store -0 as the key since the key is directly exposed when
199  // doing iteration.
200  if (key === 0) {
201    key = 0;
202  }
203  return %MapSet(this, key, value);
204}
205
206
207function MapHasJS(key) {
208  if (!IS_MAP(this)) {
209    throw MakeTypeError('incompatible_method_receiver',
210                        ['Map.prototype.has', this]);
211  }
212  return %MapHas(this, key);
213}
214
215
216function MapDeleteJS(key) {
217  if (!IS_MAP(this)) {
218    throw MakeTypeError('incompatible_method_receiver',
219                        ['Map.prototype.delete', this]);
220  }
221  return %MapDelete(this, key);
222}
223
224
225function MapGetSizeJS() {
226  if (!IS_MAP(this)) {
227    throw MakeTypeError('incompatible_method_receiver',
228                        ['Map.prototype.size', this]);
229  }
230  return %MapGetSize(this);
231}
232
233
234function MapClearJS() {
235  if (!IS_MAP(this)) {
236    throw MakeTypeError('incompatible_method_receiver',
237                        ['Map.prototype.clear', this]);
238  }
239  %MapClear(this);
240}
241
242
243function MapForEach(f, receiver) {
244  if (!IS_MAP(this)) {
245    throw MakeTypeError('incompatible_method_receiver',
246                        ['Map.prototype.forEach', this]);
247  }
248
249  if (!IS_SPEC_FUNCTION(f)) {
250    throw MakeTypeError('called_non_callable', [f]);
251  }
252
253  var iterator = new MapIterator(this, ITERATOR_KIND_ENTRIES);
254  var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f);
255  var value_array = [UNDEFINED, UNDEFINED];
256  while (%MapIteratorNext(iterator, value_array)) {
257    if (stepping) %DebugPrepareStepInIfStepping(f);
258    %_CallFunction(receiver, value_array[1], value_array[0], this, f);
259  }
260}
261
262
263// -------------------------------------------------------------------
264
265function SetUpMap() {
266  %CheckIsBootstrapping();
267
268  %SetCode($Map, MapConstructor);
269  %FunctionSetPrototype($Map, new $Object());
270  %AddNamedProperty($Map.prototype, "constructor", $Map, DONT_ENUM);
271
272  %FunctionSetLength(MapForEach, 1);
273
274  // Set up the non-enumerable functions on the Map prototype object.
275  InstallGetter($Map.prototype, "size", MapGetSizeJS);
276  InstallFunctions($Map.prototype, DONT_ENUM, $Array(
277    "get", MapGetJS,
278    "set", MapSetJS,
279    "has", MapHasJS,
280    "delete", MapDeleteJS,
281    "clear", MapClearJS,
282    "forEach", MapForEach
283  ));
284}
285
286SetUpMap();
287