From: <ny...@us...> - 2006-04-04 06:32:34
|
Revision: 6 Author: nyaochi Date: 2006-04-03 23:32:21 -0700 (Mon, 03 Apr 2006) ViewCVS: http://svn.sourceforge.net/pmplib/?rev=6&view=rev Log Message: ----------- Revised the JSPL specification. - JavaScript playlist must have one main() function - The main() function must return either an array of tracks or a JavaScript object with multiple playlists whose names are represented in attributes of the object and values store arrays of tracks. - Previous specification (i.e., match and comapre functions) was discarded. Modified Paths: -------------- trunk/include/playlist.h trunk/lib/playlist/jspl.c Modified: trunk/include/playlist.h =================================================================== --- trunk/include/playlist.h 2006-03-31 05:41:31 UTC (rev 5) +++ trunk/include/playlist.h 2006-04-04 06:32:21 UTC (rev 6) @@ -42,6 +42,22 @@ PLCALLBACK_JSPL_ERROR, }; +enum { + PLAYLIST_SUCCESS = 0, + PLAYLIST_E_FAIL = 0x80000000, + PLAYLIST_E_OUTOFMEMORY, + PLAYLIST_E_JSINITENGINE, + PLAYLIST_E_JSINITMEDIA, + PLAYLIST_E_JSEVALSCRIPT, + PLAYLIST_E_JSMAINNOTFOUND, + PLAYLIST_E_JSCALLMAIN, + PLAYLIST_E_JSINVALIDARRAY, + PLAYLIST_E_JSINVALIDCAST, + PLAYLIST_E_JSINVALIDOBJECT, + PLAYLIST_E_JSINVALIDTRACK, +}; + + typedef void (*playlist_callback_t)(void *instance, int level, ucs2char_t* message); struct tag_playlist_entry { Modified: trunk/lib/playlist/jspl.c =================================================================== --- trunk/lib/playlist/jspl.c 2006-03-31 05:41:31 UTC (rev 5) +++ trunk/lib/playlist/jspl.c 2006-04-04 06:32:21 UTC (rev 6) @@ -50,25 +50,26 @@ int num_records; JSObject **records; + JSObject *tracks; void *instance; playlist_callback_t callback; }; typedef struct tag_jspl jspl_t; -static jschar* JS_ucstrdup(JSContext *cx, ucs2char_t* src, size_t* ptr_length) +static jschar* JS_ucstrdup(JSContext *cx, const ucs2char_t* src, size_t* ptr_length) { - int i; - size_t length = ucs2len(src); + size_t i, length = ucs2len(src); jschar* dst = JS_malloc(cx, length * sizeof(jschar)); - for (i = 0;i < length;++i) { - dst[i] = src[i]; + if (dst) { + for (i = 0;i < length;++i) { + dst[i] = src[i]; + } + if (ptr_length) { + *ptr_length = length; + } } - - if (ptr_length) { - *ptr_length = length; - } return dst; } @@ -239,6 +240,21 @@ { int i; + /* Allocate a JavaScript array object. + * According to the notes for JS_NewArrayObject() function in the + * reference document, we should create an empty array, store the + * returned object in a GC root, populate its element, and then + * drop the root object to avoid unrooted jsvals in vector from + * being subject to garbage collection until the new object has + * been populated + */ + jspl->tracks = JS_NewArrayObject(jspl->context, 0, NULL); + + /* */ + if (!JS_AddRoot(jspl->context, &jspl->tracks)) { + return 1; + } + /* Allocate a records array. */ free(jspl->records); jspl->records = (JSObject**)calloc(num_records, sizeof(JSObject*)); @@ -246,6 +262,7 @@ for (i = 0;i < num_records;++i) { const pmp_record_t* record = &records[i]; JSObject* obj = NULL; + jsval jsval_obj; /* Create a JavaScript object. */ obj = JS_NewObject( @@ -259,7 +276,7 @@ } /* Attach properties to the object. */ - set_property_int(jspl, obj, "index", i); + set_property_int(jspl, obj, "id", i); set_property_string(jspl, obj, "filename", record->filename); set_property_string(jspl, obj, "title", record->title); set_property_string(jspl, obj, "artist", record->artist); @@ -280,9 +297,18 @@ /* Register the object pointer to the record array. */ jspl->records[i] = obj; + + jsval_obj = OBJECT_TO_JSVAL(obj); + if (!JS_SetElement(jspl->context, jspl->tracks, i, &jsval_obj)) { + return 1; + } } jspl->num_records = num_records; + if (!JS_RemoveRoot(jspl->context, &jspl->tracks)) { + return 1; + } + return 0; } @@ -364,37 +390,45 @@ #endif } -typedef struct { - jspl_t *jspl; - int index; -} sortitem_t; - -static int comp_order(const void* _x, const void* _y) +static int generate_playlist( + jspl_t* jspl, + playlists_t* pls, + JSObject* jsobj_array, + const ucs2char_t* name + ) { - jsval argv[2], retval; - const sortitem_t* x = (const sortitem_t*)_x; - const sortitem_t* y = (const sortitem_t*)_y; - jspl_t* jspl = x->jspl; - int ret = COMP(x->index, y->index); - - /* Call match() function. */ - argv[0] = OBJECT_TO_JSVAL(jspl->records[x->index]); - argv[1] = OBJECT_TO_JSVAL(jspl->records[y->index]); - if (JS_CallFunctionName(jspl->context, jspl->global, "compare", 2, argv, &retval)) { - if (JSVAL_IS_INT(retval)) { - ret = JSVAL_TO_INT(retval); - } else if (JSVAL_IS_DOUBLE(retval)) { - jsdouble* d = JSVAL_TO_DOUBLE(retval); - if (*d == 0) { - ret = 0; - } else { - ret = *d < 0 ? -1 : 1; + int ipl = playlist_add_playlist(pls, name); + if (ipl >= 0) { + playlist_t* pl = &pls->playlists[ipl]; + jsuint i, length; + + if (JS_HasArrayLength(jspl->context, jsobj_array, &length)) { + for (i = 0;i < length;++i) { + jsval jsval_element; + if (JS_GetElement(jspl->context, jsobj_array, (jsint)i, &jsval_element)) { + JSObject* jsobj_element = JSVAL_TO_OBJECT(jsval_element); + jsval jsval_filename; + if (JS_GetProperty(jspl->context, jsobj_element, "filename", &jsval_filename)) { + ucs2char_t* filename = JSString_to_ucs2(JSVAL_TO_STRING(jsval_filename)); + playlist_append(pl, filename, i); + } else { + return PLAYLIST_E_JSINVALIDTRACK; + } + } else { + return PLAYLIST_E_JSINVALIDARRAY; + } } + } else { + return PLAYLIST_E_JSINVALIDARRAY; } + } else { + return PLAYLIST_E_OUTOFMEMORY; } - return ret; + + return 0; } + int playlist_jspl_read( playlists_t* pls, const ucs2char_t *filename, @@ -404,16 +438,17 @@ void *instance ) { - int i, j, ret = 0; + int ret = 0; jspl_t* jspl = NULL; ucs2char_t name[MAX_PATH]; JSString* jstr_name = NULL; size_t length = 0; jschar* js_name = NULL; + jsval argv[2], retval; /* Intialize JavaScript engine. */ if (jspl_init(&jspl) != 0) { - ret = 1; + ret = PLAYLIST_E_JSINITENGINE; goto error_exit; } @@ -422,13 +457,13 @@ /* Create JavaScript objects represent media information of the records. */ if (jspl_set(jspl, records, num_records) != 0) { - ret = 1; + ret = PLAYLIST_E_JSINITMEDIA; goto error_exit; } /* Load the script. */ if (jspl_load_script(jspl, filename) != 0) { - ret = 1; + ret = PLAYLIST_E_JSEVALSCRIPT; goto error_exit; } @@ -439,68 +474,79 @@ /* Convert the type of the playlist name to JavaScript string. */ js_name = JS_ucstrdup(jspl->context, name, &length); if (!js_name) { - ret = 1; + ret = PLAYLIST_E_OUTOFMEMORY; goto error_exit; } jstr_name = JS_NewUCString(jspl->context, js_name, length); if (!jstr_name) { - ret = 1; + ret = PLAYLIST_E_OUTOFMEMORY; goto error_exit; } - if (!function_exists(jspl, "match")) { - ret = 1; + /* Check if main() function exists. */ + if (!function_exists(jspl, "main")) { + ret = PLAYLIST_E_JSMAINNOTFOUND; goto error_exit; } - /* Generate playlist(s) by applying match() function. */ - for (i = 0;i < jspl->num_records;++i) { - jsval argv[2], retval; + /* Call main() function. */ + argv[0] = OBJECT_TO_JSVAL(jspl->tracks); + argv[1] = STRING_TO_JSVAL(jstr_name); + if (!JS_CallFunctionName(jspl->context, jspl->global, "main", 2, argv, &retval)) { + ret = PLAYLIST_E_JSCALLMAIN; + goto error_exit; + } - /* Call match() function. */ - argv[0] = OBJECT_TO_JSVAL(jspl->records[i]); - argv[1] = STRING_TO_JSVAL(jstr_name); - if (!JS_CallFunctionName(jspl->context, jspl->global, "match", 2, argv, &retval)) { - continue; + /* Check if the returned value is a JavaScript object. */ + if (JSVAL_IS_OBJECT(retval) && !JSVAL_IS_PRIMITIVE(retval)) { + /* Convert the return value to JSObject. */ + JSObject* jsobj = JSVAL_TO_OBJECT(retval); + if (!jsobj) { + ret = PLAYLIST_E_JSINVALIDCAST; + goto error_exit; } - if (JSVAL_IS_BOOLEAN(retval) && JSVAL_TO_BOOLEAN(retval)) { - /* Add this media file to the default playlist. */ - int index = playlist_add_playlist(pls, name); - if (index >= 0) { - playlist_t* pl = &pls->playlists[index]; - playlist_append(pl, records[i].filename, i); + if (JS_IsArrayObject(jspl->context, jsobj)) { + /* Single playlist when the returned value is JavaScript array. */ + ret = generate_playlist(jspl, pls, jsobj, name); + if (ret != 0) { + goto error_exit; } - } else if (JSVAL_IS_STRING(retval)) { - /* Add this media file to a playlist named as the retval. */ - ucs2char_t* thisname = JSString_to_ucs2(JSVAL_TO_STRING(retval)); - int index = playlist_add_playlist(pls, thisname); - if (index >= 0) { - playlist_t* pl = &pls->playlists[index]; - playlist_append(pl, records[i].filename, i); + } else { + /* Multiple playlists when the returned value is JavaScript object with attributes. */ + jsint n; + JSIdArray* keys = JS_Enumerate(jspl->context, jsobj); + if (!keys) { + ret = PLAYLIST_E_JSINVALIDOBJECT; + goto error_exit; } - ucs2free(thisname); - } - } - /* Sort the playlist(s). */ - if (function_exists(jspl, "compare")) { - for (i = 0;i < pls->num_playlists;++i) { - playlist_t* pl = &pls->playlists[i]; - sortitem_t* sis = calloc(pl->num_entries, sizeof(sortitem_t)); - - for (j = 0;j < pl->num_entries;++j) { - sis[j].jspl = jspl; - sis[j].index = pl->entries[j].order; + /* Loop over attributes to enumerate all playlist names. */ + for (n = 0;n < keys->length;++n) { + /* Obtain the playlist name. */ + jsval jsval_name; + if (JS_IdToValue(jspl->context, keys->vector[n], &jsval_name)) { + jsval jsval_obj; + JSString* jsstr_name = JSVAL_TO_STRING(jsval_name); + + /* Obtain the attribute value for the playlist name. */ + if (JS_LookupUCProperty(jspl->context, jsobj, JS_GetStringChars(jsstr_name), JS_GetStringLength(jsstr_name), &jsval_obj)) { + if (JSVAL_IS_OBJECT(jsval_obj) && !JSVAL_IS_PRIMITIVE(jsval_obj)) { + JSObject* jsobj = JSVAL_TO_OBJECT(jsval_obj); + if (jsobj && JS_IsArrayObject(jspl->context, jsobj)) { + ucs2char_t* thisname = JSString_to_ucs2(jsstr_name); + ret = generate_playlist(jspl, pls, jsobj, thisname); + if (ret != 0) { + goto error_exit; + } + } + } + } + } } - - qsort(sis, pl->num_entries, sizeof(sortitem_t), comp_order); - - for (j = 0;j < pl->num_entries;++j) { - int index = sis[j].index; - ucs2cpy(pl->entries[j].filename, records[index].filename); - } } + } else { + /* Does nothing when main() function returns a non-object. */ } error_exit: This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |