Skip to content

Commit

Permalink
Merge pull request scala-js#174 from gzm0/bugfixes
Browse files Browse the repository at this point in the history
Bugfixes
  • Loading branch information
gzm0 committed Jan 27, 2014
2 parents 0386ab6 + 3f59f11 commit 431e837
Show file tree
Hide file tree
Showing 17 changed files with 406 additions and 68 deletions.
12 changes: 8 additions & 4 deletions compiler/src/main/scala/scala/scalajs/compiler/GenJSCode.scala
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,10 @@ abstract class GenJSCode extends plugins.PluginComponent
js.New(envField("ClassTypeData"), List(
js.ObjectConstr(List(classIdent -> js.IntLiteral(0))),
js.BooleanLiteral(isInterface),
js.StringLiteral(sym.fullName),
// Manually add $ to the name if we are a module class. This is to get
// <mod>.getClass.getName right. Note that we do not have to take care
// of implementation classes here since they will not end up here.
js.StringLiteral(sym.fullName + (if (sym.isModuleClass) "$" else "")),
parentData,
ancestorsRecord
) ++ (
Expand Down Expand Up @@ -2006,10 +2009,11 @@ abstract class GenJSCode extends plugins.PluginComponent
case D2L =>
genLongModuleCall("fromDouble", source)

case B2F | B2D | S2F | S2D | C2F | C2D | I2F | I2D =>
source
// Conversions to chars (except for Long)
case B2C | S2C | I2C | F2C | D2C =>
genCallHelper("num2char", source)

case F2B | F2S | F2C | F2I | D2B | D2S | D2C | D2I =>
case F2B | F2S | F2I | D2B | D2S | D2I =>
js.BinaryOp("|", source, js.IntLiteral(0))

case _ => source
Expand Down
12 changes: 10 additions & 2 deletions compiler/src/main/scala/scala/scalajs/compiler/JSEncoding.scala
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,15 @@ trait JSEncoding extends SubComponent { self: GenJSCode =>
private def encodeIsAsInstanceOf(prefix: String)(value: js.Tree, tpe: Type)(
implicit pos: Position): js.Tree = {
toTypeKind(tpe) match {
case REFERENCE(ScalaRTMapped(rtSym)) =>
encodeIsAsInstanceOf(prefix)(value, rtSym.tpe)
case array : ARRAY =>
val elemSym = array.elementKind.toType.typeSymbol match {
case ScalaRTMapped(rtSym) => rtSym
case x => x
}
js.ApplyMethod(envField(prefix+"ArrayOf"),
encodeClassFullNameIdent(array.elementKind.toType.typeSymbol),
encodeClassFullNameIdent(elemSym),
List(value, js.IntLiteral(array.dimensions)))
case _ =>
js.ApplyMethod(envField(prefix),
Expand All @@ -156,8 +162,10 @@ trait JSEncoding extends SubComponent { self: GenJSCode =>

def encodeClassDataOfType(tpe: Type)(implicit pos: Position): js.Tree = {
toTypeKind(tpe) match {
case REFERENCE(ScalaRTMapped(rtSym)) =>
encodeClassDataOfType(rtSym.tpe)
case array : ARRAY =>
var result = encodeClassDataOfSym(array.elementKind.toType.typeSymbol)
var result = encodeClassDataOfType(array.elementKind.toType)
for (i <- 0 until array.dimensions)
result = js.ApplyMethod(result, js.Ident("getArrayOf"), Nil)
result
Expand Down
17 changes: 16 additions & 1 deletion compiler/src/main/scala/scala/scalajs/compiler/TypeKinds.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ trait TypeKinds extends SubComponent {

import definitions.{ UnitClass, BooleanClass, CharClass, ByteClass,
ShortClass, IntClass, LongClass, FloatClass, DoubleClass, ArrayClass,
AnyRefClass, ObjectClass, NullClass, NothingClass, arrayType }
AnyRefClass, ObjectClass, NullClass, NothingClass, arrayType,
RuntimeNullClass, RuntimeNothingClass }

lazy val ObjectReference = REFERENCE(definitions.ObjectClass)

Expand Down Expand Up @@ -194,4 +195,18 @@ trait TypeKinds extends SubComponent {
primitiveTypeMap.getOrElse(sym, newReference(sym))
private def primitiveOrClassType(sym: Symbol, targs: List[Type]) =
primitiveTypeMap.getOrElse(sym, arrayOrClassType(sym, targs))

/**
* Extractor object for Scala runtime mapped types
*
* These are types that are mapped to a different type at runtime.
* Currently scala.Nothing and scala.Null
*/
object ScalaRTMapped {
def unapply(cls: Symbol) = cls match {
case NullClass => Some(RuntimeNullClass)
case NothingClass => Some(RuntimeNothingClass)
case _ => None
}
}
}
33 changes: 31 additions & 2 deletions corejslib/scalajsenv.js
Original file line number Diff line number Diff line change
Expand Up @@ -204,8 +204,27 @@ var ScalaJS = {
objectHashCode: function(instance) {
if (ScalaJS.isScalaJSObject(instance))
return instance.hashCode__I();
else
else if (typeof(instance) === "string") {
// calculate hash of String as specified by JavaDoc
var n = instance.length;
var res = 0;
var mul = 1; // holds pow(31, n-i-1)
// multiplications with `mul` do never overflow the 52 bits of precision:
// - we truncate `mul` to 32 bits on each operation
// - 31 has 5 significant bits only
// - s[i] has 16 significant bits max
// 32 + max(5, 16) = 48 < 52 => no overflow
for (var i = n-1; i >= 0; --i) {
// calculate s[i] * pow(31, n-i-1)
res = res + (instance.charCodeAt(i) * mul | 0) | 0
// update mul for next iteration
mul = mul * 31 | 0
}

return res;
} else {
return 42; // TODO
}
},

comparableCompareTo: function(instance, rhs) {
Expand Down Expand Up @@ -251,6 +270,16 @@ var ScalaJS = {
: ScalaJS.g["Math"]["floor"](value);
},

/** convert a number to a char (unsigned 16bit value) */
num2char: function(value) {
var x = value | 0;
while (x > 65535)
x -= 65536;
while (x < 0)
x += 65536;
return x;
},

propertiesOf: function(obj) {
var result = new Array();
for (var prop in obj)
Expand Down Expand Up @@ -379,7 +408,7 @@ ScalaJS.ArrayTypeData = function(componentData) {
var componentZero = componentData.zero;

// The zero for the Long runtime representation
// is a special case here, since the class has not
// is a special case here, since the class has not
// been defined yet, when this file is read
if (componentZero == "longZero") {
componentZero = ScalaJS.modules.scala_scalajs_runtime_Long().
Expand Down
46 changes: 36 additions & 10 deletions javalib/source/src/java/lang/Numbers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,16 @@ object Integer {
def valueOf(intValue: scala.Int) = new Integer(intValue)

def parseInt(s: String): scala.Int =
js.parseInt(s).toInt

def parseInt(s: String, radix: scala.Int): scala.Int =
js.parseInt(s, radix).toInt
// explicitly specify radix to avoid interpretation as octal (by JS)
parseInt(s, 10)

def parseInt(s: String, radix: scala.Int): scala.Int = {
val res = js.parseInt(s, radix)
if (js.isNaN(res))
throw new NumberFormatException(s"""For input string: "$s"""")
else
res.toInt
}

def toString(i: scala.Int) = valueOf(i).toString

Expand Down Expand Up @@ -215,6 +221,9 @@ object Long {
def signum(i: scala.Long): scala.Long =
if (i == 0) 0 else if (i < 0) -1 else 1

def numberOfLeadingZeros(l: scala.Long) =
toRuntimeLong(l).numberOfLeadingZeros

def toBinaryString(l: scala.Long): String =
dropLZ(toRuntimeLong(l).toBinaryString)
def toHexString(l: scala.Long): String =
Expand Down Expand Up @@ -260,8 +269,14 @@ final class Float(private val value: scala.Float) extends Number {
}

override def toString = {
val s = (value: js.Number).toString()
if (s.indexOf(".") < 0) s + ".0" else s
if (value == 0 && 1 / value < 0) {
"-0.0"
} else {
val s = (value: js.Number).toString()
if (s.indexOf(".") < 0 && !js.isNaN(value))
s + ".0"
else s
}
}

def isNaN: scala.Boolean = Float.isNaN(value)
Expand All @@ -281,8 +296,13 @@ object Float {

def valueOf(floatValue: scala.Float) = new Float(floatValue)

def parseFloat(s: String): scala.Float =
js.parseFloat(s).toFloat
def parseFloat(s: String): scala.Float = {
val res = js.parseFloat(s)
if (s != "NaN" && js.isNaN(res))
throw new NumberFormatException(s"""For input string: "$s"""")
else
res.toFloat
}

def toString(f: scala.Float) = valueOf(f).toString

Expand Down Expand Up @@ -323,8 +343,14 @@ final class Double(private val value: scala.Double) extends Number {
}

override def toString = {
val s = (value: js.Number).toString()
if (s.indexOf(".") < 0) s + ".0" else s
if (value == 0 && 1 / value < 0) {
"-0.0"
} else {
val s = (value: js.Number).toString()
if (s.indexOf(".") < 0 && !js.isNaN(value))
s + ".0"
else s
}
}

def isNaN: scala.Boolean = Double.isNaN(value)
Expand Down
15 changes: 10 additions & 5 deletions javalib/source/src/java/lang/StringBuffer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class StringBuffer(private var content: String) extends CharSequence
def this(csq: CharSequence) = this(csq.toString)

def append(s: String): StringBuffer = {
content += s
content += { if (s == null) "null" else s }
this
}

Expand All @@ -21,11 +21,16 @@ class StringBuffer(private var content: String) extends CharSequence
def append(f: Float): StringBuffer = append(f.toString())
def append(d: Double): StringBuffer = append(d.toString())

def append(obj: AnyRef): StringBuffer = append(obj.toString())
def append(obj: AnyRef): StringBuffer = {
if (obj == null) append(null: String)
else append(obj.toString())
}

def append(csq: CharSequence): StringBuffer = append(csq.toString())
def append(csq: CharSequence, start: Int, end: Int): StringBuffer =
append(csq.subSequence(start, end).toString())
def append(csq: CharSequence): StringBuffer = append(csq: AnyRef)
def append(csq: CharSequence, start: Int, end: Int): StringBuffer = {
if (csq == null) append("null", start, end)
else append(csq.subSequence(start, end).toString())
}

override def toString() = content

Expand Down
15 changes: 10 additions & 5 deletions javalib/source/src/java/lang/StringBuilder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class StringBuilder(private var content: String) extends CharSequence
def this(csq: CharSequence) = this(csq.toString)

def append(s: String): StringBuilder = {
content += s
content += { if (s == null) "null" else s }
this
}

Expand All @@ -21,11 +21,16 @@ class StringBuilder(private var content: String) extends CharSequence
def append(f: Float): StringBuilder = append(f.toString())
def append(d: Double): StringBuilder = append(d.toString())

def append(obj: AnyRef): StringBuilder = append(obj.toString())
def append(obj: AnyRef): StringBuilder = {
if (obj == null) append(null: String)
else append(obj.toString())
}

def append(csq: CharSequence): StringBuilder = append(csq.toString())
def append(csq: CharSequence, start: Int, end: Int): StringBuilder =
append(csq.subSequence(start, end).toString())
def append(csq: CharSequence): StringBuilder = append(csq: AnyRef)
def append(csq: CharSequence, start: Int, end: Int): StringBuilder = {
if (csq == null) append("null", start, end)
else append(csq.subSequence(start, end).toString())
}

override def toString() = content

Expand Down
22 changes: 21 additions & 1 deletion javalib/source/src/java/lang/System.scala
Original file line number Diff line number Diff line change
Expand Up @@ -60,23 +60,43 @@ object System {
}

private[lang] trait JSConsoleBasedPrintStream extends io.PrintStream {
private var buffer: js.String = ""
/** whether buffer is flushed. Can be true even if buffer != "" because of
* line continuations. However, if !flushed => buffer != ""
*/
private var flushed: js.Boolean = true
private var buffer: js.String = ""

private val lineContEnd : js.String = "\u21A9"
private val lineContStart: js.String = "\u21AA"

override def print(s: String): Unit = {
var rest: js.String = if (s eq null) "null" else s
while (!(!rest)) {
val nlPos = rest.indexOf("\n")
if (nlPos < 0) {
buffer += rest
flushed = false
rest = ""
} else {
doWriteLine(buffer + rest.substring(0, nlPos))
buffer = ""
flushed = true
rest = rest.substring(nlPos+1)
}
}
}

/**
* Since we cannot write a partial line in JavaScript, we write a whole
* line with continuation symbol at the end and schedule a line continuation
* symbol for the new line if the buffer is flushed.
*/
override def flush(): Unit = if (!flushed) {
doWriteLine(buffer + lineContEnd)
buffer = lineContStart
flushed = true
}

protected def doWriteLine(line: String): Unit
}

Expand Down
Loading

0 comments on commit 431e837

Please sign in to comment.