Class JsFunction

java.lang.Object
io.karatelabs.js.JsFunction
All Implemented Interfaces:
JavaCallable, ObjectLike, Runnable, Consumer<Object>, Function<Object,Object>, Predicate<Object>, Supplier<Object>, Map<String,Object>
Direct Known Subclasses:
JsFunctionWrapper

public abstract class JsFunction extends Object implements JavaCallable
Base class for JavaScript function objects.

Implements JavaCallable so JS functions can be passed to Java code that expects a callable (e.g., predicate functions, callbacks).

  • Constructor Details

    • JsFunction

      protected JsFunction()
  • Method Details

    • isExternal

      public boolean isExternal()
      Specified by:
      isExternal in interface JavaCallable
    • isConstructable

      public boolean isConstructable()
    • getSource

      public String getSource()
      Returns the source code of this function, or null if not available. User-defined functions (JsFunctionNode) override to return their original source. Built-in functions return null by default.
    • toString

      public String toString()
      Overrides:
      toString in class Object
    • getOwnAttrs

      public byte getOwnAttrs(String name)
      Spec attribute defaults for the four intrinsics every function carries. Per ECMA-262 §10.2.4 / §10.2.5:
      • length / name: {writable: false, enumerable: false, configurable: true}.
      • prototype on a user-defined function: {writable: true, enumerable: false, configurable: false} — this is the default returned here, since most idiomatic JS does Sub.prototype = Object.create(Super.prototype). Built-in constructors (Array, Date, Number, etc.) override to all-false (non-writable, non-enumerable, non-configurable).
      • constructor on a function's prototype is {writable: true, enumerable: false, configurable: true}.
      Returning these directly is cheaper than allocating a Slot per intrinsic key on every built-in function in the JVM.
    • resolveOwnIntrinsic

      protected Object resolveOwnIntrinsic(String name)
      Subclass extension hook for "own" intrinsic members that are not stored in props — e.g. JsString.length, JsRegex.source, JsFunction.name / length / prototype. Returns the intrinsic value at this level only (no prototype walk); null means "not an own intrinsic", letting the caller fall through to the prototype chain.

      Replaces the historical pattern where each subclass overrode the 1-arg ObjectLike.getMember(String) and prefixed its body with Object own = super.getMember(name); if (own != null) return own;. Centralizing the intrinsic resolution here lets the 3-arg getMember single-pass through (own slot → intrinsic hook → proto chain) and avoids the double prototype walk that the 1-arg fallback caused for accessor descriptors on the chain.

    • ownIntrinsicNames

      protected Iterable<String> ownIntrinsicNames()
      Names this subclass exposes as own intrinsics — the discovery seam used by Object.getOwnPropertyDescriptors (and any future caller that needs to enumerate intrinsics not materialized in ObjectLike.toMap()). Default returns nothing; subclasses with a resolveOwnIntrinsic(java.lang.String) override return the closed name set they resolve.

      Each subclass returns its own complete list (no chaining via super) — matches the structure of resolveOwnIntrinsic(java.lang.String), which any subclass that resolves intrinsics already overrides end-to-end. Built-in constructors install their methods as actual own slots via defineOwn (so they surface through toMap() directly) and inherit the JsFunction list for the prototype / name / length surface.

    • getPrototype

      public ObjectLike getPrototype()
      Description copied from interface: ObjectLike
      Returns the prototype (__proto__) for prototype chain walking. Default returns null (no prototype chain).
      Specified by:
      getPrototype in interface ObjectLike
    • setPrototype

      public void setPrototype(ObjectLike proto)
    • hasOwnIntrinsic

      public boolean hasOwnIntrinsic(String name)
      True iff this object exposes name as an "own" intrinsic property (e.g. Date.prototype, Date.now, Date.UTC). Derived directly from resolveOwnIntrinsic(String) — a non-null resolution is, by definition, an own intrinsic; a null resolution means the name is not on this object. Single source of truth eliminates the old "subclass declares the name set twice" drift risk (concretely: JsFunction.constructor once appeared in the boolean override but not the value resolver, so f.hasOwnProperty('constructor') reported true even though constructor lives on Function.prototype, not on each function instance).

      Subclasses with non-resolveOwnIntrinsic-based storage (JsGlobalThis bindings, JsArray numeric indices) override ObjectLike.isOwnProperty(String) directly and bypass this hook.

      Hot-path note: callers that only need an existence check pay the cost of building the resolved value. Subclasses whose resolveOwnIntrinsic allocates per call (built-in method lambdas, etc.) should install those on the prototype instead — the prototype's builtins map caches a single instance.

    • getMember

      public Object getMember(String name)
      Description copied from interface: ObjectLike
      Raw-value accessor — returns the stored data value, or null for accessor descriptors (which have no extractable raw value). Internal map / Java-interop callers (toMap, Map.get, jsEntries) and subclass super.getMember(name) chains use this.

      For JS-semantic reads that must invoke an accessor's getter, use ObjectLike.getMember(String, Object, CoreContext) instead.

      Specified by:
      getMember in interface ObjectLike
    • getMember

      public Object getMember(String name, Object receiver, io.karatelabs.js.CoreContext ctx)
      Description copied from interface: ObjectLike
      JS-semantic resolved read. For data descriptors returns the value; for accessor descriptors invokes the getter with receiver bound as this. receiver is the object the property is being read on (may differ from this when walking a prototype chain). ctx threads through to the getter call. Default delegates to ObjectLike.getMember(String) — implementations with accessor storage (JsObject, JsArray) override.
      Specified by:
      getMember in interface ObjectLike
    • isOwnProperty

      public boolean isOwnProperty(String name)
      True iff name is an own property on this object — covers non-tombstoned own slots and intrinsic properties declared by subclasses (via hasOwnIntrinsic). Use this for Object.getOwnPropertyDescriptor / hasOwn / in semantics; raw props.containsKey would include tombstones.
      Specified by:
      isOwnProperty in interface ObjectLike
    • putMember

      public void putMember(String name, Object value)
      Specified by:
      putMember in interface ObjectLike
    • putMember

      public void putMember(String name, Object value, io.karatelabs.js.CoreContext ctx, boolean strict)
      Description copied from interface: ObjectLike
      Strict-aware [[Set]] (ES 10.4.7 OrdinarySetWithOwnDescriptor). Same store as ObjectLike.putMember(String, Object), but when the write is rejected — frozen object, non-extensible new key, non-writable data property, or a get-only accessor — a sloppy caller gets the silent no-op while a strict caller gets a JsErrorException TypeError. ctx threads through for accessor setters.

      Default delegates to the lenient ObjectLike.putMember(String, Object) — host bridges that don't model property attributes are perpetually writable, so there is nothing to reject. JsObject / JsArray override to enforce.

      Specified by:
      putMember in interface ObjectLike
    • isExtensible

      public boolean isExtensible()
      Specified by:
      isExtensible in interface ObjectLike
    • isSealed

      public boolean isSealed()
      Specified by:
      isSealed in interface ObjectLike
    • isFrozen

      public boolean isFrozen()
      Specified by:
      isFrozen in interface ObjectLike
    • setExtensible

      public void setExtensible(boolean extensible)
      Specified by:
      setExtensible in interface ObjectLike
    • setSealed

      public void setSealed(boolean sealed)
      Specified by:
      setSealed in interface ObjectLike
    • setFrozen

      public void setFrozen(boolean frozen)
      Specified by:
      setFrozen in interface ObjectLike
    • method

      protected static io.karatelabs.js.JsBuiltinMethod method(String methodName, int length, io.karatelabs.js.JsCallable delegate)
      Sugar for the canonical new JsBuiltinMethod(name, length, delegate) call used by built-in constructor singletons in their resolveMember switches. Same shape as Prototype.install(String, int, JsCallable) — written so that each switch case reads case "isFinite" -> method(name, 1, (JsInvokable) this::isFinite) with the case label value flowing into the wrap as a single source of truth.
    • removeMember

      public void removeMember(String name)
      Specified by:
      removeMember in interface ObjectLike
    • removeMember

      public void removeMember(String name, io.karatelabs.js.CoreContext ctx, boolean strict)
      Description copied from interface: ObjectLike
      Strict-aware [[Delete]] (ES 13.5.1.2 / DeletePropertyOrThrow). Same removal as ObjectLike.removeMember(String), but a non-configurable own property is a silent no-op for a sloppy caller and a TypeError for a strict caller. Default delegates to the lenient ObjectLike.removeMember(String); JsObject / JsArray override to enforce.
      Specified by:
      removeMember in interface ObjectLike
    • toMap

      public Map<String,Object> toMap()
      Java-interop snapshot of own data. Accessor descriptors surface as null entries — this is the no-side-effects boundary where the absence of a CoreContext would force getters to silently fail anyway. Map.get(java.lang.Object), Map.values(), Map.containsValue(java.lang.Object), Map.entrySet() and the no-arg jsEntries() all follow the same rule for the same reason.

      Spec-correct iteration that must invoke accessor getters (Object.keys / values / entries / assign) goes through jsEntries(CoreContext) — Refactor E carved that out as the single ctx-aware seam. If you find yourself reaching for toMap from JS-semantic code, you probably want jsEntries(ctx) instead.

      Specified by:
      toMap in interface ObjectLike
    • size

      public int size()
      Specified by:
      size in interface Map<String,Object>
    • isEmpty

      public boolean isEmpty()
      Specified by:
      isEmpty in interface Map<String,Object>
    • containsKey

      public boolean containsKey(Object key)
      Specified by:
      containsKey in interface Map<String,Object>
    • containsValue

      public boolean containsValue(Object value)
      Specified by:
      containsValue in interface Map<String,Object>
    • get

      public Object get(Object key)
      Specified by:
      get in interface Map<String,Object>
    • put

      public Object put(String key, Object value)
      Specified by:
      put in interface Map<String,Object>
    • remove

      public Object remove(Object key)
      Specified by:
      remove in interface Map<String,Object>
    • putAll

      public void putAll(Map<? extends String,?> m)
      Specified by:
      putAll in interface Map<String,Object>
    • clear

      public void clear()
      Specified by:
      clear in interface Map<String,Object>
    • keySet

      public Set<String> keySet()
      Specified by:
      keySet in interface Map<String,Object>
    • values

      public Collection<Object> values()
      Specified by:
      values in interface Map<String,Object>
    • entrySet

      public Set<Map.Entry<String,Object>> entrySet()
      Specified by:
      entrySet in interface Map<String,Object>
    • jsEntries

      public Iterable<io.karatelabs.js.KeyValue> jsEntries()
      Returns an iterable for JS for-in/for-of iteration with KeyValue pairs. Used internally by JS iteration constructs and by Terms.toIterable(java.lang.Object), which is the back-end for Object.keys / values / entries / assign and for...in. All of those filter by enumerable per spec.

      Iterates props directly so each yielded value reads the slot's current value at next() time — callback-driven mutations during iteration are visible (test262 Array.prototype.map's "callback mutates earlier index, later index sees update" semantics rely on this). Subclasses with alternate storage (JsGlobalThis) override. Routes through isEnumerable so subclass getOwnAttrs overrides (e.g. JsMath returning WRITABLE | CONFIGURABLE — no enumerable bit — for its built-in methods) win.

      The no-arg form is the Java-interop seam — accessor properties have no extractable raw value here and surface as null. The jsEntries(CoreContext) overload is the spec-correct iteration for Object.keys/values/entries/assign (invokes accessor getters via PropertySlot.read(java.lang.Object, io.karatelabs.js.CoreContext)).

    • orderedOwnKeys

      public static Set<String> orderedOwnKeys(Set<String> insertionOrder)
      Spec §9.1.11.1 OrdinaryOwnPropertyKeys ordering applied to an insertion-order key set: integer-index keys (per JsArray.parseIndex(java.lang.String)) move to the front in ascending numeric order, then remaining string keys keep their insertion order. Returns the input unchanged when no integer-index keys are present (the common case for plain object literals with named properties only).

      Used at every spec seam that reports own keys for an ordinary object — jsEntries(CoreContext) (drives Object.keys / values / entries / assign) and JsObjectConstructor.ownKeys (drives Object.getOwnPropertyNames / getOwnPropertyDescriptors / defineProperties). JsArray has its own integer-first iteration via the dense list; this helper is for plain JsObject / ObjectLike backed by an insertion-ordered prop map.

    • jsEntries

      public Iterable<io.karatelabs.js.KeyValue> jsEntries(io.karatelabs.js.CoreContext ctx)
      JS-semantic iteration variant — accessor descriptors invoke their getters with ctx when ctx != null; otherwise behaves as the no-arg jsEntries() (raw values, accessors → null). This is the back-end called from Object.keys / values / entries / assign via Terms.toIterable(Object, CoreContext) so accessor descriptors observe their spec invocation.

      Yields entries in §9.1.11.1 OrdinaryOwnPropertyKeys order — integer indices ascending, then string keys insertion-order. Slots are partitioned in a single pre-pass; values are still re-read at yield time so callback-mutated values propagate.

    • hashCode

      public int hashCode()
      Specified by:
      hashCode in interface Map<String,Object>
      Overrides:
      hashCode in class Object
    • equals

      public boolean equals(Object obj)
      Specified by:
      equals in interface Map<String,Object>
      Overrides:
      equals in class Object