📄 jsicontext.cpp
字号:
// Must unroot before unlocking
val.Clear( );
if ( AccessEnd( ) == false )
rc = VXIjsi_RESULT_SYSTEM_ERROR;
return rc;
}
// Get the value of a variable
VXIjsiResult JsiContext::GetVar (const VXIchar *name,
VXIValue **value) const
{
if (( name == NULL ) || ( name[0] == 0 ) || ( value == NULL ))
return VXIjsi_RESULT_INVALID_ARGUMENT;
*value = NULL;
if ( AccessBegin( ) == false )
return VXIjsi_RESULT_SYSTEM_ERROR;
// Get the variable, we need to evaluate instead of just calling
// JS_GetUCProperty( ) in order to have it automatically look back
// through the scope chain, as well as to permit explicit scope
// references like "myscope.myvar"
JsiProtectedJsval val (context);
VXIjsiResult rc = EvaluateScript (name, &val);
if ( rc == VXIjsi_RESULT_SUCCESS )
rc = JsvalToVXIValue (context, val.Get( ), value);
else if (( rc == VXIjsi_RESULT_SYNTAX_ERROR ) ||
( rc == VXIjsi_RESULT_SCRIPT_EXCEPTION ))
rc = VXIjsi_RESULT_NON_FATAL_ERROR;
// Must unroot before unlocking
val.Clear( );
if ( AccessEnd( ) == false )
rc = VXIjsi_RESULT_SYSTEM_ERROR;
return rc;
}
// Check whether a variable is defined (not void, could be NULL)
VXIjsiResult JsiContext::CheckVar (const VXIchar *name) const
{
if (( name == NULL ) || ( name[0] == 0 ))
return VXIjsi_RESULT_INVALID_ARGUMENT;
if ( AccessBegin( ) == false )
return VXIjsi_RESULT_SYSTEM_ERROR;
// Get the variable, we need to evaluate instead of just calling
// JS_GetUCProperty( ) in order to have it automatically look back
// through the scope chain, as well as to permit explicit scope
// references like "myscope.myvar". We disable logging because
// we don't want JavaScript error reports about this var being
// undefined.
JsiProtectedJsval val (context);
VXIjsiResult rc = EvaluateScript (name, &val, false);
if (( rc == VXIjsi_RESULT_SYNTAX_ERROR ) ||
( rc == VXIjsi_RESULT_SCRIPT_EXCEPTION ) ||
( rc == VXIjsi_RESULT_NON_FATAL_ERROR ) )
rc = VXIjsi_RESULT_NON_FATAL_ERROR;
else if (( rc == VXIjsi_RESULT_SUCCESS ) &&
( JSVAL_IS_VOID (val.Get( )) ) ) // JavaScript undefined
rc = VXIjsi_RESULT_FAILURE;
// Must unroot before unlocking
val.Clear( );
if ( AccessEnd( ) == false )
rc = VXIjsi_RESULT_SYSTEM_ERROR;
return rc;
}
// Execute a script, optionally returning any execution result
VXIjsiResult JsiContext::Eval (const VXIchar *expr,
VXIValue **result)
{
if (( expr == NULL ) || ( expr[0] == 0 ))
return VXIjsi_RESULT_INVALID_ARGUMENT;
if ( result )
*result = NULL;
// Execute the script
if ( AccessBegin( ) == false )
return VXIjsi_RESULT_SYSTEM_ERROR;
JsiProtectedJsval val (context);
VXIjsiResult rc = EvaluateScript (expr, &val);
if (( result ) && ( rc == VXIjsi_RESULT_SUCCESS ) &&
( val.Get( ) != JSVAL_VOID ))
rc = JsvalToVXIValue (context, val.Get( ), result);
// Must unroot before unlocking
val.Clear( );
if ( AccessEnd( ) == false )
rc = VXIjsi_RESULT_SYSTEM_ERROR;
return rc;
}
// Push a new context onto the scope chain (add a nested scope)
VXIjsiResult JsiContext::PushScope (const VXIchar *name, const VXIjsiScopeAttr attr)
{
if (( ! name ) || ( ! name[0] ))
return VXIjsi_RESULT_INVALID_ARGUMENT;
if ( AccessBegin( ) == false )
return VXIjsi_RESULT_SYSTEM_ERROR;
// Create an object for the scope, the current scope is its parent
VXIjsiResult rc = VXIjsi_RESULT_SUCCESS;
if( attr == VXIjsi_NATIVE_SCOPE )
{
JSObject *scope = JS_NewObject (context, &SCOPE_CLASS, NULL,
currentScope->GetJsobj( ));
if ( ! scope ) {
rc = VXIjsi_RESULT_OUT_OF_MEMORY;
} else {
Diag (SBJSI_LOG_SCOPE, L"JsiContext::PushScope",
L"scope %s (0x%p), context 0x%p", name, scope, context);
// The scope chain node object locks it to protect this from
// garbage collection in case someone (possibly a malicious
// script) destroys the data member pointing at this in the global
// scope
JsiScopeChainNode *newScope =
new JsiScopeChainNode (context, currentScope, name);
if ( newScope ) {
rc = newScope->Create (scope);
if (( rc == VXIjsi_RESULT_SUCCESS ) &&
( ! JS_SetPrivate (context, scope, newScope) ))
rc = VXIjsi_RESULT_FATAL_ERROR;
} else {
rc = VXIjsi_RESULT_OUT_OF_MEMORY;
}
// Set this scope as a property of the parent scope
if ( rc == VXIjsi_RESULT_SUCCESS ) {
GET_JSCHAR_FROM_VXICHAR(tmpname, tmpnamelen, name);
if ( JS_DefineUCProperty (context, currentScope->GetJsobj( ), tmpname,
tmpnamelen, newScope->GetJsval( ), NULL, NULL, JSPROP_ENUMERATE) ) {
// Add it to the scope chain and set it as the current scope
currentScope->SetChild (newScope);
currentScope = newScope;
} else {
rc = VXIjsi_RESULT_FATAL_ERROR;
}
}
}
}
else if( attr == VXIjsi_ALIAS_SCOPE )
{
JSObject *aliasScope = JS_NewObject (context, &SCOPE_CLASS, NULL,
currentScope->GetJsobj( ));
if ( ! aliasScope ) {
rc = VXIjsi_RESULT_OUT_OF_MEMORY;
} else {
Diag (SBJSI_LOG_SCOPE, L"JsiContext::PushScope",
L"alias scope %s (0x%p), context 0x%p", name, aliasScope, context);
// The scope chain node object locks it to protect this from
// garbage collection in case someone (possibly a malicious
// script) destroys the data member pointing at this in the global
// scope
JsiScopeChainNode *newAliasScope =
new JsiScopeChainNode (context, currentScope->GetParent(), name);
if ( newAliasScope ) {
rc = newAliasScope->Create (aliasScope);
if (( rc == VXIjsi_RESULT_SUCCESS ) &&
( ! JS_SetPrivate (context, aliasScope, newAliasScope) ))
rc = VXIjsi_RESULT_FATAL_ERROR;
} else {
rc = VXIjsi_RESULT_OUT_OF_MEMORY;
}
// Set this scope as a property of the parent scope
if ( rc == VXIjsi_RESULT_SUCCESS ) {
GET_JSCHAR_FROM_VXICHAR(tmpname, tmpnamelen, name);
if ( JS_DefineUCProperty (context, currentScope->GetJsobj( ), tmpname,
tmpnamelen, newAliasScope->GetJsval( ), NULL, NULL, JSPROP_ENUMERATE) ) {
// set the alias scope to current scope by evaluating a javascript
SBjsiString aliasScript(name);
aliasScript += " = ";
aliasScript += currentScope->GetName().c_str();
currentScope->SetAliasScopeFlag();
rc = EvaluateScript (aliasScript.c_str( ), NULL);
currentScope->ResetAliasScopeFlag();
// Add it to the scope chain and set it as the current scope
if( rc == VXIjsi_RESULT_SUCCESS )
rc = currentScope->PushAlias(newAliasScope);
} else {
rc = VXIjsi_RESULT_FATAL_ERROR;
}
}
}
}
else
rc = VXIjsi_RESULT_INVALID_ARGUMENT;
if ( AccessEnd( ) == false )
rc = VXIjsi_RESULT_SYSTEM_ERROR;
return rc;
}
// Pop a context from the scope chain (remove a nested scope)
VXIjsiResult JsiContext::PopScope( )
{
// Don't pop up past the global scope
if ( currentScope == scopeChain )
return VXIjsi_RESULT_NON_FATAL_ERROR;
if ( AccessBegin( ) == false )
return VXIjsi_RESULT_SYSTEM_ERROR;
// make sure we retrieve correct native scope name and alias
const VXIchar* scopeName = (currentScope->HasAlias() ?
currentScope->GetAliasName().c_str() : currentScope->GetName().c_str());
Diag (SBJSI_LOG_SCOPE, L"JsiContext::PopScope",
L"scope %s (0x%p), context 0x%p",
scopeName, currentScope->GetJsobj( ), context);
VXIjsiResult rc = VXIjsi_RESULT_SUCCESS;
// if the current scope is an alias, we pop the alias then unlock and return
if( currentScope->HasAlias() )
{
JsiScopeChainNode *oldAlias = currentScope->PopAlias();
// Release the old scope variable within the parent scope to permit
// it to be garbage collected
rc = (oldAlias == NULL ? VXIjsi_RESULT_FAILURE : rc);
if( rc == VXIjsi_RESULT_SUCCESS )
{
jsval rval;
GET_JSCHAR_FROM_VXICHAR(tmpname, tmpnamelen, oldAlias->GetName( ).c_str( ));
if ( ! JS_DeleteUCProperty2 (context, currentScope->GetJsobj( ), tmpname,
tmpnamelen, &rval) )
rc = VXIjsi_RESULT_FATAL_ERROR;
// Now finish releasing the old scope, the actual wrapper object is
// freed by the scope finalize method when garbage collection occurs
rc = oldAlias->Release( );
}
if ( AccessEnd( ) == false )
return VXIjsi_RESULT_SYSTEM_ERROR;
return rc;
}
// Revert to the parent scope
JsiScopeChainNode *oldScope = currentScope;
currentScope = currentScope->GetParent( );
currentScope->SetChild (NULL);
// Release the old scope variable within the parent scope to permit
// it to be garbage collected
jsval rval;
GET_JSCHAR_FROM_VXICHAR(tmpname, tmpnamelen, oldScope->GetName( ).c_str( ));
if ( ! JS_DeleteUCProperty2 (context, currentScope->GetJsobj( ), tmpname,
tmpnamelen, &rval) )
rc = VXIjsi_RESULT_FATAL_ERROR;
// Now finish releasing the old scope, the actual wrapper object is
// freed by the scope finalize method when garbage collection occurs
rc = oldScope->Release( );
if ( AccessEnd( ) == false )
rc = VXIjsi_RESULT_SYSTEM_ERROR;
return rc;
}
// Reset the scope chain to the global scope (pop all nested scopes)
VXIjsiResult JsiContext::ClearScopes( )
{
// See if there is anything to do
if ( currentScope == scopeChain )
return VXIjsi_RESULT_SUCCESS;
if ( AccessBegin( ) == false )
return VXIjsi_RESULT_SYSTEM_ERROR;
Diag (SBJSI_LOG_SCOPE, L"JsiContext::ClearScopes", L"context 0x%p",
context);
// Revert to the global scope
JsiScopeChainNode *oldScope = scopeChain->GetChild( );
currentScope = scopeChain;
currentScope->SetChild (NULL);
// Release the old child scope variable within the global scope to
// permit it and all its descendants to be garbage collected
VXIjsiResult rc = VXIjsi_RESULT_SUCCESS;
jsval rval;
GET_JSCHAR_FROM_VXICHAR(tmpname, tmpnamelen, oldScope->GetName( ).c_str( ));
if ( ! JS_DeleteUCProperty2 (context, currentScope->GetJsobj( ), tmpname,
tmpnamelen, &rval) )
rc = VXIjsi_RESULT_FATAL_ERROR;
// Now finish releasing the old scope, the actual wrapper object is
// freed by the scope finalize method when garbage collection occurs
rc = oldScope->Release( );
if ( AccessEnd( ) == false )
rc = VXIjsi_RESULT_SYSTEM_ERROR;
return rc;
}
// Script evaluation
VXIjsiResult JsiContext::EvaluateScript (const VXIchar *script,
JsiProtectedJsval *retval,
bool loggingEnabled) const
{
if (( script == NULL ) || ( script[0] == 0 ))
return VXIjsi_RESULT_INVALID_ARGUMENT;
// Reset our private data for evaluation callbacks
EvaluatePrepare (loggingEnabled);
if ( retval )
retval->Clear( );
// Compile the script
VXIjsiResult rc = VXIjsi_RESULT_SUCCESS;
GET_JSCHAR_FROM_VXICHAR(tmpscript, tmpscriptlen, script);
JSScript *jsScript = JS_CompileUCScript (context, currentScope->GetJsobj( ),
tmpscript, tmpscriptlen, NULL, 1);
if ( ! jsScript )
rc = VXIjsi_RESULT_SYNTAX_ERROR;
else {
// Create a script object and root it to protect the script from
// garbage collection, note that once this object exists it owns
// the jsScript and thus we must not free it ourselves
JSObject *jsScriptObj = JS_NewScriptObject (context, jsScript);
if (( ! jsScriptObj ) ||
( ! JS_AddNamedRoot (context, &jsScriptObj, SCRIPT_OBJECT_NAME) )) {
JS_DestroyScript (context, jsScript);
rc = VXIjsi_RESULT_OUT_OF_MEMORY;
} else {
// Evaluate the script
jsval val = JSVAL_VOID;
if ( JS_ExecuteScript (context, currentScope->GetJsobj( ), jsScript,
&val) ) {
if ( retval )
rc = retval->Set (val);
} else if ( exception ) {
rc = VXIjsi_RESULT_SCRIPT_EXCEPTION;
} else if ( numBranches > maxBranches ) {
rc = VXIjsi_RESULT_SECURITY_VIOLATION;
} else {
rc = VXIjsi_RESULT_NON_FATAL_ERROR;
}
// Release the script object
if ( ! JS_RemoveRoot (context, &jsScriptObj) )
rc = VXIjsi_RESULT_FATAL_ERROR;
}
}
return rc;
}
// Convert JS values to VXIValue types
VXIjsiResult JsiContext::JsvalToVXIValue (JSContext *context,
const jsval val,
VXIValue **value)
{
if ( value == NULL )
return VXIjsi_RESULT_INVALID_ARGUMENT;
*value = NULL;
VXIjsiResult rc = VXIjsi_RESULT_SUCCESS;
if ( JSVAL_IS_STRING (val) ) {
GET_VXICHAR_FROM_JSCHAR(tmpval, JS_GetStringChars (JSVAL_TO_STRING (val)));
*value = (VXIValue *) VXIStringCreate(tmpval);
} else if ( JSVAL_IS_INT (val) )
*value = (VXIValue *) VXIIntegerCreate (JSVAL_TO_INT (val));
else if ( JSVAL_IS_DOUBLE (val) )
*value = (VXIValue *) VXIFloatCreate ((VXIflt32) *JSVAL_TO_DOUBLE (val));
else if ( JSVAL_IS_BOOLEAN (val) )
*value = (VXIValue *)
VXIBooleanCreate ((JSVAL_TO_BOOLEAN (val) ? TRUE : FALSE));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -