Home | History | Annotate | Download | only in v8
      1 /*
      2  * Copyright (C) 2009 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
     14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
     17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
     23  * THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #include "config.h"
     27 #include "bindings/core/v8/V8ScriptRunner.h"
     28 
     29 #include "bindings/core/v8/ScriptSourceCode.h"
     30 #include "bindings/core/v8/ScriptStreamer.h"
     31 #include "bindings/core/v8/V8Binding.h"
     32 #include "bindings/core/v8/V8GCController.h"
     33 #include "bindings/core/v8/V8RecursionScope.h"
     34 #include "bindings/core/v8/V8ThrowException.h"
     35 #include "core/dom/ExecutionContext.h"
     36 #include "core/fetch/CachedMetadata.h"
     37 #include "core/fetch/ScriptResource.h"
     38 #include "platform/TraceEvent.h"
     39 
     40 namespace blink {
     41 
     42 namespace {
     43 
     44 // In order to make sure all pending messages to be processed in
     45 // v8::Function::Call, we don't call handleMaxRecursionDepthExceeded
     46 // directly. Instead, we create a v8::Function of
     47 // throwStackOverflowException and call it.
     48 void throwStackOverflowException(const v8::FunctionCallbackInfo<v8::Value>& info)
     49 {
     50     V8ThrowException::throwRangeError("Maximum call stack size exceeded.", info.GetIsolate());
     51 }
     52 
     53 v8::Local<v8::Value> throwStackOverflowExceptionIfNeeded(v8::Isolate* isolate)
     54 {
     55     if (V8PerIsolateData::from(isolate)->isHandlingRecursionLevelError()) {
     56         // If we are already handling a recursion level error, we should
     57         // not invoke v8::Function::Call.
     58         return v8::Undefined(isolate);
     59     }
     60     V8PerIsolateData::from(isolate)->setIsHandlingRecursionLevelError(true);
     61     v8::Local<v8::Value> result = v8::Function::New(isolate, throwStackOverflowException)->Call(v8::Undefined(isolate), 0, 0);
     62     V8PerIsolateData::from(isolate)->setIsHandlingRecursionLevelError(false);
     63     return result;
     64 }
     65 
     66 v8::Local<v8::Script> compileAndProduceCache(v8::Isolate* isolate, v8::Handle<v8::String> code, v8::ScriptOrigin origin, ScriptResource* resource, v8::ScriptCompiler::CompileOptions options, unsigned cacheTag, Resource::MetadataCacheType cacheType)
     67 {
     68     v8::ScriptCompiler::Source source(code, origin);
     69     v8::Local<v8::Script> script = v8::ScriptCompiler::Compile(isolate, &source, options);
     70     const v8::ScriptCompiler::CachedData* cachedData = source.GetCachedData();
     71     if (resource && cachedData) {
     72         resource->clearCachedMetadata();
     73         resource->setCachedMetadata(
     74             cacheTag,
     75             reinterpret_cast<const char*>(cachedData->data),
     76             cachedData->length,
     77             cacheType);
     78     }
     79     return script;
     80 }
     81 
     82 v8::Local<v8::Script> compileAndConsumeCache(v8::Isolate* isolate, v8::Handle<v8::String> code, v8::ScriptOrigin origin, ScriptResource* resource, v8::ScriptCompiler::CompileOptions options, unsigned cacheTag)
     83 {
     84     // Consume existing cache data:
     85     CachedMetadata* cachedMetadata = resource->cachedMetadata(cacheTag);
     86     v8::ScriptCompiler::CachedData* cachedData = new v8::ScriptCompiler::CachedData(
     87         reinterpret_cast<const uint8_t*>(cachedMetadata->data()),
     88         cachedMetadata->size(),
     89         v8::ScriptCompiler::CachedData::BufferNotOwned);
     90     v8::ScriptCompiler::Source source(code, origin, cachedData);
     91     return v8::ScriptCompiler::Compile(isolate, &source, options);
     92 }
     93 
     94 } // namespace
     95 
     96 v8::Local<v8::Script> V8ScriptRunner::compileScript(const ScriptSourceCode& source, v8::Isolate* isolate, AccessControlStatus corsStatus, V8CacheOptions cacheOptions)
     97 {
     98     return compileScript(v8String(isolate, source.source()), source.url(), source.startPosition(), source.resource(), source.streamer(), isolate, corsStatus, cacheOptions);
     99 }
    100 
    101 v8::Local<v8::Script> V8ScriptRunner::compileScript(v8::Handle<v8::String> code, const String& fileName, const TextPosition& scriptStartPosition, ScriptResource* resource, ScriptStreamer* streamer, v8::Isolate* isolate, AccessControlStatus corsStatus, V8CacheOptions cacheOptions)
    102 {
    103     TRACE_EVENT1("v8", "v8.compile", "fileName", fileName.utf8());
    104     TRACE_EVENT_SCOPED_SAMPLING_STATE("v8", "V8Compile");
    105 
    106     // NOTE: For compatibility with WebCore, ScriptSourceCode's line starts at
    107     // 1, whereas v8 starts at 0.
    108     v8::Handle<v8::String> name = v8String(isolate, fileName);
    109     v8::Handle<v8::Integer> line = v8::Integer::New(isolate, scriptStartPosition.m_line.zeroBasedInt());
    110     v8::Handle<v8::Integer> column = v8::Integer::New(isolate, scriptStartPosition.m_column.zeroBasedInt());
    111     v8::Handle<v8::Boolean> isSharedCrossOrigin = corsStatus == SharableCrossOrigin ? v8::True(isolate) : v8::False(isolate);
    112     v8::ScriptOrigin origin(name, line, column, isSharedCrossOrigin);
    113 
    114     v8::Local<v8::Script> script;
    115     unsigned cacheTag = 0;
    116     if (streamer) {
    117         // We don't stream scripts which don't have a Resource.
    118         ASSERT(resource);
    119         // Failed resources should never get this far.
    120         ASSERT(!resource->errorOccurred());
    121         ASSERT(streamer->isFinished());
    122         ASSERT(!streamer->streamingSuppressed());
    123         script = v8::ScriptCompiler::Compile(isolate, streamer->source(), code, origin);
    124         // Whether to produce the cached data or not is decided when the
    125         // streamer is started. Here we only need to get the data out.
    126         const v8::ScriptCompiler::CachedData* newCachedData = streamer->source()->GetCachedData();
    127         if (newCachedData) {
    128             resource->clearCachedMetadata();
    129             resource->setCachedMetadata(streamer->cachedDataType(), reinterpret_cast<const char*>(newCachedData->data), newCachedData->length);
    130         }
    131     } else if (!resource || !resource->url().protocolIsInHTTPFamily() || code->Length() < 1024) {
    132         v8::ScriptCompiler::Source source(code, origin);
    133         script = v8::ScriptCompiler::Compile(isolate, &source, v8::ScriptCompiler::kNoCompileOptions);
    134     } else {
    135         switch (cacheOptions) {
    136         case V8CacheOptionsParse:
    137             cacheTag = tagForParserCache();
    138             script = resource->cachedMetadata(cacheTag)
    139                 ? compileAndConsumeCache(isolate, code, origin, resource, v8::ScriptCompiler::kConsumeParserCache, cacheTag)
    140                 : compileAndProduceCache(isolate, code, origin, resource, v8::ScriptCompiler::kProduceParserCache, cacheTag, Resource::SendToPlatform);
    141             break;
    142         case V8CacheOptionsCode:
    143             cacheTag = tagForCodeCache();
    144             script = resource->cachedMetadata(cacheTag)
    145                 ? compileAndConsumeCache(isolate, code, origin, resource, v8::ScriptCompiler::kConsumeCodeCache, cacheTag)
    146                 : compileAndProduceCache(isolate, code, origin, resource, v8::ScriptCompiler::kProduceCodeCache, cacheTag, Resource::SendToPlatform);
    147             break;
    148         case V8CacheOptionsOff:
    149             // Previous behaviour was to always generate an in-memory parser
    150             // cache. We emulate this here.
    151             // FIXME: Determine whether this should get its own setting, so we
    152             //        can also have a true 'off'.
    153             cacheTag = tagForParserCache();
    154             script = resource->cachedMetadata(cacheTag)
    155                 ? compileAndConsumeCache(isolate, code, origin, resource, v8::ScriptCompiler::kConsumeParserCache, cacheTag)
    156                 : compileAndProduceCache(isolate, code, origin, resource, v8::ScriptCompiler::kProduceParserCache, cacheTag, Resource::CacheLocally);
    157             break;
    158         }
    159     }
    160     return script;
    161 }
    162 
    163 v8::Local<v8::Value> V8ScriptRunner::runCompiledScript(v8::Handle<v8::Script> script, ExecutionContext* context, v8::Isolate* isolate)
    164 {
    165     if (script.IsEmpty())
    166         return v8::Local<v8::Value>();
    167     TRACE_EVENT_SCOPED_SAMPLING_STATE("v8", "V8Execution");
    168     TRACE_EVENT1("v8", "v8.run", "fileName", TRACE_STR_COPY(*v8::String::Utf8Value(script->GetUnboundScript()->GetScriptName())));
    169 
    170     if (V8RecursionScope::recursionLevel(isolate) >= kMaxRecursionDepth)
    171         return throwStackOverflowExceptionIfNeeded(isolate);
    172 
    173     RELEASE_ASSERT(!context->isIteratingOverObservers());
    174 
    175     // Run the script and keep track of the current recursion depth.
    176     v8::Local<v8::Value> result;
    177     {
    178         V8RecursionScope recursionScope(isolate);
    179         result = script->Run();
    180     }
    181 
    182     if (result.IsEmpty())
    183         return v8::Local<v8::Value>();
    184 
    185     crashIfV8IsDead();
    186     return result;
    187 }
    188 
    189 v8::Local<v8::Value> V8ScriptRunner::compileAndRunInternalScript(v8::Handle<v8::String> source, v8::Isolate* isolate, const String& fileName, const TextPosition& scriptStartPosition)
    190 {
    191     v8::Handle<v8::Script> script = V8ScriptRunner::compileScript(source, fileName, scriptStartPosition, 0, 0, isolate);
    192     if (script.IsEmpty())
    193         return v8::Local<v8::Value>();
    194 
    195     TRACE_EVENT0("v8", "v8.run");
    196     TRACE_EVENT_SCOPED_SAMPLING_STATE("v8", "V8Execution");
    197     V8RecursionScope::MicrotaskSuppression recursionScope(isolate);
    198     v8::Local<v8::Value> result = script->Run();
    199     crashIfV8IsDead();
    200     return result;
    201 }
    202 
    203 v8::Local<v8::Value> V8ScriptRunner::runCompiledInternalScript(v8::Handle<v8::Script> script, v8::Isolate* isolate)
    204 {
    205     TRACE_EVENT0("v8", "v8.run");
    206     TRACE_EVENT_SCOPED_SAMPLING_STATE("v8", "V8Execution");
    207     V8RecursionScope::MicrotaskSuppression recursionScope(isolate);
    208     v8::Local<v8::Value> result = script->Run();
    209     crashIfV8IsDead();
    210     return result;
    211 }
    212 
    213 v8::Local<v8::Value> V8ScriptRunner::callFunction(v8::Handle<v8::Function> function, ExecutionContext* context, v8::Handle<v8::Value> receiver, int argc, v8::Handle<v8::Value> args[], v8::Isolate* isolate)
    214 {
    215     TRACE_EVENT0("v8", "v8.callFunction");
    216     TRACE_EVENT_SCOPED_SAMPLING_STATE("v8", "V8Execution");
    217 
    218     if (V8RecursionScope::recursionLevel(isolate) >= kMaxRecursionDepth)
    219         return throwStackOverflowExceptionIfNeeded(isolate);
    220 
    221     RELEASE_ASSERT(!context->isIteratingOverObservers());
    222 
    223     V8RecursionScope recursionScope(isolate);
    224     v8::Local<v8::Value> result = function->Call(receiver, argc, args);
    225     crashIfV8IsDead();
    226     return result;
    227 }
    228 
    229 v8::Local<v8::Value> V8ScriptRunner::callInternalFunction(v8::Handle<v8::Function> function, v8::Handle<v8::Value> receiver, int argc, v8::Handle<v8::Value> args[], v8::Isolate* isolate)
    230 {
    231     TRACE_EVENT0("v8", "v8.callFunction");
    232     TRACE_EVENT_SCOPED_SAMPLING_STATE("v8", "V8Execution");
    233     V8RecursionScope::MicrotaskSuppression recursionScope(isolate);
    234     v8::Local<v8::Value> result = function->Call(receiver, argc, args);
    235     crashIfV8IsDead();
    236     return result;
    237 }
    238 
    239 v8::Local<v8::Value> V8ScriptRunner::callAsFunction(v8::Isolate* isolate, v8::Handle<v8::Object> object, v8::Handle<v8::Value> receiver, int argc, v8::Handle<v8::Value> args[])
    240 {
    241     TRACE_EVENT0("v8", "v8.callFunction");
    242     TRACE_EVENT_SCOPED_SAMPLING_STATE("v8", "V8Execution");
    243 
    244     V8RecursionScope::MicrotaskSuppression recursionScope(isolate);
    245     v8::Local<v8::Value> result = object->CallAsFunction(receiver, argc, args);
    246     crashIfV8IsDead();
    247     return result;
    248 }
    249 
    250 v8::Local<v8::Object> V8ScriptRunner::instantiateObject(v8::Isolate* isolate, v8::Handle<v8::ObjectTemplate> objectTemplate)
    251 {
    252     TRACE_EVENT0("v8", "v8.newInstance");
    253     TRACE_EVENT_SCOPED_SAMPLING_STATE("v8", "V8Execution");
    254 
    255     V8RecursionScope::MicrotaskSuppression scope(isolate);
    256     v8::Local<v8::Object> result = objectTemplate->NewInstance();
    257     crashIfV8IsDead();
    258     return result;
    259 }
    260 
    261 v8::Local<v8::Object> V8ScriptRunner::instantiateObject(v8::Isolate* isolate, v8::Handle<v8::Function> function, int argc, v8::Handle<v8::Value> argv[])
    262 {
    263     TRACE_EVENT0("v8", "v8.newInstance");
    264     TRACE_EVENT_SCOPED_SAMPLING_STATE("v8", "V8Execution");
    265 
    266     V8RecursionScope::MicrotaskSuppression scope(isolate);
    267     v8::Local<v8::Object> result = function->NewInstance(argc, argv);
    268     crashIfV8IsDead();
    269     return result;
    270 }
    271 
    272 v8::Local<v8::Object> V8ScriptRunner::instantiateObjectInDocument(v8::Isolate* isolate, v8::Handle<v8::Function> function, ExecutionContext* context, int argc, v8::Handle<v8::Value> argv[])
    273 {
    274     TRACE_EVENT0("v8", "v8.newInstance");
    275     TRACE_EVENT_SCOPED_SAMPLING_STATE("v8", "V8Execution");
    276     V8RecursionScope scope(isolate);
    277     v8::Local<v8::Object> result = function->NewInstance(argc, argv);
    278     crashIfV8IsDead();
    279     return result;
    280 }
    281 
    282 unsigned V8ScriptRunner::tagForParserCache()
    283 {
    284     return StringHash::hash(v8::V8::GetVersion()) * 2;
    285 }
    286 
    287 unsigned V8ScriptRunner::tagForCodeCache()
    288 {
    289     return StringHash::hash(v8::V8::GetVersion()) * 2 + 1;
    290 }
    291 
    292 } // namespace blink
    293