@@ -275,17 +275,54 @@ object Val {
275275 final case class Arr (var pos : Position , private val arr : Array [? <: Eval ]) extends Literal {
276276 def prettyName = " array"
277277
278+ // Lazy range support: null entries at indices 0..<rangeLen represent
279+ // Val.Num(_rangePos, rangeFrom + i). Avoids allocating one Val.Num per element.
280+ private var rangeFrom : Int = 0
281+ private var rangeLen : Int = 0
282+ private var _rangePos : Position = null
283+
284+ @ inline def isRange : Boolean = _rangePos != null
285+
286+ def setRange (from : Int , len : Int , rpos : Position ): Unit = {
287+ rangeFrom = from
288+ rangeLen = len
289+ _rangePos = rpos
290+ }
291+
292+ private def materializeRange (): Unit = {
293+ val a = arr.asInstanceOf [Array [Eval ]]
294+ val from = rangeFrom
295+ val rp = _rangePos
296+ val rl = rangeLen
297+ var i = 0
298+ while (i < rl) {
299+ if (a(i) == null ) a(i) = Val .Num (rp, from + i)
300+ i += 1
301+ }
302+ _rangePos = null
303+ }
304+
278305 override def asArr : Arr = this
279306 def length : Int = arr.length
280- def value (i : Int ): Val = arr(i).value
307+ def value (i : Int ): Val = {
308+ val e = arr(i)
309+ if (e != null ) e.value
310+ else Val .Num (_rangePos, rangeFrom + i)
311+ }
312+
313+ /** Raw backing array (may contain nulls for range elements). */
314+ def rawArray : Array [? <: Eval ] = arr
281315
282- def asLazyArray : Array [Eval ] = arr.asInstanceOf [Array [Eval ]]
316+ def asLazyArray : Array [Eval ] = {
317+ if (_rangePos != null ) materializeRange()
318+ arr.asInstanceOf [Array [Eval ]]
319+ }
283320 def asStrictArray : Array [Val ] = {
284321 val len = arr.length
285322 val result = new Array [Val ](len)
286323 var i = 0
287324 while (i < len) {
288- result(i) = arr (i).value
325+ result(i) = value (i)
289326 i += 1
290327 }
291328 result
@@ -296,24 +333,35 @@ object Val {
296333 val rArr = rhs.arr
297334 val lLen = lArr.length
298335 val rLen = rArr.length
336+ // Materialize rhs range to avoid propagating two sets of range metadata
337+ if (rhs._rangePos != null ) rhs.materializeRange()
299338 val result = new Array [Eval ](lLen + rLen)
300339 System .arraycopy(lArr, 0 , result, 0 , lLen)
301340 System .arraycopy(rArr, 0 , result, lLen, rLen)
302- Arr (newPos, result)
341+ val r = Arr (newPos, result)
342+ if (_rangePos != null ) {
343+ r.rangeFrom = rangeFrom
344+ r.rangeLen = rangeLen
345+ r._rangePos = _rangePos
346+ }
347+ r
303348 }
304349
305- def iterator : Iterator [Val ] = arr.iterator.map(_.value)
350+ def iterator : Iterator [Val ] = {
351+ if (_rangePos != null ) materializeRange()
352+ arr.iterator.map(_.value)
353+ }
306354 def foreach [U ](f : Val => U ): Unit = {
307355 var i = 0
308356 while (i < arr.length) {
309- f(arr (i).value )
357+ f(value (i))
310358 i += 1
311359 }
312360 }
313361 def forall (f : Val => Boolean ): Boolean = {
314362 var i = 0
315363 while (i < arr.length) {
316- if (! f(arr (i).value )) return false
364+ if (! f(value (i))) return false
317365 i += 1
318366 }
319367 true
@@ -418,7 +466,9 @@ object Val {
418466 private val staticLayout : StaticObjectLayout = null ,
419467 private val staticValues : Array [Val ] = null ,
420468 private val singleFieldKey : String = null ,
421- private val singleFieldMember : Obj .Member = null )
469+ private val singleFieldMember : Obj .Member = null ,
470+ private val inlineFieldKeys : Array [String ] = null ,
471+ private val inlineFieldMembers : Array [Obj .Member ] = null )
422472 extends Literal
423473 with Expr .ObjBody {
424474 private var asserting : Boolean = false
@@ -444,6 +494,18 @@ object Val {
444494 val m = Util .preSizedJavaLinkedHashMap[String , Val .Obj .Member ](1 )
445495 m.put(singleFieldKey, singleFieldMember)
446496 this .value0 = m
497+ } else if (inlineFieldKeys != null ) {
498+ // Multi-field inline object: lazily construct LinkedHashMap from arrays
499+ val keys = inlineFieldKeys
500+ val members = inlineFieldMembers
501+ val n = keys.length
502+ val m = Util .preSizedJavaLinkedHashMap[String , Val .Obj .Member ](n)
503+ var i = 0
504+ while (i < n) {
505+ m.put(keys(i), members(i))
506+ i += 1
507+ }
508+ this .value0 = m
447509 } else {
448510 // value0 is always defined for non-static objects, so if we're computing it here
449511 // then that implies that the object is static and therefore valueCache should be
@@ -648,13 +710,25 @@ object Val {
648710 }
649711
650712 @ inline def hasKeys : Boolean = {
651- val m = if (static || `super` != null ) getAllKeys else getValue0
652- ! m.isEmpty
713+ if (inlineFieldKeys != null && `super` == null ) inlineFieldKeys.length > 0
714+ else {
715+ val m = if (static || `super` != null ) getAllKeys else getValue0
716+ ! m.isEmpty
717+ }
653718 }
654719
655720 @ inline def containsKey (k : String ): Boolean = {
656721 if (staticLayout != null && `super` == null ) staticLayout.indices.containsKey(k)
657- else {
722+ else if (inlineFieldKeys != null && `super` == null ) {
723+ val keys = inlineFieldKeys
724+ val n = keys.length
725+ var i = 0
726+ while (i < n) {
727+ if (keys(i).equals(k)) return true
728+ i += 1
729+ }
730+ false
731+ } else {
658732 val m = if (static || `super` != null ) getAllKeys else getValue0
659733 m.containsKey(k)
660734 }
@@ -663,6 +737,16 @@ object Val {
663737 @ inline def containsVisibleKey (k : String ): Boolean = {
664738 if (static || `super` != null ) {
665739 getAllKeys.get(k) == java.lang.Boolean .FALSE
740+ } else if (inlineFieldKeys != null ) {
741+ val keys = inlineFieldKeys
742+ val members = inlineFieldMembers
743+ val n = keys.length
744+ var i = 0
745+ while (i < n) {
746+ if (keys(i).equals(k)) return members(i).visibility != Visibility .Hidden
747+ i += 1
748+ }
749+ false
666750 } else {
667751 val m = getValue0.get(k)
668752 m != null && (m.visibility != Visibility .Hidden )
@@ -671,6 +755,7 @@ object Val {
671755
672756 lazy val allKeyNames : Array [String ] = {
673757 if (staticLayout != null && `super` == null ) staticLayout.keys.clone()
758+ else if (inlineFieldKeys != null && `super` == null ) inlineFieldKeys.clone()
674759 else {
675760 val m = if (static || `super` != null ) getAllKeys else getValue0
676761 m.keySet().toArray(new Array [String ](m.size()))
@@ -682,9 +767,33 @@ object Val {
682767 if (keys.length < 2 ) keys else keys.sorted(Util .CodepointStringOrdering )
683768 }
684769
685- lazy val visibleKeyNames : Array [String ] = {
770+ lazy val visibleKeyNames : Array [String ] = computeVisibleKeyNames()
771+
772+ private def computeVisibleKeyNames (): Array [String ] = {
686773 if (static) {
687774 allKeyNames
775+ } else if (inlineFieldKeys != null && `super` == null ) {
776+ // Inline multi-field fast path: check if all visible (common case)
777+ val keys = inlineFieldKeys
778+ val members = inlineFieldMembers
779+ val n = keys.length
780+ var allVisible = true
781+ var i = 0
782+ while (allVisible && i < n) {
783+ if (members(i).visibility == Visibility .Hidden ) allVisible = false
784+ i += 1
785+ }
786+ if (allVisible) keys
787+ else {
788+ val buf = new mutable.ArrayBuilder .ofRef[String ]
789+ buf.sizeHint(n)
790+ var j = 0
791+ while (j < n) {
792+ if (members(j).visibility != Visibility .Hidden ) buf += keys(j)
793+ j += 1
794+ }
795+ buf.result()
796+ }
688797 } else {
689798 val buf = new mutable.ArrayBuilder .ofRef[String ]
690799 if (`super` == null ) {
@@ -796,6 +905,31 @@ object Val {
796905 } else {
797906 if (s == null ) null else s.valueRaw(k, self, pos, addTo, addKey)
798907 }
908+ } else if (inlineFieldKeys != null ) {
909+ // Inline multi-field fast path: linear scan over small arrays
910+ val keys = inlineFieldKeys
911+ val members = inlineFieldMembers
912+ val n = keys.length
913+ var i = 0
914+ while (i < n) {
915+ if (keys(i).equals(k)) {
916+ val m = members(i)
917+ if (! evaluator.settings.brokenAssertionLogic || ! m.deprecatedSkipAsserts) {
918+ self.triggerAllAsserts(evaluator.settings.brokenAssertionLogic)
919+ }
920+ val vv = m.invoke(self, s, pos.fileScope, evaluator)
921+ val v = if (s != null && m.add) {
922+ s.valueRaw(k, self, pos, null , null ) match {
923+ case null => vv
924+ case supValue => mergeMember(supValue, vv, pos)
925+ }
926+ } else vv
927+ if (addTo != null && m.cached) addTo.put(addKey, v)
928+ return v
929+ }
930+ i += 1
931+ }
932+ if (s == null ) null else s.valueRaw(k, self, pos, addTo, addKey)
799933 } else {
800934 getValue0.get(k) match {
801935 case null =>
0 commit comments