From 47ea334503be071f677f86db82d24a26ee09c97b Mon Sep 17 00:00:00 2001 From: benStre Date: Mon, 4 Dec 2023 05:25:20 +0100 Subject: [PATCH] symbols --- docs/manual/11 Types.md | 13 +++++++++---- runtime/pointers.ts | 20 ++++++++++++++------ runtime/runtime.ts | 14 ++++++++++++-- types/type.ts | 6 ++++-- 4 files changed, 39 insertions(+), 14 deletions(-) diff --git a/docs/manual/11 Types.md b/docs/manual/11 Types.md index d9cc6e39..3f0c9753 100644 --- a/docs/manual/11 Types.md +++ b/docs/manual/11 Types.md @@ -32,13 +32,13 @@ Datex.Type.std.Any === any Most builtin JavaScript types, like Map, Set or Array have equivalent types in the DATEX std library. There are only a few types that are implemented specifically to match JS types: -### js:TransferableFunction +### js:Function -The `js:TransferableFunction` (`Datex.Type.js.TransferableFunction`) is a special wrapper +The `js:Function` (`Datex.Type.js.Function`) is a special wrapper around a JavaScript function that can be transferred between endpoints. In contrast to a normal function (`std:Function`) that can also be mapped to a JavaScript function, -a `js:TransferableFunction` is always executed on the endpoint where it is called, not on the origin endpoint. +a `js:Function` is always executed on the endpoint where it is called, not on the origin endpoint. A transferable functions can be created from a normal JS function. Dependencies from the parent scope can be declared with a `use()` statement: @@ -47,7 +47,7 @@ import { JSTransferableFunction } from "datex-core-legacy/types/js-function.ts"; const data = $$([1,2,3]); -// create a js:TransferableFunction +// create a js:Function const transferableFn = JSTransferableFunction.create(() => { use (data); @@ -73,6 +73,11 @@ Examples for `js:Object`s: The property values of a `js:Object` are never automatically bound to pointers when the object is bound to a pointer. + +### js:Symbol + +DATEX has no native symbol type. JavaScript symbols are mapped to `js:Symbol` values. + ## Structs The `struct` helper function allows you to define DATEX types with a diff --git a/runtime/pointers.ts b/runtime/pointers.ts index 31baa551..74602280 100644 --- a/runtime/pointers.ts +++ b/runtime/pointers.ts @@ -777,10 +777,14 @@ export type JSValueWith$ = ObjectRef; // convert from any JS/DATEX value to minimal representation with reference export type MinimalJSRefGeneralTypes> = - JSPrimitiveToDatexRef<_C> extends never ? ObjectRef<_C> : JSPrimitiveToDatexRef<_C> + _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> = - JSPrimitiveToDatexRef<_C> extends never ? ObjectRef<_C> : (Pointer<_C> & (_C extends boolean ? unknown : _C)) +export type MinimalJSRef> = + _C extends symbol ? symbol : ( + JSPrimitiveToDatexRef<_C> extends never ? ObjectRef<_C> : (Pointer<_C> & (_C extends boolean ? unknown : _C)) + ) // return Pointer&T for primitives (excluding boolean) and Pointer otherwise export type PointerWithPrimitive = T extends number|string|boolean|bigint ? Pointer&T : Pointer @@ -1839,7 +1843,7 @@ export class Pointer extends Ref { #updateIsJSPrimitive(val:any = this.val) { const type = this.#type ?? Type.ofValue(val); - this.#is_js_primitive = !(Object(val) === val && !type.is_js_pseudo_primitive && !(type == Type.js.NativeObject && globalThis.Element && val instanceof globalThis.Element)) + this.#is_js_primitive = (typeof val !== "symbol") && !(Object(val) === val && !type.is_js_pseudo_primitive && !(type == Type.js.NativeObject && globalThis.Element && val instanceof globalThis.Element)) } /** @@ -2232,6 +2236,10 @@ export class Pointer extends Ref { let val = Ref.collapseValue(v,true,true); + if (typeof val == "symbol" && Symbol.keyFor(val) !== undefined) { + throw new Error("Global and well-known symbols (e.g. Symbol.for('name') or Symbol.iterator) are no yet supported as pointer values") + } + // get transform wrapper if (is_transform) val = this.getInitialTransformValue(val) @@ -2272,7 +2280,7 @@ export class Pointer extends Ref { // create proxy const value = this.addObjProxy((val instanceof UnresolvedValue) ? val[DX_VALUE] : val); // add $, $$ - this.add$Properties(value); + if (typeof value !== "symbol") this.add$Properties(value); // // add reference to this DatexPointer to the value // if (!this.is_anonymous) { @@ -3022,7 +3030,7 @@ export class Pointer extends Ref { const res = JSInterface.createProxy(obj, this, this.type); if (res != INVALID && res != NOT_EXISTING) return res; // proxy created successfully - if (obj instanceof Stream || obj instanceof DatexFunction || obj instanceof JSTransferableFunction) { // no proxy needed?! + if (typeof obj == "symbol" || obj instanceof Stream || obj instanceof DatexFunction || obj instanceof JSTransferableFunction) { // no proxy needed?! return obj; } diff --git a/runtime/runtime.ts b/runtime/runtime.ts index a2a97d3b..5426ff72 100644 --- a/runtime/runtime.ts +++ b/runtime/runtime.ts @@ -2094,8 +2094,8 @@ export class Runtime { let new_value:any = UNKNOWN_TYPE; - // only handle std namespace / js:Object - if (type.namespace == "std" || type == Type.js.NativeObject) { + // only handle std namespace / js:Object / js:Symbol + if (type.namespace == "std" || type == Type.js.NativeObject || type == Type.js.Symbol) { if (old_value instanceof Pointer) old_value = old_value.val; // handle default casts @@ -2175,6 +2175,12 @@ export class Runtime { else new_value = INVALID; break; } + case Type.js.Symbol: { + if (old_value === VOID) new_value = Symbol(); + else if (typeof old_value == "string") new_value = Symbol(old_value); + else new_value = INVALID; + break; + } case Type.std.Tuple: { if (old_value === VOID) new_value = new Tuple().seal(); else if (old_value instanceof Array){ @@ -2482,6 +2488,10 @@ export class Runtime { // primitives if (typeof value == "string" || typeof value == "boolean" || typeof value == "number" || typeof value == "bigint") return value; + + // symbol + if (typeof value == "symbol") return value.toString().slice(7,-1) || undefined + // directly return, cannot be overwritten if (value === VOID || value === null || value instanceof Endpoint || value instanceof Type) return value; if (value instanceof Scope) return value; diff --git a/types/type.ts b/types/type.ts index 4a304fed..e4bd4472 100644 --- a/types/type.ts +++ b/types/type.ts @@ -799,7 +799,7 @@ export class Type extends ExtensibleFunction { if (typeof value == "bigint") return >Type.std.integer; if (typeof value == "number") return >Type.std.decimal; if (typeof value == "boolean") return >Type.std.boolean; - if (typeof value == "symbol") return Type.std.void; // TODO?, ignores symbols + if (typeof value == "symbol") return Type.js.Symbol; if (value instanceof ArrayBuffer || value instanceof TypedArray) return >Type.std.buffer; if (value instanceof Tuple) return >Type.std.Tuple; @@ -872,6 +872,7 @@ export class Type extends ExtensibleFunction { if (_forClass == BigInt || BigInt.isPrototypeOf(_forClass)) return >Type.std.integer; if (_forClass == Number || Number.isPrototypeOf(_forClass)) return >Type.std.decimal; if (_forClass == globalThis.Boolean || globalThis.Boolean.isPrototypeOf(_forClass)) return >Type.std.boolean; + if (_forClass == Symbol || Symbol.isPrototypeOf(_forClass)) return >Type.js.Symbol; if (_forClass == ArrayBuffer || TypedArray.isPrototypeOf(_forClass)) return >Type.std.buffer; if (_forClass == Tuple || Tuple.isPrototypeOf(_forClass)) return >Type.std.Tuple; @@ -930,7 +931,8 @@ export class Type extends ExtensibleFunction { */ static js = { NativeObject: Type.get("js:Object"), // special object type for non-plain objects (objects with prototype) - no automatic children pointer initialization - TransferableFunction: Type.get("js:Function") + TransferableFunction: Type.get("js:Function"), + Symbol: Type.get("js:Symbol") } /**