|  | /* | 
|  | * Copyright 2007 Google Inc. | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); you may not | 
|  | * use this file except in compliance with the License. You may obtain a copy of | 
|  | * the License at | 
|  | * | 
|  | * http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | 
|  | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | 
|  | * License for the specific language governing permissions and limitations under | 
|  | * the License. | 
|  | */ | 
|  | #ifndef JNI_LINUX_JSROOTEDVALUE_H_ | 
|  | #define JNI_LINUX_JSROOTEDVALUE_H_ | 
|  |  | 
|  | // Mozilla header files | 
|  | #include "mozilla-headers.h" | 
|  |  | 
|  | #include "Tracer.h" | 
|  | #include <stack> | 
|  |  | 
|  | extern "C" JSClass gwt_nativewrapper_class; | 
|  |  | 
|  | /* | 
|  | * Holds a root for Javascript objects, so the JS interpreter knows not to | 
|  | * garbage-collect the underlying object as long as this object exists. | 
|  | * Java code will pass a pointer to this object around (as an int/long) for | 
|  | * referring to the underlying Javascript object. | 
|  | * | 
|  | * There are also convenience routines for manipulating the underlying value. | 
|  | * Note that all get* methods assume the type is correct, so the corresponding | 
|  | * is* method should be called first if you aren't sure of the type. | 
|  | * | 
|  | * See http://developer.mozilla.org/en/docs/JS_AddRoot for details. | 
|  | * | 
|  | * TODO(jat): rewrite this to minimize the number of roots held and to | 
|  | *    improve 64-bit compatibility. | 
|  | */ | 
|  | class JsRootedValue | 
|  | { | 
|  | private: | 
|  | // the JavaScript String class | 
|  | static JSClass* stringClass; | 
|  |  | 
|  | // Javascript runtime | 
|  | static JSRuntime* runtime; | 
|  |  | 
|  | // stack of Javascript contexts | 
|  | static std::stack<JSContext*> contextStack; | 
|  |  | 
|  | // underlying Javascript value | 
|  | jsval         value_; | 
|  |  | 
|  | protected: | 
|  | /* | 
|  | * Fetch the JavaScript String class. | 
|  | * Not inlined to minimize code bloat since it should only be called once. | 
|  | */ | 
|  | void fetchStringClass() const; | 
|  |  | 
|  | /* | 
|  | * Make sure we have the JS code to identify String objects installed. | 
|  | */ | 
|  | void ensureStringClass() const { | 
|  | if(stringClass) return; | 
|  | fetchStringClass(); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Helper for the various constructors | 
|  | */ | 
|  | void constructorHelper(const char* ctorDesc) { | 
|  | Tracer tracer(ctorDesc, this); | 
|  | if (!JS_AddNamedRootRT(runtime, &value_, ctorDesc)) { | 
|  | tracer.log("JS_AddNamedRootRT failed"); | 
|  | // TODO(jat): handle errors | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Push a new JavaScript execution context. | 
|  | */ | 
|  | static void pushContext(JSContext* context) { | 
|  | Tracer tracer("JsRootedValue::pushContext"); | 
|  | tracer.log("pushed context=%08x", unsigned(context)); | 
|  | contextStack.push(context); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Pop a JavaScript execution context from the context stack. | 
|  | */ | 
|  | static void popContext() { | 
|  | Tracer tracer("JsRootedValue::popContext"); | 
|  | JSContext* context = currentContext(); | 
|  | contextStack.pop(); | 
|  | tracer.log("popped context=%08x", unsigned(context)); | 
|  | } | 
|  |  | 
|  | public: | 
|  | /* | 
|  | * This is a helper class used to push/pop JSContext values automatically, | 
|  | * using RAII.  Simply create a ContextManager object on the stack and it | 
|  | * will push the context at creation time and pop it at destruction time, | 
|  | * so you don't have to worry about covering all the exit paths from a | 
|  | * function. | 
|  | */ | 
|  | class ContextManager { | 
|  | public: | 
|  | explicit ContextManager(JSContext* context) { | 
|  | JsRootedValue::pushContext(context); | 
|  | } | 
|  | ~ContextManager() { | 
|  | JsRootedValue::popContext(); | 
|  | } | 
|  | }; | 
|  |  | 
|  | /* | 
|  | * This is a helper class to manage short-lived roots on the stack, using | 
|  | * RAII to free the roots when no longer needed. | 
|  | */ | 
|  | class Temp { | 
|  | private: | 
|  | void* ptr_; | 
|  | public: | 
|  | explicit Temp(void* ptr) : ptr_(ptr) { | 
|  | JS_AddNamedRootRT(runtime, ptr_, "temporary root"); | 
|  | } | 
|  | ~Temp() { | 
|  | JS_RemoveRootRT(runtime, ptr_); | 
|  | } | 
|  | }; | 
|  |  | 
|  | /* | 
|  | * Copy constructor - make another rooted value that refers to the same | 
|  | * JavaScript object (or has the same value if a primitive) | 
|  | */ | 
|  | JsRootedValue(const JsRootedValue& rooted_value) : value_(rooted_value.value_) | 
|  | { | 
|  | constructorHelper("JsRootedValue copy ctor"); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Create a value with a given jsval value | 
|  | */ | 
|  | JsRootedValue(jsval value) : value_(value) | 
|  | { | 
|  | constructorHelper("JsRootedValue jsval ctor"); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Create a void value | 
|  | */ | 
|  | JsRootedValue() : value_(JSVAL_VOID) { | 
|  | constructorHelper("JsRootedValue void ctor"); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Destroy this object. | 
|  | */ | 
|  | ~JsRootedValue() { | 
|  | Tracer tracer("~JsRootedValue", this); | 
|  | // ignore error since currently it is not possible to fail | 
|  | JS_RemoveRootRT(runtime, &value_); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Save a pointer to the JSRuntime if we don't have it yet | 
|  | */ | 
|  | static void ensureRuntime(JSContext* context) { | 
|  | if(!runtime) runtime = JS_GetRuntime(context); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Return the current JavaScript execution context. | 
|  | */ | 
|  | static JSContext* currentContext() { | 
|  | Tracer tracer("JsRootedValue::currentContext"); | 
|  | if (contextStack.empty()) { | 
|  | // TODO(jat): better error handling? | 
|  | fprintf(stderr, "JsRootedValue::currentContext - context stack empty\n"); | 
|  | ::abort(); | 
|  | } | 
|  | JSContext* context = contextStack.top(); | 
|  | tracer.log("context=%08x", unsigned(context)); | 
|  | return context; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Return the underlying JS object | 
|  | */ | 
|  | jsval getValue() const { return value_; } | 
|  |  | 
|  | /* | 
|  | * Sets the value of the underlying JS object. | 
|  | * | 
|  | * Returns false if an error occurred. | 
|  | */ | 
|  | bool setValue(jsval new_value) { | 
|  | value_ = new_value; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Returns true if the underlying value is of some number type. | 
|  | */ | 
|  | bool isNumber() const { | 
|  | return JSVAL_IS_NUMBER(value_); | 
|  | } | 
|  | /* | 
|  | * Returns the underlying value as a double. | 
|  | * Result is 0.0 if the underlying value is not a number | 
|  | * type. | 
|  | */ | 
|  | double getDouble() const { | 
|  | jsdouble return_value=0.0; | 
|  | // ignore return value -- if it fails, value will remain 0.0 | 
|  | JS_ValueToNumber(currentContext(), value_, &return_value); | 
|  | return double(return_value); | 
|  | } | 
|  | /* | 
|  | * Set the underlying value to a double value. | 
|  | * | 
|  | * Returns false on failure. | 
|  | */ | 
|  | bool setDouble(double val) { | 
|  | jsval js_double; | 
|  | if(!JS_NewDoubleValue(currentContext(), jsdouble(val), &js_double)) { | 
|  | return false; | 
|  | } | 
|  | return setValue(js_double); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Returns the underlying value as an integer value.  Note that the result | 
|  | * is undefined if isInt() does not return true. | 
|  | */ | 
|  | int getInt() { | 
|  | return JSVAL_TO_INT(value_); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Set the underlying value to an integer value. | 
|  | * | 
|  | * Returns false on failure. | 
|  | */ | 
|  | bool setInt(int val) { | 
|  | // check if it fits in 31 bits (ie, top two bits are equal). | 
|  | // if not, store it as a double | 
|  | if ((val & 0x80000000) != ((val << 1) & 0x80000000)) { | 
|  | return setDouble(val); | 
|  | } else { | 
|  | return setValue(INT_TO_JSVAL(val)); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Returns true if the underlying value is a boolean. | 
|  | */ | 
|  | bool isBoolean() const { | 
|  | return JSVAL_IS_BOOLEAN(value_); | 
|  | } | 
|  | /* | 
|  | * Returns the underlying value as a boolean. | 
|  | * Result is undefined if the value is not actually | 
|  | * a boolean. | 
|  | */ | 
|  | bool getBoolean() const { | 
|  | return value_ != JSVAL_FALSE; | 
|  | } | 
|  | /* | 
|  | * Set the underlying value to a boolean value. | 
|  | * | 
|  | * Returns false on failure (impossible?). | 
|  | */ | 
|  | bool setBoolean(bool val) { | 
|  | return setValue(val ? JSVAL_TRUE : JSVAL_FALSE); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Returns true if the underlying value is a string. | 
|  | */ | 
|  | bool isInt() const { | 
|  | return JSVAL_IS_INT(value_); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Returns true if the underlying value is a string. | 
|  | */ | 
|  | bool isString() const { | 
|  | return JSVAL_IS_STRING(value_); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Check if the value is a JavaScript String object. | 
|  | */ | 
|  | bool isJavaScriptStringObject() const { | 
|  | if (!isObject()) return false; | 
|  | ensureStringClass(); | 
|  | return getObjectClass() == stringClass; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Return this value as a string, converting as necessary. | 
|  | */ | 
|  | JSString* asString() const { | 
|  | return JS_ValueToString(currentContext(), value_); | 
|  | } | 
|  |  | 
|  | /* Returns the string as a JSString pointer. | 
|  | * Result is undefined if the value is not actually a string or String object. | 
|  | */ | 
|  | const JSString* getString() const { | 
|  | if (JSVAL_IS_STRING(value_)) { | 
|  | return JSVAL_TO_STRING(value_); | 
|  | } | 
|  | return asString(); | 
|  | } | 
|  | /* | 
|  | * Returns the string as a zero-terminated array of UTF16 characters. | 
|  | * Note that this pointer may become invalid when JS performs GC, so it | 
|  | * may only be used without calling other JS functions.  Result is | 
|  | * undefined if the value is not actually a string. | 
|  | */ | 
|  | const wchar_t* getStringChars() const { | 
|  | return reinterpret_cast<const wchar_t*>(JS_GetStringChars( | 
|  | const_cast<JSString*>(getString()))); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Returns the length of the underlying string.  Result is undefined | 
|  | * if the value is not actually a string. | 
|  | */ | 
|  | int getStringLength() const { | 
|  | return JS_GetStringLength(const_cast<JSString*>(getString())); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Sets the underlying value, defined by a null-terminated array of UTF16 | 
|  | * chars. | 
|  | * | 
|  | * Returns false on failure. | 
|  | */ | 
|  | bool setString(const wchar_t* utf16) { | 
|  | JSString* str = JS_NewUCStringCopyZ(currentContext(), | 
|  | reinterpret_cast<const jschar*>(utf16)); | 
|  | return setValue(STRING_TO_JSVAL(str)); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Sets the underlying value, defined by a counted array of UTF16 chars. | 
|  | * | 
|  | * Returns false on failure. | 
|  | */ | 
|  | bool setString(const wchar_t* utf16, size_t len) { | 
|  | JSString* str = JS_NewUCStringCopyN(currentContext(), | 
|  | reinterpret_cast<const jschar*>(utf16), len); | 
|  | return setValue(STRING_TO_JSVAL(str)); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Returns true if the underlying value is an object. | 
|  | */ | 
|  | bool isObject() const { | 
|  | return JSVAL_IS_OBJECT(value_); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Returns the underlying value as an object. | 
|  | * Result is undefined if it is not actually an object. | 
|  | */ | 
|  | JSObject* getObject() const { | 
|  | return isObject() ? JSVAL_TO_OBJECT(value_) : 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Returns the class name of the underlying value. | 
|  | * | 
|  | * Result is undefined if it is not actually an object. | 
|  | */ | 
|  | const JSClass* getObjectClass() const { | 
|  | return isObject() ? JS_GET_CLASS(currentContext(), getObject()) : 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Sets the underlying value to be an object. | 
|  | * | 
|  | * Returns false on failure. | 
|  | */ | 
|  | bool setObject(JSObject* obj) { | 
|  | return setValue(OBJECT_TO_JSVAL(obj)); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Returns true if the underlying value is undefined (void). | 
|  | */ | 
|  | bool isUndefined() const { | 
|  | return JSVAL_IS_VOID(value_); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Sets the underlying value to be undefined (void). | 
|  | * | 
|  | * Returns false on failure (impossible?) | 
|  | */ | 
|  | bool setUndefined() { | 
|  | return setValue(JSVAL_VOID); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Returns true if the underlying value is null. | 
|  | */ | 
|  | bool isNull() const { | 
|  | return JSVAL_IS_NULL(value_); | 
|  | } | 
|  | /* | 
|  | * Sets the underlying value to be null. | 
|  | * | 
|  | * Returns false on failure (impossible?) | 
|  | */ | 
|  | bool setNull() { | 
|  | return setValue(JSVAL_NULL); | 
|  | } | 
|  | }; | 
|  |  | 
|  | #endif /*JNI_LINUX_JSROOTEDVALUE_H_*/ |