Skip to content

Commit

Permalink
Merge pull request #92 from unyt-org/fixes
Browse files Browse the repository at this point in the history
Fixes
  • Loading branch information
benStre authored Mar 18, 2024
2 parents 1a2e507 + 19cf7ea commit 7f1bbd9
Show file tree
Hide file tree
Showing 12 changed files with 125 additions and 65 deletions.
21 changes: 11 additions & 10 deletions compiler/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<Record<string, unknown>, number> // save variables for values with an internal variable
internal_primitive_vars: WeakMap<Record<string, unknown>, number> // save variables for primitive values with an internal variable

Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -2754,7 +2754,7 @@ export class Compiler {

// insert any value besides Maybes

insert: (value:any, SCOPE:compiler_scope, is_root=true, parents?:Set<any>, unassigned_children?:[number, any, number][], add_insert_index = true) => {
insert: (value:any, SCOPE:compiler_scope, is_root=true, parents?:Set<any>, unassigned_children?:[number, any, number][], replace_optimization = true, register_insert_index = true) => {

if (value?.[DX_REPLACE]) value = value[DX_REPLACE];

Expand Down Expand Up @@ -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);
Expand All @@ -2801,9 +2801,9 @@ export class Compiler {

// handle <Stream> 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)) {
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)
Expand All @@ -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)
Expand Down Expand Up @@ -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, <compiler_scope>_SCOPE); // TODO cast to compiler_scope might ignore uninitialized scope properties
Compiler.builder.insert(value.key, <compiler_scope>_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) {
Expand Down Expand Up @@ -5757,7 +5758,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(),

Expand Down Expand Up @@ -5877,7 +5878,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(),

Expand Down
9 changes: 8 additions & 1 deletion datex_short.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
6 changes: 6 additions & 0 deletions init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
37 changes: 36 additions & 1 deletion js_adapter/decorators.ts
Original file line number Diff line number Diff line change
@@ -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";


Expand Down Expand Up @@ -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<T>(type: string|Type<T>|Class<T>): (value: ((...args: any[])=>any)|undefined, context: PropertyDecoratorContext<T>)=>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
Expand All @@ -53,6 +69,25 @@ export function assert<T>(assertion:(val:T)=>boolean|string|undefined): (value:
})
}

export function allow<T extends (...args:any)=>any>(assertion:(meta: datex_meta) => boolean|Promise<boolean>): (value: T, context: ClassMethodDecoratorContext<unknown, T>) => 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
Expand Down
13 changes: 12 additions & 1 deletion js_adapter/js_class_adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -90,6 +91,16 @@ export class Decorators {


public static setMetadata(context:DecoratorContext, key:string|symbol, value:unknown) {
// 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<string|symbol,any>, constructor?:any}
if (context.kind == "class") {
Expand Down Expand Up @@ -766,7 +777,7 @@ export function proxyClass<T extends { new(...args: any[]): any;}>(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
Expand Down
7 changes: 6 additions & 1 deletion runtime/crypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
1 change: 1 addition & 0 deletions runtime/pointers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2788,6 +2788,7 @@ export class Pointer<T = any> extends Ref<T> {
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
Expand Down
1 change: 1 addition & 0 deletions runtime/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5658,6 +5658,7 @@ export class Runtime {

// get var
else {

let val:any;
// read special internal variables
if (name == "result") val = SCOPE.result;
Expand Down
4 changes: 2 additions & 2 deletions types/function-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
Loading

0 comments on commit 7f1bbd9

Please sign in to comment.