/**
* 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");
}