Skip to content

Commit

Permalink
fix(core): support onerror for js
Browse files Browse the repository at this point in the history
  • Loading branch information
etkmao committed Sep 1, 2023
1 parent 9a78a1b commit 357c7d3
Show file tree
Hide file tree
Showing 17 changed files with 310 additions and 25 deletions.
20 changes: 4 additions & 16 deletions driver/js/include/driver/scope.h
Original file line number Diff line number Diff line change
Expand Up @@ -275,22 +275,7 @@ class Scope : public std::enable_shared_from_this<Scope> {

inline void SetUriLoader(std::weak_ptr<UriLoader> loader) {
loader_ = loader;
auto the_loader = loader_.lock();
if (the_loader) {
the_loader->SetRequestTimePerformanceCallback([WEAK_THIS](const string_view& uri, const TimePoint& start, const TimePoint& end) {
DEFINE_AND_CHECK_SELF(Scope)
auto runner = self->GetTaskRunner();
if (runner) {
auto task = [weak_this, uri, start, end]() {
DEFINE_AND_CHECK_SELF(Scope)
auto entry = self->GetPerformance()->PerformanceResource(uri);
entry->SetLoadSourceStart(start);
entry->SetLoadSourceEnd(end);
};
runner->PostTask(std::move(task));
}
});
}
SetCallbackForUriLoader();
}

inline std::weak_ptr<UriLoader> GetUriLoader() { return loader_; }
Expand Down Expand Up @@ -325,6 +310,8 @@ class Scope : public std::enable_shared_from_this<Scope> {
return performance_;
}

void HandleUriLoaderError(const string_view& uri, const int32_t ret_code, const string_view& error_msg);

#ifdef ENABLE_INSPECTOR
inline void SetDevtoolsDataSource(std::shared_ptr<hippy::devtools::DevtoolsDataSource> devtools_data_source) {
devtools_data_source_ = devtools_data_source;
Expand Down Expand Up @@ -470,6 +457,7 @@ class Scope : public std::enable_shared_from_this<Scope> {
friend class Engine;
void BindModule();
void Bootstrap();
void SetCallbackForUriLoader();

private:
std::weak_ptr<Engine> engine_;
Expand Down
6 changes: 6 additions & 0 deletions driver/js/include/driver/vm/js_vm.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ class VM {
}

static void HandleUncaughtException(const std::shared_ptr<Ctx>& ctx, const std::shared_ptr<CtxValue>& exception);
static void HandleError(const std::shared_ptr<Ctx>& ctx,
const std::shared_ptr<CtxValue>& event,
const std::shared_ptr<CtxValue>& source,
const std::shared_ptr<CtxValue>& lineno,
const std::shared_ptr<CtxValue>& colno,
const std::shared_ptr<CtxValue>& error);
virtual std::shared_ptr<CtxValue> ParseJson(const std::shared_ptr<Ctx>& ctx, const string_view& json) = 0;
virtual std::shared_ptr<Ctx> CreateContext() = 0;
private:
Expand Down
123 changes: 123 additions & 0 deletions driver/js/lib/global/Others.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,19 @@
global.__ISHIPPY__ = true;
global.__GLOBAL__ = {
globalEventHandle: {},
globalErrorHandle: [],
};

class ErrorEvent {
constructor(message, filename, lineno, colno, error) {
this.message = message;
this.filename = filename;
this.lineno = lineno;
this.colno = colno;
this.error = error;
}
}

/**
* Register the Hippy app entry function, the native will trigger an event to execute the function
* and start the app.
Expand Down Expand Up @@ -114,6 +125,115 @@ function emit(eventName, ...args) {
}
}

/**
* Register a listener for a error event, and the listener will be called
* when the event is triggered.
*/
Object.defineProperty(Hippy, 'onerror', {
get: function() {
let listeners = __GLOBAL__.globalErrorHandle;
return listeners.length > 0 ? listeners.slice(-1) : null;
},
set: function(listener) {
if (typeof listener !== 'function') {
throw new TypeError('Hippy.onerror only accept a function as listener');
}
__GLOBAL__.globalErrorHandle = [];
let listeners = __GLOBAL__.globalErrorHandle;
listeners.push(listener);
}
});

/**
* Trigger a error event with arguments.
*
* @param {any} args - Event callback arguments: event, source, lineno, colno, error.
*/
function emitError(...args) {
if (args.length !== 5) {
throw new TypeError('Hippy.emitError only accept 5 params');
}

let errObj = new Error();
errObj.message = JSON.stringify(args[4]);

const listeners = __GLOBAL__.globalErrorHandle;
if (listeners) {
try {
listeners.forEach(listener => listener(args[0], args[1], args[2], args[3], errObj));
} catch (err) {
/* eslint-disable-next-line no-console */
console.error(err);
}
} else {
if (args[0]) {
console.error(args[0].toString());
}
}

const eventName = 'error';
const eventListeners = __GLOBAL__.globalEventHandle[eventName];
if (eventListeners) {
try {
let event = new ErrorEvent(args[0], args[1], args[2], args[3], errObj);
eventListeners.forEach(listener => listener(event));
} catch (err) {
/* eslint-disable-next-line no-console */
console.error(err);
}
} else {
if (args[0]) {
console.error(args[0].toString());
}
}
}

/**
* Register a listener for a specific event, and the listener will be called
* when the event is triggered.
*
* @param {string} eventName - The event name will be registered.
* @param {Function} listener - Event callback.
* @returns {Set} - Set of event listeners
*/
function addEventListener(eventName, listener) {
if (typeof eventName !== 'string' || typeof listener !== 'function') {
throw new TypeError('Hippy.addEventListener() only accept a string as event name and a function as listener');
}

let eventListeners = __GLOBAL__.globalEventHandle[eventName];
if (!(eventListeners instanceof Set)) {
__GLOBAL__.globalEventHandle[eventName] = new Set();
eventListeners = __GLOBAL__.globalEventHandle[eventName];
}
eventListeners.add(listener);
return eventListeners;
}

/**
* Remove specific event listener,
*
* @param {string} eventName - The event name will be removed.
* @param {Function} listener - Specific event callback will be removed,
* the listeners will clean all if not specific.
* @returns {Set | null} - Set of event listeners, or null of empty.
*/
function removeEventListener(eventName, listener) {
if (typeof eventName !== 'string') {
throw new TypeError('Hippy.removeEventListener() only accept a string as event name');
}
const eventListeners = __GLOBAL__.globalEventHandle[eventName];
if (!(eventListeners instanceof Set)) {
return null;
}
if (listener) {
eventListeners.delete(listener);
return eventListeners;
}
eventListeners.clear();
return null;
}

Hippy.device = {};
Hippy.bridge = {};
Hippy.register = {
Expand All @@ -122,3 +242,6 @@ Hippy.register = {
Hippy.on = on;
Hippy.off = off;
Hippy.emit = emit;
Hippy.emitError = emitError;
Hippy.addEventListener = addEventListener;
Hippy.removeEventListener = removeEventListener;
28 changes: 28 additions & 0 deletions driver/js/lib/modules/ErrorHandle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Tencent is pleased to support the open source community by making
* Hippy available.
*
* Copyright (C) 2017-2019 THL A29 Limited, a Tencent company.
* 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.
*/

(function errorHandler(event, source, lineno, colno, error) {
if (global.Hippy) {
global.Hippy.emitError(event, source, lineno, colno, error);
} else {
/* eslint-disable-next-line no-console */
console.error(err);
}
});
3 changes: 2 additions & 1 deletion driver/js/scripts/build-core.js
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ function getAllRequiredFiles(platform) {
getAbsolutePath('../lib/bootstrap.js'),
getAbsolutePath(`../lib/entry/${platform}/hippy.js`),
getAbsolutePath('../lib/modules/ExceptionHandle.js'),
getAbsolutePath('../lib/modules/ErrorHandle.js'),
];

rl.on('line', (line) => {
Expand Down Expand Up @@ -241,7 +242,7 @@ function generateCpp(platform, buildDirPath) {
for (let i = 0; i < fileBuffer.length; i += 1) {
byteArr.push(fileBuffer[i]);
}
if (fileName === 'bootstrap' || fileName === 'ExceptionHandle') {
if (fileName === 'bootstrap' || fileName === 'ExceptionHandle' || fileName === 'ErrorHandle') {
code += `
const uint8_t k_${fileName}[] = { ${byteArr.join(',')},0 }; // NOLINT`;
} else {
Expand Down
42 changes: 42 additions & 0 deletions driver/js/src/scope.cc
Original file line number Diff line number Diff line change
Expand Up @@ -599,5 +599,47 @@ void Scope::UnloadInstance(const std::shared_ptr<HippyValue>& value) {
}
}

void Scope::SetCallbackForUriLoader() {
auto the_loader = loader_.lock();
if (the_loader) {
the_loader->SetRequestTimePerformanceCallback([WEAK_THIS](const string_view& uri, const TimePoint& start, const TimePoint& end) {
DEFINE_AND_CHECK_SELF(Scope)
auto runner = self->GetTaskRunner();
if (runner) {
auto task = [weak_this, uri, start, end]() {
DEFINE_AND_CHECK_SELF(Scope)
auto entry = self->GetPerformance()->PerformanceResource(uri);
entry->SetLoadSourceStart(start);
entry->SetLoadSourceEnd(end);
};
runner->PostTask(std::move(task));
}
});
the_loader->SetRequestErrorCallback([WEAK_THIS](const string_view& uri, const int32_t ret_code, const string_view& error_msg) {
DEFINE_AND_CHECK_SELF(Scope)
auto runner = self->GetTaskRunner();
if (runner) {
auto task = [weak_this, uri, ret_code, error_msg]() {
DEFINE_AND_CHECK_SELF(Scope)
self->HandleUriLoaderError(uri, ret_code, error_msg);
};
runner->PostTask(std::move(task));
}
});
}
}

void Scope::HandleUriLoaderError(const string_view& uri, const int32_t ret_code, const string_view& error_msg) {
std::unordered_map<string_view, std::shared_ptr<CtxValue>> error_map;
error_map["code"] = context_->CreateNumber(static_cast<double>(ret_code));
error_map["message"] = context_->CreateString(error_msg);
auto event = context_->CreateString("vfs error");
auto source = context_->CreateString(uri);
auto lineno = context_->CreateNumber(0);
auto colno = context_->CreateNumber(0);
auto error = context_->CreateObject(error_map);
VM::HandleError(context_, event, source, lineno, colno, error);
}

}
}
38 changes: 34 additions & 4 deletions driver/js/src/vm/js_vm.cc
Original file line number Diff line number Diff line change
Expand Up @@ -31,20 +31,23 @@ inline namespace driver {
inline namespace vm {

constexpr char kEventName[] = "uncaughtException";
constexpr char kErrorHandlerJSName[] = "ExceptionHandle.js";
constexpr char kHippyErrorHandlerName[] = "HippyExceptionHandler";
constexpr char kExceptionHandlerJSName[] = "ExceptionHandle.js";
constexpr char kHippyExceptionHandlerName[] = "HippyExceptionHandler";

constexpr char kErrorHandlerJSName[] = "ErrorHandle.js";
constexpr char kHippyErrorHandlerName[] = "HippyErrorHandler";

using Ctx = hippy::Ctx;
using CtxValue = hippy::CtxValue;

void VM::HandleUncaughtException(const std::shared_ptr<Ctx>& ctx,
const std::shared_ptr<CtxValue>& exception) {
auto global_object = ctx->GetGlobalObject();
string_view error_handle_name(kHippyErrorHandlerName);
string_view error_handle_name(kHippyExceptionHandlerName);
auto error_handle_key = ctx->CreateString(error_handle_name);
auto exception_handler = ctx->GetProperty(global_object, error_handle_key);
if (!ctx->IsFunction(exception_handler)) {
const auto& source_code = hippy::GetNativeSourceCode(kErrorHandlerJSName);
const auto& source_code = hippy::GetNativeSourceCode(kExceptionHandlerJSName);
FOOTSTONE_DCHECK(source_code.data_ && source_code.length_);
string_view str_view(source_code.data_, source_code.length_);
exception_handler = ctx->RunScript(str_view, error_handle_name);
Expand All @@ -63,6 +66,33 @@ void VM::HandleUncaughtException(const std::shared_ptr<Ctx>& ctx,
}
}

void VM::HandleError(const std::shared_ptr<Ctx>& ctx,
const std::shared_ptr<CtxValue>& event,
const std::shared_ptr<CtxValue>& source,
const std::shared_ptr<CtxValue>& lineno,
const std::shared_ptr<CtxValue>& colno,
const std::shared_ptr<CtxValue>& error) {
auto global_object = ctx->GetGlobalObject();
string_view error_handle_name(kHippyErrorHandlerName);
auto error_handle_key = ctx->CreateString(error_handle_name);
auto error_handler = ctx->GetProperty(global_object, error_handle_key);
if (!ctx->IsFunction(error_handler)) {
const auto& source_code = hippy::GetNativeSourceCode(kErrorHandlerJSName);
FOOTSTONE_DCHECK(source_code.data_ && source_code.length_);
string_view str_view(source_code.data_, source_code.length_);
error_handler = ctx->RunScript(str_view, error_handle_name);
ctx->SetProperty(global_object, error_handle_key, error_handler);
}

std::shared_ptr<CtxValue> argv[5] = {event, source, lineno, colno, error};

auto try_catch = CreateTryCatchScope(true, ctx);
auto ret_value = ctx->CallFunction(error_handler, ctx->GetGlobalObject(), 5, argv);
if (try_catch->HasCaught()) {
auto message = try_catch->GetExceptionMessage();
FOOTSTONE_LOG(WARNING) << "hippy errorHandler error, description = " << message;
}
}

}
}
Expand Down
Loading

0 comments on commit 357c7d3

Please sign in to comment.