Go to most recent revision | Details | Last modification | View Log | SVN | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 969 | runge | 1 | /*************************************************************************** |
| 2 | * Copyright (C) December 10, 2008 by Mattias Runge * |
||
| 3 | * mattias@runge.se * |
||
| 4 | * virtualmachine.cpp * |
||
| 5 | * * |
||
| 6 | * This program is free software; you can redistribute it and/or modify * |
||
| 7 | * it under the terms of the GNU General Public License as published by * |
||
| 8 | * the Free Software Foundation; either version 2 of the License, or * |
||
| 9 | * (at your option) any later version. * |
||
| 10 | * * |
||
| 11 | * This program is distributed in the hope that it will be useful, * |
||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * |
||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * |
||
| 14 | * GNU General Public License for more details. * |
||
| 15 | * * |
||
| 16 | * You should have received a copy of the GNU General Public License * |
||
| 17 | * along with this program; if not, write to the * |
||
| 18 | * Free Software Foundation, Inc., * |
||
| 19 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * |
||
| 20 | ***************************************************************************/ |
||
| 21 | |||
| 22 | #include "virtualmachine.h" |
||
| 23 | |||
| 24 | VirtualMachine* VirtualMachine::myInstance = NULL; |
||
| 25 | |||
| 26 | VirtualMachine& VirtualMachine::getInstance() |
||
| 27 | { |
||
| 28 | if (myInstance == NULL) |
||
| 29 | { |
||
| 30 | myInstance = new VirtualMachine(); |
||
| 31 | } |
||
| 32 | |||
| 33 | return *myInstance; |
||
| 34 | } |
||
| 35 | |||
| 36 | void VirtualMachine::deleteInstance() |
||
| 37 | { |
||
| 38 | if (myInstance != NULL) |
||
| 39 | { |
||
| 40 | delete myInstance; |
||
| 41 | myInstance = NULL; |
||
| 42 | } |
||
| 43 | } |
||
| 44 | |||
| 45 | VirtualMachine::VirtualMachine() |
||
| 46 | { |
||
| 47 | } |
||
| 48 | |||
| 49 | VirtualMachine::~VirtualMachine() |
||
| 50 | { |
||
| 981 | runge | 51 | stop(); |
| 997 | runge | 52 | |
| 53 | ///FIXME: Verify that this works and does not cause segfaults |
||
| 54 | for (map<int, SocketThread*>::iterator iter = mySocketThreads.begin(); iter != mySocketThreads.end(); iter++) |
||
| 55 | { |
||
| 56 | delete iter->second; |
||
| 57 | mySocketThreads.erase(iter); |
||
| 58 | } |
||
| 969 | runge | 59 | } |
| 60 | |||
| 61 | void VirtualMachine::run() |
||
| 62 | { |
||
| 63 | myGlobal = ObjectTemplate::New(); |
||
| 64 | myGlobal->Set(String::New("log"), FunctionTemplate::New(VirtualMachine_log)); |
||
| 65 | myGlobal->Set(String::New("sendCanMessage"), FunctionTemplate::New(VirtualMachine_sendCanMessage)); |
||
| 986 | runge | 66 | myGlobal->Set(String::New("sendCanNMTMessage"), FunctionTemplate::New(VirtualMachine_sendCanNMTMessage)); |
| 969 | runge | 67 | myGlobal->Set(String::New("loadScript"), FunctionTemplate::New(VirtualMachine_loadScript)); |
| 971 | runge | 68 | myGlobal->Set(String::New("startIntervalThread"), FunctionTemplate::New(VirtualMachine_startIntervalThread)); |
| 69 | myGlobal->Set(String::New("stopIntervalThread"), FunctionTemplate::New(VirtualMachine_stopIntervalThread)); |
||
| 976 | runge | 70 | myGlobal->Set(String::New("startSocketThread"), FunctionTemplate::New(VirtualMachine_startSocketThread)); |
| 71 | myGlobal->Set(String::New("stopSocketThread"), FunctionTemplate::New(VirtualMachine_stopSocketThread)); |
||
| 72 | myGlobal->Set(String::New("sendToSocketThread"), FunctionTemplate::New(VirtualMachine_sendToSocketThread)); |
||
| 981 | runge | 73 | myGlobal->Set(String::New("uint2hex"), FunctionTemplate::New(VirtualMachine_uint2hex)); |
| 969 | runge | 74 | myContext = Context::New(NULL, myGlobal); |
| 75 | |||
| 76 | |||
| 997 | runge | 77 | if (loadScript("System/Base.js")) |
| 78 | { |
||
| 79 | loadScript("System/Startup.js"); |
||
| 969 | runge | 80 | |
| 997 | runge | 81 | loadScript("Autostart.js"); |
| 82 | |||
| 83 | Context::Scope contextScope(myContext); |
||
| 84 | |||
| 85 | myFunctionHandleNMTMessage = Handle<Function>::Cast(myContext->Global()->Get(String::New("handleNMTMessage"))); |
||
| 86 | myFunctionOfflineCheck = Handle<Function>::Cast(myContext->Global()->Get(String::New("offlineCheck"))); |
||
| 87 | myFunctionHandleMessage = Handle<Function>::Cast(myContext->Global()->Get(String::New("handleMessage"))); |
||
| 88 | myFunctionStartup = Handle<Function>::Cast(myContext->Global()->Get(String::New("startup"))); |
||
| 969 | runge | 89 | } |
| 90 | else |
||
| 91 | { |
||
| 997 | runge | 92 | ///FIXME: We should probably exit the application here |
| 93 | return; |
||
| 969 | runge | 94 | } |
| 95 | |||
| 96 | const int argc = 0; |
||
| 97 | Handle<Value> argv[argc] = { }; |
||
| 997 | runge | 98 | Handle<Value> result = myFunctionStartup->Call(myFunctionStartup, argc, argv); |
| 969 | runge | 99 | |
| 100 | mySemaphore.lock(); |
||
| 101 | |||
| 997 | runge | 102 | string expression; |
| 103 | CanMessage canMessage; |
||
| 104 | |||
| 105 | while (true) |
||
| 969 | runge | 106 | { |
| 107 | while (myExpressions.size() > 0) |
||
| 108 | { |
||
| 109 | expression = myExpressions.pop(); |
||
| 110 | runExpression(expression); |
||
| 111 | } |
||
| 112 | |||
| 113 | while (myCanMessages.size() > 0) |
||
| 114 | { |
||
| 115 | canMessage = myCanMessages.pop(); |
||
| 116 | |||
| 986 | runge | 117 | if (canMessage.getClassName() == "nmt") |
| 969 | runge | 118 | { |
| 997 | runge | 119 | expression = "handleNMTMessage("; |
| 120 | expression += "'" + canMessage.getClassName() + "', "; |
||
| 121 | expression += "'" + canMessage.getCommandName() + "', "; |
||
| 122 | expression += canMessage.getJSONData(); |
||
| 123 | expression += ");"; |
||
| 124 | runExpression(expression); |
||
| 969 | runge | 125 | } |
| 126 | else |
||
| 127 | { |
||
| 997 | runge | 128 | expression = "handleMessage("; |
| 129 | expression += "'" + canMessage.getClassName() + "', "; |
||
| 130 | expression += "'" + canMessage.getDirectionFlag() + "', "; |
||
| 131 | expression += "'" + canMessage.getModuleName() + "', "; |
||
| 132 | expression += "" + itos(canMessage.getModuleId()) + ", "; |
||
| 133 | expression += "'" + canMessage.getCommandName() + "', "; |
||
| 134 | expression += canMessage.getJSONData(); |
||
| 135 | expression += ");"; |
||
| 136 | runExpression(expression); |
||
| 969 | runge | 137 | } |
| 138 | } |
||
| 139 | |||
| 140 | mySemaphore.wait(); |
||
| 141 | } |
||
| 142 | |||
| 143 | mySemaphore.unlock(); |
||
| 144 | } |
||
| 145 | |||
| 146 | void VirtualMachine::queueCanMessage(CanMessage canMessage) |
||
| 147 | { |
||
| 148 | myCanMessages.push(canMessage); |
||
| 149 | mySemaphore.broadcast(); |
||
| 150 | } |
||
| 151 | |||
| 152 | void VirtualMachine::queueExpression(string expression) |
||
| 153 | { |
||
| 154 | myExpressions.push(expression); |
||
| 155 | mySemaphore.broadcast(); |
||
| 156 | } |
||
| 157 | |||
| 158 | bool VirtualMachine::loadScript(string scriptName) |
||
| 159 | { |
||
| 160 | SyslogStream &slog = SyslogStream::getInstance(); |
||
| 161 | |||
| 981 | runge | 162 | string basePath = Settings::get("BasePath") + "Services/"; |
| 969 | runge | 163 | string scriptFileName = basePath + scriptName; |
| 164 | |||
| 165 | if (!file_exists(scriptFileName)) |
||
| 166 | { |
||
| 997 | runge | 167 | slog << "Failed to load " + scriptFileName + ", file does not exist.\n"; |
| 969 | runge | 168 | return false; |
| 169 | } |
||
| 170 | |||
| 171 | string scriptSource = file_get_contents(scriptFileName); |
||
| 172 | |||
| 173 | Handle<String> source = String::New(scriptSource.c_str(), scriptSource.length()); |
||
| 174 | Handle<String> name = String::New(scriptName.c_str(), scriptName.length()); |
||
| 175 | |||
| 997 | runge | 176 | Context::Scope contextScope(myContext); |
| 177 | |||
| 995 | runge | 178 | TryCatch tryCatch; |
| 997 | runge | 179 | |
| 969 | runge | 180 | Handle<Script> script = Script::Compile(source, name); |
| 181 | |||
| 182 | if (script.IsEmpty()) |
||
| 183 | { |
||
| 995 | runge | 184 | printException(&tryCatch); |
| 969 | runge | 185 | return false; |
| 186 | } |
||
| 187 | else |
||
| 188 | { |
||
| 189 | Handle<Value> result = script->Run(); |
||
| 190 | |||
| 191 | if (result.IsEmpty()) |
||
| 192 | { |
||
| 995 | runge | 193 | printException(&tryCatch); |
| 969 | runge | 194 | return false; |
| 195 | } |
||
| 977 | runge | 196 | |
| 197 | slog << "Loaded " + scriptName + "\n"; |
||
| 969 | runge | 198 | } |
| 199 | |||
| 200 | return true; |
||
| 201 | } |
||
| 202 | |||
| 986 | runge | 203 | void VirtualMachine::callHandleNMTMessage(CanMessage canMessage) |
| 969 | runge | 204 | { |
| 986 | runge | 205 | const int argc = 3; |
| 969 | runge | 206 | |
| 986 | runge | 207 | string jsonData = canMessage.getJSONData(); |
| 208 | |||
| 209 | Handle<Value> argv[argc] = {String::New(canMessage.getClassName().c_str()), |
||
| 210 | String::New(canMessage.getCommandName().c_str()), |
||
| 211 | String::New(jsonData.c_str()) }; |
||
| 212 | |||
| 213 | Handle<Value> result = myFunctionHandleNMTMessage->Call(myFunctionHandleNMTMessage, argc, argv); // argc and argv are your standard arguments to a function |
||
| 969 | runge | 214 | } |
| 215 | |||
| 216 | void VirtualMachine::callHandleMessage(CanMessage canMessage) |
||
| 217 | { |
||
| 218 | const int argc = 6; |
||
| 981 | runge | 219 | |
| 220 | string jsonData = canMessage.getJSONData(); |
||
| 221 | |||
| 969 | runge | 222 | Handle<Value> argv[argc] = {String::New(canMessage.getClassName().c_str()), |
| 223 | String::New(canMessage.getDirectionFlag().c_str()), |
||
| 224 | String::New(canMessage.getModuleName().c_str()), |
||
| 225 | Integer::New(canMessage.getModuleId()), |
||
| 226 | String::New(canMessage.getCommandName().c_str()), |
||
| 981 | runge | 227 | String::New(jsonData.c_str()) }; |
| 969 | runge | 228 | |
| 229 | Handle<Value> result = myFunctionHandleMessage->Call(myFunctionHandleMessage, argc, argv); // argc and argv are your standard arguments to a function |
||
| 230 | } |
||
| 231 | |||
| 971 | runge | 232 | unsigned int VirtualMachine::startIntervalThread(unsigned int timeout) |
| 969 | runge | 233 | { |
| 971 | runge | 234 | IntervalThread intervalThread(timeout); |
| 969 | runge | 235 | |
| 971 | runge | 236 | myIntervalThreads[intervalThread.getId()] = intervalThread; |
| 237 | myIntervalThreads[intervalThread.getId()].start(); |
||
| 969 | runge | 238 | |
| 971 | runge | 239 | return intervalThread.getId(); |
| 969 | runge | 240 | } |
| 241 | |||
| 971 | runge | 242 | bool VirtualMachine::stopIntervalThread(unsigned int id) |
| 969 | runge | 243 | { |
| 971 | runge | 244 | if (myIntervalThreads.find(id) != myIntervalThreads.end()) |
| 969 | runge | 245 | { |
| 971 | runge | 246 | myIntervalThreads.erase(id); |
| 969 | runge | 247 | return true; |
| 248 | } |
||
| 249 | |||
| 250 | return false; |
||
| 251 | } |
||
| 252 | |||
| 976 | runge | 253 | unsigned int VirtualMachine::startSocketThread(string address, int port, unsigned int reconnectTimeout) |
| 254 | { |
||
| 983 | runge | 255 | SocketThread *socketThread = new SocketThread(address, port, reconnectTimeout); |
| 256 | mySocketThreads[socketThread->getId()] = socketThread; |
||
| 257 | mySocketThreads[socketThread->getId()]->start(); |
||
| 976 | runge | 258 | |
| 983 | runge | 259 | return socketThread->getId(); |
| 976 | runge | 260 | } |
| 261 | |||
| 262 | bool VirtualMachine::stopSocketThread(unsigned int id) |
||
| 263 | { |
||
| 264 | if (mySocketThreads.find(id) != mySocketThreads.end()) |
||
| 265 | { |
||
| 983 | runge | 266 | delete mySocketThreads[id]; |
| 267 | |||
| 976 | runge | 268 | mySocketThreads.erase(id); |
| 269 | return true; |
||
| 270 | } |
||
| 271 | |||
| 272 | return false; |
||
| 273 | } |
||
| 274 | |||
| 275 | void VirtualMachine::sendToSocketThread(unsigned int id, string data) |
||
| 276 | { |
||
| 277 | if (mySocketThreads.find(id) != mySocketThreads.end()) |
||
| 278 | { |
||
| 983 | runge | 279 | mySocketThreads[id]->send(data); |
| 976 | runge | 280 | } |
| 281 | } |
||
| 282 | |||
| 969 | runge | 283 | bool VirtualMachine::runExpression(string expression) |
| 284 | { |
||
| 285 | string scriptName = "runExpression"; |
||
| 286 | |||
| 287 | Handle<String> source = String::New(expression.c_str(), expression.length()); |
||
| 288 | Handle<String> name = String::New(scriptName.c_str(), scriptName.length()); |
||
| 289 | |||
| 997 | runge | 290 | Context::Scope contextScope(myContext); |
| 291 | |||
| 995 | runge | 292 | TryCatch tryCatch; |
| 997 | runge | 293 | |
| 969 | runge | 294 | Handle<Script> script = Script::Compile(source, name); |
| 295 | |||
| 296 | if (script.IsEmpty()) |
||
| 297 | { |
||
| 995 | runge | 298 | printException(&tryCatch); |
| 969 | runge | 299 | return false; |
| 300 | } |
||
| 301 | else |
||
| 302 | { |
||
| 303 | Handle<Value> result = script->Run(); |
||
| 304 | |||
| 305 | if (result.IsEmpty()) |
||
| 306 | { |
||
| 995 | runge | 307 | printException(&tryCatch); |
| 969 | runge | 308 | return false; |
| 309 | } |
||
| 310 | } |
||
| 311 | |||
| 312 | return true; |
||
| 313 | } |
||
| 314 | |||
| 995 | runge | 315 | void VirtualMachine::printException(TryCatch* tryCatch) |
| 316 | { |
||
| 317 | SyslogStream &slog = SyslogStream::getInstance(); |
||
| 318 | |||
| 997 | runge | 319 | HandleScope handleScope; |
| 995 | runge | 320 | String::Utf8Value exception(tryCatch->Exception()); |
| 321 | Handle<v8::Message> message = tryCatch->Message(); |
||
| 322 | |||
| 323 | string strException = *exception; |
||
| 324 | |||
| 325 | if (message.IsEmpty()) |
||
| 326 | { |
||
| 997 | runge | 327 | slog << strException + "\n\n"; |
| 995 | runge | 328 | } |
| 329 | else |
||
| 330 | { |
||
| 331 | String::Utf8Value filename(message->GetScriptResourceName()); |
||
| 332 | string strFilename = *filename; |
||
| 333 | |||
| 997 | runge | 334 | slog << strFilename + ":" + itos(message->GetLineNumber()) + ": " + strException + "\n"; |
| 995 | runge | 335 | |
| 336 | String::Utf8Value sourceline(message->GetSourceLine()); |
||
| 337 | string strSourceline = *sourceline; |
||
| 338 | |||
| 339 | slog << strSourceline + "\n"; |
||
| 340 | |||
| 997 | runge | 341 | string underline = rpad("", message->GetStartColumn(), ' '); |
| 342 | underline = rpad(underline, message->GetEndColumn(), '^'); |
||
| 995 | runge | 343 | |
| 997 | runge | 344 | slog << underline + "\n\n"; |
| 995 | runge | 345 | } |
| 346 | } |
||
| 347 | |||
| 348 | |||
| 969 | runge | 349 | Handle<Value> VirtualMachine_log(const Arguments& args) |
| 350 | { |
||
| 351 | SyslogStream &slog = SyslogStream::getInstance(); |
||
| 352 | |||
| 353 | String::AsciiValue str(args[0]); |
||
| 354 | |||
| 355 | slog << *str; |
||
| 356 | |||
| 357 | return Undefined(); |
||
| 358 | } |
||
| 359 | |||
| 360 | Handle<Value> VirtualMachine_sendCanMessage(const Arguments& args) |
||
| 361 | { |
||
| 362 | CanNetManager &canMan = CanNetManager::getInstance(); |
||
| 363 | |||
| 364 | CanMessage canMessage; |
||
| 365 | |||
| 366 | String::AsciiValue className(args[0]); |
||
| 367 | canMessage.setClassName(*className); |
||
| 368 | String::AsciiValue directionFlag(args[1]); |
||
| 369 | canMessage.setDirectionFlag(*directionFlag); |
||
| 370 | String::AsciiValue moduleName(args[2]); |
||
| 371 | canMessage.setModuleName(*moduleName); |
||
| 372 | canMessage.setModuleId(args[3]->Uint32Value()); |
||
| 373 | String::AsciiValue commandName(args[4]); |
||
| 374 | canMessage.setCommandName(*commandName); |
||
| 375 | |||
| 376 | String::AsciiValue dataString(args[5]); |
||
| 377 | |||
| 378 | vector<string> parts = explode(",", trim(*dataString, ',')); |
||
| 379 | map<string, CanVariable> data; |
||
| 380 | |||
| 381 | for (int n = 0; n < parts.size(); n++) |
||
| 382 | { |
||
| 383 | vector<string> keyAndValue = explode(":", parts[n]); |
||
| 384 | |||
| 385 | data[keyAndValue[0]] = CanVariable(keyAndValue[0], keyAndValue[1]); |
||
| 386 | } |
||
| 387 | |||
| 388 | canMessage.setData(data); |
||
| 389 | |||
| 390 | canMan.sendMessage(canMessage); |
||
| 391 | |||
| 392 | return Undefined(); |
||
| 393 | } |
||
| 394 | |||
| 986 | runge | 395 | Handle<Value> VirtualMachine_sendCanNMTMessage(const Arguments& args) |
| 396 | { |
||
| 397 | CanNetManager &canMan = CanNetManager::getInstance(); |
||
| 398 | |||
| 399 | CanMessage canMessage; |
||
| 400 | |||
| 401 | String::AsciiValue className(args[0]); |
||
| 402 | canMessage.setClassName(*className); |
||
| 403 | String::AsciiValue commandName(args[1]); |
||
| 404 | canMessage.setCommandName(*commandName); |
||
| 405 | |||
| 406 | String::AsciiValue dataString(args[2]); |
||
| 407 | |||
| 408 | vector<string> parts = explode(",", trim(*dataString, ',')); |
||
| 409 | map<string, CanVariable> data; |
||
| 410 | |||
| 411 | for (int n = 0; n < parts.size(); n++) |
||
| 412 | { |
||
| 413 | vector<string> keyAndValue = explode(":", parts[n]); |
||
| 414 | |||
| 415 | data[keyAndValue[0]] = CanVariable(keyAndValue[0], keyAndValue[1]); |
||
| 416 | } |
||
| 417 | |||
| 418 | canMessage.setData(data); |
||
| 419 | |||
| 420 | canMan.sendMessage(canMessage); |
||
| 421 | |||
| 422 | return Undefined(); |
||
| 423 | } |
||
| 424 | |||
| 969 | runge | 425 | Handle<Value> VirtualMachine_loadScript(const Arguments& args) |
| 426 | { |
||
| 427 | VirtualMachine &vm = VirtualMachine::getInstance(); |
||
| 428 | |||
| 429 | String::AsciiValue str(args[0]); |
||
| 430 | |||
| 431 | return Boolean::New(vm.loadScript(*str)); |
||
| 432 | } |
||
| 433 | |||
| 971 | runge | 434 | Handle<Value> VirtualMachine_startIntervalThread(const Arguments& args) |
| 969 | runge | 435 | { |
| 436 | VirtualMachine &vm = VirtualMachine::getInstance(); |
||
| 437 | |||
| 971 | runge | 438 | unsigned int timeout = args[0]->Uint32Value(); |
| 969 | runge | 439 | |
| 971 | runge | 440 | return Integer::New(vm.startIntervalThread(timeout)); |
| 969 | runge | 441 | } |
| 442 | |||
| 971 | runge | 443 | Handle<Value> VirtualMachine_stopIntervalThread(const Arguments& args) |
| 969 | runge | 444 | { |
| 445 | VirtualMachine &vm = VirtualMachine::getInstance(); |
||
| 446 | |||
| 973 | runge | 447 | unsigned int id = args[0]->Uint32Value(); |
| 969 | runge | 448 | |
| 976 | runge | 449 | return Boolean::New(vm.stopIntervalThread(id)); |
| 969 | runge | 450 | } |
| 976 | runge | 451 | |
| 452 | Handle<Value> VirtualMachine_startSocketThread(const Arguments& args) |
||
| 453 | { |
||
| 454 | VirtualMachine &vm = VirtualMachine::getInstance(); |
||
| 455 | |||
| 456 | String::AsciiValue address(args[0]); |
||
| 457 | int port = args[1]->Uint32Value(); |
||
| 458 | unsigned int reconnectTimeout = args[2]->Uint32Value(); |
||
| 459 | |||
| 460 | return Integer::New(vm.startSocketThread(*address, port, reconnectTimeout)); |
||
| 461 | } |
||
| 462 | |||
| 463 | Handle<Value> VirtualMachine_stopSocketThread(const Arguments& args) |
||
| 464 | { |
||
| 465 | VirtualMachine &vm = VirtualMachine::getInstance(); |
||
| 466 | |||
| 467 | unsigned int id = args[0]->Uint32Value(); |
||
| 468 | |||
| 469 | return Boolean::New(vm.stopSocketThread(id)); |
||
| 470 | } |
||
| 471 | |||
| 472 | Handle<Value> VirtualMachine_sendToSocketThread(const Arguments& args) |
||
| 473 | { |
||
| 474 | VirtualMachine &vm = VirtualMachine::getInstance(); |
||
| 475 | |||
| 476 | unsigned int id = args[0]->Uint32Value(); |
||
| 477 | String::AsciiValue data(args[1]); |
||
| 478 | |||
| 479 | vm.sendToSocketThread(id, *data); |
||
| 480 | |||
| 481 | return Undefined(); |
||
| 482 | } |
||
| 483 | |||
| 981 | runge | 484 | Handle<Value> VirtualMachine_uint2hex(const Arguments& args) |
| 485 | { |
||
| 486 | VirtualMachine &vm = VirtualMachine::getInstance(); |
||
| 487 | |||
| 488 | unsigned int id = args[0]->Uint32Value(); |
||
| 489 | unsigned int length = args[1]->Uint32Value(); |
||
| 490 | string hexId = uint2hex(id, length); |
||
| 491 | |||
| 492 | return String::New(hexId.c_str()); |
||
| 493 | } |
||
| 494 |