diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 4cac54dddf9b..fa380c290d7e 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -801,6 +801,7 @@ JSRuntime::JSRuntime() negativeInfinityValue(UndefinedValue()), positiveInfinityValue(UndefinedValue()), emptyString(NULL), + sourceHook(NULL), debugMode(false), spsProfiler(thisFromCtor()), profilingScripts(false), diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 36ed62ac3fcc..a9a841abf4a1 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -716,6 +716,8 @@ struct JSRuntime : js::RuntimeFriendFields return !JS_CLIST_IS_EMPTY(&contextList); } + JS_SourceHook sourceHook; + /* Per runtime debug hooks -- see jsprvtd.h and jsdbgapi.h. */ JSDebugHooks debugHooks; diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index c0959b3bad76..465ac7dc4545 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -22,6 +22,12 @@ using namespace js; using namespace JS; +JS_FRIEND_API(void) +JS_SetSourceHook(JSRuntime *rt, JS_SourceHook hook) +{ + rt->sourceHook = hook; +} + JS_FRIEND_API(void) JS_SetGrayGCRootsTracer(JSRuntime *rt, JSTraceDataOp traceOp, void *data) { diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index f71620f827a2..5b612d0c1bca 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -175,6 +175,11 @@ JS_END_EXTERN_C #ifdef __cplusplus +typedef bool (* JS_SourceHook)(JSContext *cx, JSScript *script, char **src, uint32_t *length); + +extern JS_FRIEND_API(void) +JS_SetSourceHook(JSRuntime *rt, JS_SourceHook hook); + namespace js { struct RuntimeFriendFields { diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index e86d4aa45b1d..76f1e74e6b35 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -590,7 +590,10 @@ JSFunction::toString(JSContext *cx, bool bodyOnly, bool lambdaParen) return NULL; } } - if (isInterpreted() && script()->source) { + bool haveSource = isInterpreted(); + if (haveSource && !script()->source && !script()->loadSource(cx, &haveSource)) + return NULL; + if (haveSource) { RootedString src(cx, script()->sourceData(cx)); if (!src) return NULL; @@ -686,6 +689,13 @@ JSFunction::toString(JSContext *cx, bool bodyOnly, bool lambdaParen) if (!out.append(")")) return NULL; } + } else if (isInterpreted()) { + if ((!bodyOnly && !out.append("() {\n ")) || + !out.append("[sourceless code]") || + (!bodyOnly && !out.append("\n}"))) + return NULL; + if (!lambdaParen && (flags & JSFUN_LAMBDA) && (!out.append(")"))) + return NULL; } else { JS_ASSERT(!(flags & JSFUN_EXPR_CLOSURE)); if ((!bodyOnly && !out.append("() {\n ")) || diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 3d44084724c6..f16278761850 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -1070,6 +1070,35 @@ SourceCompressorThread::waitOnCompression(SourceCompressionToken *userTok) } #endif /* JS_THREADSAFE */ +bool +JSScript::loadSource(JSContext *cx, bool *worked) +{ + JS_ASSERT(!source); + *worked = false; + if (!cx->runtime->sourceHook) + return true; + char *src = NULL; + uint32_t length; + if (!cx->runtime->sourceHook(cx, this, &src, &length)) + return false; + if (!src) + return true; + size_t newLength = length; + jschar *usrc = InflateString(cx, src, &newLength); + cx->free_(src); + if (!usrc) + return false; + ScriptSource *ss = ScriptSource::createFromSource(cx, usrc, length, false, NULL, true); + if (!ss) { + cx->free_(usrc); + return false; + } + source = ss; + ss->attachToRuntime(cx->runtime); + *worked = true; + return true; +} + JSFixedString * JSScript::sourceData(JSContext *cx) { @@ -1147,7 +1176,8 @@ ScriptSource::substring(JSContext *cx, uint32_t start, uint32_t stop) ScriptSource * ScriptSource::createFromSource(JSContext *cx, const jschar *src, uint32_t length, - bool argumentsNotIncluded, SourceCompressionToken *tok) + bool argumentsNotIncluded, SourceCompressionToken *tok, + bool ownSource) { ScriptSource *ss = static_cast(cx->malloc_(sizeof(*ss))); if (!ss) @@ -1160,6 +1190,7 @@ ScriptSource::createFromSource(JSContext *cx, const jschar *src, uint32_t length } ss->next = NULL; ss->length_ = length; + ss->compressedLength = 0; ss->marked = ss->onRuntime_ = false; ss->argumentsNotIncluded_ = argumentsNotIncluded; #ifdef DEBUG @@ -1176,6 +1207,8 @@ ScriptSource::createFromSource(JSContext *cx, const jschar *src, uint32_t length ss->marked = true; #endif + JS_ASSERT_IF(ownSource, !tok); + #ifdef JS_THREADSAFE if (tok) { tok->ss = ss; @@ -1183,21 +1216,23 @@ ScriptSource::createFromSource(JSContext *cx, const jschar *src, uint32_t length cx->runtime->sourceCompressorThread.compress(tok); } else #endif - ss->considerCompressing(cx->runtime, src); + ss->considerCompressing(cx->runtime, src, ownSource); return ss; } void -ScriptSource::considerCompressing(JSRuntime *rt, const jschar *src) +ScriptSource::considerCompressing(JSRuntime *rt, const jschar *src, bool ownSource) { JS_ASSERT(!ready()); const size_t memlen = length_ * sizeof(jschar); const size_t COMPRESS_THRESHOLD = 512; size_t compressedLen; - if (memlen >= COMPRESS_THRESHOLD && + if (ownSource) { + data.source = const_cast(src); + } else if (memlen >= COMPRESS_THRESHOLD && TryCompressString(reinterpret_cast(src), memlen, data.compressed, &compressedLen)) { @@ -1207,7 +1242,6 @@ ScriptSource::considerCompressing(JSRuntime *rt, const jschar *src) data.compressed = static_cast(mem); JS_ASSERT(data.compressed); } else { - compressedLength = 0; PodCopy(data.source, src, length_); } #ifdef DEBUG diff --git a/js/src/jsscript.h b/js/src/jsscript.h index 6d8434d020ca..87ae8392d363 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -620,6 +620,8 @@ struct JSScript : public js::gc::Cell JSFixedString *sourceData(JSContext *cx); + bool loadSource(JSContext *cx, bool *worked); + /* Return whether this script was compiled for 'eval' */ bool isForEval() { return isCachedEval || isActiveEval; } @@ -978,7 +980,8 @@ struct ScriptSource const jschar *src, uint32_t length, bool argumentsNotIncluded = false, - SourceCompressionToken *tok = NULL); + SourceCompressionToken *tok = NULL, + bool ownSource = false); void attachToRuntime(JSRuntime *rt); void mark() { JS_ASSERT(ready_); JS_ASSERT(onRuntime_); marked = true; } void destroy(JSRuntime *rt); @@ -1000,7 +1003,7 @@ struct ScriptSource private: bool compressed() { return !!compressedLength; } - void considerCompressing(JSRuntime *rt, const jschar *src); + void considerCompressing(JSRuntime *rt, const jschar *src, bool ownSource = false); }; #ifdef JS_THREADSAFE