#include "precompiled.h" #include "ScriptInterface.h" #include "CLocale.h" #include "StringConvert.h" #include "scripting/SpiderMonkey.h" #include "ps/CLogger.h" #define LOG_CATEGORY "i18n" using namespace I18n; #define JS_ASSERT(expr, msg) if (! (expr)) { JS_ReportError(cx, msg); return JS_FALSE; } // LookedupWord JS class: namespace JSI_LookedupWord { static JSBool GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { CLocale::LookupType* lookedup = (CLocale::LookupType*)JS_GetPrivate(cx, obj); debug_assert(lookedup); JSObject* parent = JS_GetParent(cx, obj); debug_assert(parent); CLocale* locale = (CLocale*)JS_GetPrivate(cx, parent); debug_assert(locale); JSString* prop = JS_ValueToString(cx, id); JS_ASSERT(prop, "lookup() property failed to convert to string"); jschar* prop_chars = JS_GetStringChars(prop); Str prop_str; StringConvert::jschars_to_wstring(prop_chars, JS_GetStringLength(prop), prop_str); Str result; if (! locale->LookupProperty(lookedup, prop_str, result)) result = L"(unrecognised property)"; JSString* result_str = StringConvert::wstring_to_jsstring(cx, result); JS_ASSERT(result_str, "lookup() property failed to create string"); *vp = STRING_TO_JSVAL(result_str); return JS_TRUE; } static void Finalize(JSContext *cx, JSObject *obj) { // Free the LookupType that was allocated when building this object CLocale::LookupType* lookedup = (CLocale::LookupType*)JS_GetPrivate(cx, obj); debug_assert(lookedup); delete lookedup; } static JSClass JSI_class = { "LookedupWord", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, GetProperty, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize, NULL, NULL, NULL, NULL }; } // 'i18n' JS class: namespace JSI_i18n { #define TYPE(x) \ static JSBool Create_##x(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval) \ { \ /* Set *rval = { type => "Name", value => argv[0] } */ \ \ JSObject* object = JS_NewObject(cx, NULL, NULL, obj); \ JS_ASSERT(object, "Failed to create i18n value object"); \ \ /* TODO: More error checking */ \ JS_ASSERT(argc == 1, "Create_" #x ": not enough params"); \ jsval type = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, #x)); \ jsval value = STRING_TO_JSVAL(JS_ValueToString(cx, argv[0])); \ JS_SetProperty(cx, object, "type", &type); \ JS_SetProperty(cx, object, "value", &value); \ \ *rval = OBJECT_TO_JSVAL(object); \ \ return JS_TRUE; \ } TYPE(Name) TYPE(Raw) TYPE(Noun) #undef TYPE static JSClass JSI_class = { "JSI_i18n", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, NULL, NULL, NULL, NULL }; #define TYPE(x) {#x, Create_##x, 1, 0, 0} static JSFunctionSpec JSI_funcs[] = { TYPE(Name), TYPE(Raw), TYPE(Noun), {0,0,0,0,0}, }; #undef TYPE } static JSBool JSFunc_LookupWord(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JS_ASSERT(argc == 2, "Incorrect number of parameters to lookup(dictionary, word) function"); // Get the strings JSString* dictname = JS_ValueToString(cx, argv[0]); JSString* word = JS_ValueToString(cx, argv[1]); JS_ASSERT(dictname && word, "lookup() failed to convert parameters to strings"); // and the characters from the strings jschar* dictname_chars = JS_GetStringChars(dictname); jschar* word_chars = JS_GetStringChars(word); // (can't fail) // and convert those characters into to wstrings Str dictname_str, word_str; StringConvert::jschars_to_wstring(dictname_chars, JS_GetStringLength(dictname), dictname_str); StringConvert::jschars_to_wstring(word_chars, JS_GetStringLength(word), word_str); // Extract the CLocale* from the 'global' object CLocale* locale = (CLocale*)JS_GetPrivate(cx, obj); const CLocale::LookupType* lookedup = locale->LookupWord(dictname_str, word_str); if (! lookedup) { // Couldn't find the word in the table *rval = JSVAL_NULL; return JS_TRUE; } // Create an object to be returned, containing enough data to access properties of the found word JSObject* wordobj = JS_NewObject(cx, &JSI_LookedupWord::JSI_class, NULL, obj); JS_ASSERT(wordobj, "lookup() failed to create object"); // Associate the looked-up word data with the JS object JS_SetPrivate(cx, wordobj, (void*)lookedup); *rval = OBJECT_TO_JSVAL(wordobj); return JS_TRUE; } static JSBool JSFunc_Translate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JS_ASSERT(argc >= 1, "Too few parameters to translate() function"); JSString* phrase_str = JS_ValueToString(cx, argv[0]); JS_ASSERT(phrase_str, "translate() failed to convert first parameter to string"); CStrW phrase (JS_GetStringChars(phrase_str)); jsval locale_objval; JS_ASSERT(JS_GetProperty(cx, obj, "i18n", &locale_objval), "translate() failed to find i18n object in current scope"); CLocale* locale = (CLocale*)JS_GetPrivate(cx, JSVAL_TO_OBJECT(locale_objval)); StringBuffer sb = locale->Translate(phrase.c_str()); for (uintN i=1; i& vars, const std::vector& params) { int argc = (int)params.size(); // Construct argv, converting each parameter into a jsval jsval* argv = new jsval[argc]; for (int i = 0; i < argc; ++i) argv[i] = params[i]->GetJsval(vars); jsval rval; bool called = JS_CallFunctionName(Context, Object, name, argc, argv, &rval) ? true : false; delete[] argv; if (! called) { LOG(ERROR, LOG_CATEGORY, "I18n: Error executing JS function '%s'", name); return L"(JS error)"; } // Convert rval to a string and return JSString* str = JS_ValueToString(Context, rval); if (! str) { debug_warn("Conversion to string failed"); return L"(JS error)"; } jschar* chars = JS_GetStringChars(str); if (! chars) { debug_warn("GetStringChars failed"); return L"(JS error)"; } return StrImW(chars, JS_GetStringLength(str)); } ScriptValueString::ScriptValueString(ScriptObject& script, const wchar_t* s) { Context = script.Context; JSString* str = StringConvert::wchars_to_jsstring(Context, s); if (!str) { debug_warn("Error creating JS string"); Value = JSVAL_NULL; } else { Value = STRING_TO_JSVAL(str); JS_AddRoot(Context, &Value); } } jsval ScriptValueString::GetJsval(const std::vector& UNUSED(vars)) { return Value; } ScriptValueString::~ScriptValueString() { if (! JSVAL_IS_NULL(Value)) { JS_RemoveRoot(Context, &Value); } } /************/ ScriptValueInteger::ScriptValueInteger(ScriptObject& script, const int v) { Context = script.Context; Value = INT_TO_JSVAL(v); } jsval ScriptValueInteger::GetJsval(const std::vector& UNUSED(vars)) { return Value; } ScriptValueInteger::~ScriptValueInteger() { } /************/ ScriptValueVariable::ScriptValueVariable(ScriptObject& script, const unsigned char id) { Context = script.Context; ID = id; GCVal = NULL; } jsval ScriptValueVariable::GetJsval(const std::vector& vars) { // Clean up from earlier invocations if (GCVal) { JS_RemoveRoot(Context, &GCVal); GCVal = NULL; } switch (vars[ID]->Type) { case vartype_int: { int val = ((BufferVariable_int*)vars[ID])->value; return INT_TO_JSVAL(val); } case vartype_double: { jsdouble* val = JS_NewDouble(Context, ((BufferVariable_double*)vars[ID])->value); if (!val) { debug_warn("Error creating JS double"); return JSVAL_NULL; } GCVal = (void*)val; JS_AddRoot(Context, &GCVal); return DOUBLE_TO_JSVAL(val); } case vartype_string: { JSString* val = StringConvert::wchars_to_jsstring(Context, ((BufferVariable_string*)vars[ID])->value.str()); if (!val) { debug_warn("Error creating JS string"); return JSVAL_NULL; } GCVal = (void*)val; JS_AddRoot(Context, &GCVal); return STRING_TO_JSVAL(val); } case vartype_rawstring: { JSString* val = StringConvert::wchars_to_jsstring(Context, ((BufferVariable_rawstring*)vars[ID])->value.str()); if (!val) { debug_warn("Error creating JS string"); return JSVAL_NULL; } GCVal = (void*)val; JS_AddRoot(Context, &GCVal); return STRING_TO_JSVAL(val); } default: debug_warn("Invalid type"); return JSVAL_NULL; } } ScriptValueVariable::~ScriptValueVariable() { if (GCVal) JS_RemoveRoot(Context, &GCVal); }