Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
3 : * This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "jsapi.h"
8 : #include "jscntxt.h"
9 : #include "jscompartment.h"
10 : #include "jsobj.h"
11 :
12 : #include "vm/Interpreter.h"
13 : #include "vm/PIC.h"
14 :
15 : #include "jsobjinlines.h"
16 :
17 : using namespace js;
18 : using JS::ForOfIterator;
19 :
20 : bool
21 4 : ForOfIterator::init(HandleValue iterable, NonIterableBehavior nonIterableBehavior)
22 : {
23 4 : JSContext* cx = cx_;
24 8 : RootedObject iterableObj(cx, ToObject(cx, iterable));
25 4 : if (!iterableObj)
26 0 : return false;
27 :
28 4 : MOZ_ASSERT(index == NOT_ARRAY);
29 :
30 : // Check the PIC first for a match.
31 4 : if (iterableObj->is<ArrayObject>()) {
32 3 : ForOfPIC::Chain* stubChain = ForOfPIC::getOrCreate(cx);
33 3 : if (!stubChain)
34 3 : return false;
35 :
36 : bool optimized;
37 3 : if (!stubChain->tryOptimizeArray(cx, iterableObj.as<ArrayObject>(), &optimized))
38 0 : return false;
39 :
40 3 : if (optimized) {
41 : // Got optimized stub. Array is optimizable.
42 3 : index = 0;
43 3 : iterator = iterableObj;
44 3 : return true;
45 : }
46 : }
47 :
48 1 : MOZ_ASSERT(index == NOT_ARRAY);
49 :
50 2 : RootedValue callee(cx);
51 2 : RootedId iteratorId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().iterator));
52 1 : if (!GetProperty(cx, iterableObj, iterableObj, iteratorId, &callee))
53 0 : return false;
54 :
55 : // If obj[@@iterator] is undefined and we were asked to allow non-iterables,
56 : // bail out now without setting iterator. This will make valueIsIterable(),
57 : // which our caller should check, return false.
58 1 : if (nonIterableBehavior == AllowNonIterable && callee.isUndefined())
59 0 : return true;
60 :
61 : // Throw if obj[@@iterator] isn't callable.
62 : // js::Invoke is about to check for this kind of error anyway, but it would
63 : // throw an inscrutable error message about |method| rather than this nice
64 : // one about |obj|.
65 1 : if (!callee.isObject() || !callee.toObject().isCallable()) {
66 0 : UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, iterable, nullptr);
67 0 : if (!bytes)
68 0 : return false;
69 0 : JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NOT_ITERABLE, bytes.get());
70 0 : return false;
71 : }
72 :
73 2 : RootedValue res(cx);
74 1 : if (!js::Call(cx, callee, iterable, &res))
75 0 : return false;
76 :
77 1 : if (!res.isObject())
78 0 : return ThrowCheckIsObject(cx, CheckIsObjectKind::GetIterator);
79 :
80 1 : iterator = &res.toObject();
81 1 : return true;
82 : }
83 :
84 : inline bool
85 8 : ForOfIterator::nextFromOptimizedArray(MutableHandleValue vp, bool* done)
86 : {
87 8 : MOZ_ASSERT(index != NOT_ARRAY);
88 :
89 8 : if (!CheckForInterrupt(cx_))
90 0 : return false;
91 :
92 8 : ArrayObject* arr = &iterator->as<ArrayObject>();
93 :
94 8 : if (index >= arr->length()) {
95 3 : vp.setUndefined();
96 3 : *done = true;
97 3 : return true;
98 : }
99 5 : *done = false;
100 :
101 : // Try to get array element via direct access.
102 5 : if (index < arr->getDenseInitializedLength()) {
103 5 : vp.set(arr->getDenseElement(index));
104 5 : if (!vp.isMagic(JS_ELEMENTS_HOLE)) {
105 5 : ++index;
106 5 : return true;
107 : }
108 : }
109 :
110 0 : return GetElement(cx_, iterator, iterator, index++, vp);
111 : }
112 :
113 : bool
114 12 : ForOfIterator::next(MutableHandleValue vp, bool* done)
115 : {
116 12 : MOZ_ASSERT(iterator);
117 12 : if (index != NOT_ARRAY) {
118 8 : ForOfPIC::Chain* stubChain = ForOfPIC::getOrCreate(cx_);
119 8 : if (!stubChain)
120 0 : return false;
121 :
122 8 : if (stubChain->isArrayNextStillSane())
123 8 : return nextFromOptimizedArray(vp, done);
124 :
125 : // ArrayIterator.prototype.next changed, materialize a proper
126 : // ArrayIterator instance and fall through to slowpath case.
127 0 : if (!materializeArrayIterator())
128 0 : return false;
129 : }
130 :
131 8 : RootedValue v(cx_);
132 4 : if (!GetProperty(cx_, iterator, iterator, cx_->names().next, &v))
133 0 : return false;
134 :
135 4 : if (!js::Call(cx_, v, iterator, &v))
136 0 : return false;
137 :
138 4 : if (!v.isObject())
139 0 : return ThrowCheckIsObject(cx_, CheckIsObjectKind::IteratorNext);
140 :
141 8 : RootedObject resultObj(cx_, &v.toObject());
142 4 : if (!GetProperty(cx_, resultObj, resultObj, cx_->names().done, &v))
143 0 : return false;
144 :
145 4 : *done = ToBoolean(v);
146 4 : if (*done) {
147 1 : vp.setUndefined();
148 1 : return true;
149 : }
150 :
151 3 : return GetProperty(cx_, resultObj, resultObj, cx_->names().value, vp);
152 : }
153 :
154 : // ES 2017 draft 0f10dba4ad18de92d47d421f378233a2eae8f077 7.4.6.
155 : // When completion.[[Type]] is throw.
156 : void
157 0 : ForOfIterator::closeThrow()
158 : {
159 0 : MOZ_ASSERT(iterator);
160 :
161 0 : RootedValue completionException(cx_);
162 0 : if (cx_->isExceptionPending()) {
163 0 : if (!GetAndClearException(cx_, &completionException))
164 0 : completionException.setUndefined();
165 : }
166 :
167 : // Steps 1-2 (implicit)
168 :
169 : // Step 3 (partial).
170 0 : RootedValue returnVal(cx_);
171 0 : if (!GetProperty(cx_, iterator, iterator, cx_->names().return_, &returnVal))
172 0 : return;
173 :
174 : // Step 4.
175 0 : if (returnVal.isUndefined()) {
176 0 : cx_->setPendingException(completionException);
177 0 : return;
178 : }
179 :
180 : // Step 3 (remaining part)
181 0 : if (!returnVal.isObject()) {
182 0 : JS_ReportErrorNumberASCII(cx_, GetErrorMessage, nullptr, JSMSG_RETURN_NOT_CALLABLE);
183 0 : return;
184 : }
185 0 : RootedObject returnObj(cx_, &returnVal.toObject());
186 0 : if (!returnObj->isCallable()) {
187 0 : JS_ReportErrorNumberASCII(cx_, GetErrorMessage, nullptr, JSMSG_RETURN_NOT_CALLABLE);
188 0 : return;
189 : }
190 :
191 : // Step 5.
192 0 : RootedValue innerResultValue(cx_);
193 0 : if (!js::Call(cx_, returnVal, iterator, &innerResultValue)) {
194 0 : if (cx_->isExceptionPending())
195 0 : cx_->clearPendingException();
196 : }
197 :
198 : // Step 6.
199 0 : cx_->setPendingException(completionException);
200 :
201 : // Steps 7-9 (skipped).
202 0 : return;
203 : }
204 :
205 : bool
206 0 : ForOfIterator::materializeArrayIterator()
207 : {
208 0 : MOZ_ASSERT(index != NOT_ARRAY);
209 :
210 0 : HandlePropertyName name = cx_->names().ArrayValuesAt;
211 0 : RootedValue val(cx_);
212 0 : if (!GlobalObject::getSelfHostedFunction(cx_, cx_->global(), name, name, 1, &val))
213 0 : return false;
214 :
215 0 : RootedValue indexOrRval(cx_, Int32Value(index));
216 0 : if (!js::Call(cx_, val, iterator, indexOrRval, &indexOrRval))
217 0 : return false;
218 :
219 0 : index = NOT_ARRAY;
220 : // Result of call to ArrayValuesAt must be an object.
221 0 : iterator = &indexOrRval.toObject();
222 0 : return true;
223 : }
|