<commit_before>/* Swfdec
 * Copyright (C) 2007 <NAME> <<EMAIL>>
 *
 * 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_script.h"
#include "swfdec_debug.h"
#include "swfdec_debugger.h"
#include "swfdec_scriptable.h"
#include "js/jscntxt.h"
#include "js/jsinterp.h"

#include <errno.h>
#include <math.h>
#include <string.h>
#include "swfdec_decoder.h"
#include "swfdec_js.h"
#include "swfdec_movie.h"
#include "swfdec_player_internal.h"
#include "swfdec_root_movie.h"
#include "swfdec_sprite.h"
#include "swfdec_sprite_movie.h"
#include "js/jsfun.h"
#include "js/jsscope.h"

/* Define this to get SWFDEC_WARN'd about missing properties of objects.
 * This can be useful to find out about unimplemented native properties,
 * but usually just causes a lot of spam. */
//#define SWFDEC_WARN_MISSING_PROPERTIES

/*** CONSTANT POOLS ***/

SwfdecConstantPool *
swfdec_constant_pool_new_from_action (const guint8 *data, guint len)
{
  guint8 *next;
  guint i, n;
  GPtrArray *pool;

  if (len < 2) {
    SWFDEC_ERROR ("constant pool too small");
    return NULL;
  }
  n = GUINT16_FROM_LE (*((guint16*) data));
  data += 2;
  len -= 2;
  pool = g_ptr_array_sized_new (n);
  g_ptr_array_set_size (pool, n);
  for (i = 0; i < n; i++) {
    next = memchr (data, 0, len);
    if (next == NULL) {
      SWFDEC_ERROR ("not enough strings available");
      g_ptr_array_free (pool, TRUE);
      return NULL;
    }
    next++;
    g_ptr_array_index (pool, i) = (gpointer) data;
    len -= next - data;
    data = next;
  }
  if (len != 0) {
    SWFDEC_WARNING ("constant pool didn't consume whole buffer (%u bytes leftover)", len);
  }
  return pool;
}

guint
swfdec_constant_pool_size (SwfdecConstantPool *pool)
{
  return pool->len;
}

const char *
swfdec_constant_pool_get (SwfdecConstantPool *pool, guint i)
{
  g_assert (i < pool->len);
  return g_ptr_array_index (pool, i);
}

void
swfdec_constant_pool_free (SwfdecConstantPool *pool)
{
  g_ptr_array_free (pool, TRUE);
}

/* FIXME: this is a bit hacky */
static SwfdecBuffer *
swfdec_constant_pool_get_area (SwfdecScript *script, SwfdecConstantPool *pool)
{
  guint8 *start;
  SwfdecBuffer *buffer;
  guint len;

  if (pool->len == 0)
    return NULL;
  start = (guint8 *) g_ptr_array_index (pool, 0) - 5;
  buffer = script->buffer;
  if (start < buffer->data) {
    /* DefineFunction inside DefineFunction */
    g_assert (buffer->parent != NULL);
    buffer = buffer->parent;
    g_assert (start >= buffer->data);
  }
  g_assert (start + 3 < buffer->data + buffer->length);
  g_assert (*start == 0x88);
  len = 3 + (start[1] | start[2] << 8);
  g_assert (start + len < buffer->data + buffer->length);
  return swfdec_buffer_new_subbuffer (buffer, start - buffer->data, len);
}

/*** SUPPORT FUNCTIONS ***/

static void
swfdec_script_add_to_player (SwfdecScript *script, SwfdecPlayer *player)
{
  if (SWFDEC_IS_DEBUGGER (player)) {
    swfdec_debugger_add_script (SWFDEC_DEBUGGER (player), script);
    script->debugger = player;
  }
}

/**
 * swfdec_script_ensure_stack:
 * @cx: #JSContext to check
 * @n_elements: number of elements the stack should contain
 *
 * Ensures that the stack is at least @n_elements values. If not enough stack
 * space is available, the stack is filled up with JSVAL_VOID.
 *
 * Returns: JS_TRUE on success or JS_FALSE on OOM
 **/
static inline JSBool
swfdec_script_ensure_stack (JSContext *cx, guint n_elements)
{
  JSStackFrame *fp = cx->fp;
  guint current = (guint) (fp->sp - fp->spbase);

  if (current >= n_elements)
    return JS_TRUE;

  if (n_elements > (guint) (fp->spend - fp->spbase)) {
    SWFDEC_ERROR ("FIXME: implement stack expansion, we got an overflow (want %u, have %td)",
	n_elements, (fp->spend - fp->spbase));
    return JS_FALSE;
  }

  if (current) {
    n_elements -= current;
    memmove (fp->spbase + n_elements, fp->spbase, (fp->sp - fp->spbase) * sizeof (jsval));
  }
  fp->sp += n_elements;
  while (n_elements)  {
    n_elements--;
    fp->spbase[n_elements] = JSVAL_VOID;
  }
  return JS_TRUE;
}

