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