~ubuntu-branches/ubuntu/jaunty/swfdec0.8/jaunty

« back to all changes in this revision

Viewing changes to swfdec/swfdec_as_context.c

  • Committer: Package Import Robot
  • Author(s): Didier Roche
  • Date: 2008-10-10 19:15:24 UTC
  • Revision ID: package-import@ubuntu.com-20081010191524-5z85qiky1d4bvgfa
Tags: upstream-0.8.0
ImportĀ upstreamĀ versionĀ 0.8.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Swfdec
 
2
 * Copyright (C) 2007-2008 Benjamin Otte <otte@gnome.org>
 
3
 *
 
4
 * This library is free software; you can redistribute it and/or
 
5
 * modify it under the terms of the GNU Lesser General Public
 
6
 * License as published by the Free Software Foundation; either
 
7
 * version 2.1 of the License, or (at your option) any later version.
 
8
 * 
 
9
 * This library is distributed in the hope that it will be useful,
 
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
12
 * Lesser General Public License for more details.
 
13
 * 
 
14
 * You should have received a copy of the GNU Lesser General Public
 
15
 * License along with this library; if not, write to the Free Software
 
16
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 
 
17
 * Boston, MA  02110-1301  USA
 
18
 */
 
19
 
 
20
#ifdef HAVE_CONFIG_H
 
21
#include "config.h"
 
22
#endif
 
23
 
 
24
#include <math.h>
 
25
#include <string.h>
 
26
#include <stdlib.h>
 
27
#include "swfdec_as_context.h"
 
28
#include "swfdec_as_array.h"
 
29
#include "swfdec_as_frame_internal.h"
 
30
#include "swfdec_as_function.h"
 
31
#include "swfdec_as_initialize.h"
 
32
#include "swfdec_as_internal.h"
 
33
#include "swfdec_as_interpret.h"
 
34
#include "swfdec_as_native_function.h"
 
35
#include "swfdec_as_object.h"
 
36
#include "swfdec_as_stack.h"
 
37
#include "swfdec_as_strings.h"
 
38
#include "swfdec_as_types.h"
 
39
#include "swfdec_constant_pool.h"
 
40
#include "swfdec_debug.h"
 
41
#include "swfdec_gc_object.h"
 
42
#include "swfdec_internal.h" /* for swfdec_player_preinit_global() */
 
43
#include "swfdec_script.h"
 
44
 
 
45
/*** GARBAGE COLLECTION DOCS ***/
 
46
 
 
47
/**
 
48
 * SECTION:Internals
 
49
 * @title: Internals of the script engine
 
50
 * @short_description: understanding internals such as garbage collection
 
51
 * @see_also: #SwfdecAsContext, #SwfdecGcObject
 
52
 *
 
53
 * This section deals with the internals of the Swfdec Actionscript engine. You
 
54
 * should probably read this first when trying to write code with it. If you're
 
55
 * just trying to use Swfdec for creating Flash content, you can probably skip
 
56
 * this section.
 
57
 *
 
58
 * First, I'd like to note that the Swfdec script engine has to be modeled very 
 
59
 * closely after the existing Flash players. So if there are some behaviours
 
60
 * that seem stupid at first sight, it might very well be that it was chosen for
 
61
 * a very particular reason. Now on to the features.
 
62
 *
 
63
 * The Swfdec script engine tries to imitate Actionscript as good as possible.
 
64
 * Actionscript is similar to Javascript, but not equal. Depending on the 
 
65
 * version of the script executed it might be more or less similar. An important
 
66
 * example is that Flash in versions up to 6 did case-insensitive variable 
 
67
 * lookups.
 
68
 *
 
69
 * The script engine starts its life when it is initialized via 
 
70
 * swfdec_as_context_startup(). At that point, the basic objects are created.
 
71
 * After this function has been called, you can start executing code. Code
 
72
 * execution happens by calling swfdec_as_function_call_full() or convenience
 
73
 * wrappers like swfdec_as_object_run() or swfdec_as_object_call().
 
74
 *
 
75
 * It is also easily possible to extend the environment by adding new objects.
 
76
 * In fact, without doing so, the environment is pretty bare as it just contains
 
77
 * the basic Math, String, Number, Array, Date and Boolean objects. This is done
 
78
 * by adding #SwfdecAsNative functions to the environment. The easy way
 
79
 * to do this is via swfdec_as_object_add_function().
 
80
 *
 
81
 * The Swfdec script engine is dynamically typed and knows about different types
 
82
 * of values. See #SwfdecAsValue for the different values. Memory management is
 
83
 * done using a mark and sweep garbage collector. You can initiate a garbage 
 
84
 * collection cycle by calling swfdec_as_context_gc() or 
 
85
 * swfdec_as_context_maybe_gc(). You should do this regularly to avoid excessive
 
86
 * memory use. The #SwfdecAsContext will then collect the objects and strings it
 
87
 * is keeping track of. If you want to use an object or string in the script 
 
88
 * engine, you'll have to add it first via swfdec_as_object_add() or
 
89
 * swfdec_as_context_get_string() and swfdec_as_context_give_string(), 
 
90
 * respectively.
 
91
 *
 
92
 * Garbage-collected strings are special in Swfdec in that they are unique. This
 
93
 * means the same string exists exactly once. Because of this you can do 
 
94
 * equality comparisons using == instead of strcmp. It also means that if you 
 
95
 * forget to add a string to the context before using it, your app will most 
 
96
 * likely not work, since the string will not compare equal to any other string.
 
97
 *
 
98
 * When a garbage collection cycle happens, all reachable objects and strings 
 
99
 * are marked and all unreachable ones are freed. This is done by calling the
 
100
 * context class's mark function which will mark all reachable objects. This is
 
101
 * usually called the "root set". For any reachable object, the object's mark
 
102
 * function is called so that the object in turn can mark all objects it can 
 
103
 * reach itself. Marking is done via functions described below.
 
104
 */
 
105
 
 
106
/*** GTK-DOC ***/
 
107
 
 
108
/**
 
109
 * SECTION:SwfdecAsContext
 
110
 * @title: SwfdecAsContext
 
111
 * @short_description: the main script engine context
 
112
 * @see_also: SwfdecPlayer
 
113
 *
 
114
 * A #SwfdecAsContext provides the main execution environment for Actionscript
 
115
 * execution. It provides the objects typically available in ECMAScript and
 
116
 * manages script execution, garbage collection etc. #SwfdecPlayer is a
 
117
 * subclass of the context that implements Flash specific objects on top of it.
 
118
 * However, it is possible to use the context for completely different functions
 
119
 * where a sandboxed scripting environment is needed. An example is the Swfdec 
 
120
 * debugger.
 
121
 * <note>The Actionscript engine is similar, but not equal to Javascript. It
 
122
 * is not very different, but it is different.</note>
 
123
 */
 
124
 
 
125
/**
 
126
 * SwfdecAsContext
 
127
 *
 
128
 * This is the main object ued to hold the state of a script engine. All members 
 
129
 * are private and should not be accessed.
 
130
 *
 
131
 * Subclassing this structure to get scripting support in your own appliation is
 
132
 * encouraged.
 
133
 */
 
