/* * Internal definitions for the JavaScript interpreter. * Copyright (c) 1998-1999 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/jsint.h,v $ * $Id: jsint.h,v 1.4 2006/11/10 20:17:43 njh Exp $ */ #ifndef JSINT_H #define JSINT_H /* We have always jsconfig.h */ #include "jsconfig.h" #include <stdio.h> #include <assert.h> #include <setjmp.h> #include <math.h> #include <time.h> #include <limits.h> #if HAVE_UNISTD_H #include <unistd.h> #endif #if STDC_HEADERS #include <stdlib.h> #include <errno.h> #include <string.h> #include <float.h> #else /* not STDC_HEADERS */ #if HAVE_STDLIB_H #include <stdlib.h> #endif #if HAVE_ERRNO_H #include <errno.h> #endif #if HAVE_STRING_H #include <string.h> #endif #if HAVE_FLOAT_H #include <float.h> #endif #endif /* not STDC_HEADERS */ /* Misc system headers. */ #include <sys/types.h> /* * Protability kludges. If something is missing from the w32 * environment, please edit the micros/w32.{c,h} files and implement * them. */ #ifndef WIN32 /* Directory handling. */ #include <dirent.h> #endif /* not WIN32 */ #include "js.h" #if __cplusplus extern "C" { #endif /* * Types and definitions. */ /* Some portability features. */ #ifdef WIN32 #define JS_HOST_LINE_BREAK "\r\n" #define JS_HOST_LINE_BREAK_LEN 2 #else /* not WIN32 */ #define JS_HOST_LINE_BREAK "\n" #define JS_HOST_LINE_BREAK_LEN 1 #endif /* not WIN32 */ #define JS_BC_FILE_MAGIC 0xc0014a53 #define JS_GLOBAL_NAME ".global" #define JS_SYMBOL_NULL ((JSSymbol) -1) #define JS_IS_STR_WHITE_SPACE_CHAR(ch) \ ((ch) == '\t' || (ch) == ' ' || (ch) == '\f' || (ch) == '\v' \ || (ch) == '\r' || (ch) == '\n') /* * Read macros for byte code files. */ #define JS_BC_READ_INT32(cp, var) \ (var) = (cp)[0]; \ (var) <<= 8; \ (var) |= (cp)[1]; \ (var) <<= 8; \ (var) |= (cp)[2]; \ (var) <<= 8; \ (var) |= (cp)[3] #define JS_BC_READ_INT16(cp, var) \ (var) = (cp)[0]; \ (var) <<= 8; \ (var) |= (cp)[1] #define JS_BC_READ_INT8(cp, var) \ (var) = (cp)[0] #define JS_BC_WRITE_INT32(cp, var) \ cp[3] = (unsigned char) ((var) & 0x000000ff); \ cp[2] = (unsigned char) (((var) >> 8) & 0x000000ff); \ cp[1] = (unsigned char) (((var) >> 16) & 0x000000ff); \ cp[0] = (unsigned char) (((var) >> 24) & 0x000000ff) /* General VM macros. */ /* STACKFRAME */ #define JS_SP0 sp #define JS_SP1 (sp + 1) #define JS_SP2 (sp + 2) #define JS_SP(n) (sp + (n)) #define JS_LOCAL(n) (fp - 4 - (n)) #define JS_ARG(n) (fp + 1 + (n)) #define JS_WITHPTR (fp - 2) #define JS_ARGS_FIXP (fp - 1) #define JS_PUSH() sp-- #define JS_POP() sp++ #define JS_POP_N(n) sp += (n) #define JS_COPY(to, from) \ do { \ (to)->type = (from)->type; \ (to)->u.copy.a = (from)->u.copy.a; \ (to)->u.copy.b = (from)->u.copy.b; \ } while (0) #define JS_CONST(n) (&vm->consts[(n)]) #define JS_GLOBAL(n) (&vm->globals[(n)]) #define JS_SAVE_REGS() \ do { \ vm->sp = sp; \ vm->pc = pc; \ } while (0) #define JS_CALL_HOOK(event) \ do { \ int hook_result; \ \ if (vm->hook) \ if ((hook_result = (*vm->hook) ((event), vm->hook_context)) != 0) \ { \ JS_SAVE_REGS (); \ sprintf (vm->error, "hook break %d", hook_result); \ js_vm_error (vm); \ /* NOTREACHED */ \ } \ } while (0) #define JS_VM_ALLOCATE_FD(vm, where) \ do { \ if ((vm)->fd_count == 0) \ { \ sprintf ((vm)->error, "%s: no more file descriptors allowed", \ (where)); \ js_vm_error (vm); \ } \ (vm)->fd_count--; \ } while (0) #define JS_VM_FREE_FD(vm) \ do { \ (vm)->fd_count++; \ } while (0) #define JS_MAYBE_GC() \ do { \ if (vm->gc.bytes_allocated >= vm->gc.trigger) \ { \ js_vm_garbage_collect (vm, fp, sp); \ JS_CALL_HOOK (JS_VM_EVENT_GARBAGE_COLLECT); \ } \ } while (0) #define JS_IS_TRUE(n) ((n)->type > JS_INTEGER \ || ((n)->type == JS_BOOLEAN && (n)->u.vboolean) \ || ((n)->type == JS_INTEGER && (n)->u.vinteger)) #define JS_IS_FALSE(n) ((n)->type < JS_BOOLEAN \ || ((n)->type == JS_BOOLEAN && !(n)->u.vboolean) \ || ((n)->type == JS_INTEGER && !(n)->u.vinteger)) #define JS_RESERVE_STACK_FOR_FUNCTION 10 #define JS_SUBROUTINE_CALL(function) \ do { \ /* Check that we have enought space in the stack. */ \ if (sp - JS_RESERVE_STACK_FOR_FUNCTION < vm->stack) \ ERROR ("stack overflow"); \ \ /* STACKFRAME */ \ \ /* Save frame pointer. */ \ JS_SP0->type = JS_IPTR; \ JS_SP0->u.iptr = fp; \ \ /* Update fp. */ \ fp = JS_SP0; \ JS_PUSH (); \ \ /* Insert an empty args_fix. */ \ JS_SP0->type = JS_ARGS_FIX; \ JS_SP0->u.args_fix.argc = 0; \ JS_SP0->u.args_fix.delta = 0; \ JS_PUSH (); \ \ /* Insert empty with pointer. */ \ JS_SP0->type = JS_IPTR; \ JS_SP0->u.iptr = NULL; \ JS_PUSH (); \ \ /* Save return address. */ \ JS_SP0->type = JS_IPTR; \ JS_SP0->u.iptr = pc; \ JS_PUSH (); \ \ /* And finally, jump to the method code. */ \ CALL_USER_FUNC ((function)); \ } while (0) #define JS_OPERAND_CMP_REL(_OP_) \ do { \ if (JS_SP2->type == JS_STRING && JS_SP1->type == JS_STRING) \ { \ JS_SP2->u.vboolean \ = js_compare_strings (JS_SP2, JS_SP1) _OP_ 0; \ JS_SP2->type = JS_BOOLEAN; \ JS_POP (); \ } \ else if (JS_SP2->type == JS_INTEGER && JS_SP1->type == JS_INTEGER) \ { \ JS_SP2->u.vboolean \ = JS_SP2->u.vinteger _OP_ JS_SP1->u.vinteger; \ JS_SP2->type = JS_BOOLEAN; \ JS_POP (); \ } \ else \ { \ JSNode l, r; \ \ /* Do it the hard way. */ \ switch (JS_SP2->type) \ { \ case JS_INTEGER: \ case JS_FLOAT: \ case JS_NAN: \ JS_COPY (&l, JS_SP2); \ break; \ \ default: \ js_vm_to_number (vm, JS_SP2, &l); \ break; \ } \ \ switch (JS_SP1->type) \ { \ case JS_INTEGER: \ case JS_FLOAT: \ case JS_NAN: \ JS_COPY (&r, JS_SP1); \ break; \ \ default: \ js_vm_to_number (vm, JS_SP1, &r); \ break; \ } \ \ /* Do the comparison. */ \ JS_POP (); \ \ if (l.type == JS_NAN || r.type == JS_NAN) \ JS_SP1->type = JS_UNDEFINED; \ else if (l.type == JS_INTEGER && r.type == JS_INTEGER) \ { \ JS_SP1->type = JS_BOOLEAN; \ JS_SP1->u.vboolean = l.u.vinteger _OP_ r.u.vinteger; \ } \ else \ { \ double ld, rd; \ \ if (l.type == JS_FLOAT) \ ld = l.u.vfloat; \ else \ ld = (double) l.u.vinteger; \ \ if (r.type == JS_FLOAT) \ rd = r.u.vfloat; \ else \ rd = (double) r.u.vinteger; \ \ JS_SP1->type = JS_BOOLEAN; \ JS_SP1->u.vboolean = ld _OP_ rd; \ } \ } \ } while (0) #define JS_OPERAND_CMP_EQ(_OP_, _VAL_) \ while (1) { \ int res; \ if (JS_SP2->type == JS_SP1->type) \ { \ /* Comparsion between same types. */ \ switch (JS_SP2->type) \ { \ case JS_INTEGER: \ res = JS_SP2->u.vinteger _OP_ JS_SP1->u.vinteger; \ break; \ \ case JS_STRING: \ res = js_compare_strings (JS_SP2, JS_SP1) _OP_ 0; \ break; \ \ case JS_FLOAT: \ res = JS_SP2->u.vfloat _OP_ JS_SP1->u.vfloat; \ break; \ \ case JS_NAN: \ /* 11.9.3: cases 5 and 6 */ \ res = !_VAL_; \ break; \ \ case JS_BOOLEAN: \ res = JS_SP2->u.vboolean _OP_ JS_SP1->u.vboolean; \ break; \ \ case JS_OBJECT: \ res = JS_SP2->u.vobject _OP_ JS_SP1->u.vobject; \ break; \ \ case JS_BUILTIN: \ res = ((JS_SP2->u.vbuiltin->info \ == JS_SP1->u.vbuiltin->info \ && (JS_SP2->u.vbuiltin->instance_context \ == JS_SP1->u.vbuiltin->instance_context)) \ ? _VAL_ : !_VAL_); \ break; \ \ case JS_FUNC: \ res = JS_SP2->u.vfunction _OP_ JS_SP1->u.vfunction; \ break; \ \ case JS_SYMBOL: \ res = JS_SP2->u.vsymbol _OP_ JS_SP1->u.vsymbol; \ break; \ \ case JS_IPTR: \ res = JS_SP2->u.iptr _OP_ JS_SP1->u.iptr; \ break; \ \ default: \ res = _VAL_; \ break; \ } \ } \ else \ { \ /* Type conversions between different types. */ \ \ if ((JS_SP2->type == JS_UNDEFINED || JS_SP2->type == JS_NULL) \ && (JS_SP1->type == JS_UNDEFINED \ || JS_SP1->type == JS_NULL)) \ res = _VAL_; \ \ /* Numbers. */ \ else if (JS_IS_NUMBER (JS_SP2) && JS_IS_NUMBER (JS_SP1)) \ { \ if (JS_SP2->type == JS_NAN || JS_SP1->type == JS_NAN) \ /* 11.9.3: cases 5 and 6 */ \ res = !_VAL_; \ else if (JS_SP2->type == JS_INTEGER) \ /* Integer-integer was already handled. */ \ res = (double) JS_SP2->u.vinteger _OP_ JS_SP1->u.vfloat; \ else \ /* Integer-integer was already handled. */ \ res = JS_SP2->u.vfloat _OP_ (double) JS_SP1->u.vinteger; \ } \ else \ { \ JSNode l, r; \ \ /* Must perform type casts. */ \ \ if ((JS_SP2->type == JS_STRING || JS_SP2->type == JS_BOOLEAN \ || JS_IS_NUMBER (JS_SP2)) \ && (JS_SP1->type == JS_STRING \ || JS_SP1->type == JS_BOOLEAN \ || JS_IS_NUMBER (JS_SP1))) \ { \ js_vm_to_number (vm, JS_SP2, &l); \ js_vm_to_number (vm, JS_SP1, &r); \ \ if (l.type == JS_NAN || r.type == JS_NAN) \ res = !_VAL_; \ else if (l.type == JS_INTEGER) \ { \ if (r.type == JS_INTEGER) \ res = l.u.vinteger _OP_ r.u.vinteger; \ else \ res = (double) l.u.vinteger _OP_ r.u.vfloat; \ } \ else \ { \ if (r.type == JS_INTEGER) \ res = l.u.vfloat _OP_ (double) r.u.vinteger; \ else \ res = l.u.vfloat _OP_ r.u.vfloat; \ } \ } \ else if (JS_SP2->type == JS_OBJECT \ && (JS_SP1->type == JS_STRING \ || JS_IS_NUMBER (JS_SP1))) \ { \ JSNode cvt; \ \ /* ECMA 11.9.3 21. No preferred type specified. */ \ js_vm_to_primitive (vm, JS_SP2, &cvt, JS_UNDEFINED); \ JS_COPY (JS_SP2, &cvt); \ continue; \ } \ else if (JS_SP1->type == JS_OBJECT \ && (JS_SP2->type == JS_STRING \ || JS_IS_NUMBER (JS_SP2))) \ { \ JSNode cvt; \ \ /* ECMA 11.9.3 20. No preferred type specified. */ \ js_vm_to_primitive (vm, JS_SP1, &cvt, JS_UNDEFINED); \ JS_COPY (JS_SP1, &cvt); \ continue; \ } \ else \ res = !_VAL_; \ } \ } \ \ JS_SP2->type = JS_BOOLEAN; \ JS_SP2->u.vboolean = res; \ JS_POP (); \ break; \ } #define JS_OPERAND_CMP_SEQ(_OP_, _VAL_) \ do { \ int res; \ if (JS_SP2->type == JS_SP1->type) \ { \ switch (JS_SP2->type) \ { \ case JS_INTEGER: \ res = JS_SP2->u.vinteger _OP_ JS_SP1->u.vinteger; \ break; \ \ case JS_FLOAT: \ res = JS_SP2->u.vfloat _OP_ JS_SP1->u.vfloat; \ break; \ \ case JS_NAN: \ /* 11.9.6: cases 3 and 4 */ \ res = !_VAL_; \ break; \ \ case JS_STRING: \ res = js_compare_strings (JS_SP2, JS_SP1) _OP_ 0; \ break; \ \ case JS_BOOLEAN: \ res = JS_SP2->u.vboolean _OP_ JS_SP1->u.vboolean; \ break; \ \ case JS_OBJECT: \ res = JS_SP2->u.vobject _OP_ JS_SP1->u.vobject; \ break; \ \ case JS_BUILTIN: \ res = ((JS_SP2->u.vbuiltin->info \ == JS_SP1->u.vbuiltin->info \ && (JS_SP2->u.vbuiltin->instance_context \ == JS_SP1->u.vbuiltin->instance_context)) \ ? _VAL_ : !_VAL_); \ break; \ \ case JS_FUNC: \ res = JS_SP2->u.vfunction _OP_ JS_SP1->u.vfunction; \ break; \ \ default: \ /* 11.9.6: case 12 */ \ res = !_VAL_; \ break; \ } \ } \ else \ { \ /* Only numbers are allowed here. */ \ if (JS_IS_NUMBER (JS_SP2) && JS_IS_NUMBER (JS_SP1)) \ { \ if (JS_SP2->type == JS_NAN || JS_SP1->type == JS_NAN) \ /* 11.9.6: cases 3 and 4 */ \ res = !_VAL_; \ else if (JS_SP2->type == JS_INTEGER) \ res = (double) JS_SP2->u.vinteger _OP_ JS_SP1->u.vfloat; \ else \ res = JS_SP2->u.vfloat _OP_ (double) JS_SP1->u.vinteger; \ } \ else \ res = !_VAL_; \ } \ \ JS_SP2->type = JS_BOOLEAN; \ JS_SP2->u.vboolean = res; \ JS_POP (); \ \ } while (0) #define JS_OPERAND_BINARY(_OP_) \ do { \ if (JS_SP2->type == JS_INTEGER && JS_SP1->type == JS_INTEGER) \ { \ JS_SP2->u.vinteger = ((JSInt32) JS_SP2->u.vinteger \ _OP_ (JSInt32) JS_SP1->u.vinteger); \ JS_POP (); \ } \ else \ { \ JSInt32 l, r; \ \ l = js_vm_to_int32 (vm, JS_SP2); \ r = js_vm_to_int32 (vm, JS_SP1); \ \ JS_SP2->u.vinteger = (l _OP_ r); \ JS_SP2->type = JS_INTEGER; \ JS_POP (); \ } \ } while (0) #define JS_IS_NUMBER(n) \ ((n)->type == JS_INTEGER || (n)->type == JS_FLOAT || (n)->type == JS_NAN) /* Some math macros. */ #define JS_MAKE_POSITIVE_INFINITY(node) \ do { \ (node)->type = JS_FLOAT; \ (node)->u.vfloat = HUGE_VAL; \ } while (0) #define JS_MAKE_NEGATIVE_INFINITY(node) \ do { \ (node)->type = JS_FLOAT; \ (node)->u.vfloat = -HUGE_VAL; \ } while (0) #define JS_IS_POSITIVE_INFINITY(node) \ ((node)->type == JS_FLOAT && (node)->u.vfloat == HUGE_VAL) #define JS_IS_NEGATIVE_INFINITY(node) \ ((node)->type == JS_FLOAT && (node)->u.vfloat == -HUGE_VAL) #define JS_IS_FINITE(node) \ (!JS_IS_POSITIVE_INFINITY ((node)) \ && !JS_IS_NEGATIVE_INFINITY ((node)) \ && (node)->type != JS_NAN) \ #define JS_IS_PRIMITIVE_VALUE(node) \ ((node)->type == JS_UNDEFINED || (node)->type == JS_NULL \ || (node)->type == JS_BOOLEAN || JS_IS_NUMBER ((node)) \ || (node)->type == JS_STRING) /* Macro to clear all flags from a heap memory block. */ #define JS_HEAP_MEMORY_BLOCK_CLEAR_FLAGS(mb) \ do { \ (mb)->flag_mark = 0; \ (mb)->flag_destroyable = 0; \ } while (0) #define JS_NUM_HEAP_FREELISTS 20 /* * Virtual machine security flags. When these flags are enabled in * the vm->security, the appropriate built-in modules don't implement * insecure methods. */ #define JS_VM_SECURE_FILE 0x01 #define JS_VM_SECURE_SYSTEM 0x02 /* * Noticeable virtual machine events. The `JS_VM_EVENT_OPERAND_COUNT' * event is generated only if the interpreter was configured with the * `--enable-operand-hooks' option. */ #define JS_VM_EVENT_OPERAND_COUNT 1 #define JS_VM_EVENT_GARBAGE_COLLECT 2 /* * Integer types. */ typedef unsigned char JSUInt8; typedef signed char JSInt8; typedef unsigned short JSUInt16; typedef short JSInt16; #if SIZEOF_INT == 4 typedef unsigned int JSUInt32; typedef int JSInt32; #else /* not SIZEOF_INT == 4 */ #if SIZEOF_LONG == 4 typedef unsigned long JSUInt32; typedef long JSInt32; #else /* not SIZEOF_LONG == 4 */ #error "do not know how to define a 32 bit long integer" #endif /* not SIZEOF_LONG == 4 */ #endif /* not SIZEOF_INT == 4 */ /* * An unsigned interger number that can be used to align structures to * correct byte boundaries. On 64 bit machines (Alpha) this should be * 64 bits long, etc. For now one, we just assume that the mashine is * a LP64 so the `unsigned long' is a correct type for it. */ typedef unsigned long JSUIntAlign; /* I/O streams. */ /* Buffer filler or flusher function. */ typedef int (*JSIOStreamIOFunc) (void *context, unsigned char *buffer, unsigned int todo, int *error_return); typedef int (*JSIOStreamSeek) (void *context, long offset, int whence); typedef long (*JSIOStreamGetPosition) (void *context); typedef long (*JSIOStreamGetLength) (void *context); typedef void (*JSIOStreamClose) (void *context); /* The I/O stream handle. */ struct js_io_stream_st { unsigned char *buffer; /* Must be reallocatable with js_realloc(). */ unsigned int buflen; unsigned int data_in_buf; unsigned int bufpos; /* Flags. */ unsigned int at_eof : 1; unsigned int autoflush : 1; unsigned int writep : 1; /* Does the buffer contain write data? */ /* The system error code for the last operation that failed. */ int error; /* Only one of the read and write is active. */ JSIOStreamIOFunc read; JSIOStreamIOFunc write; JSIOStreamSeek seek; JSIOStreamGetPosition get_position; JSIOStreamGetLength get_length; JSIOStreamClose close; void *context; }; typedef struct js_io_stream_st JSIOStream; /* The destroy callback for the destroyable heap blocks. */ typedef void (*JSHeapDestroyableCB) (void *ptr); /* * Each destroyable heap block must be castable to this structure e.g. * the first item in the block must be pointer to the destroy function. */ struct js_heap_destroyable_st { JSHeapDestroyableCB destroy; }; typedef struct js_heap_destroyable_st JSHeapDestroyable; /* Interned symbol. */ typedef unsigned int JSSymbol; /* JavaScript Types. */ typedef enum { JS_UNDEFINED = 0, JS_NULL = 1, JS_BOOLEAN = 2, JS_INTEGER = 3, /* Integer, float and nan are `number' */ JS_STRING = 4, JS_FLOAT = 5, JS_ARRAY = 6, JS_OBJECT = 7, /* * The following ones are the internal types, used by this implementation. */ JS_SYMBOL = 10, JS_BUILTIN = 11, JS_FUNC = 12, JS_NAN = 13, JS_IPTR = 14, JS_ARGS_FIX = 15 } JSNodeType; struct js_node_st; struct js_vm_st; struct js_builtin_info_st; /* Registry information for builtin objects. */ #define JS_PROPERTY_FOUND 1 #define JS_PROPERTY_UNKNOWN 0 typedef void (*JSBuiltinGlobalMethod) (struct js_vm_st *vm, struct js_builtin_info_st *builtin_info, void *instance_context, struct js_node_st *result_return, struct js_node_st *args); /* * Function to call method <method> from the object. Function must return * JS_PROPERTY_FOUND if the method was found or JS_PROPERTY_UNKNOWN * otherwise. */ typedef int (*JSBuiltinMethod) (struct js_vm_st *vm, struct js_builtin_info_st *builtin_info, void *instance_context, JSSymbol method, struct js_node_st *result_return, struct js_node_st *args); /* * Function to load and set property <property> of object. If <set> * is true, property <property> should be set to value <node>. Otherwise * function should return the value of property <property> in <node>. * Function must return JS_PROPERTY_FOUND if the property was found or * JS_PROPERTY_UNKNOWN otherwise. */ typedef int (*JSBuiltinProperty) (struct js_vm_st *vm, struct js_builtin_info_st *builtin_info, void *instance_context, JSSymbol property, int set, struct js_node_st *node); typedef void (*JSBuiltinNew) (struct js_vm_st *vm, struct js_builtin_info_st *builtin_info, struct js_node_st *args, struct js_node_st *result_return); typedef void (*JSBuiltinDelete) (struct js_builtin_info_st *builtin_info, void *instance_context); typedef void (*JSBuiltinMark) (struct js_builtin_info_st *builtin_info, void *instance_context); typedef void (*JSBuiltinObjectCtxDelete) (void *obj_context); struct js_builtin_info_st { JSHeapDestroyableCB destroy; JSBuiltinGlobalMethod global_method_proc; JSBuiltinMethod method_proc; JSBuiltinProperty property_proc; JSBuiltinNew new_proc; JSBuiltinDelete delete_proc; JSBuiltinMark mark_proc; void *obj_context; JSBuiltinObjectCtxDelete obj_context_delete; struct js_object_st *prototype; }; typedef struct js_builtin_info_st JSBuiltinInfo; /* Builtin object / class. */ struct js_builtin_st { JSHeapDestroyableCB destroy; JSBuiltinInfo *info; void *instance_context; struct js_object_st *prototype; }; typedef struct js_builtin_st JSBuiltin; /* String. */ struct js_string_st { /* Flags. */ unsigned int staticp : 1; unsigned char *data; unsigned int len; struct js_object_st *prototype; }; typedef struct js_string_st JSString; /* Array. */ struct js_array_st { unsigned int length; struct js_node_st *data; struct js_object_st *prototype; }; typedef struct js_array_st JSArray; /* Function. */ struct js_function_st { void *implementation; struct js_object_st *prototype; }; typedef struct js_function_st JSFunction; /* Node. */ struct js_node_st { JSNodeType type; union { unsigned int vboolean; JSString *vstring; long vinteger; double vfloat; struct js_object_st *vobject; JSArray *varray; /* Internal values. */ JSSymbol vsymbol; JSBuiltin *vbuiltin; JSFunction *vfunction; void *iptr; struct { JSUInt32 argc; JSUInt32 delta; } args_fix; struct { JSUInt32 a; JSUInt32 b; } copy; } u; }; typedef struct js_node_st JSNode; /* Object. */ /* Hash node for object's properties. */ struct js_object_prop_hash_bucket_st { struct js_object_prop_hash_bucket_st *next; unsigned char *data; unsigned int len; unsigned int value; }; typedef struct js_object_prop_hash_bucket_st JSObjectPropHashBucket; /* The attribute flags for object's properties. */ #define JS_ATTRIB_READONLY 1 #define JS_ATTRIB_DONTENUM 2 #define JS_ATTRIB_DONTDELETE 4 #define JS_ATTRIB_Internal 8 /* Object's property. */ struct js_property_st { JSSymbol name; JSNode value; unsigned int attributes; }; typedef struct js_property_st JSProperty; struct js_object_st { JSObjectPropHashBucket **hash; unsigned int *hash_lengths; unsigned int num_props; /* Number of properties in this object. */ JSProperty *props; }; typedef struct js_object_st JSObject; /* Byte code. */ typedef enum { JS_BCST_CODE = 0, JS_BCST_CONSTANTS = 1, JS_BCST_SYMTAB = 2, JS_BCST_DEBUG = 3 } JSBCSectionType; struct js_bc_sect_st { JSBCSectionType type; unsigned int length; void *data; /* <length> bytes of data */ }; typedef struct js_bc_sect_st JSBCSect; struct js_bc_st { unsigned int num_sects; JSBCSect *sects; }; typedef struct js_bc_st JSByteCode; /* Debug information. */ #define JS_DI_FILENAME 1 #define JS_DI_LINENUMBER 2 /* Heap block. */ struct js_heap_block_st { struct js_heap_block_st *next; unsigned int size; /* <size> bytes of data follows the structure. */ }; typedef struct js_heap_block_st JSHeapBlock; /* Heap memory block. */ #define JS_MEM_DEBUG 0 /* All allocated blocks have this header. */ struct js_heap_memory_block_st { #if JS_MEM_DEBUG JSUIntAlign magic; #endif JSUIntAlign flag_mark : 1; JSUIntAlign flag_destroyable : 1; JSUIntAlign size : (sizeof (JSUIntAlign) * 8 - 2); /* <size> bytes of data follows this header. */ }; typedef struct js_heap_memory_block_st JSHeapMemoryBlock; /* * When the block is on the freelist, it has this header. The first * sizeof (void *) bytes of the block's data is used to hold the * freelist next pointer. */ struct js_heap_freelist_block_st { JSHeapMemoryBlock block; JSHeapMemoryBlock *next; }; typedef struct js_heap_freelist_block_st JSHeapFreelistBlock; /* Parsed symbol table entry. */ struct js_symtab_entry_st { char *name; unsigned int offset; }; typedef struct js_symtab_entry_st JSSymtabEntry; /* * Entry points to different byte-code instruction dispatcher functions. * Each dispatcher must implement these. */ typedef int (*JSVMExecute) (struct js_vm_st *vm, JSByteCode *bc, JSSymtabEntry *symtab, unsigned int num_symtab_entries, unsigned int consts_offset, unsigned int anonymous_function_offset, unsigned char *debug_info, unsigned int debug_info_len, JSNode *object, JSNode *func, unsigned int argc, JSNode *argv); typedef const char *(*JSVMFuncName) (struct js_vm_st *vm, void *pc); typedef const char *(*JSVMDebugPosition) (struct js_vm_st *vm, unsigned int *linenum_return); /* Virtual Machine. */ #define JS_HASH_TABLE_SIZE 256 struct js_hash_bucket_st { struct js_hash_bucket_st *next; char *name; union { void *data; unsigned int ui; } u; }; typedef struct js_hash_bucket_st JSHashBucket; /* Error handler frame. */ struct js_error_handler_frame_st { struct js_error_handler_frame_st *next; jmp_buf error_jmp; /* The value thrown by the throw operand. */ JSNode thrown; /* Saved state for the `try_push' operand. */ JSNode *sp; JSNode *fp; void *pc; JSInt32 pc_delta; }; typedef struct js_error_handler_frame_st JSErrorHandlerFrame; struct js_vm_st { /* Options for the virtual machine. */ unsigned int verbose; /* verbosity has different levels. */ unsigned int stacktrace_on_error : 1; unsigned int verbose_stacktrace : 1; unsigned int warn_undef : 1; /* Security flags. */ unsigned long security; /* The default system streams. */ JSIOStream *s_stdin; JSIOStream *s_stdout; JSIOStream *s_stderr; /* The byte-code instruction dispatcher. */ JSVMDispatchMethod dispatch_method; const char *dispatch_method_name; JSVMExecute dispatch_execute; JSVMFuncName dispatch_func_name; JSVMDebugPosition dispatch_debug_position; /* Constants pool. */ JSNode *consts; unsigned int num_consts; unsigned int consts_alloc; /* * Global symbols (both functions and variables). <globals_hash> is * a name-to-index mapping between symbol names and their positions * in <globals>. */ JSHashBucket *globals_hash[JS_HASH_TABLE_SIZE]; JSNode *globals; unsigned int num_globals; unsigned int globals_alloc; /* The next anonymous function id. */ unsigned int anonymous_function_next_id; /* Stack. */ JSNode *stack; unsigned int stack_size; JSNode *sp; /* Fuzzy stack pointer. */ void *pc; /* Fuzzy program counter. */ /* Builtin objects for the primitive datatypes. */ JSBuiltinInfo *prim[JS_IPTR + 1]; /* Some commonly used symbols. */ struct { JSSymbol s___proto__; JSSymbol s_prototype; JSSymbol s_toSource; JSSymbol s_toString; JSSymbol s_valueOf; } syms; /* Heap. */ JSHeapBlock *heap; JSHeapMemoryBlock *heap_freelists[JS_NUM_HEAP_FREELISTS]; unsigned long heap_size; /* Information for the garbage collector. */ struct { unsigned long trigger; unsigned long bytes_allocated; unsigned long bytes_free; unsigned long count; } gc; /* Error handler frames. */ JSErrorHandlerFrame *error_handler; /* Buffer for the error message. Sorry, we don't support long errors ;-) */ char error[1024]; /* * The result from the latest evaluation. This is set when the * js_vm_execute(), js_vm_apply(), or js_vm_call_method() functions * return to the caller. */ JSNode exec_result; /* Event callback hook. */ int (*hook) (int event, void *context); void *hook_context; unsigned int hook_operand_count; unsigned int hook_operand_count_trigger; /* How many file descriptors can be allocated. */ unsigned long fd_count; #if PROFILING /* Byte-code operand profiling support. */ unsigned int prof_count[256]; unsigned char prof_op; #endif /* PROFILING */ }; typedef struct js_vm_st JSVirtualMachine; /* * Global variables. */ extern unsigned char js_latin1_tolower[256]; extern unsigned char js_latin1_toupper[256]; /* * Prototypes for global functions. */ /* * Memory allocation routines. If the allocation request fails, the * error recovery is performed according to the argument <vm>. If * <vm> is not NULL, an error message is formatted to vm->error and an * error is raise with js_vm_error(). If the <vm> is NULL, the * functions will return value NULL. It is an error to call these * functions with a non-NULL <vm> that has no error handler * initialized. */ #ifndef JS_DEBUG_MEMORY_LEAKS #define JS_DEBUG_MEMORY_LEAKS 0 #endif /* not JS_DEBUG_MEMORY_LEAKS */ #if JS_DEBUG_MEMORY_LEAKS #define js_malloc(vm, size) js_malloc_i ((vm), (size), \ __FILE__, __LINE__) #define js_calloc(vm, num, size) js_calloc_i ((vm), (num), (size), \ __FILE__, __LINE__) #define js_realloc(vm, ptr, size) js_realloc_i ((vm), (ptr), (size), \ __FILE__, __LINE__) #define js_strdup(vm, str) js_strdup_i ((vm), (str), \ __FILE__, __LINE__) void *js_malloc_i (JSVirtualMachine *vm, size_t size, char *, int); void *js_calloc_i (JSVirtualMachine *vm, size_t num, size_t size, char *, int); void *js_realloc_i (JSVirtualMachine *vm, void *ptr, size_t size, char *, int); void js_free (void *ptr); char *js_strdup_i (JSVirtualMachine *vm, const char *str, char *, int); #else /* not JS_DEBUG_MEMORY_LEAKS */ void *js_malloc (JSVirtualMachine *vm, size_t size); void *js_calloc (JSVirtualMachine *vm, size_t num, size_t size); void *js_realloc (JSVirtualMachine *vm, void *ptr, size_t size); void js_free (void *ptr); char *js_strdup (JSVirtualMachine *vm, const char *str); #endif /* not JS_DEBUG_MEMORY_LEAKS */ /* Byte code. */ JSByteCode *js_bc_read_file (FILE *fp); JSByteCode *js_bc_read_data (unsigned char *data, unsigned int datalen); void js_bc_free (JSByteCode *bc); /* I/O streams. */ /* Allocate one I/O stream handle. */ JSIOStream *js_iostream_new (); JSIOStream *js_iostream_file (FILE *fp, int readp, int writep, int do_close); JSIOStream *js_iostream_pipe (FILE *fp, int readp); size_t js_iostream_read (JSIOStream *stream, void *ptr, size_t size); size_t js_iostream_write (JSIOStream *stream, void *ptr, size_t size); int js_iostream_flush (JSIOStream *stream); int js_iostream_unget (JSIOStream *stream, int byte); int js_iostream_close (JSIOStream *stream); int js_iostream_seek (JSIOStream *stream, long offset, int whence); long js_iostream_get_position (JSIOStream *stream); long js_iostream_get_length (JSIOStream *stream); void js_iostream_fill_buffer (JSIOStream *stream); /* Virtual machine. */ JSVirtualMachine *js_vm_create (unsigned int stack_size, JSVMDispatchMethod dispatch_method, unsigned int verbose, int stacktrace_on_error, JSIOStream *s_stdin, JSIOStream *s_stdout, JSIOStream *s_stderr); void js_vm_destroy (JSVirtualMachine *vm); /* * Execute byte code <bc>. Function returns 1 if the operation was * successful or 0 if any errors were encountered. In case of errors, * the error message is stored at vm->error. */ int js_vm_execute (JSVirtualMachine *vm, JSByteCode *bc); /* * Apply function <func_name> to arguments <argc, argv>. If * function's name <func_name> is NULL, then <func> must specify function * to which arguments are applied. */ int js_vm_apply (JSVirtualMachine *vm, char *func_name, JSNode *func, unsigned int argc, JSNode *argv); /* * Call method <method_name> from object <objet> with arguments <argc, argv>. */ int js_vm_call_method (JSVirtualMachine *vm, JSNode *object, const char *method_name, unsigned int argc, JSNode *argv); /* Map program counter to the source file line. */ const char *js_vm_debug_position (JSVirtualMachine *vm, unsigned int *linenum_return); /* Fetch the function name from the program counter value. */ const char *js_vm_func_name (JSVirtualMachine *vm, void *pc); /* Intern symbol <name, len> to virtual machine and return its JSSymbol id. */ JSSymbol js_vm_intern_with_len (JSVirtualMachine *vm, const char *name, unsigned int len); /* Intern symbol <name> to virtual machine and return its JSSymbol id. */ static inline JSSymbol js_vm_intern (JSVirtualMachine *vm, const char *name) { return js_vm_intern_with_len (vm, name, strlen (name)); } /* Return the name of symbol <sym>. */ const char *js_vm_symname (JSVirtualMachine *vm, JSSymbol sym); /* * ToPrimitive(). Convert node <n> to its primitive value and return * the result in <result_return>. */ void js_vm_to_primitive (JSVirtualMachine *vm, const JSNode *n, JSNode *result_return, JSNodeType preferred_type); /* * ToString(). Convert node <n> to its string presentations and * return the result in <result_return>. */ void js_vm_to_string (JSVirtualMachine *vm, const JSNode *n, JSNode *result_return); /* * ToNumber(). Convert node <n> to its number presentations and * return the result in <result_return>. */ void js_vm_to_number (JSVirtualMachine *vm, const JSNode *n, JSNode *result_return); /* ToObject(). Convert node <n> to object according to its type. */ void js_vm_to_object (JSVirtualMachine *vm, const JSNode *n, JSNode *result_return); /* * ToInt32(). Convert node <n> to its signed 32 bit integer * presentation and return the result. */ JSInt32 js_vm_to_int32 (JSVirtualMachine *vm, JSNode *n); /* * ToBoolean(). Convert node <n> to a boolean value and return the * result. */ int js_vm_to_boolean (JSVirtualMachine *vm, JSNode *n); /* * Raise an error. The error message must have been saved to vm->error * before this function is called. The function never returns. */ void js_vm_error (JSVirtualMachine *vm); /* * Count a hash value for <data_len> bytes of data <data>. The resulting * hash value should be re-mapped to the correct range, for example, * with the mod operand. */ static inline unsigned int js_count_hash (const char *data, unsigned int data_len) { unsigned int val = 0, i; for (i = 0; i < data_len; i++) val = (val << 5) ^ (unsigned char) data[i] ^ (val >> 16) ^ (val >> 7); return val; } /* Prototypes for the different instruction dispatcher implementations. */ #if ALL_DISPATCHERS int js_vm_switch0_exec (JSVirtualMachine *vm, JSByteCode *bc, JSSymtabEntry *symtab, unsigned int num_symtab_entries, unsigned int consts_offset, unsigned int anonymous_function_offset, unsigned char *debug_info, unsigned int debug_info_len, JSNode *object, JSNode *func, unsigned int argc, JSNode *argv); const char *js_vm_switch0_func_name (JSVirtualMachine *vm, void *pc); const char *js_vm_switch0_debug_position (JSVirtualMachine *vm, unsigned int *linenum_return); #endif /* ALL_DISPATCHERS */ int js_vm_switch_exec (JSVirtualMachine *vm, JSByteCode *bc, JSSymtabEntry *symtab, unsigned int num_symtab_entries, unsigned int consts_offset, unsigned int anonymous_function_offset, unsigned char *debug_info, unsigned int debug_info_len, JSNode *object, JSNode *func, unsigned int argc, JSNode *argv); const char *js_vm_switch_func_name (JSVirtualMachine *vm, void *pc); const char *js_vm_switch_debug_position (JSVirtualMachine *vm, unsigned int *linenum_return); int js_vm_jumps_exec (JSVirtualMachine *vm, JSByteCode *bc, JSSymtabEntry *symtab, unsigned int num_symtab_entries, unsigned int consts_offset, unsigned int anonymous_function_offset, unsigned char *debug_info, unsigned int debug_info_len, JSNode *object, JSNode *func, unsigned int argc, JSNode *argv); const char *js_vm_jumps_func_name (JSVirtualMachine *vm, void *pc); const char *js_vm_jumps_debug_position (JSVirtualMachine *vm, unsigned int *linenum_return); /* Heap. */ void *js_vm_alloc (JSVirtualMachine *vm, unsigned int size); void *js_vm_alloc_destroyable (JSVirtualMachine *vm, unsigned int size); void *js_vm_realloc (JSVirtualMachine *vm, void *ptr, unsigned int new_size); void js_vm_free (JSVirtualMachine *vm, void *ptr); void js_vm_garbage_collect (JSVirtualMachine *vm, JSNode *fp, JSNode *sp); void js_vm_clear_heap (JSVirtualMachine *vm); void js_vm_mark (JSNode *node); int js_vm_mark_ptr (void *ptr); int js_vm_is_marked_ptr (void *ptr); /* Function. */ static inline JSFunction * js_vm_make_function (JSVirtualMachine *vm, void *implementation) { JSFunction *f = (JSFunction *) js_vm_alloc (vm, sizeof (*f)); f->implementation = implementation; f->prototype = (JSObject *) NULL; return f; } /* Built-in. */ /* Create a new built-in info. */ JSBuiltinInfo *js_vm_builtin_info_create (JSVirtualMachine *vm); /* Create a new builtin object with <info, instance_context> to <result>. */ void js_vm_builtin_create (JSVirtualMachine *vm, JSNode *result, JSBuiltinInfo *info, void *instance_context); /* Array. */ static inline void js_vm_make_array (JSVirtualMachine *vm, JSNode *n, unsigned int length) { unsigned int i; n->type = JS_ARRAY; n->u.varray = (JSArray *) js_vm_alloc (vm, sizeof (*n->u.varray)); n->u.varray->prototype = NULL; n->u.varray->length = length; n->u.varray->data = (JSNode *) js_vm_alloc (vm, length * sizeof (JSNode)); for (i = 0; i < length; i++) n->u.varray->data[i].type = JS_UNDEFINED; } static inline void js_vm_expand_array (JSVirtualMachine *vm, JSNode *n, unsigned int length) { if (n->u.varray->length < length) { n->u.varray->data = (JSNode *) js_vm_realloc (vm, n->u.varray->data, length * sizeof (JSNode)); for (; n->u.varray->length < length; n->u.varray->length++) n->u.varray->data[n->u.varray->length].type = JS_UNDEFINED; } } /* File. */ /* Enter file <fp> to the system. */ void js_builtin_File_new (JSVirtualMachine *vm, JSNode *result_return, char *path, JSIOStream *stream, int dont_close); /* RegExp. */ /* * Create a new regular expression node from <source, sourcelen> according * to <flags>. The argument <immutable> defines whether the created * regexp is immutable. The new regexp is returned in <result_return>. * If the <info> is NULL, the function will resolve it. Otherwise the given * value is used. */ void js_builtin_RegExp_new (JSVirtualMachine *vm, char *source, unsigned int source_len, unsigned int flags, int immutable, JSBuiltinInfo *info, JSNode *result_return); /* * Do search-replace for the string <data, datalen> by replacing * matches of <regexp> with <repl, repl_len>. The resulting string is * returned in <result_return> */ void js_builtin_RegExp_replace (JSVirtualMachine *vm, char *data, unsigned int datalen, JSNode *regexp, char *repl, unsigned int repl_len, JSNode *result_return); /* * Do regexp match against <data, datalen>. Format the result array * to <result_return>. */ void js_builtin_RegExp_match (JSVirtualMachine *vm, char *data, unsigned int datalen, JSNode *regexp, JSNode *result_return); /* * Do regexp search against <data, datalen>. Return the start index of * the match in <result_return>. */ void js_builtin_RegExp_search (JSVirtualMachine *vm, char *data, unsigned int datalen, JSNode *regexp, JSNode *result_return); /* * Split the string <data, datalen> by regular expression <regexp>. * Function returns an array containing the substrings. */ void js_builtin_RegExp_split (JSVirtualMachine *vm, char *data, unsigned int datalen, JSNode *regexp, unsigned int limit, JSNode *result_return); /* Object. */ JSObject *js_vm_object_new (JSVirtualMachine *vm); void js_vm_object_mark (JSObject *obj); int js_vm_object_load_property (JSVirtualMachine *vm, JSObject *obj, JSSymbol prop, JSNode *value_return); void js_vm_object_store_property (JSVirtualMachine *vm, JSObject *obj, JSSymbol prop, JSNode *value); void js_vm_object_delete_property (JSVirtualMachine *vm, JSObject *obj, JSSymbol prop); void js_vm_object_load_array (JSVirtualMachine *vm, JSObject *obj, JSNode *sel, JSNode *value_return); void js_vm_object_store_array (JSVirtualMachine *vm, JSObject *obj, JSNode *sel, JSNode *value); void js_vm_object_delete_array (JSVirtualMachine *vm, JSObject *obj, JSNode *sel); int js_vm_object_nth (JSVirtualMachine *vm, JSObject *obj, int nth, JSNode *value_return); /* Debug. */ void js_vm_stacktrace (JSVirtualMachine *vm, unsigned int num_frames); /* Strings. */ static inline void js_vm_make_string (JSVirtualMachine *vm, JSNode *n, const char *data, unsigned int data_len) { n->type = JS_STRING; n->u.vstring = (JSString *) js_vm_alloc (vm, sizeof (*n->u.vstring)); n->u.vstring->staticp = 0; n->u.vstring->prototype = NULL; n->u.vstring->len = data_len; n->u.vstring->data = (unsigned char *) js_vm_alloc (vm, data_len); if (data) memcpy (n->u.vstring->data, data, data_len); } static inline void js_vm_make_static_string (JSVirtualMachine *vm, JSNode *n, const char *data, unsigned int data_len) { n->type = JS_STRING; n->u.vstring = (JSString *) js_vm_alloc (vm, sizeof (*n->u.vstring)); n->u.vstring->staticp = 1; n->u.vstring->prototype = NULL; n->u.vstring->len = data_len; n->u.vstring->data = (unsigned char *) data; } static inline int js_compare_strings (JSNode *a, JSNode *b) { unsigned int i; for (i = 0; i < a->u.vstring->len && i < b->u.vstring->len; i++) { if (a->u.vstring->data[i] < b->u.vstring->data[i]) return -1; if (a->u.vstring->data[i] > b->u.vstring->data[i]) return 1; } if (a->u.vstring->len < b->u.vstring->len) return -1; if (a->u.vstring->len > b->u.vstring->len) return 1; return 0; } static inline char * js_string_to_c_string (JSVirtualMachine *vm, const JSNode *a) { char *cp; cp = (char *) js_malloc (vm, a->u.vstring->len + 1); memcpy (cp, a->u.vstring->data, a->u.vstring->len); cp[a->u.vstring->len] = '\0'; return cp; } /* Dynamic loading. */ /* * Try to open shared library <filename>. If the opening was * successful, a handle to the library is returned. Otherwise, the * function returns NULL, and an error message is returned in * <error_return>. The argument <error_return_len> specifies the * maximum length of the error message the function should return. */ void *js_dl_open (const char *filename, char *error_return, unsigned int error_return_len); /* * Try to fetch the address of the symbol <symbol> from shared library * <library>. */ void *js_dl_sym (void *library, char *symbol, char *error_return, unsigned int error_return_len); /* Misc helper functions. */ unsigned long js_crc32 (const unsigned char *s, unsigned int len); /* * Definitions for the JavaScript part of the JavaScript interp. */ /* Flags for the compiler. See `jsc/entry.js'. */ #define JSC_FLAG_VERBOSE 0x00000001 #define JSC_FLAG_ANNOTATE_ASSEMBLER 0x00000002 #define JSC_FLAG_GENERATE_DEBUG_INFO 0x00000004 #define JSC_FLAG_GENERATE_EXECUTABLE_BC_FILES 0x00000008 #define JSC_FLAG_OPTIMIZE_PEEPHOLE 0x00000020 #define JSC_FLAG_OPTIMIZE_JUMPS 0x00000040 #define JSC_FLAG_OPTIMIZE_BC_SIZE 0x00000080 #define JSC_FLAG_OPTIMIZE_HEAVY 0x00000100 #define JSC_FLAG_OPTIMIZE_MASK 0x0000fff0 #define JSC_FLAG_WARN_UNUSED_ARGUMENT 0x00010000 #define JSC_FLAG_WARN_UNUSED_VARIABLE 0x00020000 #define JSC_FLAG_WARN_SHADOW 0x00040000 #define JSC_FLAG_WARN_WITH_CLOBBER 0x00080000 #define JSC_FLAG_WARN_MISSING_SEMICOLON 0x00100000 #define JSC_FLAG_WARN_STRICT_ECMA 0x00200000 #define JSC_FLAG_WARN_DEPRECATED 0x00400000 #define JSC_FLAG_WARN_MASK 0xffff0000 /* JavaScript interpreter handle. */ struct js_interp_st { JSInterpOptions options; JSVirtualMachine *vm; }; /* Declaration for the JS compiler byte-code. */ extern const unsigned char js_compiler_bytecode[]; extern unsigned int js_compiler_bytecode_len; #ifdef __cplusplus } #endif #endif /* not JSINT_H */