]> git.saurik.com Git - apple/javascriptcore.git/blob - inspector/JSInjectedScriptHost.cpp
JavaScriptCore-7601.1.46.3.tar.gz
[apple/javascriptcore.git] / 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 "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
59 using namespace JSC;
60
61 namespace Inspector {
62
63 const ClassInfo JSInjectedScriptHost::s_info = { "InjectedScriptHost", &Base::s_info, 0, CREATE_METHOD_TABLE(JSInjectedScriptHost) };
64
65 JSInjectedScriptHost::JSInjectedScriptHost(VM& vm, Structure* structure, PassRefPtr<InjectedScriptHost> impl)
66 : JSDestructibleObject(vm, structure)
67 , m_impl(impl.leakRef())
68 {
69 }
70
71 void JSInjectedScriptHost::finishCreation(VM& vm)
72 {
73 Base::finishCreation(vm);
74 ASSERT(inherits(info()));
75 }
76
77 JSObject* JSInjectedScriptHost::createPrototype(VM& vm, JSGlobalObject* globalObject)
78 {
79 return JSInjectedScriptHostPrototype::create(vm, globalObject, JSInjectedScriptHostPrototype::createStructure(vm, globalObject, globalObject->objectPrototype()));
80 }
81
82 void JSInjectedScriptHost::destroy(JSC::JSCell* cell)
83 {
84 JSInjectedScriptHost* thisObject = static_cast<JSInjectedScriptHost*>(cell);
85 thisObject->JSInjectedScriptHost::~JSInjectedScriptHost();
86 }
87
88 void JSInjectedScriptHost::releaseImpl()
89 {
90 if (auto impl = std::exchange(m_impl, nullptr))
91 impl->deref();
92 }
93
94 JSInjectedScriptHost::~JSInjectedScriptHost()
95 {
96 releaseImpl();
97 }
98
99 JSValue JSInjectedScriptHost::evaluate(ExecState* exec) const
100 {
101 JSGlobalObject* globalObject = exec->lexicalGlobalObject();
102 return globalObject->evalFunction();
103 }
104
105 JSValue 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
114 JSValue 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
123 JSValue 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
184 JSValue 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
231 static 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
239 JSValue 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
347 JSValue 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
360 JSValue 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
391 JSValue 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
404 JSValue 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
434 JSValue 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
481 JSValue 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
493 JSInjectedScriptHost* toJSInjectedScriptHost(JSValue value)
494 {
495 return value.inherits(JSInjectedScriptHost::info()) ? jsCast<JSInjectedScriptHost*>(value) : nullptr;
496 }
497
498 } // namespace Inspector