134
 
 
135
/**
 
136
 * SwfdecAsContextState
 
137
 * @SWFDEC_AS_CONTEXT_NEW: the context is not yet initialized, 
 
138
 *                         swfdec_as_context_startup() needs to be called.
 
139
 * @SWFDEC_AS_CONTEXT_RUNNING: the context is running normally
 
140
 * @SWFDEC_AS_CONTEXT_INTERRUPTED: the context has been interrupted by a 
 
141
 *                             debugger
 
142
 * @SWFDEC_AS_CONTEXT_ABORTED: the context has aborted execution due to a 
 
143
 *                         fatal error
 
144
 *
 
145
 * The state of the context describes what operations are possible on the context.
 
146
 * It will be in the state @SWFDEC_AS_CONTEXT_STATE_RUNNING almost all the time. If
 
147
 * it is in the state @SWFDEC_AS_CONTEXT_STATE_ABORTED, it will not work anymore and
 
148
 * every operation on it will instantly fail.
 
149
 */
 
150
 
 
151
/*** RUNNING STATE ***/
 
152
 
 
153
/**
 
154
 * swfdec_as_context_abort:
 
155
 * @context: a #SwfdecAsContext
 
156
 * @reason: a string describing why execution was aborted
 
157
 *
 
158
 * Aborts script execution in @context. Call this functon if the script engine 
 
159
 * encountered a fatal error and cannot continue. A possible reason for this is
 
160
 * an out-of-memory condition.
 
161
 **/
 
162
void
 
163
swfdec_as_context_abort (SwfdecAsContext *context, const char *reason)
 
164
{
 
165
  g_return_if_fail (context);
 
166
 
 
167
  if (context->state != SWFDEC_AS_CONTEXT_ABORTED) {
 
168
    SWFDEC_ERROR ("abort: %s", reason);
 
169
    context->state = SWFDEC_AS_CONTEXT_ABORTED;
 
170
    g_object_notify (G_OBJECT (context), "aborted");
 
171
  }
 
172
}
 
173
 
 
174
/*** MEMORY MANAGEMENT ***/
 
175
 
 
176
/**
 
177
 * swfdec_as_context_try_use_mem:
 
178
 * @context: a #SwfdecAsContext
 
179
 * @bytes: number of bytes to use
 
180
 *
 
181
 * Tries to register @bytes additional bytes as in use by the @context. This
 
182
 * function keeps track of the memory that script code consumes. The scripting
 
183
 * engine won't be stopped, even if there wasn't enough memory left.
 
184
 *
 
185
 * Returns: %TRUE if the memory could be allocated. %FALSE on OOM.
 
186
 **/
 
187
gboolean
 
188
swfdec_as_context_try_use_mem (SwfdecAsContext *context, gsize bytes)
 
189
{
 
190
  g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (context), FALSE);
 
191
  g_return_val_if_fail (bytes > 0, FALSE);
 
192
 
 
193
  if (context->state == SWFDEC_AS_CONTEXT_ABORTED)
 
194
    return FALSE;
 
195
  
 
196
  context->memory += bytes;
 
197
  context->memory_since_gc += bytes;
 
198
  SWFDEC_LOG ("+%4"G_GSIZE_FORMAT" bytes, total %7"G_GSIZE_FORMAT" (%7"G_GSIZE_FORMAT" since GC)",
 
199
      bytes, context->memory, context->memory_since_gc);
 
200
 
 
201
  return TRUE;
 
202
}
 
203
 
 
204
/**
 
205
 * swfdec_as_context_use_mem:
 
206
 * @context: a #SwfdecAsContext
 
207
 * @bytes: number of bytes to use
 
208
 *
 
209
 * Registers @bytes additional bytes as in use by the @context. This function
 
210
 * keeps track of the memory that script code consumes. If too much memory is
 
211
 * in use, this function may decide to stop the script engine with an out of
 
212
 * memory error.
 
213
 **/
 
214
void
 
215
swfdec_as_context_use_mem (SwfdecAsContext *context, gsize bytes)
 
216
{
 
217
  g_return_if_fail (SWFDEC_IS_AS_CONTEXT (context));
 
218
  g_return_if_fail (bytes > 0);
 
219
 
 
220
  /* FIXME: Don't forget to abort on OOM */
 
221
  if (!swfdec_as_context_try_use_mem (context, bytes)) {
 
222
    swfdec_as_context_abort (context, "Out of memory");
 
223
    /* add the memory anyway, as we're gonna make use of it. */
 
224
    context->memory += bytes;
 
225
    context->memory_since_gc += bytes;
 
226
  }
 
227
}
 
228
 
 
229
/**
 
230
 * swfdec_as_context_unuse_mem:
 
231
 * @context: a #SwfdecAsContext
 
232
 * @bytes: number of bytes to release
 
233
 *
 
234
 * Releases a number of bytes previously allocated using 
 
235
 * swfdec_as_context_use_mem(). See that function for details.
 
236
 **/
 
237
void
 
238
swfdec_as_context_unuse_mem (SwfdecAsContext *context, gsize bytes)
 
239
{
 
240
  g_return_if_fail (SWFDEC_IS_AS_CONTEXT (context));
 
241
  g_return_if_fail (bytes > 0);
 
242
  g_return_if_fail (context->memory >= bytes);
 
243
 
 
244
  context->memory -= bytes;
 
245
  SWFDEC_LOG ("-%4"G_GSIZE_FORMAT" bytes, total %7"G_GSIZE_FORMAT" (%7"G_GSIZE_FORMAT" since GC)",
 
246
      bytes, context->memory, context->memory_since_gc);
 
247
}
 
248
 
 
249
/*** GC ***/
 
250
 
 
251
static gboolean
 
252
swfdec_as_context_remove_strings (gpointer key, gpointer value, gpointer data)
 
253
{
 
254
  SwfdecAsContext *context = data;
 
255
  char *string;
 
256
 
 
257
  string = (char *) value;
 
258
  /* it doesn't matter that rooted strings aren't destroyed, they're constant */
 
259
  if (string[0] & SWFDEC_AS_GC_ROOT) {
 
260
    SWFDEC_LOG ("rooted: %s", (char *) key);
 
261
    return FALSE;
 
262
  } else if (string[0] & SWFDEC_AS_GC_MARK) {
 
263
    SWFDEC_LOG ("marked: %s", (char *) key);
 
264
    string[0] &= ~SWFDEC_AS_GC_MARK;
 
265
    return FALSE;
 
266
  } else {
 
267
    gsize len;
 
268
    SWFDEC_LOG ("deleted: %s", (char *) key);
 
269
    len = (strlen ((char *) key) + 2);
 
270
    swfdec_as_context_unuse_mem (context, len);
 
271
    g_slice_free1 (len, value);
 
272
    return TRUE;
 
273
  }
 
274
}
 
275
 
 
276
static gboolean
 
277
swfdec_as_context_remove_objects (gpointer key, gpointer value, gpointer debugger)
 
