Sirikata
|
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_