tizen 2.3.1 release
[framework/web/wearable/wrt-plugins-tizen.git] / src / Common / StandaloneConsole / StandaloneConsole.cpp
1 //
2 // Tizen Web Device API
3 // Copyright (c) 2013 Samsung Electronics Co., Ltd.
4 //
5 // Licensed under the Apache License, Version 2.0 (the License);
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17
18 #include <stdio.h>
19 #include <dlog.h>
20 #include <JavaScriptCore/JavaScript.h>
21 #include "StandaloneConsole.h"
22 #include "JSConsole.h"
23 #include <Ecore.h>
24 #include <GlobalContextManager.h>
25 #include <string>
26 #include <vector>
27 #include <iostream>
28 #include <termios.h>
29 #include <JSUtil.h>
30
31 #undef LOG_TAG
32 #define LOG_TAG "TIZEN_DEVICEAPI"
33
34 using namespace std;
35 using namespace DeviceAPI::Common;
36
37 namespace DeviceAPI {
38 namespace Test {
39
40 struct _Command{
41     char * mLine;
42     StandaloneConsole *mConsole;
43     pthread_mutex_t *mLock;
44     _Command(const char * cmd, StandaloneConsole *console, pthread_mutex_t* lock){
45         mLine = strdup(cmd);
46         mConsole = console;
47         mLock = lock;
48     }
49     ~_Command(){
50         free(mLine);
51     }
52     void run(){
53         mConsole->RunLine(mLine);
54         pthread_mutex_unlock(mLock);
55     }
56 };
57
58 struct CallbackData{
59     JSObjectRef callback;
60     int id;
61     StandaloneConsole *console;
62 };
63
64 static Eina_Bool tick(void *data){
65     return true;
66 }
67
68 static Eina_Bool commandDispath(void *data){
69     _Command *cmd = (_Command*)data;
70     cmd->run();
71     delete cmd;
72     return false;
73 }
74
75 Eina_Bool StandaloneConsole::timerCb(void *data){
76     CallbackData *callback = (CallbackData*)data;
77     StandaloneConsole *console = callback->console;
78     map<int,int>::iterator itr;
79     itr = console->mTimerMap.find(callback->id);
80     if( itr == console->mTimerMap.end() ){
81         JSValueUnprotect(console->getGlobalContext(), callback->callback);
82         delete callback;
83         return false;
84     }
85     if( itr->second == 0){
86         console->mTimerMap.erase(itr);
87         JSValueUnprotect(console->getGlobalContext(), callback->callback);
88         delete callback;
89         return false;
90     }
91     if( callback->callback != NULL){
92         JSObjectCallAsFunction(console->getGlobalContext(), callback->callback, NULL, 0, 0, NULL);
93     }
94
95     if( itr->second == 2 ){
96         console->mTimerMap.erase(itr);
97         JSValueUnprotect(console->getGlobalContext(), callback->callback);
98         delete callback;
99         return false;
100     }
101
102     return true;
103 }
104
105
106 JSValueRef StandaloneConsole::alert(JSContextRef ctx, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception){
107     if( argumentCount < 1 )
108         return JSValueMakeUndefined(ctx);
109
110
111     //JSContextRef globalCtx = GlobalContextManager::getInstance()->getGlobalContext(ctx);
112     //printf(" local : %p, global : %p \n", ctx, globalCtx);
113
114     JSStringRef str = JSValueToStringCopy(ctx, arguments[0], NULL);
115     if(str == NULL){
116         return JSValueMakeUndefined(ctx);
117     }
118     int n = JSStringGetLength(str);
119     {
120         char cstr[n+1];
121         JSStringGetUTF8CString(str, cstr,n+1);
122         printf("<alert>%s\n", cstr);
123     }
124     return JSValueMakeUndefined(ctx);
125 }
126
127 JSValueRef StandaloneConsole::setInterval(JSContextRef ctx, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception){
128     static int id = 0;
129     StandaloneConsole *console = static_cast<StandaloneConsole*>(JSObjectGetPrivate(thisObject));
130     if( argumentCount < 2 ){
131         if( exception != NULL){
132
133         }
134         return JSValueMakeUndefined(ctx);
135     }
136     int handleid = id++;
137     double interval = JSValueToNumber(ctx, arguments[1], NULL);
138     interval = interval/1000;
139
140     console->mTimerMap.insert(pair<int,int>(handleid, 1));
141     CallbackData *data = new CallbackData();
142     JSValueProtect(console->getGlobalContext(), arguments[0]);
143     data->callback = JSValueToObject(ctx, arguments[0], NULL);
144     data->id = handleid;
145     data->console = console;
146
147     ecore_timer_add( interval, StandaloneConsole::timerCb , data);
148     return JSValueMakeNumber(ctx, handleid);
149
150 }
151
152 JSValueRef StandaloneConsole::setTimeout(JSContextRef ctx, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception){
153     static int id = 0;
154     StandaloneConsole *console = static_cast<StandaloneConsole*>(JSObjectGetPrivate(thisObject));
155
156     if( argumentCount < 2 ){
157         if( exception != NULL){
158
159         }
160         return JSValueMakeUndefined(ctx);
161     }
162     int handleid = id++;
163     double interval = JSValueToNumber(ctx, arguments[1], NULL);
164     interval = interval/1000;
165
166     console->mTimerMap.insert(pair<int,int>(handleid, 2));
167     CallbackData *data = new CallbackData();
168     JSValueProtect(console->getGlobalContext(), arguments[0]);
169     data->callback = JSValueToObject(ctx, arguments[0], NULL);
170     data->id = handleid;
171     data->console = console;
172
173     ecore_timer_add( interval, StandaloneConsole::timerCb , data);
174     return JSValueMakeNumber(ctx, handleid);
175
176 }
177
178
179 JSValueRef StandaloneConsole::clearInterval(JSContextRef ctx, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception){
180     StandaloneConsole *console = static_cast<StandaloneConsole*>(JSObjectGetPrivate(thisObject));
181     if(console == NULL) return JSValueMakeUndefined(ctx);
182     if( argumentCount < 1 ){
183         printf("error clearInterval\n");
184         if( exception != NULL){
185
186         }
187         return JSValueMakeUndefined(ctx);
188     }
189
190     int handleid = JSValueToNumber(ctx, arguments[0], NULL);
191     map<int,int>::iterator it;
192     it = console->mTimerMap.find(handleid);
193     if( it != console->mTimerMap.end())
194         console->mTimerMap[handleid] = 0;
195     return JSValueMakeUndefined(ctx);
196 }
197
198
199 static JSValueRef test(JSContextRef ctx, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception){
200     return JSValueMakeUndefined(ctx);
201 }
202
203
204 static void setProperty(JSContextRef ctx , JSObjectRef object, const char *name, JSValueRef value, JSPropertyAttributes attributes)
205 {
206         JSStringRef propertyName = JSStringCreateWithUTF8CString(name);
207         JSObjectSetProperty(ctx, object, propertyName, value,attributes, NULL );
208         JSStringRelease(propertyName);
209 }
210
211 static JSValueRef getProperty(JSContextRef ctx , JSObjectRef object, const char *name){
212         JSValueRef value;
213         JSStringRef propertyName = JSStringCreateWithUTF8CString(name);
214         value = JSObjectGetProperty(ctx, object, propertyName, NULL);
215         JSStringRelease(propertyName);
216         return value;
217 }
218
219 static char * toString(JSContextRef ctx , JSValueRef jsV){
220     JSValueRef exception = NULL;
221     JSStringRef jsStr = JSValueToStringCopy(ctx, jsV, &exception);
222     if( exception != NULL )
223         return NULL;
224     int n = JSStringGetMaximumUTF8CStringSize(jsStr);
225     char *buf = new char[n+1];
226     JSStringGetUTF8CString(jsStr, buf, n+1);
227     JSStringRelease(jsStr);
228     return buf;
229 }
230
231 StandaloneConsole::StandaloneConsole():mGlobalContext(NULL),mGlobalObject(NULL){
232 }
233
234 StandaloneConsole::~StandaloneConsole(){
235 }
236
237 void StandaloneConsole::initialize(){
238     // Function table
239     JSStaticFunction functions[] = {
240         { "alert", StandaloneConsole::alert , kJSPropertyAttributeNone },
241         { "setInterval", StandaloneConsole::setInterval , kJSPropertyAttributeNone },
242         { "setTimeout", StandaloneConsole::setTimeout , kJSPropertyAttributeNone },
243         { "clearInterval", StandaloneConsole::clearInterval , kJSPropertyAttributeNone },
244         { "clearTimeout", StandaloneConsole::clearInterval , kJSPropertyAttributeNone },
245         { "test", test, kJSPropertyAttributeNone },
246         { 0, 0, 0 }
247     };
248
249     // Global class
250     JSClassDefinition def = {
251         0,                                 // current (and only) version is 0
252         kJSClassAttributeNone,   //attributes
253         "global",                        //class name
254         NULL,                            // parent class
255         NULL,                            //static values
256         functions,                      // static functions
257         NULL,                            // initialize
258         NULL,                            //finalize
259         NULL,                            //hasProperty
260         NULL,                            //getProperty
261         NULL,                            //setProperty
262         NULL,                            //deleteProperty
263         NULL,                            //getPropertyNames
264         NULL,                            // callAsConstructor
265         NULL,                            // constructor
266         NULL,
267         NULL                             // convertToType
268     };
269
270     JSClassRef globalClass = JSClassCreate(&def);
271
272     mGlobalContext = JSGlobalContextCreate(globalClass);
273     mGlobalObject = JSContextGetGlobalObject(mGlobalContext);
274     JSObjectSetPrivate(mGlobalObject, this);
275     JSObjectRef console = JSConsole::createJSObject(mGlobalContext);
276     setProperty(mGlobalContext, mGlobalObject, "console", console, kJSPropertyAttributeReadOnly);
277
278
279     //is it ecore bug? event was not invoke, it was added in another thread
280     ecore_timer_add(0.001, tick, NULL);
281 }
282
283
284 JSObjectRef StandaloneConsole::getGlobalObject(){
285     return mGlobalObject;
286 }
287
288 JSContextRef StandaloneConsole::getGlobalContext(){
289     return mGlobalContext;
290 }
291
292
293 JSValueRef StandaloneConsole::RunLineEx(const char* line, JSValueRef *exception){
294     JSStringRef jsScript = JSStringCreateWithUTF8CString(line);
295     int size = strlen(line);
296     if( size != static_cast <int>(JSStringGetLength(jsScript))){
297         cout <<"error - fail to converting JSStringRef"<<endl;
298     }
299     JSValueRef ret = JSEvaluateScript(mGlobalContext, jsScript, NULL, NULL, 0, exception);
300     JSStringRelease(jsScript);
301     return ret;
302 }
303
304 JSValueRef StandaloneConsole::RunScriptEx(const char* path, JSValueRef *exception){
305
306     FILE* f = fopen(path, "r");
307     if( f == NULL ){
308         return NULL;
309     }
310
311     fseek(f, 0, SEEK_END);
312     int length = ftell(f);
313     fseek(f, 0, SEEK_SET);
314
315     if( length > 0 )
316     {
317         char buff[length+1];
318         memset(buff, '\0', length+1);
319         int r = fread(buff, 1, length, f);
320         fclose(f);
321
322         if( r != length ){
323             printf("error read\n");
324             return JSValueMakeUndefined(mGlobalContext);
325         }
326         return RunLineEx(buff, exception);
327     }
328     fclose(f);
329     return JSValueMakeUndefined(mGlobalContext);
330 }
331
332 void StandaloneConsole::RunLine(const char * line){
333     JSValueRef exception = NULL;
334     JSValueRef v = RunLineEx(line, &exception);
335     reportingResult(v,exception);
336 }
337
338 void StandaloneConsole::RunScript(const char * path){
339     JSValueRef exception = NULL;
340     JSValueRef v = RunScriptEx(path, &exception);
341     reportingResult(v,exception);
342 }
343
344 void StandaloneConsole::GarbageCollect(){
345     printf("GarbageCollect\n");
346     JSGarbageCollect(mGlobalContext);
347 }
348 void StandaloneConsole::reportingResult(JSValueRef v, JSValueRef exception){
349     if( exception != NULL ){
350         char *errStr = toString(mGlobalContext, exception);
351         if( errStr != NULL ){
352             printf("< error - %s\n", errStr);
353             delete[] errStr;
354         }
355         JSObjectRef errObj = JSValueToObject(mGlobalContext, exception, NULL);
356         if( errObj != NULL ){
357             JSValueRef stack = getProperty(mGlobalContext, errObj, "stack");
358             char *stackStr = NULL;
359             if( !JSValueIsUndefined(mGlobalContext, stack) && (stackStr = toString(mGlobalContext, stack )) != NULL){
360                 printf("stack:%s\n", stackStr);
361                 delete[] stackStr;
362             }
363         }
364     }else{
365         char *resultStr = toString(mGlobalContext, v);
366         if( resultStr != NULL ){
367             printf("< %s\n", resultStr);
368             delete[] resultStr;
369         }
370     }
371 }
372
373 JSObjectRef StandaloneConsole::registModule(const char * name, JSClassRef module, void * priv){
374     JSObjectRef obj = JSObjectMake(mGlobalContext, module, priv);
375     setProperty(mGlobalContext, mGlobalObject, name, obj, kJSPropertyAttributeReadOnly);
376     return obj;
377 }
378
379 void StandaloneConsole::appendModule(const char * name, JSObjectRef module){
380     setProperty(mGlobalContext, mGlobalObject, name, module, kJSPropertyAttributeReadOnly);
381 }
382
383
384 int getch(void)
385 {
386     int ch;
387     struct termios buf;
388     struct termios save;
389
390     tcgetattr(0, &save);
391     buf = save;
392     buf.c_lflag &= ~(ICANON|ECHO);
393     buf.c_cc[VMIN] = 1;
394     buf.c_cc[VTIME] = 0;
395     tcsetattr(0, TCSAFLUSH, &buf);
396     ch = getchar();
397     tcsetattr(0, TCSAFLUSH, &save);
398     return ch;
399 }
400
401 struct termios gSave;
402
403 void onExit(void)
404 {
405     tcsetattr(0, TCSAFLUSH, &gSave);
406 }
407
408 class LineBuffer{
409     vector<string> mHistory;
410     string mLine;
411     int mHistoryIndex;
412     unsigned int mCurrentPos;
413     unsigned int mCurrentPosTmp;
414     int mLineLength;
415 public:
416     LineBuffer():mHistoryIndex(0), mCurrentPos(0){
417         tcgetattr(0, &gSave);
418         atexit(onExit);
419     }
420     ~LineBuffer(){
421         tcsetattr(0, TCSAFLUSH, &gSave);
422     }
423
424     void backSpace( int length ){
425         for( int i =0 ; i < length ; i++){
426             putchar('\b');
427             putchar(' ');
428             putchar('\b');
429         }
430     }
431
432     void cleanLine(){
433         int diff = mLineLength - mCurrentPosTmp;
434         while( diff-- > 0 ){
435             moveCursor(false);
436         }
437         backSpace(mLineLength);
438     }
439
440     void applyHistory( unsigned int index ){
441         if( mHistory.size() > index ){
442             mLine = mHistory[index];
443             mCurrentPos = mLine.size();
444         }
445     }
446
447     void moveCursor( bool Left ){
448         putchar(27);putchar(91);
449         if( Left )
450             putchar(68);
451         else
452             putchar(67);
453     }
454
455     void moveCurrentCursorPosition(){
456         int diff = mLine.size() - mCurrentPos;
457
458         while( diff-- > 0 ){
459             moveCursor(true);
460         }
461     }
462
463     bool checkSpecialKeys(int a){
464         if( a == 8 ){
465             if( mLine.size() != 0 && mCurrentPos != 0){
466                 mCurrentPos--;
467                 mLine.erase(mCurrentPos,1);
468             }
469             return true;
470         }
471         if( a == 27 ){
472             a = getch(); // 91
473             a = getch();
474             switch( a ){
475                 case 65:
476                     //UP
477                     if( mHistoryIndex > 0 ){
478                         applyHistory(--mHistoryIndex);
479                     }
480                     break;
481                 case 66:
482                     //DOWN
483                     if( (unsigned)mHistoryIndex < mHistory.size() ){
484                         applyHistory(++mHistoryIndex);
485                     }
486                     break;
487                 case 67:
488                     //RIGHT
489                     if( mCurrentPos < mLine.size())
490                         mCurrentPos++;
491                     break;
492                 case 68:
493                     //LEFT
494                     if( mCurrentPos > 0 )
495                         mCurrentPos--;
496                     break;
497                 case 51:
498                     //delete
499                     getch();
500                     if( mCurrentPos < mLine.size())
501                         mLine.erase(mCurrentPos,1);
502                     break;
503                 case 52:
504                     //end
505                     getch();
506                     mCurrentPos = mLine.size();
507                     break;
508                 case 49:
509                     //home
510                     mCurrentPos = 0;
511                     a = getch();
512                     break;
513                 default:
514                     a = getch();
515             }
516
517             return true;
518         }
519         return false;
520     }
521
522     string Prompt(const char * prompt){
523         printf("%s", prompt);
524         mCurrentPos = 0;
525         mLine.clear();
526         mLineLength = mLine.size();
527         mCurrentPosTmp = mCurrentPos;
528         while(1){
529             int a = getch();
530             cleanLine();
531             if( a == 10 )
532                 break;
533
534             if(!checkSpecialKeys(a)){
535                 mLine.insert(mCurrentPos,1, a);
536                 mCurrentPos++;
537             }
538             cout << mLine;
539             moveCurrentCursorPosition();
540             mLineLength = mLine.size();
541             mCurrentPosTmp = mCurrentPos;
542         }
543         cout << mLine;
544         if( mLine.size() > 0 ){
545             mHistory.push_back(mLine);
546             mHistoryIndex = mHistory.size();
547         }
548         return mLine;
549     }
550
551 };
552
553
554
555 void StandaloneConsole::commandline(StandaloneConsole* console){
556     pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
557     printf("command line mode ( \"quit\" for exit  )\n");
558     LineBuffer linebuff;
559     while(1){
560         pthread_mutex_lock(&lock);
561         string line = linebuff.Prompt(">");
562         printf("\n");
563
564         if( line == "quit" )
565             break;
566         if( line == "gc" ){
567             console->GarbageCollect();
568             continue;
569         }
570         if( line.size() == 0 )
571             continue;
572         _Command *cmd = new _Command(line.c_str(), console, &lock);
573         // for thread safety
574         ecore_idler_add(commandDispath, cmd);
575     }
576 }
577
578
579 }
580 }
581