📄 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 "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 _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; // Copy simple variables runtime = rt;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -