Skip to content

Commit

Permalink
fix(ios): resolve conversion error between NSString and CtxValue
Browse files Browse the repository at this point in the history
add unit test for data conversion
  • Loading branch information
wwwcg committed Aug 28, 2024
1 parent d27e8ab commit 42f9de0
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 18 deletions.
3 changes: 1 addition & 2 deletions driver/js/include/driver/napi/jsc/jsc_ctx.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,7 @@ class JSCCtx : public Ctx {
virtual std::shared_ptr<CtxValue> CreateObject() override;
virtual std::shared_ptr<CtxValue> CreateNumber(double number) override;
virtual std::shared_ptr<CtxValue> CreateBoolean(bool b) override;
virtual std::shared_ptr<CtxValue> CreateString(
const string_view& string) override;
virtual std::shared_ptr<CtxValue> CreateString(const string_view& string) override;
virtual std::shared_ptr<CtxValue> CreateUndefined() override;
virtual std::shared_ptr<CtxValue> CreateNull() override;
virtual std::shared_ptr<CtxValue> CreateObject(const std::unordered_map<
Expand Down
3 changes: 1 addition & 2 deletions framework/ios/base/bridge/HippyBridge.mm
Original file line number Diff line number Diff line change
Expand Up @@ -787,8 +787,7 @@ - (void)actuallyInvokeAndProcessModule:(NSString *)module method:(NSString *)met
- (void)dispatchBlock:(dispatch_block_t)block queue:(dispatch_queue_t)queue {
if (HippyJSThread == queue) {
[_javaScriptExecutor executeBlockOnJavaScriptQueue:block];
}
else {
} else {
dispatch_async(queue, block);
}
}
Expand Down
26 changes: 12 additions & 14 deletions framework/ios/utils/NSObject+CtxValue.mm
Original file line number Diff line number Diff line change
Expand Up @@ -152,20 +152,17 @@ id ObjectFromCtxValue(CtxPtr context, CtxValuePtr value) {
if (context->IsString(value)) {
footstone::string_view view;
if (context->GetValueString(value, &view)) {
view = footstone::StringViewUtils::ConvertEncoding(view, footstone::string_view::Encoding::Utf16);
view = footstone::StringViewUtils::CovertToUtf16(view, view.encoding());
footstone::string_view::u16string &u16String = view.utf16_value();
NSString *string =
[NSString stringWithCharacters:(const unichar *)u16String.c_str() length:u16String.length()];
NSString *string = [NSString stringWithCharacters:(const unichar *)u16String.c_str() length:u16String.length()];
return string;
}
}
else if (context->IsNumber(value)) {
} else if (context->IsNumber(value)) {
double number = 0;
if (context->GetValueNumber(value, &number)) {
return @(number);
}
}
else if (context->IsArray(value)) {
} else if (context->IsArray(value)) {
uint32_t length = context->GetArrayLength(value);
NSMutableArray *array = [NSMutableArray arrayWithCapacity:length];
for (uint32_t index = 0; index < length; index++) {
Expand All @@ -183,8 +180,7 @@ id ObjectFromCtxValue(CtxPtr context, CtxValuePtr value) {
if (context->GetByteBuffer(value, &bytes, length, type)) {
return [NSData dataWithBytes:bytes length:length];
}
}
else if (context->IsObject(value)) {
} else if (context->IsObject(value)) {
std::unordered_map<CtxValuePtr, CtxValuePtr> map;
if (context->GetEntriesFromObject(value, map)) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:map.size()];
Expand All @@ -194,17 +190,19 @@ id ObjectFromCtxValue(CtxPtr context, CtxValuePtr value) {
if (!flag) {
continue;
}
const auto &u16String = footstone::StringViewUtils::CovertToUtf16(string_view, string_view.encoding()).utf16_value();
NSString *string = [NSString stringWithCharacters:(const unichar *)u16String.c_str() length:u16String.length()];
// Note that reference value (const auto &) cannot be used directly here,
// since the temporary string_view object will destruct the uft16 value.
// a wrong example is:
// const auto &u16Str = footstone::StringViewUtils::CovertToUtf16(string_view, string_view.encoding()).utf16_value();
string_view = footstone::StringViewUtils::CovertToUtf16(string_view, string_view.encoding());
footstone::string_view::u16string &u16Str = string_view.utf16_value();
NSString *string = [NSString stringWithCharacters:(const unichar *)u16Str.c_str() length:u16Str.length()];
auto &value = it.second;
id obj = ObjectFromCtxValue(context, value);
[dictionary setObject:obj forKey:string];
}
return [dictionary copy];
}
}
else {

}
return [NSNull null];
}
Expand Down
134 changes: 134 additions & 0 deletions tests/ios/HippyCtxValueConvertTest.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/*!
* iOS SDK
*
* Tencent is pleased to support the open source community by making
* Hippy available.
*
* Copyright (C) 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.
*/


#import <XCTest/XCTest.h>
#import <hippy/NSObject+CtxValue.h>
#import <hippy/HippyJSEnginesMapper.h>
#include "driver/engine.h"
#include "driver/vm/js_vm.h"
#include "driver/napi/js_ctx.h"


@interface HippyCtxValueConvertTest : XCTestCase

/// hippy vm for test
@property (nonatomic, assign) std::shared_ptr<hippy::VM> vm;
/// context for test
@property (nonatomic, assign) std::shared_ptr<hippy::Ctx> context;

@end

@implementation HippyCtxValueConvertTest

- (void)setUp {
_vm = hippy::CreateVM(std::make_shared<hippy::VM::VMInitParam>());
_context = _vm->CreateContext();
}

- (void)tearDown {
// nop
}

- (void)testCtxStringToNSString {
std::shared_ptr<hippy::CtxValue> ctxStr = _context->CreateString(u"shouldIgnoreLatestLogin");
id ocObj = ObjectFromCtxValue(_context, ctxStr);
XCTAssert([ocObj isEqualToString:@"shouldIgnoreLatestLogin"]);
}

- (void)testCtxObjectToNSDictionary {
const char *testKey = "MyVeryVeryLongStringKeyForUnitTest";
std::shared_ptr<hippy::CtxValue> ctxStr = _context->CreateString(u"myTestVeryLongStringValue");
std::unordered_map<footstone::string_view, std::shared_ptr<hippy::CtxValue>> map = { {testKey , ctxStr} };
std::shared_ptr<hippy::CtxValue> ctxValue = _context->CreateObject(map);
id ocObj = ObjectFromCtxValue(_context, ctxValue);
XCTAssert([ocObj isKindOfClass:NSDictionary.class]);
XCTAssertTrue([[((NSDictionary *)ocObj) objectForKey:@(testKey)] isEqualToString:@"myTestVeryLongStringValue"]);
}

// NSString
- (void)testNSStringToCtxValue {
NSString *testOCString = @"Hello Hippy";
CtxValuePtr ctxValue = [testOCString convertToCtxValue:_context];
XCTAssert(_context->IsString(ctxValue));
XCTAssert([ObjectFromCtxValue(_context, ctxValue) isEqualToString:testOCString]);
}

// NSNumber
- (void)testNSNumberToCtxValue {
NSNumber *testOCNumber = @42;
CtxValuePtr ctxValue = [testOCNumber convertToCtxValue:_context];
XCTAssert(_context->IsNumber(ctxValue));
XCTAssertTrue([ObjectFromCtxValue(_context, ctxValue) isEqualToNumber:testOCNumber]);
}

// NSArray
- (void)testNSArrayToCtxValue {
NSArray *testOCArray = @[@"Hello", @42, @YES];
CtxValuePtr ctxValue = [testOCArray convertToCtxValue:_context];
XCTAssert(_context->IsArray(ctxValue));
XCTAssert([ObjectFromCtxValue(_context, ctxValue) isEqualToArray:testOCArray]);
}

// NSDictionary
- (void)testNSDictionaryToCtxValue {
NSDictionary *testOCDict = @{@"key1": @"value1", @"key2": @42};
CtxValuePtr ctxValue = [testOCDict convertToCtxValue:_context];
XCTAssert(_context->IsObject(ctxValue));
XCTAssert([ObjectFromCtxValue(_context, ctxValue) isEqualToDictionary:testOCDict]);
}

// NSData
- (void)testNSDataToCtxValue {
NSData *testOCData = [@"Hello Hippy" dataUsingEncoding:NSUTF8StringEncoding];
CtxValuePtr ctxValue = [testOCData convertToCtxValue:_context];
XCTAssert(_context->IsByteBuffer(ctxValue));
XCTAssert([ObjectFromCtxValue(_context, ctxValue) isEqualToData:testOCData]);
}

// NSNull
- (void)testNSNullToCtxValue {
NSNull *testOCNull = [NSNull null];
CtxValuePtr ctxValue = [testOCNull convertToCtxValue:_context];
XCTAssert(_context->IsNull(ctxValue));
XCTAssert([ObjectFromCtxValue(_context, ctxValue) isKindOfClass:[NSNull class]]);
}

// NSError
- (void)testNSErrorToCtxValue {
NSError *testOCError = [NSError errorWithDomain:@"com.example" code:42 userInfo:nil];
CtxValuePtr ctxValue = [testOCError convertToCtxValue:_context];
XCTAssert(_context->IsString(ctxValue)); // NSErrors are converted as string
NSString *resultDesc = ObjectFromCtxValue(_context, ctxValue);
XCTAssert([resultDesc isKindOfClass:[NSString class]]);
XCTAssertEqualObjects(resultDesc, testOCError.description);
}

// NSURL
- (void)testNSURLToCtxValue {
NSURL *testOCURL = [NSURL URLWithString:@"https://www.example.com"];
CtxValuePtr ctxValue = [testOCURL convertToCtxValue:_context];
XCTAssert(_context->IsString(ctxValue)); // Assuming URLs are converted to strings
XCTAssert([ObjectFromCtxValue(_context, ctxValue) isEqualToString:[testOCURL absoluteString]]);
}

@end

0 comments on commit 42f9de0

Please sign in to comment.