blob: 7aac796f206ef09621f46fba0fedeb448e89cefe [file] [log] [blame]
/* --COPYRIGHT--,EPL
* Copyright (c) 2008-2020 Texas Instruments Incorporated
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Texas Instruments - initial implementation
*
* --/COPYRIGHT--*/
/*
* ======== Program ========
* This module is structured such that all of the work of fetching and
* decoding structures is handled by the decoder, and all processing of view
* init code is done by Program.
*
* The caching policy is that caching is done by the callee, so Program may
* request data from the decoder and expect that if the data is already
* available the decoder will return immediately.
*/
/* global xdc, Program */
var cmodTab = [];
var modTab = [];
var statusTab = new Array();
var timestampFunc;
var throwScanErrors = true; /* default to true for backward compatibility */
var Model = xdc.module("xdc.rov.Model");
/*
* ======== getSupportedTabs ========
* Functions of the form view$init$instance$* and view$init$module$* define
* the set of tabs supported by the module.
*/
function getSupportedTabs(modName)
{
/*
* Don't use getModuleDesc in order to avoid running useModule.
* The initial call of useModule will take the longest, so put it off
* until we have to.
*/
var mod = this.getModuleDesc(modName);
//var modCfg = Program.$modules[modName];
var tabs = new Array();
/*
* If the module is missing from the package, return a fake tab which will
* display an error message.
*/
if (mod.loadFailed) {
var tab = new Program.Tab();
tab.name = Program.loadModFailed;
tab.type = Program.getViewType(modName, tab.name);
tabs[tabs.length] = tab;
return (tabs);
}
/* Find the supported tabs in the module's view map */
if (mod.viewInfo != null) {
for each (var viewName in mod.viewInfo.viewMap.$keys) {
var tab = new Program.Tab();
tab.name = viewName;
tab.type = Program.getViewType(modName, viewName);
tabs[tabs.length] = tab;
}
/* Don't add the raw tab if showRawTab is false */
if (mod.viewInfo.showRawTab == false) {
return (tabs);
}
}
/* if mod.viewInfo.showRawTab is true, add the Raw tab */
var ViewInfo = xdc.useModule('xdc.rov.ViewInfo');
var tab = new Program.Tab();
tab.name = "Raw";
tab.type = String(ViewInfo.RAW);
tabs[tabs.length] = tab;
return (tabs);
}
/*
* ======== getViewType ========
* Returns the view type of the specified tab as an xdc.rov.ViewInfo.ViewType.
*/
function getViewType(modName, tabName)
{
tabName = _getTabName(tabName); /* remove any optional args */
if (tabName.equals("Raw")) {
var ViewInfo = xdc.useModule('xdc.rov.ViewInfo');
return (String(ViewInfo.RAW));
}
var mod = this.getModuleDesc(modName);
/*
* If the load failed, there is only one tab, which will display
* an error message.
*/
if (mod.loadFailed) {
var ViewInfo = xdc.useModule('xdc.rov.ViewInfo');
return (String(ViewInfo.RAW));
}
/* Throw an error if the module does not support the tab */
if (mod.viewInfo == null ||
mod.viewInfo.viewMap[tabName] == null) {
throw (new Error("Tab " + tabName + " not in module " + modName));
}
return String(mod.viewInfo.viewMap[tabName].type);
}
/*
* ======== scanModuleView ========
* Processes the specified module-level view of the specified module.
* This function will verify that the specified module supports the specified
* tab, so that all other functions may assume that the tab is supported.
*/
function scanModuleView(modName, args)
{
var tabName = _getTabName(args);
var tabArgs = _getTabArgs(args);
var modDesc = this.getModuleDesc(modName);
/* Verify the module supports the requested tab */
if (!modSupportsTab(modName, tabName)) {
throw (new Error(modName + " does not support the tab '"
+ tabName + "'"));
}
/* Check if the requested view has already been scanned */
if (modDesc.viewMap[tabName] != undefined) {
/* Before returning it, check if there were any problems */
var view = modDesc.viewMap[tabName];
if (throwScanErrors) {
for (var field in view.$status) {
if (view.$status[field]) {
throw (new Error("ROV detected errors in scan of '"
+ tabName
+ "' tab for module " + modName + "."));
}
}
}
return (view);
}
/*
* Make sure the module state is read in. Returns immediately if
* the state is already scanned, sets modDesc.state = null if there is no
* module state.
*/
this.stateReader.fetchModuleState(modDesc);
/* Run the view function */
var noErrors = runViewInit(modDesc, modDesc, tabName, "module", tabArgs);
/*
* If there were errors, throw an exception, so that it must be caught and
* handled if the caller wants to continue.
*/
if (throwScanErrors && !noErrors) {
throw (new Error("ROV detected errors in scan of '" + tabName +
"' tab for module " + modName + "."));
}
/* Return the view */
return (modDesc.viewMap[tabName]);
}
/*
* ======== scanInstanceView ========
* Checks to see if the particular instance view has already been scanned.
* If it has, it simply returns. Otherwise, it calls in to the decoder as
* necessary to scan the instance view.
* Once the instance view is scanned, it can be accessed through:
* for (var i in mod.instances) {
* var instView = mod.instances[i].viewMap[tabName];
* }
*/
function scanInstanceView(modName, args)
{
var tabName = _getTabName(args);
var tabArgs = _getTabArgs(args);
debugPrint("xdc.rov.Program: Scanning instance views for " + modName + " "
+ tabName);
/* Get the module from cache or useModule */
var mod = this.getModuleDesc(modName);
/* Verify the module supports the requested tab */
if (!modSupportsTab(modName, tabName)) {
throw (new Error("Module does not support the requested view level"));
}
if (!('Instance_State' in mod.useMod)) {
throw (new Error("Calling scanInstance on module that does not support instances"));
}
/*
* Scan in the instance state structures.
* Returns immediately if they were already scanned.
*/
this.stateReader.fetchAllInstStates(mod);
/* If there are no instances, return an empty array */
if (mod.instances.length == 0) {
return (new Array());
}
/* If we come across any problems, we'll throw an exception at the end */
var error = false;
/*
* At this point, we have the instance states, but may not
* have the requested view.
*
* For each of the module's instances, call the view function specified
* by tabName, then add the resulting view to the instance's $viewMap.
*/
debugPrint("xdc.rov.Program: Number of instances = " + mod.instances.length);
for each (var instDesc in mod.instances) {
debugPrint("xdc.rov.Program: Running view init on inst = 0x"
+ Number(instDesc.state.$addr).toString(16));
var res = runViewInit(mod, instDesc, tabName, "instance", tabArgs);
/* Set the error flag true if even a single instance had errors */
if (!res) {
error = true;
}
}
/*
* If there were errors, throw an exception, so that it must be caught and
* handled if the caller wants to continue.
*/
if (throwScanErrors && error) {
throw (new Error("ROV detected errors in scan of '" + tabName +
"' tab for module " + modName + "."));
}
/* Create an array of the requested instance views to return */
var instArr = new Array();
for each (var inst in mod.instances) {
instArr[instArr.length] = inst.viewMap[tabName];
}
return (instArr);
}
/*
* ======== newViewStruct ========
*/
function newViewStruct(modName, tabName)
{
/* Verify the module and tab names are valid */
if (!modSupportsTab(modName, tabName)) {
throw (new Error("Module " + modName + " does not support tab " +
tabName + "."));
}
var modDesc = this.getModuleDesc(modName);
var structName = modDesc.viewInfo.viewMap[tabName].structName;
/* Verify the specified structure exists in the module */
if (!(structName in modDesc.useMod)) {
throw (new Error("Module " + modName + " does not contain the " +
"view structure " + structName));
}
var struct = new modDesc.useMod[structName];
/* Create the status map and bind it to the struct */
var status = new Object();
for (var p in struct) {
status[p] = null;
}
struct.$$bind('$status', status);
// TODO: Seal the map's domain? Would need to add address field before
// sealing.
return (struct);
}
/*
* ========= scanInstanceDataView ==========
* TODO - Added error handling
*/
function scanInstanceDataView(modName, args)
{
var tabName = _getTabName(args);
var tabArgs = _getTabArgs(args);
/* Get the module from cache or useModule */
var modDesc = this.getModuleDesc(modName);
/* Verify the module supports the requested tab */
if (!modSupportsTab(modName, tabName)) {
throw (new Error("Module does not support the requested view level"));
}
/* Check if the view has already been scanned */
if (modDesc.viewMap[tabName] != undefined) {
/* TODO: Before returning check for errors; throw exception if found */
return (modDesc.viewMap[tabName]);
}
var Program = xdc.useModule('xdc.rov.Program');
/*
* If there is any problem reading any of the state structures,
* the scan will throw an exception.
*/
var rawView = Program.scanRawView(modName);
var viewInitFunc = getViewInitFunc(modDesc, tabName);
var dataArr = new Array();
/* Track whether any errors were encountered */
var error = false;
/*
* If the module doesn't actually have RTSC instances, as is the case for
* C modules just call the view function once without passing in anything.
*/
if (modDesc.cfg == null) {
try {
dataArr = viewInitFunc.call(modDesc.userPrivate, tabArgs);
}
catch (e) {
/*
* Since there are no instances, there's nowhere to report
* the exception.
*/
debugPrint("xdc.rov.Program: Caught exception from view init: " +
Program.exToString(e));
}
for (var i = 0; i < dataArr.length; i++) {
var viewPage = dataArr[i];
for (var j = 0; j < viewPage.elements.length; j++) {
if (viewPage.elements[j].constructor.name !=
modDesc.viewInfo.viewMap[tabName].structName) {
throw new Error("The view '" + tabName + "' returned "
+ "an object whose type is different from '"
+ modDesc.viewInfo.viewMap[tabName].structName
+ "' on the page " + i + ", row " + j + ".");
}
}
}
/*
* Check abort flag again in case the view function caught the
* abort signal.
*/
if (Model.getICallBackInst().getAbortFlag()) {
throw (new Error("abort"));
}
/* Add the view to the module's viewMap */
modDesc.viewMap[tabName] = dataArr;
/*
* Check if there were any errors reported on any fields.
* Add any errors to the status table.
*/
for (var i = 0; i < dataArr.length; i++) {
var view = dataArr[i];
for (var j = 0; j < view.elements.length; j++) {
for (var p in view.elements[j]) {
if (view.elements[j].$status[p]) {
/* Record status in the status table for reference */
addStatusEntry(modName, tabName, view.label, p,
view.elements[j].$status[p]);
error = true;
}
}
}
}
}
/*
* Otherwise, if the module does have instances, call the view function
* once per instance, passing in a view structure and the instance state.
*/
else {
for each (var instDesc in modDesc.instances) {
var instDataView = new Program.InstDataView;
/* Run the view init function for this instance */
try {
viewInitFunc.call(modDesc.userPrivate, instDataView, instDesc.state, tabArgs);
}
catch (e) {
var exception = "Caught exception in view init code: " + e.toString();
/* Add the exception to the error map to display */
instDesc.errorMap[tabName] = exception;
debugPrint("xdc.rov.Program: " + exception);
/* Record the exception in the status table for reference */
addStatusEntry(modName, tabName, instDataView.label, "N/A", exception);
}
/*
* Check abort flag again in case the view function caught the
* abort signal.
*/
if (Model.getICallBackInst().getAbortFlag()) {
throw (new Error("abort"));
}
/* Add the view to the array to return */
dataArr[dataArr.length] = instDataView;
/* Add the view to the instance's viewMap */
instDesc.viewMap[tabName] = instDataView;
/*
* Check if there were any errors reported on any fields.
* Add any errors to the status table.
*/
for each (var view in instDataView.elements) {
for (var p in view.$status) {
if (view.$status[p]) {
/* Record status in the status table for reference */
addStatusEntry(modName, tabName, instDataView.label, p,
view.$status[p]);
error = true;
}
}
}
}
}
/*
* If there were errors, throw an exception, so that it must be caught and
* handled if the caller wants to continue.
*/
if (throwScanErrors && error) {
throw (new Error("ROV detected errors in scan of '" + tabName +
"' tab for module " + modName + "."));
}
return (dataArr);
}
/*
* ========= scanModuleDataView ==========
*
*/
function scanModuleDataView(modName, args)
{
var tabName = _getTabName(args);
var tabArgs = _getTabArgs(args);
/* Get the module from cache or useModule */
var modDesc = this.getModuleDesc(modName);
/* Verify the module supports the requested tab */
if (!modSupportsTab(modName, tabName)) {
throw (new Error("Module does not support the requested view level"));
}
/* Check if the view has already been scanned */
if (modDesc.viewMap[tabName] != undefined) {
/* TODO: Before returning check for errors; throw exception if found */
return (modDesc.viewMap[tabName]);
}
var viewInitFunc = getViewInitFunc(modDesc, tabName);
var modDataView = new Program.ModDataView;
/* If the view code throws an exception, let it propagate up and be
* displayed to the user. This means the user won't see any partial data
* if there is any, but there's no other logical place to display the
* exception message.
* C-ROV modules return a view instead of filling up 'modDataView', so we
* don't even pass 'modDataView'.
*/
var dataView;
if (modDesc.cfg == null) {
dataView = viewInitFunc.call(modDesc.userPrivate, tabArgs);
/* Translate dataView into modDataView. If the function did not return
* the right type of objects, we need to report an error because the
* returned object needs to have $status property for each cell. If a
* user invokes Program.displayError() on a view of a wrong type,
* there will be an assignment to an undefined property.
*/
if (dataView instanceof Array) {
if (dataView.length > 0
&& (dataView[0].constructor.name !=
modDesc.viewInfo.viewMap[tabName].structName)) {
throw new Error("The function '" + viewInitFunc.name + "' is "
+ "not returning objects of the type '"
+ modDesc.viewInfo.viewMap[tabName].structName + "'.");
}
modDataView.elements = dataView;
}
else {
if (dataView.constructor.name !=
modDesc.viewInfo.viewMap[tabName].structName) {
throw new Error("The function '" + viewInitFunc.name + "' is "
+ "not returning objects of the type '"
+ modDesc.viewInfo.viewMap[tabName].structName + "'.");
}
var ar = new Array();
ar[0] = dataView;
modDataView.elements = ar;
}
}
else {
dataView = viewInitFunc.call(modDesc.userPrivate, modDataView, tabArgs);
}
/* Add the view to the module's viewMap */
modDesc.viewMap[tabName] = modDataView;
var error = false;
/* TODO - check for any errors and throw exception */
for (var i = 0; i < modDataView.elements.length; i++) {
var view = modDataView.elements[i];
for (var p in view) {
if (view.$status[p]) {
/* Record status in the status table for reference */
addStatusEntry(modName, tabName, "N/A", p, view.$status[p]);
error = true;
}
}
}
/*
if (throwScanErrors && error && !tabName.match("Scan for")) {
throw (new Error("ROV detected errors in scan of '" + tabName +
"' tab for module " + modName + "."));
}
*/
return (modDataView);
}
/*
* ======== runViewInit ========
* Process the view init code for the given descriptor and tabName.
* Returns false if any of the status fields report an error, returns true
* otherwise.
*
* This function is only used for instance and module type views.
*/
function runViewInit(mod, desc, tabName, type, tabArgs)
{
/* Check for abort flag before running view function */
if (Model.getICallBackInst().getAbortFlag()) {
throw (new Error("abort"));
}
/* Check if the view has already been scanned by scanHandle */
if (desc.viewMap[tabName] != undefined) {
var view = desc.viewMap[tabName];
for (var p in view.$status) {
if (view.$status[p]) {
return (false);
}
}
return (true);
}
/* Create an empty view */
var view = newViewStruct(mod.name, tabName);
/* Add the address field to the view structure */
view.$$bind("address", String(desc.addr));
view.$status["address"] = null;
/* Get the view$init function from the capsule */
var viewInitFunc = getViewInitFunc(mod, tabName);
try {
/*
* Run from the context of an object the module writer can assign
* stuff to.
*/
var state = desc.state == null ? {} : desc.state;
viewInitFunc.call(mod.userPrivate, view, state, tabArgs);
}
catch (e) {
var exception = "Caught exception in view init code: " + Program.exToString(e);
/* Add the exception to the descriptor so that it can be displayed */
desc.errorMap[tabName] = exception;
debugPrint("xdc.rov.Program: " + exception);
/* Record the exception in the status table for reference */
addStatusEntry(mod.name, tabName, getInstLabel(desc, view), "N/A", exception);
}
/*
* Check abort flag again in case the view function caught the
* abort signal.
*/
if (Model.getICallBackInst().getAbortFlag()) {
throw (new Error("abort"));
}
/* Add the view to the descriptor's viewMap */
desc.viewMap[tabName] = view;
/*
* Check if there were any errors reported on any fields.
* Add any errors to the status table, and return 'false' to indicate
* errors are present.
*/
var noErrors = true;
for (var p in view.$status) {
if (view.$status[p]) {
/* Record the status in the status table for reference */
addStatusEntry(mod.name, tabName, getInstLabel(desc, view), p, view.$status[p]);
noErrors = false;
}
}
return (noErrors);
}
/*
* ======== scanHandleView ========
* This function is called to scan a module from a different module's
* view$init code.
* Returns the instance object with the attached $viewMap.
*/
function scanHandleView(modName, instAddr, args)
{
var tabName = _getTabName(args);
var tabArgs = _getTabArgs(args);
var mod = this.getModuleDesc(modName);
/* Read in the handle's instance state */
var inst = this.stateReader.fetchHandleState(mod, Number(instAddr));
/* Check if the view has already been scanned */
if (inst.viewMap[tabName] != undefined) {
return (inst.viewMap[tabName]);
}
runViewInit(mod, inst, tabName, "instance", tabArgs);
/* Return the inst object since they would have to go find it otherwise */
return (inst.viewMap[tabName]);
}
/*
* ======== scanRawView ========
*/
function scanRawView(modName)
{
/* Get the module from cache or useModule */
var mod = this.getModuleDesc(modName);
/* Move the state data into a RawView structure to return */
var Program = xdc.useModule('xdc.rov.Program');
var rawView = new Program.RawView;
/*
* Make sure the module state is read in.
* Returns immediately if the state is already scanned, and returns null
* if the module does not have module state.
*/
this.stateReader.fetchModuleState(mod);
rawView.modState = mod.state;
/*
* Scan in the instance state structures. Returns immediately
* if they were already scanned.
*/
if ('Instance_State' in mod.useMod) {
this.stateReader.fetchAllInstStates(mod);
var instStates = new Array();
/* Add all of the instances... */
for each (var inst in mod.instances) {
instStates[instStates.length] = inst.state;
}
rawView.instStates = instStates;
}
rawView.modCfg = mod.cfg;
return (rawView);
}
/*
* ======== scanObjectView ========
* When this function returns, the object that was passed in will now have
* a $viewMap field attached where the view can be accessed.
*/
function scanObjectView(modName, obj, args)
{
var tabName = _getTabName(args);
var tabArgs = _getTabArgs(args);
/* Get the module from cache or useModule */
var mod = this.getModuleDesc(modName);
/* Verify the module supports the requested tab */
if (!modSupportsTab(modName, tabName)) {
throw (new Error("Module does not support the requested view level"));
}
if (!('Instance_State' in mod.useMod)) {
throw(new Error("Calling scanInstance on module that does not support instances"));
}
var objDesc = this.stateReader.getInstDesc(mod, obj, -1);
/* Check if the view has already been scanned */
if (objDesc.viewMap[tabName] != undefined) {
/* Before returning it, check if there were any problems */
var view = objDesc.viewMap[tabName];
if (throwScanErrors) {
for (var field in view.$status) {
if (view.$status[field]) {
throw (new Error("ROV detected errors in scan of '"
+ tabName
+ "' tab for module " + modName + "."));
}
}
}
return (view);
}
/* Create an empty view */
var view = newViewStruct(modName, tabName);
/* Add the address field */
view.$$bind("address", String(obj.$addr));
view.$status["address"] = null;
/* Get the view$init function from the capsule */
var viewInitFunc = getViewInitFunc(mod, tabName);
/* Call view init */
viewInitFunc.call(mod.userPrivate, view, obj, tabArgs);
/* Add the view to the instance's viewMap */
objDesc.viewMap[tabName] = view;
// TODO - Check view status before returning.
/* Return the descriptor directly so user doesn't have to search for it */
return (view);
}
/*
* ======== scanTreeTableView ========
*/
function scanTreeTableView(modName, args)
{
return (scanTreeView.call(this, modName, args));
}
/*
* ======== scanTreeView ========
* TODO - This function is currently identical to scanTreeTableView.
*/
function scanTreeView(modName, args)
{
var tabName = _getTabName(args);
var tabArgs = _getTabArgs(args);
/* Get the module from cache or useModule */
var modDesc = this.getModuleDesc(modName);
/* Verify the module supports the requested tab */
if (!modSupportsTab(modName, tabName)) {
throw (new Error("Module does not support the requested view level"));
}
/* Check if the view has already been scanned */
if (modDesc.viewMap[tabName] != undefined) {
/* TODO: Before returning check for errors; throw exception if found */
return (modDesc.viewMap[tabName]);
}
var viewInitFunc = getViewInitFunc(modDesc, tabName);
/*
* If the view code throws an exception, let it propagate up and be
* displayed to the user. This means the user won't see any partial data
* if there is any, but there's no other logical place to display the
* exception message.
*/
var treeView = viewInitFunc.call(modDesc.userPrivate, tabArgs);
if (modDesc.cfg == null) {
/* Pure c modules return pure JavaScript arrays or objects */
var realView = new Program.TreeNodeArr();
realView.length++;
realView[0] = new Program.TreeNode();
if (treeView.$label != undefined) {
realView[0].label = treeView.$label;
}
else {
realView[0].label = tabName;
}
if (treeView instanceof Array) {
//viewObj[0].properties = new Array();
for (var i = 0; i < treeView.length; i++) {
realView[0].properties[realView[0].properties.length++] =
treeView[i];
}
}
else {
realView[0].properties[realView[0].properties.length++] =
treeView;
}
treeView = realView;
}
/* Add the view to the module's viewMap */
modDesc.viewMap[tabName] = treeView;
if (!("$status" in treeView)) {
return (treeView);
}
for (var p in treeView.$status) {
if (treeView.$status[p]) {
/* Record the status in the status table for reference */
addStatusEntry(modName, tabName, "N/A", p, treeView.$status[p]);
}
}
/* TODO - check for any errors and throw exception */
/*
if (error) {
throw (new Error("ROV detected errors in scan of '" + tabName +
"' tab for module " + modName + "."));
}
*/
return (treeView);
}
/*
* ======== modSupportsTab ========
* Returns true if the module supports the given tab.
*/
function modSupportsTab(modName, tabName)
{
var mod = this.getModuleDesc(modName);
if (mod.viewInfo == null) {
return (false);
}
return (tabName in mod.viewInfo.viewMap);
}
/*
* ======== getViewInitFunc ========
* Retrieves the function associated with the specified tabName for the
* specified mod.
*/
function getViewInitFunc(mod, tabName)
{
var funcName = mod.viewInfo.viewMap[tabName].viewInitFxn;
if (!(funcName in mod.useMod.$capsule)) {
throw (new Error("The viewInitFxn '" + funcName
+ "' specified for " + mod.name
+ " - " + tabName + " does not exist."));
}
return (mod.useMod.$capsule[funcName]);
}
/*
* ======== addCMod ========
*/
function addCMod(cmod)
{
var ViewInfo = xdc.useModule('xdc.rov.ViewInfo');
var mod = new Program.ROVModuleDesc;
mod.name = cmod.name;
mod.viewInfo = ViewInfo.create();
mod.cfg = null; // this is how we know it's not a RTSC module
mod.viewInfo.showRawTab = false;
mod.loadFailed = false;
mod.useMod = {};
mod.useMod.$capsule = cmod.capsule;
if (cmod.viewMap.length > 0) {
for (var i = 0; i < cmod.viewMap.length; i++) {
var vMap = new Array();
//var vMap = {};
if (cmod.viewMap[i].viewType == undefined) {
vMap["type"] = ViewInfo.MODULE_DATA;
}
else if (cmod.viewMap[i].viewType == "Table") {
vMap["type"] = ViewInfo.MODULE_DATA;
}
else if (cmod.viewMap[i].viewType == "Pages") {
vMap["type"] = ViewInfo.INSTANCE_DATA;
}
else {
throw new Error("The property viewType for the view '"
+ cmod.viewMap[i].name + "' in '" + cmod.capsule.$path
+ "' must be either 'Table' or 'Pages'.");
}
vMap["viewInitFxn"] = cmod.viewMap[i].fxn;
vMap["structName"] = cmod.viewMap[i].structName;
vMap["argsName"] = cmod.viewMap[i].argsName;
mod.viewInfo.viewMap[cmod.viewMap[i].name] = vMap;
if (cmod.viewMap[i].structName in cmod.capsule) {
mod.useMod[cmod.viewMap[i].structName] =
cmod.capsule[cmod.viewMap[i].structName];
}
else {
throw new Error(
"The definition for " + cmod.viewMap[i].structName
+ " is not found in " + cmod.capsule.$path);
}
/* Check if we already encountered this structure. Nothing is
* stopping two different views sharing the same view structure.
*/
var pt = mod.useMod[cmod.viewMap[i].structName].prototype;
if (!("$status" in pt)) {
/* Create the status map and bind it to the struct */
var status = new Object();
for (var p in new mod.useMod[cmod.viewMap[i].structName]()) {
status[p] = null;
}
Object.freeze(status);
/* $status is intentionally not enumerable and not writable to
* prevent adding it as a column later. Object.freeze() above
* was added because to prevent changing values in '$status',
* while 'writable' prevents assigning something else to
* '$status'.
* The property $status is added to the prototype to serve as a
* default that is used only if no error was reported by calling
* Program.displayError().
*/
Object.defineProperty(pt, "$status",
{enumerable: false, writable: false, value: status});
}
}
}
if (cmod.argsMap && cmod.argsMap.length > 0) {
for (var ki = 0; ki < cmod.argsMap.length; ki++) {
var args = cmod.argsMap[ki];
mod.viewInfo.argsMap[args.name] = {description: args.description, args: args.args};
}
}
cmodTab[mod.name] = mod;
}
/*
* ======== getModuleDesc ========
* Gets the module from the cache or brings it in.
*/
function getModuleDesc(modName)
{
if (cmodTab[modName]) {
return (cmodTab[modName]);
}
if (modName.indexOf('.') == -1 && cmodTab["C." + modName]) {
return (cmodTab["C." + modName]);
}
/* If the module has already been brought in, just return it */
if (modTab[modName]) {
return (modTab[modName]);
}
/*
* $modules maps module names to their configurations. This comes from the
* ROV recap file; if a module is not in this map, then it is not in this
* application.
*/
var modCfg = Program.$modules[modName];
if (!modCfg) {
throw (new Error("Module " + modName
+ " does not exist in the application's configuration"));
}
var mod = new Program.ROVModuleDesc;
/* Initialize fields */
mod.name = modName;
mod.instMap = {};
mod.readAllAddrs = false;
/*
* Place for users to store data. This is the calling context of view init
* functions.
*/
mod.userPrivate = {};
/* Add the module's configuration to it as $config */
mod.cfg = modCfg;
/* Bring in the module */
try {
mod.loadFailed = false;
mod.useMod = xdc.useModule(modName);
}
catch (e) {
mod.loadFailed = true;
mod.loadFailedMsg = String(e);
mod.viewInfo = null;
/* Store the module in our cache */
modTab[modName] = mod;
return (mod);
}
/*
* Updated the structure size information based on whether this module
* has named instances. This should only be done once, so check
* allModsRead.
*/
if (!this.$private.allModsRead
&& ('Instance_State' in mod.useMod)
&& modCfg.common$.namedInstance
&& !("__name" in xdc.om[modName + '$$Instance_State'])) {
/* add the __name property to the module's $$Instance_State object */
var po = xdc.om[modName + '$$Instance_State'];
po.$$fld('__name', $$PAdr('xdc_runtime_Types_CordAddr__*', 'PE'),
undefined, 'w');
/*
* Modules with instances but no Instance_State structure won't
* have $$sizes. We catch these in the decoder by seeing that
* Instance_State does not have a $sizeof field and we just return
* an empty object.
*/
if ('$$sizes' in mod.useMod.Instance_State) {
mod.useMod.Instance_State.$$sizes.push(['__name', 'UPtr']);
}
}
/* Retrieve the ROV ViewInfo for the module */
var modSpec = mod.useMod.$spec;
var facetConfig = modSpec.queryFacet('xdc.rov.ViewInfo');
Program.debugPrint("Facet config for " + modName + " = " + facetConfig);
/* Set viewInfo to null if this module does not have a ViewInfo facet */
if (facetConfig == null) {
mod.viewInfo = null;
}
else {
mod.viewInfo = mod.cfg[facetConfig];
if (mod.viewInfo == null) {
/* should only happen if version of module used at config time
* is different from the module found by ROV
*/
Program.debugPrint(
"warning: xdc.rov.Program: no Config.ViewInfo facet for "
+ modName);
}
}
/* Store the module in our cache */
modTab[modName] = mod;
return (mod);
}
/*
* ======== fetchStruct ========
* Fetches the specified structure from the target.
*/
function fetchStruct(desc, addr, addrCheck)
{
return (Program.strDec.fetchStruct(xdc.om[desc.type], Number(addr),
addrCheck));
}
/*
* ======== fetchArray ========
* Retrieves the specified array from the target.
*/
function fetchArray(desc, addr, len, addrCheck)
{
return (Program.strDec.fetchArray(xdc.om[desc.type], Number(addr), len,
desc.isScalar, addrCheck));
}
/*
* ======== _decodeBuffer ========
* Internal method to decode a primitive value from buffer
*
* This method should never be directly called by code outside the xdc.rov
* package; future changes to an executables debug format will result in
* compatibility breaks.
*
* @param(buffer) buffer with each element containing one MAU from memory
* @param(offset) location in buffer where a primitive value starts
* @param(len) size in MAUs
* @param(encoding) one of the following Dwarf encodings (DW_AT_encoding)
* 0x01 DW_ATE_address
* 0x02 DW_ATE_boolean
* 0x04 DW_ATE_float
* 0x05 DW_ATE_signed
* 0x06 DW_ATE_signed_char
* 0x07 DW_ATE_unsigned
* 0x08 DW_ATE_unsigned_char
*
* The following encodings are not supported
* 0x03 DW_ATE_complex_float
* 0x09 DW_ATE_imaginary_float
* 0x0a DW_ATE_packed_decimal
* 0x0b DW_ATE_numeric_string
* 0x0c DW_ATE_edited
* 0x0d DW_ATE_signed_fixed
* 0x0e DW_ATE_unsigned_fixed
* 0x0f DW_ATE_decimal_float
* 0x10 DW_ATE_UTF
* 0x11 DW_ATE_UCS
* 0x12 DW_ATE_ASCII
* @a(Returns) a scalor value (via `{@link StructureDecoder StructureDecoder._decodeScalar}`)
*/
function _decodeBuffer(buffer, offset, len, encoding)
{
if (encoding < 1 || encoding > 8) {
throw new Error("unsupported encoding for the location 0x" +
Number(buffer).toString(16));
}
if (encoding == 3) {
throw new Error("complex numbers are not supported - location 0x" +
Number(buffer).toString(16));
}
var buf = {};
buf.buffer = buffer;
buf.off = offset;
var type = {};
type.align = 1;
type.size = len;
type.signed = false;
if (encoding == 5 || encoding == 6) {
type.signed = true;
}
type.isAddr = false;
if (encoding == 1) {
type.isAddr = true;
}
type.isEnum = false;
if (encoding == 4) {
if (type.size == 8) {
type.fldType = "TDouble";
}
else if (type.size == 4) {
type.fldType = "TFloat";
}
}
return (Program.strDec._decodeScalar(type, buf));
}
/*
* ======== fetchVariableByPtr ========
* Dereferences a pointer variable and reads the content as the pointed-to type
*
*/
function fetchVariableByPtr(varName)
{
var javaType = Program.ofReader.getPtrType(varName);
if (javaType == null) {
throw new Error("'" + varName + "' is not a global variable, or its "
+ "type is not a pointer type.");
}
var typespec = _convertType(javaType);
var addr = Program.lookupSymbolValue(varName);
if (addr == -1) {
throw new Error("Cannot determine the address for '" + varName + "'.");
}
var ptrSize = Model.$private.recap.build.target.stdTypes.t_Ptr.size;
var ptdBuf = Model.getMemoryImageInst().readMaus(addr, ptrSize, false);
var ptdLocation = _decodeBuffer(ptdBuf, 0, ptrSize, 1);
var newObj = {};
var buf = Model.getMemoryImageInst().readMaus(ptdLocation, typespec.size,
false);
_createObject(buf, 0, typespec, newObj, "top");
return (newObj["top"]);
}
/*
* ======== fetchFromAddr ========
* Reads a content from the address according to the spec for 'typeName'
*
*/
function fetchFromAddr(addr, typeName, count)
{
var typespec = Program.lookupType(typeName);
if (typespec == null) {
throw new Error("Type '" + typeName + "' can't be found");
}
if (count != null && count > 1) {
var newAr = [];
var buf = Model.getMemoryImageInst().readMaus(addr,
typespec.size * count, false);
for (var i = 0; i < count; i++) {
var newObj = {};
_createObject(buf, i * typespec.size, typespec, newObj, "top");
newAr.push(newObj["top"]);
}
return (newAr);
}
var buf = Model.getMemoryImageInst().readMaus(addr, typespec.size, false);
var newObj = {};
_createObject(buf, 0, typespec, newObj, "top");
return (newObj["top"]);
}
/*
* ======== fetchVariable ========
* Reads a variable from memory and returns a corresponding JavaScript object
*
*/
function fetchVariable(varName)
{
var typespec = Program.lookupTypeByVariable(varName);
if (typespec == null) {
throw new Error("Variable '" + varName + "' does not exist, or it is "
+ "not a global variable.");
}
var addr = Program.lookupSymbolValue(varName);
if (addr == -1) {
throw new Error("Cannot determine the address for '" + varName + "'.");
}
var buf = Model.getMemoryImageInst().readMaus(addr, typespec.size, false);
var newObj = {};
_createObject(buf, 0, typespec, newObj, "top");
return (newObj["top"]);
}
/*
* ======== _createObject ========
* Internal method to create a JavaScript object representing a C object
*
* This method should never be directly called by code outside
* the xdc.rov package; future changes to an executables debug format
* will result is compatibility breaks.
*
* @param(buf) buffer with each element containing one MAU from memory
* @param(offset) offset in buffer of MAUs to be interpreted
* @param(typespec) type specification for the object from memory
* @param(prnt) already created object to which a new object is added
* @param(name) name for the new object within `prnt`
*
* Typespec is a JavaScript object.
*/
function _createObject(buf, offset, typespec, prnt, name)
{
if (typespec.member == null) {
prnt[name] = _decodeBuffer(buf, offset, typespec.size,
typespec.encoding);
}
else if (typespec.elnum == null || typespec.elnum == 0) {
prnt[name] = {};
for (var prop in typespec.member) {
var val = typespec.member[prop];
_createObject(buf, offset + val.offset, val, prnt[name], prop);
}
}
else if (typespec.elnum > 0) {
prnt[name] = [];
var elemspec = typespec.member[0];
if (elemspec.encoding != 0) {
for (var i = 0; i < typespec.elnum; i++) {
prnt[name][i] = _decodeBuffer(buf, offset + i * elemspec.offset,
elemspec.size, elemspec.encoding);
}
}
else {
for (i = 0; i < typespec.elnum; i++) {
_createObject(buf, offset + i * elemspec.offset, elemspec,
prnt[name], "temp");
prnt[name][i] = prnt[name].temp;
delete prnt[name].temp;
}
}
}
}
/*
* ======== fetchString ========
* Retrieves the string starting at the specified address from the target.
*/
function fetchString(addr, addrCheck)
{
return (Program.strReader.findString(Number(addr), addrCheck));
}
/*
* ======== fetchStaticString ========
* Retrieves the string at the specified address using an object file reader.
*/
function fetchStaticString(addr)
{
return (Program.ofReader.findString(Number(addr)));
}
/*
* ======== getPrivateData ========
*/
function getPrivateData(modName) {
var modDesc = this.getModuleDesc(modName);
return (modDesc.userPrivate);
}
/*
* ======== getModuleConfig ========
* Returns the module config object for the requested module.
*/
function getModuleConfig(modName)
{
return (this.getModuleDesc(modName).cfg);
}
/*
* ======== lookupFuncName ========
*/
function lookupFuncName(addr)
{
var Model = xdc.useModule("xdc.rov.Model");
var symInst = Model.getISymbolTableInst();
var syms = symInst.lookupFuncName(Number(addr));
return (filterSymbols(syms));
}
/*
* ======== lookupDataSymbol ========
* Uses the SymbolTable to find the labels at the given address.
*/
function lookupDataSymbol(addr)
{
var Model = xdc.useModule("xdc.rov.Model");
var symInst = Model.getISymbolTableInst();
var syms = symInst.lookupDataSymbol(Number(addr));
return (filterSymbols(syms));
}
/*
* ======== _convertType ========
* Converts a Java representation of a type (with HashMaps and inherited Java
* properties) to a simple JavaScript representation.
*
* 'javaType' is always a TypeSpec Java object.
*/
function _convertType(javaType) {
var jsType = {};
for (var prop in javaType) {
/* these are the only properties, other than 'members', that we need
* to copy from Java to JavaScript. They are all integers of various
* sizes, and they can be easily copied to the JavaScript object.
*/
if (prop == "elnum" || prop == "offset" || prop == "size"
|| prop == "encoding") {
jsType[prop] = javaType[prop];
}
else if (prop == "members" && javaType.members != null) {
var entrySet = javaType.members.entrySet().toArray();
if (javaType.elnum != 0) {
/* It's an array type, which means the only element in the map
* is a description of the array's element type.
*/
jsType.member = [];
jsType.member[0] = _convertType(entrySet[0].getValue());
}
else {
/* It's a structure, we need to copy each type description
* from 'members'.
*/
jsType.member = {};
for (var i = 0; i < entrySet.length; i++) {
jsType.member[entrySet[i].getKey()]
= _convertType(entrySet[i].getValue());
}
}
}
else if (prop == "members") {
jsType.member = null;
}
}
return (jsType);
}
/*
* ======== lookupType ========
*/
function lookupType(typename)
{
var javaType = Program.ofReader.getType(typename);
if (javaType == null) {
/* try one of the standard C99 types */
var typespec = _c99Type(typename);
if (typespec == null) {
return null;
}
/* typespec is already a native JavaScript object, no need to call
* _convertType.
*/
return (typespec)
}
else {
return (_convertType(javaType));
}
}
/*
* ======== lookupTypeByVariable ========
*/
function lookupTypeByVariable(name)
{
var javaType = Program.ofReader.getTypeByVariable(name);
if (javaType == null) {
return (null);
}
var jsType = _convertType(javaType);
return (jsType);
}
/*
* ======== filterSymbols ========
* Helper function used by lookupFuncName and lookupDataSymbol.
* There may be multiple symbols at a given address. To improve the chances
* of the first symbol in the array being the correct one:
* - Sort the symbols by length (shorter symbol is more likely correct)
* - Move any symbols containing '$' to the end
*/
function filterSymbols(jSyms)
{
if (String(jSyms) != "null") {
jSyms = java.util.Arrays.asList(jSyms).get(0);
}
else {
jSyms = null;
}
if (jSyms == null || jSyms.length == 0) {
return (new Array());
}
/*
* Copy the Java String array to a JavaScript array. Filter out any symbols
* containing '$' and store these separately.
*/
var symsArr = new Array();
var dSyms = new Array();
for each (var sym in jSyms) {
if (sym.indexOf("$") != -1) {
dSyms.push(sym);
}
else {
symsArr.push(sym);
}
}
/*
* Sort the array by symbol length, from smallest to largest. In general,
* user given names are shorter than generated symbols.
*/
function compare(a, b) {
return (a.length - b.length);
}
symsArr.sort(compare);
dSyms.sort(compare);
/* Add the '$' symbols back on to the end */
return(symsArr.concat(dSyms));
}
/*
* ======== lookupSymbolValue ========
*/
function lookupSymbolValue(symName)
{
var Model = xdc.useModule("xdc.rov.Model");
var symInst = Model.getISymbolTableInst();
return (symInst.getSymbolValue(symName));
}
/*
* ======== getSymbolValue ========
*/
function getSymbolValue(symName)
{
var Model = xdc.useModule("xdc.rov.Model");
var symInst = Model.getISymbolTableInst();
return (symInst.getSymbolValue(symName));
}
/*
* ======== displayError ========
*
*/
function displayError(view, fieldName, errorMsg)
{
if (!(fieldName in view)) {
throw (new Error("Trying to set error for field '" + fieldName +
"' which does not exist in this view. The error " +
"message was: " + errorMsg));
}
if ("hasOwnProperty" in view && view.hasOwnProperty("$status") != true) {
var status = new Object();
for (var p in view) {
status[p] = null;
}
Object.defineProperty(view, "$status",
{enumerable: false, value: status, writable: true});
}
view.$status[fieldName] = errorMsg;
}
/*
* ======== ptrToHex ========
*/
function ptrToHex(ptr)
{
return ('0x' + String(ptr).substr(1));
}
/*
* ======== getShortName ========
* Parses canonical name for short name, returns either short name
* or empty string "".
*/
function getShortName(name)
{
/* Example: ti.sysbios.knl.Task@8000c5e9:myTsk0 */
var index;
if ((index = name.indexOf(':')) != -1) {
return (name.substring(index + 1, name.length));
}
return ("");
}
var traceEnable;
/*
* ======== debugPrint ========
*/
function debugPrint(msg)
{
if (traceEnable === undefined) {
/* XDC_ROV_TRACE has to be set outside of CCS for ROV to get its
* value. Anything else will work for classic ROV but not for the
* current version.
*/
traceEnable = xdc.jre.java.lang.System.getProperty("xdc.rov.traceEnable");
if (traceEnable == null) {
traceEnable = xdc.jre.java.lang.System.getenv("XDC_ROV_TRACE");
}
}
if (traceEnable == "true") {
print("[Server ] " + msg);
}
}
/*
* ======== print ========
*/
function print(msg)
{
try {
var Monitor = xdc.module("xdc.rov.runtime.Monitor");
Monitor.println(msg);
}
catch (e) {
; /* ignore missing Monitor */
}
xdc.global.print(msg);
}
/*
* ======== timestamp ========
*/
function timestamp(msg) {
if (timestampFunc != undefined) {
timestampFunc(msg);
}
else {
var time = xdc.jre.java.lang.System.currentTimeMillis();
Program.debugPrint("+++ (" + time + "ms) " + msg);
}
}
/*
* ======== setTimestampFunc ========
*/
function setTimestampFunc(func)
{
timestampFunc = func;
}
/*
* ======== resetMods ========
*/
function resetMods()
{
modTab = new Object();
statusTab = new Array();
/* For C modules, we can't just create a new cmodTab because we don't have
* rov.js files available to recreate it. Instead, we delete these modules'
* views from cache.
*/
for (var cmod in cmodTab) {
for (var prop in cmodTab[cmod].viewMap) {
delete cmodTab[cmod].viewMap[prop];
}
}
}
/*
* ======== setThrowViewErrors ========
*/
function setThrowViewErrors(flag)
{
throwScanErrors = flag;
}
/*
* ======== exToString ========
*/
function exToString(e)
{
/* Don't include the file name and line number if they're not present */
if ((e.message == undefined) || (e.fileName == undefined)
|| (e.fileName == "")) {
return("" + e);
}
else {
return('"' + e.fileName + '", line ' + e.lineNumber + ': ' + e.message);
}
}
/*
* ======== moduleIdToName ========
*/
function moduleIdToName(mid)
{
var Registry = xdc.useModule('xdc.runtime.Registry');
if (Registry.isMemberRov(mid)) {
return (Registry.lookupModIdRov(mid));
}
var modCfg = this.$modules['#' + mid];
return !modCfg ? null : modCfg.$name;
}
/*
* ======== addStatusEntry ========
*/
function addStatusEntry(mod, tab, inst, field, message)
{
var status = new Program.StatusEntry();
status.mod = mod;
status.tab = tab;
status.inst = inst;
status.field = field;
status.message = message;
statusTab[statusTab.length] = status;
}
/*
* ======== getStatusTable ========
*/
function getStatusTable()
{
return (statusTab);
}
/*
* ======== getInstLabel ========
* Returns something to display in the 'inst' field of the status table for
* the given view object.
*/
function getInstLabel(desc, view)
{
var Program = xdc.useModule('xdc.rov.Program');
/* If desc is a module descriptor, the inst field is "N/A" */
if (desc.$type.equals("xdc.rov.Program.ROVModuleDesc")) {
return ("N/A");
}
/* If a 'label' was specified, just return that */
if (('label' in view) &&
(view.label != null) &&
(view.label != undefined) &&
(view.label != "")) {
return (view.label);
}
/* Otherwise, return the handle address in parentheses */
return ("(0x" + Number(view.address).toString(16) + ")");
}
/*
* ======== _c99Type ========
* Returns a typespec for most of the standard C99 types. This function is
* called if the description of the type is not available in .dwarf_info.
*/
function _c99Type(typename) {
var spec = {
size: null,
encoding: null,
member: null,
offset: null,
elnum: null
};
var elfTarget = Program.ofReader.getTarget();
var stdTypes = Program.build.target.stdTypes;
switch (typename) {
case "uint8_t":
if (elfTarget.charsize > 1) {
return (null);
}
spec.encoding = 7;
spec.size = 1;
return (spec);
case "int8_t":
if (elfTarget.charsize > 1) {
return (null);
}
spec.encoding = 5;
spec.size = 1;
return (spec);
case "uint16_t":
spec.encoding = 7;
spec.size = 16/(elfTarget.charsize * 8);
return (spec);
case "int16_t":
spec.encoding = 5;
spec.size = 16/(elfTarget.charsize * 8);
return (spec);
case "uint32_t":
spec.encoding = 7;
spec.size = 32/(elfTarget.charsize * 8);
return (spec);
case "int32_t":
spec.encoding = 5;
spec.size = 32/(elfTarget.charsize * 8);
return (spec);
case "uint64_t":
spec.encoding = 7;
spec.size = 64/(elfTarget.charsize * 8);
return (spec);
case "int64_t":
spec.encoding = 5;
spec.size = 64/(elfTarget.charsize * 8);
return (spec);
case "uint_least8_t":
spec.encoding = 7;
spec.size = stdTypes.t_Int8.size;;
return (spec);
case "int_least8_t":
spec.encoding = 5;
spec.size = stdTypes.t_Int8.size;
return (spec);
case "uint_least16_t":
spec.encoding = 7;
spec.size = stdTypes.t_Int16.size;
return (spec);
case "int_least16_t":
spec.encoding = 5;
spec.size = stdTypes.t_Int16.size;
return (spec);
case "uint_least32_t":
spec.encoding = 7;
spec.size = stdTypes.t_Int32.size;
return (spec);
case "int_least32_t":
spec.encoding = 5;
spec.size = stdTypes.t_Int32.size;
return (spec);
case "uint_least64_t":
spec.encoding = 7;
spec.size = stdTypes.t_Int64.size;
return (spec);
case "int_least64_t":
spec.encoding = 5;
spec.size = stdTypes.t_Int64.size;
return (spec);
case "unsigned char":
case "char":
/* This is implementation specific for "char". It is an unsigned
* encoding because that's how it is on Arm.
*/
spec.encoding = 8;
spec.size = stdTypes.t_Char.size;
return (spec);
case "signed char":
spec.encoding = 6;
spec.size = stdTypes.t_Char.size;
return (spec);
case "unsigned short":
spec.encoding = 7;
spec.size = stdTypes.t_Short.size;
return (spec);
case "signed short":
case "short":
spec.encoding = 5;
spec.size = stdTypes.t_Short.size;
return (spec);
case "unsigned":
case "unsigned int":
spec.encoding = 7;
spec.size = stdTypes.t_Int.size;
return (spec);
case "signed int":
case "int":
spec.encoding = 5;
spec.size = stdTypes.t_Int.size;
return (spec);
case "unsigned long":
case "unsigned long int":
spec.encoding = 7;
spec.size = stdTypes.t_Long.size;
return (spec);
case "signed long":
case "long":
case "long int":
spec.encoding = 5;
spec.size = stdTypes.t_Long.size;
return (spec);
case "unsigned long long":
case "unsigned long long int":
spec.encoding = 7;
spec.size = stdTypes.t_LLong.size;
return (spec);
case "signed long long":
case "long long":
case "long long int":
spec.encoding = 5;
spec.size = stdTypes.t_LLong.size;
return (spec);
case "float":
spec.encoding = 4;
spec.size = stdTypes.t_Float.size;
return (spec);
case "double":
spec.encoding = 4;
spec.size = stdTypes.t_Double.size;
return (spec);
case "long double":
spec.encoding = 4;
spec.size = stdTypes.t_LDouble.size;
return (spec);
case "uintptr_t":
spec.encoding = 7;
spec.size = stdTypes.t_IArg.size;
return (spec);
case "intptr_t":
spec.encoding = 5;
spec.size = stdTypes.t_IArg.size;
return (spec);
case "size_t":
spec.encoding = 7;
spec.size = stdTypes.t_SizeT.size;
return (spec);
default:
return (null);
}
}
/*
* ======== _getTabArgs ========
* Extract optional arguments from parameters passed to get a view
*/
function _getTabArgs(args)
{
var k = args.indexOf(':');
return (k < 0 ? undefined : args.substr(k + 1));
}
/*
* ======== _getTabName ========
* Extract view name from parameters passed to get a view
*/
function _getTabName(args)
{
var k = args.indexOf(':');
return (k < 0 ? args : args.substr(0, k));
}