script_ecma.C

Go to the documentation of this file.
00001 /*
00002  * This file is part of the "Archon" framework.
00003  * (http://files3d.sourceforge.net)
00004  *
00005  * Copyright © 2002 by Kristian Spangsege and Brian Kristiansen.
00006  *
00007  * Permission to use, copy, modify, and distribute this software and
00008  * its documentation under the terms of the GNU General Public License is
00009  * hereby granted. No representations are made about the suitability of
00010  * this software for any purpose. It is provided "as is" without express
00011  * or implied warranty. See the GNU General Public License
00012  * (http://www.gnu.org/copyleft/gpl.html) for more details.
00013  *
00014  * The characters in this file are ISO8859-1 encoded.
00015  *
00016  * The documentation in this file is in "Doxygen" style
00017  * (http://www.doxygen.org).
00018  */
00019 
00063 #include <archon/x3d/server/field_type.H>
00064 #include <archon/x3d/server/dump.H>
00065 #include <archon/x3d/server/script_ecma.H>
00066 #include <archon/x3d/server/server.H>
00067 
00068 namespace Archon
00069 {
00070   namespace X3D
00071   {
00072     namespace ECMA
00073     {
00074       namespace
00075       {
00076         void printTypeOfJsval(JSContext *c, jsval v)
00077         {
00078           switch(JS_TypeOfValue(c, v))
00079           {
00080           case JSTYPE_VOID: cerr << "VOID\n"; break;
00081           case JSTYPE_OBJECT: cerr << "OBJECT\n"; break;
00082           case JSTYPE_FUNCTION: cerr << "FUNCTION\n"; break;
00083           case JSTYPE_STRING: cerr << "STRING\n"; break;
00084           case JSTYPE_NUMBER: cerr << "NUMBER\n"; break;
00085           case JSTYPE_BOOLEAN: cerr << "BOOLEAN\n"; break;
00086           case JSTYPE_LIMIT: cerr << "LIMIT\n"; break;
00087           }
00088         }
00089 
00090         /*
00091         JSBool global_myFunc(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00092         {
00093           jsdouble x, y;
00094 
00095           if(!JS_ValueToNumber(context, argv[0], &x)) return JS_FALSE;
00096           y = fabs(x);
00097           cerr << "myFunc(" << x << ") = " << y << "\n";
00098           return JS_NewDoubleValue(context, y, rval);
00099         }
00100         */
00101 
00102         JSBool browser_print(JSContext *c, JSObject *o, uintN n, jsval *v, jsval *r)
00103         {
00104           for(unsigned i=0; i<n; ++i)
00105           {
00106             JSString *s = JS_ValueToString(c, v[i]);
00107             if(!s) return JS_FALSE;
00108             if(i) cerr << " ";
00109             cerr << JS_GetStringBytes(s);
00110           }
00111           return JS_TRUE;
00112         }
00113 
00114         JSFunctionSpec globalMethods[] =
00115         {
00116           { "print", browser_print, 0 },
00117           { 0 }
00118         };
00119 
00120         JSFunctionSpec browserMethods[] =
00121         {
00122           { "print", browser_print, 0 },
00123           { 0 }
00124         };
00125 
00126         JSBool globalResolveOp(JSContext *, JSObject *, jsval);
00127 
00128         JSClass globalClass =
00129         {
00130           "Global", 0,
00131           JS_PropertyStub,  JS_PropertyStub,JS_PropertyStub, JS_PropertyStub,
00132           JS_EnumerateStub, /*globalResolveOp*/JS_ResolveStub, JS_ConvertStub,  JS_FinalizeStub
00133         };
00134 
00135         JSClass browserClass =
00136         {
00137           "Browser", 0,
00138           JS_PropertyStub,  JS_PropertyStub,JS_PropertyStub, JS_PropertyStub,
00139           JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub,  JS_FinalizeStub
00140         };
00141 
00142 
00143         void toJsval(JSContext *, bool v, jsval *r)
00144         {
00145           *r = v ? JSVAL_TRUE : JSVAL_FALSE;
00146         }
00147 
00148         bool fromJsval(JSContext *c, jsval v, bool *r)
00149         {
00150           JSBool w;
00151           if(!JS_ValueToBoolean(c, v, &w)) return false;
00152           *r = w;
00153           return true;
00154         }
00155 
00156 
00157         void toJsval(JSContext *c, double v, jsval *r)
00158         {
00159           if(!JS_NewDoubleValue(c, v, r))
00160             ARCHON_THROW1(InternalException,
00161                           "ECMA::toJsval: Allocation error");
00162         }
00163 
00164         bool fromJsval(JSContext *c, jsval v, double *r)
00165         {
00166           jsdouble w;
00167           if(!JS_ValueToNumber(c, v, &w)) return false;
00168           *r = w;
00169           return true;
00170         }
00171 
00172 
00173         void toJsval(JSContext *c, int v, jsval *r)
00174         {
00175           if(!JS_NewDoubleValue(c, v, r))
00176             ARCHON_THROW1(InternalException,
00177                           "ECMA::toJsval: Allocation error");
00178         }
00179 
00180         bool fromJsval(JSContext *c, jsval v, int *r)
00181         {
00182           if(!JS_ValueToInt32(c, v, r)) return false;
00183           return true;
00184         }
00185 
00186 
00187         void toJsval(JSContext *c, string v, jsval *r)
00188         {
00189           JSString *s = JS_NewStringCopyZ(c, v.c_str());
00190           if(!s) ARCHON_THROW1(InternalException,
00191                                "ECMA::toJsval: Allocation error");
00192           *r = STRING_TO_JSVAL(s);
00193         }
00194 
00195         bool fromJsval(JSContext *c, jsval v, string *r)
00196         {
00197           JSString *s =
00198             JSVAL_IS_STRING(v) ? JSVAL_TO_STRING(v) : JS_ValueToString(c, v);
00199           if(!s) ARCHON_THROW1(InternalException,
00200                                "ECMA::fromJsval: Allocation error");
00201           *r = string(JS_GetStringBytes(s));
00202           return true;
00203         }
00204 
00205 
00206         void toJsval(JSContext *c, Time v, jsval *r)
00207         {
00208           long double w;
00209           v.get(w);
00210           if(!JS_NewDoubleValue(c, w, r))
00211             ARCHON_THROW1(InternalException,
00212                           "ECMA::toJsval: Allocation error");
00213         }
00214 
00215         bool fromJsval(JSContext *c, jsval v, Time *r)
00216         {
00217           jsdouble w;
00218           if(!JS_ValueToNumber(c, v, &w)) return false;
00219           *r = Time(w);
00220           return true;
00221         }
00222 
00223 
00224 
00225         template<typename T>
00226         struct SimpleValueConverter: Engine::ValueConverterBase
00227         {
00228           bool fieldWrite(JSContext *c, jsval v, NodeBase *n,
00229                           const FieldBase *f, Time t) const
00230           {
00231             T w;
00232             if(!fromJsval(c, v, &w)) return false;
00233             Ref<SimpleValue<T> > u = new SimpleValue<T>(f->getType(), w);
00234             Event e(u.get(), t);
00235             f->set(n, &e, true);
00236             return true;
00237           }
00238 
00239           void fieldRead(NodeBase *n, const FieldBase *f,
00240                          JSContext *c, jsval *r) const
00241           {
00242             Ref<ValueBase> v = f->get(n);
00243             const SimpleValue<T> *w =
00244               dynamic_cast<const SimpleValue<T> *>(v.get());
00245             if(!w) ARCHON_THROW1(InternalException, "Illegal value type");
00246             toJsval(c, w->value, r);
00247           }
00248         };
00249 
00250         struct NodeContext
00251         {
00252           Ref<NodeBase> n;
00253           vector<const FieldBase *> fields;
00254           NodeContext(Ref<NodeBase> n): n(n) {}
00255         };
00256 
00257         JSBool nodeResolveOp(JSContext *, JSObject *, jsval);
00258         void nodeFinalizeOp(JSContext *, JSObject *);
00259 
00260         JSClass nodeClass = 
00261         {
00262           "Node", JSCLASS_HAS_PRIVATE,
00263           JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
00264           JS_EnumerateStub, nodeResolveOp, JS_ConvertStub, nodeFinalizeOp
00265         };
00266 
00267         struct NodeValueConverter: Engine::ValueConverterBase
00268         {
00269           bool fieldWrite(JSContext *c, jsval v, NodeBase *n,
00270                           const FieldBase *f, Time t) const
00271           {
00272             Ref<NodeBase> w;
00273             if(JSVAL_IS_NULL(v));
00274             else if(JSVAL_IS_OBJECT(v))
00275             {
00276               JSObject *o = JSVAL_TO_OBJECT(v);
00277               if(!JS_InstanceOf(c, o, &nodeClass, 0)) return false;
00278               NodeContext *nc = static_cast<NodeContext *>(JS_GetPrivate(c, o));
00279               if(!nc)
00280                 ARCHON_THROW1(InternalException,
00281                               "ECMA::NodeValueConverter::fieldWrite: "
00282                               "Node object had no context");
00283               w = nc->n;
00284             }
00285             else return false;
00286             Ref<NodeValue> u = new NodeValue(SFNode::type, w.get());
00287             Event e(u.get(), t);
00288             f->set(n, &e, true);
00289             return true;
00290           }
00291 
00292 
00299           void fieldRead(NodeBase *n, const FieldBase *f,
00300                          JSContext *c, jsval *r) const
00301           {
00302             Ref<ValueBase> v = f->get(n);
00303             const NodeValue *w = dynamic_cast<const NodeValue *>(v.get());
00304             if(!w) ARCHON_THROW1(InternalException, "Illegal value type");
00305             if(!w->value)
00306             {
00307               *r = JSVAL_NULL;
00308               return;
00309             }
00310             JSObject *o = JS_NewObject(c, &nodeClass, 0, 0);
00311             if(!o)
00312               ARCHON_THROW1(InternalException,
00313                             "ECMA::NodeValueConverter::fieldRead: "
00314                             "Could not create ECMA object");
00315             NodeContext *nc = new NodeContext(w->value);
00316             if(!JS_SetPrivate(c, o, nc))
00317             {
00318               delete nc;
00319               ARCHON_THROW1(InternalException,
00320                             "ECMA::NodeValueConverter::fieldRead: "
00321                             "Could not set private object data");
00322             }
00323             *r = OBJECT_TO_JSVAL(o);
00324           }
00325         };
00326 
00327         struct ValueConverterMap
00328         {
00329           vector<const Engine::ValueConverterBase *> m;
00330           ValueConverterMap()
00331           {
00332             m.resize(fieldTypeIndexRoof, 0);
00333 
00334             m[SFBool::index]      = new SimpleValueConverter<bool>;
00335             //m[MFBool::index]      = new SimpleValueConverter<vector<bool> >;
00336 
00337             /*
00338             m[SFColor::index]     = new SimpleValueConverter<Vector3>;
00339             m[MFColor::index]     = new SimpleValueConverter<vector<Vector3> >;
00340 
00341             m[SFColorRGBA::index] = new SimpleValueConverter<Vector4>;
00342             m[MFColorRGBA::index] = new SimpleValueConverter<vector<Vector4> >;
00343             */
00344 
00345             m[SFDouble::index]    = new SimpleValueConverter<double>;
00346             //m[MFDouble::index]    = new SimpleValueConverter<vector<double> >;
00347 
00348             m[SFFloat::index]     = new SimpleValueConverter<double>;
00349             //m[MFFloat::index]     = new SimpleValueConverter<vector<double> >;
00350 
00351             /*
00352             m[SFImage::index]     = new SimpleValueConverter<Image>;
00353             m[MFImage::index]     = new SimpleValueConverter<vector<Image> >;
00354             */
00355 
00356             m[SFInt32::index]     = new SimpleValueConverter<int>;
00357             //m[MFInt32::index]     = new SimpleValueConverter<vector<int> >;
00358 
00359             m[SFNode::index]      = new NodeValueConverter;
00360             //m[MFInt32::index]     = new SimpleValueConverter<vector<int> >;
00361 
00362             /*
00363             m[SFRotation::index]  = new SimpleValueConverter<AxisRot3>;
00364             m[MFRotation::index]  = new SimpleValueConverter<vector<AxisRot3> >;
00365             */
00366 
00367             m[SFString::index]    = new SimpleValueConverter<string>;
00368             //m[MFString::index]    = new SimpleValueConverter<vector<string> >;
00369 
00370             m[SFTime::index]      = new SimpleValueConverter<Time>;
00371             //m[MFTime::index]      = new SimpleValueConverter<vector<Time> >;
00372 
00373             /*
00374             m[SFVec2d::index]     = new SimpleValueConverter<Vector2>;
00375             m[MFVec2d::index]     = new SimpleValueConverter<vector<Vector2> >;
00376 
00377             m[SFVec2f::index]     = new SimpleValueConverter<Vector2>;
00378             m[MFVec2f::index]     = new SimpleValueConverter<vector<Vector2> >;
00379 
00380             m[SFVec3d::index]     = new SimpleValueConverter<Vector3>;
00381             m[MFVec3d::index]     = new SimpleValueConverter<vector<Vector3> >;
00382 
00383             m[SFVec3f::index]     = new SimpleValueConverter<Vector3>;
00384             m[MFVec3f::index]     = new SimpleValueConverter<vector<Vector3> >;
00385             */
00386           }
00387         };
00388 
00389         const Engine::ValueConverterBase *getValueConverter(const FieldType *t)
00390         {
00391           static ValueConverterMap m;
00392           return m.m[t->getIndex()];
00393         }
00394 
00395 
00396 
00397         struct Invocation
00398         {
00399           Engine *engine;
00400           Time timestamp;
00401           Invocation(Engine *engine, Time timestamp):
00402             engine(engine), timestamp(timestamp) {}
00403         };
00404 
00405         JSBool sfColorConstructOp(JSContext *c, JSObject *o, uintN n, jsval *v, jsval *r)
00406         {
00407           cerr << "CONSTRUCT(" << n << ")\n";
00408           return JS_TRUE;
00409         }
00410 
00411         JSClass sfColorClass = 
00412         {
00413           "SFColor", 0,
00414           JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
00415           JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
00416           0, 0, 0, sfColorConstructOp
00417         };
00418 
00419         JSBool globalResolveOp(JSContext *c, JSObject *o, jsval i)
00420         {
00421           if(!JSVAL_IS_STRING(i))
00422             ARCHON_THROW1(InternalException,
00423                           "ECMA::nodeResolveOp: "
00424                           "property ID was not a string");
00425           string n = JS_GetStringBytes(JSVAL_TO_STRING(i));
00426           cerr << "(" << n << ")\n";
00427           if(n == "SFColor")
00428           {
00429             //      JSObject *o2 =
00430               JS_DefineObject(c, o, "SFColor", &sfColorClass, 0, JSPROP_ENUMERATE);
00431             //JS_DefineFunctions(spiderMonkeyContext, browserObject, browserMethods);
00432           }
00433           return true;
00434         }
00435 
00436 
00437 
00438         Ref<CustomFieldBase> fetchCustomField(JSContext *c, jsval i,
00439                                               Engine::Field **f = 0,
00440                                               Script **s = 0,
00441                                               Time *t = 0)
00442         {
00443           const Invocation *invocation =
00444             static_cast<const Invocation *>(JS_GetContextPrivate(c));
00445           if(!invocation)
00446             ARCHON_THROW1(InternalException,
00447                           "ECMA::fetchCustomField: "
00448                           "No bound invocation");
00449           Engine *engine = invocation->engine;
00450           Script *containingNode = engine->containingNode;
00451           if(!JSVAL_IS_INT(i))
00452             ARCHON_THROW1(InternalException,
00453                           "ECMA::fetchCustomField: "
00454                           "Unexpected type of property ID");
00455           int j = JSVAL_TO_INT(i);
00456           int n = containingNode->getNumberOfCustomFields();
00457           if(j<0 || j>n)
00458             ARCHON_THROW1(InternalException,
00459                           "ECMA::fetchCustomField: "
00460                           "Property ID out of range");
00461           if(f) *f = engine->getField(j);
00462           if(s) *s = containingNode;
00463           if(t) *t = invocation->timestamp;
00464           return containingNode->getCustomFieldNoLock(j);
00465         }
00466 
00467         void errorReporter(JSContext *context, const char *message,
00468                            JSErrorReport *report)
00469         {
00470           const Invocation *invocation =
00471             static_cast<const Invocation *>(JS_GetContextPrivate(context));
00472           if(!invocation)
00473             ARCHON_THROW1(InternalException,
00474                           "ECMA::errorReporter: "
00475                           "No bound invocation");
00476           Logger *logger =
00477             invocation->engine->containingNode->context->server->getLogger();
00478           if(report)
00479             logger->log(invocation->engine->getSourceName() + ":" +
00480                         Text::toString(report->lineno) + ": " + message);
00481           else
00482             logger->log(invocation->engine->getSourceName() + ": " + message);
00483         }
00484 
00485         JSBool setPropertyNo(JSContext *c, JSObject *, jsval i, jsval *)
00486         {
00487           Ref<CustomFieldBase> f = fetchCustomField(c, i);
00488           string m = "AccessError: " + f->getName() + " is not writable";
00489           JS_ReportError(c, "%s", m.c_str());
00490           return JS_FALSE;
00491         }
00492 
00493         JSBool getPropertyNo(JSContext *c, JSObject *, jsval i, jsval *)
00494         {
00495           Ref<CustomFieldBase> f = fetchCustomField(c, i);
00496           string m = "Access error: " + f->getName() + " is not readable";
00497           JS_ReportError(c, "%s", m.c_str());
00498           return JS_FALSE;
00499         }
00500 
00501 
00502 
00503 
00504         const FieldBase *fetchNodeField(JSContext *c, JSObject *o,
00505                                         jsval i, NodeBase **n)
00506         {
00507           NodeContext *nc = static_cast<NodeContext *>(JS_GetPrivate(c, o));
00508           if(!nc)
00509             ARCHON_THROW1(InternalException,
00510                           "ECMA::fetchNodeField: "
00511                           "Object had no context");
00512           if(!JSVAL_IS_INT(i))
00513             ARCHON_THROW1(InternalException,
00514                           "ECMA::fetchNodeField: "
00515                           "Unexpected type of property ID");
00516           int j = JSVAL_TO_INT(i);
00517           if(j < 0 || unsigned(j) > nc->fields.size())
00518             ARCHON_THROW1(InternalException,
00519                           "ECMA::fetchNodeField: "
00520                           "Property ID out of range");
00521           if(n) *n = nc->n.get();
00522           return nc->fields[j];
00523         }
00524 
00525         JSBool getNodeField(JSContext *c, JSObject *o, jsval i, jsval *v)
00526         {
00527           NodeBase *n;
00528           const FieldBase *f = fetchNodeField(c, o, i, &n);
00529           getValueConverter(f->getType())->fieldRead(n, f, c, v);
00530           return JS_TRUE;
00531         }
00532 
00533         JSBool setNodeField(JSContext *c, JSObject *o, jsval i, jsval *v)
00534         {
00535           const Invocation *invocation =
00536             static_cast<const Invocation *>(JS_GetContextPrivate(c));
00537           if(!invocation)
00538             ARCHON_THROW1(InternalException,
00539                           "ECMA::setNodeField: "
00540                           "No bound invocation");
00541           NodeBase *n;
00542           const FieldBase *f = fetchNodeField(c, o, i, &n);
00543           return getValueConverter(f->getType())->
00544             fieldWrite(c, *v, n, f, invocation->timestamp) ?
00545             JS_TRUE : JS_FALSE;
00546         }
00547 
00554         JSBool nodeResolveOp(JSContext *c, JSObject *o, jsval i)
00555         {
00556           const Invocation *invocation =
00557             static_cast<const Invocation *>(JS_GetContextPrivate(c));
00558           if(!invocation)
00559             ARCHON_THROW1(InternalException,
00560                           "ECMA::nodeResolveOp: "
00561                           "No bound invocation");
00562           NodeContext *nc = static_cast<NodeContext *>(JS_GetPrivate(c, o));
00563           if(!nc)
00564             ARCHON_THROW1(InternalException,
00565                           "ECMA::nodeResolveOp: "
00566                           "Object had no context");
00567           if(!JSVAL_IS_STRING(i))
00568             ARCHON_THROW1(InternalException,
00569                           "ECMA::nodeResolveOp: "
00570                           "property ID was not a string");
00571           string n = JS_GetStringBytes(JSVAL_TO_STRING(i));
00572           const FieldBase *f = nc->n->getType()->lookupField(n);
00573           if(!f) return true;
00574           int j = nc->fields.size();
00575           if(j==255) ARCHON_THROW1(InternalException,
00576                                    "ECMA::nodeResolveOp: "
00577                                    "Too many fields in node");
00578           bool writable = 
00579             invocation->engine->containingNode->getDirectOutput() &&
00580             f->getIsEventTarget();
00581           bool readable = f->getIsEventSource();
00582           if(!JS_DefinePropertyWithTinyId(c, o, n.c_str(), j,
00583                                           JSVAL_NULL,
00584                                           readable ? getNodeField : getPropertyNo,
00585                                           writable ? setNodeField : setPropertyNo,
00586                                           JSPROP_ENUMERATE|JSPROP_PERMANENT))
00587             ARCHON_THROW1(InternalException,
00588                           "ECMA::nodeResolveOp: "
00589                           "Could not define field as global property");
00590           nc->fields.push_back(f);
00591           return true;
00592         }
00593 
00594         void nodeFinalizeOp(JSContext *c, JSObject *o)
00595         {
00596           NodeContext *nc = static_cast<NodeContext *>(JS_GetPrivate(c, o));
00597           if(!nc)
00598             ARCHON_THROW1(InternalException,
00599                           "ECMA::nodeFinalizeOp: "
00600                           "Object had no context");
00601           delete nc;
00602         }
00603       }
00604 
00605       JSBool Engine::getCustomField(JSContext *c, JSObject *, jsval i, jsval *v)
00606       {
00607         Field *g;
00608         Script *s;
00609         Ref<CustomFieldBase> f = fetchCustomField(c, i, &g, &s);
00610         g->valueConverter->fieldRead(s, f.get(), c, v);
00611         return JS_TRUE;
00612       }
00613 
00614       JSBool Engine::setCustomField(JSContext *c, JSObject *, jsval i, jsval *v)
00615       {
00616         Field *g;
00617         Script *s;
00618         Time t;
00619         Ref<CustomFieldBase> f = fetchCustomField(c, i, &g, &s, &t);
00620         return g->valueConverter->
00621           fieldWrite(c, *v, s, f.get(), t) ? JS_TRUE : JS_FALSE;
00622       }
00623 
00624       Engine::Engine(Script *containingNode, string sourceCode,
00625                      string sourceName, int sourceLine):
00626         containingNode(containingNode),
00627         sourceName(sourceName)
00628       {
00629         // Construct an engine
00630         spiderMonkeyRuntime = JS_Init(1000000L);
00631         if(!spiderMonkeyRuntime)
00632           ARCHON_THROW1(InternalException,
00633                         "ECMA::Engine::Engine: Can't create "
00634                         "SpiderMonkey runtime");
00635 
00636         spiderMonkeyContext = JS_NewContext(spiderMonkeyRuntime, 8192);
00637         if(!spiderMonkeyContext)
00638           ARCHON_THROW1(InternalException,
00639                         "ECMA::Engine::Engine: Can't create "
00640                         "SpiderMonkey context");
00641 
00642         // Bind to the context
00643         Invocation invocation(this, containingNode->
00644                               context->server->getNextTimeStamp());
00645         JS_SetContextPrivate(spiderMonkeyContext, &invocation);
00646 
00647         JS_SetErrorReporter(spiderMonkeyContext, errorReporter);
00648 
00649         // Define standard ECMA classes
00650         JSObject *globalObject =
00651           JS_NewObject(spiderMonkeyContext, &globalClass, 0, 0);
00652         JS_InitStandardClasses(spiderMonkeyContext, globalObject);
00653         JS_DefineFunctions(spiderMonkeyContext, globalObject, globalMethods);
00654 
00655         // Define X3D SAI classes
00656         JSObject *browserObject =
00657           JS_DefineObject(spiderMonkeyContext, globalObject, "Browser",
00658                           &browserClass, 0, JSPROP_ENUMERATE);
00659         JS_DefineFunctions(spiderMonkeyContext, browserObject, browserMethods);
00660 
00661         // Define custom Script fields
00662         unsigned n = containingNode->getNumberOfCustomFields();
00663         if(n>255) ARCHON_THROW1(InternalException,
00664                                 "ECMA::Engine::Engine: "
00665                                 "Too many custom fields");
00666         fields.reserve(n);
00667         for(unsigned i=0; i<n; ++i)
00668         {
00669           Ref<CustomFieldBase> f = containingNode->getCustomFieldNoLock(i);
00670           bool writable = f->getIsEventSource() || !f->getIsEventTarget();
00671           bool readable = !f->getIsEventSource() || f->getIsEventTarget();
00672           if(!JS_DefinePropertyWithTinyId(spiderMonkeyContext,
00673                                           globalObject, f->getName().c_str(),
00674                                           i, JSVAL_NULL,
00675                                           readable ? getCustomField : getPropertyNo,
00676                                           writable ? setCustomField : setPropertyNo,
00677                                           JSPROP_ENUMERATE|JSPROP_PERMANENT))
00678             ARCHON_THROW1(InternalException,
00679                           "ECMA::Engine::Engine: "
00680                           "Could not define field as global property");
00681           fields.push_back(Field(getValueConverter(f->getType())));
00682         }
00683 
00684         // Evaluate the script
00685         jsval v;
00686         uintN lineno = sourceLine < 1 ? 1 : sourceLine;
00687         JS_EvaluateScript(spiderMonkeyContext, globalObject,
00688                           sourceCode.c_str(), sourceCode.size(),
00689                           sourceName.c_str(), lineno, &v);
00690         /*
00691           cerr << "ECMA Engine created from " << sourceName << ":" << sourceLine << "\n";
00692         else cerr << "ECMA Engine failed to evaluate script from " << sourceName << ":" << sourceLine << "\n";
00693         */
00694 
00695         // Map readable fields to methods
00696         for(unsigned i=0; i<n; ++i)
00697         {
00698           Ref<CustomFieldBase> f = containingNode->getCustomFieldNoLock(i);
00699           if(f->getIsEventSource() && !f->getIsEventTarget()) continue;
00700           if(!JS_LookupProperty(spiderMonkeyContext,
00701                                 globalObject, f->getName().c_str(), &v))
00702             ARCHON_THROW1(InternalException,
00703                           "ECMA::Engine::Engine: "
00704                           "JS_LookupProperty failed");
00705           if(JS_TypeOfValue(spiderMonkeyContext, v) != JSTYPE_FUNCTION)
00706             continue;
00707           jsval &h = fields[i].handler;
00708           h = v;
00709           /*
00710            * NOTE: It is very important that the 'fields' vector is
00711            * not resized from this point on as we store the address to
00712            * its elements with the SpiderMonkey garbage collector.
00713            */
00714           JS_AddRoot(spiderMonkeyContext, &h);
00715         }
00716 
00717         // Check for the existance of an 'initialize' method
00718         if(!JS_LookupProperty(spiderMonkeyContext,
00719                              globalObject, "initialize", &v))
00720           ARCHON_THROW1(InternalException,
00721                         "ECMA::Engine::Engine: "
00722                         "JS_LookupProperty failed");
00723         if(JS_TypeOfValue(spiderMonkeyContext, v) == JSTYPE_FUNCTION)
00724         {
00725           jsval r;
00726           JS_CallFunctionValue(spiderMonkeyContext, globalObject, v, 0, 0, &r);
00727         }
00728 
00729         // Dissociate from the context
00730         JS_SetContextPrivate(spiderMonkeyContext, 0);
00731       }
00732 
00733       Engine::~Engine()
00734       {{ // The extra scope is needed to work around gcc3.2 bug #8287
00735         for(unsigned i=0; i<containingNode->getNumberOfCustomFields(); ++i)
00736         {
00737           jsval &h = fields[i].handler;
00738           if(JSVAL_IS_NULL(h)) continue;
00739           JS_RemoveRoot(spiderMonkeyContext, &h);
00740         }
00741 
00742         JS_DestroyContext(spiderMonkeyContext);
00743         JS_Finish(spiderMonkeyRuntime);
00744         //cerr << "ECMA Engine destroyed\n";
00745       }}
00746 
00752       void Engine::handleEvent(int i, Time timestamp)
00753       {
00754         // Bind to the context
00755         Invocation invocation(this, containingNode->
00756                               context->server->getNextTimeStamp());
00757         JS_SetContextPrivate(spiderMonkeyContext, &invocation);
00758 
00759         int n = fields.size();
00760         if(i<0 || i>n)
00761           ARCHON_THROW1(InternalException,
00762                         "ECMA::Engine::handleEvent: "
00763                         "fieldIndex out of range");
00764         jsval handler = fields[i].handler;
00765         if(JSVAL_IS_NULL(handler)) return;
00766         Ref<CustomFieldBase> f = containingNode->getCustomFieldNoLock(i);
00767         jsval v[2];
00768         fields[i].valueConverter->fieldRead
00769           (containingNode, f.get(), spiderMonkeyContext, &v[0]);
00770         long double t1;
00771         timestamp.get(t1);
00772         jsdouble t2 = t1;
00773         if(!JS_NewDoubleValue(spiderMonkeyContext, t2, &v[1]))
00774           ARCHON_THROW1(InternalException,
00775                         "ECMA::Engine::handleEvent: "
00776                         "Allocation error");
00777         JSObject *globalObject = JS_GetGlobalObject(spiderMonkeyContext);
00778         jsval r;
00779         JS_CallFunctionValue(spiderMonkeyContext,
00780                              globalObject, handler, 2, v, &r);
00781 
00782         // Dissociate from the context
00783         JS_SetContextPrivate(spiderMonkeyContext, 0);
00784       }
00785     }
00786   }
00787 }

Generated on Sun Jul 30 22:55:50 2006 for Archon by  doxygen 1.4.4