278
{
 
279
  SwfdecGcObject *gc;
 
280
 
 
281
  gc = key;
 
282
  /* we only check for mark here, not root, since this works on destroy, too */
 
283
  if (gc->flags & SWFDEC_AS_GC_MARK) {
 
284
    gc->flags &= ~SWFDEC_AS_GC_MARK;
 
285
    SWFDEC_LOG ("%s: %s %p", (gc->flags & SWFDEC_AS_GC_ROOT) ? "rooted" : "marked",
 
286
        G_OBJECT_TYPE_NAME (gc), gc);
 
287
    return FALSE;
 
288
  } else {
 
289
    SWFDEC_LOG ("deleted: %s %p", G_OBJECT_TYPE_NAME (gc), gc);
 
290
    g_object_unref (gc);
 
291
    return TRUE;
 
292
  }
 
293
}
 
294
 
 
295
static void
 
296
swfdec_as_context_collect (SwfdecAsContext *context)
 
297
{
 
298
  SWFDEC_INFO (">> collecting garbage");
 
299
  /* NB: This functions is called without GC from swfdec_as_context_dispose */
 
300
  g_hash_table_foreach_remove (context->strings, 
 
301
    swfdec_as_context_remove_strings, context);
 
302
  g_hash_table_foreach_remove (context->objects, 
 
303
    swfdec_as_context_remove_objects, context->debugger);
 
304
  SWFDEC_INFO (">> done collecting garbage");
 
305
}
 
306
 
 
307
/**
 
308
 * swfdec_as_string_mark:
 
309
 * @string: a garbage-collected string
 
310
 *
 
311
 * Mark @string as being in use. Calling this function is only valid during
 
312
 * the marking phase of garbage collection.
 
313
 **/
 
314
void
 
315
swfdec_as_string_mark (const char *string)
 
316
{
 
317
  char *str;
 
318
 
 
319
  g_return_if_fail (string != NULL);
 
320
 
 
321
  str = (char *) string - 1;
 
322
  if (*str == 0)
 
323
    *str = SWFDEC_AS_GC_MARK;
 
324
}
 
325
 
 
326
/**
 
327
 * swfdec_as_value_mark:
 
328
 * @value: a #SwfdecAsValue
 
329
 *
 
330
 * Mark @value as being in use. This is just a convenience function that calls
 
331
 * the right marking function depending on the value's type. Calling this 
 
332
 * function is only valid during the marking phase of garbage collection.
 
333
 **/
 
334
void
 
335
swfdec_as_value_mark (SwfdecAsValue *value)
 
336
{
 
337
  g_return_if_fail (SWFDEC_IS_AS_VALUE (value));
 
338
 
 
339
  if (SWFDEC_AS_VALUE_IS_OBJECT (value)) {
 
340
    swfdec_gc_object_mark (value->value.object);
 
341
  } else if (SWFDEC_AS_VALUE_IS_STRING (value)) {
 
342
    swfdec_as_string_mark (SWFDEC_AS_VALUE_GET_STRING (value));
 
343
  }
 
344
}
 
345
 
 
346
static void
 
347
swfdec_as_context_mark_roots (gpointer key, gpointer value, gpointer data)
 
348
{
 
349
  SwfdecGcObject *object = key;
 
350
 
 
351
  if ((object->flags & (SWFDEC_AS_GC_MARK | SWFDEC_AS_GC_ROOT)) == SWFDEC_AS_GC_ROOT)
 
352
    swfdec_gc_object_mark (object);
 
353
}
 
354
 
 
355
/* FIXME: replace this with refcounted strings? */
 
356
static void 
 
357
swfdec_as_context_mark_constant_pools (gpointer key, gpointer value, gpointer unused)
 
358
{
 
359
  SwfdecConstantPool *pool = value;
 
360
  guint i;
 
361
 
 
362
  for (i = 0; i < swfdec_constant_pool_size (pool); i++) {
 
363
    const char *s = swfdec_constant_pool_get (pool, i);
 
364
    if (s)
 
365
      swfdec_as_string_mark (s);
 
366
  }
 
367
}
 
368
 
 
369
static void
 
370
swfdec_as_context_do_mark (SwfdecAsContext *context)
 
371
{
 
372
  /* This if is needed for SwfdecPlayer */
 
373
  if (context->global) {
 
374
    swfdec_gc_object_mark (context->global);
 
375
    swfdec_gc_object_mark (context->Function);
 
376
    swfdec_gc_object_mark (context->Function_prototype);
 
377
    swfdec_gc_object_mark (context->Object);
 
378
    swfdec_gc_object_mark (context->Object_prototype);
 
379
  }
 
380
  if (context->exception)
 
381
    swfdec_as_value_mark (&context->exception_value);
 
382
  g_hash_table_foreach (context->objects, swfdec_as_context_mark_roots, NULL);
 
383
  g_hash_table_foreach (context->constant_pools, swfdec_as_context_mark_constant_pools, NULL);
 
384
}
 
385
 
 
386
/**
 
387
 * swfdec_as_context_gc:
 
388
 * @context: a #SwfdecAsContext
 
389
 *
 
390
 * Calls the Swfdec Gargbage collector and reclaims any unused memory. You 
 
391
 * should call this function or swfdec_as_context_maybe_gc() regularly.
 
392
 * <warning>Calling the GC during execution of code or initialization is not
 
393
 *          allowed.</warning>
 
394
 **/
 
395
void
 
396
swfdec_as_context_gc (SwfdecAsContext *context)
 
397
{
 
398
  SwfdecAsContextClass *klass;
 
399
 
 
400
  g_return_if_fail (SWFDEC_IS_AS_CONTEXT (context));
 
401
  g_return_if_fail (context->frame == NULL);
 
402
  g_return_if_fail (context->state == SWFDEC_AS_CONTEXT_RUNNING);
 
403
 
 
404
  if (context->state == SWFDEC_AS_CONTEXT_ABORTED)
 
405
    return;
 
406
  SWFDEC_INFO ("invoking the garbage collector");
 
407
  klass = SWFDEC_AS_CONTEXT_GET_CLASS (context);
 
408
  g_assert (klass->mark);
 
409
  klass->mark (context);
 
410
  swfdec_as_context_collect (context);
 
411
  context->memory_since_gc = 0;
 
412
}
 
413
 
 
414
static gboolean
 
415
swfdec_as_context_needs_gc (SwfdecAsContext *context)
 
416
{
 
417
  return context->memory_since_gc >= context->memory_until_gc;
 
418
}
 
419
 
 
420
/**
 
421
 * swfdec_as_context_maybe_gc:
 
422
 * @context: a #SwfdecAsContext
 
423
 *
 
424
 * Calls the garbage collector if necessary. It's a good idea to call this
 
425
 * function regularly instead of swfdec_as_context_gc() as it only does collect
 
426
 * garage as needed. For example, #SwfdecPlayer calls this function after every
 
427
 * frame advancement.
 
428
 **/
 
429
void
 
430
swfdec_as_context_maybe_gc (SwfdecAsContext *context)
 
431
{
 
432
  g_return_if_fail (SWFDEC_IS_AS_CONTEXT (context));
 
433
  g_return_if_fail (context->state == SWFDEC_AS_CONTEXT_RUNNING);
 
434
  g_return_if_fail (context->frame == NULL);
 
435
 
 
436
  if (swfdec_as_context_needs_gc (context))
 
437
    swfdec_as_context_gc (context);
 
438
}
 
439
 
 
440
/*** SWFDEC_AS_CONTEXT ***/
 
441
 
 
442
enum {
 
443
  TRACE,
 
444
  LAST_SIGNAL
 
445
};
 
