forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			591 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			591 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #include "PrecompiledScript.h"
 | |
| 
 | |
| #include "nsIIncrementalStreamLoader.h"
 | |
| #include "nsIURI.h"
 | |
| #include "nsIChannel.h"
 | |
| #include "nsNetUtil.h"
 | |
| #include "nsThreadUtils.h"
 | |
| 
 | |
| #include "jsapi.h"
 | |
| #include "jsfriendapi.h"
 | |
| #include "js/CompileOptions.h"  // JS::CompileOptions, JS::OwningCompileOptions
 | |
| #include "js/CompilationAndEvaluation.h"
 | |
| #include "js/experimental/CompileScript.h"  // JS::CompileGlobalScriptToStencil, JS::NewFrontendContext, JS::DestroyFrontendContext, JS::SetNativeStackQuota, JS::ThreadStackQuotaForSize, JS::HadFrontendErrors, JS::ConvertFrontendErrorsToRuntimeErrors
 | |
| #include "js/experimental/JSStencil.h"  // JS::Stencil, JS::CompileGlobalScriptToStencil, JS::InstantiateGlobalStencil
 | |
| #include "js/SourceText.h"  // JS::SourceText
 | |
| #include "js/Utility.h"
 | |
| 
 | |
| #include "mozilla/AlreadyAddRefed.h"  // already_AddRefed
 | |
| #include "mozilla/Assertions.h"       // MOZ_ASSERT
 | |
| #include "mozilla/Attributes.h"
 | |
| #include "mozilla/ClearOnShutdown.h"  // RunOnShutdown
 | |
| #include "mozilla/EventQueue.h"       // EventQueuePriority
 | |
| #include "mozilla/Mutex.h"
 | |
| #include "mozilla/SchedulerGroup.h"
 | |
| #include "mozilla/StaticMutex.h"
 | |
| #include "mozilla/StaticPrefs_javascript.h"
 | |
| #include "mozilla/dom/ChromeUtils.h"
 | |
| #include "mozilla/dom/Promise.h"
 | |
| #include "mozilla/dom/ScriptLoader.h"
 | |
| #include "mozilla/HoldDropJSObjects.h"
 | |
| #include "mozilla/RefPtr.h"          // RefPtr
 | |
| #include "mozilla/TaskController.h"  // TaskController, Task
 | |
| #include "mozilla/ThreadSafety.h"    // MOZ_GUARDED_BY
 | |
| #include "mozilla/Utf8.h"            // Utf8Unit
 | |
| #include "mozilla/Vector.h"
 | |
| #include "nsCCUncollectableMarker.h"
 | |
| #include "nsCycleCollectionParticipant.h"
 | |
| 
 | |
| using namespace JS;
 | |
| using namespace mozilla;
 | |
| using namespace mozilla::dom;
 | |
| 
 | |
