原生代码中的JavaScript类型
Vega Turbo模块支持各种原生类型,这些类型可以在Turbo模块参数和返回值中与JavaScript相互转换。这意味着,例如,可以使用JavaScript字符串从JavaScript调用接受std::string的Turbo模块方法,该参数将被自动转换;同样,返回std::string的Turbo模块方法将向JavaScript环境返回JavaScript字符串。其中一些类型(例如std::string和int32_t)是基本的C++类型。其他类型是Vega特有的,大多存在于com::amazon::kepler::turbomodule命名空间中。
类型映射
下表显示了TypeScript类型与其C++对应类型之间的映射。这是codegen工具使用的映射。
其中一些类型不是基本TypeScript的一部分,需要从@amazon-devices/keplerscript-turbomodule-api导入。
| TypeScript类型 | 原生类型 |
|---|---|
string |
std::string |
number |
double |
Int32 |
int32_t |
UInt32 |
uint32_t |
Int64[1] |
int64_t |
Double |
double |
Float |
float |
boolean |
bool |
void |
void |
Array<T>, T[] (homogeneous)[2] |
std::vector<T> |
Array |
JSArray |
Object |
JSObject |
Promise |
Promise |
Function(参数类型) |
Callback |
Function(返回类型) |
HostCallback |
bigint |
BigInt[3] |
ArrayBuffer |
ArrayBuffer |
NativeObject |
std::shared_ptr<NativeObject> |
*JsonContainer[4] |
utils::json::JsonContainer |
[1] int64_t可能因Number.MAX_SAFE_INTEGER发生精度损失。对于有精度要求的大整数,您可以使用BigInt。
[2] 同构数组是指其元素类型相同的数组。
[3] 对于有符号和无符号的64位整数,都支持将BigInt用作直接方法参数和返回类型。有关支持的用法的更多详细信息,请参阅BigInt。
[4] JSObject的差距得到解决后,JsonContainer将被删除。如果需要使用该类型,可以从泛型Object的@amazon-devices/keplerscript-turbomodule-api中导入JsonContainer类型,也可定义自定义类型别名,例如:
type MyJsonContainer = {myProperty: string};
注意: 对于大于JavaScript的Number.MAX_SAFE_INTEGER的整数,使用std::string表示形式来表示Turbo模块的参数和返回类型。您可以在JavaScript和C++实现中将字符串转换为更合适的类型。
JSArray
对于同构 (T[]) 数组,您可以使用std::vector<T>。对于其他数组,可以使用JSArray。
JSArray是一个std::vector,它接受各种元素类型,包括空指针、布尔值、各种int、double、float、std::string、JSArray和JSObject。因此,它的用法类似于std::vector。
示例
JSArray SampleTurboModule::getJSArray() {
auto jsObject = JSObject{};
auto jsArray = JSArray{};
jsArray.emplace_back(jsObject);
jsArray.emplace_back(std::string{"foo"});
jsArray.emplace_back(3.14);
return jsArray;
}
JSArray SampleTurboModule::extendJSArray(JSArray arr) {
arr.emplace_back(std::string{"新元素"});
return arr;
}
JSObject
JSObject是一个带有std::string键的std::map,它接受各种值类型,包括空指针、布尔值、各种int、double、float、std::string、JSArray和JSObject。因此,它的用法类似于std::map。
JSObject提供了一种静态parseJson方法。parseJson方法接受std::string JSON字符串并返回等效的JSObject。
示例
JSObject SampleTurboModule::getJSObject(std::string jsonContent) {
auto jsObject = JSObject::parseJson(jsonContent);
return jsObject;
}
JSObject SampleTurboModule::getJSObject() {
auto jsObject = JSObject{};
jsObject.emplace("intValue", 100);
jsObject.emplace("doubleValue", 25.1);
jsObject.emplace("boolValue", true);
jsObject.emplace("strValue", std::string{"字符串值"});
return jsObject;
}
JSObject SampleTurboModule::extendJSObject(JSObject obj) {
obj.emplace("foo", std::string{"bar"});
return obj;
}
代理JSObject和JSArray
除了JSObject和JSArray类型外,Vega Turbo模块还支持代理类型proxy::JSObject和proxy::JSArray。代理类型的关键功能之一是动态访问和修改。您可以直接使用C++代码对JavaScript对象和数组进行读取和写入,而不必创建副本。
但是,在使用这些代理类型时,请谨记以下的重要事项:
-
线程注意事项:
代理类型必须在JSThread上下文和Turbo模块方法调用的范围内使用。这些类型不能从任意线程存储或访问。线程安全检查仅在调试模式下执行。因此,在多线程环境中使用这些代理对象时需要小心。
-
原生结构:
虽然您可以在本地构造新的代理项目,但您需要利用Turbo模块或HostCallback方法获取所需的JSRuntime作为方法参数。
警告: 请勿使用默认的构造函数。这些代理类型的默认构造函数仅用于支持
std::tuple和std::variant等结构中的当前内部用法。您不应该依赖这些默认的构造函数,因为它们需要JSThread上下文,并且在未来的版本中将会被删除。
JSRuntime
JSRuntime类型表示JavaScript运行时环境。它是一种仅限原生的类型,任何Turbo模块或HostCallback方法都可以将其作为参数进行调用。它通常用于在C++中创建新的代理项目。
注意: JSRuntime无法从非JSThread进行构造。
void SampleTurboModuleV2::sampleMethod(JSRuntime rt) {
// 创建新的代理对象
auto newObject = proxy::JSObject(rt);
// 创建代理数组
auto newArray = proxy::JSArray(rt);
}
proxy::JSObject
借助proxy::JSObject类型,您不仅可以创建可以传递到原生代码的JavaScript对象,还可以从本机端动态访问和修改其属性和方法,而无需创建副本。
注意: 对proxy::JSObject的访问仅限于JSThread上下文,并且必须在Turbo模块方法调用的范围内进行。线程安全检查仅在调试模式下执行。
std::vector<proxy::JSObject> SampleTurboModuleV2::getProxyObject(JSRuntime rt, std::vector<proxy::JSObject> args) {
// 从接收到的对象读取属性
auto object = args[0];
auto propertyNames = object.getPropertyNames();
for (const auto& propertyName: propertyNames) {
auto propertyValue = object.getProperty(propertyName);
if (std::holds_alternative<std::string>(propertyValue)) {
auto stringValue = std::get<std::string>(propertyValue);
TMINFO("Property " + propertyName + " = " + stringValue);
}
}
// 创建并返回新对象
auto newObject = proxy::JSObject(rt);
newObject.setProperty("stringProp", "foo");
newObject.setProperty("numberProp", 1.5);
newObject.setProperty("boolProp", true);
args[0] = newObject;
return args;
}
proxy::JSArray
借助proxy::JSArray类型,您可以创建支持传递到原生代码的JavaScript数组,并从本机端动态访问和修改其元素。
注意: 对proxy::JSArray的访问仅限于JSThread上下文,并且必须在Turbo模块方法调用的范围内进行。线程安全检查仅在调试模式下执行。
proxy::JSArray SampleTurboModuleV2::getProxyArray(proxy::JSArray arg) {
size_t index = 0;
for(const auto& element: arg) {
if (std::holds_alternative<std::string>(element)) {
auto elementValue = std::get<std::string>(element);
TMDEBUG("SampleTurboModuleV2::getProxyArray initial string element = " + elementValue);
arg.set(index, "C++String");
}
++index;
}
return arg;
}
Promise
应该在单独的线程上执行Promise。
Promise是使用PromiseOperation作为输入来构造的。PromiseOperation是接受std::shared_ptr<Promise>并返回void的函数。
Promise有resolve和reject方法。可以使用Error或std::string来拒绝Promise,并使用类型映射中的任何C++ Turbo模块类型来解析。
示例
Promise<std::string> SampleTurboModule::getValueWithPromise(bool error) {
return Promise([self = this, error](const std::shared_ptr<Promise>& promise) {
std::thread([error, promise]() {
if (error) {
promise->reject(Error("用Error类拒绝了promise"));
} else {
promise->resolve(std::string{"结果"});
}
}).detach();;
});
}
Callback
应该在单独的线程上执行回调。
Callback对象只能是参数值,代表传递给C++的JavaScript回调。有关作为返回类型的Callback的信息,请参见HostCallback。
Callback对象公开的唯一方法是invoke,它使用指定的参数调用JavaScript回调,并将结果返回给C++。
示例
// JS
SampleTurboModule.getValueWithCallback((value : number) => {
console.log(value); // 这里显示的值是5,该值是通过C++的Callback.invoke方法作为参数传入的。
return 2 * value;
});
// C++
void SampleTurboModule::getValueWithCallback(Callback callback) {
std::thread([](Callback callback) {
auto future = callback.invoke(5);
std::string returnValue{"undefined"};
try {
auto jsValue = future.get();
// 从JS返回的任何数字始终是双精度类型
if(std::holds_alternative<double>(jsValue)) {
returnValue = std::to_string(std::get<double>(jsValue));
}
} catch (const std::exception& ex) {
TMERROR("Error getting the return value from callback.Exception: " + std::string(ex.what()));
}
TMINFO("Return value from callback is: " + returnValue);
}, callback).detach();
}
HostCallback
HostCallback只能是一个返回值,用于向JavaScript环境返回C++回调。有关参数类型的回调,请参见Callback。
示例
// C++
std::string sampleHostCallbackMethod(std::string jsInputString) {
return "Received JS string " + jsInputString);
}
HostCallback SampleTurboModule::getHostCallback() {
return HostCallback(sampleHostCallbackMethod);
}
// JS
const hostCallback = SampleTurboModule.getHostCallback();
console.log(hostCallback("Hello from HostCallback!")); // 输出“received JS string Hello from HostCallback!”(收到JS字符串Hello from HostCallback!)
}
您也可以使用HostCallback。来调用Callback
// JS
SampleTurboModule.invokeCallbackWithHostCallback((hostCallback: (obj: Object) => number): void {
const secret = hostCallback();
console.log(secret); // logs 17
});
// C++
void SampleTurboModule::invokeCallbackWithHostCallback(Callback callback) {
auto secret = 17;
auto lambda = [secret]() {
return secret;
};
auto hostCallback = HostCallback(std::function<int()>(lambda));
callback.invoke(hostCallback);
}
ArrayBuffer
ArrayBuffer表示通用、固定长度的原始二进制数据缓冲区。
示例
ArrayBuffer SampleTurboModule::getNativeMemoryArrayBuffer() {
return {"Hello World!"};
}
ArrayBuffer SampleTurboModule::modifyArrayBuffer(ArrayBuffer buf) {
// 没有数据副本,因为它只是在不插入新内容的情况下进行修改。
uint8_t* data = buf.data();
size_t size = buf.size();
for (auto i = 0; i < size; i++) {
data[i] = 9;
}
return buf;
}
ArrayBuffer SampleTurboModule::insertSecretData(ArrayBuffer buf) {
// 在此处插入会创建数据副本并附加终结器
uint8_t secret[] = {6, 7, 9, 9, 0};
arg.insert(5, secret);
return arg;
}
BigInt
一个BigInt代表一个整数。在JavaScript中,BigInt用于表示超过Number.MAX_SAFE_INTEGER或Number.MIN_SAFE_INTEGER的整数。在Vega中,Turbo模块支持通过基于int64_t和uint64_t的getter和constructor方法表示BigInt。
BigInt SampleTurboModule::getBigInt(BigInt bigint) {
// 创建BigInt
auto big1 = BigInt::fromI64(-9007199254740990);
auto big2 = BigInt::fromUi64(static_cast<uint64_t>(9007199254740990));
// 访问值。Getter不依赖于构造中使用的类型。
auto asI64 = bigint.getI64();
auto asUi64 = bigint.getUi64();
// getI64() 返回具有以下属性的结构
int64_t i64value = asI64.value;
bool lossless = asI64.lossless;
// getUi64() 返回具有以下属性的结构
bool is_signed = asUi64.is_signed;
uint64_t ui64value = asUi64.value;
bool lossless = asUi64.lossless;
return big1;
}
您可以在方法参数和返回类型以及回调和Promise中使用BigInt,但只能作为原始值,不能作为JSArray或JSObject的一部分。
如果JavaScript BigInt过大而无法用Vega Turbo模块BigInt类型表示(例如无法用int64_t或uint64_t表示),则会触发断言失败,Vega Turbo模块的所有无效参数也是如此。有关更多信息,请参阅“高级Turbo模块主题”中的错误处理。
utils::json::JsonContainer
将不再把JsonContainer用于JSObject类型,但目前仍存在一些功能缺口。有关支持类型的完整列表,请参阅类型映射。
示例
utils::json::JsonContainer SampleTurboModuleV2::getJsonContainer() {
auto result = utils::json::JsonContainer::createJsonObject();
result.insert("const1", true);
result.insert("const2", 375);
result.insert("const3", std::string("something"));
return result;
}
NativeObject
在大多数情况下,当您将数据从原生代码传递给JavaScript时,一个JSONContainer或JSObject便足以满足需求。但是,如果您需要一个可以从JavaScript调用其方法的原生对象,您需要创建一个实现com::amazon::kepler::turbomodule::NativeObject的对象。
示例
#include <Vega/turbomodule/NativeObject.h>
using namespace com::amazon::kepler::turbomodule;
class SampleNativeObject : NativeObject {
public:
// 必须将方法添加到此函数中的methodAggregator中才能公开给JavaScript。
void aggregateMethods(MethodAggregator<NativeObject> &methodAggregator) noexcept override {
methodAggregator.addMethod("getString", 1,
&SampleNativeObjectV2::getString);
}
std::string getString(std::string arg) {
return "再见 " + arg;
}
};
// 在TurboModule实现中:
std::shared_ptr<NativeObject> SampleTurboModule::getNativeObject() {
return std::make_shared<SampleNativeObject>();
}
// 在JavaScript中使用:
SampleTurboModule.getNativeObject().getString("foo"); // 返回“Goodbye foo”
请注意,仅支持将NativeObject作为Turbo模块方法返回类型,而不支持将其作为参数。
APMF类型
IDL使用某些APMF类型,这些类型并不总是可以在支持的Turbo模块参数/返回类型之间直接转换。您可能需要实现自定义逻辑才能进行这些转换。
相关主题
Last updated: 2025年9月30日

