[JSC] Unify how we throw TypeError from C++
[WebKit-https.git] / Source / WebCore / bridge / c / c_instance.cpp
1 /*
2  * Copyright (C) 2003, 2006 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
28 #if ENABLE(NETSCAPE_PLUGIN_API)
29
30 #include "c_instance.h"
31
32 #include "CRuntimeObject.h"
33 #include "IdentifierRep.h"
34 #include "JSDOMBinding.h"
35 #include "c_class.h"
36 #include "c_runtime.h"
37 #include "c_utility.h"
38 #include "npruntime_impl.h"
39 #include "runtime_method.h"
40 #include "runtime_root.h"
41 #include <interpreter/CallFrame.h>
42 #include <runtime/ArgList.h>
43 #include <runtime/Error.h>
44 #include <runtime/FunctionPrototype.h>
45 #include <runtime/JSLock.h>
46 #include <runtime/PropertyNameArray.h>
47 #include <wtf/Assertions.h>
48 #include <wtf/NeverDestroyed.h>
49 #include <wtf/StdLibExtras.h>
50 #include <wtf/StringExtras.h>
51 #include <wtf/Vector.h>
52
53 using namespace WebCore;
54
55 namespace JSC {
56 namespace Bindings {
57
58 static String& globalExceptionString()
59 {
60     static NeverDestroyed<String> exceptionStr;
61     return exceptionStr;
62 }
63
64 void CInstance::setGlobalException(String exception)
65 {
66     globalExceptionString() = exception;
67 }
68
69 void CInstance::moveGlobalExceptionToExecState(ExecState* exec)
70 {
71     if (globalExceptionString().isNull())
72         return;
73
74     {
75         JSLockHolder lock(exec);
76         exec->vm().throwException(exec, createError(exec, globalExceptionString()));
77     }
78
79     globalExceptionString() = String();
80 }
81
82 CInstance::CInstance(NPObject* o, RefPtr<RootObject>&& rootObject)
83     : Instance(WTFMove(rootObject))
84 {
85     _object = _NPN_RetainObject(o);
86     _class = 0;
87 }
88
89 CInstance::~CInstance()
90 {
91     _NPN_ReleaseObject(_object);
92 }
93
94 RuntimeObject* CInstance::newRuntimeObject(ExecState* exec)
95 {
96     // FIXME: deprecatedGetDOMStructure uses the prototype off of the wrong global object.
97     return CRuntimeObject::create(exec->vm(), WebCore::deprecatedGetDOMStructure<CRuntimeObject>(exec), this);
98 }
99
100 Class *CInstance::getClass() const
101 {
102     if (!_class)
103         _class = CClass::classForIsA(_object->_class);
104     return _class;
105 }
106
107 bool CInstance::supportsInvokeDefaultMethod() const
108 {
109     return _object->_class->invokeDefault;
110 }
111
112 class CRuntimeMethod : public RuntimeMethod {
113 public:
114     typedef RuntimeMethod Base;
115
116     static CRuntimeMethod* create(ExecState* exec, JSGlobalObject* globalObject, const String& name, Bindings::Method* method)
117     {
118         // FIXME: deprecatedGetDOMStructure uses the prototype off of the wrong global object
119         // We need to pass in the right global object for "i".
120         Structure* domStructure = WebCore::deprecatedGetDOMStructure<CRuntimeMethod>(exec);
121         CRuntimeMethod* runtimeMethod = new (NotNull, allocateCell<CRuntimeMethod>(*exec->heap())) CRuntimeMethod(globalObject, domStructure, method);
122         runtimeMethod->finishCreation(exec->vm(), name);
123         return runtimeMethod;
124     }
125
126     static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
127     {
128         return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
129     }
130
131     DECLARE_INFO;
132
133 private:
134     CRuntimeMethod(JSGlobalObject* globalObject, Structure* structure, Bindings::Method* method)
135         : RuntimeMethod(globalObject, structure, method)
136     {
137     }
138
139     void finishCreation(VM& vm, const String& name)
140     {
141         Base::finishCreation(vm, name);
142         ASSERT(inherits(info()));
143     }
144
145 };
146
147 const ClassInfo CRuntimeMethod::s_info = { "CRuntimeMethod", &RuntimeMethod::s_info, 0, CREATE_METHOD_TABLE(CRuntimeMethod) };
148
149 JSValue CInstance::getMethod(ExecState* exec, PropertyName propertyName)
150 {
151     Method* method = getClass()->methodNamed(propertyName, this);
152     return CRuntimeMethod::create(exec, exec->lexicalGlobalObject(), propertyName.publicName(), method);
153 }
154
155 JSValue CInstance::invokeMethod(ExecState* exec, RuntimeMethod* runtimeMethod)
156 {
157     if (!asObject(runtimeMethod)->inherits(CRuntimeMethod::info()))
158         return throwTypeError(exec, "Attempt to invoke non-plug-in method on plug-in object.");
159
160     CMethod* method = static_cast<CMethod*>(runtimeMethod->method());
161     ASSERT(method);
162
163     NPIdentifier ident = method->identifier();
164     if (!_object->_class->hasMethod(_object, ident))
165         return jsUndefined();
166
167     unsigned count = exec->argumentCount();
168     Vector<NPVariant, 8> cArgs(count);
169
170     unsigned i;
171     for (i = 0; i < count; i++)
172         convertValueToNPVariant(exec, exec->uncheckedArgument(i), &cArgs[i]);
173
174     // Invoke the 'C' method.
175     bool retval = true;
176     NPVariant resultVariant;
177     VOID_TO_NPVARIANT(resultVariant);
178
179     {
180         JSLock::DropAllLocks dropAllLocks(exec);
181         ASSERT(globalExceptionString().isNull());
182         retval = _object->_class->invoke(_object, ident, cArgs.data(), count, &resultVariant);
183         moveGlobalExceptionToExecState(exec);
184     }
185
186     if (!retval)
187         exec->vm().throwException(exec, createError(exec, ASCIILiteral("Error calling method on NPObject.")));
188
189     for (i = 0; i < count; i++)
190         _NPN_ReleaseVariantValue(&cArgs[i]);
191
192     JSValue resultValue = convertNPVariantToValue(exec, &resultVariant, m_rootObject.get());
193     _NPN_ReleaseVariantValue(&resultVariant);
194     return resultValue;
195 }
196
197
198 JSValue CInstance::invokeDefaultMethod(ExecState* exec)
199 {
200     if (!_object->_class->invokeDefault)
201         return jsUndefined();
202
203     unsigned count = exec->argumentCount();
204     Vector<NPVariant, 8> cArgs(count);
205
206     unsigned i;
207     for (i = 0; i < count; i++)
208         convertValueToNPVariant(exec, exec->uncheckedArgument(i), &cArgs[i]);
209
210     // Invoke the 'C' method.
211     bool retval = true;
212     NPVariant resultVariant;
213     VOID_TO_NPVARIANT(resultVariant);
214     {
215         JSLock::DropAllLocks dropAllLocks(exec);
216         ASSERT(globalExceptionString().isNull());
217         retval = _object->_class->invokeDefault(_object, cArgs.data(), count, &resultVariant);
218         moveGlobalExceptionToExecState(exec);
219     }
220
221     if (!retval)
222         exec->vm().throwException(exec, createError(exec, ASCIILiteral("Error calling method on NPObject.")));
223
224     for (i = 0; i < count; i++)
225         _NPN_ReleaseVariantValue(&cArgs[i]);
226
227     JSValue resultValue = convertNPVariantToValue(exec, &resultVariant, m_rootObject.get());
228     _NPN_ReleaseVariantValue(&resultVariant);
229     return resultValue;
230 }
231
232 bool CInstance::supportsConstruct() const
233 {
234     return _object->_class->construct;
235 }
236
237 JSValue CInstance::invokeConstruct(ExecState* exec, const ArgList& args)
238 {
239     if (!_object->_class->construct)
240         return jsUndefined();
241
242     unsigned count = args.size();
243     Vector<NPVariant, 8> cArgs(count);
244
245     unsigned i;
246     for (i = 0; i < count; i++)
247         convertValueToNPVariant(exec, args.at(i), &cArgs[i]);
248
249     // Invoke the 'C' method.
250     bool retval = true;
251     NPVariant resultVariant;
252     VOID_TO_NPVARIANT(resultVariant);
253     {
254         JSLock::DropAllLocks dropAllLocks(exec);
255         ASSERT(globalExceptionString().isNull());
256         retval = _object->_class->construct(_object, cArgs.data(), count, &resultVariant);
257         moveGlobalExceptionToExecState(exec);
258     }
259
260     if (!retval)
261         exec->vm().throwException(exec, createError(exec, ASCIILiteral("Error calling method on NPObject.")));
262
263     for (i = 0; i < count; i++)
264         _NPN_ReleaseVariantValue(&cArgs[i]);
265
266     JSValue resultValue = convertNPVariantToValue(exec, &resultVariant, m_rootObject.get());
267     _NPN_ReleaseVariantValue(&resultVariant);
268     return resultValue;
269 }
270
271 JSValue CInstance::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const
272 {
273     if (hint == PreferString)
274         return stringValue(exec);
275     if (hint == PreferNumber)
276         return numberValue(exec);
277     return valueOf(exec);
278 }
279
280 JSValue CInstance::stringValue(ExecState* exec) const
281 {
282     JSValue value;
283     if (toJSPrimitive(exec, "toString", value))
284         return value;
285
286     // Fallback to default implementation.
287     return jsNontrivialString(exec, ASCIILiteral("NPObject"));
288 }
289
290 JSValue CInstance::numberValue(ExecState*) const
291 {
292     // FIXME: Implement something sensible.
293     return jsNumber(0);
294 }
295
296 JSValue CInstance::booleanValue() const
297 {
298     // As per ECMA 9.2.
299     return jsBoolean(getObject());
300 }
301
302 JSValue CInstance::valueOf(ExecState* exec) const
303 {
304     JSValue value;
305     if (toJSPrimitive(exec, "valueOf", value))
306         return value;
307
308     // Fallback to default implementation.
309     return stringValue(exec);
310 }
311
312 bool CInstance::toJSPrimitive(ExecState* exec, const char* name, JSValue& resultValue) const
313 {
314     NPIdentifier ident = _NPN_GetStringIdentifier(name);
315     if (!_object->_class->hasMethod(_object, ident))
316         return false;
317
318     // Invoke the 'C' method.
319     bool retval = true;
320     NPVariant resultVariant;
321     VOID_TO_NPVARIANT(resultVariant);
322
323     {
324         JSLock::DropAllLocks dropAllLocks(exec);
325         ASSERT(globalExceptionString().isNull());
326         retval = _object->_class->invoke(_object, ident, 0, 0, &resultVariant);
327         moveGlobalExceptionToExecState(exec);
328     }
329
330     if (!retval)
331         exec->vm().throwException(exec, createError(exec, ASCIILiteral("Error calling method on NPObject.")));
332
333     resultValue = convertNPVariantToValue(exec, &resultVariant, m_rootObject.get());
334     _NPN_ReleaseVariantValue(&resultVariant);
335     return true;
336 }
337
338 void CInstance::getPropertyNames(ExecState* exec, PropertyNameArray& nameArray)
339 {
340     if (!NP_CLASS_STRUCT_VERSION_HAS_ENUM(_object->_class) || !_object->_class->enumerate)
341         return;
342
343     uint32_t count;
344     NPIdentifier* identifiers;
345
346     {
347         JSLock::DropAllLocks dropAllLocks(exec);
348         ASSERT(globalExceptionString().isNull());
349         bool ok = _object->_class->enumerate(_object, &identifiers, &count);
350         moveGlobalExceptionToExecState(exec);
351         if (!ok)
352             return;
353     }
354
355     for (uint32_t i = 0; i < count; i++) {
356         IdentifierRep* identifier = static_cast<IdentifierRep*>(identifiers[i]);
357
358         if (identifier->isString())
359             nameArray.add(identifierFromNPIdentifier(exec, identifier->string()));
360         else
361             nameArray.add(Identifier::from(exec, identifier->number()));
362     }
363
364     // FIXME: This should really call NPN_MemFree but that's in WebKit
365     free(identifiers);
366 }
367
368 }
369 }
370
371 #endif // ENABLE(NETSCAPE_PLUGIN_API)