diff --git a/autobuild.xml b/autobuild.xml index 4ed4129394..7e7e3d8790 100755 --- a/autobuild.xml +++ b/autobuild.xml @@ -254,9 +254,9 @@ archive hash - 98be22c8833aa2bca184b9fa09fbb82b + b32acce28e8f5dc404ff6d36c140ea65 url - http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/boost-1.45.0-windows-20110124.tar.bz2 + http://dl.dropbox.com/u/43361170/boost-1.45.0-windows-20120303.tar.bz2 name windows diff --git a/indra/llui/llkeywords.cpp b/indra/llui/llkeywords.cpp index c1cd04186b..cfd6125302 100644 --- a/indra/llui/llkeywords.cpp +++ b/indra/llui/llkeywords.cpp @@ -539,10 +539,12 @@ void LLKeywords::findSegments(std::vector* seg_list, const LLW // check against words llwchar prev = cur > base ? *(cur-1) : 0; - if( !isalnum( prev ) && (prev != '_') ) + // NaCl - LSL Preprocessor + if( !isalnum( prev ) && (prev != '_') && (prev != '#')) { const llwchar* p = cur; - while( isalnum( *p ) || (*p == '_') ) + while( isalnum( *p ) || (*p == '_') || (*p == '#') ) + // NaCl End { p++; } diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 9e46ee5f49..366785343f 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -2598,6 +2598,16 @@ void LLTextEditor::loadKeywords(const std::string& filename, } } +// NaCL - LSL Preprocessor +void LLTextEditor::addToken(LLKeywordToken::TOKEN_TYPE type, + const std::string& key, + const LLColor3& color, + const std::string& tool_tip) +{ + mKeywords.addToken(type,key,color,tool_tip); +} +// NaCl End + void LLTextEditor::updateSegments() { if (mReflowIndex < S32_MAX && mKeywords.isLoaded() && mParseOnTheFly) diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index 6f2fdf2388..3e380b25c9 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -208,6 +208,12 @@ public: const std::vector& funcs, const std::vector& tooltips, const LLColor3& func_color); + // NaCl - LSL Preprocessor + void addToken(LLKeywordToken::TOKEN_TYPE type, + const std::string& key, + const LLColor3& color, + const std::string& tool_tip = LLStringUtil::null); + // NaCl End LLKeywords::keyword_iterator_t keywordsBegin() { return mKeywords.begin(); } LLKeywords::keyword_iterator_t keywordsEnd() { return mKeywords.end(); } diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index a75ff8a8c5..1b883cba70 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -97,6 +97,7 @@ set(viewer_SOURCE_FILES fskeywords.cpp fslslbridge.cpp fslslbridgerequest.cpp + fslslpreproc.cpp fsfloaterprofile.cpp fspanelprofile.cpp fsradarlistctrl.cpp @@ -707,6 +708,7 @@ set(viewer_HEADER_FILES fskeywords.h fslslbridge.h fslslbridgerequest.h + fslslpreproc.h fsfloaterprofile.h fspanelprofile.h fsradarlistctrl.h diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index b8d9e0d784..f91f7db20c 100755 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -16732,6 +16732,83 @@ Change of this parameter will affect the layout of buttons in notification toast Value 4 + _NACL_LSLPreprocessor + + Comment + LSL Preprocessor + Persist + 1 + Type + Boolean + Value + 0 + + _NACL_PreProcLSLOptimizer + + Comment + LSL Optimizer + Persist + 1 + Type + Boolean + Value + 1 + + _NACL_PreProcLSLTextCompress + + Comment + LSL Text Compress + Persist + 1 + Type + Boolean + Value + 0 + + _NACL_PreProcLSLLazyLists + + Comment + LSL Lazy Lists + Persist + 1 + Type + Boolean + Value + 0 + + _NACL_PreProcLSLSwitch + + Comment + LSL Switch + Persist + 1 + Type + Boolean + Value + 0 + + _NACL_PreProcEnableHDDInclude + + Comment + orly + Persist + 1 + Type + Boolean + Value + 0 + + _NACL_PreProcHDDIncludeLocation + + Comment + toast + Persist + 1 + Type + String + Value + + FSMarkObjects Comment diff --git a/indra/newview/fslslpreproc.cpp b/indra/newview/fslslpreproc.cpp new file mode 100644 index 0000000000..8947315176 --- /dev/null +++ b/indra/newview/fslslpreproc.cpp @@ -0,0 +1,1193 @@ +/* Copyright (c) 2010 + * + * Modular Systems All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * 3. Neither the name Modular Systems nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY MODULAR SYSTEMS AND CONTRIBUTORS “AS IS” + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MODULAR SYSTEMS OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "llviewerprecompiledheaders.h" + +#include "fslslpreproc.h" +#include "llagent.h" +#include "llappviewer.h" +#include "llcurl.h" +#include "llscrolllistctrl.h" +#include "llviewertexteditor.h" +#include "llinventorymodel.h" +#include "llviewercontrol.h" +#include "llversionviewer.h" +#include +#include +#include +#include + +// NaCl - missing LSL Preprocessor includes +#include "llinventoryfunctions.h" +#include "llviewerinventory.h" +#include "llvfile.h" +#include "llvoavatarself.h" +// NaCl End + +class ScriptMatches : public LLInventoryCollectFunctor +{ +public: + ScriptMatches(std::string name) + { + sName = name; + } + virtual ~ScriptMatches() {} + virtual bool operator()(LLInventoryCategory* cat, + LLInventoryItem* item) + { + if(item) + { + //LLViewerInventoryCategory* folderp = gInventory.getCategory((item->getParentUUID()); + if(item->getName() == sName) + { + if(item->getType() == LLAssetType::AT_LSL_TEXT)return true; + } + //return (item->getName() == sName);// && cat->getName() == "#v"); + } + return false; + } +private: + std::string sName; +}; + +LLUUID FSLSLPreprocessor::findInventoryByName(std::string name) +{ + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + ScriptMatches namematches(name); + gInventory.collectDescendentsIf(gInventory.getRootFolderID(),cats,items,FALSE,namematches); + + if (items.count()) + { + LLInventoryModel::item_array_t::iterator it = items.begin(); + it = items.begin(); + LLViewerInventoryItem* foo=it->get(); + return foo->getUUID(); + } + return LLUUID::null; +} + + +////void cmdline_printchat(std::string message); + +std::map FSLSLPreprocessor::cached_assetids; + +#if !defined(LL_DARWIN) || defined(DARWINPREPROC) + +//apparently LL #defined this function which happens to precisely match +//a boost::wave function name, destroying the internet, silly grey furries +#undef equivalent + +// Work around stupid Microsoft STL warning +#ifdef LL_WINDOWS +#pragma warning (disable : 4702) // warning C4702: unreachable code +#endif + +#include +#include +#include // token class +#include // lexer class +#include +#include +#include + +using namespace boost::regex_constants; + + +#define FAILDEBUG llinfos << "line:" << __LINE__ << llendl; + + +#define encode_start std::string("//start_unprocessed_text\n/*") +#define encode_end std::string("*/\n//end_unprocessed_text") + +std::string FSLSLPreprocessor::encode(std::string script) +{ + + std::string otext = FSLSLPreprocessor::decode(script); + + BOOL mono = mono_directive(script); + + otext = boost::regex_replace(otext, boost::regex("([/*])(?=[/*|])",boost::regex::perl), "$1|"); + + //otext = curl_escape(otext.c_str(), otext.size()); + + otext = encode_start+otext+encode_end; + + otext += "\n//nfo_preprocessor_version 0"; + + //otext += "\n//^ = determine what featureset is supported"; + otext += llformat("\n//program_version %s", LLAppViewer::instance()->getWindowTitle().c_str()); + + otext += "\n"; + + if(mono)otext += "//mono\n"; + else otext += "//lsl2\n"; + + + return otext; +} + +std::string FSLSLPreprocessor::decode(std::string script) +{ + + static S32 startpoint = encode_start.length(); + + std::string tip = script.substr(0,startpoint); + + if(tip != encode_start) + { + + //cmdline_printchat("no start"); + //if(sp != -1)trigger warningg/error? + return script; + } + + S32 end = script.find(encode_end); + + if(end == -1) + { + + //cmdline_printchat("no end"); + return script; + } + + + std::string data = script.substr(startpoint,end-startpoint); + //cmdline_printchat("data="+data); + + + std::string otext = data; + + + + otext = boost::regex_replace(otext, boost::regex("([/*])\\|",boost::regex::perl), "$1"); + + + //otext = curl_unescape(otext.c_str(),otext.length()); + + return otext; +} + + +std::string scopeript2(std::string& top, S32 fstart, char left = '{', char right = '}') +{ + + if(fstart >= int(top.length()))return "begin out of bounds"; + + int cursor = fstart; + bool noscoped = true; + bool in_literal = false; + int count = 0; + char ltoken = ' '; + + do + { + char token = top.at(cursor); + if(token == '"' && ltoken != '\\')in_literal = !in_literal; + else if(token == '\\' && ltoken == '\\')token = ' '; + else if(!in_literal) + { + if(token == left) + { + count += 1; + noscoped = false; + }else if(token == right) + { + count -= 1; + noscoped = false; + } + } + ltoken = token; + cursor += 1; + }while((count > 0 || noscoped) && cursor < int(top.length())); + int end = (cursor-fstart); + if(end > int(top.length())) + { + return "end out of bounds"; + } + + return top.substr(fstart,(cursor-fstart)); +} + +inline int const_iterator_to_pos(std::string::const_iterator begin, std::string::const_iterator cursor) +{ + return std::distance(begin, cursor); +} + +void shredder(std::string& text) +{ + int cursor = 0; + if(int(text.length()) == 0) + { + text = "y u do dis?"; + return; + } + char ltoken = ' '; + do + { + char token = text[cursor]; + if (token == '"' && ltoken != '\\') + { + ltoken = token; + token = text[++cursor]; + while(cursor < int(text.length())) + { + if(token == '\\' && ltoken == '\\') token = ' '; + if(token == '"' && ltoken != '\\') + break; + ltoken = token; + ++cursor; + token = text[cursor]; + } + } + else if (token == '\\' && ltoken == '\\') + { + token = ' '; + } + + if(token != 0xA && token != 0x9 && ( + token < 0x20 || + token == '#' || + token == '$' || + token == '\\' || + token == '\'' || + token == '?' || + token >= 0x7F)) + { + text[cursor] = ' '; + } + ltoken = token; + ++cursor; + }while(cursor < int(text.length())); +} + +std::string FSLSLPreprocessor::lslopt(std::string script) +{ + + script = " \n"+script;//HACK//this should prevent regex fail for functions starting on line 0, column 0 + //added more to prevent split fail on scripts with no global data + //this should be fun + + //Removes invalid characters from the script. + shredder(script); + + try + { + boost::smatch result; + if (boost::regex_search(script, result, boost::regex("([\\S\\s]*?)(\\s*default\\s*\\{)([\\S\\s]*)"))) + { + + std::string top = result[1]; + std::string bottom = result[2]; + bottom += result[3]; + + boost::regex findfuncts("(integer|float|string|key|vector|rotation|list){0,1}[\\}\\s]+([a-zA-Z0-9_]+)\\("); + //there is a minor problem with this regex, it will + //grab extra wnhitespace/newlines in front of functions that do not return a value + //however this seems unimportant as it is only a couple chars and + //never grabs code that would actually break compilation + + boost::smatch TOPfmatch; + std::set kept_functions; + std::map functions; + + while(boost::regex_search(std::string::const_iterator(top.begin()), std::string::const_iterator(top.end()), TOPfmatch, findfuncts, boost::match_default)) + { + + //std::string type = TOPfmatch[1]; + std::string funcname = TOPfmatch[2]; + + int pos = TOPfmatch.position(boost::match_results::size_type(0)); + std::string funcb = scopeript2(top, pos); + functions[funcname] = funcb; + //cmdline_printchat("func "+funcname+" added to list["+funcb+"]"); + top.erase(pos,funcb.size()); + } + + bool repass = false; + do + { + + repass = false; + std::map::iterator func_it; + for(func_it = functions.begin(); func_it != functions.end(); func_it++) + { + + std::string funcname = func_it->first; + + if(kept_functions.find(funcname) == kept_functions.end()) + { + + boost::smatch calls; + //funcname has to be [a-zA-Z0-9_]+, so we know its safe + boost::regex findcalls(std::string("[^a-zA-Z0-9_]")+funcname+std::string("\\(")); + + std::string::const_iterator bstart = bottom.begin(); + std::string::const_iterator bend = bottom.end(); + + if(boost::regex_search(bstart, bend, calls, findcalls, boost::match_default)) + { + + std::string function = func_it->second; + kept_functions.insert(funcname); + bottom = function+"\n"+bottom; + repass = true; + } + } + } + }while(repass); + + std::map gvars; + boost::regex findvars("(integer|float|string|key|vector|rotation|list)\\s+([a-zA-Z0-9_]+)([^\\(\\);]*;)"); + boost::smatch TOPvmatch; + + while(boost::regex_search(std::string::const_iterator(top.begin()), std::string::const_iterator(top.end()), TOPvmatch, findvars, boost::match_default)) + { + + std::string varname = TOPvmatch[2]; + std::string fullref = TOPvmatch[1] + " " + varname+TOPvmatch[3]; + + gvars[varname] = fullref; + int start = const_iterator_to_pos(std::string::const_iterator(top.begin()), TOPvmatch[1].first); + top.erase(start,fullref.length()); + } + + std::map::iterator var_it; + for(var_it = gvars.begin(); var_it != gvars.end(); var_it++) + { + + std::string varname = var_it->first; + boost::regex findvcalls(std::string("[^a-zA-Z0-9_]+")+varname+std::string("[^a-zA-Z0-9_]+")); + boost::smatch vcalls; + std::string::const_iterator bstart = bottom.begin(); + std::string::const_iterator bend = bottom.end(); + + if(boost::regex_search(bstart, bend, vcalls, findvcalls, boost::match_default)) + { + bottom = var_it->second + "\n" + bottom; + } + } + + script = bottom; + } + } + catch (boost::regex_error& e) + { + std::string err = "not a valid regular expression: \""; + err += e.what(); + err += "\"; optimization skipped"; + ////cmdline_printchat(err); + } + catch (...) + { + + ////cmdline_printchat("unexpected exception caught; optimization skipped"); + } + return script; +} + +std::string FSLSLPreprocessor::lslcomp(std::string script) +{ + try + { + shredder(script); + script = boost::regex_replace(script, boost::regex("(\\s+)",boost::regex::perl), "\n"); + } + catch (boost::regex_error& e) + { + std::string err = "not a valid regular expression: \""; + err += e.what(); + err += "\"; compression skipped"; + ////cmdline_printchat(err); + } + catch (...) + { + + ////cmdline_printchat("unexpected exception caught; compression skipped"); + } + return script; +} + +struct ProcCacheInfo +{ + LLViewerInventoryItem* item; + FSLSLPreprocessor* self; +}; + +inline std::string shortfile(std::string in) +{ + return boost::filesystem::path(std::string(in)).filename(); +} + + +class trace_include_files : public boost::wave::context_policies::default_preprocessing_hooks +{ +public: + trace_include_files(FSLSLPreprocessor* proc) + : mProc(proc) + { + mAssetStack.push(LLUUID::null.asString()); + mFileStack.push(proc->mMainScriptName); + } + + + template + bool found_include_directive(ContextT const& ctx, + std::string const &filename, bool include_next) + { + std::string cfilename = filename.substr(1,filename.length()-2); + //cmdline_printchat(cfilename+":found_include_directive"); + LLUUID item_id = FSLSLPreprocessor::findInventoryByName(cfilename); + if(item_id.notNull()) + { + LLViewerInventoryItem* item = gInventory.getItem(item_id); + if(item) + { + std::map::iterator it = mProc->cached_assetids.find(cfilename); + bool not_cached = (it == mProc->cached_assetids.end()); + bool changed = true; + if(!not_cached) + { + changed = (mProc->cached_assetids[cfilename] != item->getAssetUUID()); + } + if (not_cached || changed) + { + std::set::iterator it = mProc->caching_files.find(cfilename); + if (it == mProc->caching_files.end()) + { + if(not_cached)mProc->display_error(std::string("Caching ")+cfilename); + else /*if(changed)*/mProc->display_error(cfilename+std::string(" has changed, recaching...")); + //one is always true + mProc->caching_files.insert(cfilename); + ProcCacheInfo* info = new ProcCacheInfo; + info->item = item; + info->self = mProc; + LLPermissions perm(((LLInventoryItem*)item)->getPermissions()); + gAssetStorage->getInvItemAsset(LLHost::invalid, + gAgent.getID(), + gAgent.getSessionID(), + perm.getOwner(), + LLUUID::null, + item->getUUID(), + LLUUID::null, + item->getType(), + &FSLSLPreprocessor::FSProcCacheCallback, + info, + TRUE); + return true; + } + } + } + }else + { + //todo check on HDD in user defined dir for file in question + } + //++include_depth; + return false; + } + + template + void opened_include_file(ContextT const& ctx, + std::string const &relname, std::string const& absname, + bool is_system_include) + { + + ContextT& usefulctx = const_cast(ctx); + std::string id; + std::string filename = shortfile(relname);//boost::filesystem::path(std::string(relname)).filename(); + std::map::iterator it = mProc->cached_assetids.find(filename); + if(it != mProc->cached_assetids.end()) + { + id = mProc->cached_assetids[filename].asString(); + }else id = "NOT_IN_WORLD";//I guess, still need to add external includes atm + mAssetStack.push(id); + std::string macro = "__ASSETID__"; + usefulctx.remove_macro_definition(macro, true); + std::string def = llformat("%s=\"%s\"",macro.c_str(),id.c_str()); + usefulctx.add_macro_definition(def,false); + + mFileStack.push(filename); + macro = "__SHORTFILE__"; + usefulctx.remove_macro_definition(macro, true); + def = llformat("%s=\"%s\"",macro.c_str(),filename.c_str()); + usefulctx.add_macro_definition(def,false); + } + + + template + void returning_from_include_file(ContextT const& ctx) + { + ContextT& usefulctx = const_cast(ctx); + if(mAssetStack.size() > 1) + { + mAssetStack.pop(); + std::string id = mAssetStack.top(); + std::string macro = "__ASSETID__"; + usefulctx.remove_macro_definition(macro, true); + std::string def = llformat("%s=\"%s\"",macro.c_str(),id.c_str()); + usefulctx.add_macro_definition(def,false); + + mFileStack.pop(); + std::string filename = mFileStack.top(); + macro = "__SHORTFILE__"; + usefulctx.remove_macro_definition(macro, true); + def = llformat("%s=\"%s\"",macro.c_str(),filename.c_str()); + usefulctx.add_macro_definition(def,false); + }//else wave did something really fucked up + } +private: + FSLSLPreprocessor* mProc; + std::stack mAssetStack; + std::stack mFileStack; +}; + + +std::string cachepath(std::string name) +{ + return gDirUtilp->getExpandedFilename(LL_PATH_CACHE,"lslpreproc",name); +} + +void cache_script(std::string name, std::string content) +{ + + content += "\n";/*hack!*/ + //cmdline_printchat("writing "+name+" to cache"); + std::string path = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,"lslpreproc",name); + LLAPRFile infile; + infile.open(path.c_str(), LL_APR_WB); + apr_file_t *fp = infile.getFileHandle(); + if(fp)infile.write(content.c_str(), content.length()); + infile.close(); +} + +void FSLSLPreprocessor::FSProcCacheCallback(LLVFS *vfs, const LLUUID& iuuid, LLAssetType::EType type, void *userdata, S32 result, LLExtStat extstat) +{ + + LLUUID uuid = iuuid; + //cmdline_printchat("cachecallback called"); + ProcCacheInfo* info =(ProcCacheInfo*)userdata; + LLViewerInventoryItem* item = info->item; + FSLSLPreprocessor* self = info->self; + if(item && self) + { + std::string name = item->getName(); + if(result == LL_ERR_NOERR) + { + LLVFile file(vfs, uuid, type); + S32 file_length = file.getSize(); + char* buffer = new char[file_length+1]; + file.read((U8*)buffer, file_length); + // put a EOS at the end + buffer[file_length] = 0; + + std::string content(buffer); + content = utf8str_removeCRLF(content); + content = self->decode(content); + /*content += llformat("\n#define __UP_ITEMID__ __ITEMID__\n#define __ITEMID__ %s\n",uuid.asString().c_str())+content; + content += "\n#define __ITEMID__ __UP_ITEMID__\n";*/ + //prolly wont work and ill have to be not lazy, but worth a try + delete buffer; + if(boost::filesystem::native(name)) + { + //cmdline_printchat("native name of "+name); + self->mCore->mErrorList->setCommentText(std::string("Cached ")+name); + cache_script(name, content); + std::set::iterator loc = self->caching_files.find(name); + if(loc != self->caching_files.end()) + { + //cmdline_printchat("finalizing cache"); + self->caching_files.erase(loc); + //self->cached_files.insert(name); + if(uuid.isNull())uuid.generate(); + item->setAssetUUID(uuid); + self->cached_assetids[name] = uuid;//.insert(uuid.asString()); + self->start_process(); + }else + { + ////cmdline_printchat("something fucked"); + } + }else self->mCore->mErrorList->setCommentText(std::string("Error: script named '")+name+"' isn't safe to copy to the filesystem. This include will fail."); + }else + { + self->mCore->mErrorList->setCommentText(std::string("Error caching "+name)); + } + } + + if(info) + { + delete info; + } +} + +void FSLSLPreprocessor::preprocess_script(BOOL close, BOOL defcache) +{ + mClose = close; + mDefinitionCaching = defcache; + caching_files.clear(); + //this->display_error("PreProc Starting..."); + mCore->mErrorList->setCommentText(std::string("PreProc Starting...")); + + LLFile::mkdir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,"")+gDirUtilp->getDirDelimiter()+"lslpreproc"); + std::string script = mCore->mEditor->getText(); + if(mMainScriptName == "")//more sanity + { + const LLInventoryItem* item = NULL; + LLPreview* preview = (LLPreview*)mCore->mUserdata; + if(preview) + { + item = preview->getItem(); + } + if(item) + { + mMainScriptName = item->getName(); + }else + { + mMainScriptName = "(Unknown)"; + } + } + std::string name = mMainScriptName; + cached_assetids[name] = LLUUID::null; + cache_script(name, script); + //start the party + start_process(); + +} + +const std::string lazy_list_set_func("\ +list lazy_list_set(list target, integer pos, list newval)\n\ +{\n\ + integer end = llGetListLength(target);\n\ + if(end > pos)\n\ + {\n\ + target = llListReplaceList(target,newval,pos,pos);\n\ + }else if(end == pos)\n\ + {\n\ + target += newval;\n\ + }else\n\ + {\n\ + do\n\ + {\n\ + target += [0];\n\ + end += 1;\n\ + }while(end < pos);\n\ + target += newval;\n\ + }\n\ + return target;\n\ +}\n\ +"); + +std::string reformat_lazy_lists(std::string script) +{ + BOOL add_set = FALSE; + std::string nscript = script; + nscript = boost::regex_replace(nscript, boost::regex("([a-zA-Z0-9_]+)\\[([a-zA-Z0-9_()\"]+)]\\s*=\\s*([a-zA-Z0-9_()\"\\+\\-\\*/]+)([;)])",boost::regex::perl), "$1=lazy_list_set($1,$2,[$3])$4"); + if(nscript != script) + { + add_set = TRUE; + } + + if(add_set == TRUE) + { + //add lazy_list_set function to top of script, as it is used + nscript = utf8str_removeCRLF(lazy_list_set_func) + "\n" + nscript; + } + return nscript; +} + + +inline std::string randstr(int len, std::string chars) +{ + int clen = int(chars.length()); + int built = 0; + std::string ret; + while(built < len) + { + int r = std::rand() / ( RAND_MAX / clen ); + r = r % clen;//sanity + ret += chars.at(r); + built += 1; + } + return ret; +} + +inline std::string quicklabel() +{ + return std::string("c")+randstr(5,"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); +} + +std::string minimalize_whitespace(std::string in) +{ + return boost::regex_replace(in, boost::regex("\\s*",boost::regex::perl), "\n"); +} + +std::string reformat_switch_statements(std::string script) +{ + std::string buffer = script; + { + try + { + boost::regex findswitches("\\sswitch\\(");//nasty + + boost::smatch matches; + + static std::string switchstr = "switch("; + + int escape = 100; + + while(boost::regex_search(std::string::const_iterator(buffer.begin()), std::string::const_iterator(buffer.end()), matches, findswitches, boost::match_default) && escape > 1) + { + int res = matches.position(boost::match_results::size_type(0))+1; + + static int slen = switchstr.length(); + + std::string arg = scopeript2(buffer, res+slen-1,'(',')'); + + //arg *will have* () around it + if(arg == "begin out of bounds" || arg == "end out of bounds") + { + ////cmdline_printchat(arg); + break; + } + //cmdline_printchat("arg=["+arg+"]"); + std::string rstate = scopeript2(buffer, res+slen+arg.length()-1); + + int cutlen = slen; + cutlen -= 1; + cutlen += arg.length(); + cutlen += rstate.length(); + //slen is for switch( and arg has () so we need to - 1 ( to get the right length + //then add arg len and state len to get section to excise + + //rip off the scope edges + int slicestart = rstate.find("{")+1; + rstate = rstate.substr(slicestart,(rstate.rfind("}")-slicestart)-1); + //cmdline_printchat("rstate=["+rstate+"]"); + + + + boost::regex findcases("\\scase\\s"); + + boost::smatch statematches; + + std::map ifs; + + while(boost::regex_search(std::string::const_iterator(rstate.begin()), std::string::const_iterator(rstate.end()), statematches, findcases, boost::match_default) && escape > 1) + { + //if(statematches[0].matched) + { + int case_start = statematches.position(boost::match_results::size_type(0))+1;//const_iterator2pos(statematches[0].first+1, std::string::const_iterator(rstate.begin()))-1; + int next_curl = rstate.find("{",case_start+1); + int next_semi = rstate.find(":",case_start+1); + int case_end = (next_curl < next_semi && next_curl != -1) ? next_curl : next_semi; + static int caselen = std::string("case").length(); + if(case_end != -1) + { + std::string casearg = rstate.substr(case_start+caselen,case_end-(case_start+caselen)); + //cmdline_printchat("casearg=["+casearg+"]"); + std::string label = quicklabel(); + ifs[casearg] = label; + //cmdline_printchat("BEFORE["+rstate+"]"); + bool addcurl = (case_end == next_curl ? 1 : 0); + label = "@"+label+";\n"; + if(addcurl)label += "{"; + rstate.erase(case_start,(case_end-case_start) + 1); + rstate.insert(case_start,label); + //cmdline_printchat("AFTER["+rstate+"]"); + }else + { + ////cmdline_printchat("error in regex case_end != -1"); + rstate.erase(case_start,caselen); + rstate.insert(case_start,"error; cannot find { or :"); + } + } + escape -= 1; + } + + std::string deflt = quicklabel(); + bool isdflt = false; + std::string defstate; + defstate = boost::regex_replace(rstate, boost::regex("(\\s)(default\\s*?):",boost::regex::perl), "$1\\@"+deflt+";"); + defstate = boost::regex_replace(defstate, boost::regex("(\\s)(default\\s*?)\\{",boost::regex::perl), "$1\\@"+deflt+"; \\{"); + if(defstate != rstate) + { + isdflt = true; + rstate = defstate; + } + std::string argl; + std::string jumptable = "{"; + /*std::string type = gettype(buffer, arg, res); + if(type != "void" && type != "-1") + { + std::string argl = quicklabel(); + jumptable += type+" "+argl+" = "+arg+";\n"; + arg = argl; + }else + { + cmdline_printchat("type="+type); + }*/ + + std::map::iterator ifs_it; + for(ifs_it = ifs.begin(); ifs_it != ifs.end(); ifs_it++) + { + jumptable += "if("+arg+" == ("+ifs_it->first+"))jump "+ifs_it->second+";\n"; + } + if(isdflt)jumptable += "jump "+deflt+";\n"; + + rstate = jumptable + rstate + "\n"; + + std::string brk = quicklabel(); + defstate = boost::regex_replace(rstate, boost::regex("(\\s)break\\s*;",boost::regex::perl), "$1jump "+brk+";"); + if(defstate != rstate) + { + rstate = defstate; + rstate += "\n@"+brk+";\n"; + } + rstate = rstate + "}"; + + //cmdline_printchat("replacing["+buffer.substr(res,cutlen)+"] with ["+rstate+"]"); + buffer.erase(res,cutlen); + buffer.insert(res,rstate); + + //start = buffer.begin(); + //end = buffer.end(); + + escape -= 1; + + //res = buffer.find(switchstr); + } + script = buffer; + } + catch (boost::regex_error& e) + { + std::string err = "not a valid regular expression: \""; + err += e.what(); + err += "\"; switch statements skipped"; + ////cmdline_printchat(err); + } + catch (...) + { + ////cmdline_printchat("unexpected exception caught; buffer=["+buffer+"]"); + } + } + return script; +} + +void FSLSLPreprocessor::start_process() +{ + if(waving) + { + ////cmdline_printchat("already waving?"); + return; + } + waving = TRUE; + boost::wave::util::file_position_type current_position; + std::string input = mCore->mEditor->getText(); + std::string rinput = input; + //Make sure wave does not complain about missing newline at end of script. + input += "\n"; + std::string output; + + std::string name = mMainScriptName; + BOOL lazy_lists = gSavedSettings.getBOOL("_NACL_PreProcLSLLazyLists"); + BOOL use_switch = gSavedSettings.getBOOL("_NACL_PreProcLSLSwitch"); + std::string settings; + settings = "Settings: preproc "; + if (lazy_lists == TRUE) + { + settings = settings + " Lazy Lists"; + } + if (use_switch == TRUE) + { + settings = settings + " switches"; + } + if(gSavedSettings.getBOOL("_NACL_PreProcLSLOptimizer") == TRUE) + { + settings = settings + " Optimize"; + } + if(gSavedSettings.getBOOL("_NACL_PreProcEnableHDDInclude") == TRUE) + { + settings = settings + " HDDInclude"; + } + if(gSavedSettings.getBOOL("_NACL_PreProcLSLTextCompress")== TRUE) + { + settings = settings + " Compress"; + } + //display the settings + mCore->mErrorList->setCommentText(std::string(settings)); + + ////cmdline_printchat(settings); + BOOL errored = FALSE; + std::string err; + try + { + trace_include_files tracer(this); + typedef boost::wave::cpplexer::lex_token<> token_type; + typedef boost::wave::cpplexer::lex_iterator lex_iterator_type; + typedef boost::wave::context + context_type; + + context_type ctx(input.begin(), input.end(), name.c_str(), tracer); + ctx.set_language(boost::wave::enable_long_long(ctx.get_language())); + ctx.set_language(boost::wave::enable_prefer_pp_numbers(ctx.get_language())); + ctx.set_language(boost::wave::enable_variadics(ctx.get_language())); + + std::string path = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,"")+gDirUtilp->getDirDelimiter()+"lslpreproc"+gDirUtilp->getDirDelimiter(); + ctx.add_include_path(path.c_str()); + if(gSavedSettings.getBOOL("_NACL_PreProcEnableHDDInclude")) + { + std::string hddpath = gSavedSettings.getString("_NACL_PreProcHDDIncludeLocation"); + if(hddpath != "") + { + ctx.add_include_path(hddpath.c_str()); + ctx.add_sysinclude_path(hddpath.c_str()); + } + } + std::string def = llformat("__AGENTKEY__=\"%s\"",gAgent.getID().asString().c_str());//legacy because I used it earlier + ctx.add_macro_definition(def,false); + def = llformat("__AGENTID__=\"%s\"",gAgent.getID().asString().c_str()); + ctx.add_macro_definition(def,false); + def = llformat("__AGENTIDRAW__=%s",gAgent.getID().asString().c_str()); + ctx.add_macro_definition(def,false); + std::string aname; + gAgentAvatarp->getFullname(); + def = llformat("__AGENTNAME__=\"%s\"",aname.c_str()); + ctx.add_macro_definition(def,false); + def = llformat("__ASSETID__=%s",LLUUID::null.asString().c_str()); + ctx.add_macro_definition(def,false); + def = llformat("__SHORTFILE__=\"%s\"",name.c_str()); + ctx.add_macro_definition(def,false); + + ctx.add_macro_definition("list(input)=((list)(input))",false); + ctx.add_macro_definition("float(input)=((float)(input))",false); + ctx.add_macro_definition("integer(input)=((integer)(input))",false); + ctx.add_macro_definition("key(input)=((key)(input))",false); + ctx.add_macro_definition("list(input)=((list)(input))",false); + ctx.add_macro_definition("rotation(input)=((rotation)(input))",false); + ctx.add_macro_definition("quaternion(input)=((quaternion)(input))",false); + ctx.add_macro_definition("string(input)=((string)(input))",false); + ctx.add_macro_definition("vector(input)=((vector)(input))",false); + + context_type::iterator_type first = ctx.begin(); + context_type::iterator_type last = ctx.end(); + + while (first != last) + { + if(caching_files.size() != 0) + { + waving = FALSE; + return; + } + current_position = (*first).get_position(); + + std::string token = std::string((*first).get_value().c_str());//stupid boost bitching even though we know its a std::string + + if(token == "#line") + { + token = "//#line"; + } + + output += token; + + if(lazy_lists == FALSE) + { + lazy_lists = ctx.is_defined_macro(std::string("USE_LAZY_LISTS")); + } + + if(use_switch == FALSE) + { + use_switch = ctx.is_defined_macro(std::string("USE_SWITCHES")); + } + ++first; + } + } + catch(boost::wave::cpp_exception const& e) + { + errored = TRUE; + // some preprocessing error + err = name + "(" + llformat("%d",e.line_no()) + "): " + e.description(); + ////cmdline_printchat(err); + mCore->mErrorList->setCommentText(err); + } + catch(std::exception const& e) + { + FAILDEBUG + errored = TRUE; + err = std::string(current_position.get_file().c_str()) + "(" + llformat("%d",current_position.get_line()) + "): "; + err += std::string("exception caught: ") + e.what(); + ////cmdline_printchat(err); + mCore->mErrorList->setCommentText(err); + } + catch (...) + { + FAILDEBUG + errored = TRUE; + err = std::string(current_position.get_file().c_str()) + llformat("%d",current_position.get_line()); + err += std::string("): unexpected exception caught."); + ////cmdline_printchat(err); + mCore->mErrorList->setCommentText(err); + } + + if(!errored) + { + FAILDEBUG + if(lazy_lists == TRUE) + { + try + { + mCore->mErrorList->setCommentText("Applying lazy list set transform"); + output = reformat_lazy_lists(output); + }catch(...) + { + errored = TRUE; + err = "unexpected exception in lazy list converter."; + mCore->mErrorList->setCommentText(err); + } + + } + if(use_switch == TRUE) + { + try + { + mCore->mErrorList->setCommentText("Applying switch statement transform"); + output = reformat_switch_statements(output); + }catch(...) + { + errored = TRUE; + err = "unexpected exception in switch statement converter."; + mCore->mErrorList->setCommentText(err); + } + } + } + + if(!mDefinitionCaching) + { + if(!errored) + { + if(gSavedSettings.getBOOL("_NACL_PreProcLSLOptimizer")) + { + mCore->mErrorList->setCommentText("Optimizing out unreferenced user-defined functions and global variables"); + try + { + output = lslopt(output); + }catch(...) + { + errored = TRUE; + err = "unexpected exception in lsl optimizer"; + mCore->mErrorList->setCommentText(err); + } + } + } + if(!errored) + { + if(gSavedSettings.getBOOL("_NACL_PreProcLSLTextCompress")) + { + mCore->mErrorList->setCommentText("Compressing lsltext by removing unnecessary space"); + try + { + output = lslcomp(output); + }catch(...) + { + errored = TRUE; + err = "unexpected exception in lsl compressor"; + mCore->mErrorList->setCommentText(err); + } + } + } + output = encode(rinput)+"\n\n"+output; + + + LLTextEditor* outfield = mCore->mPostEditor;//getChild("post_process"); + if(outfield) + { + outfield->setText(LLStringExplicit(output)); + } + mCore->mPostScript = output; + mCore->doSaveComplete((void*)mCore,mClose); + } + waving = FALSE; +} + +#else + +std::string FSLSLPreprocessor::encode(std::string script) +{ + display_error("(encode) Warning: Preprocessor not supported in this build."); + return script; +} + +std::string FSLSLPreprocessor::decode(std::string script) +{ + display_error("(decode) Warning: Preprocessor not supported in this build."); + return script; +} + +std::string FSLSLPreprocessor::lslopt(std::string script) +{ + display_error("(lslopt) Warning: Preprocessor not supported in this build."); + return script; +} + +void FSLSLPreprocessor::FSProcCacheCallback(LLVFS *vfs, const LLUUID& uuid, LLAssetType::EType type, void *userdata, S32 result, LLExtStat extstat) +{ +} + +void FSLSLPreprocessor::preprocess_script(BOOL close, BOOL defcache) +{ + LLTextEditor* outfield = mCore->mPostEditor; + if(outfield) + { + outfield->setText(LLStringExplicit(mCore->mEditor->getText())); + } + mCore->doSaveComplete((void*)mCore,close); +} + +#endif + +void FSLSLPreprocessor::display_error(std::string err) +{ + mCore->mErrorList->setCommentText(err); +} + + +bool FSLSLPreprocessor::mono_directive(std::string& text, bool agent_inv) +{ + bool domono = agent_inv; + + if(text.find("//mono\n") != -1) + { + domono = TRUE; + } + else if(text.find("//lsl2\n") != -1) + { + domono = FALSE; + } + return domono; +} diff --git a/indra/newview/fslslpreproc.h b/indra/newview/fslslpreproc.h new file mode 100644 index 0000000000..ec0056a1f1 --- /dev/null +++ b/indra/newview/fslslpreproc.h @@ -0,0 +1,85 @@ +/* Copyright (c) 2010 + * + * Modular Systems All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * 3. Neither the name Modular Systems nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY MODULAR SYSTEMS AND CONTRIBUTORS “AS IS” + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MODULAR SYSTEMS OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "llviewerprecompiledheaders.h" +#include "llpreviewscript.h" + +#define DARWINPREPROC +//force preproc on mac + +class FSLSLPreprocessor +{ +public: + + FSLSLPreprocessor(LLScriptEdCore* corep) + : mCore(corep), waving(FALSE), mClose(FALSE) + {} + + static bool mono_directive(std::string& text, bool agent_inv = true); + std::string encode(std::string script); + std::string decode(std::string script); + + std::string lslopt(std::string script); + std::string lslcomp(std::string script); + + static LLUUID findInventoryByName(std::string name); + static void FSProcCacheCallback(LLVFS *vfs, const LLUUID& uuid, LLAssetType::EType type, + void *userdata, S32 result, LLExtStat extstat); + void preprocess_script(BOOL close = FALSE, BOOL defcache = FALSE); + void start_process(); + void display_error(std::string err); + + std::string uncollide_string_literals(std::string script); + + //dual function, determines if files have been modified this session and if we have cached them + //also assetids exposed in-preprocessing as a predefined macro for use in include once style include files, e.g. #define THISFILE file_ ## __ASSETIDRAW__ + //in case it isn't obvious, the viewer only sets the asset id on a successful script save (of a full perm script), or in preproc on-cache + //so this is only applicable to fully permissive scripts; which is just fine, since if it isn't full perm it isn't really useful as a include anyway. + //in the event of a no-trans script (only less than full thats readable), the server sends null key, and we will set a random uuid. + //This uuid should be overwritten if they edit that script, whether with the real id or null key is irrelevant in this case. + //theoretically, if the asset IDs were exposed for full perm scripts without downloading the script at least once, it would save unnecessary caching + //as this isn't the case I'm not going to preserve this structure across logins. + + //(it seems rather dumb that readable scripts don't show the asset id without a DL, but thats beside the point.) + static std::map cached_assetids; + + static std::map decollided_literals; + + std::set caching_files; + std::set defcached_files; + bool mDefinitionCaching; + + LLScriptEdCore* mCore; + BOOL waving; + BOOL mClose; + BOOL mHDDInclude; + std::string mMainScriptName; +}; diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index 178f75126e..aecf1f1134 100644 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -92,6 +92,10 @@ #include "rlvlocks.h" // [/RLVa:KB] +// NaCl - LSL Preprocessor +#include "fslslpreproc.h" +// NaCl End + const std::string HELLO_LSL = "default\n" "{\n" @@ -207,7 +211,17 @@ LLScriptEdCore::LLScriptEdCore( setFollowsAll(); setBorderVisible(FALSE); - setXMLFilename("panel_script_ed.xml"); + // NaCl - Script Preprocessor + static LLCachedControl _NACL_LSLPreprocessor(gSavedSettings,"_NACL_LSLPreprocessor", 0); + BOOL preproc = _NACL_LSLPreprocessor; + if(preproc) + { + setXMLFilename("panel_script_ed_preproc.xml"); + mLSLProc = new FSLSLPreprocessor(this); + } + else + setXMLFilename("panel_script_ed.xml"); + // NaCl End llassert_always(mContainer != NULL); } @@ -226,6 +240,7 @@ LLScriptEdCore::~LLScriptEdCore() delete mLiveFile; } + BOOL LLScriptEdCore::postBuild() { mLineCol=getChild("line_col"); @@ -239,10 +254,24 @@ BOOL LLScriptEdCore::postBuild() mEditor = getChild("Script Editor"); + // NaCl - LSL Preprocessor + static LLCachedControl _NACL_LSLPreprocessor(gSavedSettings,"_NACL_LSLPreprocessor", 0); + BOOL preproc = _NACL_LSLPreprocessor; + if(preproc) + { + mPostEditor = getChild("Post Editor"); + if(mPostEditor) + { + mPostEditor->setFollowsAll(); + mPostEditor->setEnabled(TRUE); + } + } + // NaCl End + childSetCommitCallback("lsl errors", &LLScriptEdCore::onErrorList, this); childSetAction("Save_btn", boost::bind(&LLScriptEdCore::doSave,this,FALSE)); childSetAction("Edit_btn", boost::bind(&LLScriptEdCore::openInExternalEditor, this)); - + initMenu(); @@ -299,6 +328,66 @@ BOOL LLScriptEdCore::postBuild() } } + // NaCl - LSL Preprocessor + if(gSavedSettings.getBOOL("_NACL_LSLPreprocessor")) + if(mPostEditor) + { + mPostEditor->loadKeywords(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"keywords.ini"), funcs, tooltips, color); + mEditor->addToken(LLKeywordToken::WORD,"#assert",LLColor3(0.0f,0.0f,0.8f), + std::string("Preprocessor command. See Advanced menu of the script editor.")); + mEditor->addToken(LLKeywordToken::WORD,"#define",LLColor3(0.0f,0.0f,0.8f), + std::string("Preprocessor command. See Advanced menu of the script editor.")); + mEditor->addToken(LLKeywordToken::WORD,"#elif",LLColor3(0.0f,0.0f,0.8f), + std::string("Preprocessor command. See Advanced menu of the script editor.")); + mEditor->addToken(LLKeywordToken::WORD,"#else",LLColor3(0.0f,0.0f,0.8f), + std::string("Preprocessor command. See Advanced menu of the script editor.")); + mEditor->addToken(LLKeywordToken::WORD,"#endif",LLColor3(0.0f,0.0f,0.8f), + std::string("Preprocessor command. See Advanced menu of the script editor.")); + mEditor->addToken(LLKeywordToken::WORD,"#error",LLColor3(0.0f,0.0f,0.8f), + std::string("Preprocessor command. See Advanced menu of the script editor.")); + mEditor->addToken(LLKeywordToken::WORD,"#ident",LLColor3(0.0f,0.0f,0.8f), + std::string("Preprocessor command. See Advanced menu of the script editor.")); + mEditor->addToken(LLKeywordToken::WORD,"#sccs",LLColor3(0.0f,0.0f,0.8f), + std::string("Preprocessor command. See Advanced menu of the script editor.")); + mEditor->addToken(LLKeywordToken::WORD,"#if",LLColor3(0.0f,0.0f,0.8f), + std::string("Preprocessor command. See Advanced menu of the script editor.")); + mEditor->addToken(LLKeywordToken::WORD,"#ifdef",LLColor3(0.0f,0.0f,0.8f), + std::string("Preprocessor command. See Advanced menu of the script editor.")); + mEditor->addToken(LLKeywordToken::WORD,"#ifndef",LLColor3(0.0f,0.0f,0.8f), + std::string("Preprocessor command. See Advanced menu of the script editor.")); + mEditor->addToken(LLKeywordToken::WORD,"#import",LLColor3(0.0f,0.0f,0.8f), + std::string("Preprocessor command. See Advanced menu of the script editor.")); + mEditor->addToken(LLKeywordToken::WORD,"#include",LLColor3(0.0f,0.0f,0.8f), + std::string("Preprocessor command. See Advanced menu of the script editor.")); + mEditor->addToken(LLKeywordToken::WORD,"#include_next",LLColor3(0.0f,0.0f,0.8f), + std::string("Preprocessor command. See Advanced menu of the script editor.")); + mEditor->addToken(LLKeywordToken::WORD,"#line",LLColor3(0.0f,0.0f,0.8f), + std::string("Preprocessor command. See Advanced menu of the script editor.")); + mEditor->addToken(LLKeywordToken::WORD,"#pragma",LLColor3(0.0f,0.0f,0.8f), + std::string("Preprocessor command. See Advanced menu of the script editor.")); + mEditor->addToken(LLKeywordToken::WORD,"#unassert",LLColor3(0.0f,0.0f,0.8f), + std::string("Preprocessor command. See Advanced menu of the script editor.")); + mEditor->addToken(LLKeywordToken::WORD,"#undef",LLColor3(0.0f,0.0f,0.8f), + std::string("Preprocessor command. See Advanced menu of the script editor.")); + mEditor->addToken(LLKeywordToken::WORD,"#warning",LLColor3(0.0f,0.0f,0.8f), + std::string("Preprocessor command. See Advanced menu of the script editor.")); + mEditor->addToken(LLKeywordToken::WORD,"#",LLColor3(0.0f,0.0f,0.8f), + std::string("Preprocessor command. See Advanced menu of the script editor.")); + + if(gSavedSettings.getBOOL("_NACL_PreProcLSLSwitch")) + { + mEditor->addToken(LLKeywordToken::WORD,"switch",LLColor3(0.0f,0.0f,0.8f), + std::string("Switch statement. See Advanced menu of the script editor.")); + mEditor->addToken(LLKeywordToken::WORD,"case",LLColor3(0.0f,0.0f,0.8f), + std::string("Switch case. See Advanced menu of the script editor.")); + mEditor->addToken(LLKeywordToken::WORD,"break",LLColor3(0.0f,0.0f,0.8f), + std::string("Switch break. See Advanced menu of the script editor.")); + } + + //couldn'tr define in file because # represented a comment + } + // NaCl End + // Case-insensitive dictionary sort for primary keywords. We don't sort the secondary // keywords. They're intelligently grouped in keywords.ini. std::stable_sort( primary_keywords.begin(), primary_keywords.end(), LLSECKeywordCompare() ); @@ -376,15 +465,50 @@ void LLScriptEdCore::initMenu() menuItem->setEnableCallback(boost::bind(&LLScriptEdCore::enableSaveToFileMenu, this)); } +// NaCl - LSL Preprocessor +void LLScriptEdCore::onToggleProc(void* userdata) +{ + LLScriptEdCore* corep = (LLScriptEdCore*)userdata; + corep->mErrorList->setCommentText(std::string("Toggling the preprocessor will not take full effect unless you close and reopen this editor.")); + corep->mErrorList->selectFirstItem(); + gSavedSettings.setBOOL("_NACL_LSLPreprocessor",!gSavedSettings.getBOOL("_NACL_LSLPreprocessor")); +} +// NaCl End + void LLScriptEdCore::setScriptText(const std::string& text, BOOL is_valid) { if (mEditor) { - mEditor->setText(text); + // NaCl - LSL Preprocessor + std::string ntext = text; + if(gSavedSettings.getBOOL("_NACL_LSLPreprocessor")) + { + if(mPostEditor)mPostEditor->setText(ntext); + ntext = mLSLProc->decode(ntext); + } + LLStringUtil::replaceTabsWithSpaces(ntext, 4); + // NaCl End + mEditor->setText(ntext); mHasScriptData = is_valid; } } +// NaCl - LSL Preprocessor +std::string LLScriptEdCore::getScriptText() +{ + if(gSavedSettings.getBOOL("_NACL_LSLPreprocessor") && mPostEditor) + { + //return mPostEditor->getText(); + return mPostScript; + } + else if (mEditor) + { + return mEditor->getText(); + } + return std::string(); +} +// NaCl End + bool LLScriptEdCore::loadScriptText(const std::string& filename) { if (filename.empty()) @@ -442,7 +566,9 @@ bool LLScriptEdCore::writeToFile(const std::string& filename) return false; } - std::string utf8text = mEditor->getText(); + // NaCl - LSL Preprocessor + std::string utf8text = this->getScriptText(); + // NaCl End // Special case for a completely empty script - stuff in one space so it can store properly. See SL-46889 if (utf8text.size() == 0) @@ -812,14 +938,36 @@ void LLScriptEdCore::onBtnInsertFunction(LLUICtrl *ui, void* userdata) } void LLScriptEdCore::doSave( BOOL close_after_save ) +{ + // NaCl - LSL Preprocessor + if(gSavedSettings.getBOOL("_NACL_LSLPreprocessor")) + { + llinfos << "passing to preproc" << llendl; + this->mLSLProc->preprocess_script(close_after_save); + } + else + { + if( mSaveCallback ) + { + mSaveCallback( mUserdata, close_after_save ); + } + } + // NaCl End +} + +// NaCl - LSL Preprocessor +void LLScriptEdCore::doSaveComplete( void* userdata, BOOL close_after_save ) { LLViewerStats::getInstance()->incStat( LLViewerStats::ST_LSL_SAVE_COUNT ); + //LLScriptEdCore* self = (LLScriptEdCore*) userdata; + if( mSaveCallback ) { mSaveCallback( mUserdata, close_after_save ); } } +// NaCl End void LLScriptEdCore::openInExternalEditor() { @@ -890,8 +1038,22 @@ void LLScriptEdCore::onErrorList(LLUICtrl*, void* user_data) sscanf(line.c_str(), "%d %d", &row, &column); //llinfos << "LLScriptEdCore::onErrorList() - " << row << ", " //<< column << llendl; - self->mEditor->setCursor(row, column); - self->mEditor->setFocus(TRUE); + // NaCl - LSL Preprocessor + if(gSavedSettings.getBOOL("_NACL_LSLPreprocessor") && self->mPostEditor) + { + LLPanel* tab = self->getChild("Preprocessed"); + LLTabContainer* tabset = self->getChild("Tabset"); + if(tabset)tabset->selectTabByName("Preprocessed"); + if(tab)tab->setFocus(TRUE); + self->mPostEditor->setFocus(TRUE); + self->mPostEditor->setCursor(row, column); + } + else + { + self->mEditor->setCursor(row, column); + self->mEditor->setFocus(TRUE); + } + // NaCl End } } @@ -957,7 +1119,16 @@ BOOL LLScriptEdCore::handleKeyHere(KEY key, MASK mask) if(mSaveCallback) { // don't close after saving - mSaveCallback(mUserdata, FALSE); + // NaCl - LSL Preprocessor + if (!hasChanged()) + { + llinfos << "Save Not Needed" << llendl; + return TRUE; + } + doSave(FALSE); + // NaCl End + + //mSaveCallback(mUserdata, FALSE); } return TRUE; @@ -1305,7 +1476,7 @@ void LLPreviewLSL::saveIfNeeded(bool sync /*= true*/) std::string filename = filepath + ".lsl"; mScriptEd->writeToFile(filename); - + if (sync) { mScriptEd->sync(); @@ -1314,13 +1485,30 @@ void LLPreviewLSL::saveIfNeeded(bool sync /*= true*/) const LLInventoryItem *inv_item = getItem(); // save it out to asset server std::string url = gAgent.getRegion()->getCapability("UpdateScriptAgent"); + + // NaCL - LSL Preprocessor + BOOL domono = FSLSLPreprocessor::mono_directive(mScriptEd->getScriptText()); + if(domono == FALSE) + { + LLSD row; + if(gSavedSettings.getBOOL("_NACL_SaveInventoryScriptsAsMono")) + { + row["columns"][0]["value"] = "Detected compile-as-LSL2 directive, but debug setting SaveInventoryScriptsAsMono overrided it."; + domono = TRUE; + }else row["columns"][0]["value"] = "Detected compile-as-LSL2 directive"; + //domono = FALSE; + row["columns"][0]["font"] = "SANSSERIF_SMALL"; + mScriptEd->mErrorList->addElement(row); + } + // NaCl End if(inv_item) { getWindow()->incBusyCount(); mPendingUploads++; if (!url.empty()) { - uploadAssetViaCaps(url, filename, mItemUUID); + // NaCL - LSL Preprocessor + uploadAssetViaCaps(url, filename, mItemUUID, domono); } else if (gAssetStorage) { @@ -1331,7 +1519,8 @@ void LLPreviewLSL::saveIfNeeded(bool sync /*= true*/) void LLPreviewLSL::uploadAssetViaCaps(const std::string& url, const std::string& filename, - const LLUUID& item_id) + const LLUUID& item_id, + bool mono) { llinfos << "Update Agent Inventory via capability" << llendl; LLSD body; @@ -1433,8 +1622,11 @@ void LLPreviewLSL::uploadAssetLegacy(const std::string& filename, // static -void LLPreviewLSL::onSaveComplete(const LLUUID& asset_uuid, void* user_data, S32 status, LLExtStat ext_status) // StoreAssetData callback (fixed) +void LLPreviewLSL::onSaveComplete(const LLUUID& iuuid, void* user_data, S32 status, LLExtStat ext_status) // StoreAssetData callback (fixed) { + // NaCl - LSL Preprocessor + LLUUID asset_uuid = iuuid; + // NaCl End LLScriptSaveInfo* info = reinterpret_cast(user_data); if(0 == status) { @@ -1445,6 +1637,10 @@ void LLPreviewLSL::onSaveComplete(const LLUUID& asset_uuid, void* user_data, S32 if(item) { LLPointer new_item = new LLViewerInventoryItem(item); + // NaCl - LSL Preprocessor + if(asset_uuid.isNull()) + asset_uuid.generate(); + // NaCl End new_item->setAssetUUID(asset_uuid); new_item->setTransactionID(info->mTransactionID); new_item->updateServer(FALSE); diff --git a/indra/newview/llpreviewscript.h b/indra/newview/llpreviewscript.h index f75dee8497..2dea8311f2 100644 --- a/indra/newview/llpreviewscript.h +++ b/indra/newview/llpreviewscript.h @@ -49,6 +49,9 @@ class LLKeywordToken; class LLVFS; class LLViewerInventoryItem; class LLScriptEdContainer; +// NaCl - LSL Preprocessor +class FSLSLPreprocessor; +// NaCl End // Inner, implementation class. LLPreviewScript and LLLiveLSLEditor each own one of these. class LLScriptEdCore : public LLPanel @@ -58,6 +61,9 @@ class LLScriptEdCore : public LLPanel friend class LLLiveLSLEditor; // friend class LLFloaterScriptSearch; friend class LLScriptEdContainer; + // NaCl - LSL Preprocessor + friend class FSLSLPreprocessor; + // NaCl End protected: // Supposed to be invoked only by the container. @@ -82,6 +88,10 @@ public: bool canLoadOrSaveToFile( void* userdata ); void setScriptText(const std::string& text, BOOL is_valid); + // NaCL - LSL Preprocessor + std::string getScriptText(); + void doSaveComplete(void* userdata, BOOL close_after_save ); + // NaCl End bool loadScriptText(const std::string& filename); bool writeToFile(const std::string& filename); void sync(); @@ -108,6 +118,9 @@ public: virtual bool hasAccelerators() const { return true; } private: + // NaCl - LSL Preprocessor + static void onToggleProc(void* userdata); + // NaCl End void onBtnHelp(); void onBtnDynamicHelp(); void onBtnUndoChanges(); @@ -148,6 +161,11 @@ private: LLLiveLSLFile* mLiveFile; LLTextBox* mLineCol; LLView* mSaveBtn; + // NaCl - LSL Preprocessor + FSLSLPreprocessor* mLSLProc; + LLTextEditor* mPostEditor; + std::string mPostScript; + // NaCl End LLScriptEdContainer* mContainer; // parent view }; @@ -187,9 +205,12 @@ protected: virtual void loadAsset(); /*virtual*/ void saveIfNeeded(bool sync = true); + // NaCl - LSL Preprocessor void uploadAssetViaCaps(const std::string& url, const std::string& filename, - const LLUUID& item_id); + const LLUUID& item_id, + bool mono); + // NaCl End void uploadAssetLegacy(const std::string& filename, const LLUUID& item_id, const LLTransactionID& tid); diff --git a/indra/newview/skins/default/xui/en/panel_script_ed_preproc.xml b/indra/newview/skins/default/xui/en/panel_script_ed_preproc.xml new file mode 100644 index 0000000000..0d18e4206a --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_script_ed_preproc.xml @@ -0,0 +1,248 @@ + + + + Loading... + + + You can not view or edit this script, since it has been set as "no copy". You need full permissions to view or edit a script inside an object. + + + Public Objects cannot run scripts + + + Running + + + Script: [NAME] + + + Select an editor by setting the environment variable LL_SCRIPT_EDITOR or the ExternalEditor setting. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Loading... + + + + + Loading... + + + + + + +