| class AsyncScriptCompileTask final : public Task {
 | |
|   static mozilla::StaticMutex sOngoingTasksMutex;
 | |
|   static Vector<AsyncScriptCompileTask*> sOngoingTasks
 | |
|       MOZ_GUARDED_BY(sOngoingTasksMutex);
 | |
|   static bool sIsShutdownRegistered;
 | |
| 
 | |
|   // Compilation tasks should be cancelled before calling JS_ShutDown, in order
 | |
|   // to avoid keeping JS::Stencil and SharedImmutableString pointers alive
 | |
|   // beyond it.
 | |
|   //
 | |
|   // Cancel all ongoing tasks at ShutdownPhase::XPCOMShutdownFinal, which
 | |
|   // happens before calling JS_ShutDown.
 | |
|   static bool RegisterTask(AsyncScriptCompileTask* aTask) {
 | |
|     MOZ_ASSERT(NS_IsMainThread());
 | |
| 
 | |
|     if (!sIsShutdownRegistered) {
 | |
|       sIsShutdownRegistered = true;
 | |
| 
 | |
|       RunOnShutdown([] {
 | |
|         StaticMutexAutoLock lock(sOngoingTasksMutex);
 | |
|         for (auto* task : sOngoingTasks) {
 | |
|           task->Cancel();
 | |
|         }
 | |
|       });
 | |
|     }
 | |
| 
 | |
|     StaticMutexAutoLock lock(sOngoingTasksMutex);
 | |
|     return sOngoingTasks.append(aTask);
 | |
|   }
 | |
| 
 | |
|   static void UnregisterTask(const AsyncScriptCompileTask* aTask) {
 | |
|     StaticMutexAutoLock lock(sOngoingTasksMutex);
 | |
|     sOngoingTasks.eraseIfEqual(aTask);
 | |
|   }
 | |
| 
 | |
|  public:
 | |
|   explicit AsyncScriptCompileTask(JS::SourceText<Utf8Unit>&& aSrcBuf)
 | |
|       : Task(Kind::OffMainThreadOnly, EventQueuePriority::Normal),
 | |
|         mOptions(JS::OwningCompileOptions::ForFrontendContext()),
 | |
|         mSrcBuf(std::move(aSrcBuf)),
 | |
|         mMutex("AsyncScriptCompileTask") {}
 | |
| 
 | |
|   ~AsyncScriptCompileTask() {
 | |
|     if (mFrontendContext) {
 | |
|       JS::DestroyFrontendContext(mFrontendContext);
 | |
|     }
 | |
|     UnregisterTask(this);
 | |
|   }
 | |
| 
 | |
|   bool Init(const JS::OwningCompileOptions& aOptions) {
 | |
|     if (!RegisterTask(this)) {
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     mFrontendContext = JS::NewFrontendContext();
 | |
|     if (!mFrontendContext) {
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     if (!mOptions.copy(mFrontendContext, aOptions)) {
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   void Compile() {
 | |
|     // NOTE: The stack limit must be set from the same thread that compiles.
 | |
|     size_t stackSize = TaskController::GetThreadStackSize();
 | |
|     JS::SetNativeStackQuota(mFrontendContext,
 | |
|                             JS::ThreadStackQuotaForSize(stackSize));
 | |
| 
 | |
|     mStencil =
 | |
|         JS::CompileGlobalScriptToStencil(mFrontendContext, mOptions, mSrcBuf);
 | |
|   }
 | |
| 
 | |
|   // Cancel the task.
 | |
|   // If the task is already running, this waits for the task to finish.
 | |
|   void Cancel() {
 | |
|     MOZ_ASSERT(NS_IsMainThread());
 | |
| 
 | |
|     MutexAutoLock lock(mMutex);
 | |
| 
 | |
|     mIsCancelled = true;
 | |
| 
 | |
|     mStencil = nullptr;
 | |
|   }
 | |
| 
 | |
|  public:
 | |
|   TaskResult Run() override {
 | |
|     MutexAutoLock lock(mMutex);
 | |
| 
 | |
|     if (mIsCancelled) {
 | |
|       return TaskResult::Complete;
 | |
|     }
 | |
| 
 | |
|     Compile();
 | |
|     return TaskResult::Complete;
 | |
|   }
 | |
| 
 | |
|   already_AddRefed<JS::Stencil> StealStencil(JSContext* aCx) {
 | |
|     JS::FrontendContext* fc = mFrontendContext;
 | |
|     mFrontendContext = nullptr;
 | |
| 
 | |
|     MOZ_ASSERT(fc);
 | |
| 
 | |
|     if (JS::HadFrontendErrors(fc)) {
 | |
|       (void)JS::ConvertFrontendErrorsToRuntimeErrors(aCx, fc, mOptions);
 | |
|       JS::DestroyFrontendContext(fc);
 | |
|       return nullptr;
 | |
|     }
 | |
| 
 | |
|     // Report warnings.
 | |
|     if (!JS::ConvertFrontendErrorsToRuntimeErrors(aCx, fc, mOptions)) {
 | |
|       JS::DestroyFrontendContext(fc);
 | |
|       return nullptr;
 | |
|     }
 | |
| 
 | |
|     JS::DestroyFrontendContext(fc);
 | |
| 
 | |
|     return mStencil.forget();
 | |
|   }
 | |
| 
 | |
| #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
 | |
|   bool GetName(nsACString& aName) override {
 | |
|     aName.AssignLiteral("AsyncScriptCompileTask");
 | |
|     return true;
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|  private:
 | |
|   // Owning-pointer for the context associated with the script compilation.
 | |
|   //
 | |
|   // The context is allocated on main thread in Init method, and is freed on
 | |
|   // any thread in the destructor.
 | |
|   JS::FrontendContext* mFrontendContext = nullptr;
 | |
| 
 | |
|   JS::OwningCompileOptions mOptions;
 | |
| 
 | |
|   RefPtr<JS::Stencil> mStencil;
 | |
| 
 | |
|   JS::SourceText<Utf8Unit> mSrcBuf;
 | |
| 
 | |
|   // This mutex is locked during running the task or cancelling task.
 | |
|   mozilla::Mutex mMutex;
 | |
| 
 | |
|   bool mIsCancelled MOZ_GUARDED_BY(mMutex) = false;
 | |
| };
 | |
| 
 | |
| /* static */ mozilla::StaticMutex AsyncScriptCompileTask::sOngoingTasksMutex;
 | |
| /* static */ Vector<AsyncScriptCompileTask*>
 | |
|     AsyncScriptCompileTask::sOngoingTasks;
 | |
| /* static */ bool AsyncScriptCompileTask::sIsShutdownRegistered = false;
 | |
| 
 | |
| class AsyncScriptCompiler;
 | |
| 
 | |
| class AsyncScriptCompilationCompleteTask : public Task {
 | |
|  public:
 | |
|   AsyncScriptCompilationCompleteTask(AsyncScriptCompiler* aCompiler,
 | |
|                                      AsyncScriptCompileTask* aCompileTask)
 | |
|       : Task(Kind::MainThreadOnly, EventQueuePriority::Normal),
 | |
|         mCompiler(aCompiler),
 | |
|         mCompileTask(aCompileTask) {
 | |
|     MOZ_ASSERT(NS_IsMainThread());
 | |
|   }
 | |
| 
 | |
| #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
 | |
|   bool GetName(nsACString& aName) override {
 | |
|     aName.AssignLiteral("AsyncScriptCompilationCompleteTask");
 | |
|     return true;
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   TaskResult Run() override;
 | |
| 
 | |
|  private:
 | |
|   // NOTE:
 | |
|   // This field is main-thread only, and this task shouldn't be freed off
 | |
|   // main thread.
 | |
|   //
 | |
|   // This is guaranteed by not having off-thread tasks which depends on this
 | |
|   // task, because otherwise the off-thread task's mDependencies can be the
 | |
|   // last reference, which results in freeing this task off main thread.
 | |
|   //
 | |
|   // If such task is added, this field must be moved to separate storage.
 | |
|   RefPtr<AsyncScriptCompiler> mCompiler;
 | |
| 
 | |
|   RefPtr<AsyncScriptCompileTask> mCompileTask;
 | |
| };
 | |
| 
 | |
| class AsyncScriptCompiler final : public nsIIncrementalStreamLoaderObserver {
 | |
|  public:
 | |
|   // Note: References to this class are never held by cycle-collected objects.
 | |
|   // If at any point a reference is returned to a caller, please update this
 | |
|   // class to implement cycle collection.
 | |
|   NS_DECL_ISUPPORTS
 | |
|   NS_DECL_NSIINCREMENTALSTREAMLOADEROBSERVER
 | |
| 
 | |
|   AsyncScriptCompiler(JSContext* aCx, nsIGlobalObject* aGlobal,
 | |
|                       const nsACString& aURL, Promise* aPromise)
 | |
|       : mOptions(aCx),
 | |
|         mURL(aURL),
 | |
|         mGlobalObject(aGlobal),
 | |
|         mPromise(aPromise),
 | |
|         mScriptLength(0) {}
 | |
| 
 | |
|   [[nodiscard]] nsresult Start(JSContext* aCx,
 | |
|                                const CompileScriptOptionsDictionary& aOptions,
 | |
|                                nsIPrincipal* aPrincipal);
 | |
| 
 | |
|   void OnCompilationComplete(AsyncScriptCompileTask* aCompileTask);
 | |
| 
 | |
|  protected:
 | |
|   virtual ~AsyncScriptCompiler() {
 | |
|     if (mPromise->State() == Promise::PromiseState::Pending) {
 | |
|       mPromise->MaybeReject(NS_ERROR_FAILURE);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   void Reject(JSContext* aCx);
 | |
|   void Reject(JSContext* aCx, const char* aMxg);
 | |
| 
 | |
|   bool StartCompile(JSContext* aCx);
 | |
|   bool StartOffThreadCompile(JS::SourceText<Utf8Unit>&& aSrcBuf);
 | |
|   void FinishCompile(JSContext* aCx);
 | |
|   void Finish(JSContext* aCx, RefPtr<JS::Stencil>&& aStencil);
 | |
| 
 | |
|   OwningCompileOptions mOptions;
 | |
|   nsCString mURL;
 | |
|   nsCOMPtr<nsIGlobalObject> mGlobalObject;
 | |
|   RefPtr<Promise> mPromise;
 | |
|   nsString mCharset;
 | |
|   UniquePtr<Utf8Unit[], JS::FreePolicy> mScriptText;
 | |
|   size_t mScriptLength;
 | |
| };
 | |
| 
 | |
| NS_IMPL_ISUPPORTS(AsyncScriptCompiler, nsIIncrementalStreamLoaderObserver)
 | |
| 
 | |
| nsresult AsyncScriptCompiler::Start(
 | |
|     JSContext* aCx, const CompileScriptOptionsDictionary& aOptions,
 | |
|     nsIPrincipal* aPrincipal) {
 | |
|   mCharset = aOptions.mCharset;
 | |
| 
 | |
|   CompileOptions options(aCx);
 | |
|   options.setFile(mURL.get()).setNoScriptRval(!aOptions.mHasReturnValue);
 | |
| 
 | |
|   if (!aOptions.mLazilyParse) {
 | |
|     options.setForceFullParse();
 | |
|   }
 | |
| 
 | |
|   if (NS_WARN_IF(!mOptions.copy(aCx, options))) {
 | |
|     return NS_ERROR_OUT_OF_MEMORY;
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsIURI> uri;
 | |
|   nsresult rv = NS_NewURI(getter_AddRefs(uri), mURL);
 | |
|   NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|   nsCOMPtr<nsIChannel> channel;
 | |
|   rv = NS_NewChannel(
 | |
|       getter_AddRefs(channel), uri, aPrincipal,
 | |
|       nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
 | |
|       nsIContentPolicy::TYPE_INTERNAL_CHROMEUTILS_COMPILED_SCRIPT);
 | |
|   NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|   // allow deprecated HTTP request from SystemPrincipal
 | |
|   nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
 | |
|   loadInfo->SetAllowDeprecatedSystemRequests(true);
 | |
|   nsCOMPtr<nsIIncrementalStreamLoader> loader;
 | |
|   rv = NS_NewIncrementalStreamLoader(getter_AddRefs(loader), this);
 | |
|   NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|   return channel->AsyncOpen(loader);
 | |
| }
 | |
| 
 | |
| bool AsyncScriptCompiler::StartCompile(JSContext* aCx) {
 | |
|   JS::SourceText<Utf8Unit> srcBuf;
 | |
|   if (!srcBuf.init(aCx, std::move(mScriptText), mScriptLength)) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   // TODO: This uses the same heuristics and the same threshold as the
 | |
|   //       JS::CanCompileOffThread, but the heuristics needs to be updated to
 | |
|   //       reflect the change regarding the Stencil API, and also the thread
 | |
|   //       management on the consumer side (bug 1846388).
 | |
|   static constexpr size_t OffThreadMinimumTextLength = 5 * 1000;
 | |
| 
 | |
|   if (StaticPrefs::javascript_options_parallel_parsing() &&
 | |
|       mScriptLength >= OffThreadMinimumTextLength) {
 | |
|     if (!StartOffThreadCompile(std::move(srcBuf))) {
 | |
|       return false;
 | |
|     }
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   RefPtr<Stencil> stencil =
 | |
|       JS::CompileGlobalScriptToStencil(aCx, mOptions, srcBuf);
 | |
|   if (!stencil) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   Finish(aCx, std::move(stencil));
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool AsyncScriptCompiler::StartOffThreadCompile(
 | |
|     JS::SourceText<Utf8Unit>&& aSrcBuf) {
 | |
|   RefPtr<AsyncScriptCompileTask> compileTask =
 | |
|       new AsyncScriptCompileTask(std::move(aSrcBuf));
 | |
| 
 | |
|   RefPtr<AsyncScriptCompilationCompleteTask> complationCompleteTask =
 | |
|       new AsyncScriptCompilationCompleteTask(this, compileTask.get());
 | |
| 
 | |
|   if (!compileTask->Init(mOptions)) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   complationCompleteTask->AddDependency(compileTask.get());
 | |
| 
 | |
|   TaskController::Get()->AddTask(compileTask.forget());
 | |
|   TaskController::Get()->AddTask(complationCompleteTask.forget());
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| Task::TaskResult AsyncScriptCompilationCompleteTask::Run() {
 | |
|   mCompiler->OnCompilationComplete(mCompileTask.get());
 | |
|   mCompiler = nullptr;
 | |
|   mCompileTask = nullptr;
 | |
|   return TaskResult::Complete;
 | |
| }
 | |
| 
 | |
| void AsyncScriptCompiler::OnCompilationComplete(
 | |
|     AsyncScriptCompileTask* aCompileTask) {
 | |
|   AutoJSAPI jsapi;
 | |
|   if (!jsapi.Init(mGlobalObject)) {
 | |
|     mPromise->MaybeReject(NS_ERROR_FAILURE);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   JSContext* cx = jsapi.cx();
 | |
|   RefPtr<JS::Stencil> stencil = aCompileTask->StealStencil(cx);
 | |
|   if (!stencil) {
 | |
|     Reject(cx);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   Finish(cx, std::move(stencil));
 | |
|   return;
 | |
| }
 | |
| 
 | |
| void AsyncScriptCompiler::Finish(JSContext* aCx,
 | |
|                                  RefPtr<JS::Stencil>&& aStencil) {
 | |
|   RefPtr<PrecompiledScript> result =
 | |
|       new PrecompiledScript(mGlobalObject, aStencil, mOptions);
 | |
| 
 | |
|   mPromise->MaybeResolve(result);
 | |
| }
 | |
| 
 | |
| void AsyncScriptCompiler::Reject(JSContext* aCx) {
 | |
|   RootedValue value(aCx, JS::UndefinedValue());
 | |
|   if (JS_GetPendingException(aCx, &value)) {
 | |
|     JS_ClearPendingException(aCx);
 | |
|   }
 | |
|   mPromise->MaybeReject(value);
 | |
| }
 | |
| 
 | |
| void AsyncScriptCompiler::Reject(JSContext* aCx, const char* aMsg) {
 | |
|   nsAutoString msg;
 | |
|   msg.AppendASCII(aMsg);
 | |
|   msg.AppendLiteral(": ");
 | |
|   AppendUTF8toUTF16(mURL, msg);
 | |
| 
 | |
|   RootedValue exn(aCx);
 | |
|   if (xpc::NonVoidStringToJsval(aCx, msg, &exn)) {
 | |
|     JS_SetPendingException(aCx, exn);
 | |
|   }
 | |
| 
 | |
|   Reject(aCx);
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| AsyncScriptCompiler::OnIncrementalData(nsIIncrementalStreamLoader* aLoader,
 | |
|                                        nsISupports* aContext,
 | |
|                                        uint32_t aDataLength,
 | |
|                                        const uint8_t* aData,
 | |
|                                        uint32_t* aConsumedData) {
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| AsyncScriptCompiler::OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
 | |
|                                       nsISupports* aContext, nsresult aStatus,
 | |
|                                       uint32_t aLength, const uint8_t* aBuf) {
 | |
|   AutoJSAPI jsapi;
 | |
|   if (!jsapi.Init(mGlobalObject)) {
 | |
|     mPromise->MaybeReject(NS_ERROR_FAILURE);
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   JSContext* cx = jsapi.cx();
 | |
| 
 | |
|   if (NS_FAILED(aStatus)) {
 | |
|     Reject(cx, "Unable to load script");
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   nsresult rv = ScriptLoader::ConvertToUTF8(
 | |
|       nullptr, aBuf, aLength, mCharset, nullptr, mScriptText, mScriptLength);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     Reject(cx, "Unable to decode script");
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   if (!StartCompile(cx)) {
 | |
|     Reject(cx);
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| namespace mozilla {
 | |
| namespace dom {
 | |
| 
 | |
| /* static */
 | |
| already_AddRefed<Promise> ChromeUtils::CompileScript(
 | |
|     GlobalObject& aGlobal, const nsAString& aURL,
 | |
|     const CompileScriptOptionsDictionary& aOptions, ErrorResult& aRv) {
 | |
|   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
 | |
|   MOZ_ASSERT(global);
 | |
| 
 | |
|   RefPtr<Promise> promise = Promise::Create(global, aRv);
 | |
|   if (aRv.Failed()) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   NS_ConvertUTF16toUTF8 url(aURL);
 | |
|   RefPtr<AsyncScriptCompiler> compiler =
 | |
|       new AsyncScriptCompiler(aGlobal.Context(), global, url, promise);
 | |
| 
 | |
|   nsresult rv = compiler->Start(aGlobal.Context(), aOptions,
 | |
|                                 aGlobal.GetSubjectPrincipal());
 | |
|   if (NS_FAILED(rv)) {
 | |
|     promise->MaybeReject(rv);
 | |
|   }
 | |
| 
 | |
|   return promise.forget();
 | |
| }
 | |
| 
 | |
| PrecompiledScript::PrecompiledScript(nsISupports* aParent,
 | |
|                                      RefPtr<JS::Stencil> aStencil,
 | |
|                                      JS::ReadOnlyCompileOptions& aOptions)
 | |
|     : mParent(aParent),
 | |
|       mStencil(aStencil),
 | |
|       mURL(aOptions.filename().c_str()),
 | |
|       mHasReturnValue(!aOptions.noScriptRval) {
 | |
|   MOZ_ASSERT(aParent);
 | |
|   MOZ_ASSERT(aStencil);
 | |
| #ifdef DEBUG
 | |
|   JS::InstantiateOptions options(aOptions);
 | |
|   options.assertDefault();
 | |
| #endif
 | |
| };
 | |
| 
 | |
| void PrecompiledScript::ExecuteInGlobal(JSContext* aCx, HandleObject aGlobal,
 | |
|                                         const ExecuteInGlobalOptions& aOptions,
 | |
|                                         MutableHandleValue aRval,
 | |
|                                         ErrorResult& aRv) {
 | |
|   {
 | |
|     RootedObject targetObj(aCx, JS_FindCompilationScope(aCx, aGlobal));
 | |
|     // Use AutoEntryScript for its ReportException method call.
 | |
|     // This will ensure notified any exception happening in the content script
 | |
|     // directly to the console, so that exceptions are flagged with the right
 | |
|     // innerWindowID. It helps these exceptions to appear in the page's web
 | |
|     // console.
 | |
|     AutoEntryScript aes(targetObj, "pre-compiled-script execution");
 | |
|     JSContext* cx = aes.cx();
 | |
| 
 | |
|     // See assertion in constructor.
 | |
|     JS::InstantiateOptions options;
 | |
|     Rooted<JSScript*> script(
 | |
|         cx, JS::InstantiateGlobalStencil(cx, options, mStencil));
 | |
|     if (!script) {
 | |
|       aRv.NoteJSContextException(aCx);
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     if (!JS_ExecuteScript(cx, script, aRval)) {
 | |
|       JS::RootedValue exn(cx);
 | |
|       if (aOptions.mReportExceptions) {
 | |
|         // Note that ReportException will consume the exception.
 | |
|         aes.ReportException();
 | |
|       } else {
 | |
|         // Set the exception on our caller's cx.
 | |
|         aRv.MightThrowJSException();
 | |
|         aRv.StealExceptionFromJSContext(cx);
 | |
|       }
 | |
|       return;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   JS_WrapValue(aCx, aRval);
 | |
| }
 | |
| 
 | |
| void PrecompiledScript::GetUrl(nsAString& aUrl) { CopyUTF8toUTF16(mURL, aUrl); }
 | |
| 
 | |
| bool PrecompiledScript::HasReturnValue() { return mHasReturnValue; }
 | |
| 
 | |
| JSObject* PrecompiledScript::WrapObject(JSContext* aCx,
 | |
|                                         HandleObject aGivenProto) {
 | |
|   return PrecompiledScript_Binding::Wrap(aCx, this, aGivenProto);
 | |
| }
 | |
| 
 | |
| bool PrecompiledScript::IsBlackForCC(bool aTracingNeeded) {
 | |
|   return (nsCCUncollectableMarker::sGeneration && HasKnownLiveWrapper() &&
 | |
|           (!aTracingNeeded || HasNothingToTrace(this)));
 | |
| }
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PrecompiledScript, mParent)
 | |
| 
 | |
| NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PrecompiledScript)
 | |
|   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
 | |
|   NS_INTERFACE_MAP_ENTRY(nsISupports)
 | |
| NS_INTERFACE_MAP_END
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(PrecompiledScript)
 | |
|   return tmp->IsBlackForCC(false);
 | |
| NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(PrecompiledScript)
 | |
|   return tmp->IsBlackForCC(true);
 | |
| NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(PrecompiledScript)
 | |
|   return tmp->IsBlackForCC(false);
 | |
| NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTING_ADDREF(PrecompiledScript)
 | |
| NS_IMPL_CYCLE_COLLECTING_RELEASE(PrecompiledScript)
 | |
| 
 | |
| }  // namespace dom
 | |
| }  // namespace mozilla
 | 