446
 
 
447
enum {
 
448
  PROP_0,
 
449
  PROP_DEBUGGER,
 
450
  PROP_RANDOM_SEED,
 
451
  PROP_ABORTED,
 
452
  PROP_UNTIL_GC
 
453
};
 
454
 
 
455
G_DEFINE_TYPE (SwfdecAsContext, swfdec_as_context, G_TYPE_OBJECT)
 
456
static guint signals[LAST_SIGNAL] = { 0, };
 
457
 
 
458
static void
 
459
swfdec_as_context_get_property (GObject *object, guint param_id, GValue *value, 
 
460
    GParamSpec * pspec)
 
461
{
 
462
  SwfdecAsContext *context = SWFDEC_AS_CONTEXT (object);
 
463
 
 
464
  switch (param_id) {
 
465
    case PROP_DEBUGGER:
 
466
      g_value_set_object (value, context->debugger);
 
467
      break;
 
468
    case PROP_ABORTED:
 
469
      g_value_set_boolean (value, context->state == SWFDEC_AS_CONTEXT_ABORTED);
 
470
      break;
 
471
    case PROP_UNTIL_GC:
 
472
      g_value_set_ulong (value, (gulong) context->memory_until_gc);
 
473
      break;
 
474
    default:
 
475
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
 
476
      break;
 
477
  }
 
478
}
 
479
 
 
480
static void
 
481
swfdec_as_context_set_property (GObject *object, guint param_id, const GValue *value, 
 
482
    GParamSpec * pspec)
 
483
{
 
484
  SwfdecAsContext *context = SWFDEC_AS_CONTEXT (object);
 
485
 
 
486
  switch (param_id) {
 
487
    case PROP_DEBUGGER:
 
488
      context->debugger = SWFDEC_AS_DEBUGGER (g_value_dup_object (value));
 
489
      break;
 
490
    case PROP_RANDOM_SEED:
 
491
      g_rand_set_seed (context->rand, g_value_get_uint (value));
 
492
      break;
 
493
    case PROP_UNTIL_GC:
 
494
      context->memory_until_gc = g_value_get_ulong (value);
 
495
      break;
 
496
    default:
 
497
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
 
498
      break;
 
499
  }
 
500
}
 
501
 
 
502
static void
 
503
swfdec_as_context_dispose (GObject *object)
 
504
{
 
505
  SwfdecAsContext *context = SWFDEC_AS_CONTEXT (object);
 
506
 
 
507
  while (context->stack)
 
508
    swfdec_as_stack_pop_segment (context);
 
509
  /* We need to make sure there's no exception here. Otherwise collecting 
 
510
   * frames that are inside a try block will assert */
 
511
  swfdec_as_context_catch (context, NULL);
 
512
  swfdec_as_context_collect (context);
 
513
  if (context->memory != 0) {
 
514
    g_critical ("%zu bytes of memory left over\n", context->memory);
 
515
  }
 
516
  g_assert (g_hash_table_size (context->objects) == 0);
 
517
  g_assert (g_hash_table_size (context->constant_pools) == 0);
 
518
  g_hash_table_destroy (context->constant_pools);
 
519
  g_hash_table_destroy (context->objects);
 
520
  g_hash_table_destroy (context->strings);
 
521
  g_rand_free (context->rand);
 
522
  if (context->debugger) {
 
523
    g_object_unref (context->debugger);
 
524
    context->debugger = NULL;
 
525
  }
 
526
 
 
527
  G_OBJECT_CLASS (swfdec_as_context_parent_class)->dispose (object);
 
528
}
 
529
 
 
530
static void
 
531
swfdec_as_context_class_init (SwfdecAsContextClass *klass)
 
