Home | History | Annotate | Download | only in cpp
      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  */
     48 RIL_RadioState gRadioState = RADIO_STATE_UNAVAILABLE;
     49 
     50 v8::Handle<v8::Value> RadioStateGetter(v8::Local<v8::String> property,
     51         const v8::AccessorInfo& info) {
     52     return v8::Integer::New((int)gRadioState);
     53 }
     54 
     55 void 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
     61 v8::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
     75 v8::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 
     96 int 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 
    139 char *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
    152 v8::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
    177 v8::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 
    204 void 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
    210 v8::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  */
    240 v8::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  */
    285 void 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 
    303 void 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 
    320 void 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 
    336 void 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 
    347 void 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 
    359 void 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 
    375 void 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 
    390 void 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 
    405 void 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