Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

JavaScript.C

00001 /* $Id: JavaScript.C,v 1.2 2000/11/03 22:03:28 rpav Exp $
00002  *
00003  *
00004  * Copyright (C) 1999  Ryan Pavlik
00005  *
00006  * This program is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU General Public License
00008  * as published by the Free Software Foundation; either version 2
00009  * of the License, or (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  * 
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00019  *
00020  */
00021 
00022 /*
00023  * JavaScript-related classes
00024  *
00025  */ 
00026 
00027 #include <iostream>
00028 #include <cstdlib>
00029 #include <cstring>
00030 #include <string>
00031 #include <vector>
00032 
00033 extern "C" {
00034 #include <js.h>
00035 #include <jsint.h>
00036 }
00037 
00038 #include <SFW/JavaScript.H>
00039 #include <SFW/Plugin.H>
00040 
00041 using namespace SFW;
00042 
00043 TYPE_INIT(SFW::JavaScriptInterpreter);
00044 TYPE_INIT(SFW::JavaScriptByteCode);
00045 TYPE_INIT(SFW::JavaScriptType);
00046 TYPE_INIT(SFW::JavaScriptMethod);
00047 TYPE_INIT(SFW::JavaScriptClass);
00048 PLUGIN_INIT(none, "JavaScript Wrapper Classes", NULL);
00049 
00050 const JSTArray NullJSArg(0);
00051 
00052 /* Some local structures */
00053 struct MethodContext {
00054     JavaScriptInterpreter  * jsi;
00055     JavaScriptMethod * jsgm;
00056 };
00057 
00058 /* Local functions */
00059 static JSMethodResult callMethod(void *context,
00060                  JSInterpPtr interp, int argc,
00061                  JSType *argv, JSType *return_result,
00062                  char *error_return);
00063 static void           freeContext(void *context);
00064 static void           copyJSN(JSNode * dest, JSNode * src);
00065 
00066 /**
00067  ** JavaScriptInterpreter
00068  **
00069  **/
00070 
00071 JavaScriptInterpreter::JavaScriptInterpreter() {
00072     js_init_default_options(&jsopts);
00073     jsi = js_create_interp(&jsopts);
00074 }
00075 
00076 JavaScriptInterpreter::~JavaScriptInterpreter() {
00077     js_destroy_interp(this->jsi);
00078 }
00079 
00080 string JavaScriptInterpreter::getVersion(void) const {
00081     return (const string)js_version();
00082 }
00083 
00084 string JavaScriptInterpreter::errorMsg(void) const {
00085     return (const string)js_error_message(this->jsi);
00086 }
00087 
00088 JavaScriptType JavaScriptInterpreter::eval(const string& code) {
00089     JSNode jsn;
00090 
00091     error_cond = !js_eval_data(jsi, const_cast<char*>(code.data()), code.length());
00092     js_result(jsi, (JSType*)&jsn);
00093 
00094     return JavaScriptType(jsn);
00095 }
00096 
00097 JavaScriptType JavaScriptInterpreter::evalFile(const string& filename) {
00098     JSNode jsn;
00099     
00100     error_cond = !js_eval_file(jsi, const_cast<char*>(filename.c_str()));
00101     js_result(jsi, (JSType*)&jsn);
00102 
00103     return JavaScriptType(jsn);
00104 }
00105 
00106 JavaScriptType JavaScriptInterpreter::exec(const JavaScriptByteCode &_jsbc) {
00107     JSNode jsn;
00108     
00109     error_cond = !js_execute_byte_code(jsi, _jsbc.data, _jsbc.len);
00110     js_result(jsi, (JSType*)&jsn);
00111 
00112     return JavaScriptType(jsn);
00113 }
00114 
00115 bool JavaScriptInterpreter::error() const {
00116     return error_cond;
00117 }
00118 
00119 JavaScriptByteCode JavaScriptInterpreter::compile(const string &code) {
00120     JavaScriptByteCode jsbc;
00121     unsigned int   len;
00122     unsigned char *data;
00123 
00124     error_cond = !js_compile_data_to_byte_code(jsi, const_cast<char*>(code.data()),
00125                            code.length(), &data, &len);
00126 
00127     jsbc.len  = len;
00128     jsbc.data = new unsigned char[len];
00129 
00130     memcpy(jsbc.data, data, len);
00131 
00132     return jsbc;
00133 }
00134 
00135 void JavaScriptInterpreter::setVar(const string& name, JavaScriptType& o) {
00136     js_set_var(jsi, const_cast<char*>(name.c_str()), (JSType*)o.jsn);
00137 }
00138 
00139 JavaScriptType JavaScriptInterpreter::getVar(const string& name) {
00140     JSNode jsn;
00141 
00142     js_get_var(jsi, const_cast<char*>(name.c_str()), (JSType*)&jsn);
00143 
00144     return JavaScriptType(jsn);
00145 }
00146 
00147 JavaScriptType
00148 JavaScriptInterpreter::callMethod(JavaScriptType& ob, const string& method,
00149                   const vector<JavaScriptType>& argv) {
00150     JSNode * jsnav;
00151     int      argc  = argv.size();
00152     int      i     = 0;
00153     
00154     // OK now this is screwed up. If it's a BUILTIN (which objects we
00155     // make happen to be), then we don't pass argc as a argc, we pass
00156     // it as the _first_ argv[]. Grumble.
00157     if(ob.jsn->type == JS_BUILTIN) {
00158     jsnav = new JSNode[argc+1];
00159     jsnav[0].u.vinteger = argc;
00160     i++; argc++;
00161     } else {
00162     jsnav = new JSNode[argc];
00163     }
00164     
00165     for(; i < argc; i++)
00166     jsnav[i] = *argv[i].jsn;
00167 
00168     // Weird, you'd think they'd have a higher-level function for this.
00169     js_vm_call_method(jsi->vm, ob.jsn, const_cast<char*>(method.c_str()),
00170               argc, jsnav);
00171 
00172     delete[] jsnav;
00173     
00174     return JavaScriptType(jsi->vm->exec_result);
00175 }
00176 
00177 JSTMap JavaScriptInterpreter::objectProperties(JavaScriptType &_jst) {
00178     if(_jst.jsn->type != JS_OBJECT) {
00179     return JSTMap();
00180     }
00181 
00182     JSTMap  jstm;
00183     int     i = 0, ret = 0;
00184     JSNode *t = _jst.jsn, n, n1;
00185 
00186     ret = js_vm_object_nth(jsi->vm, t->u.vobject, i, &n);
00187 
00188     while(ret != 0) {
00189     /*** Weird and mysterious things happen if the commented
00190      *** version is used, i've left it here for future reference.
00191      ***
00192      *** Specifically, any symbol whose length is a perfect
00193      *** multiple of 2 and >=4 is not found.
00194      ***/
00195      
00196     /* APPARENTLY, to get a symbol, you use js_vm_intern,
00197        which returns the symbol if it's been interned, or makes
00198        a new one if not */
00199     /* Also note that the symbol's name is the bare name of the
00200        property, and not "x.foo" or something. */
00201     /*JSSymbol sym = js_vm_intern_with_len(jsi->vm,
00202       (const char*)n.u.vstring->data,
00203       n.u.vstring->len);*/
00204     /*js_vm_object_load_property(jsi->vm, t->u.vobject, sym, &n1);*/
00205 
00206     
00207     js_vm_object_load_array(jsi->vm, t->u.vobject, &n, &n1);
00208     jstm[string((const char*)n.u.vstring->data,
00209             (size_t)n.u.vstring->len)] = JavaScriptType(n1);
00210 
00211     i++;
00212     ret = js_vm_object_nth(jsi->vm, t->u.vobject, i, &n);
00213     }
00214 
00215     return jstm;
00216 }
00217 
00218 JSPropList JavaScriptInterpreter::listProperties(JavaScriptType &_jst) {
00219     JSPropList jstl;
00220 
00221     if(_jst.getJSType() != JS_OBJECT)
00222        return jstl;
00223     
00224     int           i = 0, ret = 0;
00225     JSNode       *t = _jst.jsn, n;
00226 
00227     ret = js_vm_object_nth(jsi->vm, t->u.vobject, i, &n);
00228 
00229     while(ret != 0) {
00230     jstl.push_back(string((const char*)n.u.vstring->data,
00231                   (size_t)n.u.vstring->len));
00232 
00233     i++;
00234     ret = js_vm_object_nth(jsi->vm, t->u.vobject, i, &n);
00235     }
00236 
00237     return jstl;
00238 }
00239 
00240 void JavaScriptInterpreter::setProperty(JavaScriptType &_o,
00241                     const JavaScriptType &_name,
00242                     JavaScriptType &_value) {
00243     /* If this is a "blank" variable, make a new object */
00244     if(_o.getJSType() == JS_TYPE_UNDEFINED) {
00245         _o.jsn->type      = JS_OBJECT;
00246         _o.jsn->u.vobject = js_vm_object_new(jsi->vm);
00247     }
00248         
00249     if(_o.getJSType() != JS_OBJECT)
00250     return;
00251 
00252     js_vm_object_store_array(jsi->vm, _o.jsn->u.vobject,
00253                  _name.jsn, _value.jsn);
00254 }
00255 
00256 JavaScriptType JavaScriptInterpreter::getProperty(JavaScriptType &_o,
00257                           const JavaScriptType &_name) {
00258     if(_o.getJSType() != JS_OBJECT)
00259     return JavaScriptType();
00260 
00261     JSNode n;
00262 
00263     js_vm_object_load_array(jsi->vm, _o.jsn->u.vobject,
00264                 _name.jsn, &n);
00265 
00266     JavaScriptType t(&n);
00267 
00268     return t; 
00269 }
00270 
00271 void JavaScriptInterpreter::delProperty(JavaScriptType &_o,
00272                     const JavaScriptType &_name) {
00273     if(_o.getJSType() != JS_OBJECT)
00274     return;
00275     
00276     js_vm_object_delete_array(jsi->vm, _o.jsn->u.vobject,
00277                   _name.jsn);
00278 }
00279 
00280 void JavaScriptInterpreter::define(JavaScriptMethod& m) {
00281     string name = m.getName();
00282 
00283     MethodContext * mc = new MethodContext;
00284     mc->jsi = this;
00285     mc->jsgm = &m;
00286     
00287     js_create_global_method(jsi, const_cast<char*>(name.c_str()),
00288                 (JSGlobalMethodProc)::callMethod,
00289                 (void*)mc, (JSFreeProc)freeContext);
00290 }
00291 
00292 void JavaScriptInterpreter::define(JavaScriptClass& jsc) {
00293     string name = jsc.getName();
00294     
00295     js_define_class(jsi, jsc.jsc, const_cast<char*>(name.c_str()));
00296 }
00297 
00298 string JavaScriptInterpreter::format(JavaScriptType &_jst) {
00299     char   buf[32];
00300     string res;
00301     
00302     switch(_jst.getJSType()) {
00303     case JS_UNDEFINED:
00304         res = "<undefined>";
00305         break;
00306 
00307     case JS_NULL:
00308         res = "null";
00309         break;
00310 
00311     case JS_BOOLEAN:
00312         res = ((bool)_jst ? "true" : "false");
00313         break;
00314 
00315     case JS_INTEGER:
00316         snprintf(buf, sizeof(buf), "%i", (int)_jst);
00317         res = buf;
00318         break;
00319 
00320     case JS_STRING:
00321         res = "\"" + (string)_jst + "\"";
00322         break;
00323 
00324     case JS_FLOAT:
00325         snprintf(buf, sizeof(buf), "%.19g", (double)_jst);
00326         res = buf;
00327         break;
00328 
00329     case JS_ARRAY:
00330         {
00331         JSTArray jsta = (JSTArray)_jst;
00332 
00333         res = "[ ";
00334         for(JSTArray::iterator i = jsta.begin(); i != jsta.end();) {
00335             res += format(*i);
00336             i++;
00337             if(i != jsta.end())
00338             res += ", ";
00339         }
00340         res += " ]";
00341         }
00342 
00343         break;
00344 
00345     case JS_OBJECT:
00346         {
00347         JSTMap jstm = objectProperties(_jst);
00348 
00349         res = "({ ";
00350         for(JSTMap::iterator i = jstm.begin(); i != jstm.end();) {
00351             res += i->first + ": " + format(i->second);
00352             i++;
00353             if(i != jstm.end())
00354             res += ", ";
00355         }
00356         res += " })";
00357         }
00358 
00359         break;
00360 
00361         case JS_SYMBOL:
00362             res = "<symbol>";
00363             break;
00364 
00365         case JS_BUILTIN:
00366             res = "<builtin>";
00367             break;
00368 
00369         case JS_FUNC:
00370             res = "<function>";
00371             break;
00372 
00373         case JS_NAN:
00374             res = "NaN";
00375             break;
00376 
00377     default:
00378         res = "<unknown>";
00379     }
00380 
00381     return res;
00382 }
00383 
00384 /**
00385  ** JavaScriptType
00386  **
00387  **/
00388 JavaScriptType::JavaScriptType() {
00389     // Default to undefined
00390     jsn       = new JSNode;
00391     jsn->type = JS_UNDEFINED;
00392     owned     = true;
00393 }
00394 
00395 JavaScriptType::JavaScriptType(const JavaScriptType& o) {
00396     jsn       = new JSNode;
00397     owned     = true;
00398     copyJSN(jsn, o.jsn);
00399 }
00400 
00401 JavaScriptType::JavaScriptType(JSNode &o, bool copy) {
00402     if(!copy) {
00403         jsn   = &o;
00404     owned = false;
00405     return;
00406     }
00407 
00408     jsn       = new JSNode;
00409     owned     = true;
00410     copyJSN(jsn, &o);
00411 }
00412 
00413 JavaScriptType::JavaScriptType(const string& s) {
00414     jsn                       = new JSNode;
00415     jsn->type                 = JS_STRING;
00416     jsn->u.vstring            = new JSString;
00417     jsn->u.vstring->staticp   = 0;
00418     jsn->u.vstring->data      = new unsigned char[s.length()];
00419     jsn->u.vstring->len       = s.length();
00420     jsn->u.vstring->prototype = NULL;
00421     
00422     owned                     = true;
00423 
00424     s.copy((char*)jsn->u.vstring->data, s.length());
00425 }
00426 
00427 JavaScriptType::JavaScriptType(int i) {
00428     jsn             = new JSNode;
00429     jsn->type       = JS_INTEGER;
00430     jsn->u.vinteger = i;
00431     owned           = true;
00432 }
00433 
00434 JavaScriptType::JavaScriptType(bool b) {
00435     jsn             = new JSNode;
00436     jsn->type       = JS_BOOLEAN;
00437     jsn->u.vboolean = b;
00438     owned           = true;
00439 }
00440 
00441 JavaScriptType::JavaScriptType(double d) {
00442     jsn           = new JSNode;
00443     jsn->type     = JS_FLOAT;
00444     jsn->u.vfloat = d;
00445     owned         = true;
00446 }
00447 
00448 JavaScriptType::JavaScriptType(const JSTArray &_a) {
00449     jsn           = new JSNode;
00450     jsn->type     = JS_ARRAY;
00451     jsn->u.varray = new JSArray;
00452     owned         = true;
00453 
00454     jsn->u.varray->length    = _a.size();
00455     jsn->u.varray->data      = new JSNode[_a.size()];
00456     jsn->u.varray->prototype = NULL;
00457 
00458     for(int i = 0; i < _a.size(); i++)
00459     copyJSN(&jsn->u.varray->data[i], _a[i].jsn);
00460 }
00461 
00462 void delJSN(JSNode *jsn) {
00463     if(jsn->type == JS_STRING) {
00464     delete jsn->u.vstring->data;
00465     delete jsn->u.vstring;
00466     } else if (jsn->type == JS_ARRAY) {
00467     for(int i = 0; i < jsn->u.varray->length; i++)
00468         delJSN(&jsn->u.varray->data[i]);
00469 
00470     delete jsn->u.varray->data;
00471         delete jsn->u.varray;
00472     }
00473 }
00474 
00475 JavaScriptType::~JavaScriptType() {
00476     if(owned) {
00477     delJSN(jsn);
00478     delete jsn;
00479     }
00480 }
00481 
00482 int JavaScriptType::getJSType(void) {
00483     return jsn->type;
00484 }
00485 
00486 bool JavaScriptType::copyToI(JSType * t, JSInterpPtr i) const {
00487     if(!t) return false;
00488     
00489     if(jsn->type == JS_STRING)
00490     js_type_make_string(i, t, jsn->u.vstring->data,
00491                             jsn->u.vstring->len);
00492     else
00493     *t = *(JSType*)jsn;
00494 
00495     return true;
00496 }
00497 
00498 JavaScriptType& JavaScriptType::operator= (const JavaScriptType& o) {
00499     delJSN(jsn);
00500     copyJSN(jsn, o.jsn);
00501     return *this;
00502 }
00503 
00504 JavaScriptType::operator bool() {
00505     if(jsn->type != JS_INTEGER &&
00506        jsn->type != JS_BOOLEAN)
00507     return false;
00508 
00509     return (bool)jsn->u.vboolean;
00510 }
00511 
00512 JavaScriptType::operator string() {
00513     if(jsn->type != JS_STRING) {
00514     return (string)"";
00515     }
00516 
00517     return string((const char*)jsn->u.vstring->data,
00518                   (size_t)jsn->u.vstring->len);
00519 }
00520 
00521 JavaScriptType::operator int() {
00522     if(jsn->type != JS_INTEGER) {
00523     return 0;
00524     }
00525 
00526     return jsn->u.vinteger;
00527 }
00528 
00529 JavaScriptType::operator double() {
00530     if(jsn->type != JS_FLOAT) {
00531     return 0.0;
00532     }
00533 
00534     return jsn->u.vfloat;
00535 }
00536 
00537 JavaScriptType::operator JSTArray() {
00538     if(jsn->type != JS_ARRAY) {
00539     return JSTArray(0);
00540     }
00541     
00542     JSTArray jsta(jsn->u.varray->length);
00543 
00544     for(int i = 0; i < jsn->u.varray->length; i++)
00545     jsta[i] = JavaScriptType(jsn->u.varray->data[i], true);
00546 
00547     return jsta;
00548 }
00549 
00550 /**
00551  ** JavaScriptByteCode
00552  **
00553  **/
00554 JavaScriptByteCode::JavaScriptByteCode() {
00555     len  = 0;
00556     data = 0;
00557 }
00558 
00559 JavaScriptByteCode::JavaScriptByteCode(const JavaScriptByteCode &_jsbc) {
00560     len  = _jsbc.len;
00561     data = new unsigned char[len];
00562 
00563     memcpy(data, _jsbc.data, len);
00564 }
00565 
00566 JavaScriptByteCode::~JavaScriptByteCode() {
00567     if(data)
00568     delete data;
00569 }
00570 
00571 unsigned int JavaScriptByteCode::getLength() {
00572     return len;
00573 }
00574 
00575 JavaScriptByteCode& JavaScriptByteCode::operator= (const JavaScriptByteCode &_jsbc) {
00576     if(data)
00577     delete data;
00578 
00579     len  = _jsbc.len;
00580     data = new unsigned char[len];
00581 
00582     memcpy(data, _jsbc.data, len);
00583     
00584     return *this;
00585 }
00586 
00587 /**
00588  ** JavaScriptClass
00589  **
00590  **/
00591 JavaScriptClass::JavaScriptClass(const string& s) {
00592     name = s;
00593     jsc  = 0;
00594 }
00595 
00596 JavaScriptClass::~JavaScriptClass() {
00597     if(jsc)
00598     js_class_destroy(jsc);
00599 }
00600 
00601 const string& JavaScriptClass::getName(void) const {
00602     return name;
00603 }
00604 
00605 JavaScriptType JavaScriptClass::instantiate(JavaScriptInterpreter &jsi,
00606                                             void *ctx,
00607                                             JSFreeProc destructor) {
00608     JSNode jsn;
00609     js_instantiate_class(jsi.jsi, jsc, ctx, destructor, (JSType*)&jsn);
00610     return JavaScriptType(jsn);
00611 }
00612 
00613 /**
00614  ** JavaScriptMethod
00615  **
00616  **/
00617 JavaScriptMethod::JavaScriptMethod(const string& s) {
00618     name = s;
00619 }
00620 
00621 
00622 const string JavaScriptMethod::getName() {
00623     return name;
00624 }
00625 
00626 /**
00627  ** Various functions
00628  **
00629  **/
00630 
00631 static JSMethodResult callMethod(void *context,
00632                  JSInterpPtr interp, int argc,
00633                  JSType *argv, JSType *return_result,
00634                  char *error_return) {
00635     MethodContext   * mc    = (MethodContext*)context;
00636     vector<JavaScriptType> jstav(argc);
00637     JavaScriptType ret;
00638     string error;
00639     bool res;
00640 
00641     for(int i = 0; i < argc; i++)
00642     jstav[i] = static_cast<JavaScriptType>(*(JSNode*)&argv[i]);
00643 
00644     res = mc->jsgm->proc(jstav, ret, error);
00645 
00646     error.copy(error_return, error.length());
00647     error_return[error.length()] = (char)0;
00648 
00649     return (res ? JS_OK : JS_ERROR);
00650 }
00651 
00652 static void freeContext(void *context) {
00653     delete (MethodContext*)context;
00654 }
00655 
00656 static void copyJSN(JSNode * dest, JSNode * src) {
00657     dest->type  = src->type;
00658 
00659     switch(src->type) {
00660         case JS_TYPE_BOOLEAN:
00661     case JS_TYPE_INTEGER:
00662         dest->u.vinteger = src->u.vinteger;
00663         break;
00664 
00665     case JS_TYPE_STRING:
00666         dest->u.vstring       = new JSString;
00667         dest->u.vstring->data = new unsigned char[src->u.vstring->len];
00668         dest->u.vstring->len  = src->u.vstring->len;
00669             dest->u.vstring->prototype = src->u.vstring->prototype;
00670         memcpy(dest->u.vstring->data,
00671                    src->u.vstring->data,
00672                    dest->u.vstring->len);
00673         break;
00674 
00675     case JS_TYPE_DOUBLE:
00676         dest->u.vfloat = src->u.vfloat;
00677         break;
00678 
00679     case JS_TYPE_ARRAY:
00680         dest->u.varray         = new JSArray;
00681         dest->u.varray->length = src->u.varray->length;
00682         dest->u.varray->data   = new JSNode[src->u.varray->length];
00683             dest->u.varray->prototype = src->u.varray->prototype;
00684         
00685         for(int i = 0; i < dest->u.varray->length; i++)
00686         copyJSN(&dest->u.varray->data[i], &src->u.varray->data[i]);
00687         
00688         break;
00689 
00690         case JS_OBJECT:
00691             /* Not sure if this is entirely safe
00692              * but it should be, since the VM is doing
00693              * the memory management. */
00694             dest->u.vobject = src->u.vobject;
00695             break;
00696 
00697     default:
00698         memcpy((void*)dest, (void*)src, sizeof(JSNode));
00699         break;
00700     }
00701 }

Generated at Tue Jan 2 15:38:33 2001 for SFW by doxygen1.2.4 written by Dimitri van Heesch, © 1997-2000