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 "ArrayPrototype.h"
31#include "BuiltinNames.h"
32#include "Completion.h"
33#include "DateInstance.h"
34#include "DirectArguments.h"
35#include "Error.h"
36#include "FunctionPrototype.h"
37#include "HeapIterationScope.h"
38#include "InjectedScriptHost.h"
39#include "IterationKind.h"
40#include "IteratorOperations.h"
41#include "IteratorPrototype.h"
42#include "JSArray.h"
43#include "JSBoundFunction.h"
44#include "JSCInlines.h"
45#include "JSFunction.h"
46#include "JSGlobalObjectFunctions.h"
47#include "JSInjectedScriptHostPrototype.h"
48#include "JSMap.h"
49#include "JSPromise.h"
50#include "JSPromisePrototype.h"
51#include "JSSet.h"
52#include "JSStringIterator.h"
53#include "JSTypedArrays.h"
54#include "JSWeakMap.h"
55#include "JSWeakSet.h"
56#include "JSWithScope.h"
57#include "MapIteratorPrototype.h"
58#include "MapPrototype.h"
59#include "MarkedSpaceInlines.h"
60#include "ObjectConstructor.h"
61#include "ObjectPrototype.h"
62#include "ProxyObject.h"
63#include "RegExpObject.h"
64#include "ScopedArguments.h"
65#include "SetIteratorPrototype.h"
66#include "SetPrototype.h"
67#include "SourceCode.h"
68#include "TypedArrayInlines.h"
69
70using namespace JSC;
71
72namespace Inspector {
73
74const ClassInfo JSInjectedScriptHost::s_info = { "InjectedScriptHost", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSInjectedScriptHost) };
75
76JSInjectedScriptHost::JSInjectedScriptHost(VM& vm, Structure* structure, Ref<InjectedScriptHost>&& impl)
77 : JSDestructibleObject(vm, structure)
78 , m_wrapped(WTFMove(impl))
79{
80}
81
82void JSInjectedScriptHost::finishCreation(VM& vm)
83{
84 Base::finishCreation(vm);
85 ASSERT(inherits(vm, info()));
86}
87
88JSObject* JSInjectedScriptHost::createPrototype(VM& vm, JSGlobalObject* globalObject)
89{
90 return JSInjectedScriptHostPrototype::create(vm, globalObject, JSInjectedScriptHostPrototype::createStructure(vm, globalObject, globalObject->objectPrototype()));
91}
92
93void JSInjectedScriptHost::destroy(JSC::JSCell* cell)
94{
95 JSInjectedScriptHost* thisObject = static_cast<JSInjectedScriptHost*>(cell);
96 thisObject->JSInjectedScriptHost::~JSInjectedScriptHost();
97}
98
99JSValue JSInjectedScriptHost::evaluate(ExecState* exec) const
100{
101 JSGlobalObject* globalObject = exec->lexicalGlobalObject();
102 return globalObject->evalFunction();
103}
104
105JSValue JSInjectedScriptHost::evaluateWithScopeExtension(ExecState* exec)
106{
107 VM& vm = exec->vm();
108 auto scope = DECLARE_THROW_SCOPE(vm);
109
110 JSValue scriptValue = exec->argument(0);
111 if (!scriptValue.isString())
112 return throwTypeError(exec, scope, "InjectedScriptHost.evaluateWithScopeExtension first argument must be a string."_s);
113
114 String program = asString(scriptValue)->value(exec);
115 RETURN_IF_EXCEPTION(scope, JSValue());
116
117 NakedPtr<Exception> exception;
118 JSObject* scopeExtension = exec->argument(1).getObject();
119 JSValue result = JSC::evaluateWithScopeExtension(exec, makeSource(program, exec->callerSourceOrigin()), scopeExtension, exception);
120 if (exception)
121 throwException(exec, scope, exception);
122
123 return result;
124}
125
126JSValue JSInjectedScriptHost::internalConstructorName(ExecState* exec)
127{
128 if (exec->argumentCount() < 1)
129 return jsUndefined();
130
131 JSObject* object = jsCast<JSObject*>(exec->uncheckedArgument(0).toThis(exec, NotStrictMode));
132 return jsString(exec, JSObject::calculatedClassName(object));
133}
134
135JSValue JSInjectedScriptHost::isHTMLAllCollection(ExecState* exec)
136{
137 if (exec->argumentCount() < 1)
138 return jsUndefined();
139
140 VM& vm = exec->vm();
141 JSValue value = exec->uncheckedArgument(0);
142 return jsBoolean(impl().isHTMLAllCollection(vm, value));
143}
144
145JSValue JSInjectedScriptHost::subtype(ExecState* exec)
146{
147 VM& vm = exec->vm();
148 if (exec->argumentCount() < 1)
149 return jsUndefined();
150
151 JSValue value = exec->uncheckedArgument(0);
152 if (value.isString())
153 return vm.smallStrings.stringString();
154 if (value.isBoolean())
155 return vm.smallStrings.booleanString();
156 if (value.isNumber())
157 return vm.smallStrings.numberString();
158 if (value.isSymbol())
159 return vm.smallStrings.symbolString();
160
161 if (auto* object = jsDynamicCast<JSObject*>(vm, value)) {
162 if (object->isErrorInstance())
163 return jsNontrivialString(exec, "error"_s);
164
165 // Consider class constructor functions class objects.
166 JSFunction* function = jsDynamicCast<JSFunction*>(vm, value);
167 if (function && function->isClassConstructorFunction())
168 return jsNontrivialString(exec, "class"_s);
169
170 if (object->inherits<JSArray>(vm))
171 return jsNontrivialString(exec, "array"_s);
172 if (object->inherits<DirectArguments>(vm) || object->inherits<ScopedArguments>(vm))
173 return jsNontrivialString(exec, "array"_s);
174
175 if (object->inherits<DateInstance>(vm))
176 return jsNontrivialString(exec, "date"_s);
177 if (object->inherits<RegExpObject>(vm))
178 return jsNontrivialString(exec, "regexp"_s);
179 if (object->inherits<ProxyObject>(vm))
180 return jsNontrivialString(exec, "proxy"_s);
181
182 if (object->inherits<JSMap>(vm))
183 return jsNontrivialString(exec, "map"_s);
184 if (object->inherits<JSSet>(vm))
185 return jsNontrivialString(exec, "set"_s);
186 if (object->inherits<JSWeakMap>(vm))
187 return jsNontrivialString(exec, "weakmap"_s);
188 if (object->inherits<JSWeakSet>(vm))
189 return jsNontrivialString(exec, "weakset"_s);
190
191 if (object->inherits<JSStringIterator>(vm))
192 return jsNontrivialString(exec, "iterator"_s);
193
194 if (object->getDirect(vm, vm.propertyNames->builtinNames().arrayIteratorNextIndexPrivateName())
195 || object->getDirect(vm, vm.propertyNames->builtinNames().mapBucketPrivateName())
196 || object->getDirect(vm, vm.propertyNames->builtinNames().setBucketPrivateName()))
197 return jsNontrivialString(exec, "iterator"_s);
198
199 if (object->inherits<JSInt8Array>(vm)
200 || object->inherits<JSInt16Array>(vm)
201 || object->inherits<JSInt32Array>(vm)
202 || object->inherits<JSUint8Array>(vm)
203 || object->inherits<JSUint8ClampedArray>(vm)
204 || object->inherits<JSUint16Array>(vm)
205 || object->inherits<JSUint32Array>(vm)
206 || object->inherits<JSFloat32Array>(vm)
207 || object->inherits<JSFloat64Array>(vm))
208 return jsNontrivialString(exec, "array"_s);
209 }
210
211 return impl().subtype(exec, value);
212}
213
214JSValue JSInjectedScriptHost::functionDetails(ExecState* exec)
215{
216 if (exec->argumentCount() < 1)
217 return jsUndefined();
218
219 VM& vm = exec->vm();
220 JSValue value = exec->uncheckedArgument(0);
221 auto* function = jsDynamicCast<JSFunction*>(vm, value);
222 if (!function)
223 return jsUndefined();
224
225 // FIXME: <https://webkit.org/b/87192> Web Inspector: Expose function scope / closure data
226
227 // FIXME: This should provide better details for JSBoundFunctions.
228
229 const SourceCode* sourceCode = function->sourceCode();
230 if (!sourceCode)
231 return jsUndefined();
232
233 // In the inspector protocol all positions are 0-based while in SourceCode they are 1-based
234 int lineNumber = sourceCode->firstLine().oneBasedInt();
235 if (lineNumber)
236 lineNumber -= 1;
237 int columnNumber = sourceCode->startColumn().oneBasedInt();
238 if (columnNumber)
239 columnNumber -= 1;
240
241 String scriptID = String::number(sourceCode->provider()->asID());
242 JSObject* location = constructEmptyObject(exec);
243 location->putDirect(vm, Identifier::fromString(exec, "scriptId"), jsString(exec, scriptID));
244 location->putDirect(vm, Identifier::fromString(exec, "lineNumber"), jsNumber(lineNumber));
245 location->putDirect(vm, Identifier::fromString(exec, "columnNumber"), jsNumber(columnNumber));
246
247 JSObject* result = constructEmptyObject(exec);
248 result->putDirect(vm, Identifier::fromString(exec, "location"), location);
249
250 String name = function->name(vm);
251 if (!name.isEmpty())
252 result->putDirect(vm, Identifier::fromString(exec, "name"), jsString(exec, name));
253
254 String displayName = function->displayName(vm);
255 if (!displayName.isEmpty())
256 result->putDirect(vm, Identifier::fromString(exec, "displayName"), jsString(exec, displayName));
257
258 return result;
259}
260
261static JSObject* constructInternalProperty(ExecState* exec, const String& name, JSValue value)
262{
263 VM& vm = exec->vm();
264 JSObject* result = constructEmptyObject(exec);
265 result->putDirect(vm, Identifier::fromString(exec, "name"), jsString(exec, name));
266 result->putDirect(vm, Identifier::fromString(exec, "value"), value);
267 return result;
268}
269
270JSValue JSInjectedScriptHost::getInternalProperties(ExecState* exec)
271{
272 if (exec->argumentCount() < 1)
273 return jsUndefined();
274
275 VM& vm = exec->vm();
276 auto scope = DECLARE_THROW_SCOPE(vm);
277 JSValue value = exec->uncheckedArgument(0);
278
279 JSValue internalProperties = impl().getInternalProperties(vm, exec, value);
280 if (internalProperties)
281 return internalProperties;
282
283 if (JSPromise* promise = jsDynamicCast<JSPromise*>(vm, value)) {
284 unsigned index = 0;
285 JSArray* array = constructEmptyArray(exec, nullptr);
286 RETURN_IF_EXCEPTION(scope, JSValue());
287 switch (promise->status(vm)) {
288 case JSPromise::Status::Pending:
289 scope.release();
290 array->putDirectIndex(exec, index++, constructInternalProperty(exec, "status"_s, jsNontrivialString(exec, "pending"_s)));
291 return array;
292 case JSPromise::Status::Fulfilled:
293 array->putDirectIndex(exec, index++, constructInternalProperty(exec, "status"_s, jsNontrivialString(exec, "resolved"_s)));
294 RETURN_IF_EXCEPTION(scope, JSValue());
295 scope.release();
296 array->putDirectIndex(exec, index++, constructInternalProperty(exec, "result"_s, promise->result(vm)));
297 return array;
298 case JSPromise::Status::Rejected:
299 array->putDirectIndex(exec, index++, constructInternalProperty(exec, "status"_s, jsNontrivialString(exec, "rejected"_s)));
300 RETURN_IF_EXCEPTION(scope, JSValue());
301 scope.release();
302 array->putDirectIndex(exec, index++, constructInternalProperty(exec, "result"_s, promise->result(vm)));
303 return array;
304 }
305 // FIXME: <https://webkit.org/b/141664> Web Inspector: ES6: Improved Support for Promises - Promise Reactions
306 RELEASE_ASSERT_NOT_REACHED();
307 }
308
309 if (JSBoundFunction* boundFunction = jsDynamicCast<JSBoundFunction*>(vm, value)) {
310 unsigned index = 0;
311 JSArray* array = constructEmptyArray(exec, nullptr);
312 RETURN_IF_EXCEPTION(scope, JSValue());
313 array->putDirectIndex(exec, index++, constructInternalProperty(exec, "targetFunction", boundFunction->targetFunction()));
314 RETURN_IF_EXCEPTION(scope, JSValue());
315 array->putDirectIndex(exec, index++, constructInternalProperty(exec, "boundThis", boundFunction->boundThis()));
316 RETURN_IF_EXCEPTION(scope, JSValue());
317 if (boundFunction->boundArgs()) {
318 scope.release();
319 array->putDirectIndex(exec, index++, constructInternalProperty(exec, "boundArgs", boundFunction->boundArgsCopy(exec)));
320 return array;
321 }
322 return array;
323 }
324
325 if (ProxyObject* proxy = jsDynamicCast<ProxyObject*>(vm, value)) {
326 unsigned index = 0;
327 JSArray* array = constructEmptyArray(exec, nullptr, 2);
328 RETURN_IF_EXCEPTION(scope, JSValue());
329 array->putDirectIndex(exec, index++, constructInternalProperty(exec, "target"_s, proxy->target()));
330 RETURN_IF_EXCEPTION(scope, JSValue());
331 scope.release();
332 array->putDirectIndex(exec, index++, constructInternalProperty(exec, "handler"_s, proxy->handler()));
333 return array;
334 }
335
336 if (JSObject* iteratorObject = jsDynamicCast<JSObject*>(vm, value)) {
337 if (iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().arrayIteratorNextIndexPrivateName())) {
338 JSValue iteratedValue = iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().iteratedObjectPrivateName());
339 JSValue kind = iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().arrayIteratorKindPrivateName());
340
341 unsigned index = 0;
342 JSArray* array = constructEmptyArray(exec, nullptr, 2);
343 RETURN_IF_EXCEPTION(scope, JSValue());
344 array->putDirectIndex(exec, index++, constructInternalProperty(exec, "array", iteratedValue));
345 RETURN_IF_EXCEPTION(scope, JSValue());
346 scope.release();
347 array->putDirectIndex(exec, index++, constructInternalProperty(exec, "kind", kind));
348 return array;
349 }
350
351 if (iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().mapBucketPrivateName())) {
352 JSValue iteratedValue = iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().iteratedObjectPrivateName());
353 String kind;
354 switch (static_cast<IterationKind>(iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().mapIteratorKindPrivateName()).asInt32())) {
355 case IterateKey:
356 kind = "key"_s;
357 break;
358 case IterateValue:
359 kind = "value"_s;
360 break;
361 case IterateKeyValue:
362 kind = "key+value"_s;
363 break;
364 }
365 unsigned index = 0;
366 JSArray* array = constructEmptyArray(exec, nullptr, 2);
367 RETURN_IF_EXCEPTION(scope, JSValue());
368 array->putDirectIndex(exec, index++, constructInternalProperty(exec, "map", iteratedValue));
369 RETURN_IF_EXCEPTION(scope, JSValue());
370 scope.release();
371 array->putDirectIndex(exec, index++, constructInternalProperty(exec, "kind", jsNontrivialString(exec, kind)));
372 return array;
373 }
374
375 if (iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().setBucketPrivateName())) {
376 JSValue iteratedValue = iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().iteratedObjectPrivateName());
377 String kind;
378 switch (static_cast<IterationKind>(iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().setIteratorKindPrivateName()).asInt32())) {
379 case IterateKey:
380 kind = "key"_s;
381 break;
382 case IterateValue:
383 kind = "value"_s;
384 break;
385 case IterateKeyValue:
386 kind = "key+value"_s;
387 break;
388 }
389 unsigned index = 0;
390 JSArray* array = constructEmptyArray(exec, nullptr, 2);
391 RETURN_IF_EXCEPTION(scope, JSValue());
392 array->putDirectIndex(exec, index++, constructInternalProperty(exec, "set", iteratedValue));
393 RETURN_IF_EXCEPTION(scope, JSValue());
394 scope.release();
395 array->putDirectIndex(exec, index++, constructInternalProperty(exec, "kind", jsNontrivialString(exec, kind)));
396 return array;
397 }
398 }
399
400 if (JSStringIterator* stringIterator = jsDynamicCast<JSStringIterator*>(vm, value)) {
401 unsigned index = 0;
402 JSArray* array = constructEmptyArray(exec, nullptr, 1);
403 RETURN_IF_EXCEPTION(scope, JSValue());
404 scope.release();
405 array->putDirectIndex(exec, index++, constructInternalProperty(exec, "string", stringIterator->iteratedValue(exec)));
406 return array;
407 }
408
409 return jsUndefined();
410}
411
412JSValue JSInjectedScriptHost::proxyTargetValue(ExecState *exec)
413{
414 if (exec->argumentCount() < 1)
415 return jsUndefined();
416
417 VM& vm = exec->vm();
418 JSValue value = exec->uncheckedArgument(0);
419 ProxyObject* proxy = jsDynamicCast<ProxyObject*>(vm, value);
420 if (!proxy)
421 return jsUndefined();
422
423 JSObject* target = proxy->target();
424 while (ProxyObject* proxy = jsDynamicCast<ProxyObject*>(vm, target))
425 target = proxy->target();
426
427 return target;
428}
429
430JSValue JSInjectedScriptHost::weakMapSize(ExecState* exec)
431{
432 if (exec->argumentCount() < 1)
433 return jsUndefined();
434
435 VM& vm = exec->vm();
436 JSValue value = exec->uncheckedArgument(0);
437 JSWeakMap* weakMap = jsDynamicCast<JSWeakMap*>(vm, value);
438 if (!weakMap)
439 return jsUndefined();
440
441 return jsNumber(weakMap->size());
442}
443
444JSValue JSInjectedScriptHost::weakMapEntries(ExecState* exec)
445{
446 if (exec->argumentCount() < 1)
447 return jsUndefined();
448
449 VM& vm = exec->vm();
450 auto scope = DECLARE_THROW_SCOPE(vm);
451 JSValue value = exec->uncheckedArgument(0);
452 JSWeakMap* weakMap = jsDynamicCast<JSWeakMap*>(vm, value);
453 if (!weakMap)
454 return jsUndefined();
455
456 unsigned numberToFetch = 100;
457
458 JSValue numberToFetchArg = exec->argument(1);
459 double fetchDouble = numberToFetchArg.toInteger(exec);
460 if (fetchDouble >= 0)
461 numberToFetch = static_cast<unsigned>(fetchDouble);
462
463 JSArray* array = constructEmptyArray(exec, nullptr);
464 RETURN_IF_EXCEPTION(scope, JSValue());
465
466 MarkedArgumentBuffer buffer;
467 weakMap->takeSnapshot(buffer, numberToFetch);
468
469 for (unsigned index = 0; index < buffer.size(); index += 2) {
470 JSObject* entry = constructEmptyObject(exec);
471 entry->putDirect(vm, Identifier::fromString(exec, "key"), buffer.at(index));
472 entry->putDirect(vm, Identifier::fromString(exec, "value"), buffer.at(index + 1));
473 array->putDirectIndex(exec, index / 2, entry);
474 RETURN_IF_EXCEPTION(scope, JSValue());
475 }
476
477 return array;
478}
479
480JSValue JSInjectedScriptHost::weakSetSize(ExecState* exec)
481{
482 if (exec->argumentCount() < 1)
483 return jsUndefined();
484
485 VM& vm = exec->vm();
486 JSValue value = exec->uncheckedArgument(0);
487 JSWeakSet* weakSet = jsDynamicCast<JSWeakSet*>(vm, value);
488 if (!weakSet)
489 return jsUndefined();
490
491 return jsNumber(weakSet->size());
492}
493
494JSValue JSInjectedScriptHost::weakSetEntries(ExecState* exec)
495{
496 if (exec->argumentCount() < 1)
497 return jsUndefined();
498
499 VM& vm = exec->vm();
500 auto scope = DECLARE_THROW_SCOPE(vm);
501 JSValue value = exec->uncheckedArgument(0);
502 JSWeakSet* weakSet = jsDynamicCast<JSWeakSet*>(vm, value);
503 if (!weakSet)
504 return jsUndefined();
505
506 unsigned numberToFetch = 100;
507
508 JSValue numberToFetchArg = exec->argument(1);
509 double fetchDouble = numberToFetchArg.toInteger(exec);
510 if (fetchDouble >= 0)
511 numberToFetch = static_cast<unsigned>(fetchDouble);
512
513 JSArray* array = constructEmptyArray(exec, nullptr);
514 RETURN_IF_EXCEPTION(scope, JSValue());
515
516 MarkedArgumentBuffer buffer;
517 weakSet->takeSnapshot(buffer, numberToFetch);
518
519 for (unsigned index = 0; index < buffer.size(); ++index) {
520 JSObject* entry = constructEmptyObject(exec);
521 entry->putDirect(vm, Identifier::fromString(exec, "value"), buffer.at(index));
522 array->putDirectIndex(exec, index, entry);
523 RETURN_IF_EXCEPTION(scope, JSValue());
524 }
525
526 return array;
527}
528
529static JSObject* cloneArrayIteratorObject(ExecState* exec, VM& vm, JSObject* iteratorObject, JSGlobalObject* globalObject, JSValue nextIndex, JSValue iteratedObject)
530{
531 ASSERT(iteratorObject->type() == FinalObjectType);
532 JSObject* clone = constructEmptyObject(exec, ArrayIteratorPrototype::create(vm, globalObject, ArrayIteratorPrototype::createStructure(vm, globalObject, globalObject->iteratorPrototype())));
533 clone->putDirect(vm, vm.propertyNames->builtinNames().iteratedObjectPrivateName(), iteratedObject);
534 clone->putDirect(vm, vm.propertyNames->builtinNames().arrayIteratorKindPrivateName(), iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().arrayIteratorKindPrivateName()));
535 clone->putDirect(vm, vm.propertyNames->builtinNames().arrayIteratorNextIndexPrivateName(), nextIndex);
536 clone->putDirect(vm, vm.propertyNames->builtinNames().arrayIteratorNextPrivateName(), iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().arrayIteratorNextPrivateName()));
537 clone->putDirect(vm, vm.propertyNames->builtinNames().arrayIteratorIsDonePrivateName(), iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().arrayIteratorIsDonePrivateName()));
538 return clone;
539}
540
541static JSObject* cloneMapIteratorObject(ExecState* exec, VM& vm, JSObject* iteratorObject, JSGlobalObject* globalObject, JSValue mapBucket, JSValue iteratedObject)
542{
543 ASSERT(iteratorObject->type() == FinalObjectType);
544 JSObject* clone = constructEmptyObject(exec, MapIteratorPrototype::create(vm, globalObject, MapIteratorPrototype::createStructure(vm, globalObject, globalObject->iteratorPrototype())));
545 clone->putDirect(vm, vm.propertyNames->builtinNames().iteratedObjectPrivateName(), iteratedObject);
546 clone->putDirect(vm, vm.propertyNames->builtinNames().mapIteratorKindPrivateName(), iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().mapIteratorKindPrivateName()));
547 clone->putDirect(vm, vm.propertyNames->builtinNames().mapBucketPrivateName(), mapBucket);
548 return clone;
549}
550
551static JSObject* cloneSetIteratorObject(ExecState* exec, VM& vm, JSObject* iteratorObject, JSGlobalObject* globalObject, JSValue setBucket, JSValue iteratedObject)
552{
553 ASSERT(iteratorObject->type() == FinalObjectType);
554 JSObject* clone = constructEmptyObject(exec, SetIteratorPrototype::create(vm, globalObject, SetIteratorPrototype::createStructure(vm, globalObject, globalObject->iteratorPrototype())));
555 clone->putDirect(vm, vm.propertyNames->builtinNames().iteratedObjectPrivateName(), iteratedObject);
556 clone->putDirect(vm, vm.propertyNames->builtinNames().setIteratorKindPrivateName(), iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().setIteratorKindPrivateName()));
557 clone->putDirect(vm, vm.propertyNames->builtinNames().setBucketPrivateName(), setBucket);
558 return clone;
559}
560
561JSValue JSInjectedScriptHost::iteratorEntries(ExecState* exec)
562{
563 if (exec->argumentCount() < 1)
564 return jsUndefined();
565
566 VM& vm = exec->vm();
567 auto scope = DECLARE_THROW_SCOPE(vm);
568
569 JSValue iterator;
570 JSGlobalObject* globalObject = exec->lexicalGlobalObject();
571 JSValue value = exec->uncheckedArgument(0);
572 if (JSStringIterator* stringIterator = jsDynamicCast<JSStringIterator*>(vm, value)) {
573 if (globalObject->isStringPrototypeIteratorProtocolFastAndNonObservable())
574 iterator = stringIterator->clone(exec);
575 } else if (JSObject* iteratorObject = jsDynamicCast<JSObject*>(vm, value)) {
576 // Detect an ArrayIterator by checking for one of its unique private properties.
577 JSValue iteratedObject = iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().iteratedObjectPrivateName());
578 if (JSValue nextIndex = iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().arrayIteratorNextIndexPrivateName())) {
579 if (isJSArray(iteratedObject)) {
580 JSArray* array = jsCast<JSArray*>(iteratedObject);
581 if (array->isIteratorProtocolFastAndNonObservable())
582 iterator = cloneArrayIteratorObject(exec, vm, iteratorObject, globalObject, nextIndex, iteratedObject);
583 } else if (iteratedObject.isObject() && TypeInfo::isArgumentsType(asObject(iteratedObject)->type())) {
584 if (globalObject->isArrayPrototypeIteratorProtocolFastAndNonObservable())
585 iterator = cloneArrayIteratorObject(exec, vm, iteratorObject, globalObject, nextIndex, iteratedObject);
586 }
587 } else if (JSValue mapBucket = iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().mapBucketPrivateName())) {
588 if (jsCast<JSMap*>(iteratedObject)->isIteratorProtocolFastAndNonObservable())
589 iterator = cloneMapIteratorObject(exec, vm, iteratorObject, globalObject, mapBucket, iteratedObject);
590 } else if (JSValue setBucket = iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().setBucketPrivateName())) {
591 if (jsCast<JSSet*>(iteratedObject)->isIteratorProtocolFastAndNonObservable())
592 iterator = cloneSetIteratorObject(exec, vm, iteratorObject, globalObject, setBucket, iteratedObject);
593 }
594 }
595 RETURN_IF_EXCEPTION(scope, { });
596 if (!iterator)
597 return jsUndefined();
598
599 IterationRecord iterationRecord = { iterator, iterator.get(exec, vm.propertyNames->next) };
600
601 unsigned numberToFetch = 5;
602 JSValue numberToFetchArg = exec->argument(1);
603 double fetchDouble = numberToFetchArg.toInteger(exec);
604 RETURN_IF_EXCEPTION(scope, { });
605 if (fetchDouble >= 0)
606 numberToFetch = static_cast<unsigned>(fetchDouble);
607
608 JSArray* array = constructEmptyArray(exec, nullptr);
609 RETURN_IF_EXCEPTION(scope, { });
610
611 for (unsigned i = 0; i < numberToFetch; ++i) {
612 JSValue next = iteratorStep(exec, iterationRecord);
613 if (UNLIKELY(scope.exception()) || next.isFalse())
614 break;
615
616 JSValue nextValue = iteratorValue(exec, next);
617 RETURN_IF_EXCEPTION(scope, { });
618
619 JSObject* entry = constructEmptyObject(exec);
620 entry->putDirect(vm, Identifier::fromString(exec, "value"), nextValue);
621 array->putDirectIndex(exec, i, entry);
622 if (UNLIKELY(scope.exception())) {
623 scope.release();
624 iteratorClose(exec, iterationRecord);
625 break;
626 }
627 }
628
629 return array;
630}
631
632static bool checkForbiddenPrototype(ExecState* exec, JSValue value, JSValue proto)
633{
634 if (value == proto)
635 return true;
636
637 // Check that the prototype chain of proto hasn't been modified to include value.
638 return JSObject::defaultHasInstance(exec, proto, value);
639}
640
641JSValue JSInjectedScriptHost::queryObjects(ExecState* exec)
642{
643 if (exec->argumentCount() < 1)
644 return jsUndefined();
645
646 VM& vm = exec->vm();
647 auto scope = DECLARE_THROW_SCOPE(vm);
648
649 JSValue prototypeOrConstructor = exec->uncheckedArgument(0);
650 if (!prototypeOrConstructor.isObject())
651 return throwTypeError(exec, scope, "queryObjects first argument must be an object."_s);
652
653 JSObject* object = asObject(prototypeOrConstructor);
654 if (object->inherits<ProxyObject>(vm))
655 return throwTypeError(exec, scope, "queryObjects cannot be called with a Proxy."_s);
656
657 JSValue prototype = object;
658
659 PropertySlot prototypeSlot(object, PropertySlot::InternalMethodType::VMInquiry);
660 if (object->getPropertySlot(exec, vm.propertyNames->prototype, prototypeSlot)) {
661 RETURN_IF_EXCEPTION(scope, { });
662 if (prototypeSlot.isValue()) {
663 JSValue prototypeValue = prototypeSlot.getValue(exec, vm.propertyNames->prototype);
664 if (prototypeValue.isObject()) {
665 prototype = prototypeValue;
666 object = asObject(prototype);
667 }
668 }
669 }
670
671 if (object->inherits<ProxyObject>(vm) || prototype.inherits<ProxyObject>(vm))
672 return throwTypeError(exec, scope, "queryObjects cannot be called with a Proxy."_s);
673
674 // FIXME: implement a way of distinguishing between internal and user-created objects.
675 JSGlobalObject* lexicalGlobalObject = exec->lexicalGlobalObject();
676 if (checkForbiddenPrototype(exec, object, lexicalGlobalObject->objectPrototype()))
677 return throwTypeError(exec, scope, "queryObjects cannot be called with Object."_s);
678 if (checkForbiddenPrototype(exec, object, lexicalGlobalObject->functionPrototype()))
679 return throwTypeError(exec, scope, "queryObjects cannot be called with Function."_s);
680 if (checkForbiddenPrototype(exec, object, lexicalGlobalObject->arrayPrototype()))
681 return throwTypeError(exec, scope, "queryObjects cannot be called with Array."_s);
682 if (checkForbiddenPrototype(exec, object, lexicalGlobalObject->mapPrototype()))
683 return throwTypeError(exec, scope, "queryObjects cannot be called with Map."_s);
684 if (checkForbiddenPrototype(exec, object, lexicalGlobalObject->jsSetPrototype()))
685 return throwTypeError(exec, scope, "queryObjects cannot be called with Set."_s);
686 if (checkForbiddenPrototype(exec, object, lexicalGlobalObject->promisePrototype()))
687 return throwTypeError(exec, scope, "queryObjects cannot be called with Promise."_s);
688
689 sanitizeStackForVM(&vm);
690 vm.heap.collectNow(Sync, CollectionScope::Full);
691
692 JSArray* array = constructEmptyArray(exec, nullptr);
693 RETURN_IF_EXCEPTION(scope, { });
694
695 {
696 HeapIterationScope iterationScope(vm.heap);
697 vm.heap.objectSpace().forEachLiveCell(iterationScope, [&] (HeapCell* cell, HeapCell::Kind kind) {
698 if (!isJSCellKind(kind))
699 return IterationStatus::Continue;
700
701 JSValue value(static_cast<JSCell*>(cell));
702 if (value.inherits<ProxyObject>(vm))
703 return IterationStatus::Continue;
704
705 if (JSObject::defaultHasInstance(exec, value, prototype))
706 array->putDirectIndex(exec, array->length(), value);
707
708 return IterationStatus::Continue;
709 });
710 }
711
712 return array;
713}
714
715} // namespace Inspector
716