1/** 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include <v8.h> 18#include "ril.h" 19 20#include "ctrl_server.h" 21#include "logging.h" 22#include "node_buffer.h" 23#include "node_object_wrap.h" 24#include "node_util.h" 25#include "protobuf_v8.h" 26#include "responses.h" 27#include "status.h" 28#include "util.h" 29#include "worker.h" 30#include "worker_v8.h" 31 32#include "js_support.h" 33 34//#define JS_SUPPORT_DEBUG 35#ifdef JS_SUPPORT_DEBUG 36 37#define DBG(...) ALOGD(__VA_ARGS__) 38 39#else 40 41#define DBG(...) 42 43#endif 44 45/** 46 * Current Radio state 47 */ 48RIL_RadioState gRadioState = RADIO_STATE_UNAVAILABLE; 49 50v8::Handle<v8::Value> RadioStateGetter(v8::Local<v8::String> property, 51 const v8::AccessorInfo& info) { 52 return v8::Integer::New((int)gRadioState); 53} 54 55void RadioStateSetter(v8::Local<v8::String> property, 56 v8::Local<v8::Value> value, const v8::AccessorInfo& info) { 57 gRadioState = RIL_RadioState(value->Int32Value()); 58} 59 60// A javascript sleep for a number of milli-seconds 61v8::Handle<v8::Value> MsSleep(const v8::Arguments& args) { 62 if (args.Length() != 1) { 63 DBG("MsSleep: expecting milli-seconds to sleep"); 64 } else { 65 v8::Handle<v8::Value> v8MsValue(args[0]->ToObject()); 66 int ms = int(v8MsValue->NumberValue()); 67 v8::Unlocker unlocker; 68 usleep(ms * 1000); 69 v8::Locker locker; 70 } 71 return v8::Undefined(); 72} 73 74// A javascript print function 75v8::Handle<v8::Value> Print(const v8::Arguments& args) { 76 bool first = true; 77 const int str_size = 1000; 78 char* str = new char[str_size]; 79 int offset = 0; 80 for (int i = 0; i < args.Length(); i++) { 81 v8::HandleScope handle_scope; 82 if (first) { 83 first = false; 84 } else { 85 offset += snprintf(&str[offset], str_size, " "); 86 } 87 v8::String::Utf8Value strUtf8(args[i]); 88 const char* cstr = ToCString(strUtf8); 89 offset += snprintf(&str[offset], str_size, "%s", cstr); 90 } 91 ALOGD("%s", str); 92 delete [] str; 93 return v8::Undefined(); 94} 95 96int ReadFile(const char *fileName, char** data, size_t *length) { 97 int status; 98 char* buffer = NULL; 99 size_t fileLength = 0; 100 FILE *f; 101 102 DBG("ReadFile E fileName=%s", fileName); 103 104 f = fopen(fileName, "rb"); 105 if (f == NULL) { 106 DBG("Could not fopen '%s'", fileName); 107 status = STATUS_COULD_NOT_OPEN_FILE; 108 } else { 109 // Determine the length of the file 110 fseek(f, 0, SEEK_END); 111 fileLength = ftell(f); 112 DBG("fileLength=%d", fileLength); 113 rewind(f); 114 115 // Read file into a buffer 116 buffer = new char[fileLength+1]; 117 size_t readLength = fread(buffer, 1, fileLength, f); 118 if (readLength != fileLength) { 119 DBG("Couldn't read entire file"); 120 delete [] buffer; 121 buffer = NULL; 122 status = STATUS_COULD_NOT_READ_FILE; 123 } else { 124 DBG("File read"); 125 buffer[fileLength] = 0; 126 status = STATUS_OK; 127 } 128 fclose(f); 129 } 130 131 if (length != NULL) { 132 *length = fileLength; 133 } 134 *data = buffer; 135 DBG("ReadFile X status=%d", status); 136 return status; 137} 138 139char *CreateFileName(const v8::Arguments& args) { 140 v8::String::Utf8Value fileNameUtf8Value(args[0]); 141 const char* fileName = ToCString(fileNameUtf8Value); 142 const char* directory = "/sdcard/data/"; 143 144 int fullPathLength = strlen(directory) + strlen(fileName) + 1; 145 char * fullPath = new char[fullPathLength]; 146 strncpy(fullPath, directory, fullPathLength); 147 strncat(fullPath, fileName, fullPathLength); 148 return fullPath; 149} 150 151// A javascript read file function arg[0] = filename 152v8::Handle<v8::Value> ReadFileToString(const v8::Arguments& args) { 153 DBG("ReadFileToString E"); 154 v8::HandleScope handle_scope; 155 v8::Handle<v8::Value> retValue; 156 157 if (args.Length() < 1) { 158 // No file name return Undefined 159 DBG("ReadFile X no argumens"); 160 return v8::Undefined(); 161 } else { 162 char *fileName = CreateFileName(args); 163 164 char *buffer; 165 int status = ReadFile(fileName, &buffer); 166 if (status == 0) { 167 retValue = v8::String::New(buffer); 168 } else { 169 retValue = v8::Undefined(); 170 } 171 } 172 DBG("ReadFileToString X"); 173 return retValue; 174} 175 176// A javascript read file function arg[0] = filename 177v8::Handle<v8::Value> ReadFileToBuffer(const v8::Arguments& args) { 178 DBG("ReadFileToBuffer E"); 179 v8::HandleScope handle_scope; 180 v8::Handle<v8::Value> retValue; 181 182 if (args.Length() < 1) { 183 // No file name return Undefined 184 DBG("ReadFileToBuffer X no argumens"); 185 return v8::Undefined(); 186 } else { 187 char *fileName = CreateFileName(args); 188 189 char *buffer; 190 size_t length; 191 int status = ReadFile(fileName, &buffer, &length); 192 if (status == 0) { 193 Buffer *buf = Buffer::New(length); 194 memmove(buf->data(), buffer, length); 195 retValue = buf->handle_; 196 } else { 197 retValue = v8::Undefined(); 198 } 199 } 200 DBG("ReadFileToBuffer X"); 201 return retValue; 202} 203 204void ErrorCallback(v8::Handle<v8::Message> message, 205 v8::Handle<v8::Value> data) { 206 LogErrorMessage(message, ""); 207} 208 209// Read, compile and run a javascript file 210v8::Handle<v8::Value> Include(const v8::Arguments& args) { 211 DBG("Include E"); 212 v8::HandleScope handle_scope; 213 v8::Handle<v8::Value> retValue; 214 v8::TryCatch try_catch; 215 try_catch.SetVerbose(true); 216 217 if (args.Length() < 1) { 218 // No file name return Undefined 219 DBG("Include X no argumens"); 220 return v8::Undefined(); 221 } else { 222 char *fileName = CreateFileName(args); 223 224 char *buffer; 225 int status = ReadFile(fileName, &buffer); 226 if (status == 0) { 227 runJs(v8::Context::GetCurrent(), &try_catch, fileName, buffer); 228 } else { 229 retValue = v8::Undefined(); 230 } 231 } 232 DBG("Include X"); 233 return retValue; 234} 235 236 237/** 238 * Create a JsContext, must be called within a HandleScope? 239 */ 240v8::Persistent<v8::Context> makeJsContext() { 241 v8::HandleScope handle_scope; 242 v8::TryCatch try_catch; 243 244 // Add a Message listner to Catch errors as they occur 245 v8::V8::AddMessageListener(ErrorCallback); 246 247 // Create a template for the global object and 248 // add the function template for print to it. 249 v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New(); 250 global->SetAccessor(v8::String::New("gRadioState"), 251 RadioStateGetter, RadioStateSetter); 252 global->Set(v8::String::New("msSleep"), v8::FunctionTemplate::New(MsSleep)); 253 global->Set(v8::String::New("print"), v8::FunctionTemplate::New(Print)); 254 global->Set(v8::String::New("readFileToBuffer"), 255 v8::FunctionTemplate::New(ReadFileToBuffer)); 256 global->Set(v8::String::New("readFileToString"), 257 v8::FunctionTemplate::New(ReadFileToString)); 258 global->Set(v8::String::New("sendRilRequestComplete"), 259 v8::FunctionTemplate::New(SendRilRequestComplete)); 260 global->Set(v8::String::New("sendRilUnsolicitedResponse"), 261 v8::FunctionTemplate::New(SendRilUnsolicitedResponse)); 262 global->Set(v8::String::New("sendCtrlRequestComplete"), 263 v8::FunctionTemplate::New(SendCtrlRequestComplete)); 264 global->Set(v8::String::New("include"), v8::FunctionTemplate::New(Include)); 265 WorkerV8ObjectTemplateInit(global); 266 SchemaObjectTemplateInit(global); 267 Buffer::InitializeObjectTemplate(global); 268 269 // Create context with our globals and make it the current scope 270 v8::Persistent<v8::Context> context = v8::Context::New(NULL, global); 271 272 273 if (try_catch.HasCaught()) { 274 DBG("makeJsContext: Exception making the context"); 275 ReportException(&try_catch); 276 try_catch.ReThrow(); 277 } 278 279 return context; 280} 281 282/** 283 * Run some javascript code. 284 */ 285void runJs(v8::Handle<v8::Context> context, v8::TryCatch *try_catch, 286 const char *fileName, const char *code) { 287 v8::HandleScope handle_scope; 288 289 // Compile the source 290 v8::Handle<v8::Script> script = v8::Script::Compile( 291 v8::String::New(code), v8::String::New(fileName)); 292 if (try_catch->HasCaught()) { 293 ALOGE("-- Compiling the source failed"); 294 } else { 295 // Run the resulting script 296 v8::Handle<v8::Value> result = script->Run(); 297 if (try_catch->HasCaught()) { 298 ALOGE("-- Running the script failed"); 299 } 300 } 301} 302 303void testRadioState(v8::Handle<v8::Context> context) { 304 ALOGD("testRadioState E:"); 305 v8::HandleScope handle_scope; 306 307 v8::TryCatch try_catch; 308 try_catch.SetVerbose(true); 309 310 runJs(context, &try_catch, "local-string", 311 "for(i = 0; i < 10; i++) {\n" 312 " gRadioState = i;\n" 313 " print('gRadioState=' + gRadioState);\n" 314 "}\n" 315 "gRadioState = 1;\n" 316 "print('last gRadioState=' + gRadioState);\n"); 317 ALOGD("testRadioState X:"); 318} 319 320void testMsSleep(v8::Handle<v8::Context> context) { 321 ALOGD("testMsSleep E:"); 322 v8::HandleScope handle_scope; 323 324 v8::TryCatch try_catch; 325 try_catch.SetVerbose(true); 326 327 runJs(context, &try_catch, "local-string", 328 "for(i = 0; i < 10; i++) {\n" 329 " sleeptime = i * 200\n" 330 " print('msSleep ' + sleeptime);\n" 331 " msSleep(sleeptime);\n" 332 "}\n"); 333 ALOGD("testMsSleep X:"); 334} 335 336void testPrint(v8::Handle<v8::Context> context) { 337 ALOGD("testPrint E:"); 338 v8::HandleScope handle_scope; 339 340 v8::TryCatch try_catch; 341 try_catch.SetVerbose(true); 342 343 runJs(context, &try_catch, "local-string", "print(\"Hello\")"); 344 ALOGD("testPrint X:"); 345} 346 347void testCompileError(v8::Handle<v8::Context> context) { 348 ALOGD("testCompileError E:"); 349 v8::HandleScope handle_scope; 350 351 v8::TryCatch try_catch; 352 try_catch.SetVerbose(true); 353 354 // +++ generate a compile time error 355 runJs(context, &try_catch, "local-string", "+++"); 356 ALOGD("testCompileError X:"); 357} 358 359void testRuntimeError(v8::Handle<v8::Context> context) { 360 ALOGD("testRuntimeError E:"); 361 v8::HandleScope handle_scope; 362 363 v8::TryCatch try_catch; 364 try_catch.SetVerbose(true); 365 366 // Runtime error 367 runJs(context, &try_catch, "local-string", 368 "function hello() {\n" 369 " print(\"Hi there\");\n" 370 "}\n" 371 "helloo()"); 372 ALOGD("testRuntimeError X:"); 373} 374 375void testReadFile() { 376 char *buffer; 377 size_t length; 378 int status; 379 380 ALOGD("testReadFile E:"); 381 382 status = ReadFile("/sdcard/data/no-file", &buffer, &length); 383 ALOGD("testReadFile expect status != 0, status=%d, buffer=%p, length=%d", 384 status, buffer, length); 385 386 ALOGD("testReadFile X:"); 387} 388 389 390void testReadFileToStringBuffer(v8::Handle<v8::Context> context) { 391 ALOGD("testReadFileToStringBuffer E:"); 392 v8::HandleScope handle_scope; 393 394 v8::TryCatch try_catch; 395 try_catch.SetVerbose(true); 396 397 runJs(context, &try_catch, "local-string", 398 "fileContents = readFileToString(\"mock_ril.js\");\n" 399 "print(\"fileContents:\\n\" + fileContents);\n" 400 "buffer = readFileToBuffer(\"ril.desc\");\n" 401 "print(\"buffer.length=\" + buffer.length);\n"); 402 ALOGD("testReadFileToStringBuffer X:"); 403} 404 405void testJsSupport(v8::Handle<v8::Context> context) { 406 ALOGD("testJsSupport E: ********"); 407 testRadioState(context); 408 testMsSleep(context); 409 testPrint(context); 410 testCompileError(context); 411 testRuntimeError(context); 412 testReadFile(); 413 testReadFileToStringBuffer(context); 414 ALOGD("testJsSupport X: ********\n"); 415} 416