summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorYuriy Glukhov <yuriy.glukhov@gmail.com>2016-01-22 11:12:34 +0200
committerYuriy Glukhov <yuriy.glukhov@gmail.com>2016-01-22 11:24:00 +0200
commitc3d09aeeac35d64b3b707b16d53a2945bb5ce348 (patch)
tree85b8959b756c664bc1da493bcafd6d7ff92af65c
parent732479b797422adaadf6891b8d8c32230f548692 (diff)
downloadNim-c3d09aeeac35d64b3b707b16d53a2945bb5ce348.tar.gz
Fixed unicode strings in JS
-rw-r--r--compiler/jsgen.nim35
-rw-r--r--lib/system/jssys.nim45
-rw-r--r--tests/js/tstringitems.nim84
3 files changed, 136 insertions, 28 deletions
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index c36f5a5a3..1e3adf1a9 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -163,8 +163,31 @@ proc mangleName(s: PSym): Rope =
     add(result, rope(s.id))
     s.loc.r = result
 
-proc makeJSString(s: string): Rope =
-  (if s.isNil: "null".rope else: strutils.escape(s).rope)
+proc escapeJSString(s: string): string =
+   result = newStringOfCap(s.len + s.len shr 2)
+   result.add("\"")
+   for c in items(s):
+     case c
+     of '\l': result.add("\\n")
+     of '\r': result.add("\\r")
+     of '\t': result.add("\\t")
+     of '\b': result.add("\\b")
+     of '\a': result.add("\\a")
+     of '\e': result.add("\\e")
+     of '\v': result.add("\\v")
+     of '\\': result.add("\\\\")
+     of '\'': result.add("\\'")
+     of '\"': result.add("\\\"")
+     else: add(result, c)
+   result.add("\"")
+
+proc makeJSString(s: string, escapeNonAscii = true): Rope =
+  if s.isNil:
+    result = "null".rope
+  elif escapeNonAscii:
+    result = strutils.escape(s).rope
+  else:
+    result = escapeJSString(s).rope
 
 include jstypes
 
@@ -568,7 +591,7 @@ proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) =
           if stringSwitch:
             case e.kind
             of nkStrLit..nkTripleStrLit: addf(p.body, "case $1: ",
-                [makeJSString(e.strVal)])
+                [makeJSString(e.strVal, false)])
             else: internalError(e.info, "jsgen.genCaseStmt: 2")
           else:
             gen(p, e, cond)
@@ -1596,10 +1619,10 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
       r.kind = resExpr
   of nkStrLit..nkTripleStrLit:
     if skipTypes(n.typ, abstractVarRange).kind == tyString:
-      useMagic(p, "cstrToNimstr")
-      r.res = "cstrToNimstr($1)" % [makeJSString(n.strVal)]
+      useMagic(p, "makeNimstrLit")
+      r.res = "makeNimstrLit($1)" % [makeJSString(n.strVal)]
     else:
-      r.res = makeJSString(n.strVal)
+      r.res = makeJSString(n.strVal, false)
     r.kind = resExpr
   of nkFloatLit..nkFloat64Lit:
     let f = n.floatVal
diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim
index 3df460952..6eadae17a 100644
--- a/lib/system/jssys.nim
+++ b/lib/system/jssys.nim
@@ -165,15 +165,46 @@ proc SetConstr() {.varargs, asmNoStackFrame, compilerproc.} =
     return result;
   """
 
+proc makeNimstrLit(c: cstring): string {.asmNoStackFrame, compilerproc.} =
+  {.emit: """
+  var ln = `c`.length;
+  var result = new Array(ln + 1);
+  var i = 0;
+  for (; i < ln; ++i) {
+    result[i] = `c`.charCodeAt(i);
+  }
+  result[i] = 0; // terminating zero
+  return result;
+  """.}
+
 proc cstrToNimstr(c: cstring): string {.asmNoStackFrame, compilerproc.} =
-  asm """
-    var result = [];
-    for (var i = 0; i < `c`.length; ++i) {
-      result[i] = `c`.charCodeAt(i);
+  {.emit: """
+  var ln = `c`.length;
+  var result = new Array(ln);
+  var r = 0;
+  for (var i = 0; i < ln; ++i) {
+    var ch = `c`.charCodeAt(i);
+
+    if (ch < 128) {
+      result[r] = ch;
     }
-    result[result.length] = 0; // terminating zero
-    return result;
-  """
+    else if((ch > 127) && (ch < 2048)) {
+      result[r] = (ch >> 6) | 192;
+      ++r;
+      result[r] = (ch & 63) | 128;
+    }
+    else {
+      result[r] = (ch >> 12) | 224;
+      ++r;
+      result[r] = ((ch >> 6) & 63) | 128;
+      ++r;
+      result[r] = (ch & 63) | 128;
+    }
+    ++r;
+  }
+  result[r] = 0; // terminating zero
+  return result;
+  """.}
 
 proc toJSStr(s: string): cstring {.asmNoStackFrame, compilerproc.} =
   asm """
diff --git a/tests/js/tstringitems.nim b/tests/js/tstringitems.nim
index f4ea02fec..20aed6e8b 100644
--- a/tests/js/tstringitems.nim
+++ b/tests/js/tstringitems.nim
@@ -3,22 +3,76 @@ discard """
 Hello'''
 """
 
-# bug #2581
+block: # bug #2581
+  const someVars = [ "Hello" ]
+  var someVars2 = [ "Hello" ]
 
-const someVars = [ "Hello" ]
-var someVars2 = [ "Hello" ]
+  proc getSomeVar: string =
+      for i in someVars:
+          if i == "Hello":
+              result = i
+              break
 
-proc getSomeVar: string =
-    for i in someVars:
-        if i == "Hello":
-            result = i
-            break
+  proc getSomeVar2: string =
+      for i in someVars2:
+          if i == "Hello":
+              result = i
+              break
 
-proc getSomeVar2: string =
-    for i in someVars2:
-        if i == "Hello":
-            result = i
-            break
+  echo getSomeVar()
+  echo getSomeVar2()
 
-echo getSomeVar()
-echo getSomeVar2()
+block: # Test compile-time binary data generation, invalid unicode
+  proc signatureMaker(): string {. compiletime .} =
+    const signatureBytes = [137, 80, 78, 71, 13, 10, 26, 10]
+    result = ""
+    for c in signatureBytes: result.add chr(c)
+
+  const cSig = signatureMaker()
+
+  var rSig = newString(8)
+  rSig[0] = chr(137)
+  rSig[1] = chr(80)
+  rSig[2] = chr(78)
+  rSig[3] = chr(71)
+  rSig[4] = chr(13)
+  rSig[5] = chr(10)
+  rSig[6] = chr(26)
+  rSig[7] = chr(10)
+
+  doAssert(rSig == cSig)
+
+block: # Test unicode strings
+  const constStr = "Привет!"
+  var jsStr : cstring
+  {.emit: """`jsStr`[0] = "Привет!";""".}
+
+  doAssert($jsStr == constStr)
+  var runtimeStr = "При"
+  runtimeStr &= "вет!"
+
+  doAssert(runtimeStr == constStr)
+
+block: # Conversions from/to cstring
+  proc stringSaysHelloInRussian(s: cstring): bool =
+    {.emit: """`result` = (`s` === "Привет!");""".}
+
+  doAssert(stringSaysHelloInRussian("Привет!"))
+
+  const constStr = "Привет!"
+  doAssert(stringSaysHelloInRussian(constStr))
+
+  var rtStr = "Привет!"
+  doAssert(stringSaysHelloInRussian(rtStr))
+
+block: # String case of
+  const constStr = "Привет!"
+  var s = "Привет!"
+
+  case s
+  of constStr: discard
+  else: doAssert(false)
+
+  case s
+  of "Привет!": discard
+  else: doAssert(false)