static gboolean
swfdec_action_has_register (JSContext *cx, guint i)
{
  if (cx->fp->fun == NULL)
    return i < 4;
  else
    return i < cx->fp->fun->nvars;
}

static SwfdecMovie *
swfdec_action_get_target (JSContext *cx)
{
  JSObject *object = cx->fp->scopeChain;
  /* this whole function needs a big FIXME */
  if (JS_GetClass (object) == &js_WithClass)
    object = JS_GetPrototype (cx, object);
  return swfdec_scriptable_from_jsval (cx, OBJECT_TO_JSVAL (object), SWFDEC_TYPE_MOVIE);
}

static JSBool
swfdec_action_push_string (JSContext *cx, const char *s)
{
  JSString *string = JS_NewStringCopyZ (cx, s);
  if (string == NULL)
    return JS_FALSE;
  *cx->fp->sp++ = STRING_TO_JSVAL (string);
  return JS_TRUE;
}

static JSBool
swfdec_value_to_boolean_5 (JSContext *cx, jsval val)
{
  if (JSVAL_IS_BOOLEAN (val)) {
    return JSVAL_TO_BOOLEAN (val);
  } else if (JSVAL_IS_INT (val)) {
    return JSVAL_TO_INT (val) != 0;
  } else if (JSVAL_IS_DOUBLE (val)) {
    double d = *JSVAL_TO_DOUBLE (val);
    return d != 0.0 && !isnan (d);
  } else if (JSVAL_IS_STRING (val)) {
    double d;
    if (!JS_ValueToNumber (cx, val, &d))
      return 0;
    return d != 0.0 && !isnan (d);
  } else if (JSVAL_IS_NULL (val)) {
    return JS_FALSE;
  } else if (JSVAL_IS_VOID (val)) {
    return JS_FALSE;
  } else if (JSVAL_IS_OBJECT (val)) {
    return JS_TRUE;
  }
  g_assert_not_reached ();
  return JS_FALSE;
}

static JSBool
swfdec_value_to_boolean_7 (JSContext *cx, jsval val)
{
  if (JSVAL_IS_BOOLEAN (val)) {
    return JSVAL_TO_BOOLEAN (val);
  } else if (JSVAL_IS_INT (val)) {
    return JSVAL_TO_INT (val) != 0;
  } else if (JSVAL_IS_DOUBLE (val)) {
    double d = *JSVAL_TO_DOUBLE (val);
    return d != 0.0 && !isnan (d);
  } else if (JSVAL_IS_STRING (val)) {
    return JS_GetStringLength (JSVAL_TO_STRING (val)) > 0;
  } else if (JSVAL_IS_NULL (val)) {
    return JS_FALSE;
  } else if (JSVAL_IS_VOID (val)) {
    return JS_FALSE;
  } else if (JSVAL_IS_OBJECT (val)) {
    return JS_TRUE;
  }
  g_assert_not_reached ();
  return JS_FALSE;
}

static double
swfdec_value_to_number (JSContext *cx, jsval val)
{
  if (JSVAL_IS_INT (val)) {
    return JSVAL_TO_INT (val);
  } else if (JSVAL_IS_DOUBLE (val)) {
    return *JSVAL_TO_DOUBLE (val);
  } else if (JSVAL_IS_BOOLEAN (val)) {
    return JSVAL_TO_BOOLEAN (val);
  } else if (JSVAL_IS_STRING (val)) {
    double d;
    if (!JS_ValueToNumber (cx, val, &d))
      return 0;
    return isnan (d) ? 0 : d;
  } else if (JSVAL_IS_OBJECT(val) && (((SwfdecScript *) cx->fp->swf)->version >= 6)) {
    return JSVAL_IS_NULL (val) ? 0 : *cx->runtime->jsNaN;
  } else {
    return 0;
  }
}

