Skip to content

[UE] Bug: QuickJS作为backend时,cpp静态绑定的函数调用会产生崩溃 #2239

@watsonsong

Description

@watsonsong

前置阅读 | Pre-reading

Puer的版本 | Puer Version

Master(>1.0.9)

UE的版本 | UE Version

5.6.1

发生在哪个平台 | Platform

Editor(win)

错误信息 | Error Message

准备使用QuickJS作为backend以便支持v8无法支持的平台,包括NS和PS的平台。但是切换到QuickJS的时候发现了一些问题。
我们的注册代码发生在另外一个模块(DLL)里面。当我们使用C++绑定去注册一些API给TS的时候,带参数的函数调用,在参数传递时都会产生崩溃。例如在C++中是如此注册的API:

struct FBindTest
{
	static void CallFuncWithBoolean(bool Value)
	{
		UE_LOG(LogTemp, Log, TEXT("CallFuncWithBoolean: %d"), Value);
	}
	
	static void CallFuncWithString(const FString& Value)
	{
		UE_LOG(LogTemp, Log, TEXT("CallFuncWithBoolean: %s"), *Value);
	}
	
	static void CallFuncWithInt(int Value)
	{
		UE_LOG(LogTemp, Log, TEXT("CallFuncWithInt: %d"), Value);
	}
};

UsingCppType(FBindTest);

void BindJSExtension()
{
	puerts::DefineClass<FBindTest>()
		.Function("CallFuncWithBoolean", MakeFunction(&FBindTest::CallFuncWithBoolean))
		.Function("CallFuncWithString", MakeFunction(&FBindTest::CallFuncWithString))
		.Function("CallFuncWithInt", MakeFunction(&FBindTest::CallFuncWithInt))
		.Register();
}

在脚本中用引擎类继承派生了一个GameModeBase,其中调用这个C++的API。如下使用:

class StartupGameMode extends UE.GameModeBase {
    override ReceiveBeginPlay(): void {
        console.info("## Statup Game");
        cpp.FBindTest.CallFuncWithBoolean(true);
        cpp.FBindTest.CallFuncWithString('Hello');
        cpp.FBindTest.CallFuncWithInt(1);
        console.info("## Statup Game End");
    }
}

实际QuickJs作为backend的时候,传递int参数会产生崩溃:

static T toCpp(v8::Local<v8::Context> context, const v8::Local<v8::Value>& value)
{
    return static_cast<T>(value->Int32Value(context).ToChecked());
}

Maybe<int32_t> Value::Int32Value(Local<Context> context) const {
    double d;
    if (JS_ToFloat64(Isolate::current_->current_context_->context_, &d, value_)) {
        return Maybe<int32_t>();
    }
    else {
        return Maybe<int32_t>((int32_t)d);
    }
}

发现一种情况是如果在JsEnv.Build.cs中把ForceStaticLibInEditor = true给去掉,这个问题会解决。可能是因为一个静态库如果有两个DLL模块访问的话,其中的静态变量会每个模块有一份。对于QuickJS的实现来说,static Isolate* current_;这个静态变量会碰到这个问题。只要访问Puerts的C++注册代码发生在多个模块,就可能出现这个问题。最好的方法是避免这个静态变量,似乎大部分访问都可以通过传递的参数获取到Isolate。

问题重现 | Bug reproduce

我构造了一个演示工程:https://github.com/watsonsong/Puerts_QuickJSTest
下载这个工程,使用UE 5.6.1或者其他版本打开。打开默认是Startup的一个空场景,PIE直接运行Startup可以复现。

BTW顺便Quick下还有一个问题是JsEnv->Start("PuertsEditor/CodeAnalyze");执行速度非常慢,会在编辑器启动时卡很久。主要时间消耗在:let diagnostics = ts.getPreEmitDiagnostics(program);上面。即使这个测试工程都会非常慢,如果比较大一些的工程就几乎不可用。

Metadata

Metadata

Assignees

Labels

UnrealbugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions