/*
* Copyright (c) 2002-2006 Samit Basu
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "FunctionDef.hpp"
#include "Interpreter.hpp"
#include "Parser.hpp"
#include "Exception.hpp"
#include <iostream>
#include <signal.h>
#include "SymbolTable.hpp"
#include "Types.hpp"
#include "MexInterface.hpp"
#include <QDebug>
#include <sys/stat.h>
#if HAVE_AVCALL
#include "avcall.h"
#endif
#define MSGBUFLEN 2048
QMutex functiondefmutex;
stringVector IdentifierList(tree t) {
stringVector retval;
for (unsigned index=0;index<t.numchildren();index++) {
if (t.child(index).is('&'))
retval.push_back("&" + t.child(index).first().text());
else
retval.push_back(t.child(index).text());
}
return retval;
}
void VariableReferencesList(const tree & t, stringVector& idents) {
if (t.is(TOK_NEST_FUNC)) return;
if (t.is(TOK_VARIABLE)) {
bool exists = false;
for (int i=0;(i<idents.size());i++) {
exists = (idents[i] == t.first().text());
if (exists) break;
}
if (!exists)
idents.push_back(t.first().text());
}
for (int i=0;i<t.numchildren();i++)
VariableReferencesList(t.child(i),idents);
}
AnonymousFunctionDef::AnonymousFunctionDef() {
}
AnonymousFunctionDef::~AnonymousFunctionDef() {
}
int AnonymousFunctionDef::inputArgCount() {
return arguments.size();
}
int AnonymousFunctionDef::outputArgCount() {
return -1;
}
ArrayVector AnonymousFunctionDef::evaluateFunction(Interpreter *eval, ArrayVector& inputs, int nargout) {
ArrayVector outputs;
if (!code.valid()) return outputs;
Context * context = eval->getContext();
context->pushScope("anonymous");
eval->pushDebug("anonymous","anonymous");
stringVector workspaceVars(workspace.getCompletions(""));
for (int i=0;i<workspaceVars.size();i++)
context->insertVariableLocally(workspaceVars[i],*workspace.findSymbol(workspaceVars[i]));
int minCount = (inputs.size() < arguments.size()) ? inputs.size() : arguments.size();
for (int i=0;i<minCount;i++)
context->insertVariableLocally(arguments[i],inputs[i]);
try {
try {
eval->multiexpr(code,outputs);
} catch (InterpreterBreakException& e) {
} catch (InterpreterContinueException& e) {
} catch (InterpreterReturnException& e) {
}
context->popScope();
eval->popDebug();
} catch (Exception& e) {
context->popScope();
eval->popDebug();
throw;
} catch (InterpreterRetallException& e) {
context->popScope();
eval->popDebug();
throw;
}
return outputs;
}
void AnonymousFunctionDef::initialize(const tree &t, Interpreter *eval) {
name = t.text();
arguments = IdentifierList(t.first());
code = t.second();
scriptFlag = false;
temporaryFlag = false;
graphicsFunction = false;
stringVector vars;
VariableReferencesList(t.second(),vars);
for (int i=0;i<vars.size();i++) {
ArrayReference ptr(eval->getContext()->lookupVariable(vars[i]));
if (ptr.valid()) {
// cout << "Captured VAR: " << vars[i] << "\r\n";
workspace.insertSymbol(vars[i],*ptr);
}
}
}
MFunctionDef::MFunctionDef() {
functionCompiled = false;
timeStamp = 0;
localFunction = false;
pcodeFunction = false;
#warning - check pcode
nestedFunction = false;
capturedFunction = false;
}
MFunctionDef::~MFunctionDef() {
}
int MFunctionDef::inputArgCount() {
if (arguments.size() == 0) return 0;
if (arguments[arguments.size()-1] == "varargin")
return -1;
else
return arguments.size();
}
int MFunctionDef::outputArgCount() {
if (returnVals.size() == 0) return 0;
if (returnVals[returnVals.size()-1] == "varargout")
return -1;
else
return returnVals.size();
}
void MFunctionDef::printMe(Interpreter*eval) {
stringVector tmp;
char msgBuffer[MSGBUFLEN];
snprintf(msgBuffer,MSGBUFLEN,"Function name:%s\n",name.c_str());
eval->outputMessage(msgBuffer);
eval->outputMessage("Function class: Compiled M function\n");
eval->outputMessage("returnVals: ");
tmp = returnVals;
int i;
for (i=0;i<tmp.size();i++) {
snprintf(msgBuffer,MSGBUFLEN,"%s ",tmp[i].c_str());
eval->outputMessage(msgBuffer);
}
eval->outputMessage("\n");
eval->outputMessage("arguments: ");
tmp = arguments;
for (i=0;i<tmp.size();i++) {
snprintf(msgBuffer,MSGBUFLEN,"%s ",tmp[i].c_str());
eval->outputMessage(msgBuffer);
}
eval->outputMessage("\ncode: \n");
code.print();
}
#warning - This design causes an unneccesary copy - should use readonly pass first
void CaptureFunctionPointer(FuncPtr &val, Interpreter *walker,
MFunctionDef *parent, ScopePtr &workspace) {
if (val->type() == FM_M_FUNCTION) {
MFunctionDef* mptr = (MFunctionDef*) val;
if (mptr->nestedFunction && !mptr->capturedFunction) {
MFunctionDef* optr = new MFunctionDef;
(*optr) = (*mptr);
Context* context = walker->getContext();
string myScope = context->scopeName();
context->bypassScope(1);
string parentScope = context->scopeName();
context->restoreScope(1);
if (!Scope::nests(parentScope,myScope)) {
// Now capture the variables in our current scope
for (int i=0;i<optr->variablesAccessed.size();i++) {
ArrayReference ptr(context->lookupVariable(optr->variablesAccessed[i]));
if (ptr.valid()) {
if (!workspace)
workspace = new Scope("captured",false);
workspace->insertVariable(optr->variablesAccessed[i],*ptr);
}
}
optr->workspace = workspace;
optr->capturedFunction = true;
}
val = optr;
}
}
}
void CaptureFunctionPointers(ArrayVector& outputs, Interpreter *walker,
MFunctionDef *parent) {
ScopePtr workspace = NULL;
// First check for any
for (int i=0;i<outputs.size();i++) {
if (outputs[i].dataClass() == FM_FUNCPTR_ARRAY) {
FuncPtr *dp = (FuncPtr*) outputs[i].getReadWriteDataPointer();
for (int j=0;j<outputs[i].getLength();j++)
CaptureFunctionPointer(dp[j],walker,parent,workspace);
}
}
}
ArrayVector MFunctionDef::evaluateFunction(Interpreter *walker,
ArrayVector& inputs,
int nargout) {
ArrayVector outputs;
Context* context;
bool warningIssued;
int minCount;
if (!code.valid()) return outputs;
context = walker->getContext();
context->pushScope(name,nestedFunction);
context->setVariablesAccessed(variablesAccessed);
context->setLocalVariablesList(returnVals);
if (capturedFunction && workspace) {
stringVector workspaceVars(workspace->getCompletions(""));
for (int i=0;i<workspaceVars.size();i++)
context->insertVariableLocally(workspaceVars[i],*workspace->lookupVariable(workspaceVars[i]));
}
walker->pushDebug(fileName,name);
// When the function is called, the number of inputs is
// at sometimes less than the number of arguments requested.
// Check the argument count. If this is a non-varargin
// argument function, then use the following logic:
minCount = 0;
if (inputArgCount() != -1) {
minCount = (inputs.size() < arguments.size()) ?
inputs.size() : arguments.size();
for (int i=0;i<minCount;i++) {
std::string arg(arguments[i]);
if (arg[0] == '&')
arg.erase(0,1);
context->insertVariableLocally(arg,inputs[i]);
}
context->insertVariableLocally("nargin",
Array::int32Constructor(minCount));
} else {
// Count the number of supplied arguments
int inputCount = inputs.size();
context->insertVariableLocally("nargin",
Array::int32Constructor(inputCount));
// Get the number of explicit arguments
int explicitCount = arguments.size() - 1;
// For each explicit argument (that we have an input for),
// insert it into the scope.
minCount = (explicitCount < inputCount) ? explicitCount : inputCount;
int i;
for (i=0;i<minCount;i++) {
std::string arg(arguments[i]);
if (arg[0] == '&')
arg.erase(0,1);
context->insertVariableLocally(arg,inputs[i]);
}
inputCount -= minCount;
// Put minCount...inputCount
Array varg(FM_CELL_ARRAY);
varg.vectorResize(inputCount);
Array* dp = (Array *) varg.getReadWriteDataPointer();
for (i=0;i<inputCount;i++)
dp[i] = inputs[i+minCount];
context->insertVariableLocally("varargin",varg);
}
context->insertVariableLocally("nargout",
Array::int32Constructor(nargout));
try {
try {
walker->block(code);
} catch (InterpreterBreakException& e) {
} catch (InterpreterContinueException& e) {
} catch (InterpreterReturnException& e) {
}
warningIssued = false;
if (outputArgCount() != -1) {
// special case - if nargout == 0, and none of the
// outputs are predefined, we don't do anything
bool nonpredefed = true;
for (int i=0;i<returnVals.size()&&nonpredefed;i++) {
Array *ptr = context->lookupVariableLocally(returnVals[i]);
nonpredefed = nonpredefed && (!ptr);
}
if ((nargout > 0) ||
((nargout == 0) && (!nonpredefed))) {
outputs = ArrayVector();
for (int i=0;i<returnVals.size();i++) outputs.push_back(Array());
// outputs = ArrayVector(returnVals.size());
for (int i=0;i<returnVals.size();i++) {
Array *ptr = context->lookupVariableLocally(returnVals[i]);
if (!ptr)
outputs[i] = Array::emptyConstructor();
else
outputs[i] = *ptr;
if (!ptr && (i < nargout))
if (!warningIssued) {
walker->warningMessage("one or more outputs not assigned in call (1)");
warningIssued = true;
}
}
}
} else {
outputs = ArrayVector();
for (int i=0;i<nargout;i++) outputs.push_back(Array());
int explicitCount = returnVals.size() - 1;
// For each explicit argument (that we have), insert it
// into the scope.
for (int i=0;i<explicitCount;i++) {
Array *ptr = context->lookupVariableLocally(returnVals[i]);
if (!ptr)
outputs[i] = Array::emptyConstructor();
else
outputs[i] = *ptr;
if (!ptr && (i < nargout))
if (!warningIssued) {
walker->warningMessage("one or more outputs not assigned in call (2)");
warningIssued = true;
}
}
// Are there any outputs not yet filled?
if (nargout > explicitCount) {
Array varargout, *ptr;
// Yes, get a pointer to the "vargout" variable that should be defined
ptr = context->lookupVariableLocally("varargout");
if (!ptr)
throw Exception("The special variable 'varargout' was not defined as expected");
varargout = *ptr;
if (varargout.dataClass() != FM_CELL_ARRAY)
throw Exception("The special variable 'varargout' was not defined as a cell-array");
// Get the data pointer
const Array *dp = ((const Array*) varargout.getDataPointer());
// Get the length
int varlen = varargout.getLength();
int toFill = nargout - explicitCount;
if (toFill > varlen)
throw Exception("Not enough outputs in varargout to satisfy call");
for (int i=0;i<toFill;i++)
outputs[explicitCount+i] = dp[i];
}
// Special case - nargout = 0, only variable outputs from function
if ((nargout == 0) && (explicitCount == 0)) {
Array varargout, *ptr;
// Yes, get a pointer to the "vargout" variable that should be defined
ptr = context->lookupVariableLocally("varargout");
if (ptr) {
varargout = *ptr;
if (varargout.dataClass() != FM_CELL_ARRAY)
throw Exception("The special variable 'varargout' was not defined as a cell-array");
// Get the data pointer
const Array *dp = ((const Array*) varargout.getDataPointer());
if (varargout.getLength() > 0)
outputs << dp[0];
}
}
}
// Check for arguments that were passed by reference, and
// update their values.
for (int i=0;i<minCount;i++) {
std::string arg(arguments[i]);
if (arg[0] == '&')
arg.erase(0,1);
Array *ptr = context->lookupVariableLocally(arg);
if (ptr)
inputs[i] = *ptr;
}
// Check the outputs for function pointers
CaptureFunctionPointers(outputs,walker,this);
if (capturedFunction && workspace) {
stringVector workspaceVars(workspace->getCompletions(""));
for (int i=0;i<workspaceVars.size();i++) {
Array *ptr = context->lookupVariableLocally(workspaceVars[i]);
workspace->insertVariable(workspaceVars[i],*ptr);
}
}
context->popScope();
walker->popDebug();
return outputs;
} catch (Exception& e) {
if (capturedFunction && workspace) {
stringVector workspaceVars(workspace->getCompletions(""));
for (int i=0;i<workspaceVars.size();i++) {
Array *ptr = context->lookupVariableLocally(workspaceVars[i]);
workspace->insertVariable(workspaceVars[i],*ptr);
}
}
context->popScope();
walker->popDebug();
throw;
}
catch (InterpreterRetallException& e) {
if (capturedFunction && workspace) {
stringVector workspaceVars(workspace->getCompletions(""));
for (int i=0;i<workspaceVars.size();i++) {
Array *ptr = context->lookupVariableLocally(workspaceVars[i]);
workspace->insertVariable(workspaceVars[i],*ptr);
}
}
context->popScope();
walker->popDebug();
throw;
}
}
static string ReadFileIntoString(FILE *fp) {
struct stat st;
clearerr(fp);
fstat(fileno(fp),&st);
long cpos = st.st_size;
// Allocate enough for the text, an extra newline, and null
char *buffer = (char*) calloc(cpos+2,sizeof(char));
int n = fread(buffer,sizeof(char),cpos,fp);
buffer[n]='\n';
buffer[n+1]=0;
string retval(buffer);
free(buffer);
return retval;
}
//MFunctionDef* ConvertParseTreeToMFunctionDef(tree t, string fileName) {
// MFunctionDef *fp = new MFunctionDef;
// fp->returnVals = IdentifierList(t.first());
// fp->name = t.second().text();
// fp->arguments = IdentifierList(t.child(2));
// fp->code = t.child(3);
// fp->fileName = fileName;
// return fp;
//}
//
//MFunctionDef* ConvertParseTreeToMFunctionDefs(treeVector t,
// string fileName) {
// MFunctionDef* last = NULL;
// for (int index = t.size()-1;index>=0;index--) {
// MFunctionDef* rp = ConvertParseTreeToMFunctionDef(t[index],fileName);
// if (index>0)
// rp->localFunction = true;
// else
// rp->localFunction = false;
// rp->nextFunction = last;
// if (last)
// last->prevFunction = rp;
// last = rp;
// }
// return last;
//}
void RegisterNested(const tree &t, Interpreter *m_eval, MFunctionDef *parent) {
if (t.is(TOK_NEST_FUNC)) {
MFunctionDef *fp = new MFunctionDef;
fp->localFunction = parent->localFunction;
fp->nestedFunction = true;
fp->returnVals = IdentifierList(t.first());
fp->name = parent->name + "/" + t.second().text();
fp->arguments = IdentifierList(t.child(2));
fp->code = t.child(3);
VariableReferencesList(fp->code,fp->variablesAccessed);
fp->fileName = parent->fileName;
// Register any nested functions for the local functions
m_eval->getContext()->insertFunction(fp,false);
RegisterNested(fp->code,m_eval,fp);
} else
for (int i=0;i<t.numchildren();i++)
RegisterNested(t.child(i),m_eval,parent);
}
// Compile the function...
bool MFunctionDef::updateCode(Interpreter *m_eval) {
if (localFunction) return false;
if (pcodeFunction) return false;
if (nestedFunction) return false;
// First, stat the file to get its time stamp
struct stat filestat;
stat(fileName.c_str(),&filestat);
if (!functionCompiled || (filestat.st_mtime != timeStamp)) {
// Record the time stamp
timeStamp = filestat.st_mtime;
// Next, open the function's file
FILE *fp = fopen(fileName.c_str(),"r");
if (fp == NULL)
throw Exception(std::string("Unable to open file :") + fileName);
// read lines until we get to a non-comment line
bool commentsOnly;
commentsOnly = true;
helpText.clear();
char buffer[1000];
while (!feof(fp) && commentsOnly) {
buffer[0] = 0;
fgets(buffer,1000,fp);
char *cp;
cp = buffer;
while ((*cp == ' ') || (*cp == '\t'))
cp++;
if (*cp == '\n')
continue;
if (*cp != '%')
commentsOnly = false;
else {
string htext(++cp);
if ((htext.size() > 1) && (htext[htext.size()-1] != '\n'))
helpText.push_back(htext + "\n");
else
helpText.push_back(htext);
}
}
if (helpText.size() == 0)
helpText.push_back(buffer);
rewind(fp);
try {
// Read the file into a string
string fileText = ReadFileIntoString(fp);
Scanner S(fileText,fileName);
Parser P(S);
tree pcode = P.Process();
fclose(fp);
if (pcode.is(TOK_FUNCTION_DEFS)) {
scriptFlag = false;
// Get the main function..
tree MainFuncCode = pcode.first();
returnVals = IdentifierList(MainFuncCode.first());
// The name is mangled by the interpreter... We ignore the
// name as parsed in the function.
// name = MainFuncCode.second().text();
arguments = IdentifierList(MainFuncCode.child(2));
code = MainFuncCode.child(3);
VariableReferencesList(code,variablesAccessed);
// Register any nested functions
RegisterNested(code,m_eval,this);
localFunction = false;
// Process the local functions
for (int index = 1;index < pcode.numchildren();index++) {
tree LocalFuncCode = pcode.child(index);
MFunctionDef *fp = new MFunctionDef;
fp->localFunction = true;
fp->returnVals = IdentifierList(LocalFuncCode.first());
fp->name = name + "/" + LocalFuncCode.second().text();
fp->arguments = IdentifierList(LocalFuncCode.child(2));
fp->code = LocalFuncCode.child(3);
VariableReferencesList(fp->code,fp->variablesAccessed);
fp->fileName = fileName;
// Register any nested functions for the local functions
// local functions are not marked as temporary. This yields
// clutter in the name space, but solves the troublesome
// issue of local functions being flushed by the CD command.
m_eval->getContext()->insertFunction(fp,false);
// qDebug() << "Registering " << QString::fromStdString(fp->name);
RegisterNested(fp->code,m_eval,this);
}
functionCompiled = true;
} else {
scriptFlag = true;
functionCompiled = true;
code = pcode.first();
}
} catch (Exception &e) {
if (fp) fclose(fp);
functionCompiled = false;
throw;
}
return true;
}
return false;
}
bool StatementTypeNode(tree t) {
return (t.is('=') || t.is(TOK_MULTI) || t.is(TOK_SPECIAL) ||
t.is(TOK_FOR) || t.is(TOK_WHILE) || t.is(TOK_IF) ||
t.is(TOK_BREAK) || t.is(TOK_CONTINUE) || t.is(TOK_DBSTEP) ||
t.is(TOK_RETURN) || t.is(TOK_SWITCH) || t.is(TOK_TRY) ||
t.is(TOK_QUIT) || t.is(TOK_RETALL) || t.is(TOK_KEYBOARD) ||
t.is(TOK_GLOBAL) || t.is(TOK_PERSISTENT) || t.is(TOK_EXPR));
}
// Find the smallest line number larger than the argument
// if our line number is larger than the target, then we
//
void TreeLine(tree t, unsigned &bestLine, unsigned lineTarget) {
if (!t.valid()) return;
// Nested functions are tracked separately - so that we do not
// check them for line numbers
if (t.is(TOK_NEST_FUNC)) return;
if (StatementTypeNode(t)) {
unsigned myLine = (t.context() & 0xffff);
if ((myLine >= lineTarget) && (myLine < bestLine))
bestLine = myLine;
}
for (int i=0;i<t.numchildren();i++)
TreeLine(t.child(i),bestLine,lineTarget);
}
// Find the closest line number to the requested
unsigned MFunctionDef::ClosestLine(unsigned line) {
unsigned bestline;
bestline = 1000000000;
TreeLine(code,bestline,line);
if (bestline == 1000000000)
throw Exception(string("Unable to find a line close to ") +
line + string(" in routine ") + name);
return bestline;
}
void FreezeMFunction(MFunctionDef *fptr, Serialize *s) {
s->putString(fptr->name.c_str());
s->putString(fptr->fileName.c_str());
s->putBool(fptr->scriptFlag);
s->putStringVector(fptr->arguments);
s->putStringVector(fptr->returnVals);
s->putBool(fptr->functionCompiled);
s->putBool(fptr->localFunction);
s->putBool(fptr->nestedFunction);
s->putBool(fptr->capturedFunction);
s->putStringVector(fptr->helpText);
s->putStringVector(fptr->variablesAccessed);
FreezeScope(fptr->workspace,s);
FreezeTree(fptr->code,s);
}
MFunctionDef* ThawMFunction(Serialize *s) {
MFunctionDef *t = new MFunctionDef();
t->name = std::string(s->getString());
t->fileName = std::string(s->getString());
t->scriptFlag = s->getBool();
t->arguments = s->getStringVector();
t->returnVals = s->getStringVector();
t->functionCompiled = s->getBool();
t->localFunction = s->getBool();
t->nestedFunction = s->getBool();
t->capturedFunction = s->getBool();
t->helpText = s->getStringVector();
t->variablesAccessed = s->getStringVector();
t->workspace = ThawScope(s);
t->code = ThawTree(s);
return t;
}
BuiltInFunctionDef::BuiltInFunctionDef() {
}
BuiltInFunctionDef::~BuiltInFunctionDef() {
}
int BuiltInFunctionDef::inputArgCount() {
return argCount;
}
int BuiltInFunctionDef::outputArgCount() {
return retCount;
}
void BuiltInFunctionDef::printMe(Interpreter *eval) {
stringVector tmp;
char msgBuffer[MSGBUFLEN];
snprintf(msgBuffer,MSGBUFLEN," Function name:%s\n",name.c_str());
eval->outputMessage(msgBuffer);
eval->outputMessage(" Function class: Built in\n");
snprintf(msgBuffer,MSGBUFLEN," Return count: %d\n",retCount);
eval->outputMessage(msgBuffer);
snprintf(msgBuffer,MSGBUFLEN," Argument count: %d\n",argCount);
eval->outputMessage(msgBuffer);
}
ArrayVector BuiltInFunctionDef::evaluateFunction(Interpreter *walker, ArrayVector& inputs,
int nargout) {
ArrayVector outputs;
int i;
walker->pushDebug(name,"built in");
try {
outputs = fptr(nargout,inputs);
walker->popDebug();
return outputs;
} catch(Exception& e) {
walker->popDebug();
throw;
}
catch (InterpreterRetallException& e) {
walker->popDebug();
throw;
}
}
SpecialFunctionDef::SpecialFunctionDef() {
}
SpecialFunctionDef::~SpecialFunctionDef() {
}
ArrayVector SpecialFunctionDef::evaluateFunction(Interpreter *walker,
ArrayVector& inputs, int nargout) {
ArrayVector outputs;
walker->pushDebug(name,"built in");
try {
outputs = fptr(nargout,inputs,walker);
walker->popDebug();
return outputs;
} catch(Exception& e) {
walker->popDebug();
throw;
}
catch (InterpreterRetallException& e) {
walker->popDebug();
throw;
}
}
void SpecialFunctionDef::printMe(Interpreter *eval) {
}
FunctionDef::FunctionDef() {
scriptFlag = false;
graphicsFunction = false;
temporaryFlag = false;
refcount = 0;
}
void FunctionDef::lock() {
QMutexLocker lockit(&functiondefmutex);
refcount++;
}
void FunctionDef::unlock() {
QMutexLocker lockit(&functiondefmutex);
refcount--;
}
bool FunctionDef::referenced() {
QMutexLocker lockit(&functiondefmutex);
return (refcount>0);
}
FunctionDef::~FunctionDef() {
}
ImportedFunctionDef::ImportedFunctionDef(GenericFuncPointer address_arg,
stringVector types_arg,
stringVector arguments_arg,
treeVector sizeChecks,
std::string retType_arg) {
address = address_arg;
types = types_arg;
arguments = arguments_arg;
sizeCheckExpressions = sizeChecks;
retType = retType_arg;
/*
* Set up the cif...
*/
argCount = types_arg.size();
if (retType == "void")
retCount = 0;
else
retCount = 1;
}
ImportedFunctionDef::~ImportedFunctionDef() {
}
void ImportedFunctionDef::printMe(Interpreter *) {
}
Class mapTypeNameToClass(std::string name) {
if (name == "logical") return FM_LOGICAL;
if (name == "uint8") return FM_UINT8;
if (name == "int8") return FM_INT8;
if (name == "uint16") return FM_UINT16;
if (name == "int16") return FM_INT16;
if (name == "uint32") return FM_UINT32;
if (name == "int32") return FM_INT32;
if (name == "uint64") return FM_UINT64;
if (name == "int64") return FM_INT64;
if (name == "float") return FM_FLOAT;
if (name == "complex") return FM_COMPLEX;
if (name == "double") return FM_DOUBLE;
if (name == "dcomplex") return FM_DCOMPLEX;
if (name == "string") return FM_STRING;
if (name == "void") return FM_UINT32;
throw Exception("unrecognized type " + name + " in imported function setup");
}
ArrayVector ImportedFunctionDef::evaluateFunction(Interpreter *walker,
ArrayVector& inputs,
int nargout) {
#if HAVE_AVCALL
walker->pushDebug(name,"imported");
/**
* To actually evaluate the function, we have to process each of
* the arguments and get them into the right form.
*/
int i;
for (i=0;i<inputs.size();i++)
inputs[i].promoteType(mapTypeNameToClass(types[i]));
/**
* Next, we count how many of the inputs are to be passed by
* reference.
*/
int passByReference = 0;
for (int j=0;j<inputs.size();j++)
if ((arguments[j][0] == '&') || (types[j] == "string") ||
(sizeCheckExpressions[j].valid()))
passByReference++;
/**
* Next, we check to see if any bounds-checking expressions are
* active.
*/
bool boundsCheckActive = false;
int m=0;
while (m < inputs.size() && !boundsCheckActive)
boundsCheckActive = (sizeCheckExpressions[m++].valid());
if (boundsCheckActive) {
/**
* If the bounds-checking is active, we have to create a
* new context, and insert the defined arguments into the
* context (much as for an M-function call).
*/
Context* context;
context = walker->getContext();
context->pushScope("temp");
walker->pushDebug(name,"bounds check");
try {
for (i=0;i<inputs.size();i++) {
if (arguments[i][0] != '&') {
context->insertVariableLocally(arguments[i],inputs[i]);
} else {
string nme(arguments[i]);
nme.erase(nme.begin());
context->insertVariableLocally(nme,inputs[i]);
}
}
/*
* Next, evaluate each size check expression
*/
for (i=0;i<inputs.size();i++) {
if (sizeCheckExpressions[i].valid()) {
Array ret(walker->expression(sizeCheckExpressions[i]));
ret.promoteType(FM_INT32);
int len;
len = ret.getContentsAsIntegerScalar();
if (len != inputs[i].getLength()) {
throw Exception("array input " + arguments[i] +
" length different from computed bounds" +
" check length");
}
}
}
} catch (Exception& e) {
context->popScope();
throw;
} catch (InterpreterRetallException& e) {
context->popScope();
throw;
}
context->popScope();
walker->popDebug();
}
/**
* Allocate an array of pointers to store for variables passed
* by reference.
*/
void **refPointers;
refPointers = (void**) malloc(sizeof(void*)*passByReference);
/**
* Allocate an array of value pointers...
*/
void **values;
values = (void**) malloc(sizeof(void*)*inputs.size());
int ptr = 0;
for (i=0;i<inputs.size();i++) {
if (types[i] != "string") {
if ((arguments[i][0] == '&') || (sizeCheckExpressions[i].valid())) {
refPointers[ptr] = inputs[i].getReadWriteDataPointer();
values[i] = &refPointers[ptr];
ptr++;
} else {
values[i] = inputs[i].getReadWriteDataPointer();
}
} else {
refPointers[ptr] = strdup(inputs[i].getContentsAsString().c_str());
values[i] = &refPointers[ptr];
ptr++;
}
}
Array retArray;
// The argument list object
av_alist alist;
// Holders for the return values
uint8 ret_uint8;
int8 ret_int8;
uint16 ret_uint16;
int16 ret_int16;
uint32 ret_uint32;
int32 ret_int32;
float ret_float;
double ret_double;
// First pass - based on the return type, call the right version of av_start_*
if ((retType == "uint8") || (retType == "logical")) {
av_start_uchar(alist,address,&ret_uint8);
} else if (retType == "int8") {
av_start_schar(alist,address,&ret_int8);
} else if (retType == "uint16") {
av_start_ushort(alist,address,&ret_uint16);
} else if (retType == "int16") {
av_start_short(alist,address,&ret_int16);
} else if (retType == "uint32") {
av_start_uint(alist,address,&ret_uint32);
} else if (retType == "int32") {
av_start_int(alist,address,&ret_int32);
} else if (retType == "float") {
av_start_float(alist,address,&ret_float);
} else if (retType == "double") {
av_start_double(alist,address,&ret_double);
} else if (retType == "void") {
av_start_void(alist,address);
} else
throw Exception("Unsupported return type " + retType + " in imported function call");
// Second pass - Loop through the arguments
for (int i=0;i<types.size();i++) {
if (arguments[i][0] == '&' || types[i] == "string" ||
sizeCheckExpressions[i].valid())
av_ptr(alist,void*,*((void**)values[i]));
else {
if ((types[i] == "logical") || (types[i] == "uint8"))
av_uchar(alist,*((uint8*)values[i]));
else if (types[i] == "int8")
av_char(alist,*((int8*)values[i]));
else if (types[i] == "uint16")
av_ushort(alist,*((uint16*)values[i]));
else if (types[i] == "int16")
av_short(alist,*((int16*)values[i]));
else if (types[i] == "uint32")
av_uint(alist,*((uint32*)values[i]));
else if (types[i] == "int32")
av_int(alist,*((int32*)values[i]));
else if ((types[i] == "float") || (types[i] == "complex"))
av_float(alist,*((float*)values[i]));
else if ((types[i] == "double") || (types[i] == "dcomplex"))
av_double(alist,*((double*)values[i]));
}
}
// Call the function
av_call(alist);
// Extract the return value
if ((retType == "uint8") || (retType == "logical")) {
retArray = Array::uint8Constructor(ret_uint8);
} else if (retType == "int8") {
retArray = Array::int8Constructor(ret_int8);
} else if (retType == "uint16") {
retArray = Array::uint16Constructor(ret_uint16);
} else if (retType == "int16") {
retArray = Array::int16Constructor(ret_int16);
} else if (retType== "uint32") {
retArray = Array::uint32Constructor(ret_uint32);
} else if (retType == "int32") {
retArray = Array::int32Constructor(ret_int32);
} else if ((retType == "float") || (retType == "complex")) {
retArray = Array::floatConstructor(ret_float);
} else if ((retType == "double") || (retType == "dcomplex")) {
retArray = Array::doubleConstructor(ret_double);
} else {
retArray = Array::emptyConstructor();
}
// Strings that were passed by reference have to be
// special-cased
ptr = 0;
for (i=0;i<inputs.size();i++) {
if ((arguments[i][0] == '&') || (types[i] == "string")) {
if ((types[i] == "string") && (arguments[i][0] == '&'))
inputs[i] = Array::stringConstructor((char*) refPointers[ptr]);
ptr++;
}
}
free(refPointers);
free(values);
walker->popDebug();
return singleArrayVector(retArray);
#else
throw Exception("Support for the import command requires that the avcall library be installed. FreeMat was compiled without this library being available, and hence imported functions are unavailable. To enable imported commands, please install avcall and recompile FreeMat.");
#endif
}
MexFunctionDef::MexFunctionDef(std::string fullpathname) {
fullname = fullpathname;
importSuccess = false;
lib = new DynLib(fullname);
try {
address = (mexFuncPtr) lib->GetSymbol("mexFunction");
importSuccess = true;
} catch (Exception& e) {
}
}
bool MexFunctionDef::LoadSuccessful() {
return importSuccess;
}
MexFunctionDef::~MexFunctionDef() {
}
void MexFunctionDef::printMe(Interpreter *) {
}
ArrayVector MexFunctionDef::evaluateFunction(Interpreter *walker,
ArrayVector& inputs,
int nargout) {
// Convert arguments to mxArray
mxArray** args = new mxArray*[inputs.size()];
for (int i=0;i<inputs.size();i++)
args[i] = MexArrayFromArray(inputs[i]);
// Allocate output array vector
int lhsCount = nargout;
lhsCount = (lhsCount < 1) ? 1 : lhsCount;
mxArray** rets = new mxArray*[lhsCount];
try {
address(lhsCount,rets,inputs.size(),(const mxArray**)args);
} catch (std::string &e) {
throw Exception(e);
}
ArrayVector retvec;
for (int i=0;i<lhsCount;i++) {
retvec.push_back(ArrayFromMexArray(rets[i]));
mxDestroyArray(rets[i]);
}
delete[] rets;
return retvec;
}
syntax highlighted by Code2HTML, v. 0.9.1