static JSBool
swfdec_value_to_number_7 (JSContext *cx, jsval val, double *d)
{
  if (JSVAL_IS_OBJECT (val)) {<commit_msg>add some fixes for number conversion<commit_after>/* Swfdec
 * Copyright (C) 2007 <NAME> <<EMAIL>>
 *
 * 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_script.h"
#include "swfdec_debug.h"
#include "swfdec_debugger.h"
#include "swfdec_scriptable.h"
#include "js/jscntxt.h"
#include "js/jsinterp.h"

#include <errno.h>
#include <math.h>
#include <string.h>
#include "swfdec_decoder.h"
#include "swfdec_js.h"
#include "swfdec_movie.h"
#include "swfdec_player_internal.h"
#include "swfdec_root_movie.h"
#include "swfdec_sprite.h"
#include "swfdec_sprite_movie.h"
#include "js/jsfun.h"
#include "js/jsscope.h"

/* Define this to get SWFDEC_WARN'd about missing properties of objects.
 * This can be useful to find out about unimplemented native properties,
 * but usually just causes a lot of spam. */
//#define SWFDEC_WARN_MISSING_PROPERTIES

/*** CONSTANT POOLS ***/

SwfdecConstantPool *
swfdec_constant_pool_new_from_action (const guint8 *data, guint len)
{
  guint8 *next;
  guint i, n;
  GPtrArray *pool;

  if (len < 2) {
    SWFDEC_ERROR ("constant pool too small");
    return NULL;
  }
  n = GUINT16_FROM_LE (*((guint16*) data));
  data += 2;
  len -= 2;
  pool = g_ptr_array_sized_new (n);
  g_ptr_array_set_size (pool, n);
  for (i = 0; i < n; i++) {
    next = memchr (data, 0, len);
    if (next == NULL) {
      SWFDEC_ERROR ("not enough strings available");
      g_ptr_array_free (pool, TRUE);
      return NULL;
    }
    next++;
    g_ptr_array_index (pool, i) = (gpointer) data;
    len -= next - data;
    data = next;
  }
  if (len != 0) {
    SWFDEC_WARNING ("constant pool didn't consume whole buffer (%u bytes leftover)", len);
  }
  return pool;
}

guint
swfdec_constant_pool_size (SwfdecConstantPool *pool)
{
  return pool->len;
}

const char *
swfdec_constant_pool_get (SwfdecConstantPool *pool, guint i)
{
  g_assert (i < pool->len);
  return g_ptr_array_index (pool, i);
}

void
swfdec_constant_pool_free (SwfdecConstantPool *pool)
{
  g_ptr_array_free (pool, TRUE);
}

/* FIXME: this is a bit hacky */
static SwfdecBuffer *
swfdec_constant_pool_get_area (SwfdecScript *script, SwfdecConstantPool *pool)
{
  guint8 *start;
  SwfdecBuffer *buffer;
  guint len;

  if (pool->len == 0)
    return NULL;
  start = (guint8 *) g_ptr_array_index (pool, 0) - 5;
  buffer = script->buffer;
  if (start < buffer->data) {
    /* DefineFunction inside DefineFunction */
    g_assert (buffer->parent != NULL);
    buffer = buffer->parent;
    g_assert (start >= buffer->data);
  }
  g_assert (start + 3 < buffer->data + buffer->length);
  g_assert (*start == 0x88);
  len = 3 + (start[1] | start[2] << 8);
  g_assert (start + len < buffer->data + buffer->length);
  return swfdec_buffer_new_subbuffer (buffer, start - buffer->data, len);
}

/*** SUPPORT FUNCTIONS ***/

static void
swfdec_script_add_to_player (SwfdecScript *script, SwfdecPlayer *player)
{
  if (SWFDEC_IS_DEBUGGER (player)) {
    swfdec_debugger_add_script (SWFDEC_DEBUGGER (player), script);
    script->debugger = player;
  }
}

/**
 * swfdec_script_ensure_stack:
 * @cx: #JSContext to check
 * @n_elements: number of elements the stack should contain
 *
 * Ensures that the stack is at least @n_elements values. If not enough stack
 * space is available, the stack is filled up with JSVAL_VOID.
 *
 * Returns: JS_TRUE on success or JS_FALSE on OOM
 **/
static inline JSBool
swfdec_script_ensure_stack (JSContext *cx, guint n_elements)
{
  JSStackFrame *fp = cx->fp;
  guint current = (guint) (fp->sp - fp->spbase);

  if (current >= n_elements)
    return JS_TRUE;

  if (n_elements > (guint) (fp->spend - fp->spbase)) {
    SWFDEC_ERROR ("FIXME: implement stack expansion, we got an overflow (want %u, have %td)",
	n_elements, (fp->spend - fp->spbase));
    return JS_FALSE;
  }

  if (current) {
    n_elements -= current;
    memmove (fp->spbase + n_elements, fp->spbase, (fp->sp - fp->spbase) * sizeof (jsval));
  }
  fp->sp += n_elements;
  while (n_elements)  {
    n_elements--;
    fp->spbase[n_elements] = JSVAL_VOID;
  }
  return JS_TRUE;
}

static gboolean
swfdec_action_has_register (JSContext *cx, guint i)
{
  if (cx->fp->fun == NULL)
    return i < 4;
  else
    return i < cx->fp->fun->nvars;
}

static SwfdecMovie *
swfdec_action_get_target (JSContext *cx)
{
  JSObject *object = cx->fp->scopeChain;
  /* this whole function needs a big FIXME */
  if (JS_GetClass (object) == &js_WithClass)
    object = JS_GetPrototype (cx, object);
  return swfdec_scriptable_from_jsval (cx, OBJECT_TO_JSVAL (object), SWFDEC_TYPE_MOVIE);
}

static JSBool
swfdec_action_push_string (JSContext *cx, const char *s)
{
  JSString *string = JS_NewStringCopyZ (cx, s);
  if (string == NULL)
    return JS_FALSE;
  *cx->fp->sp++ = STRING_TO_JSVAL (string);
  return JS_TRUE;
}

static JSBool
swfdec_value_to_boolean_5 (JSContext *cx, jsval val)
{
  if (JSVAL_IS_BOOLEAN (val)) {
    return JSVAL_TO_BOOLEAN (val);
  } else if (JSVAL_IS_INT (val)) {
    return JSVAL_TO_INT (val) != 0;
  } else if (JSVAL_IS_DOUBLE (val)) {
    double d = *JSVAL_TO_DOUBLE (val);
    return d != 0.0 && !isnan (d);
  } else if (JSVAL_IS_STRING (val)) {
    double d;
    if (!JS_ValueToNumber (cx, val, &d))
      return 0;
    return d != 0.0 && !isnan (d);
  } else if (JSVAL_IS_NULL (val)) {
    return JS_FALSE;
  } else if (JSVAL_IS_VOID (val)) {
    return JS_FALSE;
  } else if (JSVAL_IS_OBJECT (val)) {
    return JS_TRUE;
  }
  g_assert_not_reached ();
  return JS_FALSE;
}

static JSBool
swfdec_value_to_boolean_7 (JSContext *cx, jsval val)
{
  if (JSVAL_IS_BOOLEAN (val)) {
    return JSVAL_TO_BOOLEAN (val);
  } else if (JSVAL_IS_INT (val)) {
    return JSVAL_TO_INT (val) != 0;
  } else if (JSVAL_IS_DOUBLE (val)) {
    double d = *JSVAL_TO_DOUBLE (val);
    return d != 0.0 && !isnan (d);
  } else if (JSVAL_IS_STRING (val)) {
    return JS_GetStringLength (JSVAL_TO_STRING (val)) > 0;
  } else if (JSVAL_IS_NULL (val)) {
    return JS_FALSE;
  } else if (JSVAL_IS_VOID (val)) {
    return JS_FALSE;
  } else if (JSVAL_IS_OBJECT (val)) {
    return JS_TRUE;
  }
  g_assert_not_reached ();
  return JS_FALSE;
}

static double
swfdec_value_to_number (JSContext *cx, jsval val)
{
  if (JSVAL_IS_INT (val)) {
    return JSVAL_TO_INT (val);
  } else if (JSVAL_IS_DOUBLE (val)) {
    return *JSVAL_TO_DOUBLE (val);
  } else if (JSVAL_IS_BOOLEAN (val)) {
    return JSVAL_TO_BOOLEAN (val);
  } else if (JSVAL_IS_STRING (val)) {
    double d;
    if (!JS_ValueToNumber (cx, val, &d))
      return 0;
    return d;
  } else if (JSVAL_IS_OBJECT(val) && (((SwfdecScript *) cx->fp->swf)->version >= 6)) {
    /* Checking for version 6 is completely wrong, but a lot of the testsuite 
     * depends on it (oops).
     * The code calls the valueOf function and returns 0 if no such function exists.
     */
    return JSVAL_IS_NULL (val) ? 0 : *cx->runtime->jsNaN;
  } else {
    return 0;
  }
}

static JSBool
swfdec_value_to_number_7 (JSContext *cx, jsval val, double *d)
{
  if (JSVAL_IS_OBJECT (val)) {
{'fraction_non_alphanumeric': 0.12522935779816513, 'fraction_numerical': 0.008060288335517694, 'mean_word_length': 3.7482887367766025, 'pattern_counts': {'":': 0, '<': 35, '<?xml version=': 0, '>': 107, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4979059', 'n_tokens_mistral': 5780, 'n_tokens_neox': 5696, 'n_words': 1607}