class CustomFunction {
public:

	explicit CustomFunction(v8::Isolate* _isolate, Database* _db, v8::Local<v8::Function> _fn, const char* _name, bool _safe_ints)
		: name(COPY(_name)), db(_db), isolate(_isolate), fn(_isolate, _fn), safe_ints(_safe_ints) {}
	virtual ~CustomFunction() { delete[] name; }

	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> maybe_return_value = v8::Local<v8::Function>::New(isolate, self->fn)->Call(OnlyContext, v8::Undefined(isolate), argc, args);
		if (args != args_fast) delete[] args;

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

	void ThrowResultValueError(sqlite3_context* invocation) {
		ThrowTypeError(CONCAT("User-defined function ", name, "() returned an invalid value").c_str());
		PropagateJSError(invocation);
	}

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

private:
	const char* const name;
	Database* const db;
protected:
	v8::Isolate* const isolate;
	const CopyablePersistent<v8::Function> fn;
	const bool safe_ints;
};
