@@ -17,6 +17,7 @@ import org.jacodb.ets.model.EtsDivExpr
1717import org.jacodb.ets.model.EtsEntity
1818import org.jacodb.ets.model.EtsEqExpr
1919import org.jacodb.ets.model.EtsExpExpr
20+ import org.jacodb.ets.model.EtsFunctionType
2021import org.jacodb.ets.model.EtsGlobalRef
2122import org.jacodb.ets.model.EtsGtEqExpr
2223import org.jacodb.ets.model.EtsGtExpr
@@ -198,6 +199,7 @@ class EtsConcreteInterpreter(
198199 // Immediates
199200 is EtsLocal -> frame.locals[e.name]
200201 ? : frame.args.getOrNull(parameterIndexOfLocal(frame, e.name) ? : - 1 )
202+ ? : functionValueOf(e)
201203 ? : VUndefined
202204
203205 is EtsNumberConstant -> VNumber (e.value)
@@ -325,6 +327,20 @@ class EtsConcreteInterpreter(
325327 private fun isZeroConstant (e : EtsEntity ): Boolean =
326328 e is EtsNumberConstant && e.value == 0.0
327329
330+ /* *
331+ * A local of a function type that was never assigned is a function
332+ * *literal reference*: its type signature names the lowered method
333+ * (e.g. `factorial := %AM0$%dflt` in the file initializer).
334+ */
335+ private fun functionValueOf (e : EtsLocal ): VValue ? {
336+ val fnType = e.type as ? EtsFunctionType ? : return null
337+ val method = resolver.methodByFunctionType(fnType)
338+ ? : resolver.resolveFunctionPointer(fnType.signature)
339+ ? : resolver.functionAliasFor(e.name)
340+ ? : return null
341+ return VFunction (method)
342+ }
343+
328344 private inline fun numeric (
329345 left : EtsEntity ,
330346 right : EtsEntity ,
@@ -357,6 +373,9 @@ class EtsConcreteInterpreter(
357373 }
358374 return when (v) {
359375 is VArray -> typeName == " Array"
376+ is VMap -> typeName == " Map"
377+ is VSet -> typeName == " Set"
378+ is VFunction -> typeName == " Function"
360379 is VObject -> {
361380 var cls = v.cls
362381 val visited = mutableSetOf<String >()
@@ -398,6 +417,8 @@ class EtsConcreteInterpreter(
398417 is VObject -> instance.fields[name] ? : VUndefined
399418 is VArray -> if (name == " length" ) VNumber (instance.elements.size.toDouble()) else VUndefined
400419 is VString -> if (name == " length" ) VNumber (instance.value.length.toDouble()) else VUndefined
420+ is VMap -> if (name == " size" ) VNumber (instance.entries.size.toDouble()) else VUndefined
421+ is VSet -> if (name == " size" ) VNumber (instance.elements.size.toDouble()) else VUndefined
401422 is VNamespace -> Intrinsics .namespaceField(instance.name, name)
402423 ? : throw UnsupportedFeatureSignal (" namespace field: ${instance.name} .$name " )
403424
@@ -414,11 +435,24 @@ class EtsConcreteInterpreter(
414435 }
415436
416437 private fun newObject (e : EtsNewExpr ): VValue {
438+ val typeName = when (val t = e.type) {
439+ is EtsClassType -> t.signature.name
440+ is EtsUnclearRefType -> t.name
441+ else -> null
442+ }
417443 val cls = when (val t = e.type) {
418444 is EtsClassType -> resolver.classBySignature(t.signature)
419445 is EtsUnclearRefType -> resolver.classByName(t.name)
420446 else -> null
421447 }
448+ if (cls == null ) {
449+ // Built-in containers are not scene classes
450+ when (typeName) {
451+ " Array" -> return VArray ()
452+ " Map" -> return VMap ()
453+ " Set" -> return VSet ()
454+ }
455+ }
422456 return VObject (cls)
423457 }
424458
@@ -504,14 +538,46 @@ class EtsConcreteInterpreter(
504538 }
505539 }
506540
507- // Constructor of a class outside the scene (e.g. `new Error(msg)`):
508- // model as a record initialization.
509- if (name == CONSTRUCTOR_NAME && receiver is VObject ) {
510- args.firstOrNull()?.let { receiver.fields[" message" ] = it }
511- return receiver
541+ // Constructors of classes outside the scene.
542+ if (name == CONSTRUCTOR_NAME ) {
543+ when (receiver) {
544+ is VObject -> {
545+ // e.g. `new Error(msg)`: model as record initialization
546+ args.firstOrNull()?.let { receiver.fields[" message" ] = it }
547+ return receiver
548+ }
549+
550+ is VArray -> {
551+ // `new Array(n)` / `new Array(a, b, ...)`
552+ if (args.size == 1 && args[0 ] is VNumber ) {
553+ val n = (args[0 ] as VNumber ).value
554+ if (n == floor(n) && n >= 0 ) {
555+ receiver.elements.clear()
556+ repeat(n.toInt()) { receiver.elements.add(VUndefined ) }
557+ } else {
558+ throw JsThrowSignal (VString (" RangeError: Invalid array length" ))
559+ }
560+ } else {
561+ receiver.elements.clear()
562+ receiver.elements.addAll(args)
563+ }
564+ return receiver
565+ }
566+
567+ is VMap , is VSet -> {
568+ if (args.isEmpty() || args[0 ] == VUndefined || args[0 ] == VNull ) return receiver
569+ if (receiver is VSet && args[0 ] is VArray ) {
570+ (args[0 ] as VArray ).elements.forEach { receiver.elements.add(it) }
571+ return receiver
572+ }
573+ throw UnsupportedFeatureSignal (" iterable constructor argument for Map/Set" )
574+ }
575+
576+ else -> Unit
577+ }
512578 }
513579
514- Intrinsics .callInstance(receiver, name, args)
580+ Intrinsics .callInstance(receiver, name, args, ::invokeFunction )
515581 ? : throw UnsupportedFeatureSignal (
516582 " instance method: $name on ${receiver::class .simpleName} "
517583 )
@@ -528,7 +594,21 @@ class EtsConcreteInterpreter(
528594 runMethod(callee, VUndefined , padArgs(callee, args))
529595 }
530596
531- is EtsPtrCallExpr -> throw UnsupportedFeatureSignal (" ptr call: $expr " )
597+ is EtsPtrCallExpr -> {
598+ // Prefer the dynamic function value held by the pointer local
599+ val ptrValue = eval(expr.ptr, frame)
600+ if (ptrValue is VFunction ) {
601+ runMethod(ptrValue.method, ptrValue.thisValue, padArgs(ptrValue.method, args))
602+ } else {
603+ val callee = resolver.resolveFunctionPointer(expr.callee)
604+ if (callee != null ) {
605+ runMethod(callee, frame.thisValue, padArgs(callee, args))
606+ } else {
607+ Intrinsics .callConversion(expr.callee.name, args)
608+ ? : throw UnsupportedFeatureSignal (" ptr call: ${expr.callee.name} " )
609+ }
610+ }
611+ }
532612
533613 else -> throw UnsupportedFeatureSignal (" call kind: ${expr::class .simpleName} " )
534614 }
@@ -538,5 +618,9 @@ class EtsConcreteInterpreter(
538618 if (args.size >= callee.parameters.size) return args
539619 return args + List (callee.parameters.size - args.size) { VUndefined }
540620 }
621+
622+ /* * Host callback for higher-order intrinsics (`arr.map(f)`, `map.forEach(f)`, ...). */
623+ private fun invokeFunction (fn : VFunction , args : List <VValue >): VValue =
624+ runMethod(fn.method, fn.thisValue, padArgs(fn.method, args))
541625 }
542626}
0 commit comments