Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
ddee368
Tigten IR checking of NewLambda.
sjrd Feb 2, 2026
4cb8c94
Fix #5145: Add {ClassDef,IR}Checker tests for NewLambda.
sjrd Feb 16, 2026
41e6063
Close #5311: Deprecate support for JDK < 17.
sjrd Feb 16, 2026
21a2387
Add LinkingInfo.moduleKind as a link-time property.
sjrd Feb 19, 2026
e133016
Use `LinkingInfo.moduleKind` instead of config-dependent `ExportLoopb…
sjrd Feb 19, 2026
186650b
Bump @tootallnate/once and jsdom
dependabot[bot] Mar 4, 2026
55e86db
Merge pull request #5328 from scala-js/dependabot/npm_and_yarn/multi-…
sjrd Mar 5, 2026
3ff1644
Fix #5331: Transform the body of a void typed closure as a statement.
sjrd Mar 8, 2026
62bcaf1
Merge pull request #5332 from sjrd/fix-void-typed-closure-transform
gzm0 Mar 8, 2026
a52330a
Merge pull request #5322 from sjrd/tighten-ir-check-newlambda
gzm0 Mar 8, 2026
2af249a
Merge pull request #5325 from sjrd/link-time-prop-module-kind
gzm0 Mar 8, 2026
af6919b
Merge pull request #5323 from sjrd/deprecate-jdk-under-17
sjrd Mar 8, 2026
218e649
Fix #5144: More direct hashing of method names for lambda class names.
sjrd Feb 16, 2026
5a94e42
Merge pull request #5324 from sjrd/direct-lambda-make-class-name
sjrd Mar 10, 2026
00b2d3f
Grow linear memory in malloc if required
tanishiking Mar 2, 2026
7a289da
Merge pull request #190 from scala-wasm/grow-memory
tanishiking Mar 11, 2026
9dd12da
Fix the buffer growing logic in InputStream.readNBytes.
sjrd Mar 12, 2026
4fb13b0
Merge remote-tracking branch 'upstream/main' into sync-upstream
tanishiking Mar 12, 2026
6c1d1d1
Merge pull request #194 from scala-wasm/sync-upstream
tanishiking Mar 13, 2026
7c7d6d3
Use dedicated ModuleKind's for Wasm without a JS environment.
sjrd Mar 13, 2026
22d48c0
Fix #5335: Throw a user-friendly exception on inconsistent config.
sjrd Mar 13, 2026
07813a1
Merge pull request #5336 from sjrd/early-error-for-inconcistent-config
sjrd Mar 13, 2026
20f9f18
Bump undici from 7.22.0 to 7.24.1
dependabot[bot] Mar 13, 2026
84f168b
Merge pull request #5337 from scala-js/dependabot/npm_and_yarn/undici…
sjrd Mar 14, 2026
74fb0d1
Merge pull request #196 from scala-js/main
tanishiking Mar 14, 2026
15e1fbc
Merge pull request #195 from sjrd/module-kinds-for-wasm-only-settings
tanishiking Mar 14, 2026
0cce77c
Fix: Return 0 in CharArrayReader(_, _, 0) when past the end.
sjrd Mar 4, 2026
dbf2ef5
In StringBuilder, rely on the underlying string's StringIOOBEs.
sjrd Mar 4, 2026
bfed364
Throw a specified IndexOutOfBoundsException in CharArrayWriter.
sjrd Mar 6, 2026
7b06e2c
Trigger UB for StringIOOBEs in all jl.String methods.
sjrd Mar 6, 2026
23bf595
Throw IOOBE instead of ArrayIOOBE in CharArrayReader.read.
sjrd Mar 4, 2026
edd74b6
Trigger a UB `ArrayStoreException` in `System.arraycopy`.
sjrd Mar 4, 2026
dd7be5a
Replace explicit NegativeArraySizeException's by UB ones.
sjrd Mar 4, 2026
4796da5
Merge pull request #5334 from sjrd/fix-inputstream-readnbytes-grow-bu…
gzm0 Mar 15, 2026
705ac07
Merge pull request #5329 from sjrd/javalib-always-ub-for-ub-exceptions
sjrd Mar 15, 2026
417be19
Merge pull request #197 from scala-js/main
tanishiking Mar 19, 2026
cb6b295
Don't use js.Map in ClassValue when targeting pure Wasm
Mar 9, 2026
a5d576e
Fix array of resource
tanishiking Mar 24, 2026
da917b7
Merge pull request #199 from scala-wasm/fix-array-resource
tanishiking Mar 26, 2026
b22230a
Merge pull request #192 from lostflydev/fix/147-classvalue-linktime
tanishiking Mar 27, 2026
d845e81
Allow to allocate direct byte buffers without typed arrays.
sjrd Mar 13, 2026
29b9745
Merge pull request #5340 from sjrd/allow-direct-bytebuffers-without-t…
sjrd Mar 28, 2026
d74a453
Be maximally tolerant in `String.startsWith` with a `null` prefix.
sjrd Mar 16, 2026
3a2e7cf
Make ju.Objects.requireNonNull overloads follow Semantics for UB NPE.
sjrd Mar 15, 2026
c48475d
Use `requireNonNull` for all explicit null checks in the javalib.
sjrd Mar 15, 2026
93f7e0c
Fix missing NPE check in `StringBuilder.replace`.
sjrd Mar 4, 2026
5c94fb4
Merge pull request #5338 from sjrd/javalib-ub-requirenonnull
sjrd Mar 29, 2026
3ca1804
Version 1.21.0.
sjrd Mar 29, 2026
c4e7f43
Towards 1.21.1.
sjrd Mar 30, 2026
5f8a083
Refactor: retrieve WIT type information from symbols in `exitingPhase…
tanishiking Apr 2, 2026
167d23d
Merge pull request #202 from scala-wasm/refactor-exiting-phase
tanishiking Apr 2, 2026
84aea99
Merge remote-tracking branch 'upstream/main' into sync-upstream
tanishiking Apr 3, 2026
aa2022d
Amend ByteBuffer.allocateDirect to take the no-JS code path for pure …
tanishiking Apr 3, 2026
f9d70f0
Merge pull request #203 from scala-wasm/sync-upstream
tanishiking Apr 3, 2026
2f58550
Implement URI for pure Wasm (#151)
Apr 5, 2026
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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ jobs:
esVersion: [ES2015, ES2021] # Some javalib features depend on the target ES version
eh-support: [true, false]
env:
SBT_SET_COMMAND: 'set Seq(Global/enableWasmEverywhere := true, ThisBuild/scalaJSLinkerConfig ~= (_.withESFeatures(_.withESVersion(ESVersion.${{ matrix.esVersion }})).withWasmFeatures(_.withTargetPureWasm(true).withExceptionHandling(${{ matrix.eh-support }}))))'
SBT_SET_COMMAND: 'set Seq(Global/enableWasmEverywhere := true, ThisBuild/scalaJSLinkerConfig ~= (_.withESFeatures(_.withESVersion(ESVersion.${{ matrix.esVersion }})).withModuleKind(ModuleKind.MinimalWasmModule).withWasmFeatures(_.withExceptionHandling(${{ matrix.eh-support }}))))'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ This is a friendly fork of Scala.js, targeting stand-alone Wasm runtimes such as
### `test-suite`
```sh
sbt:Scala.js> set Global/enableWasmEverywhere := true
sbt:Scala.js> set scalaJSLinkerConfig in testSuite.v2_12 ~= (_.withWasmFeatures(_.withTargetPureWasm(true)))
sbt:Scala.js> set scalaJSLinkerConfig in testSuite.v2_12 ~= (_.withModuleKind(ModuleKind.MinimalWasmModule))
sbt:Scala.js> testSuite2_12/test
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -696,7 +696,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
val info = jsInterop.witExportOf(dd.symbol).get
for (method <- genMethod(dd)) {
methodsBuilder += method
witExportDefsBuilder += genWitExportDef(info, method)
witExportDefsBuilder += genWitExportDef(info, dd.symbol, method)
}
} else {
methodsBuilder ++= genMethod(dd)
Expand Down
89 changes: 60 additions & 29 deletions compiler/src/main/scala/org/scalajs/nscplugin/GenWitInterop.scala
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,8 @@ trait GenWitInterop[G <: Global with Singleton] extends SubComponent {
implicit val pos = tree.pos
val sym = tree.symbol
withNewLocalNameScope {
val funcType = jsInterop.witFunctionTypeOf(sym)
val baseParams = funcType.params.map(toWIT(_))
val (paramTypes, resultType) = witMethodSignatureOf(sym)
val baseParams = paramTypes.map(toWIT(_))
val params = name match {
case _:js.WitFunctionName.Function |
_:js.WitFunctionName.ResourceConstructor |
Expand All @@ -146,7 +146,7 @@ trait GenWitInterop[G <: Global with Singleton] extends SubComponent {
}
val witFuncType = wit.FuncType(
params,
toResultWIT(funcType.resultType)
toResultWIT(resultType)
)
js.WitNativeMemberDef(flags, moduleName, name,
encodeMethodSym(sym), witFuncType)
Expand All @@ -158,8 +158,7 @@ trait GenWitInterop[G <: Global with Singleton] extends SubComponent {
val sym = tree.symbol

val flags = js.MemberFlags.empty.withNamespace(js.MemberNamespace.PublicStatic)
val funcType = jsInterop.witFunctionTypeOf(sym)

val (paramTypes, resultType) = witMethodSignatureOf(sym)
for {
methodAnnot <- sym.getAnnotation(WitResourceStaticMethodAnnotation)
resourceAnnot <- sym.owner.companionClass.getAnnotation(WitResourceImportAnnotation)
Expand All @@ -170,8 +169,8 @@ trait GenWitInterop[G <: Global with Singleton] extends SubComponent {
val name = js.WitFunctionName.ResourceStaticMethod(
func = methodName, resource = resourceName)
withNewLocalNameScope {
val params = funcType.params.map(p => toWIT(p))
val ft = wit.FuncType(params, toResultWIT(funcType.resultType))
val params = paramTypes.map(toWIT(_))
val ft = wit.FuncType(params, toResultWIT(resultType))
js.WitNativeMemberDef(flags, moduleName, name, encodeMethodSym(sym), ft)
}
}
Expand All @@ -182,7 +181,7 @@ trait GenWitInterop[G <: Global with Singleton] extends SubComponent {
val sym = tree.symbol

val flags = js.MemberFlags.empty.withNamespace(js.MemberNamespace.PublicStatic)
val funcType = jsInterop.witFunctionTypeOf(sym)
val (paramTypes, resultType) = witMethodSignatureOf(sym)

for {
methodAnnot <- sym.getAnnotation(WitResourceConstructorAnnotation)
Expand All @@ -192,19 +191,20 @@ trait GenWitInterop[G <: Global with Singleton] extends SubComponent {
} yield {
val name = js.WitFunctionName.ResourceConstructor(resourceName)
withNewLocalNameScope {
val params = funcType.params.map(p => toWIT(p))
val ft = wit.FuncType(params, toResultWIT(funcType.resultType))
val params = paramTypes.map(toWIT(_))
val ft = wit.FuncType(params, toResultWIT(resultType))
js.WitNativeMemberDef(flags, moduleName, name, encodeMethodSym(sym), ft)
}
}
}

def genWitExportDef(info: jsInterop.WitExportInfo,
def genWitExportDef(info: jsInterop.WitExportInfo, sym: Symbol,
methodDef: js.MethodDef): js.WitExportDef = {
withNewLocalNameScope {
val (paramTypes, resultType) = witMethodSignatureOf(sym)
val signature = wit.FuncType(
info.signature.params.map(toWIT(_)),
toResultWIT(info.signature.resultType)
paramTypes.map(toWIT(_)),
toResultWIT(resultType)
)
js.WitExportDef(
info.moduleName,
Expand All @@ -215,15 +215,46 @@ trait GenWitInterop[G <: Global with Singleton] extends SubComponent {
}
}

private def witMethodSignatureOf(sym: Symbol): (List[Type], Type) = {
exitingPhase(currentRun.typerPhase) {
val methodType = sym.tpe
val params =
if (methodType.paramss.isEmpty) Nil
else methodType.paramss.head.map(_.tpe)
(params, methodType.resultType)
}
}

private def witVariantValueTypeOf(sym: Symbol): Type = {
exitingPhase(currentRun.typerPhase) {
if (sym.isModuleClass) {
UnitTpe
} else if (sym.isClass && sym.isFinal && !sym.isTrait) {
sym.primaryConstructor.paramss.flatten match {
case Nil =>
UnitTpe
case param :: Nil =>
param.tpe
case _ =>
throw new AssertionError(s"Invalid WIT variant case shape for $sym")
}
} else {
throw new AssertionError(s"Invalid WIT variant case symbol $sym")
}
}
}

private def toWIT(tpe: Type): wit.ValType = {
unsigned2WIT.get(tpe.typeSymbolDirect).orElse {
toWITMaybeArray(tpe.dealiasWiden)
val widenedTpe = exitingPhase(currentRun.typerPhase)(tpe.dealiasWiden)

unsigned2WIT.get(widenedTpe.typeSymbolDirect).orElse {
toWITMaybeArray(widenedTpe)
}.orElse {
primitiveIRWIT.get(toIRType(tpe.dealiasWiden))
primitiveIRWIT.get(toIRType(widenedTpe))
}.getOrElse {
tpe.dealiasWiden.typeSymbol match {
widenedTpe.typeSymbol match {
case tsym if isWasmComponentTupleClass(tsym) =>
wit.TupleType(tpe.typeArgs.map(toWIT(_)))
wit.TupleType(widenedTpe.baseType(tsym).typeArgs.map(toWIT(_)))

case tsym if tsym.hasAnnotation(WitFlagsAnnotation) =>
// Read numFlags from annotation parameter
Expand All @@ -236,26 +267,26 @@ trait GenWitInterop[G <: Global with Singleton] extends SubComponent {
wit.FlagsType(className, numFlags)

case tsym if isWasmWitRecordClass(tsym) =>
// TODO: it needs to be sorted by the order of record in wit definition
val className = encodeClassName(tsym)
val fields: List[wit.FieldType] = tsym.info.decls.collect {
case f if f.isField =>
val label = encodeFieldSym(f)(f.pos).name
val fieldType = jsInterop.witVariantValueTypeOf(f)
val valueType = toWIT(fieldType)
wit.FieldType(label, valueType)
}.toList
val fields = exitingPhase(currentRun.typerPhase) {
tsym.primaryConstructor.paramss.flatten.map { param =>
(param.name.dropLocal.toString(), param.tpe)
}
}.map { case (fieldName, fieldType) =>
val label = Names.FieldName(className, Names.SimpleFieldName(fieldName))
wit.FieldType(label, toWIT(fieldType))
}
wit.RecordType(className, fields)

case tsym if isWasmWitResourceType(tsym) =>
wit.ResourceType(encodeClassName(tsym))

case tsym if tsym.isSubClass(ComponentResultClass) && tsym.isSealed =>
val List(ok, err) = tpe.typeArgs
val List(ok, err) = widenedTpe.baseType(ComponentResultClass).typeArgs
wit.ResultType(toResultWIT(ok), toResultWIT(err))

case tsym if tsym.fullName == "java.util.Optional" =>
val List(t) = tpe.dealiasWiden.typeArgs
val List(t) = widenedTpe.baseType(tsym).typeArgs
wit.OptionType(toWIT(t))

case tsym if tsym.hasAnnotation(WitVariantAnnotation) && tsym.isSealed =>
Expand All @@ -266,7 +297,7 @@ trait GenWitInterop[G <: Global with Singleton] extends SubComponent {
val cases = tsym.sealedChildren.toList.sortBy(_.pos.line) map { child =>
// assert(child.isFinal)
// assert(child.isClass)
val valueType = jsInterop.witVariantValueTypeOf(child)
val valueType = witVariantValueTypeOf(child)
val caseTyp = if (toIRType(valueType) == jstpe.VoidType) {
None
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,6 @@ trait JSGlobalAddons extends JSDefinitions with CompatComponent {
private val jsNativeLoadSpecs =
mutable.Map.empty[Symbol, JSNativeLoadSpec]

private val componentFunctionTypes =
mutable.Map.empty[Symbol, WitFunctionType]

private val WitVariantValueTypes =
mutable.Map.empty[Symbol, Type]

private val exportPrefix = "$js$exported$"
private val methodExportPrefix = exportPrefix + "meth$"
private val propExportPrefix = exportPrefix + "prop$"
Expand All @@ -125,16 +119,10 @@ trait JSGlobalAddons extends JSDefinitions with CompatComponent {
val pos: Position)
extends ExportInfo

case class WitExportInfo(moduleName: String, name: String,
signature: WitFunctionType)(
case class WitExportInfo(moduleName: String, name: String)(
val pos: Position)
extends ExportInfo

case class WitFunctionType(
params: List[Type],
resultType: Type
)

case class StaticExportInfo(jsName: String)(val pos: Position) extends ExportInfo

sealed abstract class JSName {
Expand Down Expand Up @@ -420,17 +408,6 @@ trait JSGlobalAddons extends JSDefinitions with CompatComponent {
def jsNativeLoadSpecOfOption(sym: Symbol): Option[JSNativeLoadSpec] =
jsNativeLoadSpecs.get(sym)

def storeWitVariantValueType(sym: Symbol, valueType: Type): Unit =
WitVariantValueTypes(sym) = valueType

def witVariantValueTypeOf(sym: Symbol): Type =
WitVariantValueTypes(sym)

def storeWitFunctionType(sym: Symbol, funcType: WitFunctionType): Unit =
componentFunctionTypes(sym) = funcType

def witFunctionTypeOf(sym: Symbol): WitFunctionType =
componentFunctionTypes(sym)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -140,11 +140,7 @@ trait PrepJSExports[G <: Global with Singleton] { this: PrepJSInterop[G] =>

val wasmComponent = exports.collect {
case info @ ExportInfo(moduleName, ExportDestination.WasmComponent(name)) =>
val signature = jsInterop.WitFunctionType(
(if (sym.tpe.paramss.isEmpty) Nil else sym.tpe.paramss.head).map(_.tpe),
sym.tpe.resultType
)
jsInterop.WitExportInfo(moduleName, name, signature)(info.pos)
jsInterop.WitExportInfo(moduleName, name)(info.pos)
}

if (sym.isMethod && wasmComponent.nonEmpty) {
Expand Down
29 changes: 0 additions & 29 deletions compiler/src/main/scala/org/scalajs/nscplugin/PrepJSInterop.scala
Original file line number Diff line number Diff line change
Expand Up @@ -862,12 +862,6 @@ abstract class PrepJSInterop[G <: Global with Singleton](val global: G)
reporter.error(pos,
s"Return type '${returnType}' is not compatible with Component Model")
}

val funcType = jsInterop.WitFunctionType(
(if (sym.tpe.paramss.isEmpty) Nil else sym.tpe.paramss.head).map(_.tpe),
sym.tpe.resultType
)
jsInterop.storeWitFunctionType(sym, funcType)
}
}
}
Expand Down Expand Up @@ -953,12 +947,6 @@ abstract class PrepJSInterop[G <: Global with Singleton](val global: G)
overridden.fullName)
}
}

val funcType = jsInterop.WitFunctionType(
(if (member.tpe.paramss.isEmpty) Nil else member.tpe.paramss.head).map(_.tpe),
member.tpe.resultType
)
jsInterop.storeWitFunctionType(member, funcType)
}
}

Expand Down Expand Up @@ -991,12 +979,6 @@ abstract class PrepJSInterop[G <: Global with Singleton](val global: G)
s"Public method '${member.name}' in companion object of @WitResourceImport trait must be " +
"annotated with @WitResourceConstructor or @WitResourceStaticMethod")
}

val funcType = jsInterop.WitFunctionType(
(if (member.tpe.paramss.isEmpty) Nil else member.tpe.paramss.head).map(_.tpe),
member.tpe.resultType
)
jsInterop.storeWitFunctionType(member, funcType)
}
}
}
Expand Down Expand Up @@ -1024,7 +1006,6 @@ abstract class PrepJSInterop[G <: Global with Singleton](val global: G)
private def validateWitVariantCase(caseSym: Symbol): Unit = {
if (caseSym.isModuleClass) {
// Regular object (enum case without payload)
jsInterop.storeWitVariantValueType(caseSym, definitions.UnitTpe)
} else if (caseSym.isClass && caseSym.isFinal && !caseSym.isTrait) {
// Final class (variant case with payload)
val primaryCtor = caseSym.primaryConstructor
Expand All @@ -1051,12 +1032,9 @@ abstract class PrepJSInterop[G <: Global with Singleton](val global: G)
} else if (!isComponentModelCompatible(fieldType)) {
reporter.error(param.pos,
s"Field '${param.name}' has type '${fieldType}' which is not compatible with Component Model. ")
} else {
jsInterop.storeWitVariantValueType(caseSym, fieldType)
}
} else {
// Zero parameters - enum case defined as a class
jsInterop.storeWitVariantValueType(caseSym, definitions.UnitTpe)
}
} else {
reporter.error(caseSym.pos,
Expand Down Expand Up @@ -1121,13 +1099,6 @@ abstract class PrepJSInterop[G <: Global with Singleton](val global: G)
s"Field '${param.name}' has type '${fieldType}' which is not compatible with Component Model")
}
}
// Store field types for code generation
for {
f <- sym.info.decls
if !f.isMethod && f.isField
} {
jsInterop.storeWitVariantValueType(f, f.tpe)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import java.util.Optional
@WitImplementation
object TestImportsImpl extends TestImports {
override def run(): Unit = {
def newCounterArray(size: Int): Array[Counter] =
new Array[Counter](size)

val start = System.currentTimeMillis()

Expand Down Expand Up @@ -62,6 +64,15 @@ object TestImportsImpl extends TestImports {
val arr3 = Array[C1](C1.A(0), C1.B(3))
assert(arr3.sameElements(roundtripListVariant(arr3)))

val counter1 = Counter(1)
val counter2 = Counter(2)
val counterArr = newCounterArray(2)
assert(counterArr.length == 2)
counterArr(0) = counter1
counterArr(1) = counter2
assert(counterArr(0).valueOf() == 1)
assert(counterArr(1).valueOf() == 2)

assert("foo" == roundtripString("foo"))
assert("" == roundtripString(""))
assert(Point(0, 5) == roundtripPoint(Point(0, 5)))
Expand Down
4 changes: 2 additions & 2 deletions ir/shared/src/main/scala/org/scalajs/ir/ScalaJSVersions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ import java.util.concurrent.ConcurrentHashMap
import scala.util.matching.Regex

object ScalaJSVersions extends VersionChecks(
current = "1.21.0-SNAPSHOT",
binaryEmitted = "1.21-SNAPSHOT"
current = "1.21.1-SNAPSHOT",
binaryEmitted = "1.21"
)

/** Helper class to allow for testing of logic. */
Expand Down
Loading
Loading