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