/* Swfdec * Copyright (C) 2007-2008 Benjamin Otte * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "swfdec_as_native_function.h" #include "swfdec_as_context.h" #include "swfdec_as_frame_internal.h" #include "swfdec_as_internal.h" #include "swfdec_as_stack.h" #include "swfdec_as_strings.h" #include "swfdec_debug.h" /*** GTK-DOC ***/ /** * SwfdecAsNative: * @context: #SwfdecAsContext * @thisp: the this object. Can be %NULL. * @argc: number of arguments passed to this function * @argv: the @argc arguments passed to this function * @retval: set to the return value. Initialized to undefined by default * * This is the prototype for all native functions. */ /** * SwfdecAsNativeFunction: * * This is the object type for native functions. */ /*** IMPLEMENTATION ***/ G_DEFINE_TYPE (SwfdecAsNativeFunction, swfdec_as_native_function, SWFDEC_TYPE_AS_FUNCTION) static void swfdec_as_native_function_call (SwfdecAsFunction *function, SwfdecAsObject *thisp, gboolean construct, SwfdecAsObject *super_reference, guint n_args, const SwfdecAsValue *args, SwfdecAsValue *return_value) { SwfdecAsNativeFunction *native = SWFDEC_AS_NATIVE_FUNCTION (function); SwfdecAsContext *cx = swfdec_gc_object_get_context (function); SwfdecAsFrame frame = { NULL, }; SwfdecAsValue rval = { 0, }; SwfdecAsValue *argv; g_assert (native->name); swfdec_as_frame_init_native (&frame, cx); frame.construct = construct; frame.function = function; /* We copy the target here so we have a proper SwfdecMovie reference inside native * functions. This is for example necessary for swfdec_player_get_movie_by_value() * and probably other stuff that does variable lookups inside native functions. */ /* FIXME: copy target or original target? */ if (frame.next) { frame.target = frame.next->original_target; frame.original_target = frame.target; } if (thisp) { thisp = swfdec_as_object_resolve (thisp); swfdec_as_frame_set_this (&frame, thisp); } frame.argc = n_args; frame.argv = args; frame.return_value = return_value; frame.construct = construct; if (frame.argc == 0 || frame.argv != NULL) { /* FIXME FIXME FIXME: no casting here please! */ argv = (SwfdecAsValue *) frame.argv; } else { SwfdecAsStack *stack; SwfdecAsValue *cur; guint i; if (frame.argc > 128) { SWFDEC_FIXME ("allow calling native functions with more than 128 args (this one has %u)", frame.argc); frame.argc = 128; } argv = g_new (SwfdecAsValue, frame.argc); stack = cx->stack; cur = cx->cur; for (i = 0; i < frame.argc; i++) { if (cur <= &stack->elements[0]) { stack = stack->next; cur = &stack->elements[stack->used_elements]; } cur--; argv[i] = *cur; } } native->native (cx, thisp, frame.argc, argv, &rval); if (argv != frame.argv) g_free (argv); swfdec_as_context_return (cx, &rval); } static void swfdec_as_native_function_dispose (GObject *object) { SwfdecAsNativeFunction *function = SWFDEC_AS_NATIVE_FUNCTION (object); g_free (function->name); function->name = NULL; G_OBJECT_CLASS (swfdec_as_native_function_parent_class)->dispose (object); } static void swfdec_as_native_function_class_init (SwfdecAsNativeFunctionClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); SwfdecAsFunctionClass *function_class = SWFDEC_AS_FUNCTION_CLASS (klass); object_class->dispose = swfdec_as_native_function_dispose; function_class->call = swfdec_as_native_function_call; } static void swfdec_as_native_function_init (SwfdecAsNativeFunction *function) { } /** * swfdec_as_native_function_new: * @context: a #SwfdecAsContext * @name: name of the function * @native: function to call when executed * * Creates a new native function, that will execute @native when called. You * might want to use swfdec_as_object_add_function() instead of this function. * * Returns: a new #SwfdecAsFunction **/ SwfdecAsFunction * swfdec_as_native_function_new (SwfdecAsContext *context, const char *name, SwfdecAsNative native) { SwfdecAsFunction *fun; SwfdecAsObject *object; g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (context), NULL); g_return_val_if_fail (native != NULL, NULL); fun = swfdec_as_native_function_new_bare (context, name, native); object = swfdec_as_relay_get_as_object (SWFDEC_AS_RELAY (fun)); swfdec_as_object_set_constructor_by_name (object, SWFDEC_AS_STR_Function, NULL); swfdec_as_object_set_variable_flags (object, SWFDEC_AS_STR___proto__, SWFDEC_AS_VARIABLE_VERSION_6_UP); return fun; } SwfdecAsFunction * swfdec_as_native_function_new_bare (SwfdecAsContext *context, const char *name, SwfdecAsNative native) { SwfdecAsNativeFunction *fun; SwfdecAsObject *object; g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (context), NULL); g_return_val_if_fail (native != NULL, NULL); fun = g_object_new (SWFDEC_TYPE_AS_NATIVE_FUNCTION, "context", context, NULL); fun->native = native; fun->name = g_strdup (name); object = swfdec_as_object_new_empty (context); swfdec_as_object_set_relay (object, SWFDEC_AS_RELAY (fun)); return SWFDEC_AS_FUNCTION (fun); } /** * SWFDEC_AS_CHECK: * @type: required type of this object or 0 for ignoring * @result: converted this object * @...: conversion string and pointers taking converted values * * This is a shortcut macro for calling swfdec_as_native_function_check() at * the beginning of a native function. See that function for details. * It requires the native function parameters to have the default name. So your * function must be declared like this: * |[static void * my_function (SwfdecAsContext *cx, SwfdecAsObject *object, * guint argc, SwfdecAsValue *argv, SwfdecAsValue *rval);]| */ /** * swfdec_as_native_function_check: * @cx: a #SwfdecAsContext * @object: this object passed to the native function * @type: expected type of @object or 0 for any * @result: pointer to variable taking cast result of @object * @argc: count of arguments passed to the function * @argv: arguments passed to the function * @args: argument conversion string * @...: pointers to variables taking converted arguments * * This function is a convenience function to validate and convert arguments to * a native function while avoiding common pitfalls. You typically want to call * it at the beginning of every native function you write. Or you can use the * SWFDEC_AS_CHECK() macro instead which calls this function. * The @cx, @object, @argc and @argv paramters should be passed verbatim from * the function call to your native function. If @type is not 0, @object is then * checked to be of that type and cast to @result. After that the @args string * is used to convert the arguments. Every character in @args describes the * conversion of one argument. For that argument, you have to pass a pointer * that takes the value. For the conversion, the default conversion functions * like swfdec_as_value_to_string() are used. If not enough arguments are * available, the function stops converting and returns %NULL. The following * conversion characters are allowed: * "b": convert to boolean. Requires a %gboolean pointer * * "i": convert to integer. Requires an %integer pointer * * "m": convert to an existing movieclip. This is only valid * inside Swfdec. Requires a %SwfdecMovie pointer. * * "M": convert to a movieclip or %NULL. This is only valid * inside Swfdec. If the movie has already been destroyed, * the pointer will be set to %NULL * "n": convert to number. Requires a %double pointer * * "o": convert to object. Requires a #SwfdecAsObject pointer. * If the conversion fails, this function immediately returns * %FALSE. * "O": convert to object or %NULL. Requires a #SwfdecAsObject * pointer. * "s": convert to garbage-collected string. Requires a const * %char pointer * "v": copy the value. The given argument must be a pointer * to a #SwfdecAsValue * "|": optional arguments follow. Optional arguments will be * initialized to the empty value for their type. This * conversion character is only allowed once in the conversion * string. * * * Returns: %TRUE if the conversion succeeded, %FALSE otherwise **/ gboolean swfdec_as_native_function_check (SwfdecAsContext *cx, SwfdecAsObject *object, GType type, gpointer *result, guint argc, SwfdecAsValue *argv, const char *args, ...) { gboolean ret; va_list varargs; g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (cx), FALSE); g_return_val_if_fail (type == 0 || result != NULL, FALSE); va_start (varargs, args); ret = swfdec_as_native_function_checkv (cx, object, type, result, argc, argv, args, varargs); va_end (varargs); return ret; } /** * swfdec_as_native_function_checkv: * @cx: a #SwfdecAsContext * @object: this object passed to the native function * @type: expected type of @object * @result: pointer to variable taking cast result of @object * @argc: count of arguments passed to the function * @argv: arguments passed to the function * @args: argument conversion string * @varargs: pointers to variables taking converted arguments * * This is the valist version of swfdec_as_native_function_check(). See that * function for details. * * Returns: %TRUE if the conversion succeeded, %FALSE otherwise **/ gboolean swfdec_as_native_function_checkv (SwfdecAsContext *cx, SwfdecAsObject *object, GType type, gpointer *result, guint argc, SwfdecAsValue *argv, const char *args, va_list varargs) { guint i; gboolean optional = FALSE; g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (cx), FALSE); g_return_val_if_fail (type == 0 || result != NULL, FALSE); /* check that we got a valid type */ if (type) { if (G_TYPE_CHECK_INSTANCE_TYPE (object, type)) { *result = object; } else if (object && G_TYPE_CHECK_INSTANCE_TYPE (object->relay, type)) { *result = object->relay; } else { return FALSE; } } for (i = 0; *args && i < argc; i++, args++) { switch (*args) { case 'v': { SwfdecAsValue *val = va_arg (varargs, SwfdecAsValue *); *val = argv[i]; } break; case 'b': { gboolean *b = va_arg (varargs, gboolean *); *b = swfdec_as_value_to_boolean (cx, argv[i]); } break; case 'i': { int *j = va_arg (varargs, int *); *j = swfdec_as_value_to_integer (cx, argv[i]); } break; case 'm': case 'M': { SwfdecMovie *m; SwfdecMovie **arg = va_arg (varargs, SwfdecMovie **); if (SWFDEC_AS_VALUE_IS_MOVIE (argv[i])) { m = SWFDEC_AS_VALUE_GET_MOVIE (argv[i]); } else { m = NULL; } if (m == NULL && *args != 'M') return FALSE; *arg = m; } break; case 'n': { double *d = va_arg (varargs, double *); *d = swfdec_as_value_to_number (cx, argv[i]); } break; case 's': { const char **s = va_arg (varargs, const char **); *s = swfdec_as_value_to_string (cx, argv[i]); } break; case 'o': case 'O': { SwfdecAsObject **o = va_arg (varargs, SwfdecAsObject **); *o = swfdec_as_value_to_object (cx, argv[i]); if (*o == NULL && *args != 'O') return FALSE; } break; case '|': g_return_val_if_fail (optional == FALSE, FALSE); optional = TRUE; i--; break; default: g_warning ("'%c' is not a valid type conversion", *args); return FALSE; } } if (*args && !optional && *args != '|') return FALSE; return TRUE; }