532
{
 
533
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
534
 
 
535
  object_class->dispose = swfdec_as_context_dispose;
 
536
  object_class->get_property = swfdec_as_context_get_property;
 
537
  object_class->set_property = swfdec_as_context_set_property;
 
538
 
 
539
  g_object_class_install_property (object_class, PROP_DEBUGGER,
 
540
      g_param_spec_object ("debugger", "debugger", "debugger used in this player",
 
541
          SWFDEC_TYPE_AS_DEBUGGER, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 
542
  g_object_class_install_property (object_class, PROP_RANDOM_SEED,
 
543
      g_param_spec_uint ("random-seed", "random seed", 
 
544
          "seed used for calculating random numbers",
 
545
          0, G_MAXUINT32, 0, G_PARAM_WRITABLE)); /* FIXME: make this readwrite for replaying? */
 
546
  g_object_class_install_property (object_class, PROP_ABORTED,
 
547
      g_param_spec_boolean ("aborted", "aborted", "set when the script engine aborts due to an error",
 
548
        FALSE, G_PARAM_READABLE));
 
549
  g_object_class_install_property (object_class, PROP_UNTIL_GC,
 
550
      g_param_spec_ulong ("memory-until-gc", "memory until gc", 
 
551
          "amount of bytes that need to be allocated before garbage collection triggers",
 
552
          0, G_MAXULONG, 8 * 1024 * 1024, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
 
553
 
 
554
  /**
 
555
   * SwfdecAsContext::trace:
 
556
   * @context: the #SwfdecAsContext affected
 
557
   * @text: the debugging string
 
558
   *
 
559
   * Emits a debugging string while running. The effect of calling any swfdec 
 
560
   * functions on the emitting @context is undefined.
 
561
   */
 
562
  signals[TRACE] = g_signal_new ("trace", G_TYPE_FROM_CLASS (klass),
 
563
      G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__STRING,
 
564
      G_TYPE_NONE, 1, G_TYPE_STRING);
 
565
 
 
566
  klass->mark = swfdec_as_context_do_mark;
 
567
}
 
568
 
 
569
static void
 
570
swfdec_as_context_init (SwfdecAsContext *context)
 
571
{
 
572
  const char *s;
 
573
 
 
574
  context->version = G_MAXUINT;
 
575
 
 
576
  context->strings = g_hash_table_new (g_str_hash, g_str_equal);
 
577
  context->objects = g_hash_table_new (g_direct_hash, g_direct_equal);
 
578
  context->constant_pools = g_hash_table_new (g_direct_hash, g_direct_equal);
 
579
 
 
580
  for (s = swfdec_as_strings; *s == '\2'; s += strlen (s) + 1) {
 
581
    g_hash_table_insert (context->strings, (char *) s + 1, (char *) s);
 
582
  }
 
583
  g_assert (*s == 0);
 
584
  context->rand = g_rand_new ();
 
585
  g_get_current_time (&context->start_time);
 
586
}
 
587
 
 
588
/*** STRINGS ***/
 
589
 
 
590
static const char *
 
591
swfdec_as_context_create_string (SwfdecAsContext *context, const char *string, gsize len)
 
592
{
 
593
  char *new;
 
594
  
 
595
  if (!swfdec_as_context_try_use_mem (context, sizeof (char) * (2 + len))) {
 
596
    swfdec_as_context_abort (context, "Out of memory");
 
597
    return SWFDEC_AS_STR_EMPTY;
 
598
  }
 
599
 
 
600
  new = g_slice_alloc (2 + len);
 
601
  memcpy (&new[1], string, len);
 
602
  new[len + 1] = 0;
 
603
  new[0] = 0; /* GC flags */
 
604
  g_hash_table_insert (context->strings, new + 1, new);
 
605
 
 
606
  return new + 1;
 
607
}
 
608
 
 
609
/**
 
610
 * swfdec_as_context_get_string:
 
611
 * @context: a #SwfdecAsContext
 
612
 * @string: a sting that is not garbage-collected
 
613
 *
 
614
 * Gets the garbage-collected version of @string. You need to call this function
 
615
 * for every not garbage-collected string that you want to use in Swfdecs script
 
616
 * interpreter.
 
617
 *
 
618
 * Returns: the garbage-collected version of @string
 
619
 **/
 
620
const char *
 
621
swfdec_as_context_get_string (SwfdecAsContext *context, const char *string)
 
622
{
 
623
  const char *ret;
 
624
  gsize len;
 
625
 
 
626
  g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (context), NULL);
 
627
  g_return_val_if_fail (string != NULL, NULL);
 
628
 
 
629
  if (g_hash_table_lookup_extended (context->strings, string, (gpointer) &ret, NULL))
 
630
    return ret;
 
631
 
 
632
  len = strlen (string);
 
633
  return swfdec_as_context_create_string (context, string, len);
 
634
}
 
635
 
 
636
/**
 
637
 * swfdec_as_context_give_string:
 
638
 * @context: a #SwfdecAsContext
 
639
 * @string: string to make refcounted
 
640
 *
 
641
 * Takes ownership of @string and returns a refcounted version of the same 
 
642
 * string. This function is the same as swfdec_as_context_get_string(), but 
 
643
 * takes ownership of @string.
 
644
 *
 
645
 * Returns: A refcounted string
 
646
 **/
 
647
const char *
 
648
swfdec_as_context_give_string (SwfdecAsContext *context, char *string)
 
649
{
 
650
  const char *ret;
 
651
 
 
652
  g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (context), NULL);
 
653
  g_return_val_if_fail (string != NULL, NULL);
 
654
 
 
655
  ret = swfdec_as_context_get_string (context, string);
 
656
  g_free (string);
 
657
  return ret;
 
658
}
 
659
 
 
660
/**
 
661
 * swfdec_as_context_is_constructing:
 
662
 * @context: a #SwfdecAsConstruct
 
663
 *
 
664
 * Determines if the contexxt is currently constructing. This information is
 
665
 * used by various constructors to do different things when they are 
 
666
 * constructing and when they are not. The Boolean, Number and String functions
 
667
 * for example setup the newly constructed objects when constructing but only
 
668
 * cast the provided argument when being called.
 
669
 *
 
670
 * Returns: %TRUE if the currently executing frame is a constructor
 
671
 **/
 
672
gboolean
 
673
swfdec_as_context_is_constructing (SwfdecAsContext *context)
 
674
{
 
675
  g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (context), FALSE);
 
676
 
 
677
  return context->frame && context->frame->construct;
 
678
}
 
679
 
 
680
/**
 
681
 * swfdec_as_context_get_frame:
 
682
 * @context: a #SwfdecAsContext
 
683
 *
 
684
 * This is a debugging function. It gets the topmost stack frame that is 
 
685
 * currently executing. If no function is executing, %NULL is returned. You can
 
686
 * easily get a backtrace with code like this:
 
687
 * |[for (frame = swfdec_as_context_get_frame (context); frame != NULL; 
 
688
 *     frame = swfdec_as_frame_get_next (frame)) {
 
689
 *   char *s = swfdec_as_object_get_debug (SWFDEC_AS_OBJECT (frame));
 
690
 *   g_print ("%s\n", s);
 
691
 *   g_free (s);
 
692
 * }]|
 
693
 *
 
694
 * Returns: the currently executing frame or %NULL if none
 
695
 **/
 
696
SwfdecAsFrame *
 
697
swfdec_as_context_get_frame (SwfdecAsContext *context)
 
698
{
 
699
  g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (context), NULL);
 
700
 
 
701
  return context->frame;
 
702
}
 
703
 
 
704
/**
 
705
 * swfdec_as_context_throw:
 
706
 * @context: a #SwfdecAsContext
 
707
 * @value: a #SwfdecAsValue to be thrown
 
708
 *
 
709
 * Throws a new exception in the @context using the given @value. This function
 
710
 * can only be called if the @context is not already throwing an exception.
 
711
 **/
 
712
void
 
713
swfdec_as_context_throw (SwfdecAsContext *context, const SwfdecAsValue *value)
 
714
{
 
715
  g_return_if_fail (SWFDEC_IS_AS_CONTEXT (context));
 
716
  g_return_if_fail (SWFDEC_IS_AS_VALUE (value));
 
717
  g_return_if_fail (!context->exception);
 
718
 
 
719
  context->exception = TRUE;
 
720
  context->exception_value = *value;
 
721
}
 
722
 
 
723
/**
 
724
 * swfdec_as_context_catch:
 
725
 * @context: a #SwfdecAsContext
 
726
 * @value: a #SwfdecAsValue to be thrown
 
727
 *
 
728
 * Removes the currently thrown exception from @context and sets @value to the
 
729
 * thrown value
 
730
 *
 
731
 * Returns: %TRUE if an exception was catched, %FALSE otherwise
 
732
 **/
 
733
gboolean
 
734
swfdec_as_context_catch (SwfdecAsContext *context, SwfdecAsValue *value)
 
735
{
 
736
  g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (context), FALSE);
 
737
 
 
738
  if (!context->exception)
 
739
    return FALSE;
 
740
 
 
741
  if (value != NULL)
 
742
    *value = context->exception_value;
 
743
 
 
744
  context->exception = FALSE;
 
745
  SWFDEC_AS_VALUE_SET_UNDEFINED (&context->exception_value);
 
746
 
 
747
  return TRUE;
 
748
}
 
749
 
 
750
/**
 
751
 * swfdec_as_context_get_time:
 
752
 * @context: a #SwfdecAsContext
 
753
 * @tv: a #GTimeVal to be set to the context's time
 
754
 *
 
755
 * This function queries the time to be used inside this context. By default,
 
756
 * this is the same as g_get_current_time(), but it may be overwriten to allow
 
757
 * things such as slower or faster playback.
 
758
 **/
 
759
void
 
760
swfdec_as_context_get_time (SwfdecAsContext *context, GTimeVal *tv)
 
761
{
 
762
  SwfdecAsContextClass *klass;
 
763
 
 
764
  g_return_if_fail (SWFDEC_IS_AS_CONTEXT (context));
 
765
  g_return_if_fail (tv != NULL);
 
766
 
 
767
  klass = SWFDEC_AS_CONTEXT_GET_CLASS (context);
 
768
  if (klass->get_time)
 
769
    klass->get_time (context, tv);
 
770
  else
 
771
    g_get_current_time (tv);
 
772
}
 
773
 
 
774
/**
 
775
 * swfdec_as_context_run:
 
776
 * @context: a #SwfdecAsContext
 
777
 *
 
778
 * Continues running the script engine. Executing code in this engine works
 
779
 * in 2 steps: First, you push the frame to be executed onto the stack, then
 
780
 * you call this function to execute it. So this function is the single entry
 
781
 * point to script execution. This might be helpful when debugging your 
 
782
 * application. 
 
783
 * <note>A lot of convenience functions like swfdec_as_object_run() call this 
 
784
 * function automatically.</note>
 
785
 **/
 
