Skip to content
This repository has been archived by the owner on Jul 12, 2024. It is now read-only.

Remove library patches; keep only our box classes. #20

Merged
merged 4 commits into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ object HijackedClassesDispatchTest {
testToString(true, "true") &&
testToString(54321, "54321") &&
testToString(obj, "Test class") &&
testToString(obj2, "[object]") &&
testToStringStartsWith(obj2, "testsuite.core.HijackedClassesDispatchTest$Test2@") &&
testToString('A', "A") &&
testHashCode(true, 1231) &&
testHashCode(54321, 54321) &&
Expand Down Expand Up @@ -43,6 +43,9 @@ object HijackedClassesDispatchTest {
def testToString(x: Any, expected: String): Boolean =
x.toString() == expected

def testToStringStartsWith(x: Any, expectedPrefix: String): Boolean =
x.toString().startsWith(expectedPrefix)

def testHashCode(x: Any, expected: Int): Boolean =
x.hashCode() == expected

Expand Down
118 changes: 9 additions & 109 deletions wasm/src/main/scala/ir2wasm/LibraryPatches.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,125 +17,25 @@ import wasm.utils.MemClassDefIRFile
/** Patches that we apply to the standard library classes to make them wasm-friendly. */
object LibraryPatches {
def patchIRFiles(irFiles: Seq[IRFile])(implicit ec: ExecutionContext): Future[Seq[IRFile]] = {
val derivedCharBox = new java.util.concurrent.atomic.AtomicReference[IRFile](null)
val derivedLongBox = new java.util.concurrent.atomic.AtomicReference[IRFile](null)

val patched1: Future[Seq[IRFile]] = Future.traverse(irFiles) { irFile =>
val derivedIRFiles: Future[Seq[Option[IRFile]]] = Future.traverse(irFiles) { irFile =>
val irFileImpl = IRFileImpl.fromIRFile(irFile)
irFileImpl.entryPointsInfo.flatMap { entryPointsInfo =>
MethodPatches.get(entryPointsInfo.className) match {
case None =>
entryPointsInfo.className match {
case BoxedCharacterClass | BoxedLongClass =>
irFileImpl.tree.map { classDef =>
val derivedBox = MemClassDefIRFile(deriveBoxClass(classDef))
if (classDef.className == BoxedCharacterClass)
derivedCharBox.set(derivedBox)
else
derivedLongBox.set(derivedBox)
irFile
}
case _ =>
Future.successful(irFile)
entryPointsInfo.className match {
case BoxedCharacterClass | BoxedLongClass =>
irFileImpl.tree.map { classDef =>
Some(MemClassDefIRFile(deriveBoxClass(classDef)))
}
case Some(patches) =>
irFileImpl.tree.map(classDef => MemClassDefIRFile(applyMethodPatches(classDef, patches)))
case _ =>
Future.successful(None)
}
}
}

patched1.map { irFiles1 =>
val extra = List(FloatingPointBitsIRFile, derivedCharBox.get(), derivedLongBox.get())
extra ++ irFiles1
derivedIRFiles.map { derived =>
derived.flatten ++ irFiles
}
}

private val FloatingPointBitsIRFile: IRFile = {
val classDef = ClassDef(
ClassIdent("java.lang.FloatingPointBits$"),
NON,
ModuleClass,
None,
Some(ClassIdent(ObjectClass)),
Nil,
None,
None,
Nil,
List(
trivialCtor("java.lang.FloatingPointBits$"),
MethodDef(
EMF, MethodIdent(m("numberHashCode", List(D), I)), NON,
List(paramDef("value", DoubleType)), IntType,
Some(Block(
// TODO This is not a compliant but it's good enough for now
UnaryOp(UnaryOp.DoubleToInt, VarRef("value")(DoubleType))
))
)(EOH, NOV)
),
None,
Nil,
Nil,
Nil
)(EOH)

MemClassDefIRFile(classDef)
}

private val MethodPatches: Map[ClassName, List[MethodDef]] = {
Map(
ObjectClass -> List(
// TODO Remove this patch when we support getClass() and full string concatenation
MethodDef(
EMF, m("toString", Nil, T), NON,
Nil, ClassType(BoxedStringClass),
Some(StringLiteral("[object]"))
)(EOH, NOV)
),

BoxedCharacterClass.withSuffix("$") -> List(
MethodDef(
EMF, m("toString", List(C), T), NON,
List(paramDef("c", CharType)), ClassType(BoxedStringClass),
Some(BinaryOp(BinaryOp.String_+, StringLiteral(""), VarRef("c")(CharType)))
)(EOH, NOV)
),

BoxedIntegerClass.withSuffix("$") -> List(
MethodDef(
EMF, m("toHexString", List(I), T), NON,
List(paramDef("i", IntType)), ClassType(BoxedStringClass),
Some(
// TODO Write a compliant implementation
BinaryOp(BinaryOp.String_+, StringLiteral(""), VarRef("i")(IntType))
)
)(EOH, NOV)
)
)
}

private def applyMethodPatches(classDef: ClassDef, patches: List[MethodDef]): ClassDef = {
val patchesMap = patches.map(m => m.name.name -> m).toMap
val patchedMethods = classDef.methods.map(m => patchesMap.getOrElse(m.name.name, m))

import classDef._
ClassDef(
name,
originalName,
kind,
jsClassCaptures,
superClass,
interfaces,
jsSuperClass,
jsNativeLoadSpec,
fields,
patchedMethods,
jsConstructor,
jsMethodProps,
jsNativeMembers,
topLevelExportDefs
)(EOH)(pos)
}

/** Generates the accompanying Box class of `Character` or `Long`.
*
* These box classes will be used as the generic representation of `char`s
Expand Down
2 changes: 1 addition & 1 deletion wasm/src/main/scala/ir2wasm/WasmExpressionBuilder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -697,7 +697,7 @@ private class WasmExpressionBuilder private (
case CharToInt | ByteToInt | ShortToInt =>
() // these are no-ops because they are all represented as i32's with the right mathematical value
case IntToLong =>
instrs += I64_EXTEND32_S
instrs += I64_EXTEND_I32_S
case IntToDouble =>
instrs += F64_CONVERT_I32_S
case FloatToDouble =>
Expand Down