mirror of https://github.com/Cisco-Talos/clamav
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
657 lines
16 KiB
657 lines
16 KiB
/*
|
|
* The builtin Array object.
|
|
* Copyright (c) 1998 New Generation Software (NGS) Oy
|
|
*
|
|
* Author: Markku Rossi <mtr@ngs.fi>
|
|
*/
|
|
|
|
/*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 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
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the Free
|
|
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
|
* MA 02111-1307, USA
|
|
*/
|
|
|
|
/*
|
|
* $Source: /tmp/cvsroot-15-2-2007/clamav-devel/libclamav/js/b_array.c,v $
|
|
* $Id: b_array.c,v 1.2 2006/10/28 11:27:44 njh Exp $
|
|
*/
|
|
|
|
/*
|
|
* Mehtods:
|
|
*
|
|
* concat (array) => array
|
|
* join ([glue]) => string
|
|
* pop () => last_element
|
|
* push (any...) => last_element_added
|
|
* reverse ()
|
|
* shift () => first_element
|
|
* slice (start, end) => array
|
|
* splice (index, how_many[, any...]) => array
|
|
* sort ([sort_function])
|
|
* toString () => string
|
|
* unshift (any...) => length_of_the_array
|
|
*
|
|
* Properties:
|
|
*
|
|
* length
|
|
*/
|
|
|
|
#if HAVE_CONFIG_H
|
|
#include "clamav-config.h"
|
|
#endif
|
|
|
|
#ifdef CL_EXPERIMENTAL
|
|
|
|
#include "jsint.h"
|
|
#include "mrgsort.h"
|
|
|
|
/*
|
|
* Types and definitions.
|
|
*/
|
|
|
|
/* Class context. */
|
|
struct array_ctx_st
|
|
{
|
|
JSSymbol s_concat;
|
|
JSSymbol s_join;
|
|
JSSymbol s_pop;
|
|
JSSymbol s_push;
|
|
JSSymbol s_reverse;
|
|
JSSymbol s_shift;
|
|
JSSymbol s_slice;
|
|
JSSymbol s_splice;
|
|
JSSymbol s_sort;
|
|
JSSymbol s_unshift;
|
|
|
|
JSSymbol s_length;
|
|
};
|
|
|
|
typedef struct array_ctx_st ArrayCtx;
|
|
|
|
/* Context for array sorts with JavaScript functions. */
|
|
struct array_sort_ctx_st
|
|
{
|
|
JSVirtualMachine *vm;
|
|
JSNode *func;
|
|
JSNode argv[3];
|
|
};
|
|
|
|
typedef struct array_sort_ctx_st ArraySortCtx;
|
|
|
|
/*
|
|
* Prototypes for static functions.
|
|
*/
|
|
|
|
static void new_proc (JSVirtualMachine *vm, JSBuiltinInfo *builtin_info,
|
|
JSNode *args, JSNode *result_return);
|
|
|
|
|
|
/*
|
|
* Static functions.
|
|
*/
|
|
|
|
static int
|
|
sort_default_cmp_func (const void *aptr, const void *bptr, void *context)
|
|
{
|
|
JSVirtualMachine *vm = context;
|
|
const JSNode *a = aptr;
|
|
const JSNode *b = bptr;
|
|
JSNode astr, bstr;
|
|
|
|
if (a->type == JS_UNDEFINED)
|
|
return 1;
|
|
if (b->type == JS_UNDEFINED)
|
|
return -1;
|
|
|
|
js_vm_to_string (vm, a, &astr);
|
|
js_vm_to_string (vm, b, &bstr);
|
|
|
|
return js_compare_strings (&astr, &bstr);
|
|
}
|
|
|
|
|
|
static int
|
|
sort_js_cmp_func (const void *aptr, const void *bptr, void *context)
|
|
{
|
|
ArraySortCtx *ctx = context;
|
|
const JSNode *a = aptr;
|
|
const JSNode *b = bptr;
|
|
|
|
/*
|
|
* Finalize the argument array. The argumnet count has already been set.
|
|
* when the context were initialized.
|
|
*/
|
|
JS_COPY (&ctx->argv[1], a);
|
|
JS_COPY (&ctx->argv[2], b);
|
|
|
|
/* Call the function. */
|
|
if (!js_vm_apply (ctx->vm, NULL, ctx->func, 3, ctx->argv))
|
|
/* Raise an error. */
|
|
js_vm_error (ctx->vm);
|
|
|
|
/* Fetch the return value. */
|
|
if (ctx->vm->exec_result.type != JS_INTEGER)
|
|
{
|
|
sprintf (ctx->vm->error,
|
|
"Array.sort(): comparison function didn't return integer");
|
|
js_vm_error (ctx->vm);
|
|
}
|
|
|
|
return ctx->vm->exec_result.u.vinteger;
|
|
}
|
|
|
|
|
|
/* Global method proc. */
|
|
static void
|
|
global_method (JSVirtualMachine *vm, JSBuiltinInfo *builtin_info,
|
|
void *instance_context, JSNode *result_return,
|
|
JSNode *args)
|
|
{
|
|
/* This does exactly the same as the new_proc. */
|
|
new_proc (vm, builtin_info, args, result_return);
|
|
}
|
|
|
|
|
|
/* Method proc. */
|
|
static int
|
|
method (JSVirtualMachine *vm, JSBuiltinInfo *builtin_info,
|
|
void *instance_context, JSSymbol method, JSNode *result_return,
|
|
JSNode *args)
|
|
{
|
|
ArrayCtx *ctx = builtin_info->obj_context;
|
|
JSNode *n = instance_context;
|
|
int i;
|
|
|
|
/* XXX 15.7.4.3 toSource(). */
|
|
|
|
/* Handle static methods here. */
|
|
if (instance_context == NULL)
|
|
{
|
|
if (method == vm->syms.s_toString)
|
|
js_vm_make_static_string (vm, result_return, "Array", 5);
|
|
/* ************************************************************ */
|
|
else
|
|
return JS_PROPERTY_UNKNOWN;
|
|
|
|
return JS_PROPERTY_FOUND;
|
|
}
|
|
|
|
/* Handle the instance methods. */
|
|
|
|
/* Set the default result type. */
|
|
result_return->type = JS_UNDEFINED;
|
|
|
|
if (method == ctx->s_concat)
|
|
{
|
|
int nlen;
|
|
int pos;
|
|
|
|
/* Count the new len; */
|
|
|
|
nlen = n->u.varray->length;
|
|
for (i = 0; i < args->u.vinteger; i++)
|
|
{
|
|
if (args[i + 1].type != JS_ARRAY)
|
|
goto argument_error;
|
|
|
|
nlen += args[i + 1].u.varray->length;
|
|
}
|
|
|
|
js_vm_make_array (vm, result_return, nlen);
|
|
|
|
/* Insert the items. */
|
|
memcpy (result_return->u.varray->data, n->u.varray->data,
|
|
n->u.varray->length * sizeof (JSNode));
|
|
pos = n->u.varray->length;
|
|
|
|
for (i = 0; i < args->u.vinteger; i++)
|
|
{
|
|
memcpy (&result_return->u.varray->data[pos],
|
|
args[i + 1].u.varray->data,
|
|
args[i + 1].u.varray->length * sizeof (JSNode));
|
|
pos += args[i + 1].u.varray->length;
|
|
}
|
|
}
|
|
/* ********************************************************************** */
|
|
else if (method == ctx->s_join || method == vm->syms.s_toString)
|
|
{
|
|
char *glue = NULL;
|
|
|
|
if (method == vm->syms.s_toString)
|
|
{
|
|
if (args->u.vinteger != 0)
|
|
goto argument_error;
|
|
}
|
|
else
|
|
{
|
|
if (args->u.vinteger == 0)
|
|
;
|
|
else if (args->u.vinteger == 1)
|
|
{
|
|
JSNode glue_result;
|
|
|
|
js_vm_to_string (vm, &args[1], &glue_result);
|
|
glue = js_string_to_c_string (vm, &glue_result);
|
|
}
|
|
else
|
|
goto argument_error;
|
|
}
|
|
|
|
/* Ok, ready to run. */
|
|
if (n->u.varray->length == 0)
|
|
js_vm_make_static_string (vm, result_return, "", 0);
|
|
else
|
|
{
|
|
int len;
|
|
int glue_len = glue ? strlen (glue) : 1;
|
|
|
|
/* Estimate the result length. */
|
|
len = (n->u.varray->length * 5
|
|
+ (n->u.varray->length - 1) * glue_len);
|
|
|
|
js_vm_make_string (vm, result_return, NULL, len);
|
|
result_return->u.vstring->len = 0;
|
|
|
|
/* Do the join. */
|
|
for (i = 0; i < n->u.varray->length; i++)
|
|
{
|
|
JSNode sitem;
|
|
int delta;
|
|
|
|
js_vm_to_string (vm, &n->u.varray->data[i], &sitem);
|
|
delta = sitem.u.vstring->len;
|
|
|
|
if (i + 1 < n->u.varray->length)
|
|
delta += glue_len;
|
|
|
|
result_return->u.vstring->data
|
|
= js_vm_realloc (vm, result_return->u.vstring->data,
|
|
result_return->u.vstring->len + delta);
|
|
|
|
memcpy (result_return->u.vstring->data
|
|
+ result_return->u.vstring->len,
|
|
sitem.u.vstring->data,
|
|
sitem.u.vstring->len);
|
|
result_return->u.vstring->len += sitem.u.vstring->len;
|
|
|
|
if (i + 1 < n->u.varray->length)
|
|
{
|
|
if (glue)
|
|
{
|
|
memcpy (result_return->u.vstring->data
|
|
+ result_return->u.vstring->len,
|
|
glue, glue_len);
|
|
result_return->u.vstring->len += glue_len;
|
|
}
|
|
else
|
|
result_return->u.vstring->data
|
|
[result_return->u.vstring->len++] = ',';
|
|
}
|
|
}
|
|
}
|
|
|
|
if (glue)
|
|
js_free (glue);
|
|
}
|
|
/* ********************************************************************** */
|
|
else if (method == ctx->s_pop)
|
|
{
|
|
if (args->u.vinteger != 0)
|
|
goto argument_error;
|
|
|
|
if (n->u.varray->length == 0)
|
|
result_return->type = JS_UNDEFINED;
|
|
else
|
|
{
|
|
JS_COPY (result_return, &n->u.varray->data[n->u.varray->length - 1]);
|
|
n->u.varray->length--;
|
|
}
|
|
}
|
|
/* ********************************************************************** */
|
|
else if (method == ctx->s_push)
|
|
{
|
|
int old_len;
|
|
|
|
if (args->u.vinteger == 0)
|
|
goto argument_error;
|
|
|
|
old_len = n->u.varray->length;
|
|
js_vm_expand_array (vm, n, n->u.varray->length + args->u.vinteger);
|
|
|
|
for (i = 0; i < args->u.vinteger; i++)
|
|
JS_COPY (&n->u.varray->data[old_len + i], &args[i + 1]);
|
|
|
|
JS_COPY (result_return, &args[i]);
|
|
}
|
|
/* ********************************************************************** */
|
|
else if (method == ctx->s_reverse)
|
|
{
|
|
if (args->u.vinteger != 0)
|
|
goto argument_error;
|
|
|
|
for (i = 0; i < n->u.varray->length / 2; i++)
|
|
{
|
|
JSNode tmp;
|
|
|
|
JS_COPY (&tmp, &n->u.varray->data[i]);
|
|
JS_COPY (&n->u.varray->data[i],
|
|
&n->u.varray->data[n->u.varray->length - i - 1]);
|
|
JS_COPY (&n->u.varray->data[n->u.varray->length - i - 1], &tmp);
|
|
}
|
|
}
|
|
/* ********************************************************************** */
|
|
else if (method == ctx->s_shift)
|
|
{
|
|
if (args->u.vinteger != 0)
|
|
goto argument_error;
|
|
|
|
if (n->u.varray->length == 0)
|
|
result_return->type = JS_UNDEFINED;
|
|
else
|
|
{
|
|
JS_COPY (result_return, &n->u.varray->data[0]);
|
|
memmove (&n->u.varray->data[0], &n->u.varray->data[1],
|
|
(n->u.varray->length - 1) * sizeof (JSNode));
|
|
n->u.varray->length--;
|
|
}
|
|
}
|
|
/* ********************************************************************** */
|
|
else if (method == ctx->s_slice)
|
|
{
|
|
int start, end;
|
|
|
|
if (args->u.vinteger < 1 || args->u.vinteger > 2)
|
|
goto argument_error;
|
|
|
|
if (args[1].type != JS_INTEGER)
|
|
goto argument_type_error;
|
|
|
|
start = args[1].u.vinteger;
|
|
|
|
if (args->u.vinteger == 2)
|
|
{
|
|
if (args[2].type != JS_INTEGER)
|
|
goto argument_type_error;
|
|
|
|
end = args[2].u.vinteger;
|
|
}
|
|
else
|
|
end = n->u.varray->length;
|
|
|
|
if (end < 0)
|
|
end += n->u.varray->length;
|
|
if (end < 0)
|
|
end = start;
|
|
|
|
js_vm_make_array (vm, result_return, end - start);
|
|
|
|
/* Copy items. */
|
|
for (i = 0; i < end - start; i++)
|
|
JS_COPY (&result_return->u.varray->data[i],
|
|
&n->u.varray->data[start + i]);
|
|
}
|
|
/* ********************************************************************** */
|
|
else if (method == ctx->s_splice)
|
|
{
|
|
unsigned int new_length;
|
|
unsigned int old_length;
|
|
int delta;
|
|
|
|
if (args->u.vinteger < 2)
|
|
goto argument_error;
|
|
if (args[1].type != JS_INTEGER || args[2].type != JS_INTEGER)
|
|
goto argument_type_error;
|
|
|
|
if (args[2].u.vinteger == 0 && args->u.vinteger == 2)
|
|
/* No deletions: must specify at least one item to insert. */
|
|
goto argument_error;
|
|
|
|
old_length = new_length = n->u.varray->length;
|
|
if (args[1].u.vinteger < new_length)
|
|
{
|
|
if (args[2].u.vinteger > new_length - args[1].u.vinteger)
|
|
{
|
|
args[2].u.vinteger = new_length - args[1].u.vinteger;
|
|
new_length = args[1].u.vinteger;
|
|
}
|
|
else
|
|
new_length -= args[2].u.vinteger;
|
|
}
|
|
else
|
|
{
|
|
new_length = args[1].u.vinteger;
|
|
args[2].u.vinteger = 0;
|
|
}
|
|
|
|
new_length += args->u.vinteger - 2;
|
|
|
|
if (new_length > n->u.varray->length)
|
|
js_vm_expand_array (vm, n, new_length);
|
|
else
|
|
/* Cut the array. */
|
|
n->u.varray->length = new_length;
|
|
|
|
/* Do the stuffs we must do. */
|
|
|
|
/* Create the result array. */
|
|
if (args[2].u.vinteger == 0)
|
|
result_return->type = JS_UNDEFINED;
|
|
else
|
|
{
|
|
js_vm_make_array (vm, result_return, args[2].u.vinteger);
|
|
for (i = 0; i < args[2].u.vinteger; i++)
|
|
JS_COPY (&result_return->u.varray->data[i],
|
|
&n->u.varray->data[args[1].u.vinteger + i]);
|
|
}
|
|
|
|
/* Delete and move. */
|
|
delta = args->u.vinteger - 2 - args[2].u.vinteger;
|
|
memmove (&n->u.varray->data[args[1].u.vinteger + args[2].u.vinteger
|
|
+ delta],
|
|
&n->u.varray->data[args[1].u.vinteger + args[2].u.vinteger],
|
|
(old_length - (args[1].u.vinteger + args[2].u.vinteger))
|
|
* sizeof (JSNode));
|
|
|
|
/* Insert. */
|
|
for (i = 0; i < args->u.vinteger - 2; i++)
|
|
JS_COPY (&n->u.varray->data[args[1].u.vinteger + i], &args[i + 3]);
|
|
}
|
|
/* ********************************************************************** */
|
|
else if (method == ctx->s_sort)
|
|
{
|
|
MergesortCompFunc func;
|
|
ArraySortCtx array_sort_ctx;
|
|
void *func_ctx = NULL; /* Initialized to keep compiler quiet. */
|
|
|
|
if (args->u.vinteger == 0)
|
|
{
|
|
func = sort_default_cmp_func;
|
|
func_ctx = vm;
|
|
}
|
|
else if (args->u.vinteger == 1)
|
|
{
|
|
if (args[1].type != JS_FUNC && args[1].type != JS_BUILTIN)
|
|
goto argument_type_error;
|
|
|
|
func = sort_js_cmp_func;
|
|
|
|
/* Init context. */
|
|
array_sort_ctx.vm = vm;
|
|
array_sort_ctx.func = &args[1];
|
|
|
|
/* Init the argc part of the argument vector here. */
|
|
array_sort_ctx.argv[0].type = JS_INTEGER;
|
|
array_sort_ctx.argv[0].u.vinteger = 3;
|
|
|
|
func_ctx = &array_sort_ctx;
|
|
}
|
|
else
|
|
goto argument_error;
|
|
|
|
mergesort_r (n->u.varray->data, n->u.varray->length, sizeof (JSNode),
|
|
func, func_ctx);
|
|
}
|
|
/* ********************************************************************** */
|
|
else if (method == ctx->s_unshift)
|
|
{
|
|
int old_len;
|
|
|
|
if (args->u.vinteger == 0)
|
|
goto argument_error;
|
|
|
|
old_len = n->u.varray->length;
|
|
js_vm_expand_array (vm, n, n->u.varray->length + args->u.vinteger);
|
|
|
|
memmove (&n->u.varray->data[args->u.vinteger], n->u.varray->data,
|
|
old_len * sizeof (JSNode));
|
|
|
|
for (i = 0; i < args->u.vinteger; i++)
|
|
JS_COPY (&n->u.varray->data[i], &args[args->u.vinteger - i]);
|
|
|
|
result_return->type = JS_INTEGER;
|
|
result_return->u.vinteger = n->u.varray->length;
|
|
}
|
|
/* ********************************************************************** */
|
|
else
|
|
return JS_PROPERTY_UNKNOWN;
|
|
|
|
return JS_PROPERTY_FOUND;
|
|
|
|
|
|
/*
|
|
* Error handling.
|
|
*/
|
|
|
|
argument_error:
|
|
sprintf (vm->error, "Array.%s(): illegal amount of arguments",
|
|
js_vm_symname (vm, method));
|
|
js_vm_error (vm);
|
|
|
|
argument_type_error:
|
|
sprintf (vm->error, "Array.%s(): illegal argument",
|
|
js_vm_symname (vm, method));
|
|
js_vm_error (vm);
|
|
|
|
/* NOTREACHED */
|
|
return 0;
|
|
}
|
|
|
|
/* Property proc. */
|
|
static int
|
|
property (JSVirtualMachine *vm, JSBuiltinInfo *builtin_info,
|
|
void *instance_context, JSSymbol property, int set, JSNode *node)
|
|
{
|
|
ArrayCtx *ctx = builtin_info->obj_context;
|
|
JSNode *n = instance_context;
|
|
|
|
if (property == ctx->s_length)
|
|
{
|
|
if (set)
|
|
goto immutable;
|
|
|
|
node->type = JS_INTEGER;
|
|
node->u.vinteger = n->u.varray->length;
|
|
}
|
|
else
|
|
{
|
|
if (!set)
|
|
node->type = JS_UNDEFINED;
|
|
|
|
return JS_PROPERTY_UNKNOWN;
|
|
}
|
|
|
|
return JS_PROPERTY_FOUND;
|
|
|
|
|
|
/*
|
|
* Error handling.
|
|
*/
|
|
|
|
immutable:
|
|
sprintf (vm->error, "Array.%s: immutable property",
|
|
js_vm_symname (vm, property));
|
|
js_vm_error (vm);
|
|
|
|
/* NOTREACHED */
|
|
return 0;
|
|
}
|
|
|
|
/* New proc. */
|
|
static void
|
|
new_proc (JSVirtualMachine *vm, JSBuiltinInfo *builtin_info, JSNode *args,
|
|
JSNode *result_return)
|
|
{
|
|
int i;
|
|
|
|
if (args->u.vinteger == 1 && args[1].type == JS_INTEGER)
|
|
{
|
|
/* Create a fixed length array. */
|
|
js_vm_make_array (vm, result_return, args[1].u.vinteger);
|
|
}
|
|
else
|
|
{
|
|
if (args->u.vinteger < 0)
|
|
/* We are called from the array initializer. */
|
|
args->u.vinteger = -args->u.vinteger;
|
|
|
|
js_vm_make_array (vm, result_return, args->u.vinteger);
|
|
for (i = 0; i < args->u.vinteger; i++)
|
|
JS_COPY (&result_return->u.varray->data[i], &args[i + 1]);
|
|
}
|
|
/* Set the [[Prototype]] and [[Class]] properties. */
|
|
/* XXX 15.7.2.1 */
|
|
}
|
|
|
|
|
|
/*
|
|
* Global functions.
|
|
*/
|
|
|
|
void
|
|
js_builtin_Array (JSVirtualMachine *vm)
|
|
{
|
|
ArrayCtx *ctx;
|
|
JSNode *n;
|
|
JSBuiltinInfo *info;
|
|
|
|
ctx = js_calloc (vm, 1, sizeof (*ctx));
|
|
|
|
ctx->s_concat = js_vm_intern (vm, "concat");
|
|
ctx->s_join = js_vm_intern (vm, "join");
|
|
ctx->s_pop = js_vm_intern (vm, "pop");
|
|
ctx->s_push = js_vm_intern (vm, "push");
|
|
ctx->s_reverse = js_vm_intern (vm, "reverse");
|
|
ctx->s_shift = js_vm_intern (vm, "shift");
|
|
ctx->s_slice = js_vm_intern (vm, "slice");
|
|
ctx->s_splice = js_vm_intern (vm, "splice");
|
|
ctx->s_sort = js_vm_intern (vm, "sort");
|
|
ctx->s_unshift = js_vm_intern (vm, "unshift");
|
|
|
|
ctx->s_length = js_vm_intern (vm, "length");
|
|
|
|
info = js_vm_builtin_info_create (vm);
|
|
vm->prim[JS_ARRAY] = info;
|
|
|
|
info->global_method_proc = global_method;
|
|
info->method_proc = method;
|
|
info->property_proc = property;
|
|
info->new_proc = new_proc;
|
|
info->obj_context = ctx;
|
|
info->obj_context_delete = js_free;
|
|
|
|
/* Define it. */
|
|
n = &vm->globals[js_vm_intern (vm, "Array")];
|
|
js_vm_builtin_create (vm, n, info, NULL);
|
|
}
|
|
#endif /*CL_EXPERIMENTAL*/
|
|
|