diff options
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.scala | 338 |
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) + } + } + +} |