📄 jsicontext.cpp
字号:
/****************License************************************************
*
* Copyright 2000-2003. ScanSoft, Inc.
*
* Use of this software is subject to notices and obligations set forth
* in the SpeechWorks Public License - Software Version 1.2 which is
* included with this software.
*
* ScanSoft is a registered trademark of ScanSoft, Inc., and OpenSpeech,
* SpeechWorks and the SpeechWorks logo are registered trademarks or
* trademarks of SpeechWorks International, Inc. in the United States
* and other countries.
*
***********************************************************************/
/*****************************************************************************
*****************************************************************************
*
*
* JsiContext, class for managing JavaScript contexts
*
* The JsiContext class represents a JavaScript context, a script
* execution state. All JavaScript variables are maintained in a
* context, and all scripts are executed in reference to a context
* (for accessing variables and maintaining script side-effects). Each
* context may have one or more scopes that are used to layer the
* state information so that it is possible for clients to control the
* lifetime of state information within the context.
* SBjsiInterface, definition of the real SBjsi resource object
*
*****************************************************************************
****************************************************************************/
// -----1=0-------2=0-------3=0-------4=0-------5=0-------6=0-------7=0-------8
#include <vxibuildopts.h>
#if P_VXI
#include "SBjsiInternal.h"
#include "JsiRuntime.hpp" // For the JsiRuntime class
#include "JsiContext.hpp" // Defines this class
#include <string.h> // For strlen( )
#include <deque> // for deque
#include "SBjsiLog.h" // For logging
#include "SBjsiString.hpp" // For SBjsiString
#include "JsiCharCvt.hpp" // For jschar <-> VXIchar conversion
// SpiderMonkey class that describes our scope objects which we use
// as contexts
static JSClass SCOPE_CLASS = {
"__SBjsiScope", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JsiContext::SCOPE_CLASS_SetProperty,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub,
JsiContext::ScopeFinalize, JSCLASS_NO_OPTIONAL_MEMBERS
};
// SpiderMonkey class that describes our content objects which we use
// to point at VXIContent objects
static JSClass CONTENT_CLASS = {
"__SBjsiContent", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub,
JsiContext::ContentFinalize, JSCLASS_NO_OPTIONAL_MEMBERS
};
// SpiderMonkey class that describes our objects which we create from
// a VXIMap, just very simple objects
static JSClass MAP_CLASS = {
"__SBjsiMap", 0,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub,
JS_FinalizeStub, JSCLASS_NO_OPTIONAL_MEMBERS
};
// Names for JS objects we protect from garbage collection, only used
// for SpiderMonkey debugging purposes
static const char GLOBAL_SCOPE_NAME[] = "__SBjsiGlobalScope";
static const wchar_t GLOBAL_SCOPE_NAME_W[] = L"__SBjsiGlobalScope";
static const char SCRIPT_OBJECT_NAME[] = "__SBjsiScriptObject";
static const char PROTECTED_JSVAL_NAME[] = "__SBjsiProtectedJsval";
// Global variable we use for temporaries
static const char GLOBAL_TEMP_VAR[] = "__SBjsiTempVar";
static const wchar_t GLOBAL_TEMP_VAR_W[] = L"__SBjsiTempVar";
// -----1=0-------2=0-------3=0-------4=0-------5=0-------6=0-------7=0-------8
#define GET_VXICHAR_FROM_JSCHAR(out, in) \
JscharToVXIchar convert_##out(in); \
const VXIchar *out = convert_##out.c_str( )
#define GET_JSCHAR_FROM_VXICHAR(out, outlen, in) \
VXIcharToJschar convert_##out(in); \
const jschar *out = convert_##out.c_str( ); \
size_t outlen = convert_##out.length( )
// -----1=0-------2=0-------3=0-------4=0-------5=0-------6=0-------7=0-------8
// Wrapper class around jsval that automatically protects the held
// jsval from garbage collection
class JsiProtectedJsval {
public:
// Constructor and destructor
JsiProtectedJsval (JSContext *c) : context(c), val(JSVAL_VOID) { }
~JsiProtectedJsval( ) { Clear( ); }
// Clear the value
VXIjsiResult Clear( ) {
VXIjsiResult rc;
rc = ((( JSVAL_IS_GCTHING(val) ) && ( ! JS_RemoveRoot (context, &val) )) ?
VXIjsi_RESULT_FATAL_ERROR : VXIjsi_RESULT_SUCCESS);
val = JSVAL_VOID;
return rc;
}
// Set the value
VXIjsiResult Set (jsval v) {
VXIjsiResult rc = Clear( );
val = v;
if (( JSVAL_IS_GCTHING(val) ) &&
( ! JS_AddNamedRoot (context, &val, PROTECTED_JSVAL_NAME) )){
rc = VXIjsi_RESULT_OUT_OF_MEMORY; // blown away by GC already!
}
return rc;
}
// Accessor
const jsval Get( ) const { return val; }
private:
// Disabled copy constructor and assignment operator
JsiProtectedJsval (const JsiProtectedJsval &v);
JsiProtectedJsval &operator=(const JsiProtectedJsval &v);
private:
JSContext *context;
jsval val;
};
// -----1=0-------2=0-------3=0-------4=0-------5=0-------6=0-------7=0-------8
// Scope chain class, used to keep the scope chain independantly of
// the actual user visible scope chain to avoid crashes from invalid
// or malicious scripts (push a scope called "session", then overwrite
// that variable with an integer named "session", then try to do more
// work in that scope)
class JsiScopeChainNode {
public:
// Constructor and destructor
JsiScopeChainNode (JSContext *context, JsiScopeChainNode *p,
const VXIchar *n) :
pushAliasFlag(false), name(n), parent(p), child(NULL), jsVal(context) { }
~JsiScopeChainNode( ) { }
// Creation method
VXIjsiResult Create (JSObject *scopeObj) {
return jsVal.Set (OBJECT_TO_JSVAL(scopeObj)); }
// Accessors
const SBjsiString & GetName( ) const { return name; }
jsval GetJsval( ) { return jsVal.Get( ); }
JSObject * GetJsobj( ) { return JSVAL_TO_OBJECT(jsVal.Get( )); }
JsiScopeChainNode * GetParent( ) { return parent; }
JsiScopeChainNode * GetChild( ) { return child; }
// Set the child scope
void SetChild (JsiScopeChainNode *c) { child = c; }
// Release this scope and all under it
VXIjsiResult Release( );
// Alias functions
VXIjsiResult PushAlias(JsiScopeChainNode *_alias)
{
VXIjsiResult rc = VXIjsi_RESULT_SUCCESS;
if( _alias )
{
aliasList.push_front(_alias);
}
else
rc = VXIjsi_RESULT_FAILURE;
return rc;
}
JsiScopeChainNode* PopAlias(void)
{
JsiScopeChainNode *a = NULL;
if( !aliasList.empty() )
{
a = aliasList.front();
aliasList.pop_front();
}
return a;
}
bool HasAlias(void) { return !aliasList.empty(); }
const SBjsiString & GetAliasName(void) const
{
if( !aliasList.empty() )
return aliasList.front()->GetName();
return GetName();
}
void SetAliasScopeFlag(void) { pushAliasFlag = true; }
bool IsSetAliasScopeFlag(void) { return pushAliasFlag; }
void ResetAliasScopeFlag(void) { pushAliasFlag = false; }
private:
SBjsiString name; // Name of this scope
JsiScopeChainNode *parent; // Parent scope
JsiScopeChainNode *child; // Child scope, may be NULL
JsiProtectedJsval jsVal; // JS object for the scope
// alias list
std::deque<JsiScopeChainNode*> aliasList;
bool pushAliasFlag;
};
VXIjsiResult JsiScopeChainNode::Release( )
{
JsiScopeChainNode *node = this, *next = NULL, *alias = NULL;
while (node) {
// if node is an alias, just pop the alias and continue
if( node->HasAlias() )
{
alias = node->PopAlias();
if( alias ) alias->jsVal.Clear( );
continue;
}
// Release the lock on the underlying object
node->jsVal.Clear( );
// Clear pointers for safety and advance to child scopes
next = node->child;
node->parent = NULL;
node->child = NULL;
node = next;
// NOTE: the deletion of this node and the child nodes is handled
// by the ScopeFinalize( ) method that is called when the JS
// object is garbage collected
}
return VXIjsi_RESULT_SUCCESS;
}
// To support VXML 2.0 SPEC, that added the read-only attribute to a namespace.
// Therefore any attempt to modify, create any variable in this namespace will result
// in java script semantic error. This is the callback that will be called
// every time set property function is called. It will check for read-only flag
// of the current scope to determine if it is read-only, if so return false.
JSBool JsiContext::SCOPE_CLASS_SetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
uintN attrp;
JSBool foundp;
JsiScopeChainNode * prdata = (JsiScopeChainNode *) JS_GetPrivate(cx, obj);
if( prdata )
{
// return true, if pushing alias scope
if (prdata->IsSetAliasScopeFlag() )
return JS_TRUE;
GET_JSCHAR_FROM_VXICHAR(tmpname, tmpnamelen, prdata->GetName().c_str());
JSObject *pobj = (prdata->GetParent() ? prdata->GetParent()->GetJsobj() : prdata->GetJsobj());
if( JS_GetUCPropertyAttributes(cx, pobj,
tmpname, tmpnamelen,
&attrp, &foundp) )
{
if( foundp && (attrp & JSPROP_READONLY) )
{
return JS_FALSE;
}
}
}
return JS_TRUE;
}
VXIjsiResult JsiContext::CheckWriteable(JSContext *cx, JSObject *obj, const VXIchar* varname)
{
VXIjsiResult rc = VXIjsi_RESULT_SUCCESS;
uintN attrp;
JSBool foundp;
JsiScopeChainNode * prdata = (JsiScopeChainNode *) JS_GetPrivate(cx, obj);
// evaluate var name to get the root, i.e a.b.c, then the root is a
// if there is no root i.e, a then we don't need to evaluate
if( wcschr (varname, L'.') )
{
//VXIchar* rootvar, *state = NULL;
VXIchar* rootvar = NULL;
VXIchar _varname[1024];
wcscpy(_varname, varname);
#ifdef WIN32
rootvar = wcstok(_varname, L".");
#else
rootvar = wcstok(_varname, L".", &state);
#endif
if( rootvar )
{
JsiProtectedJsval val (context);
VXIjsiResult rc = EvaluateScript (rootvar, &val);
JSObject *classobj = JSVAL_TO_OBJECT(val.Get());
// if this object is scope class then get the private data,
// otherwise just ignore it
if( rc == VXIjsi_RESULT_SUCCESS && JS_InstanceOf (cx, classobj, &SCOPE_CLASS, NULL) )
{
prdata = (JsiScopeChainNode *) JS_GetPrivate(cx, classobj);
}
val.Clear( );
}
}
if( prdata )
{
// get scope from the current parent
JSObject* pscope = (prdata->GetParent() ? prdata->GetParent()->GetJsobj() : prdata->GetJsobj());
// retrieve read-only attribute
GET_JSCHAR_FROM_VXICHAR(tmpname, tmpnamelen, prdata->GetName().c_str());
if( JS_GetUCPropertyAttributes(cx, pscope,
tmpname, tmpnamelen,
&attrp, &foundp) )
{
if( foundp && (attrp & JSPROP_READONLY) )
{
return rc = VXIjsi_RESULT_NON_FATAL_ERROR;
}
}
}
return rc;
}
// -----1=0-------2=0-------3=0-------4=0-------5=0-------6=0-------7=0-------8
// Constructor, only does initialization that cannot fail
JsiContext::JsiContext( ) : SBjsiLogger (MODULE_SBJSI, NULL, 0),
version(JSVERSION_DEFAULT), runtime(NULL), context(NULL),
contextRefs(0), scopeChain(NULL), currentScope(NULL), logEnabled(true),
maxBranches(0L), numBranches(0L), exception(NULL)
{
}
// Destructor
JsiContext::~JsiContext( )
{
Diag (SBJSI_LOG_CONTEXT, L"JsiContext::~JsiContext",
L"start 0x%p, JS context 0x%p", this, context);
if ( exception )
VXIValueDestroy (&exception);
if ( context ) {
// Lock the context for access
#ifdef JS_THREADSAFE
JS_ResumeRequest (context, contextRefs);
#else
if ( ! AccessBegin( ) )
return;
#endif
// Destroy the scope chain, which automatically unroots the global
// scope to allow garbage collection of everything
if ( scopeChain )
scopeChain->Release( );
// Release the lock, must be done before destroying the context
#ifdef JS_THREADSAFE
JS_EndRequest (context);
#else
AccessEnd( );
#endif
// Destroy the context, is set to NULL
runtime->DestroyContext (&context);
}
Diag (SBJSI_LOG_CONTEXT, L"JsiContext::~JsiContext", L"end 0x%p", this);
}
// Creation method
VXIjsiResult JsiContext::Create (JsiRuntime *rt,
long contextSize,
long mb,
VXIlogInterface *l,
VXIunsigned diagTagBase)
{
if (( rt == NULL ) || ( contextSize < 1 ) || ( mb < 1 ) ||
( l == NULL ))
return VXIjsi_RESULT_INVALID_ARGUMENT;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -