| /* | |
| * 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_*/ |