From 388ac24d7b9face79475d34f15935f81073896b5 Mon Sep 17 00:00:00 2001 From: benStre Date: Mon, 4 Mar 2024 12:37:03 +0100 Subject: [PATCH 1/4] minor fixes --- compiler/compiler.ts | 10 +++---- datex_short.ts | 9 +++++- js_adapter/decorators.ts | 37 ++++++++++++++++++++++++- js_adapter/js_class_adapter.ts | 3 +- runtime/pointers.ts | 1 + types/function-utils.ts | 4 +-- types/function.ts | 50 ++++++++-------------------------- types/js-function.ts | 4 +-- types/type.ts | 34 +++++++++++++++++------ 9 files changed, 93 insertions(+), 59 deletions(-) diff --git a/compiler/compiler.ts b/compiler/compiler.ts index 3f6fae9d..d0e437a7 100644 --- a/compiler/compiler.ts +++ b/compiler/compiler.ts @@ -202,7 +202,7 @@ export type compiler_scope = { max_block_size?: number, // max size of each block, if not Infinity (default), dxb might be split into multiple blocks - internal_var_index: number // count up for every new internal variable + internal_var_index: [number] // count up for every new internal variable internal_vars: WeakMap, number> // save variables for values with an internal variable internal_primitive_vars: WeakMap, number> // save variables for primitive values with an internal variable @@ -1345,7 +1345,7 @@ export class Compiler { // get value from dynamic index if (index instanceof Array) index = index[0]; - const var_number = SCOPE.internal_var_index++; + const var_number = SCOPE.internal_var_index[0]++; const add_scope_global = !SCOPE.assignment_end_indices.has(index); // only add if not already an assignment before const gap = Uint16Array.BYTES_PER_ELEMENT + 2 + (add_scope_global?1:0); @@ -2801,7 +2801,7 @@ export class Compiler { // handle and ReadableStream, if streaming (<<) if ((value instanceof Stream || value instanceof ReadableStream) && SCOPE.uint8[SCOPE.b_index-1] == BinaryCode.STREAM) return Compiler.builder.handleStream(value, SCOPE); - + // same value already inserted -> refer to the value with an internal variable if (add_insert_index && SCOPE.inserted_values?.has(value)) { // get variable for the already-existing value @@ -5757,7 +5757,7 @@ export class Compiler { b_index: 0, - internal_var_index: 0, + internal_var_index: options.parent_scope?.internal_var_index ?? [0], internal_vars: new WeakMap(), internal_primitive_vars: new Map(), @@ -5877,7 +5877,7 @@ export class Compiler { b_index: 0, - internal_var_index: 0, + internal_var_index: options.parent_scope?.internal_var_index ?? [0], internal_vars: new WeakMap(), internal_primitive_vars: new Map(), diff --git a/datex_short.ts b/datex_short.ts index e5fea4d9..9380445e 100644 --- a/datex_short.ts +++ b/datex_short.ts @@ -4,7 +4,7 @@ import { baseURL, Runtime, PrecompiledDXB, Type, Pointer, Ref, PointerProperty, primitive, Target, IdEndpoint, 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, dc } from "./datex_all.ts"; /** make decorators global */ -import { assert as _assert, timeout as _timeout, entrypoint as _entrypoint, entrypointProperty as _entrypointProperty, property as _property, struct as _struct, endpoint as _endpoint, sync as _sync} from "./datex_all.ts"; +import { assert as _assert, timeout as _timeout, entrypoint as _entrypoint, ref as _ref, entrypointProperty as _entrypointProperty, property as _property, struct as _struct, endpoint as _endpoint, sync as _sync, allow as _allow} from "./datex_all.ts"; import { effect as _effect, always as _always, reactiveFn as _reactiveFn, asyncAlways as _asyncAlways, toggle as _toggle, map as _map, equals as _equals, selectProperty as _selectProperty, not as _not } from "./functions.ts"; export * from "./functions.ts"; import { NOT_EXISTING, DX_SLOTS, SLOT_GET, SLOT_SET } from "./runtime/constants.ts"; @@ -20,7 +20,10 @@ export {instance} from "./js_adapter/js_class_adapter.ts"; declare global { const property: typeof _property; + const ref: typeof _ref; const assert: typeof _assert; + const allow: typeof _allow; + const struct: typeof _struct; const endpoint: typeof _endpoint; @@ -56,7 +59,11 @@ declare global { // @ts-ignore global globalThis.property = _property; // @ts-ignore global +globalThis.ref = _ref; +// @ts-ignore global globalThis.assert = _assert; +// @ts-ignore global +globalThis.allow = _allow; // @ts-ignore global globalThis.struct = _struct; diff --git a/js_adapter/decorators.ts b/js_adapter/decorators.ts index 14842015..75b4e4ed 100644 --- a/js_adapter/decorators.ts +++ b/js_adapter/decorators.ts @@ -1,6 +1,9 @@ import { endpoint_name, target_clause } from "../datex_all.ts"; +import { Endpoint } from "../types/addressing.ts"; +import { PermissionError } from "../types/errors.ts"; +import { JSTransferableFunction } from "../types/js-function.ts"; import type { Type } from "../types/type.ts"; -import type { Class } from "../utils/global_types.ts"; +import type { Class, datex_meta } from "../utils/global_types.ts"; import { Decorators } from "./js_class_adapter.ts"; @@ -42,6 +45,19 @@ export function property(type: ((...args: any[])=>any)|undefined|string|Type|Cla }) } +// TODO: experimental alias for @property: +/** + * Binds a (static) class property to a DATEX ref + * @param type optional type for the ref, must match the declared TypeScript property type + */ +export function ref(type: string|Type|Class): (value: ((...args: any[])=>any)|undefined, context: PropertyDecoratorContext)=>void +export function ref(value: ((...args: any[])=>any)|undefined, context: PropertyDecoratorContext): void +export function ref(type: ((...args: any[])=>any)|undefined|string|Type|Class, context?: PropertyDecoratorContext) { + return handleClassFieldDecoratorWithOptionalArgs([type], context as ClassFieldDecoratorContext, ([type], context:PropertyDecoratorContext) => { + return Decorators.property(type as Type, context) + }) +} + /** * Adds an assertion to a class field that is checked before the field is set @@ -53,6 +69,25 @@ export function assert(assertion:(val:T)=>boolean|string|undefined): (value: }) } +export function allowany>(assertion:(meta: datex_meta) => boolean|Promise): (value: T, context: ClassMethodDecoratorContext) => T { + return handleClassMethodDecoratorWithArgs([assertion], ([assertion], fn, context) => { + // async + if (JSTransferableFunction.functionIsAsync(assertion as (...args:any)=>any)) { + return async function(this:any, ...args:any) { + if (!await assertion(datex.meta)) throw new PermissionError("Endpoint has no permission to call this function") + return fn.apply(this, args) + } as any + } + // sync + else { + return function(this:any, ...args:any) { + if (!assertion(datex.meta)) throw new PermissionError("Endpoint has no permission to call this function") + return fn.apply(this, args) + } as any + } + }) +} + /** * Make a class publicly accessible for an endpoint (only static methods and properties marked with @property are exposed) * Also enables calling static class methods on other endpoints diff --git a/js_adapter/js_class_adapter.ts b/js_adapter/js_class_adapter.ts index a11a4a0d..5fd5d377 100644 --- a/js_adapter/js_class_adapter.ts +++ b/js_adapter/js_class_adapter.ts @@ -90,6 +90,7 @@ export class Decorators { public static setMetadata(context:DecoratorContext, key:string|symbol, value:unknown) { + // TODO: handle nested inheritance: if metadata has prototype but no own properties, inherit nested if (!context.metadata[key]) context.metadata[key] = {} const data = context.metadata[key] as {public?:Record, constructor?:any} if (context.kind == "class") { @@ -766,7 +767,7 @@ export function proxyClass(original_clas } }, apply(target,_thisArg,argArray) { - return Pointer.createOrGet(instance(target, argArray[0])).js_value + return Pointer.createOrGet(instance(target, argArray[0])).js_value }, getPrototypeOf(target) { return original_class diff --git a/runtime/pointers.ts b/runtime/pointers.ts index ea7ae1c6..8ce5cda0 100644 --- a/runtime/pointers.ts +++ b/runtime/pointers.ts @@ -2788,6 +2788,7 @@ export class Pointer extends Ref { if (initialValue === VOID) throw new ValueError("initial tranform value cannot be void"); this.setVal(initialValue, true, true); + if (transform instanceof Scope) this.#transform_scope = transform; // store transform scope else if (persistent_datex_transform) { await this.setDatexTransform(persistent_datex_transform) // TODO: only workaround diff --git a/types/function-utils.ts b/types/function-utils.ts index 08491168..7072873e 100644 --- a/types/function-utils.ts +++ b/types/function-utils.ts @@ -22,7 +22,7 @@ const EXTRACT_USED_VARS = Symbol("EXTRACT_USED_VARS") * ``` * @param variables */ -export function use(noDatex: 'no-datex', ...variables: unknown[]): true +export function use(noDatex: 'standalone', ...variables: unknown[]): true /** * Used to declare all variables from the parent scope that are used inside the current function. * This is required for functions that are transferred to a different context or restored from eternal pointers. @@ -66,7 +66,7 @@ function getUsedVars(fn: (...args:unknown[])=>unknown) { const usedVars = usedVarsSource.split(",").map(v=>v.trim()).filter(v=>!!v) const flags = [] for (const usedVar of usedVars) { - if (usedVar == `"no-datex"` || usedVar == `'no-datex'`) flags.push("no-datex"); + if (usedVar == `"standalone"` || usedVar == `'standalone'`) flags.push("standalone"); else if (!usedVar.match(/^[a-zA-Z_$][0-9a-zA-Z_$\u0080-\uFFFF]*$/)) throw new RuntimeError("Unexpected identifier in 'use' declaration: '" + usedVar+ "' - only variable names are allowed."); } if (flags.length) usedVars.splice(0, flags.length); // remove flags diff --git a/types/function.ts b/types/function.ts index 6a3d24b5..ff7feabf 100644 --- a/types/function.ts +++ b/types/function.ts @@ -65,8 +65,6 @@ export class Function any = (...args: any) => any> e is_async = true; - meta_index?: number - datex_timeout?: number about?:Markdown @@ -101,8 +99,7 @@ export class Function any = (...args: any) => any> e location:Endpoint = Runtime.endpoint, allowed_callers?:target_clause, anonymize_result=false, - params?:Tuple, - meta_index?:number + params?:Tuple ):Function<(...args:Parameters)=>ReturnType> & Callable, ReturnType> { if (ntarget.name.startsWith("bound ")) { @@ -119,10 +116,10 @@ export class Function any = (...args: any) => any> e if (ntarget instanceof Function) return ntarget; // auto detect params and meta index - if (params == null && meta_index == null) { - [meta_index, params] = this.getFunctionParamsAndMetaIndex(ntarget, context, key_in_parent) + if (params == null ) { + params = this.getFunctionParamsAndMetaIndex(ntarget, context, key_in_parent)[1] } - return )=>ReturnType> & Callable, ReturnType>> new Function<(...args:Parameters)=>ReturnType>(undefined, ntarget, context, location, allowed_callers, anonymize_result, params, meta_index); + return )=>ReturnType> & Callable, ReturnType>> new Function<(...args:Parameters)=>ReturnType>(undefined, ntarget, context, location, allowed_callers, anonymize_result, params); } @@ -138,10 +135,9 @@ export class Function any = (...args: any) => any> e return new Function(scope, undefined, context, location, allowed_callers, anonymize_result, params); } - private constructor(body?:Scope, ntarget?:T, context?:object|Pointer, location:Endpoint = Runtime.endpoint, allowed_callers?:target_clause, anonymize_result=false, params?:Tuple, meta_index?:number) { + private constructor(body?:Scope, ntarget?:T, context?:object|Pointer, location:Endpoint = Runtime.endpoint, allowed_callers?:target_clause, anonymize_result=false, params?:Tuple) { super((...args:any[]) => this.handleApply(new Tuple(args))); - this.meta_index = meta_index; this.params = params??new Tuple(); this.params_keys = [...this.params.named.keys()]; @@ -151,7 +147,6 @@ export class Function any = (...args: any) => any> e // execute DATEX code if (body instanceof Scope) { - this.meta_index = 0; const ctx = context instanceof Pointer ? context.val : context; this.fn = (meta, ...args:any[])=>body.execute(meta.sender, ctx, args); // execute DATEX code } @@ -375,7 +370,7 @@ export class Function any = (...args: any) => any> e // record if (value instanceof Tuple) { params = []; - for (let [key, val] of value.entries()) { + for (const [key, val] of value.entries()) { // normal number index if (!isNaN(Number(key.toString()))) { if (Number(key.toString()) < 0) logger.warn(Datex.Pointer.getByValue(this)?.idString() + ": Invalid function arguments: '" + key + "'"); @@ -406,10 +401,12 @@ export class Function any = (...args: any) => any> e } + let isSpread = false; // has argument with ..., unlimited args size // argument type checking if (this.params) { let i = 0; for (const [name, required_type] of this.params.entries()) { + if (typeof name == "string" && name.startsWith("...")) isSpread = true; const actual_type = Type.ofValue(params[i]); @@ -433,34 +430,11 @@ export class Function any = (...args: any) => any> e // no meta index, still crop the params to the required size if possible // injects meta to stack trace, can be accessed via Datex.getMeta() - if (this.meta_index==undefined) { - const call = (this.is_async ? callWithMetadataAsync : callWithMetadata); - if (this.unknown_params || params.length<=required_param_nr) { - return call(meta, this.fn, params, context) // this.fn.call(context, ...params); - } - else return call(meta, this.fn, params.slice(0,required_param_nr), context);// this.fn.call(context, ...params.slice(0,required_param_nr)); - } - // inject meta information at given index when calling the function - else if (this.meta_index==-1) { - // crop params to required size - if (this.unknown_params || params.length==required_param_nr) return this.fn.call(context, ...params, meta); - else if (params.length>required_param_nr) return this.fn.call(context, ...params.slice(0,required_param_nr), meta); - else return this.fn.call(context, ...params, ...Array(required_param_nr-params.length), meta); - } - // add meta index at the beginning - else if (this.meta_index==0) return this.fn.call(context, meta, ...params); - // insert meta index inbetween - else if (this.meta_index > 0) { - const p1 = params.slice(0,this.meta_index); - const p2 = params.slice(this.meta_index); - // is the size of p1 right? - if (p1.length == this.meta_index) return this.fn.call(context, ...p1, meta, ...p2); - // p1 too short (p2 is empty in this case) - else return this.fn.call(context, ...p1, ...Array(this.meta_index-p1.length), meta); - } - else { - throw new RuntimeError("Invalid index for the meta parameter", SCOPE); + const call = (this.is_async ? callWithMetadataAsync : callWithMetadata); + if (this.unknown_params || isSpread || params.length<=required_param_nr) { + return call(meta, this.fn, params, context) // this.fn.call(context, ...params); } + else return call(meta, this.fn, params.slice(0,required_param_nr), context);// this.fn.call(context, ...params.slice(0,required_param_nr)); } diff --git a/types/js-function.ts b/types/js-function.ts index ee7a1cfc..5ee8a5fe 100644 --- a/types/js-function.ts +++ b/types/js-function.ts @@ -85,8 +85,8 @@ export class JSTransferableFunction extends ExtensibleFunction { * Important: use createAsync for async functions instead * @param fn */ - static createunknown>(fn: T, options:JSTransferableFunctionOptions = {}): JSTransferableFunction & Callable, ReturnType> { - const {vars, flags} = getDeclaredExternalVariables(fn); + static createunknown>(fn: T, options:JSTransferableFunctionOptions = {}, useDeclaration?: {vars:{[k:string]:unknown}, flags?:string[]}): JSTransferableFunction & Callable, ReturnType> { + const {vars, flags} = useDeclaration ?? getDeclaredExternalVariables(fn); options.isLocal ??= true; return this.#createTransferableFunction(getSourceWithoutUsingDeclaration(fn), vars, flags, options) as any; } diff --git a/types/type.ts b/types/type.ts index 74d51703..eadf7042 100644 --- a/types/type.ts +++ b/types/type.ts @@ -22,7 +22,7 @@ import type { Iterator } from "./iterator.ts"; import {StorageMap, StorageWeakMap} from "./storage-map.ts" import {StorageSet, StorageWeakSet} from "./storage-set.ts" import { ExtensibleFunction } from "./function-utils.ts"; -import type { JSTransferableFunction } from "./js-function.ts"; +import { JSTransferableFunction } from "./js-function.ts"; import type { MatchCondition } from "../storage/storage.ts"; export type inferDatexType = T extends Type ? JST : any; @@ -169,7 +169,7 @@ export class Type extends ExtensibleFunction { // maps DATEX template type representation to corresponding typescript types public setTemplate(template: NT):Type ? TT : any ) })>> public setTemplate(template: object) { - DatexObject.freeze(template); + // DatexObject.freeze(template); this.#template = template; this.#visible_children = new Set(Object.keys(this.#template)); // add extended types from template @@ -198,6 +198,7 @@ export class Type extends ExtensibleFunction { // @ts-ignore this.#template is always a Tuple const required_type = this.#template[key]; + // check if can set property (has setter of value) const desc = Object.getOwnPropertyDescriptor(assign_to_object, key); if (desc && !desc.writable) { @@ -315,14 +316,14 @@ export class Type extends ExtensibleFunction { } /** returns an object with a [INIT_PROPS] function that can be passed to newJSInstance() or called manually */ - public getPropertyInitializer(value:any) { + public getPropertyInitializer(value:any, strict = true) { const initialized = {i:false}; // property initializer - sets existing property for pointer object (is passed as first constructor argument when reconstructing) return Object.freeze({ [INIT_PROPS]: (instance:any)=>{ if (initialized.i) return; initialized.i=true; - this.initProperties(instance, value) + this.initProperties(instance, value, strict) } }) } @@ -335,13 +336,20 @@ export class Type extends ExtensibleFunction { return instance; } - public initProperties(instance:any, value:any) { + public initProperties(instance:any, value:any, strict = true) { if (!value) return; // initialize with template - if (this.#template) this.createFromTemplate(value, instance) + if (strict && this.#template) this.createFromTemplate(value, instance) // just copy all properties if no template found else { - Object.assign(instance, value); + for (const [key, val] of Object.entries(value)) { + if (val instanceof JSTransferableFunction) { + // workaround create new transferable function with correct "this" context + instance[key] = $$(JSTransferableFunction.recreate(val.source, {...val.deps, 'this':instance})); + } + else if (typeof val == "function") instance[key] = val.bind(instance); + else instance[key] = val; + } } } @@ -354,8 +362,16 @@ export class Type extends ExtensibleFunction { } // call custom DATEX constructor or replicator - if (is_constructor && this.#constructor_fn) this.#constructor_fn.apply(instance, args); - else if (!is_constructor && this.#replicator_fn) this.#replicator_fn.apply(instance, args); + if (is_constructor && this.#constructor_fn) { + const res = this.#constructor_fn.apply(instance, args) + // catch promise rejections (not awaited) + if (res instanceof Promise) res.catch(e=>{console.error(e)}) + } + else if (!is_constructor && this.#replicator_fn) { + const res = this.#replicator_fn.apply(instance, args); + // catch promise rejections (not awaited) + if (res instanceof Promise) res.catch(e=>{console.error(e)}) + } return instance; } From 0cb094af7087ea1748521a421d2be893ba6f3e30 Mon Sep 17 00:00:00 2001 From: benStre Date: Tue, 5 Mar 2024 00:32:22 +0100 Subject: [PATCH 2/4] fix compiler internal variable issues, datex function calls --- compiler/compiler.ts | 11 ++++++----- init.ts | 6 ++++++ runtime/runtime.ts | 1 + types/function.ts | 3 +++ types/type.ts | 2 +- 5 files changed, 17 insertions(+), 6 deletions(-) diff --git a/compiler/compiler.ts b/compiler/compiler.ts index d0e437a7..33770abd 100644 --- a/compiler/compiler.ts +++ b/compiler/compiler.ts @@ -2754,7 +2754,7 @@ export class Compiler { // insert any value besides Maybes - insert: (value:any, SCOPE:compiler_scope, is_root=true, parents?:Set, unassigned_children?:[number, any, number][], add_insert_index = true) => { + insert: (value:any, SCOPE:compiler_scope, is_root=true, parents?:Set, unassigned_children?:[number, any, number][], replace_optimization = true, register_insert_index = true) => { if (value?.[DX_REPLACE]) value = value[DX_REPLACE]; @@ -2790,7 +2790,7 @@ export class Compiler { if (typeof mapped == "number") { Compiler.builder.insertVariable(SCOPE, undefined, ACTION_TYPE.GET, undefined, mapped); SCOPE.uint8[SCOPE.b_index++] = BinaryCode.CHILD_GET_REF - Compiler.builder.insert(value.key, SCOPE); + Compiler.builder.insert(value.key, SCOPE, undefined, undefined, undefined, undefined, false); } else { throw new Error("Invalid DX_BOUND_LOCAL_SLOT: " + v_name); @@ -2803,7 +2803,7 @@ export class Compiler { if ((value instanceof Stream || value instanceof ReadableStream) && SCOPE.uint8[SCOPE.b_index-1] == BinaryCode.STREAM) return Compiler.builder.handleStream(value, SCOPE); // same value already inserted -> refer to the value with an internal variable - if (add_insert_index && SCOPE.inserted_values?.has(value)) { + if (replace_optimization && SCOPE.inserted_values?.has(value)) { // get variable for the already-existing value const value_index = SCOPE.inserted_values.get(value)!; const existing_val_var = Compiler.builder.createInternalVariableAtIndex(value_index, SCOPE, value) @@ -2817,7 +2817,8 @@ export class Compiler { const start_index = Compiler.builder.getDynamicIndex(SCOPE.b_index, SCOPE); // add original value to inserted values map (only if useful, exclude short values like boolean and null) - if (!indirectReferencePtr && !(SCOPE.options.no_duplicate_value_optimization && (typeof value == "bigint" || typeof value == "number" || typeof value == "string")) && value!==VOID && + if (register_insert_index && !indirectReferencePtr && !(SCOPE.options.no_duplicate_value_optimization && + (typeof value == "bigint" || typeof value == "number" || typeof value == "string")) && value!==VOID && value !==null && typeof value != "boolean" && !((typeof value == "bigint" || typeof value == "number") && value<=Compiler.MAX_INT_32 && value>=Compiler.MIN_INT_32) @@ -2965,7 +2966,7 @@ export class Compiler { _SCOPE.inner_scope.path_info_index = _SCOPE.b_index++; _SCOPE.uint8[_SCOPE.inner_scope.path_info_index] = BinaryCode.CHILD_GET_REF; // key - Compiler.builder.insert(value.key, _SCOPE); // TODO cast to compiler_scope might ignore uninitialized scope properties + Compiler.builder.insert(value.key, _SCOPE, undefined, undefined, undefined, undefined, false); // TODO cast to compiler_scope might ignore uninitialized scope properties // insert injected var if extract pointers if (SCOPE.extract_pointers) { diff --git a/init.ts b/init.ts index 7a3059eb..805b0af5 100644 --- a/init.ts +++ b/init.ts @@ -34,11 +34,17 @@ if (client_type == "deno") { }); } + +let initialized = false; + /** * Runtime init (sets ENV, storage, endpoint, ...) */ export async function init() { + if (initialized) return; + initialized = true; + // register DatexStorage as pointer source registerStorageAsPointerSource(); diff --git a/runtime/runtime.ts b/runtime/runtime.ts index e39868c4..3fa0fc0d 100644 --- a/runtime/runtime.ts +++ b/runtime/runtime.ts @@ -5658,6 +5658,7 @@ export class Runtime { // get var else { + let val:any; // read special internal variables if (name == "result") val = SCOPE.result; diff --git a/types/function.ts b/types/function.ts index ff7feabf..8fca6c28 100644 --- a/types/function.ts +++ b/types/function.ts @@ -428,6 +428,9 @@ export class Function any = (...args: any) => any> e const required_param_nr = this.params.size; + // is datex function + if (this.body) return this.fn.call(context, meta, ...params); + // no meta index, still crop the params to the required size if possible // injects meta to stack trace, can be accessed via Datex.getMeta() const call = (this.is_async ? callWithMetadataAsync : callWithMetadata); diff --git a/types/type.ts b/types/type.ts index eadf7042..1441a271 100644 --- a/types/type.ts +++ b/types/type.ts @@ -347,7 +347,7 @@ export class Type extends ExtensibleFunction { // workaround create new transferable function with correct "this" context instance[key] = $$(JSTransferableFunction.recreate(val.source, {...val.deps, 'this':instance})); } - else if (typeof val == "function") instance[key] = val.bind(instance); + else if (typeof val == "function" && typeof val.bind == "function") instance[key] = val.bind(instance); else instance[key] = val; } } From aeed5496fe131dc1dc590b9747691194b2432914 Mon Sep 17 00:00:00 2001 From: benStre Date: Tue, 5 Mar 2024 13:18:37 +0100 Subject: [PATCH 3/4] fix nested decorator metadata inheritance --- js_adapter/js_class_adapter.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/js_adapter/js_class_adapter.ts b/js_adapter/js_class_adapter.ts index 5fd5d377..9ed2a12f 100644 --- a/js_adapter/js_class_adapter.ts +++ b/js_adapter/js_class_adapter.ts @@ -27,6 +27,7 @@ import { Conjunction, Disjunction, Logical } from "../types/logic.ts"; import { client_type } from "../utils/constants.ts"; import { Assertion } from "../types/assertion.ts"; import { getCallerInfo } from "../utils/caller_metadata.ts"; +import { createFunctionWithDependencyInjectionsResolveLazyPointers } from "../types/function-utils.ts"; const { Reflect: MetadataReflect } = client_type == 'deno' ? await import("https://deno.land/x/reflect_metadata@v0.1.12/mod.ts") : {Reflect}; @@ -90,7 +91,16 @@ export class Decorators { public static setMetadata(context:DecoratorContext, key:string|symbol, value:unknown) { - // TODO: handle nested inheritance: if metadata has prototype but no own properties, inherit nested + // handle inheritance for nested object: if metadata has prototype but no own properties, inherit nested + if (Object.getPrototypeOf(context.metadata) && !Object.getOwnPropertyNames(context.metadata).length && !Object.getOwnPropertySymbols(context.metadata).length) { + const proto = Object.getPrototypeOf(context.metadata); + for (const key of [...Object.getOwnPropertyNames(proto), ...Object.getOwnPropertySymbols(proto)]) { + context.metadata[key] = {}; + if (proto[key]?.public) (context.metadata[key] as any).public = Object.create(proto[key].public); + if (proto[key]?.constructor)(context.metadata[key] as any).constructor = proto[key].constructor; + } + } + if (!context.metadata[key]) context.metadata[key] = {} const data = context.metadata[key] as {public?:Record, constructor?:any} if (context.kind == "class") { From 19cf7ea25bb547b3bcb49b8f85e0c053005607ff Mon Sep 17 00:00:00 2001 From: benStre Date: Tue, 5 Mar 2024 15:41:37 +0100 Subject: [PATCH 4/4] fix endpoint key network requests --- runtime/crypto.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/runtime/crypto.ts b/runtime/crypto.ts index e4c56437..3a3393e4 100644 --- a/runtime/crypto.ts +++ b/runtime/crypto.ts @@ -332,7 +332,12 @@ export class Crypto { // get endpoint public keys // TODO: don't sign?, does not work when running as @+unyt2: await datex('#public.Blockchain.getEndpointPublicKeys(?)', [endpoint], Target.get('@+unyt2'), false) try { - exported_keys = await Runtime.Blockchain.getEndpointPublicKeys(endpoint); + try { + exported_keys = await Runtime.Blockchain!.getEndpointPublicKeys(endpoint); + } + catch { + logger.debug("Blockchain request failed, trying network interface"); + } if (!exported_keys) exported_keys = await NetworkUtils.get_keys(endpoint); // if (exported_keys) await this.storeKeys(endpoint, exported_keys); if (!exported_keys) {