@@ -20,45 +20,80 @@ import scala.scalajs.runtime.linkingInfo
20
20
21
21
import java .{util => ju }
22
22
23
- import Utils ._
24
-
25
23
object System {
26
- var out : PrintStream = new JSConsoleBasedPrintStream (isErr = false )
27
- var err : PrintStream = new JSConsoleBasedPrintStream (isErr = true )
28
- var in : InputStream = null
24
+ /* System contains a bag of unrelated features. If we naively implement
25
+ * everything inside System, reaching any of these features can reach
26
+ * unrelated code. For example, using `nanoTime()` would reach
27
+ * `JSConsoleBasedPrintStream` and therefore a bunch of `java.io` classes.
28
+ *
29
+ * Instead, every feature that requires its own fields is extracted in a
30
+ * separate private object, and corresponding methods of System delegate to
31
+ * methods of that private object.
32
+ *
33
+ * All non-intrinsic methods are marked `@inline` so that the module accessor
34
+ * of `System` can always be completely elided.
35
+ */
36
+
37
+ // Standard streams (out, err, in) ------------------------------------------
38
+
39
+ private object Streams {
40
+ var out : PrintStream = new JSConsoleBasedPrintStream (isErr = false )
41
+ var err : PrintStream = new JSConsoleBasedPrintStream (isErr = true )
42
+ var in : InputStream = null
43
+ }
29
44
45
+ @ inline
46
+ def out : PrintStream = Streams .out
47
+
48
+ @ inline
49
+ def err : PrintStream = Streams .err
50
+
51
+ @ inline
52
+ def in : InputStream = Streams .in
53
+
54
+ @ inline
30
55
def setIn (in : InputStream ): Unit =
31
- this .in = in
56
+ Streams .in = in
32
57
58
+ @ inline
33
59
def setOut (out : PrintStream ): Unit =
34
- this .out = out
60
+ Streams .out = out
35
61
62
+ @ inline
36
63
def setErr (err : PrintStream ): Unit =
37
- this .err = err
64
+ Streams .err = err
65
+
66
+ // System time --------------------------------------------------------------
38
67
39
- def currentTimeMillis (): scala.Long = {
68
+ @ inline
69
+ def currentTimeMillis (): scala.Long =
40
70
(new js.Date ).getTime().toLong
41
- }
42
71
43
- private [this ] val getHighPrecisionTime : js.Function0 [scala.Double ] = {
44
- import Utils .DynamicImplicits .truthValue
72
+ private object NanoTime {
73
+ val getHighPrecisionTime : js.Function0 [scala.Double ] = {
74
+ import Utils .DynamicImplicits .truthValue
45
75
46
- if (js.typeOf(global.performance) != " undefined" ) {
47
- if (global.performance.now) {
48
- () => global.performance.now().asInstanceOf [scala.Double ]
49
- } else if (global.performance.webkitNow) {
50
- () => global.performance.webkitNow().asInstanceOf [scala.Double ]
76
+ if (js.typeOf(global.performance) != " undefined" ) {
77
+ if (global.performance.now) {
78
+ () => global.performance.now().asInstanceOf [scala.Double ]
79
+ } else if (global.performance.webkitNow) {
80
+ () => global.performance.webkitNow().asInstanceOf [scala.Double ]
81
+ } else {
82
+ () => new js.Date ().getTime()
83
+ }
51
84
} else {
52
85
() => new js.Date ().getTime()
53
86
}
54
- } else {
55
- () => new js.Date ().getTime()
56
87
}
57
88
}
58
89
90
+ @ inline
59
91
def nanoTime (): scala.Long =
60
- (getHighPrecisionTime() * 1000000 ).toLong
92
+ (NanoTime .getHighPrecisionTime() * 1000000 ).toLong
93
+
94
+ // arraycopy ----------------------------------------------------------------
61
95
96
+ // Intrinsic
62
97
def arraycopy (src : Object , srcPos : scala.Int , dest : Object ,
63
98
destPos : scala.Int , length : scala.Int ): Unit = {
64
99
@@ -144,79 +179,93 @@ object System {
144
179
})
145
180
}
146
181
147
- def identityHashCode (x : Object ): scala.Int = {
148
- (x : Any ) match {
149
- case null => 0
150
- case _:scala.Boolean | _:scala.Double | _:String | () =>
151
- x.hashCode()
152
- case _ =>
153
- import IDHashCode ._
154
- if (x.getClass == null ) {
155
- /* x is not a Scala.js object: we have delegate to x.hashCode().
156
- * This is very important, as we really need to go through
157
- * `$objectHashCode()` in `CoreJSLib` instead of using our own
158
- * `idHashCodeMap`. That's because `$objectHashCode()` uses the
159
- * intrinsic `$systemIdentityHashCode` for JS objects, regardless of
160
- * whether the optimizer is enabled or not. If we use our own
161
- * `idHashCodeMap`, we will get different hash codes when obtained
162
- * through `System.identityHashCode(x)` than with `x.hashCode()`.
163
- */
164
- x.hashCode()
165
- } else if (linkingInfo.assumingES6 || idHashCodeMap != null ) {
166
- // Use the global WeakMap of attributed id hash codes
167
- val hash = idHashCodeMap.get(x.asInstanceOf [js.Any ])
168
- if (! Utils .isUndefined(hash)) {
169
- hash.asInstanceOf [Int ]
170
- } else {
171
- val newHash = nextIDHashCode()
172
- idHashCodeMap.set(x.asInstanceOf [js.Any ], newHash.asInstanceOf [js.Any ])
173
- newHash
174
- }
175
- } else {
176
- val hash = x.asInstanceOf [js.Dynamic ].selectDynamic(" $idHashCode$0" )
177
- if (! Utils .isUndefined(hash)) {
178
- /* Note that this can work even if x is sealed, if
179
- * identityHashCode() was called for the first time before x was
180
- * sealed.
181
- */
182
- hash.asInstanceOf [Int ]
183
- } else if (! js.Object .isSealed(x.asInstanceOf [js.Object ])) {
184
- /* If x is not sealed, we can (almost) safely create an additional
185
- * field with a bizarre and relatively long name, even though it is
186
- * technically undefined behavior.
187
- */
188
- val newHash = nextIDHashCode()
189
- x.asInstanceOf [js.Dynamic ].updateDynamic(" $idHashCode$0" )(newHash.asInstanceOf [js.Any ])
190
- newHash
191
- } else {
192
- // Otherwise, we unfortunately have to return a constant.
193
- 42
194
- }
195
- }
196
- }
197
- }
182
+ // identityHashCode ---------------------------------------------------------
198
183
199
184
private object IDHashCode {
200
- private var lastIDHashCode : Int = 0
185
+ private [ this ] var lastIDHashCode : Int = 0
201
186
202
- val idHashCodeMap =
187
+ private [ this ] val idHashCodeMap = {
203
188
if (linkingInfo.assumingES6 || js.typeOf(global.WeakMap ) != " undefined" )
204
189
js.Dynamic .newInstance(global.WeakMap )()
205
190
else
206
191
null
192
+ }
207
193
208
- def nextIDHashCode (): Int = {
194
+ @ inline
195
+ private def nextIDHashCode (): Int = {
209
196
val r = lastIDHashCode + 1
210
197
lastIDHashCode = r
211
198
r
212
199
}
200
+
201
+ def idHashCode (x : Object ): scala.Int = {
202
+ (x : Any ) match {
203
+ case null =>
204
+ 0
205
+ case _:scala.Boolean | _:scala.Double | _:String | () =>
206
+ x.hashCode()
207
+ case _ =>
208
+ if (x.getClass == null ) {
209
+ /* x is not a Scala.js object: we have delegate to x.hashCode().
210
+ * This is very important, as we really need to go through
211
+ * `$objectHashCode()` in `CoreJSLib` instead of using our own
212
+ * `idHashCodeMap`. That's because `$objectHashCode()` uses the
213
+ * intrinsic `$systemIdentityHashCode` for JS objects, regardless
214
+ * of whether the optimizer is enabled or not. If we use our own
215
+ * `idHashCodeMap`, we will get different hash codes when obtained
216
+ * through `System.identityHashCode(x)` than with `x.hashCode()`.
217
+ */
218
+ x.hashCode()
219
+ } else if (linkingInfo.assumingES6 || idHashCodeMap != null ) {
220
+ // Use the global WeakMap of attributed id hash codes
221
+ val hash = idHashCodeMap.get(x.asInstanceOf [js.Any ])
222
+ if (hash ne ().asInstanceOf [AnyRef ]) {
223
+ hash.asInstanceOf [Int ]
224
+ } else {
225
+ val newHash = nextIDHashCode()
226
+ idHashCodeMap.set(x.asInstanceOf [js.Any ],
227
+ newHash.asInstanceOf [js.Any ])
228
+ newHash
229
+ }
230
+ } else {
231
+ val hash = x.asInstanceOf [js.Dynamic ].selectDynamic(" $idHashCode$0" )
232
+ if (hash ne ().asInstanceOf [AnyRef ]) {
233
+ /* Note that this can work even if x is sealed, if
234
+ * identityHashCode() was called for the first time before x was
235
+ * sealed.
236
+ */
237
+ hash.asInstanceOf [Int ]
238
+ } else if (! js.Object .isSealed(x.asInstanceOf [js.Object ])) {
239
+ /* If x is not sealed, we can (almost) safely create an
240
+ * additional field with a bizarre and relatively long name, even
241
+ * though it is technically undefined behavior.
242
+ */
243
+ val newHash = nextIDHashCode()
244
+ x.asInstanceOf [js.Dynamic ].updateDynamic(" $idHashCode$0" )(
245
+ newHash.asInstanceOf [js.Any ])
246
+ newHash
247
+ } else {
248
+ // Otherwise, we unfortunately have to return a constant.
249
+ 42
250
+ }
251
+ }
252
+ }
253
+ }
213
254
}
214
255
256
+ // Intrinsic
257
+ def identityHashCode (x : Object ): scala.Int =
258
+ IDHashCode .idHashCode(x)
259
+
260
+ // System properties --------------------------------------------------------
261
+
215
262
private object SystemProperties {
216
- var dict : js.Dictionary [String ] = loadSystemProperties()
217
- var properties : ju.Properties = null
263
+ import Utils ._
264
+
265
+ private [this ] var dict : js.Dictionary [String ] = loadSystemProperties()
266
+ private [this ] var properties : ju.Properties = null
218
267
219
- private [ System ] def loadSystemProperties (): js.Dictionary [String ] = {
268
+ private def loadSystemProperties (): js.Dictionary [String ] = {
220
269
val result = new js.Object ().asInstanceOf [js.Dictionary [String ]]
221
270
dictSet(result, " java.version" , " 1.8" )
222
271
dictSet(result, " java.vm.specification.version" , " 1.8" )
@@ -233,7 +282,7 @@ object System {
233
282
result
234
283
}
235
284
236
- private [ System ] def forceProperties (): ju.Properties = {
285
+ def getProperties (): ju.Properties = {
237
286
if (properties eq null ) {
238
287
properties = new ju.Properties
239
288
val keys = js.Object .keys(dict.asInstanceOf [js.Object ])
@@ -244,60 +293,90 @@ object System {
244
293
}
245
294
properties
246
295
}
296
+
297
+ def setProperties (properties : ju.Properties ): Unit = {
298
+ if (properties eq null ) {
299
+ dict = loadSystemProperties()
300
+ this .properties = null
301
+ } else {
302
+ dict = null
303
+ this .properties = properties
304
+ }
305
+ }
306
+
307
+ def getProperty (key : String ): String =
308
+ if (dict ne null ) dictGetOrElse(dict, key, null )
309
+ else properties.getProperty(key)
310
+
311
+ def getProperty (key : String , default : String ): String =
312
+ if (dict ne null ) dictGetOrElse(dict, key, default)
313
+ else properties.getProperty(key, default)
314
+
315
+ def clearProperty (key : String ): String =
316
+ if (dict ne null ) dictGetOrElseAndRemove(dict, key, null )
317
+ else properties.remove(key).asInstanceOf [String ]
318
+
319
+ def setProperty (key : String , value : String ): String = {
320
+ if (dict ne null ) {
321
+ val oldValue = getProperty(key)
322
+ dictSet(dict, key, value)
323
+ oldValue
324
+ } else {
325
+ properties.setProperty(key, value).asInstanceOf [String ]
326
+ }
327
+ }
247
328
}
248
329
330
+ @ inline
249
331
def getProperties (): ju.Properties =
250
- SystemProperties .forceProperties ()
332
+ SystemProperties .getProperties ()
251
333
334
+ @ inline
252
335
def lineSeparator (): String = " \n "
253
336
254
- def setProperties (properties : ju.Properties ): Unit = {
255
- if (properties eq null ) {
256
- SystemProperties .dict = SystemProperties .loadSystemProperties()
257
- SystemProperties .properties = null
258
- } else {
259
- SystemProperties .dict = null
260
- SystemProperties .properties = properties
261
- }
262
- }
337
+ @ inline
338
+ def setProperties (properties : ju.Properties ): Unit =
339
+ SystemProperties .setProperties(properties)
263
340
341
+ @ inline
264
342
def getProperty (key : String ): String =
265
- if (SystemProperties .dict ne null ) dictGetOrElse(SystemProperties .dict, key, null )
266
- else SystemProperties .properties.getProperty(key)
343
+ SystemProperties .getProperty(key)
267
344
345
+ @ inline
268
346
def getProperty (key : String , default : String ): String =
269
- if (SystemProperties .dict ne null ) dictGetOrElse(SystemProperties .dict, key, default)
270
- else SystemProperties .properties.getProperty(key, default)
347
+ SystemProperties .getProperty(key, default)
271
348
349
+ @ inline
272
350
def clearProperty (key : String ): String =
273
- if (SystemProperties .dict ne null ) dictGetOrElseAndRemove(SystemProperties .dict, key, null )
274
- else SystemProperties .properties.remove(key).asInstanceOf [String ]
275
-
276
- def setProperty (key : String , value : String ): String = {
277
- if (SystemProperties .dict ne null ) {
278
- val oldValue = getProperty(key)
279
- dictSet(SystemProperties .dict, key, value)
280
- oldValue
281
- } else {
282
- SystemProperties .properties.setProperty(key, value).asInstanceOf [String ]
283
- }
284
- }
351
+ SystemProperties .clearProperty(key)
352
+
353
+ @ inline
354
+ def setProperty (key : String , value : String ): String =
355
+ SystemProperties .setProperty(key, value)
285
356
357
+ // Environment variables ----------------------------------------------------
358
+
359
+ @ inline
286
360
def getenv (): ju.Map [String , String ] =
287
361
ju.Collections .emptyMap()
288
362
363
+ @ inline
289
364
def getenv (name : String ): String = {
290
365
if (name eq null )
291
366
throw new NullPointerException
292
367
293
368
null
294
369
}
295
370
371
+ // Runtime ------------------------------------------------------------------
372
+
296
373
// def exit(status: scala.Int): Unit
374
+
375
+ @ inline
297
376
def gc (): Unit = Runtime .getRuntime().gc()
298
377
}
299
378
300
- private [lang] final class JSConsoleBasedPrintStream (isErr : scala.Boolean )
379
+ private final class JSConsoleBasedPrintStream (isErr : scala.Boolean )
301
380
extends PrintStream (new JSConsoleBasedPrintStream .DummyOutputStream ) {
302
381
303
382
import JSConsoleBasedPrintStream ._
0 commit comments