786
void
 
787
swfdec_as_context_run (SwfdecAsContext *context)
 
788
{
 
789
  SwfdecAsFrame *frame;
 
790
  SwfdecScript *script;
 
791
  const SwfdecActionSpec *spec;
 
792
  const guint8 *startpc, *pc, *endpc, *nextpc, *exitpc;
 
793
#ifndef G_DISABLE_ASSERT
 
794
  SwfdecAsValue *check;
 
795
#endif
 
796
  guint action, len;
 
797
  const guint8 *data;
 
798
  guint original_version;
 
799
  void (* step) (SwfdecAsDebugger *debugger, SwfdecAsContext *context);
 
800
  gboolean check_block; /* some opcodes avoid a scope check */
 
801
 
 
802
  g_return_if_fail (SWFDEC_IS_AS_CONTEXT (context));
 
803
  g_return_if_fail (context->frame != NULL);
 
804
  g_return_if_fail (context->frame->script != NULL);
 
805
  g_return_if_fail (context->global); /* check here because of swfdec_sandbox_(un)use() */
 
806
 
 
807
  /* setup data */
 
808
  frame = context->frame;
 
809
  original_version = context->version;
 
810
 
 
811
  /* sanity checks */
 
812
  if (context->state == SWFDEC_AS_CONTEXT_ABORTED)
 
813
    goto error;
 
814
  if (!swfdec_as_context_check_continue (context))
 
815
    goto error;
 
816
  if (context->call_depth > 256) {
 
817
    /* we've exceeded our maximum call depth, throw an error and abort */
 
818
    swfdec_as_context_abort (context, "Stack overflow");
 
819
    goto error;
 
820
  }
 
821
 
 
822
  if (context->debugger) {
 
823
    SwfdecAsDebuggerClass *klass = SWFDEC_AS_DEBUGGER_GET_CLASS (context->debugger);
 
824
    step = klass->step;
 
825
  } else {
 
826
    step = NULL;
 
827
  }
 
828
 
 
829
  g_assert (frame->target);
 
830
  script = frame->script;
 
831
  context->version = script->version;
 
832
  startpc = script->buffer->data;
 
833
  endpc = startpc + script->buffer->length;
 
834
  exitpc = script->exit;
 
835
  pc = frame->pc;
 
836
  check_block = TRUE;
 
837
 
 
838
  while (context->state < SWFDEC_AS_CONTEXT_ABORTED) {
 
839
    if (context->exception) {
 
840
      swfdec_as_frame_handle_exception (frame);
 
841
      if (frame != context->frame)
 
842
        goto out;
 
843
      pc = frame->pc;
 
844
      continue;
 
845
    }
 
846
    if (pc == exitpc) {
 
847
      swfdec_as_frame_return (frame, NULL);
 
848
      goto out;
 
849
    }
 
850
    if (pc < startpc || pc >= endpc) {
 
851
      SWFDEC_ERROR ("pc %p not in valid range [%p, %p) anymore", pc, startpc, endpc);
 
852
      goto error;
 
853
    }
 
854
    while (check_block && (pc < frame->block_start || pc >= frame->block_end)) {
 
855
      SWFDEC_LOG ("code exited block");
 
856
      swfdec_as_frame_pop_block (frame, context);
 
857
      pc = frame->pc;
 
858
      if (frame != context->frame)
 
859
        goto out;
 
860
      if (context->exception)
 
861
        break;
 
862
    }
 
863
    if (context->exception)
 
864
      continue;
 
865
 
 
866
    /* decode next action */
 
867
    action = *pc;
 
868
    /* invoke debugger if there is one */
 
869
    if (step) {
 
870
      frame->pc = pc;
 
871
      (* step) (context->debugger, context);
 
872
      if (frame != context->frame)
 
873
        goto out;
 
874
      if (frame->pc != pc)
 
875
        continue;
 
876
    }
 
877
    /* prepare action */
 
878
    spec = swfdec_as_actions + action;
 
879
    if (action & 0x80) {
 
880
      if (pc + 2 >= endpc) {
 
881
        SWFDEC_ERROR ("action %u length value out of range", action);
 
882
        goto error;
 
883
      }
 
884
      data = pc + 3;
 
885
      len = pc[1] | pc[2] << 8;
 
886
      if (data + len > endpc) {
 
887
        SWFDEC_ERROR ("action %u length %u out of range", action, len);
 
888
        goto error;
 
889
      }
 
890
      nextpc = pc + 3 + len;
 
891
    } else {
 
892
      data = NULL;
 
893
      len = 0;
 
894
      nextpc = pc + 1;
 
895
    }
 
896
    /* check action is valid */
 
897
    if (!spec->exec) {
 
898
      SWFDEC_WARNING ("cannot interpret action %3u 0x%02X %s for version %u, skipping it", action,
 
899
          action, spec->name ? spec->name : "Unknown", script->version);
 
900
      frame->pc = pc = nextpc;
 
901
      check_block = TRUE;
 
902
      continue;
 
903
    }
 
904
    if (script->version < spec->version) {
 
905
      SWFDEC_WARNING ("cannot interpret action %3u 0x%02X %s for version %u, using version %u instead",
 
906
          action, action, spec->name ? spec->name : "Unknown", script->version, spec->version);
 
907
    }
 
908
    if (spec->remove > 0) {
 
909
      if (spec->add > spec->remove)
 
910
        swfdec_as_stack_ensure_free (context, spec->add - spec->remove);
 
911
      swfdec_as_stack_ensure_size (context, spec->remove);
 
912
    } else {
 
913
      if (spec->add > 0)
 
914
        swfdec_as_stack_ensure_free (context, spec->add);
 
915
    }
 
916
    if (context->state > SWFDEC_AS_CONTEXT_RUNNING) {
 
917
      SWFDEC_WARNING ("context not running anymore, aborting");
 
918
      goto error;
 
919
    }
 
920
#ifndef G_DISABLE_ASSERT
 
921
    check = (spec->add >= 0 && spec->remove >= 0) ? context->cur + spec->add - spec->remove : NULL;
 
922
#endif
 
923
    /* execute action */
 
924
    spec->exec (context, action, data, len);
 
925
    /* adapt the pc if the action did not, otherwise, leave it alone */
 
926
    /* FIXME: do this via flag? */
 
927
    if (frame->pc == pc) {
 
928
      frame->pc = pc = nextpc;
 
929
      check_block = TRUE;
 
930
    } else {
 
931
      if (frame->pc < pc &&
 
932
          !swfdec_as_context_check_continue (context)) {
 
933
        goto error;
 
934
      }
 
935
      pc = frame->pc;
 
936
      check_block = FALSE;
 
937
    }
 
938
    if (frame == context->frame) {
 
939
#ifndef G_DISABLE_ASSERT
 
940
      if (check != NULL && check != context->cur) {
 
941
        g_error ("action %s was supposed to change the stack by %d (+%d -%d), but it changed by %td",
 
942
            spec->name, spec->add - spec->remove, spec->add, spec->remove,
 
943
            context->cur - check + spec->add - spec->remove);
 
944
      }
 
945
#endif
 
946
    } else {
 
947
      /* someone called/returned from a function, reread variables */
 
948
      goto out;
 
949
    }
 
950
  }
 
951
 
 
952
error:
 
953
  if (context->frame == frame)
 
954
    swfdec_as_frame_return (frame, NULL);
 
955
out:
 
956
  context->version = original_version;
 
957
  return;
 
958
}
 
959
 
 
960
/*** AS CODE ***/
 
961
 
 
962
static void
 
963
swfdec_as_context_ASSetPropFlags_set_one_flag (SwfdecAsObject *object,
 
964
    const char *s, guint *flags)
 
965
{
 
966
  swfdec_as_object_unset_variable_flags (object, s, flags[1]);
 
967
  swfdec_as_object_set_variable_flags (object, s, flags[0]);
 
968
}
 
969
 
 
970
static gboolean
 
971
swfdec_as_context_ASSetPropFlags_foreach (SwfdecAsObject *object,
 
972
    const char *s, SwfdecAsValue *val, guint cur_flags, gpointer data)
 
973
{
 
974
  guint *flags = data;
 
975
 
 
976
  /* shortcut if the flags already match */
 
977
  if (cur_flags == ((cur_flags &~ flags[1]) | flags[0]))
 
978
    return TRUE;
 
979
 
 
980
  swfdec_as_context_ASSetPropFlags_set_one_flag (object, s, flags);
 
981
  return TRUE;
 
982
}
 
983
 
 
984
SWFDEC_AS_NATIVE (1, 0, swfdec_as_context_ASSetPropFlags)
 
985
void
 
986
swfdec_as_context_ASSetPropFlags (SwfdecAsContext *cx, SwfdecAsObject *object, 
 
987
    guint argc, SwfdecAsValue *argv, SwfdecAsValue *retval)
 
988
{
 
989
  guint flags[2]; /* flags and mask - array so we can pass it as data pointer */
 
990
  SwfdecAsObject *obj;
 
991
 
 
992
  if (argc < 3)
 
993
    return;
 
994
 
 
995
  if (!SWFDEC_AS_VALUE_IS_OBJECT (&argv[0]))
 
996
    return;
 
997
  obj = SWFDEC_AS_VALUE_GET_OBJECT (&argv[0]);
 
998
  flags[0] = swfdec_as_value_to_integer (cx, &argv[2]);
 
999
  flags[1] = (argc > 3) ? swfdec_as_value_to_integer (cx, &argv[3]) : 0;
 
1000
 
 
1001
  if (flags[0] == 0 && flags[1] == 0) {
 
1002
    // we should add autosizing length attribute here
 
1003
    SWFDEC_FIXME ("ASSetPropFlags to set special length attribute not implemented");
 
1004
    return;
 
1005
  }
 
1006
 
 
1007
  if (SWFDEC_AS_VALUE_IS_NULL (&argv[1])) {
 
1008
    swfdec_as_object_foreach (obj, swfdec_as_context_ASSetPropFlags_foreach, flags);
 
1009
  } else {
 
1010
    char **split =
 
1011
      g_strsplit (swfdec_as_value_to_string (cx, &argv[1]), ",", -1);
 
1012
    guint i;
 
1013
    for (i = 0; split[i]; i++) {
 
1014
      swfdec_as_context_ASSetPropFlags_set_one_flag (obj, 
 
1015
          swfdec_as_context_get_string (cx, split[i]), flags);
 
1016
    }
 
1017
    g_strfreev (split); 
 
1018
  }
 
1019
}
 
1020
 
 
1021
SWFDEC_AS_NATIVE (200, 19, swfdec_as_context_isFinite)
 
1022
void
 
1023
swfdec_as_context_isFinite (SwfdecAsContext *cx, SwfdecAsObject *object, 
 
1024
    guint argc, SwfdecAsValue *argv, SwfdecAsValue *retval)
 
1025
{
 
1026
  double d;
 
1027
 
 
1028
  if (argc < 1)
 
1029
    return;
 
1030
 
 
1031
  d = swfdec_as_value_to_number (cx, &argv[0]);
 
1032
  SWFDEC_AS_VALUE_SET_BOOLEAN (retval, isfinite (d) ? TRUE : FALSE);
 
1033
}
 
1034
 
 
1035
SWFDEC_AS_NATIVE (200, 18, swfdec_as_context_isNaN)
 
1036
void
 
1037
swfdec_as_context_isNaN (SwfdecAsContext *cx, SwfdecAsObject *object, 
 
1038
    guint argc, SwfdecAsValue *argv, SwfdecAsValue *retval)
 
1039
{
 
1040
  double d;
 
1041
 
 
1042
  if (argc < 1)
 
1043
    return;
 
1044
 
 
1045
  d = swfdec_as_value_to_number (cx, &argv[0]);
 
1046
  SWFDEC_AS_VALUE_SET_BOOLEAN (retval, isnan (d) ? TRUE : FALSE);
 
1047
}
 
1048
 
 
1049
SWFDEC_AS_NATIVE (100, 2, swfdec_as_context_parseInt)
 
1050
void
 
1051
swfdec_as_context_parseInt (SwfdecAsContext *cx, SwfdecAsObject *object, 
 
1052
    guint argc, SwfdecAsValue *argv, SwfdecAsValue *retval)
 
1053
{
 
1054
  const char *s;
 
1055
  char *tail;
 
1056
  int radix = 10;
 
1057
  gint64 i;
 
1058
 
 
1059
  SWFDEC_AS_CHECK (0, NULL, "s|i", &s, &radix);
 
1060
 
 
1061
  if (argc >= 2 && (radix < 2 || radix > 36)) {
 
1062
    SWFDEC_AS_VALUE_SET_NUMBER (retval, NAN);
 
1063
    return;
 
1064
  }
 
1065
 
 
1066
  // special case, don't allow sign in front of the 0x
 
1067
  if ((s[0] == '-' || s[0] == '+') && s[1] == '0' &&
 
1068
      (s[2] == 'x' || s[2] == 'X')) {
 
1069
    SWFDEC_AS_VALUE_SET_NUMBER (retval, NAN);
 
1070
    return;
 
1071
  }
 
1072
 
 
1073
  // automatic radix
 
1074
  if (argc < 2) {
 
1075
    if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
 
1076
      radix = 16;
 
1077
    } else if ((s[0] == '0' || ((s[0] == '+' || s[0] == '-') && s[1] == '0')) &&
 
1078
        s[strspn (s+1, "01234567") + 1] == '\0') {
 
1079
      radix = 8;
 
1080
    } else {
 
1081
      radix = 10;
 
1082
    }
 
1083
  }
 
1084
 
 
1085
  // skip 0x at the start
 
1086
  if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
 
1087
    s += 2;
 
1088
 
 
1089
  // strtoll parses strings with 0x when given radix 16, but we don't want that
 
1090
  if (radix == 16) {
 
1091
    const char *skip = s + strspn (s, " \t\r\n");
 
1092
    if (skip != s && (skip[0] == '-' || skip[0] == '+'))
 
1093
      skip++;
 
1094
    if (skip != s && skip[0] == '0' && (skip[1] == 'x' || skip[1] == 'X')) {
 
1095
      SWFDEC_AS_VALUE_SET_NUMBER (retval, 0);
 
1096
      return;
 
1097
    }
 
1098
  }
 
