Commit 82f0751c authored by Aleksey Konovalov's avatar Aleksey Konovalov

c++ wide strings support

parent 1dc5a248
...@@ -145,7 +145,7 @@ class CppMarshal(spec: Spec) extends Marshal(spec) { ...@@ -145,7 +145,7 @@ class CppMarshal(spec: Spec) extends Marshal(spec) {
} }
def base(m: Meta): String = m match { def base(m: Meta): String = m match {
case p: MPrimitive => p.cName case p: MPrimitive => p.cName
case MString => "std::string" case MString => if (spec.cppUseWideStrings) "std::wstring" else "std::string"
case MDate => "std::chrono::system_clock::time_point" case MDate => "std::chrono::system_clock::time_point"
case MBinary => "std::vector<uint8_t>" case MBinary => "std::vector<uint8_t>"
case MOptional => spec.cppOptionalTemplate case MOptional => spec.cppOptionalTemplate
......
...@@ -98,7 +98,7 @@ class JNIMarshal(spec: Spec) extends Marshal(spec) { ...@@ -98,7 +98,7 @@ class JNIMarshal(spec: Spec) extends Marshal(spec) {
} }
case MOptional => "Optional" case MOptional => "Optional"
case MBinary => "Binary" case MBinary => "Binary"
case MString => "String" case MString => if (spec.cppUseWideStrings) "WString" else "String"
case MDate => "Date" case MDate => "Date"
case MList => "List" case MList => "List"
case MSet => "Set" case MSet => "Set"
......
...@@ -35,6 +35,7 @@ object Main { ...@@ -35,6 +35,7 @@ object Main {
var cppNnHeader: Option[String] = None var cppNnHeader: Option[String] = None
var cppNnType: Option[String] = None var cppNnType: Option[String] = None
var cppNnCheckExpression: Option[String] = None var cppNnCheckExpression: Option[String] = None
var cppUseWideStrings: Boolean = false
var javaOutFolder: Option[File] = None var javaOutFolder: Option[File] = None
var javaPackage: Option[String] = None var javaPackage: Option[String] = None
var javaCppException: Option[String] = None var javaCppException: Option[String] = None
...@@ -133,6 +134,8 @@ object Main { ...@@ -133,6 +134,8 @@ object Main {
.text("The type to use for non-nullable pointers (as a substitute for std::shared_ptr)") .text("The type to use for non-nullable pointers (as a substitute for std::shared_ptr)")
opt[String]("cpp-nn-check-expression").valueName("<header>").foreach(x => cppNnCheckExpression = Some(x)) opt[String]("cpp-nn-check-expression").valueName("<header>").foreach(x => cppNnCheckExpression = Some(x))
.text("The expression to use for building non-nullable pointers") .text("The expression to use for building non-nullable pointers")
opt[Boolean]( "cpp-use-wide-strings").valueName("<true/false>").foreach(x => cppUseWideStrings = x)
.text("Use wide strings in C++ code (default: false)")
note("") note("")
opt[File]("jni-out").valueName("<out-folder>").foreach(x => jniOutFolder = Some(x)) opt[File]("jni-out").valueName("<out-folder>").foreach(x => jniOutFolder = Some(x))
.text("The folder for the JNI C++ output files (Generator disabled if unspecified).") .text("The folder for the JNI C++ output files (Generator disabled if unspecified).")
...@@ -293,6 +296,7 @@ object Main { ...@@ -293,6 +296,7 @@ object Main {
cppNnHeader, cppNnHeader,
cppNnType, cppNnType,
cppNnCheckExpression, cppNnCheckExpression,
cppUseWideStrings,
jniOutFolder, jniOutFolder,
jniHeaderOutFolder, jniHeaderOutFolder,
jniIncludePrefix, jniIncludePrefix,
......
...@@ -49,6 +49,7 @@ package object generatorTools { ...@@ -49,6 +49,7 @@ package object generatorTools {
cppNnHeader: Option[String], cppNnHeader: Option[String],
cppNnType: Option[String], cppNnType: Option[String],
cppNnCheckExpression: Option[String], cppNnCheckExpression: Option[String],
cppUseWideStrings: Boolean,
jniOutFolder: Option[File], jniOutFolder: Option[File],
jniHeaderOutFolder: Option[File], jniHeaderOutFolder: Option[File],
jniIncludePrefix: String, jniIncludePrefix: String,
......
...@@ -178,6 +178,25 @@ namespace djinni ...@@ -178,6 +178,25 @@ namespace djinni
} }
}; };
struct WString
{
using CppType = std::wstring;
using JniType = jstring;
using Boxed = WString;
static CppType toCpp(JNIEnv* jniEnv, JniType j)
{
assert(j != nullptr);
return jniWStringFromString(jniEnv, j);
}
static LocalRef<JniType> fromCpp(JNIEnv* jniEnv, const CppType& c)
{
return {jniEnv, jniStringFromWString(jniEnv, c.c_str())};
}
};
struct Binary struct Binary
{ {
using CppType = std::vector<uint8_t>; using CppType = std::vector<uint8_t>;
......
...@@ -371,6 +371,15 @@ jstring jniStringFromUTF8(JNIEnv * env, const std::string & str) { ...@@ -371,6 +371,15 @@ jstring jniStringFromUTF8(JNIEnv * env, const std::string & str) {
return res; return res;
} }
jstring jniStringFromWString(JNIEnv * env, const std::wstring & str) {
std::u16string utf16(str.begin(), str.end());;
jstring res = env->NewString(
reinterpret_cast<const jchar *>(utf16.data()), utf16.length());
DJINNI_ASSERT(res, env);
return res;
}
// UTF-16 decode helpers. // UTF-16 decode helpers.
static inline bool is_high_surrogate(char16_t c) { return (c >= 0xD800) && (c < 0xDC00); } static inline bool is_high_surrogate(char16_t c) { return (c >= 0xD800) && (c < 0xDC00); }
static inline bool is_low_surrogate(char16_t c) { return (c >= 0xDC00) && (c < 0xE000); } static inline bool is_low_surrogate(char16_t c) { return (c >= 0xDC00) && (c < 0xE000); }
...@@ -441,6 +450,20 @@ std::string jniUTF8FromString(JNIEnv * env, const jstring jstr) { ...@@ -441,6 +450,20 @@ std::string jniUTF8FromString(JNIEnv * env, const jstring jstr) {
return out; return out;
} }
std::wstring jniWStringFromString(JNIEnv * env, const jstring jstr) {
DJINNI_ASSERT(jstr, env);
const jsize length = env->GetStringLength(jstr);
jniExceptionCheck(env);
const auto deleter = [env, jstr] (const jchar * c) { env->ReleaseStringChars(jstr, c); };
std::unique_ptr<const jchar, decltype(deleter)> ptr(env->GetStringChars(jstr, nullptr),
deleter);
const char16_t* begin = reinterpret_cast<const char16_t *>(ptr.get());
const char16_t* end = begin + length;
return std::wstring(begin, end);
}
DJINNI_WEAK_DEFINITION DJINNI_WEAK_DEFINITION
void jniSetPendingFromCurrent(JNIEnv * env, const char * ctx) noexcept { void jniSetPendingFromCurrent(JNIEnv * env, const char * ctx) noexcept {
jniDefaultSetPendingFromCurrent(env, ctx); jniDefaultSetPendingFromCurrent(env, ctx);
......
...@@ -543,6 +543,9 @@ private: ...@@ -543,6 +543,9 @@ private:
jstring jniStringFromUTF8(JNIEnv * env, const std::string & str); jstring jniStringFromUTF8(JNIEnv * env, const std::string & str);
std::string jniUTF8FromString(JNIEnv * env, const jstring jstr); std::string jniUTF8FromString(JNIEnv * env, const jstring jstr);
jstring jniStringFromWString(JNIEnv * env, const std::wstring & str);
std::wstring jniWStringFromString(JNIEnv * env, const jstring jstr);
class JniEnum { class JniEnum {
public: public:
/* /*
......
wchar_test_rec = record {
s: string;
}
wchar_test_helpers = interface +c {
static get_record() : wchar_test_rec;
static get_string() : string;
static check_string(s: string) : bool;
static check_record(r: wchar_test_rec) : bool;
}
#include "wchar_test_helpers.hpp"
#include "wchar_test_rec.hpp"
namespace testsuite {
static const std::wstring str1 = L"some string with unicode \u263a symbol";
static const std::wstring str2 = L"another string with unicode \u263b symbol";
WcharTestRec WcharTestHelpers::get_record()
{
return WcharTestRec(str1);
}
std::wstring WcharTestHelpers::get_string()
{
return str2;
}
bool WcharTestHelpers::check_string(const std::wstring & s)
{
return s == str2;
}
bool WcharTestHelpers::check_record(const WcharTestRec & r)
{
return r.s == str1;
}
} // namespace testsuite
...@@ -22,6 +22,7 @@ public class AllTests extends TestSuite { ...@@ -22,6 +22,7 @@ public class AllTests extends TestSuite {
mySuite.addTestSuite(TokenTest.class); mySuite.addTestSuite(TokenTest.class);
mySuite.addTestSuite(DurationTest.class); mySuite.addTestSuite(DurationTest.class);
mySuite.addTestSuite(MockRecordTest.class); mySuite.addTestSuite(MockRecordTest.class);
mySuite.addTestSuite(WcharTest.class);
return mySuite; return mySuite;
} }
......
package com.dropbox.djinni.test;
import junit.framework.TestCase;
public class WcharTest extends TestCase {
private static final String STR1 = "some string with unicode \u263a symbol";
private static final String STR2 = "another string with unicode \u263b symbol";
public void test() {
assertEquals(WcharTestHelpers.getRecord().getS(), STR1);
assertEquals(WcharTestHelpers.getString(), STR2);
assertEquals(WcharTestHelpers.checkString(STR2), true);
assertEquals(WcharTestHelpers.checkRecord(new WcharTestRec(STR1)), true);
}
}
...@@ -17,12 +17,14 @@ base_dir=$(cd "`dirname "$loc"`" && pwd) ...@@ -17,12 +17,14 @@ base_dir=$(cd "`dirname "$loc"`" && pwd)
temp_out="$base_dir/djinni-output-temp" temp_out="$base_dir/djinni-output-temp"
in="$base_dir/djinni/all.djinni" in="$base_dir/djinni/all.djinni"
wchar_in="$base_dir/djinni/wchar_test.djinni"
# Relative version of in and temp_out are used for Djinni call below so that # Relative version of in and temp_out are used for Djinni call below so that
# generated lists of infiles/outfiles are not machine-dependent. This # generated lists of infiles/outfiles are not machine-dependent. This
# is an artifact of the test suite, where we want the genereated files # is an artifact of the test suite, where we want the genereated files
# to be in git for examination. # to be in git for examination.
in_relative="djinni/all.djinni" in_relative="djinni/all.djinni"
wchar_in_relative="djinni/wchar_test.djinni"
temp_out_relative="djinni-output-temp" temp_out_relative="djinni-output-temp"
cpp_out="$base_dir/generated-src/cpp" cpp_out="$base_dir/generated-src/cpp"
...@@ -59,6 +61,35 @@ fi ...@@ -59,6 +61,35 @@ fi
"$base_dir/../src/build" "$base_dir/../src/build"
[ ! -e "$temp_out" ] || rm -r "$temp_out" [ ! -e "$temp_out" ] || rm -r "$temp_out"
(cd "$base_dir" && \ (cd "$base_dir" && \
"$base_dir/../src/run-assume-built" \
--java-out "$temp_out_relative/java" \
--java-package $java_package \
--java-nullable-annotation "javax.annotation.CheckForNull" \
--java-nonnull-annotation "javax.annotation.Nonnull" \
--java-use-final-for-record false \
--ident-java-field mFooBar \
\
--cpp-out "$temp_out_relative/cpp" \
--cpp-namespace testsuite \
--ident-cpp-enum-type foo_bar \
--cpp-optional-template "std::experimental::optional" \
--cpp-optional-header "<experimental/optional>" \
--cpp-extended-record-include-prefix "../../handwritten-src/cpp/" \
--cpp-use-wide-strings true \
\
--jni-out "$temp_out_relative/jni" \
--ident-jni-class NativeFooBar \
--ident-jni-file NativeFooBar \
\
--objc-out "$temp_out_relative/objc" \
--objcpp-out "$temp_out_relative/objc" \
--objc-type-prefix DB \
\
--yaml-out "$temp_out_relative/yaml" \
--yaml-out-file "yaml-test.yaml" \
--yaml-prefix "test_" \
\
--idl "$wchar_in_relative" && \
"$base_dir/../src/run-assume-built" \ "$base_dir/../src/run-assume-built" \
--java-out "$temp_out_relative/java" \ --java-out "$temp_out_relative/java" \
--java-package $java_package \ --java-package $java_package \
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment