Speculatively change iteration protocall to use the same next function
[WebKit-https.git] / Source / JavaScriptCore / inspector / JSInjectedScriptHost.cpp
1 /*
2  * Copyright (C) 2013, 2015-2016 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "JSInjectedScriptHost.h"
28
29 #include "ArrayIteratorPrototype.h"
30 #include "BuiltinNames.h"
31 #include "Completion.h"
32 #include "DateInstance.h"
33 #include "DirectArguments.h"
34 #include "Error.h"
35 #include "InjectedScriptHost.h"
36 #include "IterationKind.h"
37 #include "IteratorOperations.h"
38 #include "IteratorPrototype.h"
39 #include "JSArray.h"
40 #include "JSBoundFunction.h"
41 #include "JSCInlines.h"
42 #include "JSFunction.h"
43 #include "JSGlobalObjectFunctions.h"
44 #include "JSInjectedScriptHostPrototype.h"
45 #include "JSMap.h"
46 #include "JSPromise.h"
47 #include "JSSet.h"
48 #include "JSStringIterator.h"
49 #include "JSTypedArrays.h"
50 #include "JSWeakMap.h"
51 #include "JSWeakSet.h"
52 #include "JSWithScope.h"
53 #include "MapIteratorPrototype.h"
54 #include "ObjectConstructor.h"
55 #include "ProxyObject.h"
56 #include "RegExpObject.h"
57 #include "ScopedArguments.h"
58 #include "SetIteratorPrototype.h"
59 #include "SourceCode.h"
60 #include "TypedArrayInlines.h"
61 #include "WeakMapBase.h"
62
63 using namespace JSC;
64
65 namespace Inspector {
66
67 const ClassInfo JSInjectedScriptHost::s_info = { "InjectedScriptHost", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSInjectedScriptHost) };
68
69 JSInjectedScriptHost::JSInjectedScriptHost(VM& vm, Structure* structure, Ref<InjectedScriptHost>&& impl)
70     : JSDestructibleObject(vm, structure)
71     , m_wrapped(WTFMove(impl))
72 {
73 }
74
75 void JSInjectedScriptHost::finishCreation(VM& vm)
76 {
77     Base::finishCreation(vm);
78     ASSERT(inherits(vm, info()));
79 }
80
81 JSObject* JSInjectedScriptHost::createPrototype(VM& vm, JSGlobalObject* globalObject)
82 {
83     return JSInjectedScriptHostPrototype::create(vm, globalObject, JSInjectedScriptHostPrototype::createStructure(vm, globalObject, globalObject->objectPrototype()));
84 }
85
86 void JSInjectedScriptHost::destroy(JSC::JSCell* cell)
87 {
88     JSInjectedScriptHost* thisObject = static_cast<JSInjectedScriptHost*>(cell);
89     thisObject->JSInjectedScriptHost::~JSInjectedScriptHost();
90 }
91
92 JSValue JSInjectedScriptHost::evaluate(ExecState* exec) const
93 {
94     JSGlobalObject* globalObject = exec->lexicalGlobalObject();
95     return globalObject->evalFunction();
96 }
97
98 JSValue JSInjectedScriptHost::evaluateWithScopeExtension(ExecState* exec)
99 {
100     VM& vm = exec->vm();
101     auto scope = DECLARE_THROW_SCOPE(vm);
102
103     JSValue scriptValue = exec->argument(0);
104     if (!scriptValue.isString())
105         return throwTypeError(exec, scope, ASCIILiteral("InjectedScriptHost.evaluateWithScopeExtension first argument must be a string."));
106
107     String program = asString(scriptValue)->value(exec);
108     RETURN_IF_EXCEPTION(scope, JSValue());
109
110     NakedPtr<Exception> exception;
111     JSObject* scopeExtension = exec->argument(1).getObject();
112     JSValue result = JSC::evaluateWithScopeExtension(exec, makeSource(program, exec->callerSourceOrigin()), scopeExtension, exception);
113     if (exception)
114         throwException(exec, scope, exception);
115
116     return result;
117 }
118
119 JSValue JSInjectedScriptHost::internalConstructorName(ExecState* exec)
120 {
121     if (exec->argumentCount() < 1)
122         return jsUndefined();
123
124     JSObject* object = jsCast<JSObject*>(exec->uncheckedArgument(0).toThis(exec, NotStrictMode));
125     return jsString(exec, JSObject::calculatedClassName(object));
126 }
127
128 JSValue JSInjectedScriptHost::isHTMLAllCollection(ExecState* exec)
129 {
130     if (exec->argumentCount() < 1)
131         return jsUndefined();
132
133     VM& vm = exec->vm();
134     JSValue value = exec->uncheckedArgument(0);
135     return jsBoolean(impl().isHTMLAllCollection(vm, value));
136 }
137
138 JSValue JSInjectedScriptHost::subtype(ExecState* exec)
139 {
140     VM& vm = exec->vm();
141     if (exec->argumentCount() < 1)
142         return jsUndefined();
143
144     JSValue value = exec->uncheckedArgument(0);
145     if (value.isString())
146         return vm.smallStrings.stringString();
147     if (value.isBoolean())
148         return vm.smallStrings.booleanString();
149     if (value.isNumber())
150         return vm.smallStrings.numberString();
151     if (value.isSymbol())
152         return vm.smallStrings.symbolString();
153
154     JSObject* object = asObject(value);
155     if (object) {
156         if (object->isErrorInstance())
157             return jsNontrivialString(exec, ASCIILiteral("error"));
158
159         // Consider class constructor functions class objects.
160         JSFunction* function = jsDynamicCast<JSFunction*>(vm, value);
161         if (function && function->isClassConstructorFunction())
162             return jsNontrivialString(exec, ASCIILiteral("class"));
163     }
164
165     if (value.inherits(vm, JSArray::info()))
166         return jsNontrivialString(exec, ASCIILiteral("array"));
167     if (value.inherits(vm, DirectArguments::info()) || value.inherits(vm, ScopedArguments::info()))
168         return jsNontrivialString(exec, ASCIILiteral("array"));
169
170     if (value.inherits(vm, DateInstance::info()))
171         return jsNontrivialString(exec, ASCIILiteral("date"));
172     if (value.inherits(vm, RegExpObject::info()))
173         return jsNontrivialString(exec, ASCIILiteral("regexp"));
174     if (value.inherits(vm, ProxyObject::info()))
175         return jsNontrivialString(exec, ASCIILiteral("proxy"));
176
177     if (value.inherits(vm, JSMap::info()))
178         return jsNontrivialString(exec, ASCIILiteral("map"));
179     if (value.inherits(vm, JSSet::info()))
180         return jsNontrivialString(exec, ASCIILiteral("set"));
181     if (value.inherits(vm, JSWeakMap::info()))
182         return jsNontrivialString(exec, ASCIILiteral("weakmap"));
183     if (value.inherits(vm, JSWeakSet::info()))
184         return jsNontrivialString(exec, ASCIILiteral("weakset"));
185
186     if (value.inherits(vm, JSStringIterator::info()))
187         return jsNontrivialString(exec, ASCIILiteral("iterator"));
188
189     if (object) {
190         if (object->getDirect(vm, vm.propertyNames->builtinNames().arrayIteratorNextIndexPrivateName())
191             || object->getDirect(vm, vm.propertyNames->builtinNames().mapBucketPrivateName())
192             || object->getDirect(vm, vm.propertyNames->builtinNames().setBucketPrivateName()))
193             return jsNontrivialString(exec, ASCIILiteral("iterator"));
194     }
195
196     if (value.inherits(vm, JSInt8Array::info())
197         || value.inherits(vm, JSInt16Array::info())
198         || value.inherits(vm, JSInt32Array::info())
199         || value.inherits(vm, JSUint8Array::info())
200         || value.inherits(vm, JSUint8ClampedArray::info())
201         || value.inherits(vm, JSUint16Array::info())
202         || value.inherits(vm, JSUint32Array::info())
203         || value.inherits(vm, JSFloat32Array::info())
204         || value.inherits(vm, JSFloat64Array::info()))
205         return jsNontrivialString(exec, ASCIILiteral("array"));
206
207     return impl().subtype(exec, value);
208 }
209
210 JSValue JSInjectedScriptHost::functionDetails(ExecState* exec)
211 {
212     if (exec->argumentCount() < 1)
213         return jsUndefined();
214
215     VM& vm = exec->vm();
216     JSValue value = exec->uncheckedArgument(0);
217     if (!value.asCell()->inherits(vm, JSFunction::info()))
218         return jsUndefined();
219
220     // FIXME: <https://webkit.org/b/87192> Web Inspector: Expose function scope / closure data
221
222     // FIXME: This should provide better details for JSBoundFunctions.
223
224     JSFunction* function = jsCast<JSFunction*>(value);
225     const SourceCode* sourceCode = function->sourceCode();
226     if (!sourceCode)
227         return jsUndefined();
228
229     // In the inspector protocol all positions are 0-based while in SourceCode they are 1-based
230     int lineNumber = sourceCode->firstLine().oneBasedInt();
231     if (lineNumber)
232         lineNumber -= 1;
233     int columnNumber = sourceCode->startColumn().oneBasedInt();
234     if (columnNumber)
235         columnNumber -= 1;
236
237     String scriptID = String::number(sourceCode->provider()->asID());
238     JSObject* location = constructEmptyObject(exec);
239     location->putDirect(vm, Identifier::fromString(exec, "scriptId"), jsString(exec, scriptID));
240     location->putDirect(vm, Identifier::fromString(exec, "lineNumber"), jsNumber(lineNumber));
241     location->putDirect(vm, Identifier::fromString(exec, "columnNumber"), jsNumber(columnNumber));
242
243     JSObject* result = constructEmptyObject(exec);
244     result->putDirect(vm, Identifier::fromString(exec, "location"), location);
245
246     String name = function->name(vm);
247     if (!name.isEmpty())
248         result->putDirect(vm, Identifier::fromString(exec, "name"), jsString(exec, name));
249
250     String displayName = function->displayName(vm);
251     if (!displayName.isEmpty())
252         result->putDirect(vm, Identifier::fromString(exec, "displayName"), jsString(exec, displayName));
253
254     return result;
255 }
256
257 static JSObject* constructInternalProperty(ExecState* exec, const String& name, JSValue value)
258 {
259     VM& vm = exec->vm();
260     JSObject* result = constructEmptyObject(exec);
261     result->putDirect(vm, Identifier::fromString(exec, "name"), jsString(exec, name));
262     result->putDirect(vm, Identifier::fromString(exec, "value"), value);
263     return result;
264 }
265
266 JSValue JSInjectedScriptHost::getInternalProperties(ExecState* exec)
267 {
268     if (exec->argumentCount() < 1)
269         return jsUndefined();
270
271     VM& vm = exec->vm();
272     auto scope = DECLARE_THROW_SCOPE(vm);
273     JSValue value = exec->uncheckedArgument(0);
274
275     if (JSPromise* promise = jsDynamicCast<JSPromise*>(vm, value)) {
276         unsigned index = 0;
277         JSArray* array = constructEmptyArray(exec, nullptr);
278         RETURN_IF_EXCEPTION(scope, JSValue());
279         switch (promise->status(vm)) {
280         case JSPromise::Status::Pending:
281             scope.release();
282             array->putDirectIndex(exec, index++, constructInternalProperty(exec, ASCIILiteral("status"), jsNontrivialString(exec, ASCIILiteral("pending"))));
283             return array;
284         case JSPromise::Status::Fulfilled:
285             array->putDirectIndex(exec, index++, constructInternalProperty(exec, ASCIILiteral("status"), jsNontrivialString(exec, ASCIILiteral("resolved"))));
286             RETURN_IF_EXCEPTION(scope, JSValue());
287             scope.release();
288             array->putDirectIndex(exec, index++, constructInternalProperty(exec, ASCIILiteral("result"), promise->result(vm)));
289             return array;
290         case JSPromise::Status::Rejected:
291             array->putDirectIndex(exec, index++, constructInternalProperty(exec, ASCIILiteral("status"), jsNontrivialString(exec, ASCIILiteral("rejected"))));
292             RETURN_IF_EXCEPTION(scope, JSValue());
293             scope.release();
294             array->putDirectIndex(exec, index++, constructInternalProperty(exec, ASCIILiteral("result"), promise->result(vm)));
295             return array;
296         }
297         // FIXME: <https://webkit.org/b/141664> Web Inspector: ES6: Improved Support for Promises - Promise Reactions
298         RELEASE_ASSERT_NOT_REACHED();
299     }
300
301     if (JSBoundFunction* boundFunction = jsDynamicCast<JSBoundFunction*>(vm, value)) {
302         unsigned index = 0;
303         JSArray* array = constructEmptyArray(exec, nullptr);
304         RETURN_IF_EXCEPTION(scope, JSValue());
305         array->putDirectIndex(exec, index++, constructInternalProperty(exec, "targetFunction", boundFunction->targetFunction()));
306         RETURN_IF_EXCEPTION(scope, JSValue());
307         array->putDirectIndex(exec, index++, constructInternalProperty(exec, "boundThis", boundFunction->boundThis()));
308         RETURN_IF_EXCEPTION(scope, JSValue());
309         if (boundFunction->boundArgs()) {
310             scope.release();
311             array->putDirectIndex(exec, index++, constructInternalProperty(exec, "boundArgs", boundFunction->boundArgsCopy(exec)));
312             return array;
313         }
314         return array;
315     }
316
317     if (ProxyObject* proxy = jsDynamicCast<ProxyObject*>(vm, value)) {
318         unsigned index = 0;
319         JSArray* array = constructEmptyArray(exec, nullptr, 2);
320         RETURN_IF_EXCEPTION(scope, JSValue());
321         array->putDirectIndex(exec, index++, constructInternalProperty(exec, ASCIILiteral("target"), proxy->target()));
322         RETURN_IF_EXCEPTION(scope, JSValue());
323         scope.release();
324         array->putDirectIndex(exec, index++, constructInternalProperty(exec, ASCIILiteral("handler"), proxy->handler()));
325         return array;
326     }
327
328     if (JSObject* iteratorObject = jsDynamicCast<JSObject*>(vm, value)) {
329         if (iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().arrayIteratorNextIndexPrivateName())) {
330             JSValue iteratedValue = iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().iteratedObjectPrivateName());
331             JSValue kind = iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().arrayIteratorKindPrivateName());
332
333             unsigned index = 0;
334             JSArray* array = constructEmptyArray(exec, nullptr, 2);
335             RETURN_IF_EXCEPTION(scope, JSValue());
336             array->putDirectIndex(exec, index++, constructInternalProperty(exec, "array", iteratedValue));
337             RETURN_IF_EXCEPTION(scope, JSValue());
338             scope.release();
339             array->putDirectIndex(exec, index++, constructInternalProperty(exec, "kind", kind));
340             return array;
341         }
342
343         if (iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().mapBucketPrivateName())) {
344             JSValue iteratedValue = iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().iteratedObjectPrivateName());
345             String kind;
346             switch (static_cast<IterationKind>(iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().mapIteratorKindPrivateName()).asInt32())) {
347             case IterateKey:
348                 kind = ASCIILiteral("key");
349                 break;
350             case IterateValue:
351                 kind = ASCIILiteral("value");
352                 break;
353             case IterateKeyValue:
354                 kind = ASCIILiteral("key+value");
355                 break;
356             }
357             unsigned index = 0;
358             JSArray* array = constructEmptyArray(exec, nullptr, 2);
359             RETURN_IF_EXCEPTION(scope, JSValue());
360             array->putDirectIndex(exec, index++, constructInternalProperty(exec, "map", iteratedValue));
361             RETURN_IF_EXCEPTION(scope, JSValue());
362             scope.release();
363             array->putDirectIndex(exec, index++, constructInternalProperty(exec, "kind", jsNontrivialString(exec, kind)));
364             return array;
365         }
366
367         if (iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().setBucketPrivateName())) {
368             JSValue iteratedValue = iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().iteratedObjectPrivateName());
369             String kind;
370             switch (static_cast<IterationKind>(iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().setIteratorKindPrivateName()).asInt32())) {
371             case IterateKey:
372                 kind = ASCIILiteral("key");
373                 break;
374             case IterateValue:
375                 kind = ASCIILiteral("value");
376                 break;
377             case IterateKeyValue:
378                 kind = ASCIILiteral("key+value");
379                 break;
380             }
381             unsigned index = 0;
382             JSArray* array = constructEmptyArray(exec, nullptr, 2);
383             RETURN_IF_EXCEPTION(scope, JSValue());
384             array->putDirectIndex(exec, index++, constructInternalProperty(exec, "set", iteratedValue));
385             RETURN_IF_EXCEPTION(scope, JSValue());
386             scope.release();
387             array->putDirectIndex(exec, index++, constructInternalProperty(exec, "kind", jsNontrivialString(exec, kind)));
388             return array;
389         }
390     }
391
392     if (JSStringIterator* stringIterator = jsDynamicCast<JSStringIterator*>(vm, value)) {
393         unsigned index = 0;
394         JSArray* array = constructEmptyArray(exec, nullptr, 1);
395         RETURN_IF_EXCEPTION(scope, JSValue());
396         scope.release();
397         array->putDirectIndex(exec, index++, constructInternalProperty(exec, "string", stringIterator->iteratedValue(exec)));
398         return array;
399     }
400
401     return jsUndefined();
402 }
403
404 JSValue JSInjectedScriptHost::proxyTargetValue(ExecState *exec)
405 {
406     if (exec->argumentCount() < 1)
407         return jsUndefined();
408
409     VM& vm = exec->vm();
410     JSValue value = exec->uncheckedArgument(0);
411     ProxyObject* proxy = jsDynamicCast<ProxyObject*>(vm, value);
412     if (!proxy)
413         return jsUndefined();
414
415     JSObject* target = proxy->target();
416     while (ProxyObject* proxy = jsDynamicCast<ProxyObject*>(vm, target))
417         target = proxy->target();
418
419     return target;
420 }
421
422 JSValue JSInjectedScriptHost::weakMapSize(ExecState* exec)
423 {
424     if (exec->argumentCount() < 1)
425         return jsUndefined();
426
427     VM& vm = exec->vm();
428     JSValue value = exec->uncheckedArgument(0);
429     JSWeakMap* weakMap = jsDynamicCast<JSWeakMap*>(vm, value);
430     if (!weakMap)
431         return jsUndefined();
432
433     return jsNumber(weakMap->size());
434 }
435
436 JSValue JSInjectedScriptHost::weakMapEntries(ExecState* exec)
437 {
438     if (exec->argumentCount() < 1)
439         return jsUndefined();
440
441     VM& vm = exec->vm();
442     auto scope = DECLARE_THROW_SCOPE(vm);
443     JSValue value = exec->uncheckedArgument(0);
444     JSWeakMap* weakMap = jsDynamicCast<JSWeakMap*>(vm, value);
445     if (!weakMap)
446         return jsUndefined();
447
448     unsigned fetched = 0;
449     unsigned numberToFetch = 100;
450
451     JSValue numberToFetchArg = exec->argument(1);
452     double fetchDouble = numberToFetchArg.toInteger(exec);
453     if (fetchDouble >= 0)
454         numberToFetch = static_cast<unsigned>(fetchDouble);
455
456     JSArray* array = constructEmptyArray(exec, nullptr);
457     RETURN_IF_EXCEPTION(scope, JSValue());
458     for (auto it = weakMap->begin(); it != weakMap->end(); ++it) {
459         JSObject* entry = constructEmptyObject(exec);
460         entry->putDirect(vm, Identifier::fromString(exec, "key"), it->key);
461         entry->putDirect(vm, Identifier::fromString(exec, "value"), it->value.get());
462         array->putDirectIndex(exec, fetched++, entry);
463         RETURN_IF_EXCEPTION(scope, JSValue());
464         if (numberToFetch && fetched >= numberToFetch)
465             break;
466     }
467
468     return array;
469 }
470
471 JSValue JSInjectedScriptHost::weakSetSize(ExecState* exec)
472 {
473     if (exec->argumentCount() < 1)
474         return jsUndefined();
475
476     VM& vm = exec->vm();
477     JSValue value = exec->uncheckedArgument(0);
478     JSWeakSet* weakSet = jsDynamicCast<JSWeakSet*>(vm, value);
479     if (!weakSet)
480         return jsUndefined();
481
482     return jsNumber(weakSet->size());
483 }
484
485 JSValue JSInjectedScriptHost::weakSetEntries(ExecState* exec)
486 {
487     if (exec->argumentCount() < 1)
488         return jsUndefined();
489
490     VM& vm = exec->vm();
491     auto scope = DECLARE_THROW_SCOPE(vm);
492     JSValue value = exec->uncheckedArgument(0);
493     JSWeakSet* weakSet = jsDynamicCast<JSWeakSet*>(vm, value);
494     if (!weakSet)
495         return jsUndefined();
496
497     unsigned fetched = 0;
498     unsigned numberToFetch = 100;
499
500     JSValue numberToFetchArg = exec->argument(1);
501     double fetchDouble = numberToFetchArg.toInteger(exec);
502     if (fetchDouble >= 0)
503         numberToFetch = static_cast<unsigned>(fetchDouble);
504
505     JSArray* array = constructEmptyArray(exec, nullptr);
506     RETURN_IF_EXCEPTION(scope, JSValue());
507     for (auto it = weakSet->begin(); it != weakSet->end(); ++it) {
508         JSObject* entry = constructEmptyObject(exec);
509         entry->putDirect(vm, Identifier::fromString(exec, "value"), it->key);
510         array->putDirectIndex(exec, fetched++, entry);
511         RETURN_IF_EXCEPTION(scope, JSValue());
512         if (numberToFetch && fetched >= numberToFetch)
513             break;
514     }
515
516     return array;
517 }
518
519 static JSObject* cloneArrayIteratorObject(ExecState* exec, VM& vm, JSObject* iteratorObject, JSGlobalObject* globalObject, JSValue nextIndex, JSValue iteratedObject)
520 {
521     ASSERT(iteratorObject->type() == FinalObjectType);
522     JSObject* clone = constructEmptyObject(exec, ArrayIteratorPrototype::create(vm, globalObject, ArrayIteratorPrototype::createStructure(vm, globalObject, globalObject->iteratorPrototype())));
523     clone->putDirect(vm, vm.propertyNames->builtinNames().iteratedObjectPrivateName(), iteratedObject);
524     clone->putDirect(vm, vm.propertyNames->builtinNames().arrayIteratorKindPrivateName(), iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().arrayIteratorKindPrivateName()));
525     clone->putDirect(vm, vm.propertyNames->builtinNames().arrayIteratorNextIndexPrivateName(), nextIndex);
526     clone->putDirect(vm, vm.propertyNames->builtinNames().arrayIteratorNextPrivateName(), iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().arrayIteratorNextPrivateName()));
527     clone->putDirect(vm, vm.propertyNames->builtinNames().arrayIteratorIsDonePrivateName(), iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().arrayIteratorIsDonePrivateName()));
528     return clone;
529 }
530
531 static JSObject* cloneMapIteratorObject(ExecState* exec, VM& vm, JSObject* iteratorObject, JSGlobalObject* globalObject, JSValue mapBucket, JSValue iteratedObject)
532 {
533     ASSERT(iteratorObject->type() == FinalObjectType);
534     JSObject* clone = constructEmptyObject(exec, MapIteratorPrototype::create(vm, globalObject, MapIteratorPrototype::createStructure(vm, globalObject, globalObject->iteratorPrototype())));
535     clone->putDirect(vm, vm.propertyNames->builtinNames().iteratedObjectPrivateName(), iteratedObject);
536     clone->putDirect(vm, vm.propertyNames->builtinNames().mapIteratorKindPrivateName(), iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().mapIteratorKindPrivateName()));
537     clone->putDirect(vm, vm.propertyNames->builtinNames().mapBucketPrivateName(), mapBucket);
538     return clone;
539 }
540
541 static JSObject* cloneSetIteratorObject(ExecState* exec, VM& vm, JSObject* iteratorObject, JSGlobalObject* globalObject, JSValue setBucket, JSValue iteratedObject)
542 {
543     ASSERT(iteratorObject->type() == FinalObjectType);
544     JSObject* clone = constructEmptyObject(exec, SetIteratorPrototype::create(vm, globalObject, SetIteratorPrototype::createStructure(vm, globalObject, globalObject->iteratorPrototype())));
545     clone->putDirect(vm, vm.propertyNames->builtinNames().iteratedObjectPrivateName(), iteratedObject);
546     clone->putDirect(vm, vm.propertyNames->builtinNames().setIteratorKindPrivateName(), iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().setIteratorKindPrivateName()));
547     clone->putDirect(vm, vm.propertyNames->builtinNames().setBucketPrivateName(), setBucket);
548     return clone;
549 }
550
551 JSValue JSInjectedScriptHost::iteratorEntries(ExecState* exec)
552 {
553     if (exec->argumentCount() < 1)
554         return jsUndefined();
555
556     VM& vm = exec->vm();
557     auto scope = DECLARE_THROW_SCOPE(vm);
558
559     JSValue iterator;
560     JSGlobalObject* globalObject = exec->lexicalGlobalObject();
561     JSValue value = exec->uncheckedArgument(0);
562     if (JSStringIterator* stringIterator = jsDynamicCast<JSStringIterator*>(vm, value)) {
563         if (globalObject->isStringPrototypeIteratorProtocolFastAndNonObservable())
564             iterator = stringIterator->clone(exec);
565     } else if (JSObject* iteratorObject = jsDynamicCast<JSObject*>(vm, value)) {
566         // Detect an ArrayIterator by checking for one of its unique private properties.
567         JSValue iteratedObject = iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().iteratedObjectPrivateName());
568         if (JSValue nextIndex = iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().arrayIteratorNextIndexPrivateName())) {
569             if (isJSArray(iteratedObject)) {
570                 JSArray* array = jsCast<JSArray*>(iteratedObject);
571                 if (array->isIteratorProtocolFastAndNonObservable())
572                     iterator = cloneArrayIteratorObject(exec, vm, iteratorObject, globalObject, nextIndex, iteratedObject);
573             } else if (iteratedObject.isObject() && TypeInfo::isArgumentsType(asObject(iteratedObject)->type())) {
574                 if (globalObject->isArrayPrototypeIteratorProtocolFastAndNonObservable())
575                     iterator = cloneArrayIteratorObject(exec, vm, iteratorObject, globalObject, nextIndex, iteratedObject);
576             }
577         } else if (JSValue mapBucket = iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().mapBucketPrivateName())) {
578             if (jsCast<JSMap*>(iteratedObject)->isIteratorProtocolFastAndNonObservable())
579                 iterator = cloneMapIteratorObject(exec, vm, iteratorObject, globalObject, mapBucket, iteratedObject);
580         } else if (JSValue setBucket = iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().setBucketPrivateName())) {
581             if (jsCast<JSSet*>(iteratedObject)->isIteratorProtocolFastAndNonObservable())
582                 iterator = cloneSetIteratorObject(exec, vm, iteratorObject, globalObject, setBucket, iteratedObject);
583         }
584     }
585     RETURN_IF_EXCEPTION(scope, { });
586     if (!iterator)
587         return jsUndefined();
588
589     IterationRecord iterationRecord = { iterator, iterator.get(exec, vm.propertyNames->next) };
590
591     unsigned numberToFetch = 5;
592     JSValue numberToFetchArg = exec->argument(1);
593     double fetchDouble = numberToFetchArg.toInteger(exec);
594     RETURN_IF_EXCEPTION(scope, { });
595     if (fetchDouble >= 0)
596         numberToFetch = static_cast<unsigned>(fetchDouble);
597
598     JSArray* array = constructEmptyArray(exec, nullptr);
599     RETURN_IF_EXCEPTION(scope, { });
600
601     for (unsigned i = 0; i < numberToFetch; ++i) {
602         JSValue next = iteratorStep(exec, iterationRecord);
603         if (UNLIKELY(scope.exception()) || next.isFalse())
604             break;
605
606         JSValue nextValue = iteratorValue(exec, next);
607         RETURN_IF_EXCEPTION(scope, { });
608
609         JSObject* entry = constructEmptyObject(exec);
610         entry->putDirect(vm, Identifier::fromString(exec, "value"), nextValue);
611         array->putDirectIndex(exec, i, entry);
612         if (UNLIKELY(scope.exception())) {
613             scope.release();
614             iteratorClose(exec, iterationRecord);
615             break;
616         }
617     }
618
619     return array;
620 }
621
622 } // namespace Inspector