/** * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <v8.h> #include <telephony/ril.h> #include "ctrl_server.h" #include "logging.h" #include "node_buffer.h" #include "node_object_wrap.h" #include "node_util.h" #include "protobuf_v8.h" #include "responses.h" #include "status.h" #include "util.h" #include "worker.h" #include "worker_v8.h" #include "js_support.h" //#define JS_SUPPORT_DEBUG #ifdef JS_SUPPORT_DEBUG #define DBG(...) LOGD(__VA_ARGS__) #else #define DBG(...) #endif /** * Current Radio state */ RIL_RadioState gRadioState = RADIO_STATE_UNAVAILABLE; v8::Handle<v8::Value> RadioStateGetter(v8::Local<v8::String> property, const v8::AccessorInfo& info) { return v8::Integer::New((int)gRadioState); } void RadioStateSetter(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::AccessorInfo& info) { gRadioState = RIL_RadioState(value->Int32Value()); } // A javascript sleep for a number of milli-seconds v8::Handle<v8::Value> MsSleep(const v8::Arguments& args) { if (args.Length() != 1) { DBG("MsSleep: expecting milli-seconds to sleep"); } else { v8::Handle<v8::Value> v8MsValue(args[0]->ToObject()); int ms = int(v8MsValue->NumberValue()); v8::Unlocker unlocker; usleep(ms * 1000); v8::Locker locker; } return v8::Undefined(); } // A javascript print function v8::Handle<v8::Value> Print(const v8::Arguments& args) { bool first = true; const int str_size = 1000; char* str = new char[str_size]; int offset = 0; for (int i = 0; i < args.Length(); i++) { v8::HandleScope handle_scope; if (first) { first = false; } else { offset += snprintf(&str[offset], str_size, " "); } v8::String::Utf8Value strUtf8(args[i]); const char* cstr = ToCString(strUtf8); offset += snprintf(&str[offset], str_size, "%s", cstr); } LOGD("%s", str); delete [] str; return v8::Undefined(); } int ReadFile(const char *fileName, char** data, size_t *length) { int status; char* buffer = NULL; size_t fileLength = 0; FILE *f; DBG("ReadFile E fileName=%s", fileName); f = fopen(fileName, "rb"); if (f == NULL) { DBG("Could not fopen '%s'", fileName); status = STATUS_COULD_NOT_OPEN_FILE; } else { // Determine the length of the file fseek(f, 0, SEEK_END); fileLength = ftell(f); DBG("fileLength=%d", fileLength); rewind(f); // Read file into a buffer buffer = new char[fileLength+1]; size_t readLength = fread(buffer, 1, fileLength, f); if (readLength != fileLength) { DBG("Couldn't read entire file"); delete [] buffer; buffer = NULL; status = STATUS_COULD_NOT_READ_FILE; } else { DBG("File read"); buffer[fileLength] = 0; status = STATUS_OK; } fclose(f); } if (length != NULL) { *length = fileLength; } *data = buffer; DBG("ReadFile X status=%d", status); return status; } char *CreateFileName(const v8::Arguments& args) { v8::String::Utf8Value fileNameUtf8Value(args[0]); const char* fileName = ToCString(fileNameUtf8Value); const char* directory = "/sdcard/data/"; int fullPathLength = strlen(directory) + strlen(fileName) + 1; char * fullPath = new char[fullPathLength]; strncpy(fullPath, directory, fullPathLength); strncat(fullPath, fileName, fullPathLength); return fullPath; } // A javascript read file function arg[0] = filename v8::Handle<v8::Value> ReadFileToString(const v8::Arguments& args) { DBG("ReadFileToString E"); v8::HandleScope handle_scope; v8::Handle<v8::Value> retValue; if (args.Length() < 1) { // No file name return Undefined DBG("ReadFile X no argumens"); return v8::Undefined(); } else { char *fileName = CreateFileName(args); char *buffer; int status = ReadFile(fileName, &buffer); if (status == 0) { retValue = v8::String::New(buffer); } else { retValue = v8::Undefined(); } } DBG("ReadFileToString X"); return retValue; } // A javascript read file function arg[0] = filename v8::Handle<v8::Value> ReadFileToBuffer(const v8::Arguments& args) { DBG("ReadFileToBuffer E"); v8::HandleScope handle_scope; v8::Handle<v8::Value> retValue; if (args.Length() < 1) { // No file name return Undefined DBG("ReadFileToBuffer X no argumens"); return v8::Undefined(); } else { char *fileName = CreateFileName(args); char *buffer; size_t length; int status = ReadFile(fileName, &buffer, &length); if (status == 0) { Buffer *buf = Buffer::New(length); memmove(buf->data(), buffer, length); retValue = buf->handle_; } else { retValue = v8::Undefined(); } } DBG("ReadFileToBuffer X"); return retValue; } void ErrorCallback(v8::Handle<v8::Message> message, v8::Handle<v8::Value> data) { LogErrorMessage(message, ""); } // Read, compile and run a javascript file v8::Handle<v8::Value> Include(const v8::Arguments& args) { DBG("Include E"); v8::HandleScope handle_scope; v8::Handle<v8::Value> retValue; v8::TryCatch try_catch; try_catch.SetVerbose(true); if (args.Length() < 1) { // No file name return Undefined DBG("Include X no argumens"); return v8::Undefined(); } else { char *fileName = CreateFileName(args); char *buffer; int status = ReadFile(fileName, &buffer); if (status == 0) { runJs(v8::Context::GetCurrent(), &try_catch, fileName, buffer); } else { retValue = v8::Undefined(); } } DBG("Include X"); return retValue; } /** * Create a JsContext, must be called within a HandleScope? */ v8::Persistent<v8::Context> makeJsContext() { v8::HandleScope handle_scope; v8::TryCatch try_catch; // Add a Message listner to Catch errors as they occur v8::V8::AddMessageListener(ErrorCallback); // Create a template for the global object and // add the function template for print to it. v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New(); global->SetAccessor(v8::String::New("gRadioState"), RadioStateGetter, RadioStateSetter); global->Set(v8::String::New("msSleep"), v8::FunctionTemplate::New(MsSleep)); global->Set(v8::String::New("print"), v8::FunctionTemplate::New(Print)); global->Set(v8::String::New("readFileToBuffer"), v8::FunctionTemplate::New(ReadFileToBuffer)); global->Set(v8::String::New("readFileToString"), v8::FunctionTemplate::New(ReadFileToString)); global->Set(v8::String::New("sendRilRequestComplete"), v8::FunctionTemplate::New(SendRilRequestComplete)); global->Set(v8::String::New("sendRilUnsolicitedResponse"), v8::FunctionTemplate::New(SendRilUnsolicitedResponse)); global->Set(v8::String::New("sendCtrlRequestComplete"), v8::FunctionTemplate::New(SendCtrlRequestComplete)); global->Set(v8::String::New("include"), v8::FunctionTemplate::New(Include)); WorkerV8ObjectTemplateInit(global); SchemaObjectTemplateInit(global); Buffer::InitializeObjectTemplate(global); // Create context with our globals and make it the current scope v8::Persistent<v8::Context> context = v8::Context::New(NULL, global); if (try_catch.HasCaught()) { DBG("makeJsContext: Exception making the context"); ReportException(&try_catch); try_catch.ReThrow(); } return context; } /** * Run some javascript code. */ void runJs(v8::Handle<v8::Context> context, v8::TryCatch *try_catch, const char *fileName, const char *code) { v8::HandleScope handle_scope; // Compile the source v8::Handle<v8::Script> script = v8::Script::Compile( v8::String::New(code), v8::String::New(fileName)); if (try_catch->HasCaught()) { LOGE("-- Compiling the source failed"); } else { // Run the resulting script v8::Handle<v8::Value> result = script->Run(); if (try_catch->HasCaught()) { LOGE("-- Running the script failed"); } } } void testRadioState(v8::Handle<v8::Context> context) { LOGD("testRadioState E:"); v8::HandleScope handle_scope; v8::TryCatch try_catch; try_catch.SetVerbose(true); runJs(context, &try_catch, "local-string", "for(i = 0; i < 10; i++) {\n" " gRadioState = i;\n" " print('gRadioState=' + gRadioState);\n" "}\n" "gRadioState = 1;\n" "print('last gRadioState=' + gRadioState);\n"); LOGD("testRadioState X:"); } void testMsSleep(v8::Handle<v8::Context> context) { LOGD("testMsSleep E:"); v8::HandleScope handle_scope; v8::TryCatch try_catch; try_catch.SetVerbose(true); runJs(context, &try_catch, "local-string", "for(i = 0; i < 10; i++) {\n" " sleeptime = i * 200\n" " print('msSleep ' + sleeptime);\n" " msSleep(sleeptime);\n" "}\n"); LOGD("testMsSleep X:"); } void testPrint(v8::Handle<v8::Context> context) { LOGD("testPrint E:"); v8::HandleScope handle_scope; v8::TryCatch try_catch; try_catch.SetVerbose(true); runJs(context, &try_catch, "local-string", "print(\"Hello\")"); LOGD("testPrint X:"); } void testCompileError(v8::Handle<v8::Context> context) { LOGD("testCompileError E:"); v8::HandleScope handle_scope; v8::TryCatch try_catch; try_catch.SetVerbose(true); // +++ generate a compile time error runJs(context, &try_catch, "local-string", "+++"); LOGD("testCompileError X:"); } void testRuntimeError(v8::Handle<v8::Context> context) { LOGD("testRuntimeError E:"); v8::HandleScope handle_scope; v8::TryCatch try_catch; try_catch.SetVerbose(true); // Runtime error runJs(context, &try_catch, "local-string", "function hello() {\n" " print(\"Hi there\");\n" "}\n" "helloo()"); LOGD("testRuntimeError X:"); } void testReadFile() { char *buffer; size_t length; int status; LOGD("testReadFile E:"); status = ReadFile("/sdcard/data/no-file", &buffer, &length); LOGD("testReadFile expect status != 0, status=%d, buffer=%p, length=%d", status, buffer, length); LOGD("testReadFile X:"); } void testReadFileToStringBuffer(v8::Handle<v8::Context> context) { LOGD("testReadFileToStringBuffer E:"); v8::HandleScope handle_scope; v8::TryCatch try_catch; try_catch.SetVerbose(true); runJs(context, &try_catch, "local-string", "fileContents = readFileToString(\"mock_ril.js\");\n" "print(\"fileContents:\\n\" + fileContents);\n" "buffer = readFileToBuffer(\"ril.desc\");\n" "print(\"buffer.length=\" + buffer.length);\n"); LOGD("testReadFileToStringBuffer X:"); } void testJsSupport(v8::Handle<v8::Context> context) { LOGD("testJsSupport E: ********"); testRadioState(context); testMsSleep(context); testPrint(context); testCompileError(context); testRuntimeError(context); testReadFile(); testReadFileToStringBuffer(context); LOGD("testJsSupport X: ********\n"); }