1099
 
 
1100
  i = g_ascii_strtoll (s, &tail, radix);
 
1101
 
 
1102
  if (tail == s) {
 
1103
    SWFDEC_AS_VALUE_SET_NUMBER (retval, NAN);
 
1104
    return;
 
1105
  }
 
1106
 
 
1107
  if (i > G_MAXINT32 || i < G_MININT32) {
 
1108
    SWFDEC_AS_VALUE_SET_NUMBER (retval, i);
 
1109
  } else {
 
1110
    SWFDEC_AS_VALUE_SET_INT (retval, i);
 
1111
  }
 
1112
}
 
1113
 
 
1114
SWFDEC_AS_NATIVE (100, 3, swfdec_as_context_parseFloat)
 
1115
void
 
1116
swfdec_as_context_parseFloat (SwfdecAsContext *cx, SwfdecAsObject *object,
 
1117
    guint argc, SwfdecAsValue *argv, SwfdecAsValue *retval)
 
1118
{
 
1119
  char *s, *p, *tail;
 
1120
  double d;
 
1121
 
 
1122
  if (argc < 1)
 
1123
    return;
 
1124
 
 
1125
  // we need to remove everything after x or I, since strtod parses hexadecimal
 
1126
  // numbers and Infinity
 
1127
  s = g_strdup (swfdec_as_value_to_string (cx, &argv[0]));
 
1128
  if ((p = strpbrk (s, "xXiI")) != NULL) {
 
1129
    *p = '\0';
 
1130
  }
 
1131
 
 
1132
  d = g_ascii_strtod (s, &tail);
 
1133
 
 
1134
  if (tail == s) {
 
1135
    SWFDEC_AS_VALUE_SET_NUMBER (retval, NAN);
 
1136
  } else {
 
1137
    SWFDEC_AS_VALUE_SET_NUMBER (retval, d);
 
1138
  }
 
1139
 
 
1140
  g_free (s);
 
1141
}
 
1142
 
 
1143
static void
 
1144
swfdec_as_context_init_global (SwfdecAsContext *context)
 
1145
{
 
1146
  SwfdecAsValue val;
 
1147
 
 
1148
  SWFDEC_AS_VALUE_SET_NUMBER (&val, NAN);
 
1149
  swfdec_as_object_set_variable (context->global, SWFDEC_AS_STR_NaN, &val);
 
1150
  SWFDEC_AS_VALUE_SET_NUMBER (&val, HUGE_VAL);
 
1151
  swfdec_as_object_set_variable (context->global, SWFDEC_AS_STR_Infinity, &val);
 
1152
}
 
1153
 
 
1154
void
 
1155
swfdec_as_context_run_init_script (SwfdecAsContext *context, const guint8 *data, 
 
1156
    gsize length, guint version)
 
1157
{
 
1158
  g_return_if_fail (SWFDEC_IS_AS_CONTEXT (context));
 
1159
  g_return_if_fail (data != NULL);
 
1160
  g_return_if_fail (length > 0);
 
1161
 
 
1162
  if (version > 4) {
 
1163
    SwfdecBits bits;
 
1164
    SwfdecScript *script;
 
1165
    swfdec_bits_init_data (&bits, data, length);
 
1166
    script = swfdec_script_new_from_bits (&bits, "init", version);
 
1167
    if (script == NULL) {
 
1168
      g_warning ("script passed to swfdec_as_context_run_init_script is invalid");
 
1169
      return;
 
1170
    }
 
1171
    swfdec_as_object_run (context->global, script);
 
1172
    swfdec_script_unref (script);
 
1173
  } else {
 
1174
    SWFDEC_LOG ("not running init script, since version is <= 4");
 
1175
  }
 
1176
}
 
1177
 
 
1178
/**
 
1179
 * swfdec_as_context_startup:
 
1180
 * @context: a #SwfdecAsContext
 
1181
 *
 
1182
 * Starts up the context. This function must be called before any Actionscript
 
1183
 * is called on @context.
 
1184
 **/
 
1185
void
 
1186
swfdec_as_context_startup (SwfdecAsContext *context)
 
1187
{
 
1188
  g_return_if_fail (SWFDEC_IS_AS_CONTEXT (context));
 
1189
  g_return_if_fail (context->state == SWFDEC_AS_CONTEXT_NEW);
 
1190
 
 
1191
  if (context->cur == NULL &&
 
1192
      !swfdec_as_stack_push_segment (context))
 
1193
    return;
 
1194
  if (context->global == NULL)
 
1195
    context->global = swfdec_as_object_new_empty (context);
 
1196
  /* init the two internal functions */
 
1197
  /* FIXME: remove them for normal contexts? */
 
1198
  swfdec_player_preinit_global (context);
 
1199
  /* get the necessary objects up to define objects and functions sanely */
 
1200
  swfdec_as_function_init_context (context);
 
1201
  swfdec_as_object_init_context (context);
 
1202
  /* define the global object and other important ones */
 
1203
  swfdec_as_context_init_global (context);
 
1204
 
 
1205
  /* run init script */
 
1206
  swfdec_as_context_run_init_script (context, swfdec_as_initialize, sizeof (swfdec_as_initialize), 8);
 
1207
 
 
1208
  if (context->state == SWFDEC_AS_CONTEXT_NEW)
 
1209
    context->state = SWFDEC_AS_CONTEXT_RUNNING;
 
1210
}
 
1211
 
 
1212
/**
 
1213
 * swfdec_as_context_check_continue:
 
1214
 * @context: the context that might be running too long
 
1215
 *
 
1216
 * Checks if the context has been running too long. If it has, it gets aborted.
 
1217
 *
 
1218
 * Returns: %TRUE if this player aborted.
 
1219
 **/
 
1220
gboolean
 
1221
swfdec_as_context_check_continue (SwfdecAsContext *context)
 
1222
{
 
1223
  SwfdecAsContextClass *klass;
 
1224
 
 
1225
  g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (context), TRUE);
 
1226
 
 
1227
  klass = SWFDEC_AS_CONTEXT_GET_CLASS (context);
 
1228
  if (klass->check_continue == NULL)
 
1229
    return TRUE;
 
1230
  if (!klass->check_continue (context)) {
 
1231
    swfdec_as_context_abort (context, "Runtime exceeded");
 
1232
    return FALSE;
 
1233
  }
 
1234
  return TRUE;
 
1235
}
 
1236
 
 
1237
/**
 
1238
 * swfdec_as_context_is_aborted:
 
1239
 * @context: a #SwfdecAsContext
 
1240
 *
 
1241
 * Determines if the given context is aborted. An aborted context is not able
 
1242
 * to execute any scripts. Aborting can happen if the script engine detects bad 
 
1243
 * scripts that cause excessive memory usage, infinite loops or other problems.
 
1244
 * In that case the script engine aborts for safety reasons.
 
1245
 *
 
1246
 * Returns: %TRUE if the player is aborted, %FALSE if it runs normally.
 
1247
 **/
 
1248
gboolean
 
1249
swfdec_as_context_is_aborted (SwfdecAsContext *context)
 
1250
{
 
1251
  g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (context), TRUE);
 
1252
 
 
1253
  return context->state == SWFDEC_AS_CONTEXT_ABORTED;
 
1254
}
 
1255