Comments (3)
This seems to workaround the problem:
private val privateFieldsSymbol = {
// Check that there is only one private fields symbol.
// This might break if Scala.js changes the way this is implemented and we want to know when that happens.
val first = scala.scalajs.runtime.privateFieldsSymbol()
val second = scala.scalajs.runtime.privateFieldsSymbol()
if (first != second) throw new UnsupportedOperationException("Unable to determine privateFieldSymbol.")
first.asInstanceOf[String | js.Symbol]
}
private def isObject(a: Any): Boolean =
a != null && js.typeOf(a) == "object"
private def combinePropertyDescriptor(first: PropertyDescriptor, second: PropertyDescriptor): PropertyDescriptor = {
// This only updates the value on the descriptor not the property on the object that the descriptor came from.
val newValue = first.value.fold(second.value) { firstValue =>
if (isObject(firstValue) && isObject(second.value)) {
combineOwnProperties[js.Object, js.Object](firstValue.asInstanceOf[js.Object], second.value.asInstanceOf[js.Object])
} else {
second.value
}
}
combineOwnProperties(second, new PropertyDescriptor {
value = newValue
})
}
/**
* Combines all the own properties – enumerable and not enumerable, strings and symbols — of two objects.
*
* Properties from the `second` object will be created after properties from the `first`. When a property is not
* unique, the one from the `second` object will replace the one from the `first`.
*
* @return a new object with the combined properties from both the `first` and `second` objects
*/
def combineOwnProperties[A <: js.Object, B <: js.Object](first: A, second: B): A with B = {
val result = js.Object()
val firstKeys = Reflect.ownKeys(first)
val secondKeys = Reflect.ownKeys(second)
val secondKeysSet = secondKeys.toSet
// We want to be careful with property order.
// Some things may provide a deterministic order in the order that the properties are created.
// All unique keys from the first object will retain their relative order and will come before all other keys.
// Keys from the second object (unique or not) will retain their relative order.
// This means that the position of non-unique keys will move position (unless they are all at the end of the first
// and the start of the second).
firstKeys.foreach { key =>
if (!secondKeysSet.contains(key)) {
val descriptor = Reflect.getOwnPropertyDescriptor(first, key).get
Reflect.defineProperty(result, key, descriptor)
}
}
secondKeys.foreach { key =>
val maybeFirstDescriptor = Reflect.getOwnPropertyDescriptor(first, key)
val secondDescriptor = Reflect.getOwnPropertyDescriptor(second, key).get
val descriptor = maybeFirstDescriptor.fold(secondDescriptor) { firstDescriptor =>
if (key == privateFieldsSymbol) combinePropertyDescriptor(firstDescriptor, secondDescriptor)
else secondDescriptor
}
Reflect.defineProperty(result, key, descriptor)
}
result.asInstanceOf[A with B]
}
from scala-js.
AFAICT, this is as designed. The new Config { ... }
requires some private field to hold routeTitle
. We don't promise that only visible fields will be used to implement a JS class. The compiler is free to create additional private fields.
If you want absolute control over the internals of the produced object, you can create as a js.Dynamic.literal(...)
and fill in all the members yourself. That would be much more robust than fiddling with the internal structure after the fact. Note that scala.scalajs.runtime
is not part of the public API (it's not shown in Scaladoc), just like scala.runtime
.
from scala-js.
My 2 cts here: I do agree that the current behavior violates the principle of least surprise.
In fact, I do not understand why we need the private field:
object HelloWorld {
trait Config extends js.Object {
def title(): String
}
def config() = {
// Inlining this fixes the problem.
// Moving it out of the current block scope also fixes the problem.
// It is getting stored as a private symbol on the config object.
val routeTitle = "My page"
val config = new Config {
override def title(): String = routeTitle
}
config
}
}
Compiles to:
module class helloworld.HelloWorld$ extends java.lang.Object {
def config;Lhelloworld.HelloWorld$Config(): any = {
val routeTitle: java.lang.String = "My page";
val config: any = (arrow-lambda<superClass$: any = constructorOf[scala.scalajs.js.Object], routeTitle$1$2{routeTitle$1}: java.lang.String = routeTitle>(): any = {
var routeTitle$1: java.lang.String = null;
val overload: int = {
routeTitle$1 = routeTitle$1$2;
0
};
val this{this}: any = {};
global:Object["defineProperty"](this, mod:scala.scalajs.runtime.package$.privateFieldsSymbol;Ljava.lang.Object(), {
"value": {
"helloworld.HelloWorld$$anon$1::routeTitle$1": null
}
});
this["title"] = (lambda<>(): any = {
helloworld.HelloWorld$$anon$1::title;Lhelloworld.HelloWorld$$anon$1;Ljava.lang.String(this)
});
this[mod:scala.scalajs.runtime.package$.privateFieldsSymbol;Ljava.lang.Object()]["routeTitle$1"] = routeTitle$1;
(void 0);
this
})();
config
}
constructor def <init>;V() {
this.java.lang.Object::<init>;V();
<storeModule>
}
}
But the following would be clearly better:
module class helloworld.HelloWorld$ extends java.lang.Object {
def config;Lhelloworld.HelloWorld$Config(): any = {
val routeTitle: java.lang.String = "My page";
val config: any = (arrow-lambda<superClass$: any = constructorOf[scala.scalajs.js.Object], routeTitle$1$2{routeTitle$1}: java.lang.String = routeTitle>(): any = {
var routeTitle$1: java.lang.String = null;
val overload: int = {
routeTitle$1 = routeTitle$1$2;
0
};
val this{this}: any = {};
this["title"] = (lambda<routeTitle$3 = routeTitle$1>(): any = {
routeTitle$3
});
(void 0);
this
})();
config
}
constructor def <init>;V() {
this.java.lang.Object::<init>;V();
<storeModule>
}
}
I realize that this will need much more massaging the Scala compiler. But is there ever a case where a capture in Scala cannot be compiled to a closure or class capture in Scala.js IR?
from scala-js.
Related Issues (20)
- Support for "asynchronous exports" HOT 8
- String#codePoints does not compile on scala.js HOT 6
- Implement Java Streams HOT 5
- Add WebAssembly Linker Backend (with WasmGC and Wasm ExceptionHandling) HOT 13
- Assertion failed: Trying to move a local VarDef after the super constructor call of a non-native JS class HOT 4
- AnalyzerRun.WorkTracker loses track of tasks when there is a Failure
- Investigate Fishy Record Names in Optimizer Core HOT 2
- Prevent implicit conversion of A | Null to A | B HOT 1
- Wrong pretty-printing of JS tree that *starts* with an object lit
- Error while running `sbt`: bad constant pool index: 0 at pos: 48445 using Java version 21.0.1 HOT 3
- Race condition in AnalyzerRun.WorkTracker during `allowComplete()`.
- scala.util.Try does not catch `ClassCastException` HOT 1
- ConflictingTopLevelExport error reports incomplete error message
- linkerJS2_13/test spuriously fails too often on the CI (Killed)
- Improve GCC/ Minifier documentation HOT 10
- Analyzer never marks Interfaces with isAnySubclassInstantiated.
- Formatting a byte as a hexadecimal integer produces an incorrect result for a negative value HOT 4
- Generated modules export unnecessary symbols HOT 4
- Excess modules are generated HOT 11
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from scala-js.