📄 swfdec_as_context.c
字号:
/* Swfdec * Copyright (C) 2007 Benjamin Otte <otte@gnome.org> * * 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 <math.h>#include <string.h>#include "swfdec_as_context.h"#include "swfdec_as_array.h"#include "swfdec_as_boolean.h"#include "swfdec_as_frame_internal.h"#include "swfdec_as_function.h"#include "swfdec_as_internal.h"#include "swfdec_as_interpret.h"#include "swfdec_as_math.h"#include "swfdec_as_native_function.h"#include "swfdec_as_number.h"#include "swfdec_as_object.h"#include "swfdec_as_stack.h"#include "swfdec_as_string.h"#include "swfdec_as_strings.h"#include "swfdec_as_types.h"#include "swfdec_debug.h"#include "swfdec_script.h"/*** GARBAGE COLLECTION DOCS ***/#define SWFDEC_AS_GC_MARK (1 << 0) /* only valid during GC */#define SWFDEC_AS_GC_ROOT (1 << 1) /* for objects: rooted, for strings: static *//** * SECTION:Internals * @title: Internals and garbage collection * @short_description: understanding internals such as garbage collection * @see_also: #SwfdecAsContext * * This section deals with the internals of the Swfdec Actionscript engine. You * should probably read this first when trying to write code with it. If you're * just trying to use Swfdec for creating Flash content, you can probably skip * this section. * * First, I'd like to note that the Swfdec script engine has to be modeled very * closely after the existing Flash players. So if there are some behaviours * that seem stupid at first sight, it might very well be that it was chosen for * a very particular reason. Now on to the features. * * The Swfdec script engine tries to imitate Actionscript as good as possible. * Actionscript is similar to Javascript, but not equal. Depending on the * version of the script executed it might be more or less similar. An important * example is that Flash in versions up to 6 did case-insensitive variable * lookups. * * The script engine starts its life when it is initialized via * swfdec_as_context_startup (). At that point, the basic objects are created. * After this function has been called, you can start executing code. All code * execution happens by creating a new #SwfdecAsFrame and then calling * swfdec_as_context_run() to execute it. This function is the single entry * point for code execution. Convenience functions exist that make executing * code easy, most notably swfdec_as_object_run() and * swfdec_as_object_call(). * * It is also easily possible to extend the environment by adding new objects. * In fact, without doing so, the environment is pretty bare as it just contains * the basic Math, String, Number, Array, Date and Boolean objects. This is done * by adding #SwfdecAsNative functions to the environment. The easy way * to do this is via swfdec_as_object_add_function(). * * The Swfdec script engine is dynamically typed and knows about different types * of values. See #SwfdecAsValue for the different values. Memory management is * done using a mark and sweep garbage collector. You can initiate a garbage * collection cycle by calling swfdec_as_context_gc() or * swfdec_as_context_maybe_gc(). You should do this regularly to avoid excessive * memory use. The #SwfdecAsContext will then collect the objects and strings it * is keeping track of. If you want to use an object or string in the script * engine, you'll have to add it first via swfdec_as_object_add() or * swfdec_as_context_get_string() and swfdec_as_context_give_string(), * respectively. * * Garbage-collected strings are special in Swfdec in that they are unique. This * means the same string exists exactly once. Because of this you can do * equality comparisons using == instead of strcmp. It also means that if you * forget to add a string to the context before using it, your app will most * likely not work, since the string will not compare equal to any other string. * * When a garbage collection cycle happens, all reachable objects and strings * are marked and all unreachable ones are freed. This is done by calling the * context class's mark function which will mark all reachable objects. This is * usually called the "root set". For any reachable object, the object's mark * function is called so that the object in turn can mark all objects it can * reach itself. Marking is done via functions described below. *//*** GTK-DOC ***//** * SECTION:SwfdecAsContext * @title: SwfdecAsContext * @short_description: the main script engine context * @see_also: SwfdecPlayer * * A #SwfdecAsContext provides the main execution environment for Actionscript * execution. It provides the objects typically available in ECMAScript and * manages script execution, garbage collection etc. #SwfdecPlayer is a * subclass of the context that implements Flash specific objects on top of it. * However, it is possible to use the context for completely different functions * where a sandboxed scripting environment is needed. An example is the Swfdec * debugger. * <note>The Actionscript engine is similar, but not equal to Javascript. It * is not very different, but it is different.</note> *//** * SwfdecAsContext * * This is the main object ued to hold the state of a script engine. All members * are private and should not be accessed. * * Subclassing this structure to get scripting support in your own appliation is * encouraged. *//** * SwfdecAsContextState * @SWFDEC_AS_CONTEXT_NEW: the context is not yet initialized, * swfdec_as_context_startup() needs to be called. * @SWFDEC_AS_CONTEXT_RUNNING: the context is running normally * @SWFDEC_AS_CONTEXT_INTERRUPTED: the context has been interrupted by a * debugger * @SWFDEC_AS_CONTEXT_ABORTED: the context has aborted execution due to a * fatal error * * The state of the context describes what operations are possible on the context. * It will be in the state @SWFDEC_AS_CONTEXT_STATE_RUNNING almost all the time. If * it is in the state @SWFDEC_AS_CONTEXT_STATE_ABORTED, it will not work anymore and * every operation on it will instantly fail. *//*** RUNNING STATE ***//** * swfdec_as_context_abort: * @context: a #SwfdecAsContext * @reason: a string describing why execution was aborted * * Aborts script execution in @context. Call this functon if the script engine * encountered a fatal error and cannot continue. A possible reason for this is * an out-of-memory condition. **/voidswfdec_as_context_abort (SwfdecAsContext *context, const char *reason){ g_return_if_fail (context); SWFDEC_ERROR ("%s", reason); context->state = SWFDEC_AS_CONTEXT_ABORTED;}/*** MEMORY MANAGEMENT ***//** * swfdec_as_context_use_mem: * @context: a #SwfdecAsContext * @bytes: number of bytes to use * * Registers @bytes additional bytes as in use by the @context. This function * keeps track of the memory that script code consumes. If too many memory is * in use, this function may decide to stop the script engine with an out of * memory error. * * Returns: %TRUE if the memory could be allocated. %FALSE on OOM. **/gbooleanswfdec_as_context_use_mem (SwfdecAsContext *context, gsize bytes){ g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (context), FALSE); g_return_val_if_fail (bytes > 0, FALSE); if (context->state == SWFDEC_AS_CONTEXT_ABORTED) return FALSE; context->memory += bytes; context->memory_since_gc += bytes; /* FIXME: Don't foget to abort on OOM */ return TRUE;}/** * swfdec_as_context_unuse_mem: * @context: a #SwfdecAsContext * @bytes: number of bytes to release * * Releases a number of bytes previously allocated using * swfdec_as_context_use_mem(). See that function for details. **/voidswfdec_as_context_unuse_mem (SwfdecAsContext *context, gsize bytes){ g_return_if_fail (SWFDEC_IS_AS_CONTEXT (context)); g_return_if_fail (bytes > 0); g_return_if_fail (context->memory >= bytes); context->memory -= bytes;}/*** GC ***/static gbooleanswfdec_as_context_remove_strings (gpointer key, gpointer value, gpointer data){ SwfdecAsContext *context = data; char *string; string = (char *) value; /* it doesn't matter that rooted strings aren't destroyed, they're constant */ if (string[0] & SWFDEC_AS_GC_ROOT) { SWFDEC_LOG ("rooted: %s", (char *) key); return FALSE; } else if (string[0] & SWFDEC_AS_GC_MARK) { SWFDEC_LOG ("marked: %s", (char *) key); string[0] &= ~SWFDEC_AS_GC_MARK; return FALSE; } else { gsize len; SWFDEC_LOG ("deleted: %s", (char *) key); len = (strlen ((char *) key) + 2); swfdec_as_context_unuse_mem (context, len); g_slice_free1 (len, value); return TRUE; }}static gbooleanswfdec_as_context_remove_objects (gpointer key, gpointer value, gpointer debugger){ SwfdecAsObject *object; object = key; /* we only check for mark here, not root, since this works on destroy, too */ if (object->flags & SWFDEC_AS_GC_MARK) { object->flags &= ~SWFDEC_AS_GC_MARK; SWFDEC_LOG ("%s: %s %p", (object->flags & SWFDEC_AS_GC_ROOT) ? "rooted" : "marked", G_OBJECT_TYPE_NAME (object), object); return FALSE; } else { SWFDEC_LOG ("deleted: %s %p", G_OBJECT_TYPE_NAME (object), object); if (debugger) { SwfdecAsDebuggerClass *klass = SWFDEC_AS_DEBUGGER_GET_CLASS (debugger); if (klass->remove) klass->remove (debugger, object->context, object); } swfdec_as_object_collect (object); return TRUE; }}static voidswfdec_as_context_collect (SwfdecAsContext *context){ SWFDEC_INFO (">> collecting garbage"); /* NB: This functions is called without GC from swfdec_as_context_dispose */ g_hash_table_foreach_remove (context->strings, swfdec_as_context_remove_strings, context); g_hash_table_foreach_remove (context->objects, swfdec_as_context_remove_objects, context->debugger); SWFDEC_INFO (">> done collecting garbage");}/** * swfdec_as_object_mark: * @object: a #SwfdecAsObject * * Mark @object as being in use. Calling this function is only valid during * the marking phase of garbage collection. **/voidswfdec_as_object_mark (SwfdecAsObject *object){ SwfdecAsObjectClass *klass; if (object->flags & SWFDEC_AS_GC_MARK) return; object->flags |= SWFDEC_AS_GC_MARK; klass = SWFDEC_AS_OBJECT_GET_CLASS (object); g_assert (klass->mark); klass->mark (object);}/** * swfdec_as_string_mark: * @string: a garbage-collected string * * Mark @string as being in use. Calling this function is only valid during * the marking phase of garbage collection. **/voidswfdec_as_string_mark (const char *string){ char *str = (char *) string - 1; if (*str == 0) *str = SWFDEC_AS_GC_MARK;}/** * swfdec_as_value_mark: * @value: a #SwfdecAsValue * * Mark @value as being in use. This is just a convenience function that calls * the right marking function depending on the value's type. Calling this * function is only valid during the marking phase of garbage collection. **/voidswfdec_as_value_mark (SwfdecAsValue *value){ g_return_if_fail (SWFDEC_IS_AS_VALUE (value)); if (SWFDEC_AS_VALUE_IS_OBJECT (value)) { swfdec_as_object_mark (SWFDEC_AS_VALUE_GET_OBJECT (value)); } else if (SWFDEC_AS_VALUE_IS_STRING (value)) { swfdec_as_string_mark (SWFDEC_AS_VALUE_GET_STRING (value)); }}static voidswfdec_as_context_mark_roots (gpointer key, gpointer value, gpointer data){ SwfdecAsObject *object = key; if ((object->flags & (SWFDEC_AS_GC_MARK | SWFDEC_AS_GC_ROOT)) == SWFDEC_AS_GC_ROOT) swfdec_as_object_mark (object);}static voidswfdec_as_context_do_mark (SwfdecAsContext *context){ swfdec_as_object_mark (context->global); swfdec_as_object_mark (context->Function); if (context->Function_prototype) swfdec_as_object_mark (context->Function_prototype); swfdec_as_object_mark (context->Object); swfdec_as_object_mark (context->Object_prototype); swfdec_as_object_mark (context->Array); g_hash_table_foreach (context->objects, swfdec_as_context_mark_roots, NULL);}/** * swfdec_as_context_gc: * @context: a #SwfdecAsContext * * Calls the Swfdec Gargbage collector and reclaims any unused memory. You * should call this function or swfdec_as_context_maybe_gc() regularly. * <warning>Calling the GC during execution of code or initialization is not * allowed.</warning> **/voidswfdec_as_context_gc (SwfdecAsContext *context){ SwfdecAsContextClass *klass; g_return_if_fail (SWFDEC_IS_AS_CONTEXT (context)); g_return_if_fail (context->frame == NULL); g_return_if_fail (context->state != SWFDEC_AS_CONTEXT_NEW); if (context->state == SWFDEC_AS_CONTEXT_ABORTED) return; SWFDEC_INFO ("invoking the garbage collector"); klass = SWFDEC_AS_CONTEXT_GET_CLASS (context); g_assert (klass->mark); klass->mark (context); swfdec_as_context_collect (context); context->memory_since_gc = 0;}static gbooleanswfdec_as_context_needs_gc (SwfdecAsContext *context){ return context->memory_since_gc >= context->memory_until_gc;}/** * swfdec_as_context_maybe_gc: * @context: a #SwfdecAsContext * * Calls the garbage collector if necessary. It's a good idea to call this * function regularly instead of swfdec_as_context_gc() as it only does collect * garage as needed. For example, #SwfdecPlayer calls this function after every * frame advancement. **/voidswfdec_as_context_maybe_gc (SwfdecAsContext *context){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -