text: /*
   * Copyright (C) 2009 Google Inc. 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.
   *
   * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "bindings/core/v8/V8ScriptRunner.h"
  
  #include "bindings/core/v8/ScriptSourceCode.h"
  #include "bindings/core/v8/ScriptStreamer.h"
  #include "bindings/core/v8/V8Binding.h"
  #include "bindings/core/v8/V8GCController.h"
  #include "bindings/core/v8/V8ThrowException.h"
  #include "core/dom/Document.h"
  #include "core/dom/ExecutionContext.h"
  #include "core/fetch/CachedMetadata.h"
  #include "core/frame/LocalFrame.h"
  #include "core/frame/PerformanceMonitor.h"
  #include "core/inspector/InspectorTraceEvents.h"
  #include "core/inspector/ThreadDebugger.h"
  #include "core/loader/resource/ScriptResource.h"
  #include "platform/Histogram.h"
  #include "platform/ScriptForbiddenScope.h"
  #include "platform/tracing/TraceEvent.h"
  #include "public/platform/Platform.h"
  #include "wtf/CurrentTime.h"
  
  #if OS(WIN)
  #include <malloc.h>
  #else
  #include <alloca.h>
  #endif
  
  namespace blink {
  
  namespace {
  
  // Used to throw an exception before we exceed the C++ stack and crash.
  // This limit was arrived at arbitrarily. crbug.com/449744
  const int kMaxRecursionDepth = 44;
  
  class V8CompileHistogram {
   public:
    enum Cacheability { Cacheable, Noncacheable, InlineScript };
    explicit V8CompileHistogram(Cacheability);
    ~V8CompileHistogram();
  
   private:
    Cacheability m_cacheability;
    double m_timeStamp;
  };
  
  V8CompileHistogram::V8CompileHistogram(
      V8CompileHistogram::Cacheability cacheability)
      : m_cacheability(cacheability), m_timeStamp(WTF::currentTime()) {}
  
  V8CompileHistogram::~V8CompileHistogram() {
    int64_t elapsedMicroSeconds =
        static_cast<int64_t>((WTF::currentTime() - m_timeStamp) * 1000000);
    switch (m_cacheability) {
      case Cacheable: {
        DEFINE_THREAD_SAFE_STATIC_LOCAL(
            CustomCountHistogram, compileCacheableHistogram,
            new CustomCountHistogram("V8.CompileCacheableMicroSeconds", 0,
                                     1000000, 50));
        compileCacheableHistogram.count(elapsedMicroSeconds);
        break;
      }
      case Noncacheable: {
        DEFINE_THREAD_SAFE_STATIC_LOCAL(
            CustomCountHistogram, compileNonCacheableHistogram,
            new CustomCountHistogram("V8.CompileNoncacheableMicroSeconds", 0,
                                     1000000, 50));
        compileNonCacheableHistogram.count(elapsedMicroSeconds);
        break;
      }
      case InlineScript: {
        DEFINE_THREAD_SAFE_STATIC_LOCAL(
            CustomCountHistogram, compileInlineHistogram,
            new CustomCountHistogram("V8.CompileInlineScriptMicroSeconds", 0,
                                     1000000, 50));
        compileInlineHistogram.count(elapsedMicroSeconds);
        break;
      }
    }
  }
  
  // In order to make sure all pending messages to be processed in
  // v8::Function::Call, we don't call throwStackOverflowException
  // directly. Instead, we create a v8::Function of
  // throwStackOverflowException and call it.
  void throwStackOverflowException(
      const v8::FunctionCallbackInfo<v8::Value>& info) {
    V8ThrowException::throwRangeError(info.GetIsolate(),
                                      "Maximum call stack size exceeded.");
  }
  
  void throwScriptForbiddenException(v8::Isolate* isolate) {
    V8ThrowException::throwError(isolate, "Script execution is forbidden.");
  }
  
  v8::Local<v8::Value> throwStackOverflowExceptionIfNeeded(v8::Isolate* isolate) {
    if (V8PerIsolateData::from(isolate)->isHandlingRecursionLevelError()) {
      // If we are already handling a recursion level error, we should
      // not invoke v8::Function::Call.
      return v8::Undefined(isolate);
    }
    v8::MicrotasksScope microtasksScope(isolate,
                                        v8::MicrotasksScope::kDoNotRunMicrotasks);
    V8PerIsolateData::from(isolate)->setIsHandlingRecursionLevelError(true);
    v8::Local<v8::Value> result =
        v8::Function::New(isolate->GetCurrentContext(),
                          throwStackOverflowException, v8::Local<v8::Value>(), 0,
                          v8::ConstructorBehavior::kThrow)
            .ToLocalChecked()
            ->Call(v8::Undefined(isolate), 0, 0);
    V8PerIsolateData::from(isolate)->setIsHandlingRecursionLevelError(false);
    return result;
  }
  
  // Compile a script without any caching or compile options.
  v8::MaybeLocal<v8::Script> compileWithoutOptions(
      V8CompileHistogram::Cacheability cacheability,
      v8::Isolate* isolate,
      v8::Local<v8::String> code,
      v8::ScriptOrigin origin) {
    V8CompileHistogram histogramScope(cacheability);
    v8::ScriptCompiler::Source source(code, origin);
    return v8::ScriptCompiler::Compile(isolate->GetCurrentContext(), &source,
                                       v8::ScriptCompiler::kNoCompileOptions);
  }
  
  // Compile a script, and consume a V8 cache that was generated previously.
  static v8::MaybeLocal<v8::Script> compileAndConsumeCache(
      CachedMetadataHandler* cacheHandler,
      PassRefPtr<CachedMetadata> cachedMetadata,
      v8::ScriptCompiler::CompileOptions compileOptions,
      v8::Isolate* isolate,
      v8::Local<v8::String> code,
      v8::ScriptOrigin origin) {
    V8CompileHistogram histogramScope(V8CompileHistogram::Cacheable);
    const char* data = cachedMetadata->data();
    int length = cachedMetadata->size();
    v8::ScriptCompiler::CachedData* cachedData =
        new v8::ScriptCompiler::CachedData(
            reinterpret_cast<const uint8_t*>(data), length,
            v8::ScriptCompiler::CachedData::BufferNotOwned);
    v8::ScriptCompiler::Source source(code, origin, cachedData);
    v8::MaybeLocal<v8::Script> script = v8::ScriptCompiler::Compile(
        isolate->GetCurrentContext(), &source, compileOptions);
    if (cachedData->rejected)
      cacheHandler->clearCachedMetadata(CachedMetadataHandler::SendToPlatform);
    return script;
  }
  
  // Compile a script, and produce a V8 cache for future use.
  v8::MaybeLocal<v8::Script> compileAndProduceCache(
      CachedMetadataHandler* cacheHandler,
      uint32_t tag,
      v8::ScriptCompiler::CompileOptions compileOptions,
      CachedMetadataHandler::CacheType cacheType,
      v8::Isolate* isolate,
      v8::Local<v8::String> code,
      v8::ScriptOrigin origin) {
    V8CompileHistogram histogramScope(V8CompileHistogram::Cacheable);
    v8::ScriptCompiler::Source source(code, origin);
    v8::MaybeLocal<v8::Script> script = v8::ScriptCompiler::Compile(
        isolate->GetCurrentContext(), &source, compileOptions);
    const v8::ScriptCompiler::CachedData* cachedData = source.GetCachedData();
    if (cachedData) {
      const char* data = reinterpret_cast<const char*>(cachedData->data);
      int length = cachedData->length;
      if (length > 1024) {
        // Omit histogram samples for small cache data to avoid outliers.
        int cacheSizeRatio = static_cast<int>(100.0 * length / code->Length());
        DEFINE_THREAD_SAFE_STATIC_LOCAL(
            CustomCountHistogram, codeCacheSizeHistogram,
            new CustomCountHistogram("V8.CodeCacheSizeRatio", 0, 10000, 50));
        codeCacheSizeHistogram.count(cacheSizeRatio);
      }
      cacheHandler->clearCachedMetadata(CachedMetadataHandler::CacheLocally);
      cacheHandler->setCachedMetadata(tag, data, length, cacheType);
    }
    return script;
  }
  
  // Compile a script, and consume or produce a V8 Cache, depending on whether the
  // given resource already has cached data available.
  v8::MaybeLocal<v8::Script> compileAndConsumeOrProduce(
      CachedMetadataHandler* cacheHandler,
      uint32_t tag,
      v8::ScriptCompiler::CompileOptions consumeOptions,
      v8::ScriptCompiler::CompileOptions produceOptions,
      CachedMetadataHandler::CacheType cacheType,
      v8::Isolate* isolate,
      v8::Local<v8::String> code,
      v8::ScriptOrigin origin) {
    RefPtr<CachedMetadata> codeCache(cacheHandler->cachedMetadata(tag));
    return codeCache.get()
               ? compileAndConsumeCache(cacheHandler, codeCache, consumeOptions,
                                        isolate, code, origin)
               : compileAndProduceCache(cacheHandler, tag, produceOptions,
                                        cacheType, isolate, code, origin);
  }
  
  enum CacheTagKind {
    CacheTagParser = 0,
    CacheTagCode = 1,
    CacheTagTimeStamp = 3,
    CacheTagLast
  };
  
  static const int kCacheTagKindSize = 2;
  
  uint32_t cacheTag(CacheTagKind kind, CachedMetadataHandler* cacheHandler) {
    static_assert((1 << kCacheTagKindSize) >= CacheTagLast,
                  "CacheTagLast must be large enough");
  
    static uint32_t v8CacheDataVersion =
        v8::ScriptCompiler::CachedDataVersionTag() << kCacheTagKindSize;
  
    // A script can be (successfully) interpreted with different encodings,
    // depending on the page it appears in. The cache doesn't know anything
    // about encodings, but the cached data is specific to one encoding. If we
    // later load the script from the cache and interpret it with a different
    // encoding, the cached data is not valid for that encoding.
    return (v8CacheDataVersion | kind) +
           StringHash::hash(cacheHandler->encoding());
  }
  
  // Check previously stored timestamp.
  bool isResourceHotForCaching(CachedMetadataHandler* cacheHandler,
                               int hotHours) {
    const double cacheWithinSeconds = hotHours * 60 * 60;
    uint32_t tag = cacheTag(CacheTagTimeStamp, cacheHandler);
    RefPtr<CachedMetadata> cachedMetadata = cacheHandler->cachedMetadata(tag);
    if (!cachedMetadata)
      return false;
    double timeStamp;
    const int size = sizeof(timeStamp);
    ASSERT(cachedMetadata->size() == size);
    memcpy(&timeStamp, cachedMetadata->data(), size);
    return (WTF::currentTime() - timeStamp) < cacheWithinSeconds;
  }
  
  // Final compile call for a streamed compilation. Most decisions have already
  // been made, but we need to write back data into the cache.
  v8::MaybeLocal<v8::Script> postStreamCompile(
      V8CacheOptions cacheOptions,
      CachedMetadataHandler* cacheHandler,
      ScriptStreamer* streamer,
      v8::Isolate* isolate,
      v8::Local<v8::String> code,
      v8::ScriptOrigin origin) {
    V8CompileHistogram histogramScope(V8CompileHistogram::Noncacheable);
    v8::MaybeLocal<v8::Script> script = v8::ScriptCompiler::Compile(
        isolate->GetCurrentContext(), streamer->source(), code, origin);
  
    if (!cacheHandler)
      return script;
  
    // If the non-streaming compiler uses the parser cache, retrieve and store
    // the cache data. If the code cache uses time stamp as heuristic, set that
    // time stamp.
    switch (cacheOptions) {
      case V8CacheOptionsParse: {
        const v8::ScriptCompiler::CachedData* newCachedData =
            streamer->source()->GetCachedData();
        if (!newCachedData)
          break;
        CachedMetadataHandler::CacheType cacheType =
            (cacheOptions == V8CacheOptionsParse)
                ? CachedMetadataHandler::SendToPlatform
                : CachedMetadataHandler::CacheLocally;
        cacheHandler->clearCachedMetadata(cacheType);
        cacheHandler->setCachedMetadata(
            cacheTag(CacheTagParser, cacheHandler),
            reinterpret_cast<const char*>(newCachedData->data),
            newCachedData->length, cacheType);
        break;
      }
  
      case V8CacheOptionsDefault:
      case V8CacheOptionsCode:
        V8ScriptRunner::setCacheTimeStamp(cacheHandler);
        break;
  
      case V8CacheOptionsAlways:
        // Currently V8CacheOptionsAlways doesn't support streaming.
        ASSERT_NOT_REACHED();
      case V8CacheOptionsNone:
        break;
    }
    return script;
  }
  
  typedef Function<v8::MaybeLocal<v8::Script>(v8::Isolate*,
                                              v8::Local<v8::String>,
                                              v8::ScriptOrigin)>
      CompileFn;
  
  // A notation convenience: WTF::bind<...> needs to be given the right argument
  // types. We have an awful lot of bind calls below, all with the same types, so
  // this local bind lets WTF::bind to all the work, but 'knows' the right
  // parameter types.
  // This version isn't quite as smart as the real WTF::bind, though, so you
  // sometimes may still have to call the original.
  template <typename... A>
  std::unique_ptr<CompileFn> bind(const A&... args) {
    return WTF::bind(args...);
  }
  
  // Select a compile function from any of the above, mainly depending on
  // cacheOptions.
  static std::unique_ptr<CompileFn> selectCompileFunction(
      V8CacheOptions cacheOptions,
      CachedMetadataHandler* cacheHandler,
      PassRefPtr<CachedMetadata> codeCache,
      v8::Local<v8::String> code,
      V8CompileHistogram::Cacheability cacheabilityIfNoHandler) {
    static const int minimalCodeLength = 1024;
    static const int hotHours = 72;
  
    // Caching is not available in this case.
    if (!cacheHandler)
      return bind(compileWithoutOptions, cacheabilityIfNoHandler);
  
    if (cacheOptions == V8CacheOptionsNone)
      return bind(compileWithoutOptions, V8CompileHistogram::Cacheable);
  
    // Caching is not worthwhile for small scripts.  Do not use caching
    // unless explicitly expected, indicated by the cache option.
    if (code->Length() < minimalCodeLength)
      return bind(compileWithoutOptions, V8CompileHistogram::Cacheable);
  
    // The cacheOptions will guide our strategy:
    switch (cacheOptions) {
      case V8CacheOptionsParse:
        // Use parser-cache; in-memory only.
        return bind(compileAndConsumeOrProduce, wrapPersistent(cacheHandler),
                    cacheTag(CacheTagParser, cacheHandler),
                    v8::ScriptCompiler::kConsumeParserCache,
                    v8::ScriptCompiler::kProduceParserCache,
                    CachedMetadataHandler::CacheLocally);
        break;
  
      case V8CacheOptionsDefault:
      case V8CacheOptionsCode:
      case V8CacheOptionsAlways: {
        // Use code caching for recently seen resources.
        // Use compression depending on the cache option.
        if (codeCache)
          return bind(compileAndConsumeCache, wrapPersistent(cacheHandler),
                      codeCache, v8::ScriptCompiler::kConsumeCodeCache);
        if (cacheOptions != V8CacheOptionsAlways &&
            !isResourceHotForCaching(cacheHandler, hotHours)) {
          V8ScriptRunner::setCacheTimeStamp(cacheHandler);
          return bind(compileWithoutOptions, V8CompileHistogram::Cacheable);
        }
        uint32_t codeCacheTag = cacheTag(CacheTagCode, cacheHandler);
        return bind(compileAndProduceCache, wrapPersistent(cacheHandler),
                    codeCacheTag, v8::ScriptCompiler::kProduceCodeCache,
                    CachedMetadataHandler::SendToPlatform);
        break;
      }
  
      case V8CacheOptionsNone:
        // Shouldn't happen, as this is handled above.
        // Case is here so that compiler can check all cases are handled.
        ASSERT_NOT_REACHED();
        break;
    }
  
    // All switch branches should return and we should never get here.
    // But some compilers aren't sure, hence this default.
    ASSERT_NOT_REACHED();
    return bind(compileWithoutOptions, V8CompileHistogram::Cacheable);
  }
  
  // Select a compile function for a streaming compile.
  std::unique_ptr<CompileFn> selectCompileFunction(V8CacheOptions cacheOptions,
                                                   ScriptResource* resource,
                                                   ScriptStreamer* streamer) {
    // We don't stream scripts which don't have a Resource.
    ASSERT(resource);
    // Failed resources should never get this far.
    ASSERT(!resource->errorOccurred());
    ASSERT(streamer->isFinished());
    ASSERT(!streamer->streamingSuppressed());
    return WTF::bind(postStreamCompile, cacheOptions,
                     wrapPersistent(resource->cacheHandler()),
                     wrapPersistent(streamer));
  }
  }  // namespace
  
  v8::MaybeLocal<v8::Script> V8ScriptRunner::compileScript(
      const ScriptSourceCode& source,
      v8::Isolate* isolate,
      AccessControlStatus accessControlStatus,
      V8CacheOptions cacheOptions) {
    if (source.source().length() >= v8::String::kMaxLength) {
      V8ThrowException::throwError(isolate, "Source file too large.");
      return v8::Local<v8::Script>();
    }
    return compileScript(
        v8String(isolate, source.source()), source.url(), source.sourceMapUrl(),
        source.startPosition(), isolate, source.resource(), source.streamer(),
        source.resource() ? source.resource()->cacheHandler() : nullptr,
        accessControlStatus, cacheOptions);
  }
  
  v8::MaybeLocal<v8::Script> V8ScriptRunner::compileScript(
      const String& code,
      const String& fileName,
      const String& sourceMapUrl,
      const TextPosition& textPosition,
      v8::Isolate* isolate,
      CachedMetadataHandler* cacheMetadataHandler,
      AccessControlStatus accessControlStatus,
      V8CacheOptions v8CacheOptions) {
    if (code.length() >= v8::String::kMaxLength) {
      V8ThrowException::throwError(isolate, "Source file too large.");
      return v8::Local<v8::Script>();
    }
    return compileScript(v8String(isolate, code), fileName, sourceMapUrl,
                         textPosition, isolate, nullptr, nullptr,
                         cacheMetadataHandler, accessControlStatus,
                         v8CacheOptions);
  }
  
  v8::MaybeLocal<v8::Script> V8ScriptRunner::compileScript(
      v8::Local<v8::String> code,
      const String& fileName,
      const String& sourceMapUrl,
      const TextPosition& scriptStartPosition,
      v8::Isolate* isolate,
      ScriptResource* resource,
      ScriptStreamer* streamer,
      CachedMetadataHandler* cacheHandler,
      AccessControlStatus accessControlStatus,
      V8CacheOptions cacheOptions) {
    TRACE_EVENT2(
        "v8,devtools.timeline", "v8.compile", "fileName", fileName.utf8(), "data",
        InspectorCompileScriptEvent::data(fileName, scriptStartPosition));
  
    ASSERT(!streamer || resource);
    ASSERT(!resource || resource->cacheHandler() == cacheHandler);
  
    // NOTE: For compatibility with WebCore, ScriptSourceCode's line starts at
    // 1, whereas v8 starts at 0.
    v8::ScriptOrigin origin(
        v8String(isolate, fileName),
        v8::Integer::New(isolate, scriptStartPosition.m_line.zeroBasedInt()),
        v8::Integer::New(isolate, scriptStartPosition.m_column.zeroBasedInt()),
        v8Boolean(accessControlStatus == SharableCrossOrigin, isolate),
        v8::Local<v8::Integer>(), v8Boolean(false, isolate),
        v8String(isolate, sourceMapUrl),
        v8Boolean(accessControlStatus == OpaqueResource, isolate));
  
    V8CompileHistogram::Cacheability cacheabilityIfNoHandler =
        V8CompileHistogram::Cacheability::Noncacheable;
    if (!cacheHandler && (scriptStartPosition.m_line.zeroBasedInt() == 0) &&
        (scriptStartPosition.m_column.zeroBasedInt() == 0))
      cacheabilityIfNoHandler = V8CompileHistogram::Cacheability::InlineScript;
  
    RefPtr<CachedMetadata> codeCache(
        cacheHandler
            ? cacheHandler->cachedMetadata(cacheTag(CacheTagCode, cacheHandler))
            : nullptr);
    std::unique_ptr<CompileFn> compileFn =
        streamer ? selectCompileFunction(cacheOptions, resource, streamer)
                 : selectCompileFunction(cacheOptions, cacheHandler, codeCache,
                                         code, cacheabilityIfNoHandler);
  
    return (*compileFn)(isolate, code, origin);
  }
  
  v8::MaybeLocal<v8::Value> V8ScriptRunner::runCompiledScript(
      v8::Isolate* isolate,
      v8::Local<v8::Script> script,
      ExecutionContext* context) {
    ASSERT(!script.IsEmpty());
    ScopedFrameBlamer frameBlamer(
        context->isDocument() ? toDocument(context)->frame() : nullptr);
    TRACE_EVENT1("v8", "v8.run", "fileName",
                 TRACE_STR_COPY(*v8::String::Utf8Value(
                     script->GetUnboundScript()->GetScriptName())));
  
    if (v8::MicrotasksScope::GetCurrentDepth(isolate) >= kMaxRecursionDepth)
      return throwStackOverflowExceptionIfNeeded(isolate);
  
    RELEASE_ASSERT(!context->isIteratingOverObservers());
  
    // Run the script and keep track of the current recursion depth.
    v8::MaybeLocal<v8::Value> result;
    {
      if (ScriptForbiddenScope::isScriptForbidden()) {
        throwScriptForbiddenException(isolate);
        return v8::MaybeLocal<v8::Value>();
      }
      v8::MicrotasksScope microtasksScope(isolate,
                                          v8::MicrotasksScope::kRunMicrotasks);
      PerformanceMonitor::willExecuteScript(context);
      ThreadDebugger::willExecuteScript(isolate,
                                        script->GetUnboundScript()->GetId());
      result = script->Run(isolate->GetCurrentContext());
      ThreadDebugger::didExecuteScript(isolate);
      PerformanceMonitor::didExecuteScript(context);
    }
  
    crashIfIsolateIsDead(isolate);
    return result;
  }
  
  v8::MaybeLocal<v8::Value> V8ScriptRunner::compileAndRunInternalScript(
      v8::Local<v8::String> source,
      v8::Isolate* isolate,
      const String& fileName,
      const TextPosition& scriptStartPosition) {
    v8::Local<v8::Script> script;
    if (!V8ScriptRunner::compileScript(
             source, fileName, String(), scriptStartPosition, isolate, nullptr,
             nullptr, nullptr, SharableCrossOrigin, V8CacheOptionsDefault)
             .ToLocal(&script))
      return v8::MaybeLocal<v8::Value>();
  
    TRACE_EVENT0("v8", "v8.run");
    v8::MicrotasksScope microtasksScope(isolate,
                                        v8::MicrotasksScope::kDoNotRunMicrotasks);
    v8::MaybeLocal<v8::Value> result = script->Run(isolate->GetCurrentContext());
    crashIfIsolateIsDead(isolate);
    return result;
  }
  
  v8::MaybeLocal<v8::Value> V8ScriptRunner::runCompiledInternalScript(
      v8::Isolate* isolate,
      v8::Local<v8::Script> script) {
    TRACE_EVENT0("v8", "v8.run");
    v8::MicrotasksScope microtasksScope(isolate,
                                        v8::MicrotasksScope::kDoNotRunMicrotasks);
    v8::MaybeLocal<v8::Value> result = script->Run(isolate->GetCurrentContext());
    crashIfIsolateIsDead(isolate);
    return result;
  }
  
  v8::MaybeLocal<v8::Value> V8ScriptRunner::callAsConstructor(
      v8::Isolate* isolate,
      v8::Local<v8::Object> constructor,
      ExecutionContext* context,
      int argc,
      v8::Local<v8::Value> argv[]) {
    TRACE_EVENT0("v8", "v8.callAsConstructor");
  
    int depth = v8::MicrotasksScope::GetCurrentDepth(isolate);
    if (depth >= kMaxRecursionDepth)
      return v8::MaybeLocal<v8::Value>(
          throwStackOverflowExceptionIfNeeded(isolate));
  
    CHECK(!context->isIteratingOverObservers());
  
    if (ScriptForbiddenScope::isScriptForbidden()) {
      throwScriptForbiddenException(isolate);
      return v8::MaybeLocal<v8::Value>();
    }
  
    // TODO(dominicc): When inspector supports tracing object
    // invocation, change this to use v8::Object instead of
    // v8::Function. All callers use functions because
    // CustomElementRegistry#define's IDL signature is Function.
    CHECK(constructor->IsFunction());
    v8::Local<v8::Function> function = constructor.As<v8::Function>();
  
    if (!depth)
      TRACE_EVENT_BEGIN1("devtools.timeline", "FunctionCall", "data",
                         InspectorFunctionCallEvent::data(context, function));
    v8::MicrotasksScope microtasksScope(isolate,
                                        v8::MicrotasksScope::kRunMicrotasks);
    ThreadDebugger::willExecuteScript(isolate, function->ScriptId());
    v8::MaybeLocal<v8::Value> result =
        constructor->CallAsConstructor(isolate->GetCurrentContext(), argc, argv);
    crashIfIsolateIsDead(isolate);
    ThreadDebugger::didExecuteScript(isolate);
    if (!depth)
      TRACE_EVENT_END0("devtools.timeline", "FunctionCall");
    return result;
  }
  
  v8::MaybeLocal<v8::Value> V8ScriptRunner::callFunction(
      v8::Local<v8::Function> function,
      ExecutionContext* context,
      v8::Local<v8::Value> receiver,
      int argc,
      v8::Local<v8::Value> args[],
      v8::Isolate* isolate) {
    ScopedFrameBlamer frameBlamer(
        context->isDocument() ? toDocument(context)->frame() : nullptr);
    TRACE_EVENT0("v8", "v8.callFunction");
  
    int depth = v8::MicrotasksScope::GetCurrentDepth(isolate);
    if (depth >= kMaxRecursionDepth)
      return v8::MaybeLocal<v8::Value>(
          throwStackOverflowExceptionIfNeeded(isolate));
  
    RELEASE_ASSERT(!context->isIteratingOverObservers());
  
    if (ScriptForbiddenScope::isScriptForbidden()) {
      throwScriptForbiddenException(isolate);
      return v8::MaybeLocal<v8::Value>();
    }
    if (!depth)
      TRACE_EVENT_BEGIN1("devtools.timeline", "FunctionCall", "data",
                         InspectorFunctionCallEvent::data(context, function));
  
    v8::MicrotasksScope microtasksScope(isolate,
                                        v8::MicrotasksScope::kRunMicrotasks);
    PerformanceMonitor::willExecuteScript(context);
    ThreadDebugger::willExecuteScript(isolate, function->ScriptId());
    v8::MaybeLocal<v8::Value> result =
        function->Call(isolate->GetCurrentContext(), receiver, argc, args);
    crashIfIsolateIsDead(isolate);
    ThreadDebugger::didExecuteScript(isolate);
    PerformanceMonitor::didExecuteScript(context);
    if (!depth)
      TRACE_EVENT_END0("devtools.timeline", "FunctionCall");
    return result;
  }
  
  v8::MaybeLocal<v8::Value> V8ScriptRunner::callInternalFunction(
      v8::Local<v8::Function> function,
      v8::Local<v8::Value> receiver,
      int argc,
      v8::Local<v8::Value> args[],
      v8::Isolate* isolate) {
    TRACE_EVENT0("v8", "v8.callFunction");
    v8::MicrotasksScope microtasksScope(isolate,
                                        v8::MicrotasksScope::kDoNotRunMicrotasks);
    v8::MaybeLocal<v8::Value> result =
        function->Call(isolate->GetCurrentContext(), receiver, argc, args);
    crashIfIsolateIsDead(isolate);
    return result;
  }
  
  v8::MaybeLocal<v8::Object> V8ScriptRunner::instantiateObject(
      v8::Isolate* isolate,
      v8::Local<v8::ObjectTemplate> objectTemplate) {
    TRACE_EVENT0("v8", "v8.newInstance");
  
    v8::MicrotasksScope microtasksScope(isolate,
                                        v8::MicrotasksScope::kDoNotRunMicrotasks);
    v8::MaybeLocal<v8::Object> result =
        objectTemplate->NewInstance(isolate->GetCurrentContext());
    crashIfIsolateIsDead(isolate);
    return result;
  }
  
  v8::MaybeLocal<v8::Object> V8ScriptRunner::instantiateObject(
      v8::Isolate* isolate,
      v8::Local<v8::Function> function,
      int argc,
      v8::Local<v8::Value> argv[]) {
    TRACE_EVENT0("v8", "v8.newInstance");
  
    v8::MicrotasksScope microtasksScope(isolate,
                                        v8::MicrotasksScope::kDoNotRunMicrotasks);
    v8::MaybeLocal<v8::Object> result =
        function->NewInstance(isolate->GetCurrentContext(), argc, argv);
    crashIfIsolateIsDead(isolate);
    return result;
  }
  
  v8::MaybeLocal<v8::Object> V8ScriptRunner::instantiateObjectInDocument(
      v8::Isolate* isolate,
      v8::Local<v8::Function> function,
      ExecutionContext* context,
      int argc,
      v8::Local<v8::Value> argv[]) {
    TRACE_EVENT0("v8", "v8.newInstance");
    if (ScriptForbiddenScope::isScriptForbidden()) {
      throwScriptForbiddenException(isolate);
      return v8::MaybeLocal<v8::Object>();
    }
    v8::MicrotasksScope microtasksScope(isolate,
                                        v8::MicrotasksScope::kRunMicrotasks);
    v8::MaybeLocal<v8::Object> result =
        function->NewInstance(isolate->GetCurrentContext(), argc, argv);
    crashIfIsolateIsDead(isolate);
    return result;
  }
  
  uint32_t V8ScriptRunner::tagForParserCache(
      CachedMetadataHandler* cacheHandler) {
    return cacheTag(CacheTagParser, cacheHandler);
  }
  
  uint32_t V8ScriptRunner::tagForCodeCache(CachedMetadataHandler* cacheHandler) {
    return cacheTag(CacheTagCode, cacheHandler);
  }
  
  // Store a timestamp to the cache as hint.
  void V8ScriptRunner::setCacheTimeStamp(CachedMetadataHandler* cacheHandler) {
    double now = WTF::currentTime();
    uint32_t tag = cacheTag(CacheTagTimeStamp, cacheHandler);
    cacheHandler->clearCachedMetadata(CachedMetadataHandler::CacheLocally);
    cacheHandler->setCachedMetadata(tag, reinterpret_cast<char*>(&now),
                                    sizeof(now),
                                    CachedMetadataHandler::SendToPlatform);
  }
  
  void V8ScriptRunner::throwException(v8::Isolate* isolate,
                                      v8::Local<v8::Value> exception,
                                      const v8::ScriptOrigin& origin) {
    // In order for the current TryCatch to catch this exception and
    // call MessageCallback when SetVerbose(true), create a v8::Function
    // that calls isolate->throwException().
    // Unlike throwStackOverflowExceptionIfNeeded(), create a temporary Script
    // with the specified ScriptOrigin. When the exception was created but not
    // thrown yet, the ScriptOrigin of the thrower is set to the exception.
    // v8::Function::New() has empty ScriptOrigin, and thus the exception will
    // be "muted" (sanitized in our terminology) if CORS does not allow.
    // https://html.spec.whatwg.org/multipage/webappapis.html#report-the-error
    // Avoid compile and run scripts when API is available: crbug.com/639739
    v8::Local<v8::Script> script =
        compileWithoutOptions(
            V8CompileHistogram::Cacheability::Noncacheable, isolate,
            v8AtomicString(isolate, "((e) => { throw e; })"), origin)
            .ToLocalChecked();
    v8::Local<v8::Function> thrower = runCompiledInternalScript(isolate, script)
                                          .ToLocalChecked()
                                          .As<v8::Function>();
    v8::Local<v8::Value> args[] = {exception};
    callInternalFunction(thrower, thrower, WTF_ARRAY_LENGTH(args), args, isolate);
  }
  
  }  // namespace blink
  