Sirikata
liboh/plugins/js/JSObjectScript.hpp
Go to the documentation of this file.
00001 /*  Sirikata
00002  *  JSObjectScript.hpp
00003  *
00004  *  Copyright (c) 2010, Ewen Cheslack-Postava
00005  *  All rights reserved.
00006  *
00007  *  Redistribution and use in source and binary forms, with or without
00008  *  modification, are permitted provided that the following conditions are
00009  *  met:
00010  *  * Redistributions of source code must retain the above copyright
00011  *    notice, this list of conditions and the following disclaimer.
00012  *  * Redistributions in binary form must reproduce the above copyright
00013  *    notice, this list of conditions and the following disclaimer in
00014  *    the documentation and/or other materials provided with the
00015  *    distribution.
00016  *  * Neither the name of Sirikata nor the names of its contributors may
00017  *    be used to endorse or promote products derived from this software
00018  *    without specific prior written permission.
00019  *
00020  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
00021  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
00022  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
00023  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
00024  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00025  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00026  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00027  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00028  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00029  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00030  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00031  */
00032 
00033 #ifndef __SIRIKATA_JS_OBJECT_SCRIPT_HPP__
00034 #define __SIRIKATA_JS_OBJECT_SCRIPT_HPP__
00035 
00036 
00037 #include "Platform.hpp"
00038 #include <string>
00039 #include <sirikata/oh/ObjectScript.hpp>
00040 #include <sirikata/oh/ObjectScriptManager.hpp>
00041 #include <sirikata/oh/HostedObject.hpp>
00042 #include <sirikata/proxyobject/SessionEventListener.hpp>
00043 
00044 #include <boost/filesystem.hpp>
00045 
00046 #include <v8.h>
00047 
00048 
00049 #include <sirikata/proxyobject/ProxyCreationListener.hpp>
00050 #include "JSObjects/JSInvokableObject.hpp"
00051 #include "JSEntityCreateInfo.hpp"
00052 #include <sirikata/oh/Storage.hpp>
00053 #include <sirikata/oh/PersistedObjectSet.hpp>
00054 #include "JSObjectStructs/JSPresenceStruct.hpp"
00055 #include "JSObjectStructs/JSContextStruct.hpp"
00056 #include "JSObjectScriptManager.hpp"
00057 #include "EmersonHttpManager.hpp"
00058 #include <sirikata/core/util/Liveness.hpp>
00059 #include <stack>
00060 #include <sirikata/core/network/IOStrandImpl.hpp>
00061 #include "JSCtx.hpp"
00062 #include <sirikata/core/util/SerializationCheck.hpp>
00063 
00064 
00065 namespace Sirikata {
00066 namespace JS {
00067 
00068 
00069 
00070 void printException(v8::TryCatch& try_catch);
00071 
00072 // NOTE: virtual on Liveness because EmersonMessagingManager also uses it
00073 class SIRIKATA_SCRIPTING_JS_EXPORT JSObjectScript : public ObjectScript, public virtual Liveness
00074 {
00075 public:
00076     JSObjectScript(JSObjectScriptManager* jMan, OH::Storage* storage,
00077         OH::PersistedObjectSet* persisted_set, const UUID& internal_id,
00078         JSCtx* ctx);
00079 
00080 
00081     virtual ~JSObjectScript();
00082 
00083     v8::Handle<v8::Value> debug_fileWrite(String& strToWrite,String& filename);
00084     v8::Handle<v8::Value> debug_fileRead(String& filename);
00085 
00086 
00087     v8::Handle<v8::Value> executeInSandbox(JSContextStruct* jscont, v8::Handle<v8::Function> funcToCall,int argc, v8::Handle<v8::Value>* argv);
00088 
00089 
00090     //this function returns a context with
00091     v8::Local<v8::Object> createContext(JSPresenceStruct* jspres,const SpaceObjectReference& canSendTo,uint32 capNum, JSContextStruct*& internalContextField, JSContextStruct* creator);
00092 
00093 
00094     void initialize(const String& args, const String& script,int32 maxResThresh);
00095 
00096     // Sirikata::Service Interface
00097     virtual void start();
00098     virtual void stop();
00099 
00101     void print(const String& str);
00102 
00103 
00109     v8::Handle<v8::Value> import(const String& filename, bool isJS);
00110 
00115     void shimImportAndEvalScript(JSContextStruct* jscont, const String& toEval);
00116 
00120     v8::Handle<v8::Value> require(const String& filename,bool isJS);
00121 
00122     //JSContextStructs request the JSObjectScript to call finishClear on them
00123     //when doing so won't invalidate any iterators on the JSObjectScript.
00124     virtual void registerContextForClear(JSContextStruct* jscont);
00125 
00126 
00127     //all need to have jscontext structs so that can put context struct on
00128     //evaluation stack when call.
00129     v8::Handle<v8::Value> storageBeginTransaction(JSContextStruct* jscont);
00130     v8::Handle<v8::Value> storageCommit(JSContextStruct* jscont, v8::Handle<v8::Function> cb);
00131     v8::Handle<v8::Value> storageWrite(const OH::Storage::Key& key, const String& toWrite, v8::Handle<v8::Function> cb, JSContextStruct* jscont);
00132     v8::Handle<v8::Value> storageRead(const OH::Storage::Key& key, v8::Handle<v8::Function> cb, JSContextStruct* jscont);
00133     v8::Handle<v8::Value> storageErase(const OH::Storage::Key& key, v8::Handle<v8::Function> cb, JSContextStruct* jscont);
00134     v8::Handle<v8::Value> storageRangeRead(const OH::Storage::Key& start, const OH::Storage::Key& finish, v8::Handle<v8::Function> cb, JSContextStruct* jscont);
00135     v8::Handle<v8::Value> storageRangeErase(const OH::Storage::Key& start, const OH::Storage::Key& finish, v8::Handle<v8::Function> cb, JSContextStruct* jscont);
00136     v8::Handle<v8::Value> storageCount(const OH::Storage::Key& start, const OH::Storage::Key& finish, v8::Handle<v8::Function> cb, JSContextStruct* jscont);
00137 
00138     v8::Handle<v8::Value> setRestoreScript(JSContextStruct* jscont, const String& script, v8::Handle<v8::Function> cb);
00139 
00140     //creates a new EvalContext with newDir as scripting directory.  Puts that
00141     //context on mEvalContextStack.
00142     v8::Handle<v8::Value> pushEvalContextScopeDirectory(const String& newDir);
00143     v8::Handle<v8::Value> popEvalContextScopeDirectory();
00144 
00150     bool checkCurCtxtHasCapability(JSPresenceStruct* jspres, Capabilities::Caps whatCap);
00151 
00168     v8::Handle<v8::Value> checkResources();
00169     bool checkResourcesCPP();
00170 
00171     Handle<v8::Context> context() { return mContext->mContext;}
00172 
00173     bool isRootContext(JSContextStruct* jscont);
00174 
00175     JSObjectScriptManager* manager() const { return mManager; }
00176 
00177 
00178 
00179 
00180 
00181     //lkjs; note: will need to grab most recent context from stack.
00182     v8::Local<v8::Function> functionValue(const String& em_script_str);
00183 
00184 
00185     // A generic interface for invoking callback methods, used by other classes
00186     // that have JSObjectScript* (e.g. Invokable). Probably needs a version for
00187     // contexts if the function was bound within a context
00188     v8::Handle<v8::Value> invokeCallback(JSContextStruct* ctx, v8::Handle<v8::Object>* target, v8::Handle<v8::Function>& cb, int argc, v8::Handle<v8::Value> argv[]);
00189     v8::Handle<v8::Value> invokeCallback(JSContextStruct* ctx, v8::Handle<v8::Function>& cb, int argc, v8::Handle<v8::Value> argv[]);
00190     v8::Handle<v8::Value> invokeCallback(JSContextStruct* ctx, v8::Handle<v8::Function>& cb);
00191 
00192 
00193 
00194     // Hook to invoke after a callback is invoked. Allows you to check for
00195     // conditions that may be set during the callback (kill requested, reset,
00196     // etc).
00197     virtual void postCallbackChecks() {}
00198 
00199     JSContextStruct* rootContext() const { return mContext; }
00200 
00204     bool isStopped();
00205 
00206 
00211     v8::Handle<v8::Value> emersonCompileString(const String& toCompile);
00212 
00213 
00215     v8::Handle<v8::Value> evalInGlobal(const String& contents, v8::ScriptOrigin* em_script_name,JSContextStruct* jscs);
00216 
00217 
00218     JSCtx* mCtx;
00219 
00220 protected:
00221 
00222     // Object host internal identifier for the object associated with
00223     // this script. We copy this information here because this base
00224     // class is used for emheadless, which can't get the identifier
00225     // from the HostedObjectPtr.
00226     UUID mInternalID;
00227 
00231     int32 mResourceCounter;
00239     int32 mNestedEvalCounter;
00240 
00248     void preEvalOps();
00249 
00254     void postEvalOps();
00255 
00256 
00257 
00258     // Each context has an id that is assigned from this variable.
00259     uint32 contIDTracker;
00260     std::map<uint32,JSContextStruct*> mContStructMap;
00261 
00262 
00263     // EvalContext tracks the current state w.r.t. eval-related statements which
00264     // may change in response to user actions (changing directory) or due to the
00265     // way the system defines actions (e.g. import searches the current script's
00266     // directory before trying other import paths).
00267     struct EvalContext {
00268         EvalContext(JSContextStruct* jsctx);
00269         EvalContext(const EvalContext& rhs);
00270         EvalContext(const EvalContext& rhs, JSContextStruct* jsctx);
00271 
00272         // Current directory the script being evaluated was in,
00273         // e.g. ../../liboh/plugins/js/scripts/std/movement
00274         boost::filesystem::path currentScriptDir;
00275         // Current base import-path directory the script was found in,
00276         // e.g. for the above it might look like
00277         // ../../liboh/plugins/js/scripts/.
00278         // This is used to provide nice relative paths in exceptions.
00279         boost::filesystem::path currentScriptBaseDir;
00280 
00281         // Gets the full, but relative, path for the script. In the
00282         // above example this would be std/movement because the
00283         // currentScriptBaseDir is stripped off to leave just the
00284         // relative part.
00285         boost::filesystem::path getFullRelativeScriptDir() const;
00286 
00287         std::ostream* currentOutputStream;
00288 
00289         //the associated jscontextstruct containing the evaluation context.
00290         JSContextStruct* jscont;
00291     };
00292     // This is a helper which adds an EvalContext to the stack and ensures that
00293     // when it goes out of scope it is removed. This will almost always be the
00294     // right way to add and remove an EvalContext from the stack, ensure
00295     // multiple exit paths from a method don't cause the stack to become
00296     // incorrect.
00297     struct ScopedEvalContext {
00298         ScopedEvalContext(JSObjectScript* _parent, const EvalContext& _ctx);
00299         ~ScopedEvalContext();
00300 
00301         JSObjectScript* parent;
00302     };
00303     friend class ScopedEvalContext;
00304 
00305     std::stack<EvalContext> mEvalContextStack;
00306 
00307 
00308 
00309     //indexed by which context/sandbox you're in.
00310     typedef     std::map<uint32,std::set<String>  > ImportedFileMap;
00311     typedef ImportedFileMap::iterator ImportedFileMapIter;
00312     ImportedFileMap mImportedFiles;
00313 
00314 
00315     // Resolve a relative path for import to an absolute
00316     // path. "Returns" the full path of the file as well as the import
00317     // base path.
00318     bool resolveImport(const String& filename, bool isJS, boost::filesystem::path* full_file_out, boost::filesystem::path* base_path_out);
00319     bool resolveImport(const String& filename, boost::filesystem::path* full_file_out, boost::filesystem::path* base_path_out);
00320     // Perform an import on the absolute path filename. This performs no
00321     // resolution and *always* performs the import, even if the file has already
00322     // been imported.
00323     v8::Handle<v8::Value> absoluteImport(const boost::filesystem::path& full_filename, const boost::filesystem::path& full_base_dir,bool isJS);
00324 
00325 
00326 
00327     JSContextStruct* mContext;
00328 
00329 
00330 
00331     void  printStackFrame(std::stringstream&, v8::Local<v8::StackFrame>);
00332 
00333     typedef std::map<uint32, SuspendableVec> ContIDToSuspMap;
00334     ContIDToSuspMap toFixup;
00335 
00336     JSObjectScriptManager* mManager;
00337     OH::Storage* mStorage;
00338     OH::PersistedObjectSet* mPersistedObjectSet;
00339 
00340     void storageCommitCallback(
00341         JSContextStruct* jscont, v8::Persistent<v8::Function> cb,
00342         OH::Storage::Result result, OH::Storage::ReadSet* rs,Liveness::Token objAlive,
00343         Liveness::Token ctxAlive);
00344 
00345     void storageCountCallback(JSContextStruct* jscont, v8::Persistent<v8::Function> cb,
00346         OH::Storage::Result result, int32 count,Liveness::Token objAlive,Liveness::Token ctxAlive);
00347 
00348     void iSetRestoreScriptCallback(
00349         JSContextStruct* jscont, v8::Persistent<v8::Function> cb, bool success,
00350         Liveness::Token,Liveness::Token ctxAlive);
00351 
00356     int32 maxResourceThresh;
00357 
00361     bool stopCalled;
00362 
00363     void iStop(Liveness::Token alive, bool letDie);
00364 
00365   private:
00366 
00367     //should already be inside of a frame;
00368     v8::Handle<v8::Value> compileFunctionInContext( v8::Handle<v8::Function>&cb);
00369 
00370     // Print an exception "to" the script, i.e. using its system.print
00371     // method. This is useful for callbacks which are executed directly from C++
00372     // code but which should report errors to the user.
00373     void printExceptionToScript(const String& exc);
00374 
00375     v8::Handle<v8::Value> protectedEval(const String& em_script_str, v8::ScriptOrigin* em_script_name, const EvalContext& new_ctx, bool return_exc = false, const String& cache_path = "", bool isJS=false);
00376 
00377 
00378     // is_emerson controls whether this is compiled as emerson or
00379     // javascript code.
00380     // \param return_exc if true, return the exception generated (or
00381     //         empty handle for none) instead of the return value. The
00382     //         return value is discarded. This prevents throwing an
00383     //         exception (or rethrowing a caught exception), which is
00384     //         necessary if there is no JS caller higher on the
00385     //         stack. Otherwise, V8 gets stuck with an uncaught
00386     //         exception and fails on future V8 calls.
00387     // \param cache_path if non-empty, cache compiled results to the given file
00388     v8::Handle<v8::Value> internalEval( const String& em_script_str, v8::ScriptOrigin* em_script_name, bool is_emerson, bool return_exc = false, const String& cache_path = "");
00389 
00390 
00391     //Takes the context from the top value of context stack and returns it.  If
00392     //context stack is empty, prints error, and returns context associated with mContext.
00393     v8::Handle<v8::Context> getCurrentV8Context();
00394 
00395 
00396 
00397 
00398     //all of these functions are used to post between strands.
00399     void eStorageBeginTransaction(JSContextStruct* jscont,
00400         Liveness::Token objAlive,Liveness::Token ctxAlive);
00401 
00402     void eStorageCommit(
00403         JSContextStruct* jscont, v8::Persistent<v8::Function> cb,
00404         Liveness::Token objAlive,Liveness::Token ctxAlive);
00405 
00406     void iStorageCommitCallback(
00407         JSContextStruct* jscont, v8::Persistent<v8::Function> cb,
00408         OH::Storage::Result result, OH::Storage::ReadSet* rs,Liveness::Token objAlive,
00409         Liveness::Token ctxAlive);
00410     void iStorageCountCallback(
00411         JSContextStruct* jscont, v8::Persistent<v8::Function> cb,
00412         OH::Storage::Result result, int32 count,Liveness::Token objAlive,
00413         Liveness::Token ctxAlive);
00414     void eStorageErase(
00415         const OH::Storage::Key& key, v8::Persistent<v8::Function> cb,
00416         JSContextStruct* jscont,Liveness::Token objAlive,
00417         Liveness::Token ctxAlive);
00418     void eStorageWrite(
00419         const OH::Storage::Key& key, const String& toWrite,
00420         v8::Persistent<v8::Function> cb, JSContextStruct* jscont,
00421         Liveness::Token objAlive,Liveness::Token ctxAlive);
00422     void eStorageRead(
00423         const OH::Storage::Key& key, v8::Persistent<v8::Function> cb,
00424         JSContextStruct* jscont,Liveness::Token objAlive,
00425         Liveness::Token ctxAlive);
00426 
00427     void eStorageRangeRead(
00428         const OH::Storage::Key& start, const OH::Storage::Key& finish,
00429         v8::Persistent<v8::Function> cb, JSContextStruct* jscont,
00430         Liveness::Token objAlive,Liveness::Token ctxAlive);
00431 
00432     void eStorageRangeErase(
00433         const OH::Storage::Key& start, const OH::Storage::Key& finish,
00434         v8::Persistent<v8::Function> cb, JSContextStruct* jscont,
00435         Liveness::Token objAlive,Liveness::Token ctxAlive);
00436 
00437     void eStorageCount(
00438         const OH::Storage::Key& start, const OH::Storage::Key& finish,
00439         v8::Persistent<v8::Function> cb, JSContextStruct* jscont,
00440         Liveness::Token objAlive,Liveness::Token ctxAlive);
00441 
00442     void eSetRestoreScript(
00443         JSContextStruct* jscont, const String& script,
00444         v8::Persistent<v8::Function> cb, Liveness::Token objAlive,
00445         Liveness::Token ctxAlive);
00446 
00447     void iDelContext(JSContextStruct* toDel,Liveness::Token ctxLT);
00448 
00449 };
00450 
00451 #define JSSCRIPT_SERIAL_CHECK()\
00452     Sirikata::SerializationCheck::Scoped sc (mCtx->serializationCheck());
00453 
00454 
00455 } // namespace JS
00456 } // namespace Sirikata
00457 
00458 #endif //_SIRIKATA_JS_OBJECT_SCRIPT_HPP_