Speculatively change iteration protocall to use the same next function
[WebKit-https.git] / Source / JavaScriptCore / runtime / IteratorOperations.cpp
1 /*
2  * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>.
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
35 using namespace WTF;
36
37 namespace JSC {
38
39 JSValue iteratorNext(ExecState* exec, IterationRecord iterationRecord, JSValue argument)
40 {
41     VM& vm = exec->vm();
42     auto scope = DECLARE_THROW_SCOPE(vm);
43
44     JSValue iterator = iterationRecord.iterator;
45     JSValue nextFunction = iterationRecord.nextMethod;
46
47     CallData nextFunctionCallData;
48     CallType nextFunctionCallType = getCallData(nextFunction, nextFunctionCallData);
49     if (nextFunctionCallType == CallType::None)
50         return throwTypeError(exec, scope);
51
52     MarkedArgumentBuffer nextFunctionArguments;
53     if (!argument.isEmpty())
54         nextFunctionArguments.append(argument);
55     JSValue result = call(exec, nextFunction, nextFunctionCallType, nextFunctionCallData, iterator, nextFunctionArguments);
56     RETURN_IF_EXCEPTION(scope, JSValue());
57
58     if (!result.isObject())
59         return throwTypeError(exec, scope, ASCIILiteral("Iterator result interface is not an object."));
60
61     return result;
62 }
63
64 JSValue iteratorValue(ExecState* exec, JSValue iterResult)
65 {
66     return iterResult.get(exec, exec->vm().propertyNames->value);
67 }
68
69 bool iteratorComplete(ExecState* exec, JSValue iterResult)
70 {
71     JSValue done = iterResult.get(exec, exec->vm().propertyNames->done);
72     return done.toBoolean(exec);
73 }
74
75 JSValue iteratorStep(ExecState* exec, IterationRecord iterationRecord)
76 {
77     VM& vm = exec->vm();
78     auto scope = DECLARE_THROW_SCOPE(vm);
79
80     JSValue result = iteratorNext(exec, iterationRecord);
81     RETURN_IF_EXCEPTION(scope, JSValue());
82     bool done = iteratorComplete(exec, result);
83     RETURN_IF_EXCEPTION(scope, JSValue());
84     if (done)
85         return jsBoolean(false);
86     return result;
87 }
88
89 void iteratorClose(ExecState* exec, IterationRecord iterationRecord)
90 {
91     VM& vm = exec->vm();
92     auto throwScope = DECLARE_THROW_SCOPE(vm);
93     auto catchScope = DECLARE_CATCH_SCOPE(vm);
94
95     Exception* exception = nullptr;
96     if (UNLIKELY(catchScope.exception())) {
97         exception = catchScope.exception();
98         catchScope.clearException();
99     }
100     JSValue returnFunction = iterationRecord.iterator.get(exec, vm.propertyNames->returnKeyword);
101     RETURN_IF_EXCEPTION(throwScope, void());
102
103     if (returnFunction.isUndefined()) {
104         if (exception)
105             throwException(exec, throwScope, exception);
106         return;
107     }
108
109     CallData returnFunctionCallData;
110     CallType returnFunctionCallType = getCallData(returnFunction, returnFunctionCallData);
111     if (returnFunctionCallType == CallType::None) {
112         if (exception)
113             throwException(exec, throwScope, exception);
114         else
115             throwTypeError(exec, throwScope);
116         return;
117     }
118
119     MarkedArgumentBuffer returnFunctionArguments;
120     JSValue innerResult = call(exec, returnFunction, returnFunctionCallType, returnFunctionCallData, iterationRecord.iterator, returnFunctionArguments);
121
122     if (exception) {
123         throwException(exec, throwScope, exception);
124         return;
125     }
126
127     RETURN_IF_EXCEPTION(throwScope, void());
128
129     if (!innerResult.isObject()) {
130         throwTypeError(exec, throwScope, ASCIILiteral("Iterator result interface is not an object."));
131         return;
132     }
133 }
134
135 static const PropertyOffset donePropertyOffset = 0;
136 static const PropertyOffset valuePropertyOffset = 1;
137
138 Structure* createIteratorResultObjectStructure(VM& vm, JSGlobalObject& globalObject)
139 {
140     Structure* iteratorResultStructure = vm.prototypeMap.emptyObjectStructureForPrototype(&globalObject, globalObject.objectPrototype(), JSFinalObject::defaultInlineCapacity());
141     PropertyOffset offset;
142     iteratorResultStructure = Structure::addPropertyTransition(vm, iteratorResultStructure, vm.propertyNames->done, 0, offset);
143     RELEASE_ASSERT(offset == donePropertyOffset);
144     iteratorResultStructure = Structure::addPropertyTransition(vm, iteratorResultStructure, vm.propertyNames->value, 0, offset);
145     RELEASE_ASSERT(offset == valuePropertyOffset);
146     return iteratorResultStructure;
147 }
148
149 JSObject* createIteratorResultObject(ExecState* exec, JSValue value, bool done)
150 {
151     VM& vm = exec->vm();
152     JSObject* resultObject = constructEmptyObject(exec, exec->lexicalGlobalObject()->iteratorResultObjectStructure());
153     resultObject->putDirect(vm, donePropertyOffset, jsBoolean(done));
154     resultObject->putDirect(vm, valuePropertyOffset, value);
155     return resultObject;
156 }
157
158 bool hasIteratorMethod(ExecState& state, JSValue value)
159 {
160     auto& vm = state.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(&state, callData, callType, vm.propertyNames->iteratorSymbol, ASCIILiteral("Symbol.iterator property should be callable"));
170     RETURN_IF_EXCEPTION(scope, false);
171
172     return !applyMethod.isUndefined();
173 }
174
175 JSValue iteratorMethod(ExecState& state, JSObject* object)
176 {
177     auto& vm = state.vm();
178     auto scope = DECLARE_THROW_SCOPE(vm);
179
180     CallData callData;
181     CallType callType;
182     JSValue method = object->getMethod(&state, callData, callType, vm.propertyNames->iteratorSymbol, ASCIILiteral("Symbol.iterator property should be callable"));
183     RETURN_IF_EXCEPTION(scope, jsUndefined());
184
185     return method;
186 }
187
188 IterationRecord iteratorForIterable(ExecState& state, JSObject* object, JSValue iteratorMethod)
189 {
190     VM& vm = state.vm();
191     auto scope = DECLARE_THROW_SCOPE(vm);
192
193     CallData iteratorMethodCallData;
194     CallType iteratorMethodCallType = getCallData(iteratorMethod, iteratorMethodCallData);
195     if (iteratorMethodCallType == CallType::None) {
196         throwTypeError(&state, scope);
197         return { };
198     }
199
200     ArgList iteratorMethodArguments;
201     JSValue iterator = call(&state, iteratorMethod, iteratorMethodCallType, iteratorMethodCallData, object, iteratorMethodArguments);
202     RETURN_IF_EXCEPTION(scope, { });
203
204     if (!iterator.isObject()) {
205         throwTypeError(&state, scope);
206         return { };
207     }
208
209     JSValue nextMethod = iterator.getObject()->get(&state, vm.propertyNames->next);
210     RETURN_IF_EXCEPTION(scope, { });
211
212     return { iterator, nextMethod };
213 }
214
215 IterationRecord iteratorForIterable(ExecState* state, JSValue iterable)
216 {
217     VM& vm = state->vm();
218     auto scope = DECLARE_THROW_SCOPE(vm);
219     
220     JSValue iteratorFunction = iterable.get(state, vm.propertyNames->iteratorSymbol);
221     RETURN_IF_EXCEPTION(scope, { });
222     
223     CallData iteratorFunctionCallData;
224     CallType iteratorFunctionCallType = getCallData(iteratorFunction, iteratorFunctionCallData);
225     if (iteratorFunctionCallType == CallType::None) {
226         throwTypeError(state, scope);
227         return { };
228     }
229
230     ArgList iteratorFunctionArguments;
231     JSValue iterator = call(state, iteratorFunction, iteratorFunctionCallType, iteratorFunctionCallData, iterable, iteratorFunctionArguments);
232     RETURN_IF_EXCEPTION(scope, { });
233
234     if (!iterator.isObject()) {
235         throwTypeError(state, scope);
236         return { };
237     }
238
239     JSValue nextMethod = iterator.getObject()->get(state, vm.propertyNames->next);
240     RETURN_IF_EXCEPTION(scope, { });
241
242     return { iterator, nextMethod };
243 }
244
245 } // namespace JSC