1/*
2 * Copyright (C) 2018 Igalia S.L.
3 * Copyright (C) 2006-2018 Apple Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "config.h"
28#include "JSCCallbackFunction.h"
29
30#include "APICallbackFunction.h"
31#include "APICast.h"
32#include "IsoSubspacePerVM.h"
33#include "JSCClassPrivate.h"
34#include "JSCContextPrivate.h"
35#include "JSDestructibleObjectHeapCellType.h"
36#include "JSCExceptionPrivate.h"
37#include "JSCInlines.h"
38#include "JSFunction.h"
39#include "JSGlobalObject.h"
40#include "JSLock.h"
41
42namespace JSC {
43
44static JSValueRef callAsFunction(JSContextRef callerContext, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
45{
46 return static_cast<JSCCallbackFunction*>(toJS(function))->call(callerContext, thisObject, argumentCount, arguments, exception);
47}
48
49static JSObjectRef callAsConstructor(JSContextRef callerContext, JSObjectRef constructor, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
50{
51 return static_cast<JSCCallbackFunction*>(toJS(constructor))->construct(callerContext, argumentCount, arguments, exception);
52}
53
54const ClassInfo JSCCallbackFunction::s_info = { "CallbackFunction", &InternalFunction::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSCCallbackFunction) };
55
56JSCCallbackFunction* JSCCallbackFunction::create(VM& vm, JSGlobalObject* globalObject, const String& name, Type type, JSCClass* jscClass, GRefPtr<GClosure>&& closure, GType returnType, Optional<Vector<GType>>&& parameters)
57{
58 Structure* structure = globalObject->glibCallbackFunctionStructure();
59 JSCCallbackFunction* function = new (NotNull, allocateCell<JSCCallbackFunction>(vm.heap)) JSCCallbackFunction(vm, structure, type, jscClass, WTFMove(closure), returnType, WTFMove(parameters));
60 function->finishCreation(vm, name);
61 return function;
62}
63
64JSCCallbackFunction::JSCCallbackFunction(VM& vm, Structure* structure, Type type, JSCClass* jscClass, GRefPtr<GClosure>&& closure, GType returnType, Optional<Vector<GType>>&& parameters)
65 : InternalFunction(vm, structure, APICallbackFunction::call<JSCCallbackFunction>, type == Type::Constructor ? APICallbackFunction::construct<JSCCallbackFunction> : nullptr)
66 , m_functionCallback(callAsFunction)
67 , m_constructCallback(callAsConstructor)
68 , m_type(type)
69 , m_class(jscClass)
70 , m_closure(WTFMove(closure))
71 , m_returnType(returnType)
72 , m_parameters(WTFMove(parameters))
73{
74 ASSERT(type != Type::Constructor || jscClass);
75 if (G_CLOSURE_NEEDS_MARSHAL(m_closure.get()))
76 g_closure_set_marshal(m_closure.get(), g_cclosure_marshal_generic);
77}
78
79JSValueRef JSCCallbackFunction::call(JSContextRef callerContext, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
80{
81 JSLockHolder locker(toJS(callerContext));
82 auto context = jscContextGetOrCreate(toGlobalRef(globalObject()->globalExec()));
83 auto* jsContext = jscContextGetJSContext(context.get());
84
85 if (m_type == Type::Constructor) {
86 *exception = toRef(JSC::createTypeError(toJS(jsContext), "cannot call a class constructor without |new|"_s));
87 return JSValueMakeUndefined(jsContext);
88 }
89
90 gpointer instance = nullptr;
91 if (m_type == Type::Method) {
92 instance = jscContextWrappedObject(context.get(), thisObject);
93 if (!instance) {
94 *exception = toRef(JSC::createTypeError(toJS(jsContext), "invalid instance type in method"_s));
95 return JSValueMakeUndefined(jsContext);
96 }
97 }
98
99 auto callbackData = jscContextPushCallback(context.get(), toRef(this), thisObject, argumentCount, arguments);
100
101 // GClosure always expect to have at least the instance parameter.
102 bool addInstance = instance || (m_parameters && m_parameters->isEmpty());
103
104 auto parameterCount = m_parameters ? std::min(m_parameters->size(), argumentCount) : 1;
105 if (addInstance)
106 parameterCount++;
107 auto* values = static_cast<GValue*>(g_alloca(sizeof(GValue) * parameterCount));
108 memset(values, 0, sizeof(GValue) * parameterCount);
109
110 size_t firstParameter = 0;
111 if (addInstance) {
112 g_value_init(&values[0], G_TYPE_POINTER);
113 g_value_set_pointer(&values[0], instance);
114 firstParameter = 1;
115 }
116 if (m_parameters) {
117 for (size_t i = firstParameter; i < parameterCount && !*exception; ++i)
118 jscContextJSValueToGValue(context.get(), arguments[i - firstParameter], m_parameters.value()[i - firstParameter], &values[i], exception);
119 } else {
120 auto* parameters = g_ptr_array_new_full(argumentCount, g_object_unref);
121 for (size_t i = 0; i < argumentCount; ++i)
122 g_ptr_array_add(parameters, jscContextGetOrCreateValue(context.get(), arguments[i]).leakRef());
123 g_value_init(&values[firstParameter], G_TYPE_PTR_ARRAY);
124 g_value_take_boxed(&values[firstParameter], parameters);
125 }
126
127 GValue returnValue = G_VALUE_INIT;
128 if (m_returnType != G_TYPE_NONE)
129 g_value_init(&returnValue, m_returnType);
130
131 if (!*exception)
132 g_closure_invoke(m_closure.get(), m_returnType != G_TYPE_NONE ? &returnValue : nullptr, parameterCount, values, nullptr);
133
134 for (size_t i = 0; i < parameterCount; ++i)
135 g_value_unset(&values[i]);
136
137 if (auto* jscException = jsc_context_get_exception(context.get()))
138 *exception = jscExceptionGetJSValue(jscException);
139
140 jscContextPopCallback(context.get(), WTFMove(callbackData));
141
142 if (m_returnType == G_TYPE_NONE)
143 return JSValueMakeUndefined(jsContext);
144
145 auto* retval = *exception ? JSValueMakeUndefined(jsContext) : jscContextGValueToJSValue(context.get(), &returnValue, exception);
146 g_value_unset(&returnValue);
147 return retval;
148}
149
150JSObjectRef JSCCallbackFunction::construct(JSContextRef callerContext, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
151{
152 JSLockHolder locker(toJS(callerContext));
153 auto context = jscContextGetOrCreate(toGlobalRef(globalObject()->globalExec()));
154 auto* jsContext = jscContextGetJSContext(context.get());
155
156 if (m_returnType == G_TYPE_NONE) {
157 *exception = toRef(JSC::createTypeError(toJS(jsContext), "constructors cannot be void"_s));
158 return nullptr;
159 }
160
161 auto callbackData = jscContextPushCallback(context.get(), toRef(this), nullptr, argumentCount, arguments);
162
163 GValue returnValue = G_VALUE_INIT;
164 g_value_init(&returnValue, m_returnType);
165
166 if (m_parameters && m_parameters->isEmpty()) {
167 // GClosure always expect to have at least the instance parameter.
168 GValue dummyValue = G_VALUE_INIT;
169 g_value_init(&dummyValue, G_TYPE_POINTER);
170 g_closure_invoke(m_closure.get(), &returnValue, 1, &dummyValue, nullptr);
171 g_value_unset(&dummyValue);
172 } else {
173 auto parameterCount = m_parameters ? std::min(m_parameters->size(), argumentCount) : 1;
174 auto* values = static_cast<GValue*>(g_alloca(sizeof(GValue) * parameterCount));
175 memset(values, 0, sizeof(GValue) * parameterCount);
176
177 if (m_parameters) {
178 for (size_t i = 0; i < parameterCount && !*exception; ++i)
179 jscContextJSValueToGValue(context.get(), arguments[i], m_parameters.value()[i], &values[i], exception);
180 } else {
181 auto* parameters = g_ptr_array_new_full(argumentCount, g_object_unref);
182 for (size_t i = 0; i < argumentCount; ++i)
183 g_ptr_array_add(parameters, jscContextGetOrCreateValue(context.get(), arguments[i]).leakRef());
184 g_value_init(&values[0], G_TYPE_PTR_ARRAY);
185 g_value_take_boxed(&values[0], parameters);
186 }
187
188 if (!*exception)
189 g_closure_invoke(m_closure.get(), &returnValue, parameterCount, values, nullptr);
190
191 for (size_t i = 0; i < parameterCount; ++i)
192 g_value_unset(&values[i]);
193 }
194
195 if (auto* jscException = jsc_context_get_exception(context.get()))
196 *exception = jscExceptionGetJSValue(jscException);
197
198 jscContextPopCallback(context.get(), WTFMove(callbackData));
199
200 if (*exception) {
201 g_value_unset(&returnValue);
202 return nullptr;
203 }
204
205 switch (g_type_fundamental(G_VALUE_TYPE(&returnValue))) {
206 case G_TYPE_POINTER:
207 case G_TYPE_BOXED:
208 case G_TYPE_OBJECT:
209 if (auto* ptr = returnValue.data[0].v_pointer)
210 return toRef(jscClassGetOrCreateJSWrapper(m_class.get(), context.get(), ptr));
211 *exception = toRef(JSC::createTypeError(toJS(jsContext), "constructor returned null"_s));
212 break;
213 default:
214 *exception = toRef(JSC::createTypeError(toJS(jsContext), makeString("invalid type ", g_type_name(G_VALUE_TYPE(&returnValue)), " returned by constructor")));
215 break;
216 }
217 g_value_unset(&returnValue);
218 return nullptr;
219}
220
221void JSCCallbackFunction::destroy(JSCell* cell)
222{
223 static_cast<JSCCallbackFunction*>(cell)->JSCCallbackFunction::~JSCCallbackFunction();
224}
225
226IsoSubspace* JSCCallbackFunction::subspaceForImpl(VM& vm)
227{
228 NeverDestroyed<IsoSubspacePerVM> perVM([] (VM& vm) -> IsoSubspacePerVM::SubspaceParameters { return ISO_SUBSPACE_PARAMETERS(vm.destructibleObjectHeapCellType.get(), JSCCallbackFunction); });
229 return &perVM.get().forVM(vm);
230}
231
232} // namespace JSC
233