Replace WTF::move with WTFMove
[WebKit-https.git] / Source / JavaScriptCore / inspector / JSInjectedScriptHost.cpp
1 /*
2  * Copyright (C) 2013, 2015 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 "DateInstance.h"
30 #include "DirectArguments.h"
31 #include "Error.h"
32 #include "InjectedScriptHost.h"
33 #include "IteratorOperations.h"
34 #include "JSArray.h"
35 #include "JSArrayIterator.h"
36 #include "JSBoundFunction.h"
37 #include "JSCInlines.h"
38 #include "JSFunction.h"
39 #include "JSInjectedScriptHostPrototype.h"
40 #include "JSMap.h"
41 #include "JSMapIterator.h"
42 #include "JSPromise.h"
43 #include "JSPropertyNameIterator.h"
44 #include "JSSet.h"
45 #include "JSSetIterator.h"
46 #include "JSStringIterator.h"
47 #include "JSTypedArrays.h"
48 #include "JSWeakMap.h"
49 #include "JSWeakSet.h"
50 #include "ObjectConstructor.h"
51 #include "RegExpObject.h"
52 #include "ScopedArguments.h"
53 #include "SourceCode.h"
54 #include "TypedArrayInlines.h"
55 #include "WeakMapData.h"
56
57 using namespace JSC;
58
59 namespace Inspector {
60
61 const ClassInfo JSInjectedScriptHost::s_info = { "InjectedScriptHost", &Base::s_info, 0, CREATE_METHOD_TABLE(JSInjectedScriptHost) };
62
63 JSInjectedScriptHost::JSInjectedScriptHost(VM& vm, Structure* structure, Ref<InjectedScriptHost>&& impl)
64     : JSDestructibleObject(vm, structure)
65     , m_wrapped(WTFMove(impl))
66 {
67 }
68
69 void JSInjectedScriptHost::finishCreation(VM& vm)
70 {
71     Base::finishCreation(vm);
72     ASSERT(inherits(info()));
73 }
74
75 JSObject* JSInjectedScriptHost::createPrototype(VM& vm, JSGlobalObject* globalObject)
76 {
77     return JSInjectedScriptHostPrototype::create(vm, globalObject, JSInjectedScriptHostPrototype::createStructure(vm, globalObject, globalObject->objectPrototype()));
78 }
79
80 void JSInjectedScriptHost::destroy(JSC::JSCell* cell)
81 {
82     JSInjectedScriptHost* thisObject = static_cast<JSInjectedScriptHost*>(cell);
83     thisObject->JSInjectedScriptHost::~JSInjectedScriptHost();
84 }
85
86 JSValue JSInjectedScriptHost::evaluate(ExecState* exec) const
87 {
88     JSGlobalObject* globalObject = exec->lexicalGlobalObject();
89     return globalObject->evalFunction();
90 }
91
92 JSValue JSInjectedScriptHost::internalConstructorName(ExecState* exec)
93 {
94     if (exec->argumentCount() < 1)
95         return jsUndefined();
96
97     JSObject* object = jsCast<JSObject*>(exec->uncheckedArgument(0).toThis(exec, NotStrictMode));
98     return jsString(exec, JSObject::calculatedClassName(object));
99 }
100
101 JSValue JSInjectedScriptHost::isHTMLAllCollection(ExecState* exec)
102 {
103     if (exec->argumentCount() < 1)
104         return jsUndefined();
105
106     JSValue value = exec->uncheckedArgument(0);
107     return jsBoolean(impl().isHTMLAllCollection(value));
108 }
109
110 JSValue JSInjectedScriptHost::subtype(ExecState* exec)
111 {
112     if (exec->argumentCount() < 1)
113         return jsUndefined();
114
115     JSValue value = exec->uncheckedArgument(0);
116     if (value.isString())
117         return exec->vm().smallStrings.stringString();
118     if (value.isBoolean())
119         return exec->vm().smallStrings.booleanString();
120     if (value.isNumber())
121         return exec->vm().smallStrings.numberString();
122     if (value.isSymbol())
123         return exec->vm().smallStrings.symbolString();
124
125     JSObject* object = asObject(value);
126     if (object) {
127         if (object->isErrorInstance())
128             return jsNontrivialString(exec, ASCIILiteral("error"));
129
130         // Consider class constructor functions class objects.
131         JSFunction* function = jsDynamicCast<JSFunction*>(value);
132         if (function && function->isClassConstructorFunction())
133             return jsNontrivialString(exec, ASCIILiteral("class"));
134     }
135
136     if (value.inherits(JSArray::info()))
137         return jsNontrivialString(exec, ASCIILiteral("array"));
138     if (value.inherits(DirectArguments::info()) || value.inherits(ScopedArguments::info()))
139         return jsNontrivialString(exec, ASCIILiteral("array"));
140
141     if (value.inherits(DateInstance::info()))
142         return jsNontrivialString(exec, ASCIILiteral("date"));
143     if (value.inherits(RegExpObject::info()))
144         return jsNontrivialString(exec, ASCIILiteral("regexp"));
145
146     if (value.inherits(JSMap::info()))
147         return jsNontrivialString(exec, ASCIILiteral("map"));
148     if (value.inherits(JSSet::info()))
149         return jsNontrivialString(exec, ASCIILiteral("set"));
150     if (value.inherits(JSWeakMap::info()))
151         return jsNontrivialString(exec, ASCIILiteral("weakmap"));
152     if (value.inherits(JSWeakSet::info()))
153         return jsNontrivialString(exec, ASCIILiteral("weakset"));
154
155     if (value.inherits(JSArrayIterator::info())
156         || value.inherits(JSMapIterator::info())
157         || value.inherits(JSSetIterator::info())
158         || value.inherits(JSStringIterator::info())
159         || value.inherits(JSPropertyNameIterator::info()))
160         return jsNontrivialString(exec, ASCIILiteral("iterator"));
161
162     if (value.inherits(JSInt8Array::info()) || value.inherits(JSInt16Array::info()) || value.inherits(JSInt32Array::info()))
163         return jsNontrivialString(exec, ASCIILiteral("array"));
164     if (value.inherits(JSUint8Array::info()) || value.inherits(JSUint16Array::info()) || value.inherits(JSUint32Array::info()))
165         return jsNontrivialString(exec, ASCIILiteral("array"));
166     if (value.inherits(JSFloat32Array::info()) || value.inherits(JSFloat64Array::info()))
167         return jsNontrivialString(exec, ASCIILiteral("array"));
168
169     return impl().subtype(exec, value);
170 }
171
172 JSValue JSInjectedScriptHost::functionDetails(ExecState* exec)
173 {
174     if (exec->argumentCount() < 1)
175         return jsUndefined();
176
177     JSValue value = exec->uncheckedArgument(0);
178     if (!value.asCell()->inherits(JSFunction::info()))
179         return jsUndefined();
180
181     // FIXME: This should provide better details for JSBoundFunctions.
182
183     JSFunction* function = jsCast<JSFunction*>(value);
184     const SourceCode* sourceCode = function->sourceCode();
185     if (!sourceCode)
186         return jsUndefined();
187
188     // In the inspector protocol all positions are 0-based while in SourceCode they are 1-based
189     int lineNumber = sourceCode->firstLine();
190     if (lineNumber)
191         lineNumber -= 1;
192     int columnNumber = sourceCode->startColumn();
193     if (columnNumber)
194         columnNumber -= 1;
195
196     String scriptID = String::number(sourceCode->provider()->asID());
197     JSObject* location = constructEmptyObject(exec);
198     location->putDirect(exec->vm(), Identifier::fromString(exec, "scriptId"), jsString(exec, scriptID));
199     location->putDirect(exec->vm(), Identifier::fromString(exec, "lineNumber"), jsNumber(lineNumber));
200     location->putDirect(exec->vm(), Identifier::fromString(exec, "columnNumber"), jsNumber(columnNumber));
201
202     JSObject* result = constructEmptyObject(exec);
203     result->putDirect(exec->vm(), Identifier::fromString(exec, "location"), location);
204
205     String name = function->name(exec);
206     if (!name.isEmpty())
207         result->putDirect(exec->vm(), Identifier::fromString(exec, "name"), jsString(exec, name));
208
209     String displayName = function->displayName(exec);
210     if (!displayName.isEmpty())
211         result->putDirect(exec->vm(), Identifier::fromString(exec, "displayName"), jsString(exec, displayName));
212
213     // FIXME: provide function scope data in "scopesRaw" property when JSC supports it.
214     // <https://webkit.org/b/87192> [JSC] expose function (closure) inner context to debugger
215
216     return result;
217 }
218
219 static JSObject* constructInternalProperty(ExecState* exec, const String& name, JSValue value)
220 {
221     JSObject* result = constructEmptyObject(exec);
222     result->putDirect(exec->vm(), Identifier::fromString(exec, "name"), jsString(exec, name));
223     result->putDirect(exec->vm(), Identifier::fromString(exec, "value"), value);
224     return result;
225 }
226
227 JSValue JSInjectedScriptHost::getInternalProperties(ExecState* exec)
228 {
229     if (exec->argumentCount() < 1)
230         return jsUndefined();
231
232     JSValue value = exec->uncheckedArgument(0);
233
234     if (JSPromise* promise = jsDynamicCast<JSPromise*>(value)) {
235         unsigned index = 0;
236         JSArray* array = constructEmptyArray(exec, nullptr);
237         switch (promise->status(exec->vm())) {
238         case JSPromise::Status::Pending:
239             array->putDirectIndex(exec, index++, constructInternalProperty(exec, ASCIILiteral("status"), jsNontrivialString(exec, ASCIILiteral("pending"))));
240             break;
241         case JSPromise::Status::Fulfilled:
242             array->putDirectIndex(exec, index++, constructInternalProperty(exec, ASCIILiteral("status"), jsNontrivialString(exec, ASCIILiteral("resolved"))));
243             array->putDirectIndex(exec, index++, constructInternalProperty(exec, ASCIILiteral("result"), promise->result(exec->vm())));
244             break;
245         case JSPromise::Status::Rejected:
246             array->putDirectIndex(exec, index++, constructInternalProperty(exec, ASCIILiteral("status"), jsNontrivialString(exec, ASCIILiteral("rejected"))));
247             array->putDirectIndex(exec, index++, constructInternalProperty(exec, ASCIILiteral("result"), promise->result(exec->vm())));
248             break;
249         }
250         // FIXME: <https://webkit.org/b/141664> Web Inspector: ES6: Improved Support for Promises - Promise Reactions
251         return array;
252     }
253
254     if (JSBoundFunction* boundFunction = jsDynamicCast<JSBoundFunction*>(value)) {
255         unsigned index = 0;
256         JSArray* array = constructEmptyArray(exec, nullptr, 3);
257         array->putDirectIndex(exec, index++, constructInternalProperty(exec, "targetFunction", boundFunction->targetFunction()));
258         array->putDirectIndex(exec, index++, constructInternalProperty(exec, "boundThis", boundFunction->boundThis()));
259         array->putDirectIndex(exec, index++, constructInternalProperty(exec, "boundArgs", boundFunction->boundArgs()));
260         return array;
261     }
262
263     if (JSArrayIterator* arrayIterator = jsDynamicCast<JSArrayIterator*>(value)) {
264         String kind;
265         switch (arrayIterator->kind(exec)) {
266         case ArrayIterateKey:
267             kind = ASCIILiteral("key");
268             break;
269         case ArrayIterateValue:
270             kind = ASCIILiteral("value");
271             break;
272         case ArrayIterateKeyValue:
273             kind = ASCIILiteral("key+value");
274             break;
275         }
276         unsigned index = 0;
277         JSArray* array = constructEmptyArray(exec, nullptr, 2);
278         array->putDirectIndex(exec, index++, constructInternalProperty(exec, "array", arrayIterator->iteratedValue(exec)));
279         array->putDirectIndex(exec, index++, constructInternalProperty(exec, "kind", jsNontrivialString(exec, kind)));
280         return array;
281     }
282
283     if (JSMapIterator* mapIterator = jsDynamicCast<JSMapIterator*>(value)) {
284         String kind;
285         switch (mapIterator->kind()) {
286         case MapIterateKey:
287             kind = ASCIILiteral("key");
288             break;
289         case MapIterateValue:
290             kind = ASCIILiteral("value");
291             break;
292         case MapIterateKeyValue:
293             kind = ASCIILiteral("key+value");
294             break;
295         }
296         unsigned index = 0;
297         JSArray* array = constructEmptyArray(exec, nullptr, 2);
298         array->putDirectIndex(exec, index++, constructInternalProperty(exec, "map", mapIterator->iteratedValue()));
299         array->putDirectIndex(exec, index++, constructInternalProperty(exec, "kind", jsNontrivialString(exec, kind)));
300         return array;
301     }
302         
303     if (JSSetIterator* setIterator = jsDynamicCast<JSSetIterator*>(value)) {
304         String kind;
305         switch (setIterator->kind()) {
306         case SetIterateKey:
307             kind = ASCIILiteral("key");
308             break;
309         case SetIterateValue:
310             kind = ASCIILiteral("value");
311             break;
312         case SetIterateKeyValue:
313             kind = ASCIILiteral("key+value");
314             break;
315         }
316         unsigned index = 0;
317         JSArray* array = constructEmptyArray(exec, nullptr, 2);
318         array->putDirectIndex(exec, index++, constructInternalProperty(exec, "set", setIterator->iteratedValue()));
319         array->putDirectIndex(exec, index++, constructInternalProperty(exec, "kind", jsNontrivialString(exec, kind)));
320         return array;
321     }
322
323     if (JSStringIterator* stringIterator = jsDynamicCast<JSStringIterator*>(value)) {
324         unsigned index = 0;
325         JSArray* array = constructEmptyArray(exec, nullptr, 1);
326         array->putDirectIndex(exec, index++, constructInternalProperty(exec, "string", stringIterator->iteratedValue(exec)));
327         return array;
328     }
329
330     if (JSPropertyNameIterator* propertyNameIterator = jsDynamicCast<JSPropertyNameIterator*>(value)) {
331         unsigned index = 0;
332         JSArray* array = constructEmptyArray(exec, nullptr, 1);
333         array->putDirectIndex(exec, index++, constructInternalProperty(exec, "object", propertyNameIterator->iteratedValue()));
334         return array;
335     }
336
337     return jsUndefined();
338 }
339
340 JSValue JSInjectedScriptHost::weakMapSize(ExecState* exec)
341 {
342     if (exec->argumentCount() < 1)
343         return jsUndefined();
344
345     JSValue value = exec->uncheckedArgument(0);
346     JSWeakMap* weakMap = jsDynamicCast<JSWeakMap*>(value);
347     if (!weakMap)
348         return jsUndefined();
349
350     return jsNumber(weakMap->weakMapData()->size());
351 }
352
353 JSValue JSInjectedScriptHost::weakMapEntries(ExecState* exec)
354 {
355     if (exec->argumentCount() < 1)
356         return jsUndefined();
357
358     JSValue value = exec->uncheckedArgument(0);
359     JSWeakMap* weakMap = jsDynamicCast<JSWeakMap*>(value);
360     if (!weakMap)
361         return jsUndefined();
362
363     unsigned fetched = 0;
364     unsigned numberToFetch = 100;
365
366     JSValue numberToFetchArg = exec->argument(1);
367     double fetchDouble = numberToFetchArg.toInteger(exec);
368     if (fetchDouble >= 0)
369         numberToFetch = static_cast<unsigned>(fetchDouble);
370
371     JSArray* array = constructEmptyArray(exec, nullptr);
372     for (auto it = weakMap->weakMapData()->begin(); it != weakMap->weakMapData()->end(); ++it) {
373         JSObject* entry = constructEmptyObject(exec);
374         entry->putDirect(exec->vm(), Identifier::fromString(exec, "key"), it->key);
375         entry->putDirect(exec->vm(), Identifier::fromString(exec, "value"), it->value.get());
376         array->putDirectIndex(exec, fetched++, entry);
377         if (numberToFetch && fetched >= numberToFetch)
378             break;
379     }
380
381     return array;
382 }
383
384 JSValue JSInjectedScriptHost::weakSetSize(ExecState* exec)
385 {
386     if (exec->argumentCount() < 1)
387         return jsUndefined();
388
389     JSValue value = exec->uncheckedArgument(0);
390     JSWeakSet* weakSet = jsDynamicCast<JSWeakSet*>(value);
391     if (!weakSet)
392         return jsUndefined();
393
394     return jsNumber(weakSet->weakMapData()->size());
395 }
396
397 JSValue JSInjectedScriptHost::weakSetEntries(ExecState* exec)
398 {
399     if (exec->argumentCount() < 1)
400         return jsUndefined();
401
402     JSValue value = exec->uncheckedArgument(0);
403     JSWeakSet* weakSet = jsDynamicCast<JSWeakSet*>(value);
404     if (!weakSet)
405         return jsUndefined();
406
407     unsigned fetched = 0;
408     unsigned numberToFetch = 100;
409
410     JSValue numberToFetchArg = exec->argument(1);
411     double fetchDouble = numberToFetchArg.toInteger(exec);
412     if (fetchDouble >= 0)
413         numberToFetch = static_cast<unsigned>(fetchDouble);
414
415     JSArray* array = constructEmptyArray(exec, nullptr);
416     for (auto it = weakSet->weakMapData()->begin(); it != weakSet->weakMapData()->end(); ++it) {
417         JSObject* entry = constructEmptyObject(exec);
418         entry->putDirect(exec->vm(), Identifier::fromString(exec, "value"), it->key);
419         array->putDirectIndex(exec, fetched++, entry);
420         if (numberToFetch && fetched >= numberToFetch)
421             break;
422     }
423
424     return array;
425 }
426
427 JSValue JSInjectedScriptHost::iteratorEntries(ExecState* exec)
428 {
429     if (exec->argumentCount() < 1)
430         return jsUndefined();
431
432     JSValue iterator;
433     JSValue value = exec->uncheckedArgument(0);
434     if (JSArrayIterator* arrayIterator = jsDynamicCast<JSArrayIterator*>(value))
435         iterator = arrayIterator->clone(exec);
436     else if (JSMapIterator* mapIterator = jsDynamicCast<JSMapIterator*>(value))
437         iterator = mapIterator->clone(exec);
438     else if (JSSetIterator* setIterator = jsDynamicCast<JSSetIterator*>(value))
439         iterator = setIterator->clone(exec);
440     else if (JSStringIterator* stringIterator = jsDynamicCast<JSStringIterator*>(value))
441         iterator = stringIterator->clone(exec);
442     else if (JSPropertyNameIterator* propertyNameIterator = jsDynamicCast<JSPropertyNameIterator*>(value))
443         iterator = propertyNameIterator->clone(exec);
444     else
445         return jsUndefined();
446
447     unsigned numberToFetch = 5;
448     JSValue numberToFetchArg = exec->argument(1);
449     double fetchDouble = numberToFetchArg.toInteger(exec);
450     if (fetchDouble >= 0)
451         numberToFetch = static_cast<unsigned>(fetchDouble);
452
453     JSArray* array = constructEmptyArray(exec, nullptr);
454
455     for (unsigned i = 0; i < numberToFetch; ++i) {
456         JSValue next = iteratorStep(exec, iterator);
457         if (exec->hadException())
458             break;
459         if (next.isFalse())
460             break;
461
462         JSValue nextValue = iteratorValue(exec, next);
463         if (exec->hadException())
464             break;
465
466         JSObject* entry = constructEmptyObject(exec);
467         entry->putDirect(exec->vm(), Identifier::fromString(exec, "value"), nextValue);
468         array->putDirectIndex(exec, i, entry);
469     }
470
471     iteratorClose(exec, iterator);
472
473     return array;
474 }
475
476 } // namespace Inspector