5b3d3e6d3e155effabea3369d98b5e7d00f7d64c
[WebKit-https.git] / Source / JavaScriptCore / inspector / JSInjectedScriptHost.cpp
1 /*
2  * Copyright (C) 2013 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 "Error.h"
31 #include "InjectedScriptHost.h"
32 #include "JSArray.h"
33 #include "JSBoundFunction.h"
34 #include "JSCInlines.h"
35 #include "JSFunction.h"
36 #include "JSInjectedScriptHostPrototype.h"
37 #include "JSMap.h"
38 #include "JSSet.h"
39 #include "JSTypedArrays.h"
40 #include "JSWeakMap.h"
41 #include "ObjectConstructor.h"
42 #include "RegExpObject.h"
43 #include "SourceCode.h"
44 #include "TypedArrayInlines.h"
45 #include "WeakMapData.h"
46
47 #if ENABLE(PROMISES)
48 #include "JSPromise.h"
49 #endif
50
51 using namespace JSC;
52
53 namespace Inspector {
54
55 const ClassInfo JSInjectedScriptHost::s_info = { "InjectedScriptHost", &Base::s_info, 0, CREATE_METHOD_TABLE(JSInjectedScriptHost) };
56
57 JSInjectedScriptHost::JSInjectedScriptHost(VM& vm, Structure* structure, PassRefPtr<InjectedScriptHost> impl)
58     : JSDestructibleObject(vm, structure)
59     , m_impl(impl.leakRef())
60 {
61 }
62
63 void JSInjectedScriptHost::finishCreation(VM& vm)
64 {
65     Base::finishCreation(vm);
66     ASSERT(inherits(info()));
67 }
68
69 JSObject* JSInjectedScriptHost::createPrototype(VM& vm, JSGlobalObject* globalObject)
70 {
71     return JSInjectedScriptHostPrototype::create(vm, globalObject, JSInjectedScriptHostPrototype::createStructure(vm, globalObject, globalObject->objectPrototype()));
72 }
73
74 void JSInjectedScriptHost::destroy(JSC::JSCell* cell)
75 {
76     JSInjectedScriptHost* thisObject = static_cast<JSInjectedScriptHost*>(cell);
77     thisObject->JSInjectedScriptHost::~JSInjectedScriptHost();
78 }
79
80 void JSInjectedScriptHost::releaseImpl()
81 {
82     if (m_impl) {
83         m_impl->deref();
84         m_impl = nullptr;
85     }
86 }
87
88 JSInjectedScriptHost::~JSInjectedScriptHost()
89 {
90     releaseImpl();
91 }
92
93 JSValue JSInjectedScriptHost::evaluate(ExecState* exec) const
94 {
95     JSGlobalObject* globalObject = exec->lexicalGlobalObject();
96     return globalObject->evalFunction();
97 }
98
99 JSValue JSInjectedScriptHost::internalConstructorName(ExecState* exec)
100 {
101     if (exec->argumentCount() < 1)
102         return jsUndefined();
103
104     JSObject* object = jsCast<JSObject*>(exec->uncheckedArgument(0).toThis(exec, NotStrictMode));
105     return jsString(exec, JSObject::calculatedClassName(object));
106 }
107
108 JSValue JSInjectedScriptHost::isHTMLAllCollection(ExecState* exec)
109 {
110     if (exec->argumentCount() < 1)
111         return jsUndefined();
112
113     JSValue value = exec->uncheckedArgument(0);
114     return jsBoolean(impl().isHTMLAllCollection(value));
115 }
116
117 JSValue JSInjectedScriptHost::subtype(ExecState* exec)
118 {
119     if (exec->argumentCount() < 1)
120         return jsUndefined();
121
122     JSValue value = exec->uncheckedArgument(0);
123     if (value.isString())
124         return exec->vm().smallStrings.stringString();
125     if (value.isBoolean())
126         return exec->vm().smallStrings.booleanString();
127     if (value.isNumber())
128         return exec->vm().smallStrings.numberString();
129     if (value.isSymbol())
130         return exec->vm().smallStrings.symbolString();
131
132     JSObject* object = asObject(value);
133     if (object && object->isErrorInstance())
134         return jsNontrivialString(exec, ASCIILiteral("error"));
135
136     if (value.inherits(JSArray::info()))
137         return jsNontrivialString(exec, ASCIILiteral("array"));
138     if (value.inherits(DateInstance::info()))
139         return jsNontrivialString(exec, ASCIILiteral("date"));
140     if (value.inherits(RegExpObject::info()))
141         return jsNontrivialString(exec, ASCIILiteral("regexp"));
142
143     if (value.inherits(JSMap::info()))
144         return jsNontrivialString(exec, ASCIILiteral("map"));
145     if (value.inherits(JSSet::info()))
146         return jsNontrivialString(exec, ASCIILiteral("set"));
147     if (value.inherits(JSWeakMap::info()))
148         return jsNontrivialString(exec, ASCIILiteral("weakmap"));
149
150     if (value.inherits(JSInt8Array::info()) || value.inherits(JSInt16Array::info()) || value.inherits(JSInt32Array::info()))
151         return jsNontrivialString(exec, ASCIILiteral("array"));
152     if (value.inherits(JSUint8Array::info()) || value.inherits(JSUint16Array::info()) || value.inherits(JSUint32Array::info()))
153         return jsNontrivialString(exec, ASCIILiteral("array"));
154     if (value.inherits(JSFloat32Array::info()) || value.inherits(JSFloat64Array::info()))
155         return jsNontrivialString(exec, ASCIILiteral("array"));
156
157     return impl().subtype(exec, value);
158 }
159
160 JSValue JSInjectedScriptHost::functionDetails(ExecState* exec)
161 {
162     if (exec->argumentCount() < 1)
163         return jsUndefined();
164
165     JSValue value = exec->uncheckedArgument(0);
166     if (!value.asCell()->inherits(JSFunction::info()))
167         return jsUndefined();
168
169     JSFunction* function = jsCast<JSFunction*>(value);
170     const SourceCode* sourceCode = function->sourceCode();
171     if (!sourceCode)
172         return jsUndefined();
173
174     int lineNumber = sourceCode->firstLine();
175     if (lineNumber)
176         lineNumber -= 1; // In the inspector protocol all positions are 0-based while in SourceCode they are 1-based
177
178     String scriptID = String::number(sourceCode->provider()->asID());
179     JSObject* location = constructEmptyObject(exec);
180     location->putDirect(exec->vm(), Identifier(exec, "lineNumber"), jsNumber(lineNumber));
181     location->putDirect(exec->vm(), Identifier(exec, "scriptId"), jsString(exec, scriptID));
182
183     JSObject* result = constructEmptyObject(exec);
184     result->putDirect(exec->vm(), Identifier(exec, "location"), location);
185
186     String name = function->name(exec);
187     if (!name.isEmpty())
188         result->putDirect(exec->vm(), Identifier(exec, "name"), jsString(exec, name));
189
190     String displayName = function->displayName(exec);
191     if (!displayName.isEmpty())
192         result->putDirect(exec->vm(), Identifier(exec, "displayName"), jsString(exec, displayName));
193
194     // FIXME: provide function scope data in "scopesRaw" property when JSC supports it.
195     // <https://webkit.org/b/87192> [JSC] expose function (closure) inner context to debugger
196
197     return result;
198 }
199
200 static JSObject* constructInternalProperty(ExecState* exec, const String& name, JSValue value)
201 {
202     JSObject* result = constructEmptyObject(exec);
203     result->putDirect(exec->vm(), Identifier(exec, "name"), jsString(exec, name));
204     result->putDirect(exec->vm(), Identifier(exec, "value"), value);
205     return result;
206 }
207
208 JSValue JSInjectedScriptHost::getInternalProperties(ExecState* exec)
209 {
210     if (exec->argumentCount() < 1)
211         return jsUndefined();
212
213     JSValue value = exec->uncheckedArgument(0);
214
215 #if ENABLE(PROMISES)
216     if (JSPromise* promise = jsDynamicCast<JSPromise*>(value)) {
217         unsigned index = 0;
218         JSArray* array = constructEmptyArray(exec, nullptr);
219         switch (promise->status()) {
220         case JSPromise::Status::Unresolved:
221             array->putDirectIndex(exec, index++, constructInternalProperty(exec, ASCIILiteral("status"), jsNontrivialString(exec, ASCIILiteral("pending"))));
222             break;
223         case JSPromise::Status::HasResolution:
224             array->putDirectIndex(exec, index++, constructInternalProperty(exec, ASCIILiteral("status"), jsNontrivialString(exec, ASCIILiteral("resolved"))));
225             array->putDirectIndex(exec, index++, constructInternalProperty(exec, ASCIILiteral("result"), promise->result()));
226             break;
227         case JSPromise::Status::HasRejection:
228             array->putDirectIndex(exec, index++, constructInternalProperty(exec, ASCIILiteral("status"), jsNontrivialString(exec, ASCIILiteral("rejected"))));
229             array->putDirectIndex(exec, index++, constructInternalProperty(exec, ASCIILiteral("result"), promise->result()));
230             break;
231         }
232         // FIXME: <https://webkit.org/b/141664> Web Inspector: ES6: Improved Support for Promises - Promise Reactions
233         return array;
234     }
235 #endif
236
237     if (JSBoundFunction* boundFunction = jsDynamicCast<JSBoundFunction*>(value)) {
238         unsigned index = 0;
239         JSArray* array = constructEmptyArray(exec, nullptr, 3);
240         array->putDirectIndex(exec, index++, constructInternalProperty(exec, "targetFunction", boundFunction->targetFunction()));
241         array->putDirectIndex(exec, index++, constructInternalProperty(exec, "boundThis", boundFunction->boundThis()));
242         array->putDirectIndex(exec, index++, constructInternalProperty(exec, "boundArgs", boundFunction->boundArgs()));
243         return array;
244     }
245
246     return jsUndefined();
247 }
248
249 JSValue JSInjectedScriptHost::weakMapEntries(ExecState* exec)
250 {
251     if (exec->argumentCount() < 1)
252         return jsUndefined();
253
254     JSValue value = exec->uncheckedArgument(0);
255     JSWeakMap* weakMap = jsDynamicCast<JSWeakMap*>(value);
256     if (!weakMap)
257         return jsUndefined();
258
259     unsigned fetched = 0;
260     unsigned numberToFetch = 100;
261
262     JSValue numberToFetchArg = exec->argument(1);
263     double fetchDouble = numberToFetchArg.toInteger(exec);
264     if (fetchDouble >= 0)
265         numberToFetch = static_cast<unsigned>(fetchDouble);
266
267     JSArray* array = constructEmptyArray(exec, nullptr);
268     for (auto it = weakMap->weakMapData()->begin(); it != weakMap->weakMapData()->end(); ++it) {
269         JSObject* entry = constructEmptyObject(exec);
270         entry->putDirect(exec->vm(), Identifier(exec, "key"), it->key);
271         entry->putDirect(exec->vm(), Identifier(exec, "value"), it->value.get());
272         array->putDirectIndex(exec, fetched++, entry);
273         if (numberToFetch && fetched >= numberToFetch)
274             break;
275     }
276
277     return array;
278 }
279
280 JSValue toJS(ExecState* exec, JSGlobalObject* globalObject, InjectedScriptHost* impl)
281 {
282     if (!impl)
283         return jsNull();
284
285     JSObject* prototype = JSInjectedScriptHost::createPrototype(exec->vm(), globalObject);
286     Structure* structure = JSInjectedScriptHost::createStructure(exec->vm(), globalObject, prototype);
287     JSInjectedScriptHost* injectedScriptHost = JSInjectedScriptHost::create(exec->vm(), structure, impl);
288
289     return injectedScriptHost;
290 }
291
292 JSInjectedScriptHost* toJSInjectedScriptHost(JSValue value)
293 {
294     return value.inherits(JSInjectedScriptHost::info()) ? jsCast<JSInjectedScriptHost*>(value) : nullptr;
295 }
296
297 } // namespace Inspector