/* -*- c -*- * Operand definitions for the JavaScript byte-code. * Copyright (c) 1998 New Generation Software (NGS) Oy * * Author: Markku Rossi */ /* * 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: /usr/local/cvsroot/ngs/js/src/operands.def,v $ * $Id: operands.def,v 1.22 1998/11/25 09:10:30 mtr Exp $ */ operand halt 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); } operand done 0 { DONE (); } operand nop 0 { /* Nothing here! */ } operand dup 0 { JS_COPY (JS_SP0, JS_SP1); JS_PUSH (); } operand pop 0 { JS_POP (); } operand pop_n 1 { READ_INT8 (i); JS_POP_N (i); } operand apop 1 { READ_INT8 (i); JS_COPY (JS_SP (i + 1), JS_SP1); JS_POP_N (i); } operand swap 0 { JS_COPY (JS_SP0, JS_SP2); JS_COPY (JS_SP2, JS_SP1); JS_COPY (JS_SP1, JS_SP0); } operand roll 1 { 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)); } } operand const 4 { READ_INT32 (i); JS_COPY (JS_SP0, JS_CONST (i)); JS_PUSH (); } operand const_null 0 { JS_SP0->type = JS_NULL; JS_PUSH (); } operand const_true 0 { JS_SP0->type = JS_BOOLEAN; JS_SP0->u.vboolean = 1; JS_PUSH (); } operand const_false 0 { JS_SP0->type = JS_BOOLEAN; JS_SP0->u.vboolean = 0; JS_PUSH (); } operand const_undefined 0 { JS_SP0->type = JS_UNDEFINED; JS_PUSH (); } operand const_i0 0 { JS_SP0->type = JS_INTEGER; JS_SP0->u.vinteger = 0; JS_PUSH (); } operand const_i1 0 { JS_SP0->type = JS_INTEGER; JS_SP0->u.vinteger = 1; JS_PUSH (); } operand const_i2 0 { JS_SP0->type = JS_INTEGER; JS_SP0->u.vinteger = 2; JS_PUSH (); } operand const_i3 0 { JS_SP0->type = JS_INTEGER; JS_SP0->u.vinteger = 3; JS_PUSH (); } operand const_i 4 { READ_INT32 (i); JS_SP0->type = JS_INTEGER; JS_SP0->u.vinteger = i; JS_PUSH (); } operand load_global 4 { /* symbol */ 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)); } } operand store_global 4 { /* symbol */ 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 (); } operand load_arg 1 { READ_INT8 (i); JS_COPY (JS_SP0, JS_ARG (i)); JS_PUSH (); } operand store_arg 1 { READ_INT8 (i); JS_COPY (JS_ARG (i), JS_SP1); JS_POP (); } operand load_local 2 { READ_INT16 (i); JS_COPY (JS_SP0, JS_LOCAL (i)); JS_PUSH (); } operand store_local 2 { READ_INT16 (i); JS_COPY (JS_LOCAL (i), JS_SP1); JS_POP (); } operand load_property 4 { /* symbol */ /* 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"); } operand store_property 4 { /* symbol */ /* 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 (); } operand load_array 0 { 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"); } operand store_array 0 { 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 (); } operand nth 0 { 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"); } operand cmp_eq 0 { JS_OPERAND_CMP_EQ (==, 1); } operand cmp_ne 0 { JS_OPERAND_CMP_EQ (!=, 0); } operand cmp_lt 0 { JS_OPERAND_CMP_REL (<); } operand cmp_gt 0 { JS_OPERAND_CMP_REL (>); } operand cmp_le 0 { JS_OPERAND_CMP_REL (<=); } operand cmp_ge 0 { JS_OPERAND_CMP_REL (>=); } operand cmp_seq 0 { JS_OPERAND_CMP_SEQ (==, 1); } operand cmp_sne 0 { JS_OPERAND_CMP_SEQ (!=, 0); } operand sub 0 { 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 (); } operand add 0 { 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 (); } } operand mul 0 { 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 (); } operand div 0 { { 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) { /* 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; } } } } operand mod 0 { 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 (); } operand neg 0 { 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; } } } operand and 0 { JS_OPERAND_BINARY (&); } operand not 0 { JS_SP1->u.vboolean = JS_IS_FALSE (JS_SP1); JS_SP1->type = JS_BOOLEAN; } operand or 0 { JS_OPERAND_BINARY (|); } operand xor 0 { JS_OPERAND_BINARY (^); } operand shift_left 0 { 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 (); } } operand shift_right 0 { 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 (); } } operand shift_rright 0 { { 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 (); } } operand iffalse 4 { /* jump */ READ_INT32 (i); if (JS_IS_FALSE (JS_SP1)) SETPC_RELATIVE (i); JS_POP (); } operand iftrue 4 { /* jump */ READ_INT32 (i); if (JS_IS_TRUE (JS_SP1)) SETPC_RELATIVE (i); JS_POP (); } operand call_method 4 { /* symbol */ /* 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"); } operand jmp 4 { /* jump */ READ_INT32 (i); SETPC_RELATIVE (i); } operand jsr 0 { /* 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); } } } operand return 0 { 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; } } operand typeof 0 { { 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 (); } } operand new 0 { /* 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 (); } operand delete_property 4 { /* symbol */ /* 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; } operand delete_array 0 { 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; } operand locals 2 { 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 (); } } operand min_args 1 { 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 (); } operand load_nth_arg 0 { { int index = JS_SP1->u.vinteger; JS_COPY (JS_SP1, JS_ARG (index)); } } operand with_push 0 { 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 (); } operand with_pop 1 { READ_INT8 (i); /* WITHCHAIN */ { JSUIntAlign *ip = JS_WITHPTR->u.iptr; if (ip == NULL || *ip < i) ERROR ("with stack underflow in with_pop"); *ip -= i; } } operand try_push 4 { /* jump */ 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); } } } operand try_pop 1 { READ_INT8 (i); for (; i > 0; i--) { JSErrorHandlerFrame *frame = vm->error_handler; vm->error_handler = vm->error_handler->next; js_free (frame); } } operand throw 0 { { 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 (); } } operand iffalse_b 4 { /* jump */ READ_INT32 (i); if (!JS_SP1->u.vboolean) SETPC_RELATIVE (i); JS_POP (); } operand iftrue_b 4 { /* jump */ READ_INT32 (i); if (JS_SP1->u.vboolean) SETPC_RELATIVE (i); JS_POP (); } operand add_1_i 0 { JS_SP1->u.vinteger++; } operand add_2_i 0 { JS_SP1->u.vinteger += 2; } operand load_global_w 4 { /* symbol */ 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)); } } } } operand jsr_w 4 { /* symbol */ /* 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); } } } }