summaryrefslogtreecommitdiff
path: root/examples/scala-js/library/src/main/scala/scala/scalajs/runtime/RuntimeString.scala
diff options
context:
space:
mode:
Diffstat (limited to 'examples/scala-js/library/src/main/scala/scala/scalajs/runtime/RuntimeString.scala')
-rw-r--r--examples/scala-js/library/src/main/scala/scala/scalajs/runtime/RuntimeString.scala338
1 files changed, 338 insertions, 0 deletions
diff --git a/examples/scala-js/library/src/main/scala/scala/scalajs/runtime/RuntimeString.scala b/examples/scala-js/library/src/main/scala/scala/scalajs/runtime/RuntimeString.scala
new file mode 100644
index 0000000..f65b1b5
--- /dev/null
+++ b/examples/scala-js/library/src/main/scala/scala/scalajs/runtime/RuntimeString.scala
@@ -0,0 +1,338 @@
+package scala.scalajs.runtime
+
+import scala.scalajs.js
+import scala.scalajs.js.prim.{String => jsString}
+
+import java.nio.ByteBuffer
+import java.nio.charset.Charset
+import java.util.regex._
+
+/** Implementation for methods on java.lang.String.
+ *
+ * Strings are represented at runtime by JavaScript strings, but they have
+ * a lot of methods. The compiler forwards methods on java.lang.String to the
+ * methods in the object, passing `this` as the first argument, that we
+ * consistently call `thiz` in this object.
+ */
+private[runtime] object RuntimeString {
+
+ @inline
+ def charAt(thiz: String, index: Int): Char =
+ (thiz: jsString).charCodeAt(index).asInstanceOf[Int].toChar
+
+ def codePointAt(thiz: String, index: Int): Int = {
+ val high = thiz.charAt(index)
+ if (index+1 < thiz.length) {
+ val low = thiz.charAt(index+1)
+ if (Character.isSurrogatePair(high, low))
+ Character.toCodePoint(high, low)
+ else
+ high.toInt
+ } else {
+ high.toInt
+ }
+ }
+
+ def hashCode(thiz: String): Int = {
+ var res = 0
+ var mul = 1 // holds pow(31, length-i-1)
+ var i = thiz.length-1
+ while (i >= 0) {
+ res += thiz.charAt(i) * mul
+ mul *= 31
+ i -= 1
+ }
+ res
+ }
+
+ @inline
+ def compareTo(thiz: String, anotherString: String): Int = {
+ if (thiz.equals(anotherString)) 0
+ else if ((thiz: jsString) < (anotherString: jsString)) -1
+ else 1
+ }
+
+ def compareToIgnoreCase(thiz: String, str: String): Int =
+ thiz.toLowerCase().compareTo(str.toLowerCase())
+
+ @inline
+ def equalsIgnoreCase(thiz: String, that: String): Boolean =
+ thiz.toLowerCase() == (if (that == null) null else that.toLowerCase())
+
+ @inline
+ def concat(thiz: String, s: String): String =
+ checkNull(thiz) + s
+
+ @inline
+ def contains(thiz: String, s: CharSequence): Boolean =
+ thiz.indexOf(s.toString) != -1
+
+ def endsWith(thiz: String, suffix: String): Boolean =
+ ((thiz: jsString).substring(thiz.length - suffix.length): String) == suffix
+
+ def getBytes(thiz: String): Array[Byte] =
+ thiz.getBytes(Charset.defaultCharset)
+
+ def getBytes(thiz: String, charsetName: String): Array[Byte] =
+ thiz.getBytes(Charset.forName(charsetName))
+
+ def getBytes(thiz: String, charset: Charset): Array[Byte] =
+ charset.encode(thiz).array()
+
+ def getChars(thiz: String, srcBegin: Int, srcEnd: Int,
+ dst: Array[Char], dstBegin: Int): Unit = {
+ if (srcEnd > thiz.length || // first test uses thiz
+ srcBegin < 0 ||
+ srcEnd < 0 ||
+ srcBegin > srcEnd) {
+ throw new StringIndexOutOfBoundsException("Index out of Bound")
+ }
+
+ val offset = dstBegin - srcBegin
+ var i = srcBegin
+ while (i < srcEnd) {
+ dst(i+offset) = thiz.charAt(i)
+ i += 1
+ }
+ }
+
+ def indexOf(thiz: String, ch: Int): Int =
+ thiz.indexOf(fromCodePoint(ch))
+
+ def indexOf(thiz: String, ch: Int, fromIndex: Int): Int =
+ thiz.indexOf(fromCodePoint(ch), fromIndex)
+
+ @inline
+ def indexOf(thiz: String, str: String): Int =
+ (thiz: jsString).indexOf(str).asInstanceOf[Int]
+
+ @inline
+ def indexOf(thiz: String, str: String, fromIndex: Int): Int =
+ (thiz: jsString).indexOf(str, fromIndex).asInstanceOf[Int]
+
+ /* Just returning this string is a valid implementation for `intern` in
+ * JavaScript, since strings are primitive values. Therefore, value equality
+ * and reference equality is the same.
+ */
+ @inline
+ def intern(thiz: String): String =
+ checkNull(thiz)
+
+ @inline
+ def isEmpty(thiz: String): Boolean =
+ checkNull(thiz) == ""
+
+ def lastIndexOf(thiz: String, ch: Int): Int =
+ thiz.lastIndexOf(fromCodePoint(ch))
+
+ def lastIndexOf(thiz: String, ch: Int, fromIndex: Int): Int =
+ thiz.lastIndexOf(fromCodePoint(ch), fromIndex)
+
+ @inline
+ def lastIndexOf(thiz: String, str: String): Int =
+ (thiz: jsString).lastIndexOf(str).asInstanceOf[Int]
+
+ @inline
+ def lastIndexOf(thiz: String, str: String, fromIndex: Int): Int =
+ (thiz: jsString).lastIndexOf(str, fromIndex).asInstanceOf[Int]
+
+ @inline
+ def length(thiz: String): Int =
+ (thiz: jsString).length.asInstanceOf[Int]
+
+ @inline
+ def matches(thiz: String, regex: String): Boolean = {
+ checkNull(thiz)
+ Pattern.matches(regex, thiz)
+ }
+
+ @inline
+ def replace(thiz: String, oldChar: Char, newChar: Char): String =
+ (thiz: String).replace(oldChar.toString, newChar.toString)
+
+ @inline
+ def replace(thiz: String, target: CharSequence, replacement: CharSequence): String =
+ (thiz: jsString).split(target.toString).join(replacement.toString)
+
+ def replaceAll(thiz: String, regex: String, replacement: String): String = {
+ checkNull(thiz)
+ Pattern.compile(regex).matcher(thiz).replaceAll(replacement)
+ }
+
+ def replaceFirst(thiz: String, regex: String, replacement: String): String = {
+ checkNull(thiz)
+ Pattern.compile(regex).matcher(thiz).replaceFirst(replacement)
+ }
+
+ @inline
+ def split(thiz: String, regex: String): Array[String] =
+ thiz.split(regex, 0)
+
+ def split(thiz: String, regex: String, limit: Int): Array[String] = {
+ checkNull(thiz)
+ Pattern.compile(regex).split(thiz, limit)
+ }
+
+ @inline
+ def startsWith(thiz: String, prefix: String): Boolean =
+ thiz.startsWith(prefix, 0)
+
+ @inline
+ def startsWith(thiz: String, prefix: String, toffset: Int): Boolean =
+ ((thiz: jsString).substring(toffset, prefix.length): String) == prefix
+
+ @inline
+ def subSequence(thiz: String, beginIndex: Int, endIndex: Int): CharSequence =
+ thiz.substring(beginIndex, endIndex)
+
+ @inline
+ def substring(thiz: String, beginIndex: Int): String =
+ (thiz: jsString).substring(beginIndex)
+
+ @inline
+ def substring(thiz: String, beginIndex: Int, endIndex: Int): String =
+ (thiz: jsString).substring(beginIndex, endIndex)
+
+ def toCharArray(thiz: String): Array[Char] = {
+ val length = thiz.length
+ val result = new Array[Char](length)
+ var i = 0
+ while (i < length) {
+ result(i) = thiz.charAt(i)
+ i += 1
+ }
+ result
+ }
+
+ @inline
+ def toLowerCase(thiz: String): String =
+ (thiz: jsString).toLowerCase()
+
+ @inline
+ def toUpperCase(thiz: String): String =
+ (thiz: jsString).toUpperCase()
+
+ @inline
+ def trim(thiz: String): String =
+ (thiz: jsString).trim()
+
+ // Constructors
+
+ def newString(): String = ""
+
+ def newString(value: Array[Char]): String =
+ newString(value, 0, value.length)
+
+ def newString(value: Array[Char], offset: Int, count: Int): String = {
+ val end = offset + count
+ if (offset < 0 || end < offset || end > value.length)
+ throw new StringIndexOutOfBoundsException
+
+ val charCodes = new js.Array[Int]
+ var i = offset
+ while (i != end) {
+ charCodes += value(i).toInt
+ i += 1
+ }
+ js.String.fromCharCode(charCodes: _*)
+ }
+
+ def newString(bytes: Array[Byte]): String =
+ newString(bytes, Charset.defaultCharset)
+
+ def newString(bytes: Array[Byte], charsetName: String): String =
+ newString(bytes, Charset.forName(charsetName))
+
+ def newString(bytes: Array[Byte], charset: Charset): String =
+ charset.decode(ByteBuffer.wrap(bytes)).toString()
+
+ def newString(bytes: Array[Byte], offset: Int, length: Int): String =
+ newString(bytes, offset, length, Charset.defaultCharset)
+
+ def newString(bytes: Array[Byte], offset: Int, length: Int,
+ charsetName: String): String =
+ newString(bytes, offset, length, Charset.forName(charsetName))
+
+ def newString(bytes: Array[Byte], offset: Int, length: Int,
+ charset: Charset): String =
+ charset.decode(ByteBuffer.wrap(bytes, offset, length)).toString()
+
+ def newString(codePoints: Array[Int], offset: Int, count: Int): String = {
+ val end = offset + count
+ if (offset < 0 || end < offset || end > codePoints.length)
+ throw new StringIndexOutOfBoundsException
+
+ val charCodes = new js.Array[Int]
+ var i = offset
+ while (i != end) {
+ val cp = codePoints(i)
+ if (cp < 0 || cp > Character.MAX_CODE_POINT)
+ throw new IllegalArgumentException
+ if (cp <= Character.MAX_VALUE) {
+ charCodes += cp
+ } else {
+ val offsetCp = cp - 0x10000
+ charCodes += (offsetCp >> 10) | 0xd800
+ charCodes += (offsetCp & 0x3ff) | 0xdc00
+ }
+ i += 1
+ }
+ js.String.fromCharCode(charCodes: _*)
+ }
+
+ def newString(original: String): String =
+ checkNull(original)
+
+ def newString(buffer: java.lang.StringBuffer): String =
+ buffer.toString
+
+ def newString(builder: java.lang.StringBuilder): String =
+ builder.toString
+
+ // Static methods (aka methods on the companion object)
+
+ def valueOf(value: Boolean): String = value.toString()
+ def valueOf(value: Char): String = value.toString()
+ def valueOf(value: Byte): String = value.toString()
+ def valueOf(value: Short): String = value.toString()
+ def valueOf(value: Int): String = value.toString()
+ def valueOf(value: Long): String = value.toString()
+ def valueOf(value: Float): String = value.toString()
+ def valueOf(value: Double): String = value.toString()
+
+ def valueOf(value: Object): String =
+ if (value eq null) "null" else value.toString()
+
+ def valueOf(data: Array[Char]): String =
+ valueOf(data, 0, data.length)
+
+ def valueOf(data: Array[Char], offset: Int, count: Int): String =
+ newString(data, offset, count)
+
+ def format(format: String, args: Array[AnyRef]): String = {
+ val frm = new java.util.Formatter()
+ val res = frm.format(format, args: _*).toString()
+ frm.close()
+ res
+ }
+
+ // Helpers
+
+ @inline
+ private def checkNull(s: String): s.type =
+ if (s == null) throw new NullPointerException()
+ else s
+
+ private def fromCodePoint(codePoint: Int): String = {
+ if ((codePoint & ~Character.MAX_VALUE) == 0)
+ js.String.fromCharCode(codePoint)
+ else if (codePoint < 0 || codePoint > Character.MAX_CODE_POINT)
+ throw new IllegalArgumentException
+ else {
+ val offsetCp = codePoint - 0x10000
+ js.String.fromCharCode(
+ (offsetCp >> 10) | 0xd800, (offsetCp & 0x3ff) | 0xdc00)
+ }
+ }
+
+}