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.
2164 lines
45 KiB
2164 lines
45 KiB
/* operand halt (0) */
|
|
case 0:
|
|
sprintf (buf, "VM: halt%s", JS_HOST_LINE_BREAK);
|
|
|
|
js_iostream_write (vm->s_stderr, buf, strlen (buf));
|
|
js_iostream_flush (vm->s_stderr);
|
|
|
|
while (1)
|
|
sleep (5);
|
|
break;
|
|
|
|
/* operand done (1) */
|
|
case 1:
|
|
DONE ();
|
|
break;
|
|
|
|
/* operand nop (2) */
|
|
case 2:
|
|
/* Nothing here! */
|
|
break;
|
|
|
|
/* operand dup (3) */
|
|
case 3:
|
|
JS_COPY (JS_SP0, JS_SP1);
|
|
JS_PUSH ();
|
|
break;
|
|
|
|
/* operand pop (4) */
|
|
case 4:
|
|
JS_POP ();
|
|
break;
|
|
|
|
/* operand pop_n (5) */
|
|
case 5:
|
|
READ_INT8 (i);
|
|
JS_POP_N (i);
|
|
break;
|
|
|
|
/* operand apop (6) */
|
|
case 6:
|
|
READ_INT8 (i);
|
|
JS_COPY (JS_SP (i + 1), JS_SP1);
|
|
JS_POP_N (i);
|
|
break;
|
|
|
|
/* operand swap (7) */
|
|
case 7:
|
|
JS_COPY (JS_SP0, JS_SP2);
|
|
JS_COPY (JS_SP2, JS_SP1);
|
|
JS_COPY (JS_SP1, JS_SP0);
|
|
break;
|
|
|
|
/* operand roll (8) */
|
|
case 8:
|
|
READ_INT8 (i8);
|
|
|
|
if (i8 > 1)
|
|
{
|
|
int j;
|
|
|
|
for (j = 0; j < i8; j++)
|
|
JS_COPY (JS_SP (j), JS_SP (j + 1));
|
|
|
|
JS_COPY (JS_SP (i8), JS_SP0);
|
|
}
|
|
else if (i8 < -1)
|
|
{
|
|
i8 = -i8;
|
|
|
|
JS_COPY (JS_SP0, JS_SP (i8));
|
|
for (; i8 > 0; i8--)
|
|
JS_COPY (JS_SP (i8), JS_SP (i8 - 1));
|
|
}
|
|
break;
|
|
|
|
/* operand const (9) */
|
|
case 9:
|
|
READ_INT32 (i);
|
|
JS_COPY (JS_SP0, JS_CONST (i));
|
|
JS_PUSH ();
|
|
break;
|
|
|
|
/* operand const_null (10) */
|
|
case 10:
|
|
JS_SP0->type = JS_NULL;
|
|
JS_PUSH ();
|
|
break;
|
|
|
|
/* operand const_true (11) */
|
|
case 11:
|
|
JS_SP0->type = JS_BOOLEAN;
|
|
JS_SP0->u.vboolean = 1;
|
|
JS_PUSH ();
|
|
break;
|
|
|
|
/* operand const_false (12) */
|
|
case 12:
|
|
JS_SP0->type = JS_BOOLEAN;
|
|
JS_SP0->u.vboolean = 0;
|
|
JS_PUSH ();
|
|
break;
|
|
|
|
/* operand const_undefined (13) */
|
|
case 13:
|
|
JS_SP0->type = JS_UNDEFINED;
|
|
JS_PUSH ();
|
|
break;
|
|
|
|
/* operand const_i0 (14) */
|
|
case 14:
|
|
JS_SP0->type = JS_INTEGER;
|
|
JS_SP0->u.vinteger = 0;
|
|
JS_PUSH ();
|
|
break;
|
|
|
|
/* operand const_i1 (15) */
|
|
case 15:
|
|
JS_SP0->type = JS_INTEGER;
|
|
JS_SP0->u.vinteger = 1;
|
|
JS_PUSH ();
|
|
break;
|
|
|
|
/* operand const_i2 (16) */
|
|
case 16:
|
|
JS_SP0->type = JS_INTEGER;
|
|
JS_SP0->u.vinteger = 2;
|
|
JS_PUSH ();
|
|
break;
|
|
|
|
/* operand const_i3 (17) */
|
|
case 17:
|
|
JS_SP0->type = JS_INTEGER;
|
|
JS_SP0->u.vinteger = 3;
|
|
JS_PUSH ();
|
|
break;
|
|
|
|
/* operand const_i (18) */
|
|
case 18:
|
|
READ_INT32 (i);
|
|
JS_SP0->type = JS_INTEGER;
|
|
JS_SP0->u.vinteger = i;
|
|
JS_PUSH ();
|
|
break;
|
|
|
|
/* operand load_global (19) */
|
|
case 19:
|
|
READ_INT32 (j);
|
|
|
|
/* Use the global value only. */
|
|
JS_COPY (JS_SP0, JS_GLOBAL (j));
|
|
JS_PUSH ();
|
|
|
|
if (vm->warn_undef && JS_SP1->type == JS_UNDEFINED)
|
|
{
|
|
sprintf (buf, "VM: warning: using undefined global `%s'%s",
|
|
js_vm_symname (vm, j), JS_HOST_LINE_BREAK);
|
|
js_iostream_write (vm->s_stderr, buf, strlen (buf));
|
|
}
|
|
break;
|
|
|
|
/* operand store_global (20) */
|
|
case 20:
|
|
READ_INT32 (i);
|
|
|
|
/* Operand store_global do not check the with-chain. */
|
|
/* WITHCHAIN */
|
|
|
|
/* Set the global value. */
|
|
JS_COPY (JS_GLOBAL (i), JS_SP1);
|
|
JS_POP ();
|
|
break;
|
|
|
|
/* operand load_arg (21) */
|
|
case 21:
|
|
READ_INT8 (i);
|
|
JS_COPY (JS_SP0, JS_ARG (i));
|
|
JS_PUSH ();
|
|
break;
|
|
|
|
/* operand store_arg (22) */
|
|
case 22:
|
|
READ_INT8 (i);
|
|
JS_COPY (JS_ARG (i), JS_SP1);
|
|
JS_POP ();
|
|
break;
|
|
|
|
/* operand load_local (23) */
|
|
case 23:
|
|
READ_INT16 (i);
|
|
JS_COPY (JS_SP0, JS_LOCAL (i));
|
|
JS_PUSH ();
|
|
break;
|
|
|
|
/* operand store_local (24) */
|
|
case 24:
|
|
READ_INT16 (i);
|
|
JS_COPY (JS_LOCAL (i), JS_SP1);
|
|
JS_POP ();
|
|
break;
|
|
|
|
/* operand load_property (25) */
|
|
case 25:
|
|
/* Fetch the property symbol. */
|
|
READ_INT32 (j);
|
|
|
|
if (JS_SP1->type == JS_BUILTIN)
|
|
{
|
|
JS_SAVE_REGS ();
|
|
if (JS_SP1->u.vbuiltin->info->property_proc)
|
|
{
|
|
if ((*JS_SP1->u.vbuiltin->info->property_proc) (
|
|
vm,
|
|
JS_SP1->u.vbuiltin->info,
|
|
JS_SP1->u.vbuiltin->instance_context,
|
|
j, 0, &builtin_result)
|
|
== JS_PROPERTY_UNKNOWN)
|
|
{
|
|
if (j == vm->syms.s_prototype)
|
|
{
|
|
/* Looking up the prototype. */
|
|
|
|
builtin_result.type = JS_OBJECT;
|
|
if (JS_SP1->u.vbuiltin->prototype)
|
|
/* This is an instance. */
|
|
builtin_result.u.vobject = JS_SP1->u.vbuiltin->prototype;
|
|
else
|
|
/* This is a class. */
|
|
builtin_result.u.vobject
|
|
= JS_SP1->u.vbuiltin->info->prototype;
|
|
}
|
|
else
|
|
{
|
|
/* Looking up stuffs from the prototype. */
|
|
|
|
if (JS_SP1->u.vbuiltin->prototype)
|
|
/* An instance. */
|
|
js_vm_object_load_property (vm,
|
|
JS_SP1->u.vbuiltin->prototype,
|
|
j, &builtin_result);
|
|
else
|
|
/* A class. */
|
|
js_vm_object_load_property (
|
|
vm,
|
|
JS_SP1->u.vbuiltin->info->prototype,
|
|
j, &builtin_result);
|
|
}
|
|
}
|
|
JS_COPY (JS_SP1, &builtin_result);
|
|
}
|
|
else
|
|
ERROR ("illegal builtin object for load_property");
|
|
}
|
|
else if (JS_SP1->type == JS_OBJECT)
|
|
{
|
|
js_vm_object_load_property (vm, JS_SP1->u.vobject, j, JS_SP1);
|
|
}
|
|
else if (vm->prim[JS_SP1->type])
|
|
{
|
|
/* The primitive language types. */
|
|
JS_SAVE_REGS ();
|
|
if ((*vm->prim[JS_SP1->type]->property_proc) (vm, vm->prim[JS_SP1->type],
|
|
JS_SP1, j, 0,
|
|
&builtin_result)
|
|
== JS_PROPERTY_UNKNOWN)
|
|
{
|
|
if (j == vm->syms.s_prototype)
|
|
{
|
|
/* Looking up the prototype. */
|
|
switch (JS_SP1->type)
|
|
{
|
|
case JS_STRING:
|
|
if (JS_SP1->u.vstring->prototype)
|
|
{
|
|
builtin_result.type = JS_OBJECT;
|
|
builtin_result.u.vobject = JS_SP1->u.vstring->prototype;
|
|
}
|
|
else
|
|
/* No prototype yet. */
|
|
builtin_result.type = JS_NULL;
|
|
break;
|
|
|
|
case JS_ARRAY:
|
|
if (JS_SP1->u.varray->prototype)
|
|
{
|
|
builtin_result.type = JS_OBJECT;
|
|
builtin_result.u.vobject = JS_SP1->u.varray->prototype;
|
|
}
|
|
else
|
|
/* No prototype yet. */
|
|
builtin_result.type = JS_NULL;
|
|
break;
|
|
|
|
case JS_FUNC:
|
|
if (JS_SP1->u.vfunction->prototype)
|
|
{
|
|
builtin_result.type = JS_OBJECT;
|
|
builtin_result.u.vobject
|
|
= JS_SP1->u.vfunction->prototype;
|
|
}
|
|
else
|
|
/* No prototype yet. */
|
|
builtin_result.type = JS_NULL;
|
|
break;
|
|
|
|
default:
|
|
/* The rest do not have prototype. */
|
|
builtin_result.type = JS_NULL;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Looking up stuffs from the prototype. */
|
|
switch (JS_SP1->type)
|
|
{
|
|
case JS_STRING:
|
|
if (JS_SP1->u.vstring->prototype)
|
|
js_vm_object_load_property (vm,
|
|
JS_SP1->u.vstring->prototype,
|
|
j, &builtin_result);
|
|
else
|
|
/* Take it from the class' prototype */
|
|
goto _op_load_property_try_proto;
|
|
break;
|
|
|
|
case JS_ARRAY:
|
|
if (JS_SP1->u.varray->prototype)
|
|
js_vm_object_load_property (vm,
|
|
JS_SP1->u.varray->prototype,
|
|
j, &builtin_result);
|
|
else
|
|
/* Take it from the class' prototype */
|
|
goto _op_load_property_try_proto;
|
|
break;
|
|
|
|
case JS_FUNC:
|
|
if (JS_SP1->u.vfunction->prototype)
|
|
js_vm_object_load_property (vm,
|
|
JS_SP1->u.vfunction->prototype,
|
|
j, &builtin_result);
|
|
else
|
|
/* Take it from the class' prototype */
|
|
goto _op_load_property_try_proto;
|
|
break;
|
|
|
|
default:
|
|
/*
|
|
* The rest do not have instance prototypes; use the
|
|
* class prototypes.
|
|
*/
|
|
_op_load_property_try_proto:
|
|
js_vm_object_load_property (
|
|
vm,
|
|
vm->prim[JS_SP1->type]->prototype, j,
|
|
&builtin_result);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
JS_COPY (JS_SP1, &builtin_result);
|
|
}
|
|
else
|
|
ERROR ("illegal object for load_property");
|
|
break;
|
|
|
|
/* operand store_property (26) */
|
|
case 26:
|
|
/* Fetch the property symbol. */
|
|
READ_INT32 (j);
|
|
|
|
if (JS_SP1->type == JS_BUILTIN)
|
|
{
|
|
JS_SAVE_REGS ();
|
|
if (JS_SP1->u.vbuiltin->info->property_proc)
|
|
{
|
|
if ((*JS_SP1->u.vbuiltin->info->property_proc) (
|
|
vm,
|
|
JS_SP1->u.vbuiltin->info,
|
|
JS_SP1->u.vbuiltin->instance_context,
|
|
j, 1, JS_SP2)
|
|
== JS_PROPERTY_UNKNOWN)
|
|
{
|
|
if (j == vm->syms.s_prototype)
|
|
{
|
|
/* Setting the prototype. */
|
|
if (JS_SP2->type != JS_OBJECT)
|
|
ERROR ("illegal value for set_property");
|
|
|
|
if (JS_SP1->u.vbuiltin->prototype)
|
|
/* Setting the instance's prototype. */
|
|
JS_SP1->u.vbuiltin->prototype = JS_SP2->u.vobject;
|
|
else
|
|
/* Setting the class' prototype. */
|
|
JS_SP1->u.vbuiltin->info->prototype = JS_SP2->u.vobject;
|
|
}
|
|
else
|
|
{
|
|
/* Setting stuff to the prototype. */
|
|
if (JS_SP1->u.vbuiltin->prototype)
|
|
/* An instance. */
|
|
js_vm_object_store_property (vm,
|
|
JS_SP1->u.vbuiltin->prototype,
|
|
j, JS_SP2);
|
|
else
|
|
/* A class. */
|
|
js_vm_object_store_property (
|
|
vm,
|
|
JS_SP1->u.vbuiltin->info->prototype,
|
|
j, JS_SP2);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
ERROR ("illegal builtin object for store_property");
|
|
|
|
JS_POP ();
|
|
JS_POP ();
|
|
}
|
|
else if (JS_SP1->type == JS_OBJECT)
|
|
{
|
|
js_vm_object_store_property (vm, JS_SP1->u.vobject, j, JS_SP2);
|
|
JS_POP ();
|
|
JS_POP ();
|
|
}
|
|
else if (vm->prim[JS_SP1->type])
|
|
{
|
|
/* The primitive language types. */
|
|
JS_SAVE_REGS ();
|
|
if ((*vm->prim[JS_SP1->type]->property_proc) (vm, vm->prim[JS_SP1->type],
|
|
JS_SP1, j, 1, JS_SP2)
|
|
== JS_PROPERTY_UNKNOWN)
|
|
{
|
|
if (j == vm->syms.s_prototype)
|
|
{
|
|
/* Setting the prototype. */
|
|
if (JS_SP2->type != JS_OBJECT)
|
|
ERROR ("illegal value for set_property");
|
|
|
|
switch (JS_SP1->type)
|
|
{
|
|
case JS_STRING:
|
|
JS_SP1->u.vstring->prototype = JS_SP2->u.vobject;
|
|
break;
|
|
|
|
case JS_ARRAY:
|
|
JS_SP1->u.varray->prototype = JS_SP2->u.vobject;
|
|
break;
|
|
|
|
case JS_FUNC:
|
|
JS_SP1->u.vfunction->prototype = JS_SP2->u.vobject;
|
|
break;
|
|
|
|
default:
|
|
ERROR ("illegal object for set_property");
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
JSNode prototype;
|
|
|
|
/* Setting to the prototype. We create them on demand. */
|
|
switch (JS_SP1->type)
|
|
{
|
|
case JS_STRING:
|
|
if (JS_SP1->u.vstring->prototype == NULL)
|
|
{
|
|
prototype.type = JS_OBJECT;
|
|
|
|
/* Create the prototype and set its __proto__. */
|
|
JS_SP1->u.vstring->prototype = js_vm_object_new (vm);
|
|
prototype.u.vobject = vm->prim[JS_OBJECT]->prototype;
|
|
js_vm_object_store_property (
|
|
vm,
|
|
JS_SP1->u.vstring->prototype,
|
|
vm->syms.s___proto__,
|
|
&prototype);
|
|
}
|
|
js_vm_object_store_property (vm,
|
|
JS_SP1->u.vstring->prototype,
|
|
j, JS_SP2);
|
|
break;
|
|
|
|
case JS_ARRAY:
|
|
if (JS_SP1->u.varray->prototype == NULL)
|
|
{
|
|
prototype.type = JS_OBJECT;
|
|
|
|
/* Create the prototype and set its __proto__. */
|
|
JS_SP1->u.varray->prototype = js_vm_object_new (vm);
|
|
prototype.u.vobject = vm->prim[JS_OBJECT]->prototype;
|
|
js_vm_object_store_property (
|
|
vm,
|
|
JS_SP1->u.varray->prototype,
|
|
vm->syms.s___proto__,
|
|
&prototype);
|
|
}
|
|
js_vm_object_store_property (vm,
|
|
JS_SP1->u.varray->prototype,
|
|
j, JS_SP2);
|
|
break;
|
|
|
|
case JS_FUNC:
|
|
if (JS_SP1->u.vfunction->prototype == NULL)
|
|
{
|
|
prototype.type = JS_OBJECT;
|
|
|
|
/* Create the prototype and set its __proto__. */
|
|
JS_SP1->u.vfunction->prototype = js_vm_object_new (vm);
|
|
prototype.u.vobject = vm->prim[JS_OBJECT]->prototype;
|
|
js_vm_object_store_property (
|
|
vm,
|
|
JS_SP1->u.vfunction->prototype,
|
|
vm->syms.s___proto__,
|
|
&prototype);
|
|
}
|
|
js_vm_object_store_property (vm,
|
|
JS_SP1->u.vfunction->prototype,
|
|
j, JS_SP2);
|
|
break;
|
|
|
|
default:
|
|
ERROR ("illegal object for set_property");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
JS_POP ();
|
|
JS_POP ();
|
|
}
|
|
else
|
|
ERROR ("illegal object for store_property");
|
|
|
|
JS_MAYBE_GC ();
|
|
break;
|
|
|
|
/* operand load_array (27) */
|
|
case 27:
|
|
if (JS_SP2->type == JS_BUILTIN)
|
|
{
|
|
if (JS_SP1->type == JS_INTEGER)
|
|
{
|
|
ERROR ("integer indexes not implemented yet for BUILTIN in load_array");
|
|
}
|
|
else if (JS_SP1->type == JS_STRING)
|
|
{
|
|
/* Intern the string. */
|
|
j = js_vm_intern_with_len (vm, JS_SP1->u.vstring->data,
|
|
JS_SP1->u.vstring->len);
|
|
|
|
/* The code below must be in sync with operand `load_property'. */
|
|
JS_SAVE_REGS ();
|
|
|
|
if (JS_SP2->u.vbuiltin->info->property_proc)
|
|
{
|
|
if ((*JS_SP2->u.vbuiltin->info->property_proc) (
|
|
vm,
|
|
JS_SP2->u.vbuiltin->info,
|
|
JS_SP2->u.vbuiltin->instance_context,
|
|
j, 0, &builtin_result)
|
|
== JS_PROPERTY_UNKNOWN)
|
|
{
|
|
if (j == vm->syms.s_prototype)
|
|
{
|
|
/* Looking up the prototype. */
|
|
|
|
builtin_result.type = JS_OBJECT;
|
|
if (JS_SP2->u.vbuiltin->prototype)
|
|
/* This is an instance. */
|
|
builtin_result.u.vobject
|
|
= JS_SP2->u.vbuiltin->prototype;
|
|
else
|
|
/* This is a class. */
|
|
builtin_result.u.vobject
|
|
= JS_SP2->u.vbuiltin->info->prototype;
|
|
}
|
|
else
|
|
{
|
|
/* Looking up stuffs from the prototype. */
|
|
|
|
if (JS_SP2->u.vbuiltin->prototype)
|
|
/* An instance. */
|
|
js_vm_object_load_property (
|
|
vm,
|
|
JS_SP2->u.vbuiltin->prototype,
|
|
j, &builtin_result);
|
|
else
|
|
/* A class. */
|
|
js_vm_object_load_property (
|
|
vm,
|
|
JS_SP2->u.vbuiltin->info->prototype,
|
|
j, &builtin_result);
|
|
}
|
|
}
|
|
JS_COPY (JS_SP2, &builtin_result);
|
|
JS_POP ();
|
|
}
|
|
else
|
|
ERROR ("illegal builtin object for load_array");
|
|
}
|
|
else
|
|
{
|
|
sprintf (buf, "illegal array index in load_array (%d)",
|
|
JS_SP1->type);
|
|
ERROR (buf);
|
|
}
|
|
}
|
|
else if (JS_SP2->type == JS_OBJECT)
|
|
{
|
|
js_vm_object_load_array (vm, JS_SP2->u.vobject, JS_SP1, JS_SP2);
|
|
JS_POP ();
|
|
}
|
|
else if (JS_SP2->type == JS_ARRAY)
|
|
{
|
|
if (JS_SP1->type == JS_INTEGER)
|
|
{
|
|
if (JS_SP1->u.vinteger < 0
|
|
|| JS_SP1->u.vinteger >= JS_SP2->u.varray->length)
|
|
JS_SP2->type = JS_UNDEFINED;
|
|
else
|
|
{
|
|
JSNode *n = &JS_SP2->u.varray->data[JS_SP1->u.vinteger];
|
|
JS_COPY (JS_SP2, n);
|
|
}
|
|
JS_POP ();
|
|
}
|
|
else
|
|
{
|
|
sprintf (buf, "illegal array index in load_array (%d)",
|
|
JS_SP1->type);
|
|
ERROR (buf);
|
|
}
|
|
}
|
|
else if (JS_SP2->type == JS_STRING)
|
|
{
|
|
if (JS_SP1->type == JS_INTEGER)
|
|
{
|
|
int ch;
|
|
|
|
if (JS_SP1->u.vinteger < 0
|
|
|| JS_SP1->u.vinteger >= JS_SP2->u.vstring->len)
|
|
ERROR ("string index out of range in load_array");
|
|
|
|
ch = JS_SP2->u.vstring->data[JS_SP1->u.vinteger];
|
|
JS_SP2->type = JS_INTEGER;
|
|
JS_SP2->u.vinteger = ch;
|
|
|
|
JS_POP ();
|
|
}
|
|
else
|
|
ERROR ("illegal string index in load_array");
|
|
}
|
|
else
|
|
ERROR ("illegal object for load_array");
|
|
break;
|
|
|
|
/* operand store_array (28) */
|
|
case 28:
|
|
if (JS_SP2->type == JS_BUILTIN)
|
|
{
|
|
if (JS_SP1->type == JS_INTEGER)
|
|
{
|
|
ERROR ("integer index not implemented yet for BUILTIN in store_array");
|
|
}
|
|
else if (JS_SP1->type == JS_STRING)
|
|
{
|
|
/* Intern the string. */
|
|
j = js_vm_intern_with_len (vm, JS_SP1->u.vstring->data,
|
|
JS_SP1->u.vstring->len);
|
|
|
|
/* The code below msut be in sync with operand `store_property'. */
|
|
JS_SAVE_REGS ();
|
|
if (JS_SP2->u.vbuiltin->info->property_proc)
|
|
{
|
|
if ((*JS_SP2->u.vbuiltin->info->property_proc) (
|
|
vm,
|
|
JS_SP2->u.vbuiltin->info,
|
|
JS_SP2->u.vbuiltin->instance_context,
|
|
j, 1, JS_SP (3))
|
|
== JS_PROPERTY_UNKNOWN)
|
|
{
|
|
if (j == vm->syms.s_prototype)
|
|
{
|
|
/* Setting the prototype. */
|
|
if (JS_SP (3)->type != JS_OBJECT)
|
|
ERROR ("illegal value for prototype");
|
|
|
|
if (JS_SP2->u.vbuiltin->prototype)
|
|
/* Setting the instance's prototype. */
|
|
JS_SP2->u.vbuiltin->prototype = JS_SP (3)->u.vobject;
|
|
else
|
|
/* Setting the class' prototype. */
|
|
JS_SP2->u.vbuiltin->info->prototype
|
|
= JS_SP (3)->u.vobject;
|
|
}
|
|
else
|
|
{
|
|
/* Setting stuff to the prototype. */
|
|
if (JS_SP2->u.vbuiltin->prototype)
|
|
/* An instance. */
|
|
js_vm_object_store_property (
|
|
vm,
|
|
JS_SP2->u.vbuiltin->prototype,
|
|
j, JS_SP (3));
|
|
else
|
|
/* A class. */
|
|
js_vm_object_store_property (
|
|
vm,
|
|
JS_SP2->u.vbuiltin->info->prototype,
|
|
j, JS_SP (3));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
ERROR ("illegal builtin object for store_array");
|
|
|
|
JS_POP_N (3);
|
|
}
|
|
else
|
|
ERROR ("illegal array index in store_array");
|
|
}
|
|
else if (JS_SP2->type == JS_OBJECT)
|
|
{
|
|
js_vm_object_store_array (vm, JS_SP2->u.vobject, JS_SP1, JS_SP (3));
|
|
JS_POP_N (3);
|
|
}
|
|
else if (JS_SP2->type == JS_ARRAY)
|
|
{
|
|
if (JS_SP1->type == JS_INTEGER)
|
|
{
|
|
if (JS_SP1->u.vinteger < 0)
|
|
ERROR ("negative array index in store_array");
|
|
if (JS_SP1->u.vinteger >= JS_SP2->u.varray->length)
|
|
js_vm_expand_array (vm, JS_SP2, JS_SP1->u.vinteger + 1);
|
|
|
|
JS_COPY (&JS_SP2->u.varray->data[JS_SP1->u.vinteger], JS_SP (3));
|
|
JS_POP_N (3);
|
|
}
|
|
else
|
|
ERROR ("illegal array index in store_array");
|
|
}
|
|
else if (JS_SP2->type == JS_STRING)
|
|
{
|
|
if (JS_SP1->type == JS_INTEGER)
|
|
{
|
|
if (JS_SP2->u.vstring->staticp)
|
|
ERROR ("static string in store_array");
|
|
|
|
if (JS_SP1->u.vinteger < 0)
|
|
ERROR ("negative string index in store_array");
|
|
|
|
if (JS_SP (3)->type != JS_INTEGER)
|
|
ERROR ("non-integer value to store into string in store_array");
|
|
|
|
if (JS_SP1->u.vinteger >= JS_SP2->u.vstring->len)
|
|
{
|
|
/* Expand the string. */
|
|
JS_SP2->u.vstring->data = js_vm_realloc (vm,
|
|
JS_SP2->u.vstring->data,
|
|
JS_SP1->u.vinteger + 1);
|
|
/* Fill the gap with ' '. */
|
|
for (; JS_SP2->u.vstring->len <= JS_SP1->u.vinteger;)
|
|
JS_SP2->u.vstring->data[JS_SP2->u.vstring->len++] = ' ';
|
|
}
|
|
|
|
JS_SP2->u.vstring->data[JS_SP1->u.vinteger]
|
|
= (unsigned char) JS_SP (3)->u.vinteger;
|
|
JS_POP_N (3);
|
|
}
|
|
else
|
|
ERROR ("illegal string index in store_array");
|
|
}
|
|
else
|
|
ERROR ("illegal object for store_array");
|
|
|
|
JS_MAYBE_GC ();
|
|
break;
|
|
|
|
/* operand nth (29) */
|
|
case 29:
|
|
if (JS_SP2->type == JS_STRING)
|
|
{
|
|
if (JS_SP1->u.vinteger < 0
|
|
|| JS_SP1->u.vinteger >= JS_SP2->u.vstring->len)
|
|
{
|
|
JS_SP2->type = JS_UNDEFINED;
|
|
|
|
JS_SP1->type = JS_BOOLEAN;
|
|
JS_SP1->u.vboolean = 0;
|
|
}
|
|
else
|
|
{
|
|
JS_SP2->type = JS_INTEGER;
|
|
JS_SP2->u.vinteger = JS_SP2->u.vstring->data[JS_SP1->u.vinteger];
|
|
|
|
JS_SP1->type = JS_BOOLEAN;
|
|
JS_SP1->u.vboolean = 1;
|
|
}
|
|
}
|
|
else if (JS_SP2->type == JS_ARRAY)
|
|
{
|
|
if (JS_SP1->u.vinteger < 0
|
|
|| JS_SP1->u.vinteger >= JS_SP2->u.varray->length)
|
|
{
|
|
JS_SP2->type = JS_UNDEFINED;
|
|
|
|
JS_SP1->type = JS_BOOLEAN;
|
|
JS_SP1->u.vboolean = 0;
|
|
}
|
|
else
|
|
{
|
|
JSNode *n = &JS_SP2->u.varray->data[JS_SP1->u.vinteger];
|
|
JS_COPY (JS_SP2, n);
|
|
|
|
JS_SP1->type = JS_BOOLEAN;
|
|
JS_SP1->u.vboolean = 1;
|
|
}
|
|
}
|
|
else if (JS_SP2->type == JS_OBJECT)
|
|
{
|
|
i = js_vm_object_nth (vm, JS_SP2->u.vobject, JS_SP1->u.vinteger, JS_SP2);
|
|
JS_SP1->type = JS_BOOLEAN;
|
|
JS_SP1->u.vboolean = i;
|
|
}
|
|
else
|
|
ERROR ("illegal object for nth");
|
|
break;
|
|
|
|
/* operand cmp_eq (30) */
|
|
case 30:
|
|
JS_OPERAND_CMP_EQ (==, 1);
|
|
break;
|
|
|
|
/* operand cmp_ne (31) */
|
|
case 31:
|
|
JS_OPERAND_CMP_EQ (!=, 0);
|
|
break;
|
|
|
|
/* operand cmp_lt (32) */
|
|
case 32:
|
|
JS_OPERAND_CMP_REL (<);
|
|
break;
|
|
|
|
/* operand cmp_gt (33) */
|
|
case 33:
|
|
JS_OPERAND_CMP_REL (>);
|
|
break;
|
|
|
|
/* operand cmp_le (34) */
|
|
case 34:
|
|
JS_OPERAND_CMP_REL (<=);
|
|
break;
|
|
|
|
/* operand cmp_ge (35) */
|
|
case 35:
|
|
JS_OPERAND_CMP_REL (>=);
|
|
break;
|
|
|
|
/* operand cmp_seq (36) */
|
|
case 36:
|
|
JS_OPERAND_CMP_SEQ (==, 1);
|
|
break;
|
|
|
|
/* operand cmp_sne (37) */
|
|
case 37:
|
|
JS_OPERAND_CMP_SEQ (!=, 0);
|
|
break;
|
|
|
|
/* operand sub (38) */
|
|
case 38:
|
|
if (JS_SP2->type == JS_INTEGER && JS_SP1->type == JS_INTEGER)
|
|
{
|
|
JS_SP2->u.vinteger -= JS_SP1->u.vinteger;
|
|
}
|
|
else
|
|
{
|
|
JSNode l_cvt, r_cvt;
|
|
JSNode *l, *r;
|
|
|
|
if (JS_IS_NUMBER (JS_SP2))
|
|
l = JS_SP2;
|
|
else
|
|
{
|
|
js_vm_to_number (vm, JS_SP2, &l_cvt);
|
|
l = &l_cvt;
|
|
}
|
|
|
|
if (JS_IS_NUMBER (JS_SP1))
|
|
r = JS_SP1;
|
|
else
|
|
{
|
|
js_vm_to_number (vm, JS_SP1, &r_cvt);
|
|
r = &r_cvt;
|
|
}
|
|
|
|
if (l->type == JS_NAN || r->type == JS_NAN)
|
|
JS_SP2->type = JS_NAN;
|
|
else if (l->type == JS_INTEGER)
|
|
{
|
|
if (r->type == JS_INTEGER)
|
|
{
|
|
JS_SP2->type = JS_INTEGER;
|
|
JS_SP2->u.vinteger = l->u.vinteger - r->u.vinteger;
|
|
}
|
|
else
|
|
{
|
|
JS_SP2->type = JS_FLOAT;
|
|
JS_SP2->u.vfloat = (double) l->u.vinteger - r->u.vfloat;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (r->type == JS_INTEGER)
|
|
{
|
|
JS_SP2->type = JS_FLOAT;
|
|
JS_SP2->u.vfloat = l->u.vfloat - (double) r->u.vinteger;
|
|
}
|
|
else
|
|
{
|
|
JS_SP2->type = JS_FLOAT;
|
|
JS_SP2->u.vfloat = l->u.vfloat - r->u.vfloat;
|
|
}
|
|
}
|
|
}
|
|
|
|
JS_POP ();
|
|
break;
|
|
|
|
/* operand add (39) */
|
|
case 39:
|
|
if (JS_SP2->type == JS_STRING || JS_SP1->type == JS_STRING)
|
|
{
|
|
unsigned char *d2, *d1, *ndata;
|
|
unsigned int d2_len, d1_len, nlen;
|
|
JSNode cvt;
|
|
|
|
if (JS_SP2->type == JS_STRING)
|
|
{
|
|
d2 = JS_SP2->u.vstring->data;
|
|
d2_len = JS_SP2->u.vstring->len;
|
|
}
|
|
else
|
|
{
|
|
js_vm_to_string (vm, JS_SP2, &cvt);
|
|
d2 = cvt.u.vstring->data;
|
|
d2_len = cvt.u.vstring->len;
|
|
}
|
|
if (JS_SP1->type == JS_STRING)
|
|
{
|
|
d1 = JS_SP1->u.vstring->data;
|
|
d1_len = JS_SP1->u.vstring->len;
|
|
}
|
|
else
|
|
{
|
|
js_vm_to_string (vm, JS_SP1, &cvt);
|
|
d1 = cvt.u.vstring->data;
|
|
d1_len = cvt.u.vstring->len;
|
|
}
|
|
|
|
nlen = d2_len + d1_len;
|
|
ndata = js_vm_alloc (vm, nlen);
|
|
memcpy (ndata, d2, d2_len);
|
|
memcpy (ndata + d2_len, d1, d1_len);
|
|
|
|
js_vm_make_static_string (vm, JS_SP2, ndata, nlen);
|
|
JS_SP2->u.vstring->staticp = 0;
|
|
JS_POP ();
|
|
JS_MAYBE_GC ();
|
|
}
|
|
else if (JS_SP2->type == JS_INTEGER && JS_SP1->type == JS_INTEGER)
|
|
{
|
|
JS_SP2->u.vinteger += JS_SP1->u.vinteger;
|
|
JS_POP ();
|
|
}
|
|
else
|
|
{
|
|
JSNode l_cvt, r_cvt;
|
|
JSNode *l, *r;
|
|
|
|
if (JS_IS_NUMBER (JS_SP2))
|
|
l = JS_SP2;
|
|
else
|
|
{
|
|
js_vm_to_number (vm, JS_SP2, &l_cvt);
|
|
l = &l_cvt;
|
|
}
|
|
|
|
if (JS_IS_NUMBER (JS_SP1))
|
|
r = JS_SP1;
|
|
else
|
|
{
|
|
js_vm_to_number (vm, JS_SP1, &r_cvt);
|
|
r = &r_cvt;
|
|
}
|
|
|
|
if (l->type == JS_NAN || r->type == JS_NAN)
|
|
JS_SP2->type = JS_NAN;
|
|
else if (l->type == JS_INTEGER)
|
|
{
|
|
if (r->type == JS_INTEGER)
|
|
{
|
|
JS_SP2->type = JS_INTEGER;
|
|
JS_SP2->u.vinteger = l->u.vinteger + r->u.vinteger;
|
|
}
|
|
else
|
|
{
|
|
JS_SP2->type = JS_FLOAT;
|
|
JS_SP2->u.vfloat = (double) l->u.vinteger + r->u.vfloat;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (r->type == JS_INTEGER)
|
|
{
|
|
JS_SP2->type = JS_FLOAT;
|
|
JS_SP2->u.vfloat = l->u.vfloat + (double) r->u.vinteger;
|
|
}
|
|
else
|
|
{
|
|
JS_SP2->type = JS_FLOAT;
|
|
JS_SP2->u.vfloat = l->u.vfloat + r->u.vfloat;
|
|
}
|
|
}
|
|
|
|
JS_POP ();
|
|
}
|
|
break;
|
|
|
|
/* operand mul (40) */
|
|
case 40:
|
|
if (JS_SP2->type == JS_INTEGER && JS_SP1->type == JS_INTEGER)
|
|
{
|
|
JS_SP2->u.vinteger *= JS_SP1->u.vinteger;
|
|
}
|
|
else
|
|
{
|
|
JSNode l_cvt, r_cvt;
|
|
JSNode *l, *r;
|
|
|
|
if (JS_IS_NUMBER (JS_SP2))
|
|
l = JS_SP2;
|
|
else
|
|
{
|
|
js_vm_to_number (vm, JS_SP2, &l_cvt);
|
|
l = &l_cvt;
|
|
}
|
|
|
|
if (JS_IS_NUMBER (JS_SP1))
|
|
r = JS_SP1;
|
|
else
|
|
{
|
|
js_vm_to_number (vm, JS_SP1, &r_cvt);
|
|
r = &r_cvt;
|
|
}
|
|
|
|
if (l->type == JS_NAN || r->type == JS_NAN)
|
|
JS_SP2->type = JS_NAN;
|
|
else if (l->type == JS_INTEGER)
|
|
{
|
|
if (r->type == JS_INTEGER)
|
|
{
|
|
JS_SP2->type = JS_INTEGER;
|
|
JS_SP2->u.vinteger = l->u.vinteger * r->u.vinteger;
|
|
}
|
|
else
|
|
{
|
|
if (l->u.vinteger == 0
|
|
&& (JS_IS_POSITIVE_INFINITY (r)
|
|
|| JS_IS_NEGATIVE_INFINITY (r)))
|
|
JS_SP2->type = JS_NAN;
|
|
else
|
|
{
|
|
JS_SP2->type = JS_FLOAT;
|
|
JS_SP2->u.vfloat = (double) l->u.vinteger * r->u.vfloat;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((JS_IS_POSITIVE_INFINITY (l) || JS_IS_NEGATIVE_INFINITY (l))
|
|
&& ((r->type == JS_INTEGER && r->u.vinteger == 0)
|
|
|| (r->type == JS_FLOAT && r->u.vfloat == 0.0)))
|
|
JS_SP2->type = JS_NAN;
|
|
else
|
|
{
|
|
JS_SP2->type = JS_FLOAT;
|
|
|
|
if (r->type == JS_INTEGER)
|
|
JS_SP2->u.vfloat = l->u.vfloat * (double) r->u.vinteger;
|
|
else
|
|
JS_SP2->u.vfloat = l->u.vfloat * r->u.vfloat;
|
|
}
|
|
}
|
|
}
|
|
|
|
JS_POP ();
|
|
break;
|
|
|
|
/* operand div (41) */
|
|
case 41:
|
|
{
|
|
int nan = 0;
|
|
double l, r;
|
|
int l_inf = 0;
|
|
int r_inf = 0;
|
|
JSNode *n;
|
|
JSNode cvt;
|
|
|
|
/* Convert divident to float. */
|
|
if (JS_IS_NUMBER (JS_SP2))
|
|
n = JS_SP2;
|
|
else
|
|
{
|
|
js_vm_to_number (vm, JS_SP2, &cvt);
|
|
n = &cvt;
|
|
}
|
|
|
|
switch (n->type)
|
|
{
|
|
case JS_INTEGER:
|
|
l = (double) n->u.vinteger;
|
|
break;
|
|
|
|
case JS_FLOAT:
|
|
l = n->u.vfloat;
|
|
if (JS_IS_POSITIVE_INFINITY (n) || JS_IS_NEGATIVE_INFINITY (n))
|
|
l_inf = 1;
|
|
break;
|
|
|
|
case JS_NAN:
|
|
default:
|
|
nan = 1;
|
|
break;
|
|
}
|
|
|
|
/* Convert divisor to float. */
|
|
if (JS_IS_NUMBER (JS_SP1))
|
|
n = JS_SP1;
|
|
else
|
|
{
|
|
js_vm_to_number (vm, JS_SP2, &cvt);
|
|
n = &cvt;
|
|
}
|
|
|
|
switch (n->type)
|
|
{
|
|
case JS_INTEGER:
|
|
r = (double) n->u.vinteger;
|
|
break;
|
|
|
|
case JS_FLOAT:
|
|
r = n->u.vfloat;
|
|
if (JS_IS_POSITIVE_INFINITY (n) || JS_IS_NEGATIVE_INFINITY (n))
|
|
r_inf = 1;
|
|
break;
|
|
|
|
case JS_NAN:
|
|
default:
|
|
nan = 1;
|
|
break;
|
|
}
|
|
|
|
/* Do the division. */
|
|
JS_POP ();
|
|
if (nan || (l_inf && r_inf))
|
|
JS_SP1->type = JS_NAN;
|
|
else
|
|
{
|
|
if (l_inf && r == 0.0)
|
|
{
|
|
/* <l> is already an infinity. */
|
|
JS_SP1->type = JS_FLOAT;
|
|
JS_SP1->u.vfloat = l;
|
|
}
|
|
else if (l == 0.0 && r == 0.0)
|
|
JS_SP1->type = JS_NAN;
|
|
else
|
|
{
|
|
JS_SP1->type = JS_FLOAT;
|
|
JS_SP1->u.vfloat = l / r;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
/* operand mod (42) */
|
|
case 42:
|
|
if (JS_SP2->type == JS_INTEGER && JS_SP1->type == JS_INTEGER)
|
|
{
|
|
if (JS_SP1->u.vinteger == 0)
|
|
JS_SP2->type = JS_NAN;
|
|
else
|
|
JS_SP2->u.vinteger %= JS_SP1->u.vinteger;
|
|
}
|
|
else
|
|
{
|
|
JSNode l_cvt, r_cvt;
|
|
JSNode *l, *r;
|
|
|
|
if (JS_IS_NUMBER (JS_SP2))
|
|
l = JS_SP2;
|
|
else
|
|
{
|
|
js_vm_to_number (vm, JS_SP2, &l_cvt);
|
|
l = &l_cvt;
|
|
}
|
|
|
|
if (JS_IS_NUMBER (JS_SP1))
|
|
r = JS_SP1;
|
|
else
|
|
{
|
|
js_vm_to_number (vm, JS_SP1, &r_cvt);
|
|
r = &r_cvt;
|
|
}
|
|
|
|
if (l->type == JS_NAN || r->type == JS_NAN)
|
|
JS_SP2->type = JS_NAN;
|
|
else if (JS_IS_POSITIVE_INFINITY (l)
|
|
|| JS_IS_NEGATIVE_INFINITY (l)
|
|
|| ((r->type == JS_INTEGER && r->u.vinteger == 0)
|
|
|| (r->type == JS_FLOAT && r->u.vfloat == 0.0)))
|
|
JS_SP2->type = JS_NAN;
|
|
else if (JS_IS_POSITIVE_INFINITY (r)
|
|
|| JS_IS_NEGATIVE_INFINITY (r))
|
|
JS_COPY (JS_SP2, l);
|
|
else if ((l->type == JS_INTEGER && l->u.vinteger == 0)
|
|
|| (l->type == JS_FLOAT && l->u.vfloat == 0.0))
|
|
JS_COPY (JS_SP2, l);
|
|
else
|
|
{
|
|
if (l->type == JS_INTEGER && r->type == JS_INTEGER)
|
|
{
|
|
JS_SP2->type = JS_INTEGER;
|
|
JS_SP2->u.vinteger = l->u.vinteger % r->u.vinteger;
|
|
}
|
|
else
|
|
{
|
|
double ld, rd;
|
|
int full;
|
|
|
|
if (l->type == JS_INTEGER)
|
|
ld = (double) l->u.vinteger;
|
|
else
|
|
ld = l->u.vfloat;
|
|
|
|
if (r->type == JS_INTEGER)
|
|
rd = (double) r->u.vinteger;
|
|
else
|
|
rd = r->u.vfloat;
|
|
|
|
full = ld / rd;
|
|
|
|
JS_SP2->type = JS_FLOAT;
|
|
JS_SP2->u.vfloat = ld - (full * rd);
|
|
}
|
|
}
|
|
}
|
|
|
|
JS_POP ();
|
|
break;
|
|
|
|
/* operand neg (43) */
|
|
case 43:
|
|
if (JS_SP1->type == JS_INTEGER)
|
|
JS_SP1->u.vinteger = -JS_SP1->u.vinteger;
|
|
else if (JS_SP1->type == JS_FLOAT)
|
|
JS_SP1->u.vfloat = -JS_SP1->u.vfloat;
|
|
else if (JS_SP1->type == JS_NAN)
|
|
;
|
|
else
|
|
{
|
|
JSNode cvt;
|
|
|
|
js_vm_to_number (vm, JS_SP1, &cvt);
|
|
|
|
JS_SP1->type = cvt.type;
|
|
switch (cvt.type)
|
|
{
|
|
case JS_INTEGER:
|
|
JS_SP1->u.vinteger = -cvt.u.vinteger;
|
|
break;
|
|
|
|
case JS_FLOAT:
|
|
JS_SP1->u.vfloat = -cvt.u.vfloat;
|
|
break;
|
|
|
|
case JS_NAN:
|
|
default:
|
|
/* Nothing here. */
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
/* operand and (44) */
|
|
case 44:
|
|
JS_OPERAND_BINARY (&);
|
|
break;
|
|
|
|
/* operand not (45) */
|
|
case 45:
|
|
JS_SP1->u.vboolean = JS_IS_FALSE (JS_SP1);
|
|
JS_SP1->type = JS_BOOLEAN;
|
|
break;
|
|
|
|
/* operand or (46) */
|
|
case 46:
|
|
JS_OPERAND_BINARY (|);
|
|
break;
|
|
|
|
/* operand xor (47) */
|
|
case 47:
|
|
JS_OPERAND_BINARY (^);
|
|
break;
|
|
|
|
/* operand shift_left (48) */
|
|
case 48:
|
|
if (JS_SP2->type == JS_INTEGER && JS_SP1->type == JS_INTEGER)
|
|
{
|
|
JS_SP2->u.vinteger = ((JSInt32) JS_SP2->u.vinteger
|
|
<< (JSUInt32) JS_SP1->u.vinteger);
|
|
JS_POP ();
|
|
}
|
|
else
|
|
{
|
|
JSInt32 l;
|
|
JSUInt32 r;
|
|
|
|
l = js_vm_to_int32 (vm, JS_SP2);
|
|
r = (JSUInt32) js_vm_to_int32 (vm, JS_SP1);
|
|
|
|
JS_SP2->u.vinteger = l << r;
|
|
JS_SP2->type = JS_INTEGER;
|
|
JS_POP ();
|
|
}
|
|
break;
|
|
|
|
/* operand shift_right (49) */
|
|
case 49:
|
|
if (JS_SP2->type == JS_INTEGER && JS_SP1->type == JS_INTEGER)
|
|
{
|
|
JS_SP2->u.vinteger = ((JSInt32) JS_SP2->u.vinteger
|
|
>> (JSUInt32) JS_SP1->u.vinteger);
|
|
JS_POP ();
|
|
}
|
|
else
|
|
{
|
|
JSInt32 l;
|
|
JSUInt32 r;
|
|
|
|
l = js_vm_to_int32 (vm, JS_SP2);
|
|
r = (JSUInt32) js_vm_to_int32 (vm, JS_SP1);
|
|
|
|
JS_SP2->u.vinteger = l >> r;
|
|
JS_SP2->type = JS_INTEGER;
|
|
JS_POP ();
|
|
}
|
|
break;
|
|
|
|
/* operand shift_rright (50) */
|
|
case 50:
|
|
{
|
|
JSInt32 l;
|
|
JSUInt32 r;
|
|
|
|
l = js_vm_to_int32 (vm, JS_SP2);
|
|
r = (JSUInt32) js_vm_to_int32 (vm, JS_SP1);
|
|
|
|
if (r > 0)
|
|
JS_SP2->u.vinteger = (l & 0x7fffffff) >> r;
|
|
else
|
|
JS_SP2->u.vinteger = l;
|
|
|
|
JS_SP2->type = JS_INTEGER;
|
|
JS_POP ();
|
|
}
|
|
break;
|
|
|
|
/* operand iffalse (51) */
|
|
case 51:
|
|
READ_INT32 (i);
|
|
if (JS_IS_FALSE (JS_SP1))
|
|
SETPC_RELATIVE (i);
|
|
JS_POP ();
|
|
break;
|
|
|
|
/* operand iftrue (52) */
|
|
case 52:
|
|
READ_INT32 (i);
|
|
if (JS_IS_TRUE (JS_SP1))
|
|
SETPC_RELATIVE (i);
|
|
JS_POP ();
|
|
break;
|
|
|
|
/* operand call_method (53) */
|
|
case 53:
|
|
/* Fetch the method symbol. */
|
|
READ_INT32 (j);
|
|
|
|
if (JS_SP1->type == JS_BUILTIN)
|
|
{
|
|
JS_SAVE_REGS ();
|
|
if (JS_SP1->u.vbuiltin->info->method_proc)
|
|
{
|
|
if ((*JS_SP1->u.vbuiltin->info->method_proc) (
|
|
vm,
|
|
JS_SP1->u.vbuiltin->info,
|
|
JS_SP1->u.vbuiltin->instance_context, j,
|
|
&builtin_result, JS_SP2)
|
|
== JS_PROPERTY_UNKNOWN)
|
|
ERROR ("call_method: unknown method");
|
|
}
|
|
else
|
|
ERROR ("illegal builtin object for call_method");
|
|
|
|
JS_COPY (JS_SP0, &builtin_result);
|
|
JS_PUSH ();
|
|
JS_MAYBE_GC ();
|
|
}
|
|
else if (JS_SP1->type == JS_OBJECT)
|
|
{
|
|
JSNode method;
|
|
|
|
if (js_vm_object_load_property (vm, JS_SP1->u.vobject, j, &method)
|
|
== JS_PROPERTY_FOUND)
|
|
{
|
|
/* The property has been defined in the object. */
|
|
|
|
if (method.type != JS_FUNC)
|
|
ERROR ("call_method: unknown method");
|
|
|
|
/* And once again. We must do a subroutine call here. */
|
|
JS_SUBROUTINE_CALL (method.u.vfunction->implementation);
|
|
}
|
|
else
|
|
/* Let our prototype handle this. */
|
|
goto _op_call_method_try_proto;
|
|
}
|
|
else if (vm->prim[JS_SP1->type])
|
|
{
|
|
/* The primitive language types. */
|
|
_op_call_method_try_proto:
|
|
JS_SAVE_REGS ();
|
|
if ((*vm->prim[JS_SP1->type]->method_proc) (vm, vm->prim[JS_SP1->type],
|
|
JS_SP1, j, &builtin_result,
|
|
JS_SP2)
|
|
== JS_PROPERTY_UNKNOWN)
|
|
{
|
|
JSNode method;
|
|
int result = JS_PROPERTY_UNKNOWN;
|
|
|
|
/* Let's see if we can find it from the prototype. */
|
|
if (JS_SP1->type == JS_STRING && JS_SP1->u.vstring->prototype)
|
|
result = js_vm_object_load_property (vm,
|
|
JS_SP1->u.vstring->prototype,
|
|
j, &method);
|
|
else if (JS_SP1->type == JS_ARRAY && JS_SP1->u.varray->prototype)
|
|
result = js_vm_object_load_property (vm,
|
|
JS_SP1->u.varray->prototype,
|
|
j, &method);
|
|
else if (JS_SP1->type == JS_FUNC && JS_SP1->u.vfunction->prototype)
|
|
result
|
|
= js_vm_object_load_property (vm, JS_SP1->u.vfunction->prototype,
|
|
j, &method);
|
|
|
|
if (result == JS_PROPERTY_UNKNOWN || method.type != JS_FUNC)
|
|
ERROR ("call_method: unknown method");
|
|
|
|
/* Do the subroutine call. */
|
|
JS_SUBROUTINE_CALL (method.u.vfunction->implementation);
|
|
}
|
|
else
|
|
{
|
|
JS_COPY (JS_SP0, &builtin_result);
|
|
JS_PUSH ();
|
|
JS_MAYBE_GC ();
|
|
}
|
|
}
|
|
else
|
|
ERROR ("illegal object for call_method");
|
|
break;
|
|
|
|
/* operand jmp (54) */
|
|
case 54:
|
|
READ_INT32 (i);
|
|
SETPC_RELATIVE (i);
|
|
break;
|
|
|
|
/* operand jsr (55) */
|
|
case 55:
|
|
/* Call the global method. */
|
|
{
|
|
JSNode f;
|
|
|
|
/* Fetch the function to our local variable. */
|
|
JS_COPY (&f, JS_SP1);
|
|
function = &f;
|
|
|
|
/* Reset the `this' to null. */
|
|
JS_SP1->type = JS_NULL;
|
|
|
|
if (function->type == JS_BUILTIN
|
|
&& function->u.vbuiltin->info->global_method_proc)
|
|
{
|
|
JS_SAVE_REGS ();
|
|
(*function->u.vbuiltin->info->global_method_proc) (
|
|
vm,
|
|
function->u.vbuiltin->info,
|
|
function->u.vbuiltin->instance_context,
|
|
&builtin_result,
|
|
JS_SP2);
|
|
|
|
JS_COPY (JS_SP0, &builtin_result);
|
|
JS_PUSH ();
|
|
}
|
|
else if (function->type == JS_FUNC)
|
|
{
|
|
JS_SUBROUTINE_CALL (function->u.vfunction->implementation);
|
|
}
|
|
else
|
|
{
|
|
sprintf (buf, "illegal function object in jsr");
|
|
ERROR (buf);
|
|
}
|
|
}
|
|
break;
|
|
|
|
/* operand return (56) */
|
|
case 56:
|
|
if (fp->u.iptr == NULL)
|
|
/* Return from the global scope. */
|
|
DONE ();
|
|
|
|
/* STACKFRAME */
|
|
|
|
/* Check if the stack has been modified by min_args. */
|
|
if (JS_ARGS_FIXP->u.args_fix.delta)
|
|
{
|
|
unsigned int delta = JS_ARGS_FIXP->u.args_fix.delta;
|
|
|
|
/*
|
|
* Yes it was. Truncate it back to the state where it was
|
|
* before the call.
|
|
*/
|
|
|
|
memmove (JS_SP1 + delta, JS_SP1,
|
|
(fp - JS_SP0 + JS_ARGS_FIXP->u.args_fix.argc)
|
|
* sizeof (JSNode));
|
|
|
|
sp += delta;
|
|
fp += delta;
|
|
}
|
|
|
|
/* Set pc to the saved return address. */
|
|
#if 0
|
|
if (fp[-3].type != JS_IPTR)
|
|
ERROR ("can't find saved return address");
|
|
#endif
|
|
pc = fp[-3].u.iptr;
|
|
|
|
{
|
|
void *old_fp;
|
|
|
|
/* Save old frame pointer. */
|
|
#if 0
|
|
if (fp->type != JS_IPTR)
|
|
ERROR ("can't find saved frame pointer");
|
|
#endif
|
|
old_fp = fp->u.iptr;
|
|
|
|
/* Put return value to its correct location. */
|
|
JS_COPY (fp, JS_SP1);
|
|
|
|
/* Restore sp. */
|
|
sp = &fp[-1];
|
|
|
|
/* Restore frame pointer. */
|
|
fp = old_fp;
|
|
}
|
|
break;
|
|
|
|
/* operand typeof (57) */
|
|
case 57:
|
|
{
|
|
char *typeof_name = ""; /* Initialized to make compiler quiet. */
|
|
|
|
switch (JS_SP1->type)
|
|
{
|
|
case JS_UNDEFINED:
|
|
typeof_name = "undefined";
|
|
break;
|
|
|
|
case JS_NULL:
|
|
typeof_name = "object";
|
|
break;
|
|
|
|
case JS_BOOLEAN:
|
|
typeof_name = "boolean";
|
|
break;
|
|
|
|
case JS_INTEGER:
|
|
case JS_FLOAT:
|
|
case JS_NAN:
|
|
typeof_name = "number";
|
|
break;
|
|
|
|
case JS_STRING:
|
|
typeof_name = "string";
|
|
break;
|
|
|
|
case JS_ARRAY:
|
|
typeof_name = "#array";
|
|
break;
|
|
|
|
case JS_OBJECT:
|
|
typeof_name = "object";
|
|
break;
|
|
|
|
case JS_SYMBOL:
|
|
typeof_name = "#symbol";
|
|
break;
|
|
|
|
case JS_BUILTIN:
|
|
typeof_name = "#builtin";
|
|
break;
|
|
|
|
case JS_FUNC:
|
|
typeof_name = "function";
|
|
break;
|
|
|
|
case JS_IPTR:
|
|
typeof_name = "#iptr";
|
|
break;
|
|
|
|
case JS_ARGS_FIX:
|
|
typeof_name = "#argsfix";
|
|
break;
|
|
}
|
|
|
|
js_vm_make_static_string (vm, JS_SP1, typeof_name, strlen (typeof_name));
|
|
JS_MAYBE_GC ();
|
|
}
|
|
break;
|
|
|
|
/* operand new (58) */
|
|
case 58:
|
|
/* Check object. */
|
|
if (JS_SP1->type == JS_BUILTIN && JS_SP1->u.vbuiltin->info->new_proc)
|
|
{
|
|
JS_SAVE_REGS ();
|
|
(*JS_SP1->u.vbuiltin->info->new_proc) (vm, JS_SP1->u.vbuiltin->info,
|
|
JS_SP2, JS_SP1);
|
|
|
|
/* Push a dummy return value for the constructor. This is ignored. */
|
|
JS_SP0->type = JS_UNDEFINED;
|
|
JS_PUSH ();
|
|
}
|
|
else if (JS_SP1->type == JS_FUNC)
|
|
{
|
|
JSObject *obj;
|
|
JSNode f;
|
|
JSNode prototype;
|
|
|
|
/* The prototype is an object. */
|
|
prototype.type = JS_OBJECT;
|
|
|
|
/* Create the prototype for the function, if it is not defined. */
|
|
if (JS_SP1->u.vfunction->prototype == NULL)
|
|
{
|
|
JS_SP1->u.vfunction->prototype = js_vm_object_new (vm);
|
|
|
|
/* Set its __proto__ to point to Object's prototype. */
|
|
prototype.u.vobject = vm->prim[JS_OBJECT]->prototype;
|
|
js_vm_object_store_property (vm, JS_SP1->u.vfunction->prototype,
|
|
vm->syms.s___proto__, &prototype);
|
|
}
|
|
|
|
/* Allocate a new object and set its prototype. */
|
|
|
|
obj = js_vm_object_new (vm);
|
|
|
|
prototype.u.vobject = JS_SP1->u.vfunction->prototype;
|
|
js_vm_object_store_property (vm, obj, vm->syms.s___proto__, &prototype);
|
|
|
|
/*
|
|
* Basicly we do a jsr to the function given in JS_SP1. But first,
|
|
* we must set `this' pointer to the correct value. See `jsr' for
|
|
* the details.
|
|
*/
|
|
JS_COPY (&f, JS_SP1);
|
|
|
|
/* Replace func with the new object. */
|
|
JS_SP1->type = JS_OBJECT;
|
|
JS_SP1->u.vobject = obj;
|
|
|
|
JS_SUBROUTINE_CALL (f.u.vfunction->implementation);
|
|
}
|
|
/* The primitive language types. */
|
|
else if (vm->prim[JS_SP1->type])
|
|
{
|
|
JS_SAVE_REGS ();
|
|
(*vm->prim[JS_SP1->type]->new_proc) (vm, vm->prim[JS_SP1->type], JS_SP2,
|
|
JS_SP1);
|
|
JS_PUSH ();
|
|
}
|
|
else
|
|
ERROR ("illegal object for new");
|
|
|
|
JS_MAYBE_GC ();
|
|
break;
|
|
|
|
/* operand delete_property (59) */
|
|
case 59:
|
|
/* Fetch the property symbol. */
|
|
READ_INT32 (j);
|
|
|
|
if (JS_SP1->type == JS_BUILTIN)
|
|
{
|
|
/*
|
|
* XXX It should be possible to apply delete operand to builtin
|
|
* XXX objects.
|
|
*/
|
|
ERROR ("delete_property: JS_BUILTIN: not implemented yet");
|
|
}
|
|
else if (JS_SP1->type == JS_OBJECT)
|
|
{
|
|
js_vm_object_delete_property (vm, JS_SP1->u.vobject, j);
|
|
}
|
|
else if (JS_SP1->type == JS_NULL)
|
|
{
|
|
/* Delete a property from an object in the with-chain. */
|
|
/* WITHCHAIN */
|
|
ERROR ("delete_property: not implemented yet for the with-chain objects");
|
|
}
|
|
/* The primitive language types. */
|
|
/*
|
|
* XXX Since we can't delete properties from builtins, we can't delete
|
|
* XXX them from the primitive language types.
|
|
*/
|
|
else
|
|
ERROR ("illegal object for delete_property");
|
|
|
|
/* The delete operand returns an undefined value. */
|
|
JS_SP1->type = JS_UNDEFINED;
|
|
break;
|
|
|
|
/* operand delete_array (60) */
|
|
case 60:
|
|
if (JS_SP2->type == JS_BUILTIN)
|
|
{
|
|
ERROR ("delete_array: JS_BUILTIN: not implemented yet");
|
|
}
|
|
else if (JS_SP2->type == JS_OBJECT)
|
|
{
|
|
js_vm_object_delete_array (vm, JS_SP2->u.vobject, JS_SP1);
|
|
JS_POP ();
|
|
}
|
|
else if (JS_SP2->type == JS_ARRAY)
|
|
{
|
|
if (JS_SP1->type == JS_INTEGER)
|
|
{
|
|
if (0 <= JS_SP1->u.vinteger
|
|
&& JS_SP1->u.vinteger < JS_SP2->u.varray->length)
|
|
JS_SP2->u.varray->data[JS_SP1->u.vinteger].type = JS_UNDEFINED;
|
|
JS_POP ();
|
|
}
|
|
else
|
|
ERROR ("illegal array index in delete_array");
|
|
}
|
|
else
|
|
ERROR ("illegal object for delete_array");
|
|
|
|
/* The delete operand returns an undefined value. */
|
|
JS_SP1->type = JS_UNDEFINED;
|
|
break;
|
|
|
|
/* operand locals (61) */
|
|
case 61:
|
|
READ_INT16 (i);
|
|
if (sp - i - JS_RESERVE_STACK_FOR_FUNCTION < vm->stack)
|
|
ERROR ("stack overflow");
|
|
|
|
for (; i > 0; i--)
|
|
{
|
|
JS_SP0->type = JS_UNDEFINED;
|
|
JS_PUSH ();
|
|
}
|
|
break;
|
|
|
|
/* operand min_args (62) */
|
|
case 62:
|
|
READ_INT8 (i);
|
|
|
|
if (JS_SP1->u.vinteger < i)
|
|
{
|
|
unsigned int delta = i - JS_SP1->u.vinteger;
|
|
unsigned int argc = JS_SP1->u.vinteger;
|
|
|
|
memmove (JS_SP1 - delta, JS_SP1, (fp - JS_SP0 + argc) * sizeof (JSNode));
|
|
sp -= delta;
|
|
fp -= delta;
|
|
|
|
/* Fill up the fix_args slot. */
|
|
JS_ARGS_FIXP->u.args_fix.argc = argc;
|
|
JS_ARGS_FIXP->u.args_fix.delta = delta;
|
|
|
|
for (; argc < i; argc++)
|
|
JS_ARG (argc)->type = JS_UNDEFINED;
|
|
}
|
|
|
|
JS_POP ();
|
|
break;
|
|
|
|
/* operand load_nth_arg (63) */
|
|
case 63:
|
|
{
|
|
int index = JS_SP1->u.vinteger;
|
|
JS_COPY (JS_SP1, JS_ARG (index));
|
|
}
|
|
break;
|
|
|
|
/* operand with_push (64) */
|
|
case 64:
|
|
if (JS_SP1->type != JS_OBJECT && JS_SP1->type != JS_BUILTIN)
|
|
ERROR ("illegal object for with_push");
|
|
|
|
/* WITHCHAIN */
|
|
|
|
if (JS_WITHPTR->u.iptr == NULL)
|
|
{
|
|
JSNode *np;
|
|
JSUIntAlign *ip = js_vm_alloc (vm,
|
|
sizeof (JSUIntAlign)
|
|
+ sizeof (JSNode));
|
|
*ip = 1;
|
|
np = (JSNode *) ((unsigned char *) ip + sizeof (JSUIntAlign));
|
|
|
|
JS_COPY (np, JS_SP1);
|
|
JS_WITHPTR->u.iptr = ip;
|
|
}
|
|
else
|
|
{
|
|
JSNode *np;
|
|
JSUIntAlign *ip = JS_WITHPTR->u.iptr;
|
|
JSUIntAlign ui = *ip;
|
|
|
|
ip = js_vm_realloc (vm, ip,
|
|
sizeof (JSUIntAlign)
|
|
+ ((ui + 1) * sizeof (JSNode)));
|
|
(*ip)++;
|
|
np = (JSNode *) ((unsigned char *) ip + sizeof (JSUIntAlign));
|
|
|
|
JS_COPY (&np[ui], JS_SP1);
|
|
JS_WITHPTR->u.iptr = ip;
|
|
}
|
|
|
|
JS_POP ();
|
|
break;
|
|
|
|
/* operand with_pop (65) */
|
|
case 65:
|
|
READ_INT8 (i);
|
|
|
|
/* WITHCHAIN */
|
|
|
|
{
|
|
JSUIntAlign *ip = JS_WITHPTR->u.iptr;
|
|
|
|
if (ip == NULL || *ip < i)
|
|
ERROR ("with stack underflow in with_pop");
|
|
|
|
*ip -= i;
|
|
}
|
|
break;
|
|
|
|
/* operand try_push (66) */
|
|
case 66:
|
|
READ_INT32 (i);
|
|
|
|
{
|
|
JSErrorHandlerFrame *frame = js_calloc (vm, 1, sizeof (*frame));
|
|
|
|
frame->next = vm->error_handler;
|
|
frame->sp = sp;
|
|
frame->fp = fp;
|
|
frame->pc = pc;
|
|
frame->pc_delta = i;
|
|
vm->error_handler = frame;
|
|
|
|
if (setjmp (vm->error_handler->error_jmp))
|
|
{
|
|
/* Ok, we caught an error. */
|
|
|
|
/* Restore our state. */
|
|
sp = vm->error_handler->sp;
|
|
fp = vm->error_handler->fp;
|
|
pc = vm->error_handler->pc;
|
|
i = vm->error_handler->pc_delta;
|
|
|
|
/* Push the thrown value to the stack. */
|
|
JS_COPY (JS_SP0, &vm->error_handler->thrown);
|
|
JS_PUSH ();
|
|
|
|
/* Remove this handler frame. */
|
|
frame = vm->error_handler;
|
|
vm->error_handler = vm->error_handler->next;
|
|
js_free (frame);
|
|
|
|
/* Do the jump to the catch block. */
|
|
SETPC_RELATIVE (i);
|
|
}
|
|
}
|
|
break;
|
|
|
|
/* operand try_pop (67) */
|
|
case 67:
|
|
READ_INT8 (i);
|
|
|
|
for (; i > 0; i--)
|
|
{
|
|
JSErrorHandlerFrame *frame = vm->error_handler;
|
|
|
|
vm->error_handler = vm->error_handler->next;
|
|
js_free (frame);
|
|
}
|
|
break;
|
|
|
|
/* operand throw (68) */
|
|
case 68:
|
|
{
|
|
JSErrorHandlerFrame *f = vm->error_handler;
|
|
|
|
if (f->sp == NULL)
|
|
{
|
|
JSNode cvt;
|
|
int len;
|
|
|
|
/*
|
|
* We are jumping to the C-toplevel. Convert our thrown value
|
|
* to string and store it to the vm->error.
|
|
*/
|
|
js_vm_to_string (vm, JS_SP1, &cvt);
|
|
|
|
len = cvt.u.vstring->len;
|
|
if (len + 1 > sizeof (vm->error))
|
|
len = sizeof (vm->error) - 1;
|
|
|
|
memcpy (vm->error, cvt.u.vstring->data, len);
|
|
vm->error[len] = '\0';
|
|
}
|
|
else
|
|
JS_COPY (&f->thrown, JS_SP1);
|
|
|
|
longjmp (f->error_jmp, 1);
|
|
|
|
/* NOTREACHED (I hope). */
|
|
|
|
sprintf (buf, "VM: no valid error handler initialized%s",
|
|
JS_HOST_LINE_BREAK);
|
|
js_iostream_write (vm->s_stderr, buf, strlen (buf));
|
|
js_iostream_flush (vm->s_stderr);
|
|
|
|
abort ();
|
|
}
|
|
break;
|
|
|
|
/* operand iffalse_b (69) */
|
|
case 69:
|
|
READ_INT32 (i);
|
|
if (!JS_SP1->u.vboolean)
|
|
SETPC_RELATIVE (i);
|
|
JS_POP ();
|
|
break;
|
|
|
|
/* operand iftrue_b (70) */
|
|
case 70:
|
|
READ_INT32 (i);
|
|
if (JS_SP1->u.vboolean)
|
|
SETPC_RELATIVE (i);
|
|
JS_POP ();
|
|
break;
|
|
|
|
/* operand add_1_i (71) */
|
|
case 71:
|
|
JS_SP1->u.vinteger++;
|
|
break;
|
|
|
|
/* operand add_2_i (72) */
|
|
case 72:
|
|
JS_SP1->u.vinteger += 2;
|
|
break;
|
|
|
|
/* operand load_global_w (73) */
|
|
case 73:
|
|
READ_INT32 (j);
|
|
{
|
|
int found = 0;
|
|
|
|
/* Loop over the with chain. */
|
|
/* WITHCHAIN */
|
|
if (JS_WITHPTR->u.iptr)
|
|
{
|
|
JSUIntAlign *uip = JS_WITHPTR->u.iptr;
|
|
JSUIntAlign ui = *uip;
|
|
JSNode *wp = (JSNode *) ((unsigned char *) uip
|
|
+ sizeof (JSUIntAlign));
|
|
|
|
for (i = 0; i < ui; i++)
|
|
{
|
|
JSNode *w = &wp[i];
|
|
int result = JS_PROPERTY_UNKNOWN;
|
|
|
|
if (w->type == JS_BUILTIN)
|
|
{
|
|
JS_SAVE_REGS ();
|
|
if (w->u.vbuiltin->info->property_proc)
|
|
result = (*w->u.vbuiltin->info->property_proc) (
|
|
vm,
|
|
w->u.vbuiltin->info,
|
|
w->u.vbuiltin->instance_context,
|
|
j, 0, &builtin_result);
|
|
}
|
|
else if (w->type == JS_OBJECT)
|
|
{
|
|
result = js_vm_object_load_property (vm, w->u.vobject, j,
|
|
&builtin_result);
|
|
}
|
|
else
|
|
ERROR ("corrupted with-chain in load_global");
|
|
|
|
if (result == JS_PROPERTY_FOUND)
|
|
{
|
|
JS_COPY (JS_SP0, &builtin_result);
|
|
JS_PUSH ();
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
/* Use the global value. */
|
|
JS_COPY (JS_SP0, JS_GLOBAL (j));
|
|
JS_PUSH ();
|
|
|
|
if (vm->warn_undef && JS_SP1->type == JS_UNDEFINED)
|
|
{
|
|
sprintf (buf, "VM: warning: using undefined global `%s'%s",
|
|
js_vm_symname (vm, j), JS_HOST_LINE_BREAK);
|
|
js_iostream_write (vm->s_stderr, buf, strlen (buf));
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
/* operand jsr_w (74) */
|
|
case 74:
|
|
/* Read the subroutine symbol index. */
|
|
READ_INT32 (j);
|
|
|
|
{
|
|
int found = 0;
|
|
|
|
/* Loop over the with-chain. */
|
|
/* WITHCHAIN */
|
|
if (JS_WITHPTR->u.iptr)
|
|
{
|
|
JSUIntAlign *uip = JS_WITHPTR->u.iptr;
|
|
JSUIntAlign ui = *uip;
|
|
JSNode *wp = (JSNode *) ((unsigned char *) uip
|
|
+ sizeof (JSUIntAlign));
|
|
|
|
for (i = 0; i < ui; i++)
|
|
{
|
|
JSNode *w = &wp[i];
|
|
int result = JS_PROPERTY_UNKNOWN;
|
|
|
|
if (w->type == JS_BUILTIN)
|
|
{
|
|
JS_SAVE_REGS ();
|
|
if (w->u.vbuiltin->info->method_proc)
|
|
result = (*w->u.vbuiltin->info->method_proc) (
|
|
vm,
|
|
w->u.vbuiltin->info,
|
|
w->u.vbuiltin->instance_context, j,
|
|
&builtin_result, JS_SP2);
|
|
JS_MAYBE_GC ();
|
|
|
|
if (result == JS_PROPERTY_FOUND)
|
|
{
|
|
JS_COPY (JS_SP0, &builtin_result);
|
|
JS_PUSH ();
|
|
}
|
|
}
|
|
else if (w->type == JS_OBJECT)
|
|
{
|
|
JSNode method;
|
|
|
|
js_vm_object_load_property (vm, w->u.vobject, j, &method);
|
|
if (method.type == JS_FUNC)
|
|
{
|
|
result = JS_PROPERTY_FOUND;
|
|
|
|
/* The object defines the method. Do a subroutine call. */
|
|
|
|
/* First: replace the null `this' with `w'. */
|
|
JS_COPY (JS_SP1, w);
|
|
|
|
/* Then, do the normal subroutine call. */
|
|
JS_SUBROUTINE_CALL (method.u.vfunction->implementation);
|
|
}
|
|
}
|
|
else
|
|
ERROR ("corrupted with-chain in jsr_w");
|
|
|
|
if (result == JS_PROPERTY_FOUND)
|
|
{
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
JSNode f;
|
|
|
|
/* Call the global method. */
|
|
JS_COPY (&f, JS_SP1);
|
|
function = &f;
|
|
|
|
/* Reset the `this' to null. */
|
|
JS_SP1->type = JS_NULL;
|
|
|
|
if (function->type == JS_BUILTIN
|
|
&& function->u.vbuiltin->info->global_method_proc)
|
|
{
|
|
JS_SAVE_REGS ();
|
|
(*function->u.vbuiltin->info->global_method_proc) (
|
|
vm,
|
|
function->u.vbuiltin->info,
|
|
function->u.vbuiltin->instance_context,
|
|
&builtin_result,
|
|
JS_SP2);
|
|
|
|
JS_COPY (JS_SP0, &builtin_result);
|
|
JS_PUSH ();
|
|
}
|
|
else if (function->type == JS_FUNC)
|
|
{
|
|
JS_SUBROUTINE_CALL (function->u.vfunction->implementation);
|
|
}
|
|
else
|
|
{
|
|
sprintf (buf, "symbol `%s' is undefined as function",
|
|
js_vm_symname (vm, j));
|
|
ERROR (buf);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
|