From 4197d9efd8613920436f22e72e64848a9c9bbec4 Mon Sep 17 00:00:00 2001 From: benStre Date: Thu, 25 Jan 2024 13:07:30 +0100 Subject: [PATCH 1/7] cleanup Pointers, Ref, remove primitive pointer subclasses, fix ts errors --- datex_short.ts | 16 ++-- functions.ts | 8 +- runtime/lazy-pointer.ts | 4 +- runtime/pointers.ts | 203 +++++++++++++++------------------------- runtime/runtime.ts | 57 +++++------ 5 files changed, 118 insertions(+), 170 deletions(-) diff --git a/datex_short.ts b/datex_short.ts index 15357797..ae006f2d 100644 --- a/datex_short.ts +++ b/datex_short.ts @@ -1,7 +1,7 @@ // shortcut functions // import { Datex } from "./datex.ts"; -import { baseURL, Runtime, PrecompiledDXB, Type, Pointer, Ref, PointerProperty, primitive, any_class, Target, IdEndpoint, TransformFunctionInputs, AsyncTransformFunction, TransformFunction, TextRef, Markdown, DecimalRef, BooleanRef, IntegerRef, MinimalJSRef, RefOrValue, PartialRefOrValueObject, datex_meta, ObjectWithDatexValues, Compiler, endpoint_by_endpoint_name, endpoint_name, Storage, compiler_scope, datex_scope, DatexResponse, target_clause, ValueError, logger, Class, getUnknownMeta, Endpoint, INSERT_MARK, CollapsedValueAdvanced, CollapsedValue, SmartTransformFunction, compiler_options, activePlugins, METADATA, handleDecoratorArgs, RefOrValueObject, PointerPropertyParent, InferredPointerProperty, RefLike } from "./datex_all.ts"; +import { baseURL, Runtime, PrecompiledDXB, Type, Pointer, Ref, PointerProperty, primitive, any_class, Target, IdEndpoint, TransformFunctionInputs, AsyncTransformFunction, TransformFunction, Markdown, MinimalJSRef, RefOrValue, PartialRefOrValueObject, datex_meta, ObjectWithDatexValues, Compiler, endpoint_by_endpoint_name, endpoint_name, Storage, compiler_scope, datex_scope, DatexResponse, target_clause, ValueError, logger, Class, getUnknownMeta, Endpoint, INSERT_MARK, CollapsedValueAdvanced, CollapsedValue, SmartTransformFunction, compiler_options, activePlugins, METADATA, handleDecoratorArgs, RefOrValueObject, PointerPropertyParent, InferredPointerProperty, RefLike } from "./datex_all.ts"; /** make decorators global */ import { assert as _assert, property as _property, sync as _sync, endpoint as _endpoint, template as _template, jsdoc as _jsdoc} from "./datex_all.ts"; @@ -357,25 +357,25 @@ export function val(val: RefOrValue):T { // TODO: return inferred type in // generate primitive pointers -export function decimal(value:RefOrValue = 0): DecimalRef { +export function decimal(value:RefOrValue = 0): Pointer { if (value instanceof Ref) value = value.val; // collapse return Pointer.create(undefined, Number(value)) // adds pointer or returns existing pointer } -export function integer(value:RefOrValue = 0n): IntegerRef { +export function integer(value:RefOrValue = 0n): Pointer { if (value instanceof Ref) value = value.val; // collapse return Pointer.create(undefined, BigInt(Math.floor(Number(value)))) // adds pointer or returns existing pointer } -export function text(string:TemplateStringsArray, ...vars:any[]):Promise -export function text(value?:RefOrValue): TextRef -export function text(value:RefOrValue|TemplateStringsArray = "", ...vars:any[]): TextRef|Promise { +export function text(string:TemplateStringsArray, ...vars:any[]):Promise> +export function text(value?:RefOrValue): Pointer +export function text(value:RefOrValue|TemplateStringsArray = "", ...vars:any[]): Pointer|Promise> { if (value instanceof Ref) value = value.val; // collapse // template transform if (value instanceof Array) { - return >>_datex(`always '${value.raw.map(s=>s.replace(/\(/g, '\\(').replace(/\'/g, "\\'")).join(INSERT_MARK)}'`, vars) + return >>_datex(`always '${value.raw.map(s=>s.replace(/\(/g, '\\(').replace(/\'/g, "\\'")).join(INSERT_MARK)}'`, vars) } else return Pointer.create(undefined, String(value)) // adds pointer or returns existing pointer } -export function boolean(value:RefOrValue = false): BooleanRef { +export function boolean(value:RefOrValue = false): Pointer { if (value instanceof Ref) value = value.val; // collapse return Pointer.create(undefined, Boolean(value)) // adds pointer or returns existing pointer } diff --git a/functions.ts b/functions.ts index 74d8b259..baab5293 100644 --- a/functions.ts +++ b/functions.ts @@ -4,7 +4,7 @@ */ -import { AsyncTransformFunction, BooleanRef, CollapsedValue, CollapsedValueAdvanced, Decorators, INSERT_MARK, METADATA, MaybeObjectRef, MinimalJSRef, Pointer, Ref, RefLike, RefOrValue, Runtime, SmartTransformFunction, SmartTransformOptions, TransformFunction, TransformFunctionInputs, handleDecoratorArgs, logger, primitive } from "./datex_all.ts"; +import { AsyncTransformFunction, CollapsedValue, CollapsedValueAdvanced, Decorators, INSERT_MARK, METADATA, MaybeObjectRef, MinimalJSRef, Pointer, Ref, RefLike, RefOrValue, Runtime, SmartTransformFunction, SmartTransformOptions, TransformFunction, TransformFunctionInputs, handleDecoratorArgs, logger, primitive } from "./datex_all.ts"; import { Datex } from "./mod.ts"; import { PointerError } from "./types/errors.ts"; import { IterableHandler } from "./utils/iterable-handler.ts"; @@ -333,7 +333,7 @@ export function selectProperty(property:RefLike, * @param value * @returns */ -export function not(value:RefOrValue): BooleanRef { +export function not(value:RefOrValue): Pointer { return transform([value], v=>!v); } @@ -342,7 +342,7 @@ export function not(value:RefOrValue): BooleanRef { * @param values * @returns */ -export function and(...values:RefOrValue[]): BooleanRef { +export function and(...values:RefOrValue[]): Pointer { return transform(values, (...values)=>{ for (const v of values) { if (!v) return false; @@ -356,7 +356,7 @@ export function and(...values:RefOrValue[]): BooleanRef { * @param values * @returns */ -export function or(...values:RefOrValue[]): BooleanRef { +export function or(...values:RefOrValue[]): Pointer { return transform(values, (...values)=>{ for (const v of values) { if (v) return true; diff --git a/runtime/lazy-pointer.ts b/runtime/lazy-pointer.ts index 6bc4a55e..a1ef3f46 100644 --- a/runtime/lazy-pointer.ts +++ b/runtime/lazy-pointer.ts @@ -11,8 +11,8 @@ export class LazyPointer { Pointer.onPointerForIdAdded(this.id, p => callback(Pointer.collapseValue(p) as MinimalJSRef, p)) } - static withVal(val:any, callback:(val:MinimalJSRef)=>void) { + static withVal(val:MinimalJSRef, callback:(val:MinimalJSRef)=>void) { if (val instanceof LazyPointer) val.onLoad(callback); - else callback(val, val); + else callback(val); } } \ No newline at end of file diff --git a/runtime/pointers.ts b/runtime/pointers.ts index 869c53c2..29f25efa 100644 --- a/runtime/pointers.ts +++ b/runtime/pointers.ts @@ -55,6 +55,9 @@ export abstract class Ref extends EventTarget { #val?: T; + // guarantees that x instanceof Ref works correctly, inferring RefLike + static [Symbol.hasInstance]: (val: unknown) => val is RefLike + constructor(value?:RefOrValue) { super(); value = Ref.collapseValue(value); @@ -82,7 +85,7 @@ export abstract class Ref extends EventTarget { } // same as val setter, but can be awaited - public setVal(value:T, trigger_observers = true, is_transform?:boolean):Promise|void { + public setVal(value:T, trigger_observers = true, is_transform?:boolean) { const previous = this.#val; this.#val = Ref.collapseValue(value, true, true); if (trigger_observers && previous !== this.#val) return this.triggerValueInitEvent(is_transform, previous) @@ -116,8 +119,7 @@ export abstract class Ref extends EventTarget { const handler:ProxyHandler = {}; // deno-lint-ignore no-this-alias - let pointer:Pointer = this; - + let pointer:Pointer = this as any; // double pointer property..TODO: improve, currently tries to collapse current value if (pointer instanceof PointerProperty) { @@ -125,6 +127,8 @@ export abstract class Ref extends EventTarget { if (!pointer) throw new Error("Nested pointer properties are currently not supported"); } + if (!(this instanceof Pointer)) throw new Error("Cannot use $, not a pointer"); + handler.ownKeys = () => { return Reflect.ownKeys(pointer.val); } @@ -558,7 +562,7 @@ export class PointerProperty extends Ref { public pointer?: Pointer; private lazy_pointer?: LazyPointer; - private constructor(pointer?: Pointer|LazyPointer, public key: any, leak_js_properties = false) { + private constructor(pointer: Pointer|LazyPointer|undefined, public key: any, leak_js_properties = false) { super(); if (pointer instanceof Pointer) this.setPointer(pointer); @@ -617,7 +621,7 @@ export class PointerProperty extends Ref { // get current pointer property public override get val():T { // this.handleBeforePrimitiveValueGet(); - if (this.lazy_pointer) return undefined + if (this.lazy_pointer) return undefined as T const val = this.pointer!.getProperty(this.key, this.#leak_js_properties); @@ -628,11 +632,6 @@ export class PointerProperty extends Ref { else return val; } - public override get current_val():T { - if (this.lazy_pointer) return undefined - return this.pointer!.getProperty(this.key, this.#leak_js_properties); - } - // update pointer property public override set val(value: T) { if (this.lazy_pointer) { @@ -641,6 +640,13 @@ export class PointerProperty extends Ref { } this.pointer!.handleSet(this.key, Ref.collapseValue(value, true, true)); } + + public override get current_val():T { + if (this.lazy_pointer) return undefined as T + return this.pointer!.getProperty(this.key, this.#leak_js_properties); + } + + // same as val setter, but can be awaited public override setVal(value: T) { if (this.lazy_pointer) { @@ -702,23 +708,9 @@ export class PointerProperty extends Ref { } -export type JSPrimitiveToDatexRef = ( - T extends bigint ? IntegerRef : - T extends string ? TextRef : - T extends number ? DecimalRef : - T extends boolean ? BooleanRef : - T extends Type ? TypeRef : - T extends Endpoint ? EndpointRef : - T extends URL ? URLRef : - never -) // similar to Datex.Ref, but works better for all descending generic classes in typescript strict mode -export type GenericValue = - // specific type class - JSPrimitiveToDatexRef | - // generic classes - RefLike; +export type GenericValue = RefLike; export type ReadonlyRef = Readonly>; /** @@ -739,14 +731,6 @@ export type PartialRefOrValueObject = { [P in keyof T]?: RefOrValue } // collapsed value export type CollapsedValue> = // (reverse order of inheritance) - // DATEX Primitives: --------------------- - T extends IntegerRef ? bigint : - T extends TextRef ? string : - T extends DecimalRef ? number : - T extends BooleanRef ? boolean : - T extends TypeRef ? Type : - T extends EndpointRef ? Endpoint : - T extends URLRef ? URL : // generic value classes T extends PointerProperty ? TT : T extends Pointer ? TT : @@ -755,11 +739,7 @@ export type CollapsedValue> = // collapsed value that still has a reference in JS export type CollapsedValueJSCompatible> = - // (reverse order of inheritance) - // DATEX primitive that keep reference in JS: --------------------- - T extends TypeRef ? Type : - T extends EndpointRef ? Endpoint : - T extends URLRef ? URL : + // (reverse order of inheritance) // generic value classes T extends PointerProperty ? (TT extends primitive ? T : TT) : T extends Pointer ? (TT extends primitive ? T : TT) : @@ -842,14 +822,11 @@ export type JSValueWith$ = ObjectRef; // converts Object to Record // convert from any JS/DATEX value to minimal representation with reference -export type MinimalJSRefGeneralTypes> = - _C extends symbol ? symbol : ( - JSPrimitiveToDatexRef<_C> extends never ? ObjectRef<_C> : JSPrimitiveToDatexRef<_C> - ) -// same as MinimalJSRefGeneralTypes, but returns Pointer<2|5> instead of IntegerRef export type MinimalJSRef> = - _C extends symbol ? symbol : ( - JSPrimitiveToDatexRef<_C> extends never ? ObjectRef<_C> : (Pointer<_C> & (_C extends boolean ? unknown : _C)) + _C extends symbol ? symbol : ( + _C extends number|string|boolean|bigint ? + T: // keep pointer reference + ObjectRef<_C> // collapsed object ) // return Pointer&T for primitives (excluding boolean) and Pointer otherwise @@ -1386,7 +1363,7 @@ export class Pointer extends Ref { private static loading_pointers:Map, scopeList: WeakSet}> = new Map(); // load from storage or request from remote endpoint if pointer not yet loaded - static load(id:string|Uint8Array, SCOPE?:datex_scope, only_load_local = false, sender_knows_pointer = true, allow_failure = true): Promise|Pointer{ + static load(id:string|Uint8Array, SCOPE?:datex_scope, only_load_local = false, sender_knows_pointer = true, allow_failure = true): Promise|Pointer|LazyPointer { const id_string = Pointer.normalizePointerId(id); @@ -1595,19 +1572,19 @@ export class Pointer extends Ref { } // create/get DatexPointer for value if possible (not primitive) and return value - static proxifyValue = RefOrValue>(value:C, sealed = false, allowed_access?:target_clause, anonymous = false, persistant= false, check_proxify_as_child = false): C|T { - if ((value instanceof Pointer && value.is_js_primitive) || value instanceof PointerProperty) return value; // return by reference + static proxifyValue(value:unknown, sealed = false, allowed_access?:target_clause, anonymous = false, persistant= false, check_proxify_as_child = false) { + if ((value instanceof Pointer && value.is_js_primitive) || value instanceof PointerProperty) return value; // return by reference else if (value instanceof Ref) return value.val; // return by value const type = Type.ofValue(value) - const collapsed_value = Ref.collapseValue(value,true,true) + const collapsed_value = Ref.collapseValue(value,true,true) // if proxify_as_child=false: don't create pointer for this value, return original value // e.g.: primitive values if ((check_proxify_as_child && !type.proxify_as_child) || type.is_primitive) { - return collapsed_value; + return collapsed_value; } // create or get pointer - else return Pointer.createOrGet(collapsed_value, sealed, allowed_access, anonymous, persistant).val; + else return Pointer.createOrGet(collapsed_value, sealed, allowed_access, anonymous, persistant).val; } // create a new pointer or return the existing pointer/pointer property for this value @@ -1634,17 +1611,6 @@ export class Pointer extends Ref { return this.createOrGet(value, sealed, allowed_access, anonymous, persistant); } - // create a new pointer or return the existing pointer + add a label - static createLabel(value:RefOrValue, label:string|number):Pointer{ - let ptr = Pointer.getByValue(value); // try proxify - // create new pointer - if (!ptr) { - ptr = Pointer.create(undefined, value); - } - ptr.addLabel(label); - return ptr; - } - // create a new pointer with a transform value static createTransform(observe_values:V, transform:TransformFunction, persistent_datex_transform?:string, force_transform = false) { @@ -1672,7 +1638,7 @@ export class Pointer extends Ref { // only creates the same pointer once => unique pointers // throws error if pointer is already allocated or pointer value is primitive - static create(id?:string|Uint8Array, value:RefOrValue|typeof NOT_EXISTING=NOT_EXISTING, sealed = false, origin?:Endpoint, persistant=false, anonymous = false, is_placeholder = false, allowed_access?:target_clause, timeout?:number):Pointer { + static create(id?:string|Uint8Array, value:RefOrValue|typeof NOT_EXISTING=NOT_EXISTING, sealed = false, origin?:Endpoint, persistant=false, anonymous = false, is_placeholder = false, allowed_access?:target_clause, timeout?:number): Pointer { let p:Pointer; // DatexValue: DatexPointer or DatexPointerProperty not valid as object, get the actual value instead @@ -1688,31 +1654,21 @@ export class Pointer extends Ref { if (p.is_js_primitive) { if (value!=NOT_EXISTING) p.val = value; // update value of this pointer if (origin) p.origin = origin; // override origin + return p; } else { throw new PointerError("Cannot assign a native primitive value to a initialized non-primitive pointer"); } } else { - let pp:any; - - switch (typeof value) { - case "string": pp = TextRef; break; - case "number": pp = DecimalRef; break; - case "bigint": pp = IntegerRef; break; - case "boolean": pp = BooleanRef; break; - } - - if (!pp) throw new PointerError("Cannot create a pointer for this value type"); - // create new - return new (pp)(id, value, sealed, origin, persistant, anonymous, is_placeholder, allowed_access, timeout) + return new Pointer(id, value, sealed, origin, persistant, anonymous, is_placeholder, allowed_access, timeout) } } // value already allocated to a pointer else if (this.pointer_value_map.has(value)) { - let existing_pointer = > Pointer.pointer_value_map.get(value); + const existing_pointer = > Pointer.pointer_value_map.get(value); // is placeholder, add id if (existing_pointer.is_placeholder) { existing_pointer.unPlaceholder(id) @@ -1733,13 +1689,7 @@ export class Pointer extends Ref { // create a completely new pointer else { - let pp:any = Pointer; - - if (value instanceof Type) pp = TypeRef; - else if (value instanceof URL) pp = URLRef; - else if (value instanceof Endpoint) pp = EndpointRef; - - return new (pp)(id, value, sealed, origin, persistant, anonymous, is_placeholder, allowed_access, timeout) + return new Pointer(id, value as T, sealed, origin, persistant, anonymous, is_placeholder, allowed_access, timeout) } } @@ -1773,7 +1723,7 @@ export class Pointer extends Ref { }); // clean up after garbage collection: - private static handleGarbageCollected(mockPtr: MockPointer){ + private static handleGarbageCollected(mockPtr: MockPointer|Pointer){ logger.debug("$" + mockPtr.id + " was garbage collected"); // cleanup for complex pointer that still has an instance @@ -1789,7 +1739,7 @@ export class Pointer extends Ref { } // cleanup for primitive and complex pointer - private static cleanup(mockPtr: MockPointer) { + private static cleanup(mockPtr: MockPointer|Pointer) { // unsubscribe const doUnsubscribe = !!(!mockPtr.is_origin && mockPtr.origin) if (doUnsubscribe && mockPtr.subscribed) this.unsubscribeFromPointerUpdates(mockPtr.subscribed, mockPtr.id); @@ -1800,7 +1750,7 @@ export class Pointer extends Ref { // custom datex pointer array splice function - private arraySplice(start?: number, deleteCount?: number, ...items: any[]):any[] { + private arraySplice(start?: number, deleteCount?: number, ...items: unknown[]): unknown[] { // is clear? if (start == 0 && deleteCount == (>this.shadow_object).length && items.length == 0) { this.handleClear(); @@ -1808,7 +1758,7 @@ export class Pointer extends Ref { } if (deleteCount == undefined) deleteCount = (>this.shadow_object).length; // default deleteCount: all if (deleteCount && deleteCount < 0) deleteCount = 0; - return this.handleSplice(start??0, deleteCount, items); + return this.handleSplice(start??0, deleteCount, items) ?? []; } @@ -1852,7 +1802,7 @@ export class Pointer extends Ref { this.initOrigin() // set value - if (value != NOT_EXISTING) this.val = value; + if (value != NOT_EXISTING) this.val = value; // set update_endpoint and trigger suscribers getter (get from cache) this.#update_endpoints = this.subscribers @@ -1878,7 +1828,7 @@ export class Pointer extends Ref { static getOriginFromPointerId(id_buffer: Uint8Array|string) { if (typeof id_buffer == "string") { try {id_buffer = hex2buffer(id_buffer, Pointer.MAX_POINTER_ID_SIZE, true);} - catch (e) {throw new SyntaxError('Invalid pointer id: $' + id_buffer.slice(0, 48));} + catch {throw new SyntaxError('Invalid pointer id: $' + id_buffer.slice(0, 48));} } const pointer_type = id_buffer[0]; return Target.get(id_buffer.slice(1,19), id_buffer.slice(19,21), pointer_type); @@ -1954,9 +1904,9 @@ export class Pointer extends Ref { this.delete() } - #original_value: T extends {[key:string]:unknown} ? WeakRef : void // weak ref to original value (not proxyfied) - #shadow_object: WeakRef<{[key:string]:unknown}>|T // object to make changes and get values from without triggering DATEX updates - #type:Type // type of the value + #original_value!: T extends {[key:string]:unknown} ? WeakRef : void // weak ref to original value (not proxyfied) + #shadow_object?: WeakRef<{[key:string]:unknown}>|T // object to make changes and get values from without triggering DATEX updates + #type:Type = Type.std.Any // type of the value #unwrapped_transform_type?: Type @@ -1970,19 +1920,19 @@ export class Pointer extends Ref { #is_persistent: boolean // indicates if this pointer can get garbage collected #is_anonymous: boolean // pointer should never be sent via datex as reference, always serialize the value - #pointer_type:pointer_type // pointer type (full id, static, ...) + #pointer_type!:pointer_type // pointer type (full id, static, ...) - // id as hex string and ArrayBuffer - #id:string - #id_buffer:Uint8Array - #origin: Endpoint + // set in id setter triggered in constructor + #id!:string // id as hex string + #id_buffer!:Uint8Array // id buffer + #origin!: Endpoint #is_origin = true; - #subscribed: boolean|Endpoints = false + #subscribed: false|Endpoint = false get subscribed() {return this.#subscribed} //readonly:boolean = false; // can the value ever be changed? - sealed:boolean = false; // can the value be changed from the client side? (otherwise, it can only be changed via DATEX calls) + sealed = false; // can the value be changed from the client side? (otherwise, it can only be changed via DATEX calls) #scheduler: UpdateScheduler|null = null // has fixed update_interval #allowed_access?: target_clause // who has access to this pointer?, undefined = all @@ -2389,7 +2339,7 @@ export class Pointer extends Ref { return val; } // return the value directly - else return super.val; + else return super.val!; } override set val(v: T) { @@ -2422,11 +2372,11 @@ export class Pointer extends Ref { // same as val setter, but can be awaited - don't confuse with Pointer.setValue (TODO: rename?) - override setVal(v: T, trigger_observers = true, is_transform?:boolean) { + override setVal(v: T, trigger_observers = true, is_transform?:boolean):Promise|undefined { // TODO: fixme, check this.#loaded && this.original_value!==undefined? const valueExists = this.#loaded && (this.original_value!==undefined || this.is_js_primitive); if (valueExists) return this.updateValue(v, trigger_observers, is_transform); - else return this.initializeValue(v, is_transform); // observers not relevant for init + else return this.initializeValue(v, is_transform) as undefined; // observers not relevant for init } // also trigger event for all property specific observers @@ -2638,7 +2588,7 @@ export class Pointer extends Ref { } } - let updatePromise: Promise|void; + let updatePromise: Promise|undefined; // set primitive value, reference not required if (this.is_js_primitive) { @@ -2975,8 +2925,8 @@ export class Pointer extends Ref { // remove return value if captured by getters // TODO: this this work as intended? - capturedGetters?.delete(val instanceof Pointer ? val : Pointer.getByValue(val)); - capturedGettersWithKeys?.delete(val instanceof Pointer ? val : Pointer.getByValue(val)); + capturedGetters?.delete(val instanceof Pointer ? val : Pointer.getByValue(val)!); + capturedGettersWithKeys?.delete(val instanceof Pointer ? val : Pointer.getByValue(val)!); const hasGetters = capturedGetters||capturedGettersWithKeys; const gettersCount = (capturedGetters?.size??0) + (capturedGettersWithKeys?.size??0); @@ -3043,7 +2993,13 @@ export class Pointer extends Ref { async setDatexTransform(datex_transform:string) { // TODO: fix and reenable try { - this.#transform_scope = (await Runtime.executeDatexLocally(datex_transform)).transform_scope; + const ptr = await Runtime.executeDatexLocally(datex_transform); + if (ptr instanceof Pointer && ptr.transform_scope) { + this.#transform_scope = ptr.transform_scope; + } + else { + throw new Error("invalid transform pointer") + } } catch (e) { console.log("transform error", e); @@ -3124,7 +3080,7 @@ export class Pointer extends Ref { return (>this.#shadow_object)?.deref() } - get type():Type{ + get type():Type { return this.#unwrapped_transform_type ?? this.#type; } @@ -3191,7 +3147,7 @@ export class Pointer extends Ref { // returns if a property of a @sync class can be read, returns true if not a @sync class public canReadProperty(property_name:string):boolean { - return (!this.visible_children&& !DEFAULT_HIDDEN_OBJECT_PROPERTIES.has(property_name)) || this.visible_children.has(property_name) + return (!this.visible_children && !DEFAULT_HIDDEN_OBJECT_PROPERTIES.has(property_name)) || !!(this.visible_children?.has(property_name)) } // returns if a property of a @sync class can be updated, returns true if not a @sync class @@ -3366,12 +3322,13 @@ export class Pointer extends Ref { } // proxify a (child) value, use the pointer context - private proxifyChild(name:any, value:any) { - let child = value === NOT_EXISTING ? this.shadow_object[name] : value; + private proxifyChild(name:string, value:unknown) { + if (NOT_EXISTING && !this.shadow_object) throw new Error("Cannot proxify child of non-object value"); + let child = value === NOT_EXISTING ? this.shadow_object![name] : value; // special native function -> conversion; if (typeof child == "function" && !(child instanceof DatexFunction) && !(child instanceof JSTransferableFunction)) { - child = DatexFunction.createFromJSFunction(child, this, name); + child = DatexFunction.createFromJSFunction(child as (...args: unknown[]) => unknown, this, name); } // create/get pointer, same permission filter @@ -3956,7 +3913,7 @@ export class Pointer extends Ref { } /** all values are removed */ - handleSplice(start_index:number, deleteCount:number, replace:Array) { + handleSplice(start_index:number, deleteCount:number, replace:Array) { if(!this.current_val) return; if (deleteCount == 0 && !replace.length) return; // nothing changes @@ -4150,17 +4107,17 @@ export class Pointer extends Ref { } - #active_property_observers = new Map>]>(); + #active_property_observers = new Map>]>(); #unique = {} // set observer for internal changes in property value reference - protected initShadowObjectPropertyObserver(key:string, value:Ref){ + protected initShadowObjectPropertyObserver(key:string, value:RefLike){ // console.log("set reference observer" + this.idString(),key,value) // remove previous observer for property if (this.#active_property_observers.has(key)) { const [value, handler] = this.#active_property_observers.get(key)!; - Ref.unobserve(value, handler, this.#unique); + Ref.unobserve(value, handler, this.#unique); // xxxxxx } // new observer @@ -4317,18 +4274,6 @@ export namespace Ref { } } -// js primitives -export class TextRef extends Pointer {} -export class IntegerRef extends Pointer {} -export class DecimalRef extends Pointer {} -export class BooleanRef extends Pointer {} - -// pseudo primitives -export class TypeRef extends Pointer {} -export class EndpointRef extends Pointer {} -export class URLRef extends Pointer {} - - /** proxy function (for remote calls) */ @@ -4348,9 +4293,9 @@ export function getProxyFunction(method_name:string, params:{filter:target_claus export function getProxyStaticValue(name:string, params:{filter?:target_clause, dynamic_filter?: target_clause, sign?:boolean, scope_name?:string, timeout?:number}):(...args:any[])=>Promise { return function() { - let filter = params.dynamic_filter ? new Conjunction(params.filter, params.dynamic_filter) : params.filter; + const filter = params.dynamic_filter ? new Conjunction(params.filter, params.dynamic_filter) : params.filter; - let params_proto = Object.getPrototypeOf(params); + const params_proto = Object.getPrototypeOf(params); if (params_proto!==Object.prototype) params_proto.dynamic_filter = undefined; // reset, no longer needed for call const compile_info:compile_info = [`#public.${params.scope_name}.${name}`, [], {to:filter, sign:params.sign}]; diff --git a/runtime/runtime.ts b/runtime/runtime.ts index f8f8379a..72867197 100644 --- a/runtime/runtime.ts +++ b/runtime/runtime.ts @@ -27,7 +27,7 @@ Symbol.prototype.toJSON = function(){return globalThis.String(this)} /***** imports */ import { Compiler, compiler_options, PrecompiledDXB, ProtocolDataTypesMap, DatexResponse} from "../compiler/compiler.ts"; // Compiler functions -import { DecimalRef, IntegerRef, Pointer, PointerProperty, RefOrValue, Ref, TextRef, BooleanRef, ObjectWithDatexValues, JSValueWith$, MinimalJSRef, ObjectRef, RefLike, UpdateScheduler} from "./pointers.ts"; +import { Pointer, PointerProperty, RefOrValue, Ref, ObjectWithDatexValues, JSValueWith$, MinimalJSRef, ObjectRef, RefLike, UpdateScheduler} from "./pointers.ts"; import { Endpoint, endpoints, IdEndpoint, LOCAL_ENDPOINT, Target, target_clause, WildcardTarget } from "../types/addressing.ts"; import { RuntimePerformance } from "./performance_measure.ts"; import { NetworkError, PermissionError, PointerError, RuntimeError, SecurityError, ValueError, Error as DatexError, CompilerError, TypeError, SyntaxError, AssertionError } from "../types/errors.ts"; @@ -851,7 +851,7 @@ export class Runtime { * @param forceLocalExecution execute block even if receiver is external (default false) * @returns evaluated DATEX result */ - public static async executeDXBLocally(dxb:ArrayBuffer, context_location?:URL, overrideMeta?: Partial, forceLocalExecution = false):Promise { + public static async executeDXBLocally(dxb:ArrayBuffer, context_location?:URL, overrideMeta?: Partial, forceLocalExecution = false):Promise { // generate new header using executor scope header let header:dxb_header; let dxb_body:ArrayBuffer; @@ -3865,16 +3865,19 @@ export class Runtime { const value_prim = Ref.collapseValue(value,true,true); // DatexPrimitivePointers also collapsed try { + const currentValIsIntegerRef = current_val instanceof Ref && typeof current_val.val == "bigint"; + const currentValIsDecimalRef = current_val instanceof Ref && typeof current_val.val == "number"; + // x.current_val ?= value switch (action_type) { case BinaryCode.ADD: if (current_val instanceof Array && !(current_val instanceof Tuple)) current_val.push(value); // Array push (TODO array extend?) - else if (current_val instanceof TextRef && typeof value_prim == "string") await current_val.setVal(current_val.val + value); // primitive pointer operations - else if (current_val instanceof DecimalRef && typeof value_prim == "number") await current_val.setVal(current_val.val + value_prim); - else if (current_val instanceof IntegerRef && typeof value_prim == "bigint") await current_val.setVal(current_val.val + value_prim); - else if (current_val instanceof DecimalRef && typeof value_prim == "bigint") await current_val.setVal(current_val.val + Number(value_prim)); - else if (current_val instanceof IntegerRef && typeof value_prim == "number") throw new ValueError("Cannot apply a value to an pointer", SCOPE); + else if (current_val instanceof Ref && typeof current_val.val == "string" && typeof value_prim == "string") await current_val.setVal(current_val.val + value_prim); // primitive pointer operations + else if (currentValIsDecimalRef && typeof value_prim == "number") await current_val.setVal(current_val.val + value_prim); + else if (currentValIsIntegerRef && typeof value_prim == "bigint") await current_val.setVal(current_val.val + value_prim); + else if (currentValIsDecimalRef && typeof value_prim == "bigint") await current_val.setVal(current_val.val + Number(value_prim)); + else if (currentValIsIntegerRef && typeof value_prim == "number") throw new ValueError("Cannot apply a value to an pointer", SCOPE); else if (typeof current_val_prim == "number" && typeof value_prim == "number") Runtime.runtime_actions.setProperty(SCOPE, parent, key, current_val_prim+value_prim) // add else if ((typeof current_val_prim == "number" && typeof value_prim == "bigint") || (typeof current_val_prim == "bigint" && typeof value_prim == "number")) Runtime.runtime_actions.setProperty(SCOPE, parent, key, Number(current_val_prim)+Number(value_prim)) // add else if (typeof current_val_prim == "bigint" && typeof value_prim == "bigint") Runtime.runtime_actions.setProperty(SCOPE, parent, key, current_val_prim+value_prim) // add @@ -3891,10 +3894,10 @@ export class Runtime { case BinaryCode.SUBTRACT: if (current_val instanceof Array) Runtime.runtime_actions._removeItemFromArray(current_val, value); // Array splice - else if (current_val instanceof DecimalRef && typeof value_prim == "number") await current_val.setVal(current_val.val - value_prim); // primitive pointer operations - else if (current_val instanceof IntegerRef && typeof value_prim == "bigint") await current_val.setVal(current_val.val - value_prim); - else if (current_val instanceof DecimalRef && typeof value_prim == "bigint") await current_val.setVal(current_val.val - Number(value_prim)); - else if (current_val instanceof IntegerRef && typeof value_prim == "number") throw new ValueError("Cannot apply a value to an pointer", SCOPE); + else if (currentValIsDecimalRef && typeof value_prim == "number") await current_val.setVal(current_val.val - value_prim); // primitive pointer operations + else if (currentValIsIntegerRef && typeof value_prim == "bigint") await current_val.setVal(current_val.val - value_prim); + else if (currentValIsDecimalRef && typeof value_prim == "bigint") await current_val.setVal(current_val.val - Number(value_prim)); + else if (currentValIsIntegerRef && typeof value_prim == "number") throw new ValueError("Cannot apply a value to an pointer", SCOPE); else if (typeof current_val_prim == "number" && typeof value_prim == "number") Runtime.runtime_actions.setProperty(SCOPE, parent, key, current_val_prim-value_prim) // subtract else if ((typeof current_val_prim == "number" && typeof value_prim == "bigint") || (typeof current_val_prim == "bigint" && typeof value_prim == "number")) Runtime.runtime_actions.setProperty(SCOPE, parent, key, Number(current_val_prim)-Number(value_prim)) // subtract else if (typeof current_val_prim == "bigint" && typeof value_prim == "bigint") Runtime.runtime_actions.setProperty(SCOPE, parent, key, current_val_prim-value_prim) // subtract @@ -3909,10 +3912,10 @@ export class Runtime { break; case BinaryCode.MULTIPLY: - if (current_val instanceof DecimalRef && typeof value_prim == "number") await current_val.setVal(current_val.val * value_prim); // primitive pointer operations - else if (current_val instanceof IntegerRef && typeof value_prim == "bigint") await current_val.setVal(current_val.val * value_prim); - else if (current_val instanceof DecimalRef && typeof value_prim == "bigint") await current_val.setVal(current_val.val * Number(value_prim)); - else if (current_val instanceof IntegerRef && typeof value_prim == "number") throw new ValueError("Cannot apply a value to an pointer", SCOPE); + if (currentValIsDecimalRef && typeof value_prim == "number") await current_val.setVal(current_val.val * value_prim); // primitive pointer operations + else if (currentValIsIntegerRef && typeof value_prim == "bigint") await current_val.setVal(current_val.val * value_prim); + else if (currentValIsDecimalRef && typeof value_prim == "bigint") await current_val.setVal(current_val.val * Number(value_prim)); + else if (currentValIsIntegerRef && typeof value_prim == "number") throw new ValueError("Cannot apply a value to an pointer", SCOPE); else if (typeof current_val_prim == "number" && typeof value_prim == "number") Runtime.runtime_actions.setProperty(SCOPE, parent, key, current_val_prim*value_prim) // subtract else if ((typeof current_val_prim == "number" && typeof value_prim == "bigint") || (typeof current_val_prim == "bigint" && typeof value_prim == "number")) Runtime.runtime_actions.setProperty(SCOPE, parent, key, Number(current_val_prim)*Number(value_prim)) // subtract else if (typeof current_val_prim == "bigint" && typeof value_prim == "bigint") Runtime.runtime_actions.setProperty(SCOPE, parent, key, current_val_prim*value_prim) // subtract @@ -3927,10 +3930,10 @@ export class Runtime { break; case BinaryCode.DIVIDE: - if (current_val instanceof DecimalRef && typeof value_prim == "number") await current_val.setVal(current_val.val / value_prim); // primitive pointer operations - else if (current_val instanceof IntegerRef && typeof value_prim == "bigint") await current_val.setVal(current_val.val / value_prim); - else if (current_val instanceof DecimalRef && typeof value_prim == "bigint") await current_val.setVal(current_val.val / Number(value_prim)); - else if (current_val instanceof IntegerRef && typeof value_prim == "number") throw new ValueError("Cannot apply a value to an pointer", SCOPE); + if (currentValIsDecimalRef && typeof value_prim == "number") await current_val.setVal(current_val.val / value_prim); // primitive pointer operations + else if (currentValIsIntegerRef && typeof value_prim == "bigint") await current_val.setVal(current_val.val / value_prim); + else if (currentValIsDecimalRef && typeof value_prim == "bigint") await current_val.setVal(current_val.val / Number(value_prim)); + else if (currentValIsIntegerRef && typeof value_prim == "number") throw new ValueError("Cannot apply a value to an pointer", SCOPE); else if (typeof current_val_prim == "number" && typeof value_prim == "number") Runtime.runtime_actions.setProperty(SCOPE, parent, key, current_val_prim/value_prim) // subtract else if ((typeof current_val_prim == "number" && typeof value_prim == "bigint") || (typeof current_val_prim == "bigint" && typeof value_prim == "number")) Runtime.runtime_actions.setProperty(SCOPE, parent, key, Number(current_val_prim)/Number(value_prim)) // subtract else if (typeof current_val_prim == "bigint" && typeof value_prim == "bigint") Runtime.runtime_actions.setProperty(SCOPE, parent, key, current_val_prim/value_prim) // subtract @@ -3945,13 +3948,13 @@ export class Runtime { break; case BinaryCode.POWER: - if (current_val instanceof DecimalRef && typeof value_prim == "number") await current_val.setVal(current_val.val ** value_prim); // primitive pointer operations - else if (current_val instanceof IntegerRef && typeof value_prim == "bigint") { + if (currentValIsDecimalRef && typeof value_prim == "number") await current_val.setVal(current_val.val ** value_prim); // primitive pointer operations + else if (currentValIsIntegerRef && typeof value_prim == "bigint") { if (value_prim < 0) throw new ValueError("Cannot use a negative exponent with an integer") else current_val.val = current_val.val ** value_prim; } - else if (current_val instanceof DecimalRef && typeof value_prim == "bigint") await current_val.setVal(current_val.val ** Number(value_prim)); - else if (current_val instanceof IntegerRef && typeof value_prim == "number") throw new ValueError("Cannot apply a value to an pointer", SCOPE); + else if (currentValIsDecimalRef && typeof value_prim == "bigint") await current_val.setVal(current_val.val ** Number(value_prim)); + else if (currentValIsIntegerRef && typeof value_prim == "number") throw new ValueError("Cannot apply a value to an pointer", SCOPE); else if (typeof current_val_prim == "number" && typeof value_prim == "number") Runtime.runtime_actions.setProperty(SCOPE, parent, key, current_val_prim**value_prim) // power else if ((typeof current_val_prim == "number" && typeof value_prim == "bigint") || (typeof current_val_prim == "bigint" && typeof value_prim == "number")) Runtime.runtime_actions.setProperty(SCOPE, parent, key, Number(current_val_prim)**Number(value_prim)) // power else if (typeof current_val_prim == "bigint" && typeof value_prim == "bigint") Runtime.runtime_actions.setProperty(SCOPE, parent, key, current_val_prim**value_prim) // power @@ -3966,10 +3969,10 @@ export class Runtime { break; case BinaryCode.MODULO: - if (current_val instanceof DecimalRef && typeof value_prim == "number") await current_val.setVal(current_val.val % value_prim); // primitive pointer operations - else if (current_val instanceof IntegerRef && typeof value_prim == "bigint") await current_val.setVal(current_val.val % value_prim); - else if (current_val instanceof DecimalRef && typeof value_prim == "bigint") await current_val.setVal(current_val.val % Number(value_prim)); - else if (current_val instanceof IntegerRef && typeof value_prim == "number") throw new ValueError("Cannot apply a value to an pointer", SCOPE); + if (currentValIsDecimalRef && typeof value_prim == "number") await current_val.setVal(current_val.val % value_prim); // primitive pointer operations + else if (currentValIsIntegerRef && typeof value_prim == "bigint") await current_val.setVal(current_val.val % value_prim); + else if (currentValIsDecimalRef && typeof value_prim == "bigint") await current_val.setVal(current_val.val % Number(value_prim)); + else if (currentValIsIntegerRef && typeof value_prim == "number") throw new ValueError("Cannot apply a value to an pointer", SCOPE); else if (typeof current_val_prim == "number" && typeof value_prim == "number") Runtime.runtime_actions.setProperty(SCOPE, parent, key, current_val_prim%value_prim) // subtract else if ((typeof current_val_prim == "number" && typeof value_prim == "bigint") || (typeof current_val_prim == "bigint" && typeof value_prim == "number")) Runtime.runtime_actions.setProperty(SCOPE, parent, key, Number(current_val_prim)%Number(value_prim)) // subtract else if (typeof current_val_prim == "bigint" && typeof value_prim == "bigint") Runtime.runtime_actions.setProperty(SCOPE, parent, key, current_val_prim%value_prim) // subtract From 362290d041bee67ae818118917a4b297bf266cd7 Mon Sep 17 00:00:00 2001 From: benStre Date: Thu, 25 Jan 2024 13:17:02 +0100 Subject: [PATCH 2/7] fix MinimalJsRef typing --- runtime/pointers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/pointers.ts b/runtime/pointers.ts index 29f25efa..f884e5c7 100644 --- a/runtime/pointers.ts +++ b/runtime/pointers.ts @@ -825,7 +825,7 @@ export type JSValueWith$ = ObjectRef; export type MinimalJSRef> = _C extends symbol ? symbol : ( _C extends number|string|boolean|bigint ? - T: // keep pointer reference + Pointer<_C>: // keep pointer reference ObjectRef<_C> // collapsed object ) From b8e28b48800eba19a705e56d5d1164969b21489d Mon Sep 17 00:00:00 2001 From: benStre Date: Thu, 25 Jan 2024 13:28:43 +0100 Subject: [PATCH 3/7] remove GenericValue --- runtime/pointers.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/runtime/pointers.ts b/runtime/pointers.ts index f884e5c7..974b982a 100644 --- a/runtime/pointers.ts +++ b/runtime/pointers.ts @@ -709,8 +709,6 @@ export class PointerProperty extends Ref { } -// similar to Datex.Ref, but works better for all descending generic classes in typescript strict mode -export type GenericValue = RefLike; export type ReadonlyRef = Readonly>; /** From 10bae1db0aa7c170dd85bdb53eb9ad5a8da30397 Mon Sep 17 00:00:00 2001 From: benStre Date: Thu, 25 Jan 2024 13:38:02 +0100 Subject: [PATCH 4/7] fix MinimalJsRef --- runtime/pointers.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/runtime/pointers.ts b/runtime/pointers.ts index 974b982a..f4f03fad 100644 --- a/runtime/pointers.ts +++ b/runtime/pointers.ts @@ -819,16 +819,22 @@ export type JSValueWith$ = ObjectRef; // converts Object to Record +export type WrappedPointerValue = number|string|boolean|bigint|URL|Endpoint + // convert from any JS/DATEX value to minimal representation with reference export type MinimalJSRef> = _C extends symbol ? symbol : ( - _C extends number|string|boolean|bigint ? - Pointer<_C>: // keep pointer reference + _C extends WrappedPointerValue ? + PointerWithPrimitive<_C>: // keep pointer reference ObjectRef<_C> // collapsed object ) // return Pointer&T for primitives (excluding boolean) and Pointer otherwise -export type PointerWithPrimitive = T extends number|string|boolean|bigint ? Pointer&T : Pointer +export type PointerWithPrimitive = T extends WrappedPointerValue ? + T extends primitive ? + Pointer&T : // e.g. Pointer&number + Pointer : // e.g. Pointer + Pointer // e.g. Pointer> export type CollapsedValueAdvanced, COLLAPSE_POINTER_PROPERTY extends boolean|undefined = true, COLLAPSE_PRIMITIVE_POINTER extends boolean|undefined = true, _C = CollapsedValue> = // if From 9130b2d9d88c06da590ea7fa8a863e396537adbb Mon Sep 17 00:00:00 2001 From: benStre Date: Thu, 25 Jan 2024 14:01:58 +0100 Subject: [PATCH 5/7] add support for initial null pointer values (not useable yet with cross-network pointers --- runtime/pointers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/pointers.ts b/runtime/pointers.ts index f4f03fad..0c097212 100644 --- a/runtime/pointers.ts +++ b/runtime/pointers.ts @@ -2431,7 +2431,7 @@ export class Pointer extends Ref { if (is_transform) val = this.getInitialTransformValue(val) // Save reference to original - this.#type = Type.ofValue(val); + if (val!==undefined && val !== null) this.#type = Type.ofValue(val); // console.log("loaded : "+ this.id + " - " + this.#type, val) From 21b3ff7f34958c790d75821b4138eb2a390a0fde Mon Sep 17 00:00:00 2001 From: benStre Date: Thu, 25 Jan 2024 14:03:13 +0100 Subject: [PATCH 6/7] update comment --- runtime/pointers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/pointers.ts b/runtime/pointers.ts index 0c097212..c3b2bb58 100644 --- a/runtime/pointers.ts +++ b/runtime/pointers.ts @@ -2430,7 +2430,7 @@ export class Pointer extends Ref { // get transform wrapper if (is_transform) val = this.getInitialTransformValue(val) - // Save reference to original + // Get type from initial value, keep as if initial value is null/undefined if (val!==undefined && val !== null) this.#type = Type.ofValue(val); // console.log("loaded : "+ this.id + " - " + this.#type, val) From bc208b9a480c6c4a3fb22c1ab670d512b6980ba6 Mon Sep 17 00:00:00 2001 From: benStre Date: Thu, 25 Jan 2024 17:46:10 +0100 Subject: [PATCH 7/7] update logs --- runtime/runtime.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/runtime.ts b/runtime/runtime.ts index 72867197..48a827db 100644 --- a/runtime/runtime.ts +++ b/runtime/runtime.ts @@ -1058,7 +1058,7 @@ export class Runtime { .then(finish) .catch(e => { if (wait_for_result) reject(e); - else console.error("Error sending datex block", e); + else logger.debug("Error sending datex block (flood)"); }); } // send to receivers @@ -1073,7 +1073,7 @@ export class Runtime { .then(finish) .catch(e => { if (wait_for_result) reject(e); - else console.error("Error sending datex block", e); + else logger.debug("Error sending datex block to " + to_endpoint); }); } }