blob: d388b640619184832a70b4ad7d5fc80b452b0a69 [file] [log] [blame]
/*
* Copyright 2014 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "cobalt/script/javascriptcore/jsc_global_environment.h"
#include <algorithm>
#include <string>
#include "base/debug/trace_event.h"
#include "base/string_util.h"
#include "base/stringprintf.h"
#include "cobalt/base/polymorphic_downcast.h"
#include "cobalt/script/javascriptcore/jsc_engine.h"
#include "cobalt/script/javascriptcore/jsc_object_handle_holder.h"
#include "cobalt/script/javascriptcore/jsc_source_code.h"
#include "cobalt/script/javascriptcore/util/exception_helpers.h"
#include "third_party/WebKit/Source/JavaScriptCore/config.h"
#include "third_party/WebKit/Source/JavaScriptCore/heap/StrongInlines.h"
#include "third_party/WebKit/Source/JavaScriptCore/interpreter/Interpreter.h"
#include "third_party/WebKit/Source/JavaScriptCore/runtime/Completion.h"
namespace cobalt {
namespace script {
namespace javascriptcore {
void JSCGlobalEnvironment::PreventGarbageCollection(
const scoped_refptr<Wrappable>& wrappable) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(global_object_);
JSC::JSLockHolder lock(global_object_->globalData());
JSC::JSObject* wrapper = global_object_->wrapper_factory()->GetWrapper(
global_object_.get(), wrappable);
global_object_->object_cache()->CacheJSObject(wrapper);
}
JSCGlobalEnvironment::~JSCGlobalEnvironment() {
if (global_object_.get()) {
global_object_->setReportEvalCallback(NULL);
}
}
void JSCGlobalEnvironment::CreateGlobalObject() {
DCHECK(thread_checker_.CalledOnValidThread());
JSC::JSLockHolder lock(engine_->global_data());
JSCGlobalObject* global_object = JSCGlobalObject::Create(
engine_->global_data(), engine_->script_object_registry());
SetGlobalObject(global_object);
}
void JSCGlobalEnvironment::AllowGarbageCollection(
const scoped_refptr<Wrappable>& wrappable) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(global_object_);
JSC::JSLockHolder lock(global_object_->globalData());
JSC::JSObject* wrapper = global_object_->wrapper_factory()->GetWrapper(
global_object_.get(), wrappable);
global_object_->object_cache()->UncacheJSObject(wrapper);
}
void JSCGlobalEnvironment::DisableEval(const std::string& message) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(global_object_);
global_object_->setEvalEnabled(false, message.c_str());
}
void JSCGlobalEnvironment::EnableEval() {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(global_object_);
global_object_->setEvalEnabled(true);
}
void JSCGlobalEnvironment::Bind(const std::string& identifier,
const scoped_refptr<Wrappable>& impl) {
DCHECK(thread_checker_.CalledOnValidThread());
javascriptcore::JSCGlobalObject* jsc_global_object =
base::polymorphic_downcast<javascriptcore::JSCGlobalEnvironment*>(this)
->global_object();
JSC::JSLockHolder lock(&jsc_global_object->globalData());
JSC::JSObject* wrapper =
jsc_global_object->wrapper_factory()->GetWrapper(jsc_global_object, impl);
JSC::Identifier jsc_identifier =
JSC::Identifier(jsc_global_object->globalExec(), identifier.c_str());
// Make sure the property we are binding doesn't already exist.
DCHECK(!jsc_global_object->hasOwnProperty(jsc_global_object->globalExec(),
jsc_identifier));
// Add the property to the global object.
jsc_global_object->putDirect(jsc_global_object->globalData(), jsc_identifier,
wrapper);
}
bool JSCGlobalEnvironment::EvaluateScript(
const scoped_refptr<SourceCode>& source_code, std::string* out_result) {
DCHECK(thread_checker_.CalledOnValidThread());
JSC::JSLockHolder lock(global_object_->globalData());
JSC::JSValue result;
bool success = EvaluateScriptInternal(source_code, &result);
if (out_result) {
if (!success) {
*out_result =
util::GetExceptionString(global_object_->globalExec(), result);
} else {
WTF::String return_string =
result.toWTFString(global_object_->globalExec());
*out_result = return_string.utf8().data();
}
}
return success;
}
bool JSCGlobalEnvironment::EvaluateScript(
const scoped_refptr<SourceCode>& source_code,
const scoped_refptr<Wrappable>& owning_object,
base::optional<OpaqueHandleHolder::Reference>* out_opaque_handle) {
DCHECK(thread_checker_.CalledOnValidThread());
JSC::JSLockHolder lock(global_object_->globalData());
JSC::JSValue result;
if (!EvaluateScriptInternal(source_code, &result)) {
std::string exception =
util::GetExceptionString(global_object_->globalExec(), result);
DLOG(ERROR) << exception;
return false;
}
JSCObjectHandle jsc_object_handle(JSC::asObject(result));
JSCObjectHandleHolder jsc_object_holder(
JSCObjectHandle(JSC::asObject(result)),
global_object_->script_object_registry());
out_opaque_handle->emplace(owning_object.get(), jsc_object_holder);
return true;
}
std::vector<StackFrame> JSCGlobalEnvironment::GetStackTrace(int max_frames) {
DCHECK(thread_checker_.CalledOnValidThread());
return util::GetStackTrace(global_object_->globalExec(), max_frames);
}
void JSCGlobalEnvironment::reportEval() {
if (!report_eval_cb_.is_null()) {
report_eval_cb_.Run();
}
}
void JSCGlobalEnvironment::SetGlobalObject(JSCGlobalObject* jsc_global_object) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK_EQ(reinterpret_cast<uintptr_t>(global_object_.get()), NULL);
global_object_ =
JSC::Strong<JSCGlobalObject>(*engine_->global_data(), jsc_global_object);
global_object_->setReportEvalCallback(this);
// Disable eval() unless it's explicitly enabled (by CSP, for example).
DisableEval("eval() is disabled by default.");
}
bool JSCGlobalEnvironment::EvaluateScriptInternal(
const scoped_refptr<SourceCode>& source_code, JSC::JSValue* out_result) {
TRACE_EVENT0("cobalt::script::javascriptcore",
"JSCGlobalEnvironment::EvaluateScriptInternal");
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(global_object_);
JSC::ExecState* exec = global_object_->globalExec();
// Downcast to the JSC implementation of the SourceCode interface.
JSCSourceCode* jsc_source_code =
base::polymorphic_downcast<JSCSourceCode*>(source_code.get());
// Evaluate the source code and gather the return value and exception if
// one occurred.
JSC::JSValue exception;
JSC::JSValue return_value = JSC::evaluate(exec, jsc_source_code->source(),
JSC::JSValue(), // thisValue
&exception);
if (exception) {
*out_result = exception;
exec->clearException();
return false;
} else {
*out_result = return_value;
return true;
}
}
} // namespace javascriptcore
} // namespace script
} // namespace cobalt