class CustomFunction : protected DataConverter {
public:

	explicit CustomFunction(
		v8::Isolate* isolate,
		Database* db,
		const char* name,
		v8::Local<v8::Function> fn,
		bool safe_ints
	) :
		name(name),
		db(db),
		isolate(isolate),
		fn(isolate, fn),
		safe_ints(safe_ints) {}

	virtual ~CustomFunction() {}

	static void xDestroy(void* self) {
		delete static_cast<CustomFunction*>(self);
	}

	static void xFunc(sqlite3_context* invocation, int argc, sqlite3_value** argv) {
		FUNCTION_START();

		v8::Local<v8::Value> args_fast[4];
		v8::Local<v8::Value>* args = NULL;
		if (argc != 0) {
			args = argc <= 4 ? args_fast : ALLOC_ARRAY<v8::Local<v8::Value>>(argc);
			Data::GetArgumentsJS(isolate, args, argv, argc, self->safe_ints);
		}

		v8::MaybeLocal<v8::Value> maybeReturnValue = self->fn.Get(isolate)->Call(OnlyContext, v8::Undefined(isolate), argc, args);
		if (args != args_fast) delete[] args;

		if (maybeReturnValue.IsEmpty()) self->PropagateJSError(invocation);
		else Data::ResultValueFromJS(isolate, invocation, maybeReturnValue.ToLocalChecked(), self);
	}

protected:

	void PropagateJSError(sqlite3_context* invocation) {
		assert(db->GetState()->was_js_error == false);
		db->GetState()->was_js_error = true;
		sqlite3_result_error(invocation, "", 0);
	}

	std::string GetDataErrorPrefix() {
		return std::string("User-defined function ") + name + "() returned";
	}

private:
	const std::string name;
	Database* const db;
protected:
	v8::Isolate* const isolate;
	const v8::Global<v8::Function> fn;
	const bool safe_ints;
};
