1/*
2 * Copyright (C) 2015 Yusuke Suzuki <[email protected]>.
3 * Copyright (C) 2016-2017 Apple Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "config.h"
28#include "IteratorOperations.h"
29
30#include "CatchScope.h"
31#include "Error.h"
32#include "JSCInlines.h"
33#include "ObjectConstructor.h"
34
35namespace JSC {
36
37JSValue iteratorNext(JSGlobalObject* globalObject, IterationRecord iterationRecord, JSValue argument)
38{
39 VM& vm = globalObject->vm();
40 auto scope = DECLARE_THROW_SCOPE(vm);
41
42 JSValue iterator = iterationRecord.iterator;
43 JSValue nextFunction = iterationRecord.nextMethod;
44
45 CallData nextFunctionCallData;
46 CallType nextFunctionCallType = getCallData(vm, nextFunction, nextFunctionCallData);
47 if (nextFunctionCallType == CallType::None)
48 return throwTypeError(globalObject, scope);
49
50 MarkedArgumentBuffer nextFunctionArguments;
51 if (!argument.isEmpty())
52 nextFunctionArguments.append(argument);
53 ASSERT(!nextFunctionArguments.hasOverflowed());
54 JSValue result = call(globalObject, nextFunction, nextFunctionCallType, nextFunctionCallData, iterator, nextFunctionArguments);
55 RETURN_IF_EXCEPTION(scope, JSValue());
56
57 if (!result.isObject())
58 return throwTypeError(globalObject, scope, "Iterator result interface is not an object."_s);
59
60 return result;
61}
62
63JSValue iteratorValue(JSGlobalObject* globalObject, JSValue iterResult)
64{
65 return iterResult.get(globalObject, globalObject->vm().propertyNames->value);
66}
67
68bool iteratorComplete(JSGlobalObject* globalObject, JSValue iterResult)
69{
70 JSValue done = iterResult.get(globalObject, globalObject->vm().propertyNames->done);
71 return done.toBoolean(globalObject);
72}
73
74JSValue iteratorStep(JSGlobalObject* globalObject, IterationRecord iterationRecord)
75{
76 VM& vm = globalObject->vm();
77 auto scope = DECLARE_THROW_SCOPE(vm);
78
79 JSValue result = iteratorNext(globalObject, iterationRecord);
80 RETURN_IF_EXCEPTION(scope, JSValue());
81 bool done = iteratorComplete(globalObject, result);
82 RETURN_IF_EXCEPTION(scope, JSValue());
83 if (done)
84 return jsBoolean(false);
85 return result;
86}
87
88void iteratorClose(JSGlobalObject* globalObject, IterationRecord iterationRecord)
89{
90 VM& vm = globalObject->vm();
91 auto throwScope = DECLARE_THROW_SCOPE(vm);
92 auto catchScope = DECLARE_CATCH_SCOPE(vm);
93
94 Exception* exception = nullptr;
95 if (UNLIKELY(catchScope.exception())) {
96 exception = catchScope.exception();
97 catchScope.clearException();
98 }
99 JSValue returnFunction = iterationRecord.iterator.get(globalObject, vm.propertyNames->returnKeyword);
100 RETURN_IF_EXCEPTION(throwScope, void());
101
102 if (returnFunction.isUndefined()) {
103 if (exception)
104 throwException(globalObject, throwScope, exception);
105 return;
106 }
107
108 CallData returnFunctionCallData;
109 CallType returnFunctionCallType = getCallData(vm, returnFunction, returnFunctionCallData);
110 if (returnFunctionCallType == CallType::None) {
111 if (exception)
112 throwException(globalObject, throwScope, exception);
113 else
114 throwTypeError(globalObject, throwScope);
115 return;
116 }
117
118 MarkedArgumentBuffer returnFunctionArguments;
119 ASSERT(!returnFunctionArguments.hasOverflowed());
120 JSValue innerResult = call(globalObject, returnFunction, returnFunctionCallType, returnFunctionCallData, iterationRecord.iterator, returnFunctionArguments);
121
122 if (exception) {
123 throwException(globalObject, throwScope, exception);
124 return;
125 }
126
127 RETURN_IF_EXCEPTION(throwScope, void());
128
129 if (!innerResult.isObject()) {
130 throwTypeError(globalObject, throwScope, "Iterator result interface is not an object."_s);
131 return;
132 }
133}
134
135static const PropertyOffset valuePropertyOffset = 0;
136static const PropertyOffset donePropertyOffset = 1;
137
138Structure* createIteratorResultObjectStructure(VM& vm, JSGlobalObject& globalObject)
139{
140 Structure* iteratorResultStructure = vm.structureCache.emptyObjectStructureForPrototype(&globalObject, globalObject.objectPrototype(), JSFinalObject::defaultInlineCapacity());
141 PropertyOffset offset;
142 iteratorResultStructure = Structure::addPropertyTransition(vm, iteratorResultStructure, vm.propertyNames->value, 0, offset);
143 RELEASE_ASSERT(offset == valuePropertyOffset);
144 iteratorResultStructure = Structure::addPropertyTransition(vm, iteratorResultStructure, vm.propertyNames->done, 0, offset);
145 RELEASE_ASSERT(offset == donePropertyOffset);
146 return iteratorResultStructure;
147}
148
149JSObject* createIteratorResultObject(JSGlobalObject* globalObject, JSValue value, bool done)
150{
151 VM& vm = globalObject->vm();
152 JSObject* resultObject = constructEmptyObject(vm, globalObject->iteratorResultObjectStructure());
153 resultObject->putDirect(vm, valuePropertyOffset, value);
154 resultObject->putDirect(vm, donePropertyOffset, jsBoolean(done));
155 return resultObject;
156}
157
158bool hasIteratorMethod(JSGlobalObject* globalObject, JSValue value)
159{
160 auto& vm = globalObject->vm();
161 auto scope = DECLARE_THROW_SCOPE(vm);
162
163 if (!value.isObject())
164 return false;
165
166 JSObject* object = asObject(value);
167 CallData callData;
168 CallType callType;
169 JSValue applyMethod = object->getMethod(globalObject, callData, callType, vm.propertyNames->iteratorSymbol, "Symbol.iterator property should be callable"_s);
170 RETURN_IF_EXCEPTION(scope, false);
171
172 return !applyMethod.isUndefined();
173}
174
175JSValue iteratorMethod(JSGlobalObject* globalObject, JSObject* object)
176{
177 auto& vm = globalObject->vm();
178 auto scope = DECLARE_THROW_SCOPE(vm);
179
180 CallData callData;
181 CallType callType;
182 JSValue method = object->getMethod(globalObject, callData, callType, vm.propertyNames->iteratorSymbol, "Symbol.iterator property should be callable"_s);
183 RETURN_IF_EXCEPTION(scope, jsUndefined());
184
185 return method;
186}
187
188IterationRecord iteratorForIterable(JSGlobalObject* globalObject, JSObject* object, JSValue iteratorMethod)
189{
190 VM& vm = globalObject->vm();
191 auto scope = DECLARE_THROW_SCOPE(vm);
192
193 CallData iteratorMethodCallData;
194 CallType iteratorMethodCallType = getCallData(vm, iteratorMethod, iteratorMethodCallData);
195 if (iteratorMethodCallType == CallType::None) {
196 throwTypeError(globalObject, scope);
197 return { };
198 }
199
200 ArgList iteratorMethodArguments;
201 JSValue iterator = call(globalObject, iteratorMethod, iteratorMethodCallType, iteratorMethodCallData, object, iteratorMethodArguments);
202 RETURN_IF_EXCEPTION(scope, { });
203
204 if (!iterator.isObject()) {
205 throwTypeError(globalObject, scope);
206 return { };
207 }
208
209 JSValue nextMethod = iterator.getObject()->get(globalObject, vm.propertyNames->next);
210 RETURN_IF_EXCEPTION(scope, { });
211
212 return { iterator, nextMethod };
213}
214
215IterationRecord iteratorForIterable(JSGlobalObject* globalObject, JSValue iterable)
216{
217 VM& vm = globalObject->vm();
218 auto scope = DECLARE_THROW_SCOPE(vm);
219
220 JSValue iteratorFunction = iterable.get(globalObject, vm.propertyNames->iteratorSymbol);
221 RETURN_IF_EXCEPTION(scope, { });
222
223 CallData iteratorFunctionCallData;
224 CallType iteratorFunctionCallType = getCallData(vm, iteratorFunction, iteratorFunctionCallData);
225 if (iteratorFunctionCallType == CallType::None) {
226 throwTypeError(globalObject, scope);
227 return { };
228 }
229
230 ArgList iteratorFunctionArguments;
231 JSValue iterator = call(globalObject, iteratorFunction, iteratorFunctionCallType, iteratorFunctionCallData, iterable, iteratorFunctionArguments);
232 RETURN_IF_EXCEPTION(scope, { });
233
234 if (!iterator.isObject()) {
235 throwTypeError(globalObject, scope);
236 return { };
237 }
238
239 JSValue nextMethod = iterator.getObject()->get(globalObject, vm.propertyNames->next);
240 RETURN_IF_EXCEPTION(scope, { });
241
242 return { iterator, nextMethod };
243}
244
245} // namespace JSC
246