ClamAV is an open source (GPLv2) anti-virus toolkit.
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.
 
 
 
 
 
 
clamav/libclamav/js/vm.c

974 lines
22 KiB

/*
* Common parts for the JavaScript virtual machine.
* 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/vm.c,v $
* $Id: vm.c,v 1.2 2006/10/28 11:27:44 njh Exp $
*/
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
#ifdef CL_EXPERIMENTAL
#include "jsint.h"
/*
* Types and definitions.
*/
/*
* Collect garbage if we allocated more than GC_TRIGGER bytes of
* memory since the last gc.
*/
#if SIZEOF_INT == 2
#define GC_TRIGGER (1L * 1024L * 1024L)
#else
#define GC_TRIGGER (2 * 1024 * 1024)
#endif
/*
* Prototypes for static functions.
*/
static void intern_builtins (JSVirtualMachine *vm);
/*
* Global functions.
*/
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)
{
JSVirtualMachine *vm;
vm = js_calloc (NULL, 1, sizeof (*vm));
if (vm == NULL)
return NULL;
vm->verbose = verbose;
vm->stacktrace_on_error = stacktrace_on_error;
vm->warn_undef = 1;
/* Set the system streams. */
vm->s_stdin = s_stdin;
vm->s_stdout = s_stdout;
vm->s_stderr = s_stderr;
/* Resolve the dispatch method. */
switch (dispatch_method)
{
case JS_VM_DISPATCH_SWITCH_BASIC:
#if ALL_DISPATCHERS
vm->dispatch_method = dispatch_method;
vm->dispatch_method_name = "switch-basic";
vm->dispatch_execute = js_vm_switch0_exec;
vm->dispatch_func_name = js_vm_switch0_func_name;
vm->dispatch_debug_position = js_vm_switch0_debug_position;
#endif
break;
case JS_VM_DISPATCH_JUMPS:
#if __GNUC__ && !DISABLE_JUMPS
vm->dispatch_method = dispatch_method;
vm->dispatch_method_name = "jumps";
vm->dispatch_execute = js_vm_jumps_exec;
vm->dispatch_func_name = js_vm_jumps_func_name;
vm->dispatch_debug_position = js_vm_jumps_debug_position;
#endif /* not (__GNUC__ && !DISABLE_JUMPS) */
break;
case JS_VM_DISPATCH_SWITCH:
/* This is the default, let the default catcher handle us. */
break;
}
if (vm->dispatch_execute == NULL)
{
/* Set the default that is the optimized switch. */
vm->dispatch_method = JS_VM_DISPATCH_SWITCH;
vm->dispatch_method_name = "switch";
vm->dispatch_execute = js_vm_switch_exec;
vm->dispatch_func_name = js_vm_switch_func_name;
vm->dispatch_debug_position = js_vm_switch_debug_position;
}
vm->stack_size = stack_size;
vm->stack = js_malloc (NULL, vm->stack_size * sizeof (*vm->stack));
if (vm->stack == NULL)
{
js_free (vm);
return NULL;
}
/* Set the initial stack pointer. */
vm->sp = vm->stack + vm->stack_size - 1;
vm->gc.trigger = GC_TRIGGER;
/* We need a toplevel here. */
{
JSErrorHandlerFrame handler;
int result = 1;
memset (&handler, 0, sizeof (handler));
handler.next = vm->error_handler;
vm->error_handler = &handler;
if (setjmp (vm->error_handler->error_jmp))
/* An error occurred. */
result = 0;
else
{
/* Intern some commonly used symbols. */
vm->syms.s___proto__ = js_vm_intern (vm, "__proto__");
vm->syms.s_prototype = js_vm_intern (vm, "prototype");
vm->syms.s_toSource = js_vm_intern (vm, "toSource");
vm->syms.s_toString = js_vm_intern (vm, "toString");
vm->syms.s_valueOf = js_vm_intern (vm, "valueOf");
/* Intern system built-in objects. */
intern_builtins (vm);
}
/* Pop the error handler. */
vm->error_handler = vm->error_handler->next;
if (result == 0)
{
/* Argh, the initialization failed. */
js_vm_destroy (vm);
return NULL;
}
}
return vm;
}
void
js_vm_destroy (JSVirtualMachine *vm)
{
int i;
JSHeapBlock *hb, *hb2;
JSErrorHandlerFrame *f, *f2;
JSHashBucket *hashb, *hashb_next;
/* Free all objects from the heap. */
js_vm_clear_heap (vm);
/* Free the constants. */
for (i = 0; i < vm->num_consts; i++)
if (vm->consts[i].type == JS_STRING)
js_free (vm->consts[i].u.vstring->data);
js_free (vm->consts);
/* Free the globals. */
for (i = 0; i < JS_HASH_TABLE_SIZE; i++)
for (hashb = vm->globals_hash[i]; hashb; hashb = hashb_next)
{
hashb_next = hashb->next;
js_free (hashb->name);
js_free (hashb);
}
js_free (vm->globals);
/* Stack. */
js_free (vm->stack);
/* Heap blocks. */
for (hb = vm->heap; hb; hb = hb2)
{
hb2 = hb->next;
js_free (hb);
}
/* Error handlers. */
for (f = vm->error_handler; f; f = f2)
{
f2 = f->next;
js_free (f);
}
#if PROFILING
#define NUM_OPS 68
/* Dump profiling data to the stderr. */
{
unsigned int total = 0;
int i;
/* Count total interrupts. */
for (i = 0; i <= NUM_OPS; i++)
total += vm->prof_count[i];
/* Dump individual statistics. */
for (i = 0; i <= NUM_OPS; i++)
{
char buf[512];
sprintf (buf, "%d\t%u\t%.2f%s",
i, vm->prof_count[i],
(double) vm->prof_count[i] / total * 100,
JS_HOST_LINE_BREAK);
js_iostream_write (vm->s_stderr);
}
}
#endif /* PROFILING */
/* Flush and free the default system streams. */
js_iostream_close (vm->s_stdin);
js_iostream_close (vm->s_stdout);
js_iostream_close (vm->s_stderr);
/* And finally, the VM handle. */
js_free (vm);
}
#if PROFILING
/*
* The support stuffs for the byte-code operand profiling.
*/
static JSVirtualMachine *profiling_vm = NULL;
static void
sig_alarm (int sig)
{
if (profiling_vm && profiling_vm->prof_op < 100)
profiling_vm->prof_count[profiling_vm->prof_op]++;
signal (sig, sig_alarm);
}
/* Turn on the byte-code operand profiling. */
#define PROFILING_ON() \
profiling_vm = vm; \
vm->prof_op = 255; \
signal (SIGALRM, sig_alarm); \
ualarm (1, 1)
/* Turn off the byte-code operand profiling. */
#define PROFILING_OFF() \
vm->prof_op = 255; \
ualarm (0, 0); \
signal (SIGALRM, SIG_IGN); \
profiling_vm = NULL
#else /* not PROFILING */
#define PROFILING_ON()
#define PROFILING_OFF()
#endif /* not PROFILING */
int
js_vm_execute (JSVirtualMachine *vm, JSByteCode *bc)
{
int i, sect;
unsigned int ui;
unsigned char *cp;
unsigned int consts_offset;
char buf[256];
JSSymtabEntry *symtab = NULL;
unsigned int num_symtab_entries = 0;
unsigned int code_len = 0;
JSNode *saved_sp;
JSErrorHandlerFrame *handler, *saved_handler;
int result = 1;
unsigned char *debug_info;
unsigned int debug_info_len;
unsigned int anonymous_function_offset;
/* We need a toplevel over the whole function. */
saved_sp = vm->sp;
saved_handler = vm->error_handler;
handler = js_calloc (NULL, 1, sizeof (*handler));
if (handler == NULL)
{
sprintf (vm->error, "VM: out of memory");
return 0;
}
handler->next = vm->error_handler;
vm->error_handler = handler;
if (setjmp (vm->error_handler->error_jmp))
{
/* Ok, we had an error down there somewhere. */
result = 0;
}
else
{
/* The main stuffs for the execute. */
/* Process constants. */
consts_offset = vm->num_consts;
anonymous_function_offset = vm->anonymous_function_next_id;
for (sect = 0; sect < bc->num_sects; sect++)
if (bc->sects[sect].type == JS_BCST_CONSTANTS)
{
cp = bc->sects[sect].data;
for (ui = 0; ui < bc->sects[sect].length;)
{
JSNode *c;
/* Check that we still have space for this constant. */
if (vm->num_consts >= vm->consts_alloc)
{
vm->consts = js_realloc (vm, vm->consts,
(vm->consts_alloc + 1024)
* sizeof (JSNode));
vm->consts_alloc += 1024;
}
c = &vm->consts[vm->num_consts++];
/* Process this constant. */
c->type = (JSNodeType) cp[ui++];
switch (c->type)
{
case JS_NULL:
break;
case JS_BOOLEAN:
c->u.vboolean = cp[ui++];
break;
case JS_STRING:
c->u.vstring = js_vm_alloc (vm, sizeof (*c->u.vstring));
c->u.vstring->staticp = 1;
c->u.vstring->prototype = NULL;
JS_BC_READ_INT32 (cp + ui, c->u.vstring->len);
ui += 4;
c->u.vstring->data = js_malloc (vm, c->u.vstring->len + 1);
memcpy (c->u.vstring->data, cp + ui, c->u.vstring->len);
c->u.vstring->data[c->u.vstring->len] = '\0';
ui += c->u.vstring->len;
break;
case JS_INTEGER:
JS_BC_READ_INT32 (cp + ui, c->u.vinteger);
ui += 4;
break;
case JS_FLOAT:
memcpy (&c->u.vfloat, cp + ui, 8);
ui += 8;
break;
case JS_SYMBOL:
for (i = 0; cp[ui]; ui++, i++)
buf[i] = cp[ui];
buf[i] = '\0';
/* Eat the trailing '\0' from the data. */
ui++;
if (buf[0] == '.' && buf[1] == 'F' && buf[2] == ':')
sprintf (buf + 3, "%u",
vm->anonymous_function_next_id++);
/* Intern symbol. */
c->u.vsymbol = js_vm_intern (vm, buf);
break;
case JS_BUILTIN:
/* Regular expression. */
{
unsigned char flags;
unsigned int length;
flags = cp[ui++];
JS_BC_READ_INT32 (cp + ui, length);
ui += 4;
js_builtin_RegExp_new (vm, cp + ui, length, flags, 1,
NULL, c);
ui += length;
}
break;
case JS_NAN:
/* Nothing here. */
break;
default:
case JS_IPTR:
sprintf (buf,
"js_vm_execute(): unknown constant type %d%s",
c->type,
JS_HOST_LINE_BREAK);
js_iostream_write (vm->s_stderr, buf, strlen (buf));
js_iostream_flush (vm->s_stderr);
abort ();
break;
}
}
/* All done with the constants. */
break;
}
/* Check how long the code section is. */
for (sect = 0; sect < bc->num_sects; sect++)
if (bc->sects[sect].type == JS_BCST_CODE)
{
code_len = bc->sects[sect].length;
break;
}
/* Process symbol table. */
for (sect = 0; sect < bc->num_sects; sect++)
if (bc->sects[sect].type == JS_BCST_SYMTAB)
{
JSSymtabEntry *se;
char buf[257];
cp = bc->sects[sect].data;
/* The number of symbols. */
JS_BC_READ_INT32 (cp, num_symtab_entries);
symtab = js_calloc (vm, num_symtab_entries + 1, sizeof (*symtab));
/* Make the terminator by hand. */
symtab[num_symtab_entries].offset = code_len;
/* Enter symbols. */
se = symtab;
for (ui = 4; ui < bc->sects[sect].length; se++)
{
for (i = 0; cp[ui]; ui++, i++)
buf[i] = cp[ui];
buf[i] = '\0';
se->name = js_strdup (vm, buf);
ui++;
JS_BC_READ_INT32 (cp + ui, se->offset);
ui += 4;
}
break;
}
/* Check if we have debugging information. */
debug_info = NULL;
debug_info_len = 0;
for (sect = 0; sect < bc->num_sects; sect++)
if (bc->sects[sect].type == JS_BCST_DEBUG)
{
debug_info = bc->sects[sect].data;
debug_info_len = bc->sects[sect].length;
}
/* Clear error message and old exec result. */
vm->error[0] = '\0';
vm->exec_result.type = JS_UNDEFINED;
PROFILING_ON ();
/* Execute. */
result = (*vm->dispatch_execute) (vm, bc, symtab, num_symtab_entries,
consts_offset,
anonymous_function_offset,
debug_info, debug_info_len,
NULL, NULL, 0, NULL);
}
PROFILING_OFF ();
if (symtab)
{
for (ui = 0; ui < num_symtab_entries; ui++)
js_free (symtab[ui].name);
js_free (symtab);
}
/* Pop all error handler frames from the handler chain. */
for (; vm->error_handler != saved_handler; vm->error_handler = handler)
{
handler = vm->error_handler->next;
js_free (vm->error_handler);
}
/* Restore virtual machine's idea about the stack top. */
vm->sp = saved_sp;
return result;
}
int
js_vm_apply (JSVirtualMachine *vm, char *func_name, JSNode *func,
unsigned int argc, JSNode *argv)
{
int result = 1;
JSNode *saved_sp;
JSErrorHandlerFrame *handler, *saved_handler;
/* Initialize error handler. */
saved_sp = vm->sp;
saved_handler = vm->error_handler;
handler = js_calloc (NULL, 1, sizeof (*handler));
if (handler == NULL)
{
sprintf (vm->error, "VM: out of memory");
return 0;
}
handler->next = vm->error_handler;
vm->error_handler = handler;
if (setjmp (vm->error_handler->error_jmp))
{
/* An error occurred. */
result = 0;
}
else
{
/* Clear error message and old exec result. */
vm->error[0] = '\0';
vm->exec_result.type = JS_UNDEFINED;
if (func_name)
/* Lookup the function. */
func = &vm->globals[js_vm_intern (vm, func_name)];
/* Check what kind of function should be called. */
if (func->type == JS_FUNC)
{
PROFILING_ON ();
/* Call function. */
result = (*vm->dispatch_execute) (vm, NULL, NULL, 0, 0, 0,
NULL, 0,
NULL, func, argc, argv);
}
else if (func->type == JS_BUILTIN
&& func->u.vbuiltin->info->global_method_proc != NULL)
{
(*func->u.vbuiltin->info->global_method_proc) (
vm,
func->u.vbuiltin->info,
func->u.vbuiltin->instance_context,
&vm->exec_result,
argv);
}
else
{
if (func_name)
sprintf (vm->error, "undefined function `%s' in apply",
func_name);
else
sprintf (vm->error, "undefiend function in apply");
result = 0;
}
}
PROFILING_OFF ();
/* Pop all error handler frames from the handler chain. */
for (; vm->error_handler != saved_handler; vm->error_handler = handler)
{
handler = vm->error_handler->next;
js_free (vm->error_handler);
}
/* Restore virtual machine's idea about the stack top. */
vm->sp = saved_sp;
return result;
}
int
js_vm_call_method (JSVirtualMachine *vm, JSNode *object,
const char *method_name, unsigned int argc, JSNode *argv)
{
int result = 1;
JSNode *saved_sp;
JSErrorHandlerFrame *handler, *saved_handler;
JSSymbol symbol;
/* Initialize error handler. */
saved_sp = vm->sp;
saved_handler = vm->error_handler;
handler = js_calloc (NULL, 1, sizeof (*handler));
if (handler == NULL)
{
sprintf (vm->error, "VM: out of memory");
return 0;
}
handler->next = vm->error_handler;
vm->error_handler = handler;
if (setjmp (vm->error_handler->error_jmp))
{
/* An error occurred. */
result = 0;
}
else
{
/* Intern the method name. */
symbol = js_vm_intern (vm, method_name);
/* Clear error message and old exec result. */
vm->error[0] = '\0';
vm->exec_result.type = JS_UNDEFINED;
/* What kind of object was called? */
if (object->type == JS_BUILTIN)
{
if (object->u.vbuiltin->info->method_proc)
{
if ((*object->u.vbuiltin->info->method_proc) (
vm,
object->u.vbuiltin->info,
object->u.vbuiltin->instance_context,
symbol,
&vm->exec_result, argv)
== JS_PROPERTY_UNKNOWN)
{
sprintf (vm->error, "call_method: unknown method");
result = 0;
}
}
else
{
sprintf (vm->error, "illegal builtin object for call_method");
result = 0;
}
}
else if (object->type == JS_OBJECT)
{
JSNode method;
/* Fetch the method's implementation. */
if (js_vm_object_load_property (vm, object->u.vobject, symbol,
&method)
== JS_PROPERTY_FOUND)
{
/* The property has been defined in the object. */
if (method.type != JS_FUNC)
{
sprintf (vm->error, "call_method: unknown method");
result = 0;
}
else
{
PROFILING_ON ();
result = (*vm->dispatch_execute) (vm, NULL, NULL, 0, 0, 0,
NULL, 0,
object, &method, argc,
argv);
}
}
else
/* Let the built-in Object handle this. */
goto to_builtin_please;
}
else if (vm->prim[object->type])
{
/* The primitive language types. */
to_builtin_please:
if ((*vm->prim[object->type]->method_proc) (vm,
vm->prim[object->type],
object, symbol,
&vm->exec_result,
argv)
== JS_PROPERTY_UNKNOWN)
{
sprintf (vm->error, "call_method: unknown method");
result = 0;
}
}
else
{
sprintf (vm->error, "illegal object for call_method");
result = 0;
}
}
PROFILING_OFF ();
/* Pop all error handler frames from the handler chain. */
for (; vm->error_handler != saved_handler; vm->error_handler = handler)
{
handler = vm->error_handler->next;
js_free (vm->error_handler);
}
/* Restore virtual machine's idea about the stack top. */
vm->sp = saved_sp;
return result;
}
const char *
js_vm_func_name (JSVirtualMachine *vm, void *pc)
{
return (*vm->dispatch_func_name) (vm, pc);
}
const char *
js_vm_debug_position (JSVirtualMachine *vm, unsigned int *linenum_return)
{
return (*vm->dispatch_debug_position) (vm, linenum_return);
}
unsigned int
js_vm_intern_with_len (JSVirtualMachine *vm, const char *name,
unsigned int len)
{
JSHashBucket *b;
unsigned int pos = js_count_hash (name, len) % JS_HASH_TABLE_SIZE;
for (b = vm->globals_hash[pos]; b; b = b->next)
if (strcmp (b->name, name) == 0)
return b->u.ui;
b = js_malloc (vm, sizeof (*b));
b->name = js_strdup (vm, name);
b->next = vm->globals_hash[pos];
vm->globals_hash[pos] = b;
/* Alloc space from the globals array. */
if (vm->num_globals >= vm->globals_alloc)
{
vm->globals = js_realloc (vm, vm->globals,
(vm->globals_alloc + 1024) * sizeof (JSNode));
vm->globals_alloc += 1024;
}
/* Initialize symbol's name spaces. */
vm->globals[vm->num_globals].type = JS_UNDEFINED;
b->u.ui = vm->num_globals++;
return b->u.ui;
}
const char *
js_vm_symname (JSVirtualMachine *vm, JSSymbol sym)
{
int i;
JSHashBucket *b;
for (i = 0; i < JS_HASH_TABLE_SIZE; i++)
for (b = vm->globals_hash[i]; b; b = b->next)
if (b->u.ui == sym)
return b->name;
return "???";
}
void
js_vm_error (JSVirtualMachine *vm)
{
const char *file;
unsigned int ln;
char error[1024];
file = js_vm_debug_position (vm, &ln);
if (file)
{
sprintf (error, "%s:%u: %s", file, ln, vm->error);
strcpy (vm->error, error);
}
if (vm->stacktrace_on_error)
{
sprintf (error, "VM: error: %s%s", vm->error,
JS_HOST_LINE_BREAK);
js_iostream_write (vm->s_stderr, error, strlen (error));
js_vm_stacktrace (vm, (unsigned int) -1);
}
if (vm->error_handler->sp)
/*
* We are jumping to a catch-block. Format our error message to
* the `thrown' node.
*/
js_vm_make_string (vm, &vm->error_handler->thrown,
vm->error, strlen (vm->error));
longjmp (vm->error_handler->error_jmp, 1);
/* NOTREACHED (I hope). */
sprintf (error, "VM: no valid error handler initialized%s",
JS_HOST_LINE_BREAK);
js_iostream_write (vm->s_stderr, error, strlen (error));
js_iostream_flush (vm->s_stderr);
abort ();
}
/* Delete proc for garbaged built-in objects. */
static void
destroy_builtin (void *ptr)
{
JSBuiltin *bi = ptr;
if (bi->info->delete_proc)
(*bi->info->delete_proc) (bi->info, bi->instance_context);
}
/* Delete proc for garbaged built-in info. */
static void
destroy_builtin_info (void *ptr)
{
JSBuiltinInfo *i = ptr;
if (i->obj_context_delete)
(*i->obj_context_delete) (i->obj_context);
}
JSBuiltinInfo *
js_vm_builtin_info_create (JSVirtualMachine *vm)
{
JSNode prototype;
JSBuiltinInfo *i = js_vm_alloc_destroyable (vm, sizeof (*i));
i->destroy = destroy_builtin_info;
i->prototype = js_vm_object_new (vm);
/*
* Set the __proto__ property to null. We have no prototype object
* above us.
*/
prototype.type = JS_NULL;
js_vm_object_store_property (vm, i->prototype, vm->syms.s___proto__,
&prototype);
return i;
}
void
js_vm_builtin_create (JSVirtualMachine *vm, JSNode *result,
JSBuiltinInfo *info, void *instance_context)
{
result->type = JS_BUILTIN;
result->u.vbuiltin = js_vm_alloc_destroyable (vm, sizeof (JSBuiltin));
result->u.vbuiltin->destroy = destroy_builtin;
result->u.vbuiltin->info = info;
if (instance_context)
{
JSNode prototype;
result->u.vbuiltin->instance_context = instance_context;
result->u.vbuiltin->prototype = js_vm_object_new (vm);
/* Set the __proto__ chain. */
prototype.type = JS_OBJECT;
prototype.u.vobject = info->prototype;
js_vm_object_store_property (vm, result->u.vbuiltin->prototype,
vm->syms.s___proto__, &prototype);
}
}
/*
* Static functions.
*/
extern void js_builtin_core (JSVirtualMachine *vm);
extern void js_builtin_Array (JSVirtualMachine *vm);
extern void js_builtin_Boolean (JSVirtualMachine *vm);
extern void js_builtin_Function (JSVirtualMachine *vm);
extern void js_builtin_Number (JSVirtualMachine *vm);
extern void js_builtin_Object (JSVirtualMachine *vm);
extern void js_builtin_String (JSVirtualMachine *vm);
extern void js_builtin_Date (JSVirtualMachine *vm);
extern void js_builtin_Directory (JSVirtualMachine *vm);
extern void js_builtin_File (JSVirtualMachine *vm);
extern void js_builtin_Math (JSVirtualMachine *vm);
extern void js_builtin_RegExp (JSVirtualMachine *vm);
extern void js_builtin_System (JSVirtualMachine *vm);
extern void js_builtin_VM (JSVirtualMachine *vm);
static void
intern_builtins (JSVirtualMachine *vm)
{
/*
* The initialization order is significant. The RegExp object must be
* initialized before String.
*/
/* The core global methods. */
js_builtin_core (vm);
/* Our builtin extensions. */
js_builtin_Date (vm);
js_builtin_Directory (vm);
js_builtin_File (vm);
js_builtin_Math (vm);
js_builtin_RegExp (vm);
js_builtin_System (vm);
js_builtin_VM (vm);
/* Language objects. */
js_builtin_Array (vm);
js_builtin_Boolean (vm);
js_builtin_Function (vm);
js_builtin_Number (vm);
js_builtin_Object (vm);
js_builtin_String (vm);
}
#endif /*CL_EXPERIMENTAL*/