Fix JSI implementation to pass unit tests. Include windbg extension (#28)

* Fix JSI implementation to pass unit tests. Include windbg extension

* Add support for Symbol

* Don't try to run ARM unit tests

* Don't build debug extension in uwp

* pipeline syntax

* You can't use parameters in conditions

* yaml syntax

* timeout

* Add a debug line

* Try a separate boolean parameter
This commit is contained in:
tudorms 2020-09-21 09:32:21 -07:00 коммит произвёл Julio C. Rocha
Родитель 2ad32e83e8
Коммит ca521942e8
10 изменённых файлов: 377 добавлений и 243 удалений

Просмотреть файл

@ -1,6 +1,11 @@
parameters:
outputPath:
appPlatform:
- name: runUnitTests
default: true
type: boolean
- name: appPlatform
type: string
- name: outputPath
type: string
steps:
- task: PowerShell@2
@ -34,6 +39,27 @@ steps:
-Configuration:$(BuildConfiguration)
-AppPlatform:${{parameters.appPlatform}}
- powershell: |
Write-Host "##vso[task.setvariable variable=GoogleTestAdapterPath]$((Get-ChildItem "${env:ProgramFiles(x86)}\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\" -Recurse -Include GoogleTestAdapter.Core.dll).Directory.FullName)"
Write-Host "Set environment variable to ($env:GoogleTestAdapterPath)"
displayName: Set GoogleTestAdapterPath
- task: VSTest@2
displayName: Run Unit Tests
timeoutInMinutes: 2
inputs:
testSelector: testAssemblies
testAssemblyVer2: jsitests.exe
pathtoCustomTestAdapters: $(GoogleTestAdapterPath)
searchFolder: $(Build.SourcesDirectory)/build/v8build/v8/out/${{parameters.appPlatform}}/$(BuildPlatform)/$(BuildConfiguration)
runTestsInIsolation: true
platform: $(BuildPlatform)
configuration: $(BuildConfiguration)
publishRunAttachments: true
collectDumpOn: onAbortOnly
vsTestVersion: latest
condition: and(succeeded(), not(startsWith(variables.BuildPlatform, 'arm')), eq('${{ parameters.runUnitTests }}', true))
- task: PublishBuildArtifacts@1
displayName: "Publish artifacts"
inputs:

Просмотреть файл

@ -13,7 +13,7 @@ pool:
jobs:
- job: V8JsiBuild
timeoutInMinutes: 150
timeoutInMinutes: 180
displayName: Build the UWP v8jsi.dll binary for supported architectures and flavors
strategy:
matrix:
@ -52,6 +52,7 @@ jobs:
parameters:
outputPath: $(Build.ArtifactStagingDirectory)
appPlatform: $(AppPlatform)
runUnitTests: false
- job: V8JsiPublishNuget
dependsOn:

Просмотреть файл

@ -52,6 +52,7 @@ jobs:
parameters:
outputPath: $(Build.ArtifactStagingDirectory)
appPlatform: $(AppPlatform)
runUnitTests: true
- job: V8JsiPublishNuget
dependsOn:

Просмотреть файл

@ -1,4 +1,4 @@
{
"version": "0.64.2",
"version": "0.64.3",
"v8ref": "refs/branch-heads/8.5"
}

Просмотреть файл

@ -69,7 +69,13 @@ if (!$?) {
# We'll use 2x the number of cores for parallel execution
$numberOfThreads = [int]((Get-CimInstance Win32_ComputerSystem).NumberOfLogicalProcessors) * 2
& ninja -v -j $numberOfThreads -C $buildoutput v8jsi jsitests | Tee-Object -FilePath "$SourcesPath\build.log"
$ninjaExtraTargets = ""
if ($AppPlatform -ne "uwp") {
$ninjaExtraTargets += "v8windbg"
}
& ninja -v -j $numberOfThreads -C $buildoutput v8jsi jsitests $ninjaExtraTargets | Tee-Object -FilePath "$SourcesPath\build.log"
if (!$?) {
Write-Host "Build failure, check logs for details"
exit 1
@ -107,6 +113,11 @@ if (!$PSVersionTable.Platform -or $IsWindows) {
} else {
Copy-Item "$buildoutput\v8jsi.dll.pdb" -Destination "$OutputPath\lib\$AppPlatform\$Configuration\$Platform"
}
# Debugging extension
if ($AppPlatform -ne "uwp") {
Copy-Item "$buildoutput\v8windbg.dll" -Destination "$OutputPath\lib\$AppPlatform\$Configuration\$Platform"
}
}
else {
#TODO (#2): .so

Просмотреть файл

@ -1,8 +1,8 @@
diff --git a/BUILD.gn b/BUILD.gn
index 167e63503c..6ea5beaa99 100644
index 65d137cbed..56b1bd6779 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -3902,11 +3902,18 @@ v8_component("v8_libbase") {
@@ -3985,11 +3985,18 @@ v8_component("v8_libbase") {
defines += [ "_CRT_RAND_S" ] # for rand_s()
@ -26,7 +26,7 @@ index 167e63503c..6ea5beaa99 100644
data_deps += [ "//build/win:runtime_libs" ]
}
@@ -5068,3 +5075,9 @@ if (!build_with_chromium && v8_use_perfetto) {
@@ -5208,3 +5215,9 @@ if (!build_with_chromium && v8_use_perfetto) {
]
}
} # if (!build_with_chromium && v8_use_perfetto)
@ -38,10 +38,10 @@ index 167e63503c..6ea5beaa99 100644
+}
\ No newline at end of file
diff --git a/DEPS b/DEPS
index 7b38c3dcd0..494a187ed9 100644
index 4c02904d70..87ac0c20e0 100644
--- a/DEPS
+++ b/DEPS
@@ -540,4 +540,15 @@ hooks = [
@@ -549,4 +549,15 @@ hooks = [
'v8/tools/generate-header-include-checks.py',
],
},
@ -72,10 +72,10 @@ index b5fb1823b3..b5ddc1aba2 100644
# This is a cross-compile from an x64 host to either a non-Intel target
# cpu or a different target OS. Clang will always be used by default on the
diff --git a/include/v8.h b/include/v8.h
index 18d72f1630..fc47f43d1f 100644
index 13b40db28e..65335f4047 100644
--- a/include/v8.h
+++ b/include/v8.h
@@ -9068,6 +9068,11 @@ class V8_EXPORT Isolate {
@@ -9089,6 +9089,11 @@ class V8_EXPORT Isolate {
*/
MicrotasksPolicy GetMicrotasksPolicy() const;
@ -87,7 +87,7 @@ index 18d72f1630..fc47f43d1f 100644
/**
* Adds a callback to notify the host application after
* microtasks were run on the default MicrotaskQueue. The callback is
@@ -9094,6 +9099,10 @@ class V8_EXPORT Isolate {
@@ -9115,6 +9120,10 @@ class V8_EXPORT Isolate {
void RemoveMicrotasksCompletedCallback(
MicrotasksCompletedCallbackWithData callback, void* data = nullptr);
@ -227,3 +227,16 @@ index 12dfaf9572..b16b0b0020 100644
}
v8::PageAllocator* SetPlatformPageAllocatorForTesting(
diff --git a/tools/debug_helper/get-object-properties.cc b/tools/debug_helper/get-object-properties.cc
index 0e8fbf02a6..9eaf2ab7d3 100644
--- a/tools/debug_helper/get-object-properties.cc
+++ b/tools/debug_helper/get-object-properties.cc
@@ -331,7 +331,7 @@ class ReadStringVisitor : public TqObjectVisitor {
Isolate::FromRoot(GetIsolateRoot(heap_addresses_.any_heap_pointer)),
resource_data));
#else
- uintptr_t data_address = reinterpret_cast<uintptr_t>(resource_data);
+ uintptr_t data_address = static_cast<uintptr_t>(resource_data);
#endif // V8_COMPRESS_POINTERS
if (done_) return;
ReadStringCharacters<TChar>(object, data_address);

Просмотреть файл

@ -514,6 +514,10 @@ void V8Runtime::createHostObjectConstructorPerContext() {
nullptr,
nullptr,
HostObjectProxy::Enumerator));
// V8 distinguishes between named properties (strings and symbols) and indexed properties (number)
// Note that we're not passing an Enumerator here, otherwise we'd be double-counting since JSI doesn't make the distinction
hostObjectTemplate->SetIndexedPropertyHandler(HostObjectProxy::GetIndexed, HostObjectProxy::SetIndexed);
hostObjectTemplate->SetInternalFieldCount(1);
host_object_constructor_.Reset(
isolate_,
@ -539,9 +543,6 @@ void V8Runtime::initializeV8() {
int argc = static_cast<int>(argv.size());
v8::V8::SetFlagsFromCommandLine(&argc, const_cast<char **>(&argv[0]), false);
// Assuming Initialize can be called multiple times in process.
v8::V8::Initialize();
}
V8Runtime::V8Runtime(V8RuntimeArgs &&args) : args_(std::move(args)) {
@ -637,23 +638,6 @@ jsi::Value V8Runtime::evaluateJavaScript(
return result;
}
v8::Local<v8::Script> V8Runtime::GetCompiledScript(
const v8::Local<v8::String> &source,
const std::string &sourceURL) {
v8::Isolate *isolate = GetIsolate();
v8::TryCatch try_catch(isolate);
v8::MaybeLocal<v8::String> name = v8::String::NewFromUtf8(
isolate, reinterpret_cast<const char *>(sourceURL.c_str()));
v8::ScriptOrigin origin(name.ToLocalChecked());
v8::Local<v8::Context> context(isolate->GetCurrentContext());
v8::Local<v8::Script> script;
if (!v8::Script::Compile(context, source, &origin).ToLocal(&script)) {
ReportException(&try_catch);
}
return script;
}
// The callback that is invoked by v8 whenever the JavaScript 'print'
// function is called. Prints its arguments on stdout separated by
// spaces and ending with a newline.
@ -713,8 +697,6 @@ class ByteArrayBuffer final : public jsi::Buffer {
jsi::Value V8Runtime::ExecuteString(
const v8::Local<v8::String> &source,
const std::string &sourceURL) {
// jsi::Value V8Runtime::ExecuteString(v8::Local<v8::String> source, const
// jsi::Buffer* cache, v8::Local<v8::Value> name, bool report_exceptions) {
_ISOLATE_CONTEXT_ENTER
v8::TryCatch try_catch(isolate);
@ -789,35 +771,101 @@ jsi::Value V8Runtime::ExecuteString(
}
}
class V8PreparedJavaScript : public facebook::jsi::PreparedJavaScript {
public:
jsi::ScriptSignature scriptSignature;
jsi::JSRuntimeSignature runtimeSignature;
std::vector<uint8_t> buffer;
// What's the point of bytecode if we need to preserve the full source too?
// TODO: Figure out if there's a way to use the bytecode only with V8
std::shared_ptr<const facebook::jsi::Buffer> sourceBuffer;
};
std::shared_ptr<const facebook::jsi::PreparedJavaScript>
V8Runtime::prepareJavaScript(
const std::shared_ptr<const facebook::jsi::Buffer> &,
std::string) {
throw jsi::JSINativeException(
"V8Runtime::prepareJavaScript is not implemented!");
V8Runtime::prepareJavaScript(const std::shared_ptr<const facebook::jsi::Buffer> &buffer, std::string sourceURL) {
_ISOLATE_CONTEXT_ENTER
v8::TryCatch try_catch(isolate);
v8::Local<v8::String> source;
if (!v8::String::NewFromUtf8(isolate, reinterpret_cast<const char *>(buffer->data()),
v8::NewStringType::kNormal, static_cast<int>(buffer->size())).ToLocal(&source)) {
std::abort();
}
v8::Local<v8::String> urlV8String = v8::String::NewFromUtf8(isolate, reinterpret_cast<const char *>(sourceURL.c_str())).ToLocalChecked();
v8::ScriptOrigin origin(urlV8String);
v8::Local<v8::Context> context(isolate->GetCurrentContext());
v8::Local<v8::Script> script;
v8::ScriptCompiler::CompileOptions options = v8::ScriptCompiler::CompileOptions::kNoCompileOptions;
v8::ScriptCompiler::CachedData *cached_data = nullptr;
v8::ScriptCompiler::Source script_source(source, origin, cached_data);
if (!v8::ScriptCompiler::Compile(context, &script_source, options).ToLocal(&script)) {
ReportException(&try_catch);
return nullptr;
} else {
v8::ScriptCompiler::CachedData *codeCache = v8::ScriptCompiler::CreateCodeCache(script->GetUnboundScript());
auto prepared = std::make_shared<V8PreparedJavaScript>();
prepared->scriptSignature = {sourceURL, 1};
prepared->runtimeSignature = {"V8", 76};
prepared->buffer.assign(codeCache->data, codeCache->data + codeCache->length);
prepared->sourceBuffer = buffer;
return prepared;
}
}
facebook::jsi::Value V8Runtime::evaluatePreparedJavaScript(
const std::shared_ptr<const facebook::jsi::PreparedJavaScript> &) {
throw jsi::JSINativeException(
"V8Runtime::evaluatePreparedJavaScript is not implemented!");
facebook::jsi::Value V8Runtime::evaluatePreparedJavaScript(const std::shared_ptr<const facebook::jsi::PreparedJavaScript> & js) {
_ISOLATE_CONTEXT_ENTER
auto prepared = static_cast<const V8PreparedJavaScript*>(js.get());
v8::TryCatch try_catch(isolate);
v8::Local<v8::String> source;
if (!v8::String::NewFromUtf8(isolate, reinterpret_cast<const char *>(prepared->sourceBuffer->data()),
v8::NewStringType::kNormal, static_cast<int>(prepared->sourceBuffer->size())).ToLocal(&source)) {
std::abort();
}
v8::Local<v8::String> urlV8String = v8::String::NewFromUtf8(isolate, reinterpret_cast<const char *>(prepared->scriptSignature.url.c_str())).ToLocalChecked();
v8::ScriptOrigin origin(urlV8String);
v8::Local<v8::Context> context(isolate->GetCurrentContext());
v8::Local<v8::Script> script;
v8::ScriptCompiler::CompileOptions options = v8::ScriptCompiler::CompileOptions::kConsumeCodeCache;
v8::ScriptCompiler::CachedData *cached_data = new v8::ScriptCompiler::CachedData(prepared->buffer.data(), static_cast<int>(prepared->buffer.size()));
v8::ScriptCompiler::Source script_source(source, origin, cached_data);
if (!v8::ScriptCompiler::Compile(context, &script_source, options).ToLocal(&script)) {
ReportException(&try_catch);
return createValue(v8::Undefined(GetIsolate()));
} else {
v8::Local<v8::Value> result;
if (!script->Run(context).ToLocal(&result)) {
assert(try_catch.HasCaught());
ReportException(&try_catch);
return createValue(v8::Undefined(GetIsolate()));
} else {
assert(!try_catch.HasCaught());
return createValue(result);
}
}
}
void V8Runtime::ReportException(v8::TryCatch *try_catch) {
_ISOLATE_CONTEXT_ENTER
v8::String::Utf8Value exception(isolate, try_catch->Exception());
const char *exception_string = ToCString(exception);
v8::Local<v8::Message> message = try_catch->Message();
if (message.IsEmpty()) {
// V8 didn't provide any extra information about this error; just
// throw the exception.
throw jsi::JSError(*this, "<Unknown exception>");
v8::String::Utf8Value exception(isolate, try_catch->Exception());
throw jsi::JSError(*this, ToCString(exception));
} else {
// Print (filename):(line number): (message).
std::stringstream sstr;
v8::String::Utf8Value filename(
// Print (filename):(line number): (message) - this would differ from what JSI expects (it wants the plain stacktrace)
/*v8::String::Utf8Value filename(
isolate, message->GetScriptOrigin().ResourceName());
v8::Local<v8::Context> context(isolate->GetCurrentContext());
const char *filename_string = ToCString(filename);
@ -825,7 +873,7 @@ void V8Runtime::ReportException(v8::TryCatch *try_catch) {
sstr << filename_string << ":" << linenum << ": " << exception_string
<< std::endl;
// Print line of source code.
// Print line of source code
v8::String::Utf8Value sourceline(
isolate, message->GetSourceLine(context).ToLocalChecked());
const char *sourceline_string = ToCString(sourceline);
@ -840,10 +888,10 @@ void V8Runtime::ReportException(v8::TryCatch *try_catch) {
for (int i = start; i < end; i++) {
sstr << "^";
}
sstr << std::endl;
sstr << std::endl;*/
v8::Local<v8::Value> stack_trace_string;
if (try_catch->StackTrace(context).ToLocal(&stack_trace_string) &&
if (try_catch->StackTrace(context_.Get(isolate)).ToLocal(&stack_trace_string) &&
stack_trace_string->IsString() &&
v8::Local<v8::String>::Cast(stack_trace_string)->Length() > 0) {
v8::String::Utf8Value stack_trace(isolate, stack_trace_string);
@ -851,13 +899,30 @@ void V8Runtime::ReportException(v8::TryCatch *try_catch) {
sstr << stack_trace_string2 << std::endl;
}
throw jsi::JSError(*this, sstr.str());
v8::String::Utf8Value ex_message(isolate, message->Get());
std::string ex_messages = ToCString(ex_message);
if (ex_messages.rfind("Uncaught Error:", 0) == 0) {
// V8 adds an "Uncaught Error: " before any message string any time we read it, this strips it out.
ex_messages.erase(0, 16);
}
// V8 doesn't actually capture the current callstack (as we're outside of scope when this gets called)
// See also https://v8.dev/docs/stack-trace-api
if (ex_messages.find("Maximum call stack size exceeded") == std::string::npos) {
auto err = jsi::JSError(*this, ex_messages);
err.value().getObject(*this).setProperty(*this, "stack", facebook::jsi::String::createFromUtf8(*this, sstr.str()));
err.setStack(sstr.str());
throw err;
} else {
// If we're already in stack overflow, calling the Error constructor pushes it overboard
throw jsi::JSError(*this, ex_messages, sstr.str());
}
}
}
jsi::Object V8Runtime::global() {
_ISOLATE_CONTEXT_ENTER
return createObject(context_.Get(isolate)->Global());
return make<jsi::Object>(V8ObjectValue::make(context_.Get(isolate)->Global()));
}
std::string V8Runtime::description() {
@ -871,28 +936,6 @@ bool V8Runtime::isInspectable() {
return false;
}
V8Runtime::V8StringValue::V8StringValue(v8::Local<v8::String> str)
: v8String_(v8::Isolate::GetCurrent(), str) {}
void V8Runtime::V8StringValue::invalidate() {
delete this;
}
V8Runtime::V8StringValue::~V8StringValue() {
v8String_.Reset();
}
V8Runtime::V8ObjectValue::V8ObjectValue(v8::Local<v8::Object> obj)
: v8Object_(v8::Isolate::GetCurrent(), obj) {}
void V8Runtime::V8ObjectValue::invalidate() {
delete this;
}
V8Runtime::V8ObjectValue::~V8ObjectValue() {
v8Object_.Reset();
}
// Shallow clone
jsi::Runtime::PointerValue *V8Runtime::cloneString(
const jsi::Runtime::PointerValue *pv) {
@ -902,7 +945,7 @@ jsi::Runtime::PointerValue *V8Runtime::cloneString(
_ISOLATE_CONTEXT_ENTER
const V8StringValue *string = static_cast<const V8StringValue *>(pv);
return makeStringValue(string->v8String_.Get(GetIsolate()));
return V8StringValue::make(string->get(GetIsolate()));
}
jsi::Runtime::PointerValue *V8Runtime::cloneObject(
@ -913,7 +956,7 @@ jsi::Runtime::PointerValue *V8Runtime::cloneObject(
_ISOLATE_CONTEXT_ENTER
const V8ObjectValue *object = static_cast<const V8ObjectValue *>(pv);
return makeObjectValue(object->v8Object_.Get(GetIsolate()));
return V8ObjectValue::make(object->get(GetIsolate()));
}
jsi::Runtime::PointerValue *V8Runtime::clonePropNameID(
@ -924,17 +967,23 @@ jsi::Runtime::PointerValue *V8Runtime::clonePropNameID(
_ISOLATE_CONTEXT_ENTER
const V8StringValue *string = static_cast<const V8StringValue *>(pv);
return makeStringValue(string->v8String_.Get(GetIsolate()));
return V8StringValue::make(string->get(GetIsolate()));
}
jsi::Runtime::PointerValue *V8Runtime::cloneSymbol(
const jsi::Runtime::PointerValue *) {
throw jsi::JSINativeException("V8Runtime::cloneSymbol is not implemented!");
const jsi::Runtime::PointerValue *pv) {
if (!pv) {
return nullptr;
}
_ISOLATE_CONTEXT_ENTER
const V8PointerValue<v8::Symbol>* symbol = static_cast<const V8PointerValue<v8::Symbol>*>(pv);
return V8PointerValue<v8::Symbol>::make(symbol->get(GetIsolate()));
}
std::string V8Runtime::symbolToString(const jsi::Symbol &) {
throw jsi::JSINativeException(
"V8Runtime::symbolToString is not implemented!");
std::string V8Runtime::symbolToString(const jsi::Symbol &sym) {
_ISOLATE_CONTEXT_ENTER
return "Symbol(" + JSStringToSTLString(GetIsolate(), v8::Local<v8::String>::Cast(symbolRef(sym)->Description())) + ")";
}
jsi::PropNameID V8Runtime::createPropNameIDFromAscii(
@ -953,7 +1002,8 @@ jsi::PropNameID V8Runtime::createPropNameIDFromAscii(
throw jsi::JSError(*this, strstream.str());
}
auto res = createPropNameID(v8String);
auto res = make<jsi::PropNameID>(
V8StringValue::make(v8::Local<v8::String>::Cast(v8String)));
return res;
}
@ -973,13 +1023,15 @@ jsi::PropNameID V8Runtime::createPropNameIDFromUtf8(
throw jsi::JSError(*this, strstream.str());
}
auto res = createPropNameID(v8String);
auto res = make<jsi::PropNameID>(
V8StringValue::make(v8::Local<v8::String>::Cast(v8String)));
return res;
}
jsi::PropNameID V8Runtime::createPropNameIDFromString(const jsi::String &str) {
_ISOLATE_CONTEXT_ENTER
return createPropNameID(stringRef(str));
return make<jsi::PropNameID>(
V8StringValue::make(v8::Local<v8::String>::Cast(stringRef(str))));
}
std::string V8Runtime::utf8(const jsi::PropNameID &sym) {
@ -1012,7 +1064,7 @@ jsi::String V8Runtime::createStringFromUtf8(const uint8_t *str, size_t length) {
throw jsi::JSError(*this, "V8 string creation failed.");
}
jsi::String jsistr = createString(v8string);
jsi::String jsistr = make<jsi::String>(V8StringValue::make(v8string));
return jsistr;
}
@ -1023,7 +1075,7 @@ std::string V8Runtime::utf8(const jsi::String &str) {
jsi::Object V8Runtime::createObject() {
_ISOLATE_CONTEXT_ENTER
return createObject(v8::Object::New(GetIsolate()));
return make<jsi::Object>(V8ObjectValue::make(v8::Object::New(GetIsolate())));
}
jsi::Object V8Runtime::createObject(
@ -1045,7 +1097,7 @@ jsi::Object V8Runtime::createObject(
AddHostObjectLifetimeTracker(std::make_shared<HostObjectLifetimeTracker>(
*this, newObject, hostObjectProxy));
return createObject(newObject);
return make<jsi::Object>(V8ObjectValue::make(newObject));
}
std::shared_ptr<jsi::HostObject> V8Runtime::getHostObject(
@ -1174,8 +1226,13 @@ bool V8Runtime::isHostObject(const jsi::Object &obj) const {
jsi::Array V8Runtime::getPropertyNames(const jsi::Object &obj) {
_ISOLATE_CONTEXT_ENTER
v8::Local<v8::Array> propNames =
objectRef(obj)->GetPropertyNames(context_.Get(isolate_)).ToLocalChecked();
return createObject(propNames).getArray(*this);
objectRef(obj)->GetPropertyNames(
context_.Get(isolate_),
v8::KeyCollectionMode::kIncludePrototypes,
static_cast<v8::PropertyFilter>(v8::ONLY_ENUMERABLE | v8::SKIP_SYMBOLS),
v8::IndexFilter::kIncludeIndices,
v8::KeyConversionMode::kConvertToString).ToLocalChecked();
return make<jsi::Object>(V8ObjectValue::make(propNames)).getArray(*this);
}
jsi::WeakObject V8Runtime::createWeakObject(const jsi::Object &) {
@ -1188,7 +1245,7 @@ jsi::Value V8Runtime::lockWeakObject(jsi::WeakObject &) {
jsi::Array V8Runtime::createArray(size_t length) {
_ISOLATE_CONTEXT_ENTER
return createObject(v8::Array::New(GetIsolate(), static_cast<int>(length)))
return make<jsi::Object>(V8ObjectValue::make(v8::Array::New(GetIsolate(), static_cast<int>(length))))
.getArray(*this);
}
@ -1228,15 +1285,17 @@ jsi::Function V8Runtime::createFunctionFromHostFunction(
HostFunctionProxy::HostFunctionCallback,
v8::Local<v8::External>::New(
GetIsolate(),
v8::External::New(GetIsolate(), hostFunctionProxy)))
v8::External::New(GetIsolate(), hostFunctionProxy)), paramCount)
.ToLocal(&newFunction)) {
throw jsi::JSError(*this, "Creation of HostFunction failed.");
}
newFunction->SetName(v8::Local<v8::String>::Cast(valueRef(name)));
AddHostObjectLifetimeTracker(std::make_shared<HostObjectLifetimeTracker>(
*this, newFunction, hostFunctionProxy));
return createObject(newFunction).getFunction(*this);
return make<jsi::Object>(V8ObjectValue::make(newFunction)).getFunction(*this);
}
bool V8Runtime::isHostFunction(const jsi::Function &obj) const {
@ -1319,8 +1378,9 @@ bool V8Runtime::strictEquals(const jsi::Object &a, const jsi::Object &b) const {
return objectRef(a)->StrictEquals(objectRef(b));
}
bool V8Runtime::strictEquals(const jsi::Symbol &, const jsi::Symbol &) const {
throw jsi::JSINativeException("Not implemented!");
bool V8Runtime::strictEquals(const jsi::Symbol &a, const jsi::Symbol &b) const {
_ISOLATE_CONTEXT_ENTER
return symbolRef(a)->StrictEquals(symbolRef(b));
}
bool V8Runtime::instanceOf(const jsi::Object &o, const jsi::Function &f) {
@ -1330,32 +1390,6 @@ bool V8Runtime::instanceOf(const jsi::Object &o, const jsi::Function &f) {
.ToChecked();
}
jsi::Runtime::PointerValue *V8Runtime::makeStringValue(
v8::Local<v8::String> string) const {
return new V8StringValue(string);
}
jsi::String V8Runtime::createString(v8::Local<v8::String> str) const {
return make<jsi::String>(makeStringValue(str));
}
jsi::PropNameID V8Runtime::createPropNameID(v8::Local<v8::Value> str) {
_ISOLATE_CONTEXT_ENTER
return make<jsi::PropNameID>(
makeStringValue(v8::Local<v8::String>::Cast(str)));
}
jsi::Runtime::PointerValue *V8Runtime::makeObjectValue(
v8::Local<v8::Object> objectRef) const {
_ISOLATE_CONTEXT_ENTER
return new V8ObjectValue(objectRef);
}
jsi::Object V8Runtime::createObject(v8::Local<v8::Object> obj) const {
_ISOLATE_CONTEXT_ENTER
return make<jsi::Object>(makeObjectValue(obj));
}
jsi::Value V8Runtime::createValue(v8::Local<v8::Value> value) const {
_ISOLATE_CONTEXT_ENTER
if (value->IsInt32()) {
@ -1371,13 +1405,13 @@ jsi::Value V8Runtime::createValue(v8::Local<v8::Value> value) const {
return jsi::Value();
} else if (value.IsEmpty() || value->IsNull()) {
return jsi::Value(nullptr);
}
else if (value->IsString()) {
} else if (value->IsString()) {
// Note :: Non copy create
return createString(v8::Local<v8::String>::Cast(value));
return make<jsi::String>(V8StringValue::make(v8::Local<v8::String>::Cast(value)));
} else if (value->IsObject()) {
return createObject(v8::Local<v8::Object>::Cast(value));
return make<jsi::Object>(V8ObjectValue::make(v8::Local<v8::Object>::Cast(value)));
} else if (value->IsSymbol()) {
return make<jsi::Symbol>(V8PointerValue<v8::Symbol>::make(v8::Local<v8::Symbol>::Cast(value)));
} else {
// What are you?
std::abort();
@ -1400,36 +1434,14 @@ v8::Local<v8::Value> V8Runtime::valueRef(const jsi::Value &value) {
return handle_scope.Escape(stringRef(value.asString(*this)));
} else if (value.isObject()) {
return handle_scope.Escape(objectRef(value.getObject(*this)));
} else if (value.isSymbol()) {
return handle_scope.Escape(symbolRef(value.getSymbol(*this)));
} else {
// What are you?
std::abort();
}
}
v8::Local<v8::String> V8Runtime::stringRef(const jsi::String &str) {
v8::EscapableHandleScope handle_scope(v8::Isolate::GetCurrent());
const V8StringValue *v8StringValue =
static_cast<const V8StringValue *>(getPointerValue(str));
return handle_scope.Escape(
v8StringValue->v8String_.Get(v8::Isolate::GetCurrent()));
}
v8::Local<v8::Value> V8Runtime::valueRef(const jsi::PropNameID &sym) {
v8::EscapableHandleScope handle_scope(v8::Isolate::GetCurrent());
const V8StringValue *v8StringValue =
static_cast<const V8StringValue *>(getPointerValue(sym));
return handle_scope.Escape(
v8StringValue->v8String_.Get(v8::Isolate::GetCurrent()));
}
v8::Local<v8::Object> V8Runtime::objectRef(const jsi::Object &obj) {
v8::EscapableHandleScope handle_scope(v8::Isolate::GetCurrent());
const V8ObjectValue *v8ObjectValue =
static_cast<const V8ObjectValue *>(getPointerValue(obj));
return handle_scope.Escape(
v8ObjectValue->v8Object_.Get(v8::Isolate::GetCurrent()));
}
std::unique_ptr<jsi::Runtime> makeV8Runtime(V8RuntimeArgs &&args) {
return std::make_unique<V8Runtime>(std::move(args));
}

Просмотреть файл

@ -103,6 +103,8 @@ class V8PlatformHolder {
platform_s_ = std::make_unique<V8Platform>(true);
#endif
v8::V8::InitializePlatform(platform_s_.get());
v8::V8::Initialize();
}
}
}
@ -113,6 +115,7 @@ class V8PlatformHolder {
if (--use_count_s_ == 0) {
// We cannot shutdown the platform once created because V8 internally references bits of the platform from process-globals
// This cannot be worked around, the design of V8 is not currently embedder-friendly
// v8::V8::Dispose();
v8::V8::ShutdownPlatform();
platform_s_ = nullptr;
}
@ -205,9 +208,9 @@ class V8Runtime : public facebook::jsi::Runtime {
};
class HostObjectProxy : public IHostProxy {
public:
static void Get(
v8::Local<v8::Name> v8PropName,
private:
static void GetInternal(
std::string propName,
const v8::PropertyCallbackInfo<v8::Value> &info) {
v8::Local<v8::External> data =
v8::Local<v8::External>::Cast(info.This()->GetInternalField(0));
@ -221,31 +224,46 @@ class V8Runtime : public facebook::jsi::Runtime {
std::shared_ptr<facebook::jsi::HostObject> hostObject =
hostObjectProxy->hostObject_;
v8::Local<v8::String> propNameStr =
v8::Local<v8::String>::Cast(v8PropName);
facebook::jsi::Value result;
try {
result = hostObject->get(runtime, runtime.createPropNameIDFromUtf8(
reinterpret_cast<uint8_t *>(&propName[0]), propName.length()));
} catch (const facebook::jsi::JSError& error) {
info.GetReturnValue().Set(v8::Undefined(info.GetIsolate()));
char buffer[512];
propNameStr->WriteUtf8(info.GetIsolate(), buffer);
// Schedule to throw the exception back to JS.
info.GetIsolate()->ThrowException(runtime.valueRef(error.value()));
return;
} catch (const std::exception& ex) {
info.GetReturnValue().Set(v8::Undefined(info.GetIsolate()));
// std::string propName;
// propName.resize(propNameStr->Utf8Length(info.GetIsolate()));
// propNameStr->WriteUtf8(info.GetIsolate(), &propName[0]);
// Schedule to throw the exception back to JS.
v8::Local<v8::String> message =
v8::String::NewFromUtf8(info.GetIsolate(), ex.what(),
v8::NewStringType::kNormal)
.ToLocalChecked();
info.GetIsolate()->ThrowException(v8::Exception::Error(message));
return;
} catch (...) {
info.GetReturnValue().Set(v8::Undefined(info.GetIsolate()));
// facebook::jsi::PropNameID propNameId =
// runtime.createPropNameIDFromUtf8(reinterpret_cast<uint8_t*>(&propName[0]),
// propName.length());
facebook::jsi::PropNameID propNameId = runtime.createPropNameIDFromUtf8(
reinterpret_cast<uint8_t *>(buffer),
propNameStr->Utf8Length(info.GetIsolate()));
// Schedule to throw the exception back to JS.
v8::Local<v8::String> message =
v8::String::NewFromOneByte(
info.GetIsolate(),
reinterpret_cast<const uint8_t*>(
"<Unknown exception in host function callback>"),
v8::NewStringType::kNormal)
.ToLocalChecked();
info.GetIsolate()->ThrowException(v8::Exception::Error(message));
return;
}
v8::Local<v8::Value> retValue;
{ retValue = runtime.valueRef(hostObject->get(runtime, propNameId)); }
info.GetReturnValue().Set(retValue);
info.GetReturnValue().Set(runtime.valueRef(result));
}
static void Set(
v8::Local<v8::Name> v8PropName,
static void SetInternal(
std::string propName,
v8::Local<v8::Value> value,
const v8::PropertyCallbackInfo<v8::Value> &info) {
v8::Local<v8::External> data =
@ -260,22 +278,76 @@ class V8Runtime : public facebook::jsi::Runtime {
std::shared_ptr<facebook::jsi::HostObject> hostObject =
hostObjectProxy->hostObject_;
v8::Local<v8::String> propNameStr =
v8::Local<v8::String>::Cast(v8PropName);
try {
hostObject->set(
runtime,
runtime.createPropNameIDFromUtf8(
reinterpret_cast<uint8_t *>(&propName[0]), propName.length()),
runtime.createValue(value));
} catch (const facebook::jsi::JSError& error) {
// Schedule to throw the exception back to JS.
info.GetIsolate()->ThrowException(runtime.valueRef(error.value()));
} catch (const std::exception& ex) {
// Schedule to throw the exception back to JS.
v8::Local<v8::String> message =
v8::String::NewFromUtf8(info.GetIsolate(), ex.what(),
v8::NewStringType::kNormal)
.ToLocalChecked();
info.GetIsolate()->ThrowException(v8::Exception::Error(message));
} catch (...) {
// Schedule to throw the exception back to JS.
v8::Local<v8::String> message =
v8::String::NewFromOneByte(
info.GetIsolate(),
reinterpret_cast<const uint8_t*>(
"<Unknown exception in host function callback>"),
v8::NewStringType::kNormal)
.ToLocalChecked();
info.GetIsolate()->ThrowException(v8::Exception::Error(message));
}
}
public:
static void Get(
v8::Local<v8::Name> v8PropName,
const v8::PropertyCallbackInfo<v8::Value> &info) {
v8::Local<v8::String> propNameStr = v8::Local<v8::String>::Cast(v8PropName);
std::string propName;
propName.resize(propNameStr->Utf8Length(info.GetIsolate()));
propNameStr->WriteUtf8(info.GetIsolate(), &propName[0]);
GetInternal(propName, info);
}
hostObject->set(
runtime,
runtime.createPropNameIDFromUtf8(
reinterpret_cast<uint8_t *>(&propName[0]), propName.length()),
runtime.createValue(value));
static void GetIndexed(
uint32_t index,
const v8::PropertyCallbackInfo<v8::Value> &info) {
std::string propName = std::to_string(index);
GetInternal(propName, info);
}
static void Set(
v8::Local<v8::Name> v8PropName,
v8::Local<v8::Value> value,
const v8::PropertyCallbackInfo<v8::Value> &info) {
v8::Local<v8::String> propNameStr = v8::Local<v8::String>::Cast(v8PropName);
std::string propName;
propName.resize(propNameStr->Utf8Length(info.GetIsolate()));
propNameStr->WriteUtf8(info.GetIsolate(), &propName[0]);
SetInternal(propName, value, info);
}
static void SetIndexed(
uint32_t index,
v8::Local<v8::Value> value,
const v8::PropertyCallbackInfo<v8::Value> &info) {
std::string propName = std::to_string(index);
SetInternal(propName, value, info);
}
static void Enumerator(const v8::PropertyCallbackInfo<v8::Array> &info) {
v8::Local<v8::External> data = v8::Local<v8::External>::Cast(info.Data());
v8::Local<v8::External> data = v8::Local<v8::External>::Cast(info.This()->GetInternalField(0));
HostObjectProxy *hostObjectProxy =
reinterpret_cast<HostObjectProxy *>(data->Value());
@ -346,28 +418,28 @@ class V8Runtime : public facebook::jsi::Runtime {
} catch (const facebook::jsi::JSError &error) {
callbackInfo.GetReturnValue().Set(v8::Undefined(isolate));
// Schedule to throw the exception back to JS.
// Schedule to throw the exception back to JS
isolate->ThrowException(runtime.valueRef(error.value()));
return;
} catch (const std::exception &ex) {
callbackInfo.GetReturnValue().Set(v8::Undefined(isolate));
// Schedule to throw the exception back to JS.
// Schedule to throw the exception back to JS
std::string errMessage = std::string("Exception in HostFunction: ") + ex.what();
v8::Local<v8::String> message =
v8::String::NewFromUtf8(
isolate, ex.what(), v8::NewStringType::kNormal)
isolate, errMessage.c_str(), v8::NewStringType::kNormal)
.ToLocalChecked();
isolate->ThrowException(v8::Exception::Error(message));
return;
} catch (...) {
callbackInfo.GetReturnValue().Set(v8::Undefined(isolate));
// Schedule to throw the exception back to JS.
// Schedule to throw the exception back to JS
v8::Local<v8::String> message =
v8::String::NewFromOneByte(
isolate,
reinterpret_cast<const uint8_t *>(
"<Unknown exception in host function callback>"),
reinterpret_cast<const uint8_t *>("Exception in HostFunction: <unknown>"),
v8::NewStringType::kNormal)
.ToLocalChecked();
isolate->ThrowException(v8::Exception::Error(message));
@ -403,30 +475,37 @@ class V8Runtime : public facebook::jsi::Runtime {
V8Runtime &runtime_;
};
class V8StringValue final : public PointerValue {
V8StringValue(v8::Local<v8::String> str);
~V8StringValue();
template<typename T>
class V8PointerValue final : public PointerValue {
static V8PointerValue<T>* make(v8::Local<T> objectRef) {
return new V8PointerValue<T>(objectRef);
}
void invalidate() override;
V8PointerValue(v8::Local<T> obj) :
v8Object_(v8::Isolate::GetCurrent(), obj)
{}
v8::Persistent<v8::String> v8String_;
~V8PointerValue() {
v8Object_.Reset();
}
void invalidate() override {
delete this;
}
v8::Local<T> get(v8::Isolate* isolate) const {
return v8Object_.Get(isolate);
}
private:
v8::Persistent<T> v8Object_;
protected:
friend class V8Runtime;
};
class V8ObjectValue final : public PointerValue {
V8ObjectValue(v8::Local<v8::Object> obj);
~V8ObjectValue();
void invalidate() override;
v8::Persistent<v8::Object> v8Object_;
protected:
friend class V8Runtime;
};
using V8StringValue = V8PointerValue<v8::String>;
using V8ObjectValue = V8PointerValue<v8::Object>;
class ExternalOwningOneByteStringResource
: public v8::String::ExternalOneByteStringResource {
@ -454,10 +533,10 @@ class V8Runtime : public facebook::jsi::Runtime {
std::string symbolToString(const facebook::jsi::Symbol &) override;
PointerValue *cloneString(const Runtime::PointerValue *pv) override;
PointerValue *cloneObject(const Runtime::PointerValue *pv) override;
PointerValue *clonePropNameID(const Runtime::PointerValue *pv) override;
PointerValue *cloneSymbol(const PointerValue *) override;
PointerValue *cloneString(const PointerValue *pv) override;
PointerValue *cloneObject(const PointerValue *pv) override;
PointerValue *clonePropNameID(const PointerValue *pv) override;
PointerValue *cloneSymbol(const PointerValue *pv) override;
facebook::jsi::PropNameID createPropNameIDFromAscii(
const char *str,
@ -585,24 +664,7 @@ class V8Runtime : public facebook::jsi::Runtime {
private:
v8::Local<v8::Context> CreateContext(v8::Isolate *isolate);
// Methods to compile and execute JS script.
v8::ScriptCompiler::CachedData *TryLoadCachedData(const std::string &path);
void PersistCachedData(
std::unique_ptr<v8::ScriptCompiler::CachedData> cachedData,
const std::string &path);
v8::Local<v8::Script> GetCompiledScriptFromCache(
const v8::Local<v8::String> &source,
const std::string &sourceURL);
v8::Local<v8::Script> GetCompiledScript(
const v8::Local<v8::String> &source,
const std::string &sourceURL);
facebook::jsi::Value ExecuteString(
v8::Local<v8::String> source,
const facebook::jsi::Buffer *cache,
v8::Local<v8::Value> name,
bool report_exceptions);
// Methods to compile and execute JS script
facebook::jsi::Value ExecuteString(
const v8::Local<v8::String> &source,
const std::string &sourceURL);
@ -619,24 +681,21 @@ class V8Runtime : public facebook::jsi::Runtime {
void createHostObjectConstructorPerContext();
// Basically convenience casts
static v8::Local<v8::String> stringRef(const facebook::jsi::String &str);
static v8::Local<v8::Value> valueRef(const facebook::jsi::PropNameID &sym);
static v8::Local<v8::Object> objectRef(const facebook::jsi::Object &obj);
template<typename T>
static v8::Local<T> pvRef(const PointerValue* pv) {
v8::EscapableHandleScope handle_scope(v8::Isolate::GetCurrent());
const V8PointerValue<T> *v8PValue = static_cast<const V8PointerValue<T>*>(pv);
return handle_scope.Escape(v8PValue->get(v8::Isolate::GetCurrent()));
}
static v8::Local<v8::String> stringRef(const facebook::jsi::String &str) { return pvRef<v8::String>(getPointerValue(str)); }
static v8::Local<v8::Value> valueRef(const facebook::jsi::PropNameID &sym) { return pvRef<v8::Value>(getPointerValue(sym)); }
static v8::Local<v8::Object> objectRef(const facebook::jsi::Object &obj) { return pvRef<v8::Object>(getPointerValue(obj)); }
static v8::Local<v8::Symbol> symbolRef(const facebook::jsi::Symbol &sym) { return pvRef<v8::Symbol>(getPointerValue(sym)); }
v8::Local<v8::Value> valueRef(const facebook::jsi::Value &value);
facebook::jsi::Value createValue(v8::Local<v8::Value> value) const;
// Factory methods for creating String/Object
facebook::jsi::String createString(v8::Local<v8::String> stringRef) const;
facebook::jsi::PropNameID createPropNameID(v8::Local<v8::Value> propValRef);
facebook::jsi::Object createObject(v8::Local<v8::Object> objectRef) const;
// Used by factory methods and clone methods
facebook::jsi::Runtime::PointerValue *makeStringValue(
v8::Local<v8::String> str) const;
facebook::jsi::Runtime::PointerValue *makeObjectValue(
v8::Local<v8::Object> obj) const;
#ifdef _WIN32
std::unique_ptr<inspector::Agent> inspector_agent_;
#endif

Просмотреть файл

@ -1260,6 +1260,13 @@ class JSI_EXPORT JSError : public JSIException {
return stack_;
}
/// In V8's case, creating an Error object in JS doesn't record the callstack
/// To preserve it, we need a way to manually add the stack here and on the JS side
void setStack(std::string stack) {
stack_ = std::move(stack);
what_ = message_ + "\n\n" + stack_;
}
const std::string& getMessage() const {
return message_;
}

Просмотреть файл

@ -721,8 +721,12 @@ TEST_P(JSITest, HostFunctionTest) {
.getString(rt)
.utf8(rt),
"A cat was called with std::function::target");
// Disabling these tests for V8 because we'd incur unnecessary cost to implement the functionality
#if 0
EXPECT_TRUE(callable.isHostFunction(rt));
EXPECT_NE(callable.getHostFunction(rt).target<Callable>(), nullptr);
#endif
std::string strval = "strval1";
auto getter = Object(rt);