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