├── .github └── workflows │ └── test.yml ├── .gitignore ├── .prettierignore ├── LICENSE ├── README.md ├── lerna.json ├── package-lock.json ├── package.json └── packages ├── assembly ├── README.md ├── asconfig.json ├── configuration.ts ├── index.d.ts ├── index.ts ├── package.json ├── tsconfig.json └── util.ts ├── test ├── as-pect.asconfig.json ├── as-pect.config.js ├── asconfig.json ├── assembly │ ├── __tests__ │ │ ├── as-pect.d.ts │ │ └── index.spec.ts │ ├── as_types.d.ts │ └── tsconfig.json ├── index.js ├── output.txt └── package.json └── transform ├── README.md ├── package.json ├── src ├── createAsonAlignofValueofMethod.ts ├── createAsonInstanceOfMethod.ts ├── createAsonLengthMethod.ts ├── createAsonPutMethod.ts └── index.ts └── tsconfig.json /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | jobs: 8 | test: 9 | runs-on: ${{ matrix.platform }} 10 | name: "${{ matrix.platform }} ${{ matrix.nodeversion }}" 11 | strategy: 12 | matrix: 13 | platform: [ubuntu-latest, macos-latest] 14 | nodeversion: ['16.x', '17.x', '18.x', '19.x'] 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Use Node.js v"${{ matrix.nodeversion }}"" 18 | uses: actions/setup-node@v1 19 | with: 20 | node-version: ${{ matrix.nodeversion }} 21 | - name: Install dependencies 22 | run: npm install --force 23 | - name: test 24 | run: npm run test 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | build/ 51 | *.wat 52 | *.wasm 53 | *.map 54 | *.asm.js 55 | **/lib 56 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | packages/assembly/**/*.* 2 | **/package.json 3 | **/package-lock.json 4 | **/node_modules/**/*.* 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Joshua Tenner 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Introduction to ASON: AssemblyScript Object Notation 2 | 3 | ASON is a data oriented algorithm designed for compact and speedy storage of AssemblyScript objects in a binary format. 4 | 5 | There are many Serialization methods that can serialize a conceptual object into a buffer or string of some kind, like JSON and protobuf. ASON however is fine tuned just for AssemblyScript objects. 6 | 7 | JSON and XML are declarative tree-like data structure formats. ASON uses a data-oriented approach, which means instead of a declarative data structure, it uses a collection of tables to describe object shapes. Assembling a tree becomes a linear time operation with a minimal amount of jumps. 8 | 9 | # Uses 10 | 11 | This library is perfect for transferring references from one module of the same type to another module of the exact same type. 12 | 13 | These serialization methods are also great for helping store references like configuration files on disk. If JSON is too verbose, or requires too much memory, or takes too long to parse for the fast world of WebAssembly, ASON is a better alternative, since it reduces overhead byte storage by a very large amount. 14 | 15 | # How To Use 16 | 17 | Install from npm: 18 | 19 | ``` 20 | npm install --save-dev @ason/assembly 21 | ``` 22 | 23 | Modify your asconfig to include the transform: 24 | 25 | ```ts 26 | { 27 | "options": { 28 | ... // other options here 29 | "transform": ["@ason/transform"] 30 | } 31 | } 32 | ``` 33 | 34 | Import the library and serialize away! 35 | 36 | You can use the built-in functions `Ason.serialize()` and `Ason.deserialize()`: 37 | 38 | ```ts 39 | import { ASON } from "@ason/assembly"; 40 | 41 | // serialize can determine type information 42 | let buffer: StaticArray = ASON.serialize([3.14, 99, 25.624] as Array); 43 | 44 | // deserialize must have the type passed (to perform type assertions) 45 | let result: Array = ASON.deserialize>(buffer); 46 | 47 | assert(result.length == 3); 48 | assert(result[0] == 3.14); 49 | assert(result[1] == 99); 50 | assert(result[2] == 25.624); 51 | ``` 52 | 53 | It's also possible to save heap allocations, by declaring a new `Serializer` and `Deserializer` object. This is optimal when serializing multiple objects of the same type: 54 | 55 | ```ts 56 | import { Serializer, Deserializer } from "@ason/assembly"; 57 | class Vec3 { 58 | constructor(public x: f32, public y: f32, public z: f32) {} 59 | } 60 | 61 | let result = new Array>(); // an array of buffers 62 | let serializer = new ASON.Serializer(); 63 | 64 | for (let i = 0; i < 10; i++) { 65 | result.push(serializer.serialize(new Vec3(1, 2, 3))); 66 | } 67 | 68 | let deserializer = new ASON.Deserializer(); 69 | 70 | for (let i = 0; i < 10; i++) { 71 | let vec = deserializer.deserialize(result[i]); 72 | assert(vec); // make sure the reference isn't null 73 | assert(vec.x == 1); // check the properties 74 | assert(vec.y == 2); 75 | assert(vec.x == 3); 76 | } 77 | ``` 78 | 79 | ## Advanced: Writing Custom Serializers and Deserializers 80 | 81 | Some objects have values that don't necessarily need to be stored in an ASON Byte Array, in order to be preserved. In order to save on space, you can roll your own serializer and deserializer functions for your objects that don't store those extraneous values. Do this by defining two functions for your object: 82 | 83 | - `__asonSerialize(): StaticArray` - This function should take all values you wish to preserve in your object, store them in a `StaticArray`, and return that Byte Array. 84 | - `__asonDeserialize(buffer: StaticArray): void` - This function should take in the Byte Array generated by the `__asonSerialize()` function, and use that to rebuild the object. 85 | 86 | ```ts 87 | class CustomVector { 88 | x: f32 = 1; 89 | y: f32 = 2; 90 | z: f32 = 3; 91 | 92 | __asonSerialize(): StaticArray { 93 | let result = new StaticArray(offsetof()); 94 | memory.copy(changetype(result), changetype(this), offsetof()); 95 | return result; 96 | } 97 | 98 | __asonDeserialize(buffer: StaticArray): void { 99 | assert(buffer.length == offsetof()); 100 | memory.copy(changetype(this), changetype(buffer), offsetof()); 101 | } 102 | } 103 | ``` 104 | 105 | # Caveats 106 | 107 | - If the modules using this library are different, then runtime type information might not match. This will result in runtime errors, `instanceof` checks failing, and undefined behavior. ASON also performs type information validation for objects at the top level, so providing the wrong reference type parameter to the `ASON.deserialize` function will result in a runtime error. 108 | 109 | - ASON serialization optimizes for large object trees, at the cost of making simple serialization slightly more expensive. 110 | 111 | - ASON cannot serialize objects with more than `2^32-1` values or references in them. This is because `u32.MAX_VALUE` is reserved for null references so that map keys and set entries can contain null values. We have chosen to accept this limitation, because if you are attempting to serialize single objects that are 4 Gigabytes in size (at an absolute minimum), we will not pass judgment, but we will recommend refactoring. 112 | 113 | # Implementation 114 | 115 | The object that the serializer returns is a `StaticArray`, which is just a buffer of bytes. This array is composed of a Header at the beginning of the buffer (which will be changing quite a lot until the API becomes stable,) and the individual Tables that compose an ASON serialization. The header simply contains the byte length of each Table. 116 | 117 | After the `ASONHeader` is a series of Tables describing the shape and contents of every field, every reference (stored like a Table of c-like pointers), and every possible combination of data segments, sets, maps, etc. that could be contained within an object. 118 | 119 | It also holds a linking Table, that defines every way objects are linked to each other within the serialized object. These links must be defined, and asserted while deserializing, otherwise the garbage collection algorithm could potentially free objects, or otherwise mishandle them while they are being assembled back together at deserialization time. An additional benefit of using this kind of linking Table is the way this gracefully handles circular references: the serializer will recognize that a reference to a specific object already exists within one of the other Tables, and instead of adding a duplicate reference, the linking Table will simply point to the existing reference. 120 | 121 | Lastly, we assert that the `entryId` stored at entry `0` is the primary entry of the buffer. If the generic type `T` of the Serializer is a number, it asserts that the primary entry of the reference table is a `Box` instead of a `T`. The serializer wraps the primary entry in a `Box`, if it does not already have a box. 122 | 123 | # MIT License 124 | 125 | ``` 126 | MIT License 127 | 128 | Copyright (c) 2021 Joshua Tenner 129 | 130 | Permission is hereby granted, free of charge, to any person obtaining a copy 131 | of this software and associated documentation files (the "Software"), to deal 132 | in the Software without restriction, including without limitation the rights 133 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 134 | copies of the Software, and to permit persons to whom the Software is 135 | furnished to do so, subject to the following conditions: 136 | 137 | The above copyright notice and this permission notice shall be included in all 138 | copies or substantial portions of the Software. 139 | 140 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 141 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 142 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 143 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 144 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 145 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 146 | SOFTWARE. 147 | ``` 148 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [ 3 | "packages/*" 4 | ], 5 | "version": "0.11.1" 6 | } 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ason", 3 | "version": "0.0.4", 4 | "description": "AssemblyScript Objects Encoded and Decoded in a standardized way", 5 | "main": "index.js", 6 | "scripts": { 7 | "postinstall": "lerna bootstrap --hoist", 8 | "pretest": "npm run build", 9 | "test": "cd packages/test && npm run test", 10 | "build": "lerna exec tsc --scope '*/transform'" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/jtenner/ason.git" 15 | }, 16 | "keywords": [ 17 | "assemblyscript", 18 | "data", 19 | "serialization", 20 | "transform" 21 | ], 22 | "author": "Joshua Tenner ", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/jtenner/ason/issues" 26 | }, 27 | "workspaces": [ 28 | "assembly", 29 | "transform" 30 | ], 31 | "homepage": "https://github.com/jtenner/ason#readme", 32 | "devDependencies": { 33 | "@as-pect/cli": "^8.0.0", 34 | "lerna": "^6.1.0", 35 | "npm-run-all": "^4.1.5", 36 | "typescript": "^4.9.3", 37 | "assemblyscript": "^0.24.1" 38 | }, 39 | "dependencies": { 40 | "visitor-as": "^0.11.3" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/assembly/README.md: -------------------------------------------------------------------------------- 1 | # @ason/assembly 2 | 3 | This package is the assemblyscript library for the `Serializer` and `Deserializer` class implementations. To see the full documentation, please visit the [ason](https://github.com/jtenner/ason) github repo. 4 | 5 | 6 | # License 7 | 8 | MIT 9 | 10 | ``` 11 | MIT License 12 | 13 | Copyright (c) 2021 Joshua Tenner 14 | 15 | Permission is hereby granted, free of charge, to any person obtaining a copy 16 | of this software and associated documentation files (the "Software"), to deal 17 | in the Software without restriction, including without limitation the rights 18 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | copies of the Software, and to permit persons to whom the Software is 20 | furnished to do so, subject to the following conditions: 21 | 22 | The above copyright notice and this permission notice shall be included in all 23 | copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 | SOFTWARE. 32 | ``` 33 | -------------------------------------------------------------------------------- /packages/assembly/asconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "targets": { 3 | "debug": { 4 | "binaryFile": "build/untouched.wasm", 5 | "textFile": "build/untouched.wat", 6 | "sourceMap": true, 7 | "debug": true 8 | }, 9 | "release": { 10 | "binaryFile": "build/optimized.wasm", 11 | "textFile": "build/optimized.wat", 12 | "sourceMap": true, 13 | "optimizeLevel": 3, 14 | "shrinkLevel": 1, 15 | "converge": false, 16 | "noAssert": false 17 | } 18 | }, 19 | "options": {} 20 | } -------------------------------------------------------------------------------- /packages/assembly/configuration.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | export const ASON_EFFECTIVE_INITIAL_DATA_SEGMENT_TABLE_LENGTH: usize = 4 | isDefined(ASON_INITIAL_DATA_SEGMENT_TABLE_LENGTH) 5 | ? ASON_INITIAL_DATA_SEGMENT_TABLE_LENGTH 6 | : u16.MAX_VALUE; 7 | 8 | export const ASON_EFFECTIVE_INITIAL_ARRAY_DATA_SEGMENT_TABLE_LENGTH: usize = 9 | isDefined(ASON_INITIAL_ARRAY_DATA_SEGMENT_TABLE_LENGTH) 10 | ? ASON_INITIAL_ARRAY_DATA_SEGMENT_TABLE_LENGTH 11 | : u16.MAX_VALUE; 12 | 13 | export const ASON_EFFECTIVE_INITIAL_LINK_TABLE_LENGTH: usize = 14 | isDefined(ASON_INITIAL_LINK_TABLE_LENGTH) 15 | ? ASON_INITIAL_LINK_TABLE_LENGTH 16 | : u16.MAX_VALUE; 17 | 18 | export const ASON_EFFECTIVE_INITIAL_REFERENCE_TABLE_LENGTH: usize = 19 | isDefined(ASON_INITIAL_REFERENCE_TABLE_LENGTH) 20 | ? ASON_INITIAL_REFERENCE_TABLE_LENGTH 21 | : u16.MAX_VALUE; 22 | 23 | export const ASON_EFFECTIVE_INITIAL_ARRAY_TABLE_LENGTH: usize = 24 | isDefined(ASON_INITIAL_ARRAY_TABLE_LENGTH) 25 | ? ASON_INITIAL_ARRAY_TABLE_LENGTH 26 | : u16.MAX_VALUE; 27 | 28 | export const ASON_EFFECTIVE_INITIAL_ARRAY_LINK_TABLE_LENGTH: usize = 29 | isDefined(ASON_INITIAL_ARRAY_LINK_TABLE_LENGTH) 30 | ? ASON_INITIAL_ARRAY_LINK_TABLE_LENGTH 31 | : u16.MAX_VALUE; 32 | 33 | export const ASON_EFFECTIVE_INITIAL_SET_ENTRY_TABLE_LENGTH: usize = 34 | isDefined(ASON_INITIAL_SET_ENTRY_TABLE_LENGTH) 35 | ? ASON_INITIAL_SET_ENTRY_TABLE_LENGTH 36 | : u16.MAX_VALUE; 37 | 38 | export const ASON_EFFECTIVE_INITIAL_MAP_KEY_VALUE_PAIR_ENTRY_TABLE_LENGTH: usize = 39 | isDefined(ASON_INITIAL_MAP_KEY_VALUE_PAIR_ENTRY_TABLE_LENGTH) 40 | ? ASON_INITIAL_MAP_KEY_VALUE_PAIR_ENTRY_TABLE_LENGTH 41 | : u16.MAX_VALUE; 42 | 43 | export const ASON_EFFECTIVE_MAP_REFERENCE_TABLE_LENGTH: usize = 44 | isDefined(ASON_MAP_REFERENCE_TABLE_LENGTH) 45 | ? ASON_MAP_REFERENCE_TABLE_LENGTH 46 | : u16.MAX_VALUE; 47 | 48 | export const ASON_EFFECTIVE_INITIAL_SET_REFERENCE_TABLE_LENGTH: usize = 49 | isDefined(ASON_INITIAL_SET_REFERENCE_TABLE_LENGTH) 50 | ? ASON_INITIAL_SET_REFERENCE_TABLE_LENGTH 51 | : u16.MAX_VALUE; 52 | 53 | export const ASON_EFFECTIVE_INITIAL_CUSTOM_TABLE_LENGTH: usize = 54 | isDefined(ASON_INITIAL_CUSTOM_TABLE_LENGTH) 55 | ? ASON_INITIAL_CUSTOM_TABLE_LENGTH 56 | : u16.MAX_VALUE; 57 | 58 | export const ASON_EFFECTIVE_INITIAL_STATIC_REFERENCE_TABLE_LENGTH: usize = 59 | isDefined(ASON_INITIAL_STATIC_REFERENCE_TABLE_LENGTH) 60 | ? ASON_INITIAL_STATIC_REFERENCE_TABLE_LENGTH 61 | : u16.MAX_VALUE; 62 | -------------------------------------------------------------------------------- /packages/assembly/index.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // These constants are potentially provided the compile --use flag, and tweak how tables 5 | // are created. 6 | declare const ASON_INITIAL_DATA_SEGMENT_TABLE_LENGTH: i32; 7 | declare const ASON_INITIAL_ARRAY_DATA_SEGMENT_TABLE_LENGTH: i32; 8 | declare const ASON_INITIAL_ARRAY_TABLE_LENGTH: i32; 9 | declare const ASON_INITIAL_ARRAY_LINK_TABLE_LENGTH: i32; 10 | declare const ASON_INITIAL_LINK_TABLE_LENGTH: i32; 11 | declare const ASON_INITIAL_REFERENCE_TABLE_LENGTH: i32; 12 | declare const ASON_INITIAL_SET_REFERENCE_TABLE_LENGTH: i32; 13 | declare const ASON_INITIAL_SET_ENTRY_TABLE_LENGTH: i32; 14 | declare const ASON_MAP_REFERENCE_TABLE_LENGTH: i32; 15 | declare const ASON_INITIAL_MAP_KEY_VALUE_PAIR_ENTRY_TABLE_LENGTH: i32; 16 | declare const ASON_INITIAL_CUSTOM_TABLE_LENGTH: i32; 17 | declare const ASON_INITIAL_STATIC_REFERENCE_TABLE_LENGTH: i32; 18 | -------------------------------------------------------------------------------- /packages/assembly/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ASON_EFFECTIVE_INITIAL_ARRAY_DATA_SEGMENT_TABLE_LENGTH, 3 | ASON_EFFECTIVE_INITIAL_ARRAY_LINK_TABLE_LENGTH, 4 | ASON_EFFECTIVE_INITIAL_ARRAY_TABLE_LENGTH, 5 | ASON_EFFECTIVE_INITIAL_CUSTOM_TABLE_LENGTH, 6 | ASON_EFFECTIVE_INITIAL_DATA_SEGMENT_TABLE_LENGTH, 7 | ASON_EFFECTIVE_INITIAL_LINK_TABLE_LENGTH, 8 | ASON_EFFECTIVE_INITIAL_MAP_KEY_VALUE_PAIR_ENTRY_TABLE_LENGTH, 9 | ASON_EFFECTIVE_INITIAL_REFERENCE_TABLE_LENGTH, 10 | ASON_EFFECTIVE_INITIAL_SET_ENTRY_TABLE_LENGTH, 11 | ASON_EFFECTIVE_INITIAL_SET_REFERENCE_TABLE_LENGTH, 12 | ASON_EFFECTIVE_MAP_REFERENCE_TABLE_LENGTH, 13 | ASON_EFFECTIVE_INITIAL_STATIC_REFERENCE_TABLE_LENGTH, 14 | } from "./configuration"; 15 | 16 | // @ts-ignore rt/common is defined by assemblyscript 17 | import { TOTAL_OVERHEAD, OBJECT } from "rt/common"; 18 | import { 19 | ArrayDataSegmentEntry, 20 | ArrayEntry, 21 | ArrayLinkEntry, 22 | ASONHeader, 23 | CustomEntry, 24 | DataSegmentEntry, 25 | LinkEntry, 26 | MapKeyValuePairEntry, 27 | MapKeyValueType, 28 | MapReferenceEntry, 29 | ReferenceEntry, 30 | SetKeyEntry, 31 | SetReferenceEntry, 32 | StaticReferenceEntry, 33 | Table, 34 | } from "./util"; 35 | 36 | class Dummy {} 37 | 38 | /** Structure of a set entry. Credit: https://github.com/AssemblyScript/assemblyscript/blob/master/std/assembly/set.ts */ 39 | @unmanaged class SetEntry { 40 | key: K; 41 | taggedNext: usize; // LSB=1 indicates EMPTY 42 | } 43 | 44 | /** Structure of a map entry. Credit: https://github.com/AssemblyScript/assemblyscript/blob/master/std/assembly/map.ts */ 45 | @unmanaged class MapEntry { 46 | key: K; 47 | value: V; 48 | taggedNext: usize; // LSB=1 indicates EMPTY 49 | } 50 | 51 | // @ts-ignore: valid inline 52 | @inline 53 | function getObjectSize(value: T): usize { 54 | return changetype(changetype(value) - TOTAL_OVERHEAD).rtSize; 55 | } 56 | // @ts-ignore: valid inline 57 | @inline 58 | function getObjectType(value: usize): u32 { 59 | return changetype(value - TOTAL_OVERHEAD).rtId; 60 | } 61 | 62 | function __unusedVoidCallDeserialize(value: T): bool { 63 | let buffer: StaticArray; 64 | // @ts-ignore 65 | value.__asonDeserialize(buffer); 66 | return true; 67 | } 68 | // @ts-ignore: valid global 69 | @global 70 | export namespace ASON { 71 | export class Serializer { 72 | // entries 73 | private entryId: u32; 74 | private entries: Map = new Map(); 75 | private segments: Map = new Map(); 76 | 77 | // tables 78 | private arrayDataSegmentTable: Table = new Table(ASON_EFFECTIVE_INITIAL_ARRAY_DATA_SEGMENT_TABLE_LENGTH); 79 | private arrayLinkTable: Table = new Table(ASON_EFFECTIVE_INITIAL_ARRAY_LINK_TABLE_LENGTH); 80 | private arrayTable: Table = new Table(ASON_EFFECTIVE_INITIAL_ARRAY_TABLE_LENGTH); 81 | private customTable: Table = new Table(ASON_EFFECTIVE_INITIAL_CUSTOM_TABLE_LENGTH); 82 | private dataSegmentTable: Table = new Table(ASON_EFFECTIVE_INITIAL_DATA_SEGMENT_TABLE_LENGTH); 83 | private linkTable: Table = new Table(ASON_EFFECTIVE_INITIAL_LINK_TABLE_LENGTH); 84 | private mapKeyValuePairsTable: Table = new Table(ASON_EFFECTIVE_INITIAL_MAP_KEY_VALUE_PAIR_ENTRY_TABLE_LENGTH); 85 | private mapReferenceTable: Table = new Table(ASON_EFFECTIVE_MAP_REFERENCE_TABLE_LENGTH); 86 | private referenceTable: Table = new Table(ASON_EFFECTIVE_INITIAL_REFERENCE_TABLE_LENGTH) 87 | private setEntryTable: Table = new Table(ASON_EFFECTIVE_INITIAL_SET_ENTRY_TABLE_LENGTH); 88 | private setReferenceTable: Table = new Table(ASON_EFFECTIVE_INITIAL_SET_REFERENCE_TABLE_LENGTH); 89 | private staticReferenceTable: Table = new Table(ASON_EFFECTIVE_INITIAL_STATIC_REFERENCE_TABLE_LENGTH); 90 | 91 | constructor() {} 92 | 93 | /** 94 | * Serialize a given `T`, and return a buffer. 95 | * @param {T} value - The T to be serialized. 96 | * @returns {StaticArray} - A buffer. 97 | */ 98 | public serialize(value: T): StaticArray { 99 | if (isReference(value)) { 100 | if (changetype(value) == 0) return new StaticArray(0); 101 | if (isManaged(value) && !isFunction(value)) { 102 | // @ts-ignore inside isDefined() 103 | if (isDefined(ASON_TRACE)) { 104 | // @ts-ignore interface added at runtime 105 | // trace(`serializing ${(value as InternalTransformInterface).__asonNameof()}`); 106 | } 107 | } 108 | } 109 | 110 | // reset entry id indicies 111 | this.entryId = 0; 112 | this.entries.clear(); 113 | this.entries.set(0, u32.MAX_VALUE); 114 | 115 | this.segments.clear(); 116 | 117 | // reset all the tables to index = 0 118 | this.arrayDataSegmentTable.reset(); 119 | this.arrayLinkTable.reset(); 120 | this.arrayTable.reset(); 121 | this.customTable.reset(); 122 | this.dataSegmentTable.reset(); 123 | this.linkTable.reset(); 124 | this.mapKeyValuePairsTable.reset(); 125 | this.mapReferenceTable.reset(); 126 | this.referenceTable.reset(); 127 | this.setEntryTable.reset(); 128 | this.setReferenceTable.reset(); 129 | this.staticReferenceTable.reset(); 130 | 131 | let entryId: u32; 132 | 133 | if (isReference(value)) { 134 | entryId = this.put(value); 135 | } else { 136 | entryId = this.put(new Box(value)); 137 | } 138 | assert(entryId == 0); 139 | 140 | // write everything to a buffer 141 | return this.commit(); 142 | } 143 | 144 | /** This is a private method that must be publicly exposed to work. Please do not use it. */ 145 | @unsafe public put(value: U): u32 { 146 | if (isReference(value)) { 147 | if (this.entries.has(changetype(value))) return this.entries.get(changetype(value)); 148 | if (isManaged(value) && !isFunction(value)) { 149 | if (isDefined(ASON_TRACE)) { 150 | // @ts-ignore interface added at runtime 151 | // trace(`putting ${(value as InternalTransformInterface).__asonNameof()}`); 152 | } 153 | } 154 | } 155 | 156 | // @ts-ignore: safe compile time check 157 | if (isDefined(value.__asonSerialize)) { 158 | // @ts-ignore: safe compile time check 159 | if (isDefined(value.__asonDeserialize)) { 160 | // custom serialization 161 | let temp: StaticArray; 162 | // @ts-ignore: safe compile time check 163 | if (!isDefined(temp = value.__asonSerialize())) { 164 | ERROR("__asonSerialize method must return a StaticArray"); 165 | } 166 | // @ts-ignore: safe compile time check 167 | if (!isDefined(__unusedVoidCallDeserialize(value))) { 168 | ERROR("__asonDeserialize method must accept a StaticArray"); 169 | } 170 | if (!isReference(value)) { 171 | ERROR("Custom ASON serializtion can only be performed on a reference."); 172 | } 173 | 174 | if (isNullable(value)) { 175 | return this.putCustom(value!); 176 | } else { 177 | return this.putCustom(value); 178 | } 179 | } 180 | } 181 | 182 | if (isFunction(value)) { 183 | return this.putDataSegment(value); 184 | // @ts-ignore: ArrayBufferView is global 185 | } else if (value instanceof ArrayBufferView) { 186 | return this.putArrayDataSegment(value); 187 | } else if (value instanceof Map) { 188 | return this.putMap(value); 189 | } else if (value instanceof Set) { 190 | return this.putSet(value); 191 | } else if (value instanceof ArrayBuffer) { 192 | // arraybuffer 193 | return this.putDataSegment(value); 194 | } else if (value instanceof String) { 195 | if (changetype(value) < __heap_base) return this.putStaticReference(value); 196 | // strings 197 | return this.putDataSegment(value); 198 | } else if (value instanceof StaticArray) { 199 | let __dummyValue = unchecked(value[0]) 200 | if (isReference(__dummyValue)) { 201 | // reference array 202 | let parent = this.putReference(value); 203 | 204 | // check for null regardless if the type is nullable to prevent runtime errors 205 | if (changetype(value) != 0) { 206 | // link the children 207 | let length = value.length; 208 | for (let i = 0; i < length; i++) { 209 | // link the child if it isn't null 210 | let child = unchecked(value[i]); 211 | if (changetype(child) != 0) { 212 | this.putLink(this.put(child), parent, i << alignof()); 213 | } 214 | } 215 | } 216 | return parent; 217 | } 218 | // static arrays with data 219 | return this.putDataSegment(value); 220 | } else if (value instanceof Array) { 221 | let __dummyValue = unchecked(value[0]); 222 | if (isReference(__dummyValue)) { 223 | let parent = this.putArray(value); 224 | // link the children 225 | let length = value.length; 226 | for (let i = 0; i < length; i++) { 227 | // link the child if it isn't null 228 | let child = unchecked(value[i]); 229 | if (changetype(child) != 0) { 230 | this.putArrayLink(this.put(child), parent, i); 231 | } 232 | } 233 | return parent; 234 | } 235 | // it's not a reference, we are a data segment 236 | return this.putArrayDataSegment(value); 237 | } else { 238 | return this.putReferenceAndFields(value); 239 | } 240 | } 241 | 242 | private putCustom(value: U): u32 { 243 | // @ts-ignore: safe at compile time 244 | let buffer: StaticArray = value.__asonSerialize(); 245 | let length = buffer.length; 246 | let customTable = this.customTable; 247 | let entry = customTable.allocate(); 248 | let entryId = this.entryId++; 249 | entry.entryId = entryId; 250 | entry.rtId = getObjectType(changetype(value)); 251 | entry.offset = offsetof(); 252 | entry.byteLength = length; 253 | // @ts-ignore: method index access, safe 254 | entry.deserializeFuncIndex = value.__asonDeserialize.index; 255 | let copyToPtr = customTable.allocateSegment(length); 256 | memory.copy(copyToPtr, changetype(buffer), length); 257 | this.entries.set(changetype(value), entryId); 258 | return entryId; 259 | } 260 | 261 | private putStaticReference(value: U): u32 { 262 | let entryId = this.entryId++; 263 | let entry = this.staticReferenceTable.allocate(); 264 | entry.entryId = entryId; 265 | entry.ptr = changetype(value); 266 | this.entries.set(changetype(value), entryId); 267 | return entryId; 268 | } 269 | 270 | private putMap(value: U): u32 { 271 | // @ts-ignore: U can be indexed and valued 272 | if (!isReference>() && !isReference>()) { 273 | return this.putReferenceAndFields(value); 274 | } 275 | 276 | let mapEntryId: u32 = this.entryId++; 277 | 278 | let mapEntry = this.mapReferenceTable.allocate(); 279 | mapEntry.entryId = mapEntryId; 280 | mapEntry.rtId = getObjectType(changetype(value)); 281 | 282 | // @ts-ignore: type U is guaranteed to be a Map 283 | let maxkv = max(sizeof>(), sizeof>()); 284 | // @ts-ignore: type U is guaranteed to be a Map 285 | let align = max(maxkv, sizeof()) - 1; 286 | // @ts-ignore: type U is guaranteed to be a Map 287 | let entryOffset = offsetof, valueof>>(); 288 | let entrySize = (entryOffset + align) & ~align; 289 | mapEntry.entrySize = entrySize; 290 | 291 | // @ts-ignore: type U is guaranteed to be a Map 292 | mapEntry.capacity = 1 << (32 - clz(value.size)); 293 | 294 | // @ts-ignore: type U is guaranteed to be a Map 295 | let keys = value.keys(); 296 | // @ts-ignore: type U is guaranteed to be a Map 297 | let values = value.values(); 298 | // @ts-ignore: type U is guaranteed to be a Map 299 | let size = value.size; 300 | let mapKeyValuePairsTable = this.mapKeyValuePairsTable; 301 | 302 | // Loop over every key value pair 303 | for (let i = 0; i < size; i++) { 304 | let entry = mapKeyValuePairsTable.allocate(); 305 | let key = unchecked(keys[i]); 306 | let value = unchecked(values[i]); 307 | 308 | // set the parent entry id 309 | entry.parentEntryId = mapEntryId; 310 | 311 | // if the key is a reference, we store an entryId 312 | if (isReference(key)) { 313 | entry.keyType = isString(key) ? MapKeyValueType.String : MapKeyValueType.Dummy; 314 | let keyEntryId = this.put(key) 315 | store( 316 | changetype(entry), 317 | keyEntryId, 318 | offsetof("key"), 319 | ); 320 | } else { 321 | // numbers require entry size, isSigned and storing it 322 | entry.keyType = MapKeyValueType.Number; 323 | // @ts-ignore: type U is guaranteed to be a Map 324 | store>( 325 | changetype(entry), 326 | key, 327 | offsetof("key"), 328 | ); 329 | // @ts-ignore: type U is guaranteed to be a Map 330 | entry.keySize = sizeof>(); 331 | // @ts-ignore: type U is guaranteed to be a Map 332 | entry.keyIsSigned = isSigned>() || isFloat>(); 333 | } 334 | if (isReference(value)) { 335 | entry.valueType = MapKeyValueType.Dummy; 336 | let valueEntryId = this.put(value); 337 | store( 338 | changetype(entry), 339 | valueEntryId, 340 | offsetof("value"), 341 | ); 342 | } else { 343 | entry.valueType = MapKeyValueType.Number; 344 | // @ts-ignore: type U is guaranteed to be a Map 345 | store>( 346 | changetype(entry), 347 | value, 348 | offsetof("value"), 349 | ); 350 | // @ts-ignore: type U is guaranteed to be a Map 351 | entry.valueSize = sizeof>(); 352 | } 353 | } 354 | return mapEntryId; 355 | } 356 | 357 | private putSet(value: U): u32 { 358 | // @ts-ignore: indexof defined, and do memory copies 359 | if (!isReference>()) { 360 | return this.putReferenceAndFields(value); 361 | } 362 | // @ts-ignore: U is garunteed to be a Set 363 | let capacity = 1 << (32 - clz(value.size)); 364 | const align = sizeof() - 1; 365 | const entrySize = (offsetof>() + align) & ~align; 366 | 367 | // store a set reference to the setReferenceTable 368 | let entryId = this.entryId++; 369 | let entry = this.setReferenceTable.allocate(); 370 | entry.entryId = entryId; 371 | entry.rtId = getObjectType(changetype(value)); 372 | entry.entrySize = entrySize; 373 | entry.capacity = capacity; 374 | 375 | // @ts-ignore: childentries is array> 376 | let childEntries = value.values(); 377 | let length: i32 = childEntries.length; 378 | let setEntryTable = this.setEntryTable; 379 | 380 | // loop over each child, and add a setEntry 381 | for (let i = 0; i < length; i++) { 382 | let child = unchecked(childEntries[i]); 383 | let childEntryId = this.put(child); 384 | let childEntry = setEntryTable.allocate(); 385 | childEntry.childEntryId = childEntryId; 386 | childEntry.parentEntryId = entryId; 387 | childEntry.isString = isString(child); 388 | } 389 | return entryId; 390 | } 391 | 392 | private putReferenceAndFields(value: U): u32 { 393 | let entryId = this.putReference(value); 394 | if (isNullable(value)) { 395 | // @ts-ignore: defined in each class 396 | (value! as InternalTransformInterface).__asonPut(this, entryId); 397 | } else { 398 | // @ts-ignore: defined in each class 399 | (value as InternalTransformInterface).__asonPut(this, entryId); 400 | } 401 | return entryId; 402 | } 403 | 404 | private putDataSegment(value: U): u32 { 405 | let entryId = this.entryId++; 406 | this.entries.set(changetype(value), entryId); 407 | let size = getObjectSize(value); 408 | 409 | let entry = this.dataSegmentTable.allocate(); 410 | 411 | entry.byteLength = size; 412 | entry.entryId = entryId; 413 | entry.rtId = getObjectType(changetype(value)); 414 | 415 | // write the data to the table after the header 416 | let segment = this.dataSegmentTable.allocateSegment(size); 417 | memory.copy(segment, changetype(value), size); 418 | 419 | // return the entry id 420 | return entryId; 421 | } 422 | 423 | private putArrayDataSegment(value: U): u32 { 424 | let entryId = this.entryId++; 425 | this.entries.set(changetype(value), entryId); 426 | 427 | let arrayLength = (value as InternalTransformInterface).__asonLength(); 428 | 429 | let entry = this.arrayDataSegmentTable.allocate(); 430 | const align = (value as InternalTransformInterface).__asonAlignofValueofParameter(); 431 | entry.length = arrayLength; 432 | entry.align = align; 433 | entry.entryId = entryId; 434 | entry.rtId = getObjectType(changetype(value)); 435 | 436 | let size = arrayLength << align; 437 | 438 | // copy the data 439 | let dataStart = load(changetype(value), offsetof("dataStart")); 440 | 441 | let segment = this.arrayDataSegmentTable.allocateSegment(size); 442 | memory.copy(segment, dataStart, size); 443 | // return the entry written 444 | return entryId; 445 | } 446 | 447 | private putLink(childEntryId: u32, parentEntryId: u32, offset: usize): void { 448 | let entry = this.linkTable.allocate(); 449 | entry.childEntryId = childEntryId; 450 | entry.parentEntryId = parentEntryId; 451 | entry.offset = offset; 452 | assert(this.segments.has(parentEntryId)); 453 | let parentSegment = this.segments.get(parentEntryId); 454 | store(parentSegment + offset, 0); 455 | } 456 | 457 | private putReference(value: U): u32 { 458 | let entryId = this.entryId++; 459 | this.entries.set(changetype(value), entryId); 460 | let entry = this.referenceTable.allocate(); 461 | entry.entryId = entryId; 462 | let offset = getObjectSize(value); 463 | entry.offset = offset; 464 | entry.rtId = getObjectType(changetype(value)); 465 | let segment = this.referenceTable.allocateSegment(offset); 466 | this.segments.set(entryId, segment); 467 | return entryId; 468 | } 469 | 470 | private putArray>>(value: U): u32 { 471 | let entryId = this.entryId++; 472 | this.entries.set(changetype(value), entryId); 473 | let entry = this.arrayTable.allocate(); 474 | entry.entryId = entryId; 475 | if (isNullable(value)) { 476 | entry.length = value!.length; 477 | } else { 478 | entry.length = value.length; 479 | } 480 | entry.rtId = getObjectType(changetype(value)); 481 | return entryId; 482 | } 483 | 484 | private putArrayLink(childEntryId: u32, parentEntryId: u32, index: i32): void { 485 | let entry = this.arrayLinkTable.allocate(); 486 | entry.childEntryId = childEntryId; 487 | entry.parentEntryId = parentEntryId; 488 | entry.index = index; 489 | } 490 | 491 | public putField(entryId: u32, value: U, offset: usize): void { 492 | if (isReference(value)) { 493 | if (changetype(value) != 0) { 494 | if (isNullable(value)) { 495 | this.putLink(this.put(value!), entryId, offset); 496 | } else { 497 | this.putLink(this.put(value), entryId, offset); 498 | } 499 | } 500 | return; 501 | } 502 | assert(this.segments.has(entryId)); 503 | let parentSegment = this.segments.get(entryId); 504 | store(parentSegment + offset, value); 505 | } 506 | 507 | private commit(): StaticArray { 508 | // reference every table 509 | let referenceTable = this.referenceTable; 510 | let dataSegmentTable = this.dataSegmentTable; 511 | let arrayTable = this.arrayTable; 512 | let arrayDataSegmentTable = this.arrayDataSegmentTable; 513 | let linkTable = this.linkTable; 514 | let arrayLinkTable = this.arrayLinkTable; 515 | let staticReferenceTable = this.staticReferenceTable; 516 | let customTable = this.customTable; 517 | let setReferenceTable = this.setReferenceTable; 518 | let setEntryTable = this.setEntryTable; 519 | let mapReferenceTable = this.mapReferenceTable; 520 | let mapKeyValueEntryTable = this.mapKeyValuePairsTable; 521 | 522 | // get every index 523 | let referenceTableIndex = referenceTable.index; 524 | let dataSegmentTableIndex = dataSegmentTable.index; 525 | let arrayTableIndex = arrayTable.index; 526 | let arrayDataSegmentTableIndex = arrayDataSegmentTable.index; 527 | let linkTableIndex = linkTable.index; 528 | let arrayLinkTableIndex = arrayLinkTable.index; 529 | let staticReferenceTableIndex = staticReferenceTable.index; 530 | let customTableIndex = customTable.index; 531 | let setReferenceTableIndex = setReferenceTable.index; 532 | let setEntryTableIndex = setEntryTable.index; 533 | let mapReferenceTableIndex = mapReferenceTable.index; 534 | let mapKeyValueEntryTableIndex = mapKeyValueEntryTable.index; 535 | 536 | // calculate the buffer length 537 | let length = referenceTableIndex 538 | + dataSegmentTableIndex 539 | + arrayTableIndex 540 | + arrayDataSegmentTableIndex 541 | + linkTableIndex 542 | + arrayLinkTableIndex 543 | + staticReferenceTableIndex 544 | + customTableIndex 545 | + setReferenceTableIndex 546 | + setEntryTableIndex 547 | + mapReferenceTableIndex 548 | + mapKeyValueEntryTableIndex 549 | + offsetof(); 550 | 551 | // create a buffer 552 | let result = new StaticArray(length); 553 | 554 | // write all the data to the buffer header 555 | let header = changetype(result); 556 | header.referenceTableByteLength = referenceTableIndex; 557 | header.dataSegmentTableByteLength = dataSegmentTableIndex; 558 | header.arrayTableByteLength = arrayTableIndex; 559 | header.arrayDataSegmentTableByteLength = arrayDataSegmentTableIndex; 560 | header.linkTableByteLength = linkTableIndex; 561 | header.arrayLinkTableByteLength = arrayLinkTableIndex; 562 | header.staticReferenceTableByteLength = staticReferenceTableIndex; 563 | header.customTableByteLength = customTableIndex; 564 | header.setReferenceTableByteLength = setReferenceTableIndex; 565 | header.setEntryTableByteLength = setEntryTableIndex; 566 | header.mapReferenceTableByteLength = mapReferenceTableIndex; 567 | header.mapKeyValueEntryTableByteLength = mapKeyValueEntryTableIndex; 568 | 569 | // copy each table to the buffer in succession 570 | let offset = offsetof(); 571 | referenceTable.copyTo(result, offset); 572 | offset += referenceTableIndex; 573 | dataSegmentTable.copyTo(result, offset); 574 | offset += dataSegmentTableIndex; 575 | arrayTable.copyTo(result, offset); 576 | offset += arrayTableIndex; 577 | arrayDataSegmentTable.copyTo(result, offset); 578 | offset += arrayDataSegmentTableIndex; 579 | linkTable.copyTo(result, offset); 580 | offset += linkTableIndex; 581 | arrayLinkTable.copyTo(result, offset); 582 | offset += arrayLinkTableIndex; 583 | staticReferenceTable.copyTo(result, offset); 584 | offset += staticReferenceTableIndex; 585 | customTable.copyTo(result, offset); 586 | offset += customTableIndex; 587 | setReferenceTable.copyTo(result, offset); 588 | offset += setReferenceTableIndex; 589 | setEntryTable.copyTo(result, offset); 590 | offset += setEntryTableIndex; 591 | mapReferenceTable.copyTo(result, offset); 592 | offset += mapReferenceTableIndex; 593 | mapKeyValueEntryTable.copyTo(result, offset); 594 | 595 | // return the result 596 | return result; 597 | } 598 | } 599 | 600 | /** 601 | * A class that deserializes a buffer and assembles a final reference. 602 | */ 603 | export class Deserializer { 604 | /** 605 | * deserialize 606 | * Deserializes an object T from a StaticArary generated by the ason Serializer. 607 | * @param data: Array of u8, representing the serialized object 608 | * @returns: The deserialized object T 609 | */ 610 | public deserialize(data: StaticArray): T { 611 | if (isReference()) { 612 | if (data.length == 0) { 613 | if (isNullable()) { 614 | // @ts-ignore: T is nullable 615 | return null; 616 | } else { 617 | throw new Error("Cannot return null with null buffer when type T is not nullable."); 618 | } 619 | } 620 | } 621 | let startPointer = changetype(data); 622 | 623 | // Assert data is larger than the ASONHeader object. 624 | let length = data.length; 625 | assert(length > offsetof(), "Inputted array is too small."); 626 | 627 | // Get the header. 628 | let header = changetype(data); 629 | 630 | // Cache values. 631 | let arrayDataSegmentTableByteLength = header.arrayDataSegmentTableByteLength; 632 | let arrayLinkTableByteLength = header.arrayLinkTableByteLength; 633 | let arrayTableByteLength = header.arrayTableByteLength; 634 | let customTableByteLength = header.customTableByteLength; 635 | let dataSegmentTableByteLength = header.dataSegmentTableByteLength; 636 | let linkTableByteLength = header.linkTableByteLength; 637 | let mapKeyValueEntryTableByteLength = header.mapKeyValueEntryTableByteLength; 638 | let mapReferenceTableByteLength = header.mapReferenceTableByteLength; 639 | let referenceTableByteLength = header.referenceTableByteLength; 640 | let setEntryTableByteLength = header.setEntryTableByteLength; 641 | let setReferenceTableByteLength = header.setReferenceTableByteLength; 642 | let staticReferenceTableByteLength = header.staticReferenceTableByteLength; 643 | 644 | // Assert the sizes from the header match the length of data. 645 | assert(length == offsetof() + 646 | referenceTableByteLength + 647 | dataSegmentTableByteLength + 648 | arrayTableByteLength + 649 | arrayDataSegmentTableByteLength + 650 | linkTableByteLength + 651 | arrayLinkTableByteLength + 652 | staticReferenceTableByteLength + 653 | customTableByteLength + 654 | setReferenceTableByteLength + 655 | setEntryTableByteLength + 656 | mapReferenceTableByteLength + 657 | mapKeyValueEntryTableByteLength, "Inputted array is malformed."); 658 | 659 | // Find the start of each table. 660 | let referenceTablePointer = startPointer + offsetof(); 661 | let dataSegmentTablePointer = referenceTablePointer + referenceTableByteLength; 662 | let arrayTablePointer = dataSegmentTablePointer + dataSegmentTableByteLength; 663 | let arrayDataSegmentTablePointer = arrayTablePointer + arrayTableByteLength; 664 | let linkTablePointer = arrayDataSegmentTablePointer + arrayDataSegmentTableByteLength; 665 | let arrayLinkTablePointer = linkTablePointer + linkTableByteLength; 666 | let staticReferenceTablePointer = arrayLinkTablePointer + arrayLinkTableByteLength; 667 | let customTablePointer = staticReferenceTablePointer + staticReferenceTableByteLength;; 668 | let setReferenceTablePointer = customTablePointer + customTableByteLength; 669 | let setEntryTablePointer = setReferenceTablePointer + setReferenceTableByteLength; 670 | let mapReferenceTablePointer = setEntryTablePointer + setEntryTableByteLength; 671 | let mapKeyValueEntryTablePointer = mapReferenceTablePointer + mapReferenceTableByteLength; 672 | 673 | // Generate tables. 674 | let referenceTable = Table.from(referenceTablePointer, referenceTableByteLength); 675 | let dataSegmentTable = Table.from(dataSegmentTablePointer, dataSegmentTableByteLength); 676 | let arrayTable = Table.from(arrayTablePointer, arrayTableByteLength); 677 | let arrayDataSegmentTable = Table.from(arrayDataSegmentTablePointer, arrayDataSegmentTableByteLength); 678 | let linkTable = Table.from(linkTablePointer, linkTableByteLength); 679 | let arrayLinkTable = Table.from(arrayLinkTablePointer, arrayLinkTableByteLength); 680 | let staticReferenceTable = Table.from(staticReferenceTablePointer, staticReferenceTableByteLength); 681 | let customTable = Table.from(customTablePointer, customTableByteLength); 682 | let setReferenceTable = Table.from(setReferenceTablePointer, setReferenceTableByteLength); 683 | let setEntryTable = Table.from(setEntryTablePointer, setEntryTableByteLength); 684 | let mapReferenceTable = Table.from(mapReferenceTablePointer, mapReferenceTableByteLength); 685 | let mapKeyValueEntryTable = Table.from(mapKeyValueEntryTablePointer, mapKeyValueEntryTableByteLength); 686 | 687 | // Make the object that will eventually become the object T. 688 | let entryMap = new Map(); 689 | entryMap.set(u32.MAX_VALUE, changetype(0)); 690 | 691 | // Set up references of the object in the entryMap. 692 | let i: usize = 0; 693 | while (i < referenceTableByteLength) { 694 | let entry = referenceTable.allocate(); 695 | let offset = entry.offset; 696 | let referencePointer = __new(offset, entry.rtId); 697 | entryMap.set(entry.entryId, changetype(referencePointer)); 698 | 699 | let segment = referenceTable.allocateSegment(offset); 700 | memory.copy(referencePointer, segment, offset); 701 | i = referenceTable.index; 702 | } 703 | 704 | // Set up the DataSegments of the object in the entryMap. 705 | i = 0; 706 | while (i < dataSegmentTableByteLength) { 707 | let entry = dataSegmentTable.allocate(); 708 | let segmentLength = entry.byteLength; 709 | let segment = dataSegmentTable.allocateSegment(segmentLength); 710 | let referencePointer = __new(entry.byteLength, entry.rtId); 711 | memory.copy(referencePointer, segment, segmentLength); 712 | entryMap.set(entry.entryId, changetype(referencePointer)); 713 | i = dataSegmentTable.index; 714 | } 715 | 716 | // Set up the Arrays of the object in the entryMap. 717 | i = 0; 718 | while (i < arrayTableByteLength) { 719 | let entry = arrayTable.allocate(); 720 | let length = entry.length; 721 | let temp = new ArrayBuffer(length << alignof()); 722 | let referencePointer = __newArray(length, alignof(), entry.rtId, changetype(temp)); 723 | entryMap.set(entry.entryId, changetype(referencePointer)); 724 | i += offsetof(); 725 | } 726 | 727 | // Set up the Array DataSegments of the object in the entryMap. 728 | i = 0; 729 | while (i < arrayDataSegmentTableByteLength) { 730 | let entry = arrayDataSegmentTable.allocate(); 731 | let length = entry.length; 732 | let segment = arrayDataSegmentTable.allocateSegment((length << (entry.align))); 733 | let referencePointer = __newArray(length, entry.align, entry.rtId, segment); 734 | entryMap.set(entry.entryId, changetype(referencePointer)); 735 | i = arrayDataSegmentTable.index; 736 | } 737 | 738 | i = 0; 739 | while (i < setReferenceTableByteLength) { 740 | let entry = setReferenceTable.allocate(); 741 | let entryId = entry.entryId; 742 | let rtId = entry.rtId; 743 | let entrySize = entry.entrySize; 744 | let capacity = entry.capacity; 745 | 746 | let set = changetype(__new(offsetof>(), rtId)); 747 | entryMap.set(entryId, set); 748 | // private buckets: ArrayBuffer = new ArrayBuffer(INITIAL_CAPACITY * BUCKET_SIZE); 749 | let buckets = new ArrayBuffer(capacity * sizeof()); 750 | store( 751 | changetype(set), 752 | changetype(buckets), 753 | offsetof>("buckets"), 754 | ); 755 | __link(changetype(set), changetype(buckets), false); 756 | 757 | // private bucketsMask: u32 = INITIAL_CAPACITY - 1; 758 | store( 759 | changetype(set), 760 | capacity - 1, 761 | offsetof>("bucketsMask"), 762 | ); 763 | // buckets referencing their respective first entry, usize[bucketsMask + 1] 764 | 765 | // entries in insertion order, SetEntry[entriesCapacity] 766 | // private entries: ArrayBuffer = new ArrayBuffer(INITIAL_CAPACITY * ENTRY_SIZE()); 767 | let entries = new ArrayBuffer(capacity * entrySize); 768 | store( 769 | changetype(set), 770 | changetype(entries), 771 | offsetof>("entries"), 772 | ); 773 | __link(changetype(set), changetype(buckets), false); 774 | // private entriesCapacity: i32 = INITIAL_CAPACITY; 775 | store( 776 | changetype(set), 777 | capacity, 778 | offsetof>("entriesCapacity"), 779 | ); 780 | changetype(changetype(set) - TOTAL_OVERHEAD).rtId = rtId; 781 | 782 | i += offsetof(); 783 | } 784 | 785 | i = 0; 786 | while (i < mapReferenceTableByteLength) { 787 | let entry = mapReferenceTable.allocate(); 788 | let entryId = entry.entryId; 789 | let rtId = entry.rtId; 790 | let entrySize = entry.entrySize; 791 | let capacity = entry.capacity; 792 | 793 | let mapPtr = changetype(__new(rtId, offsetof>())); 794 | // private buckets: ArrayBuffer = new ArrayBuffer(INITIAL_CAPACITY * BUCKET_SIZE); 795 | let buckets = new ArrayBuffer(capacity * sizeof()); 796 | store( 797 | changetype(mapPtr), 798 | changetype(buckets), 799 | offsetof>("buckets"), 800 | ); 801 | __link(changetype(mapPtr), changetype(buckets), false); 802 | 803 | // private bucketsMask: u32 = INITIAL_CAPACITY - 1; // 0b0111 804 | store( 805 | changetype(mapPtr), 806 | capacity - 1, 807 | offsetof>("bucketsMask"), 808 | ); 809 | // entries in insertion order, MapEntry[entriesCapacity] 810 | // private entries: ArrayBuffer = new ArrayBuffer(INITIAL_CAPACITY * ENTRY_SIZE()); 811 | let entries = new ArrayBuffer(capacity * entrySize); 812 | store( 813 | changetype(mapPtr), 814 | changetype(entries), 815 | offsetof>("entries"), 816 | ); 817 | __link(changetype(mapPtr), changetype(entries), false); 818 | // private entriesCapacity: i32 = INITIAL_CAPACITY; 819 | store( 820 | changetype(mapPtr), 821 | capacity, 822 | offsetof>("entriesCapacity"), 823 | ); 824 | 825 | changetype(changetype(mapPtr) - TOTAL_OVERHEAD).rtId = rtId; 826 | entryMap.set(entryId, mapPtr); 827 | i += offsetof(); 828 | } 829 | 830 | // static strings and immutable references 831 | i = 0; 832 | while (i < staticReferenceTableByteLength) { 833 | let entry = staticReferenceTable.allocate(); 834 | entryMap.set(entry.entryId, changetype(entry.ptr)); 835 | i += offsetof(); 836 | } 837 | 838 | // custom serialization section 839 | i = 0; 840 | while (i < customTableByteLength) { 841 | let entry = customTable.allocate(); 842 | let byteLength = entry.byteLength; 843 | let entryId = entry.entryId; 844 | let deserializeFuncIndex = entry.deserializeFuncIndex; 845 | let offset = entry.offset; 846 | let rtId = entry.rtId; 847 | 848 | let ptr = __new(offset, rtId); 849 | entryMap.set(entryId, changetype(ptr)); 850 | 851 | let buffer = new StaticArray(byteLength); 852 | let sourcePtr = customTable.allocateSegment(byteLength); 853 | memory.copy(changetype(buffer), sourcePtr, byteLength); 854 | // obj.__asonDeserialize(buffer) 855 | call_indirect(deserializeFuncIndex, ptr /* this */, buffer /* StaticArray */); 856 | 857 | i = customTable.index; 858 | } 859 | 860 | // all the references have been allocated, let's get entry 0 and validate type info 861 | let entry0 = changetype(entryMap.get(0)); 862 | if (isReference()) { 863 | // in the case of functions, idof() returns 0, and breaks everything 864 | if (!isFunction()) { 865 | 866 | let success: bool = false; 867 | 868 | if (isNullable()) { 869 | // @ts-ignore interface added at runtime 870 | success = changetype(entry0) instanceof T || changetype(entry0)!.__asonInstanceOf(getObjectType(entry0)); 871 | } else { 872 | // @ts-ignore interface added at runtime 873 | success = changetype(entry0) instanceof T || changetype(entry0).__asonInstanceOf(getObjectType(entry0)); 874 | } 875 | 876 | if (!success) { 877 | // @ts-ignore inside isDefined() 878 | if (isDefined(ASON_TRACE)) { 879 | assert(false, `Deserialize: expected ${nameof()} (${idof()}), received ${getObjectType(entry0)}`); 880 | } else { 881 | assert(false, `Deserialize: received invalid type`); 882 | } 883 | } 884 | } 885 | } else { 886 | // the type is a number, and we can validate the rtid of a Box 887 | assert(getObjectType(entry0) == idof>()); 888 | } 889 | 890 | // Link every part in the entryMap. 891 | i = 0; 892 | while (i < linkTableByteLength) { 893 | let entry = linkTable.allocate(); 894 | 895 | // get the parent, make sure it exists 896 | let parentEntryId = entry.parentEntryId; 897 | assert(entryMap.has(parentEntryId)); 898 | let parentPointer = changetype(entryMap.get(parentEntryId)); 899 | 900 | // get the child, make sure it exists 901 | let childEntryId = entry.childEntryId; 902 | assert(entryMap.has(childEntryId)); 903 | let childPointer = changetype(entryMap.get(childEntryId)); 904 | 905 | // form the link and attach it to the parent 906 | __link(parentPointer, childPointer, false); 907 | store(parentPointer + entry.offset, childPointer); 908 | 909 | // advance to the next link 910 | i += offsetof(); 911 | } 912 | 913 | // Link the Arrays in the entryMap. 914 | i = 0; 915 | while (i < arrayLinkTableByteLength) { 916 | let entry = arrayLinkTable.allocate(); 917 | 918 | let parentEntryId = entry.parentEntryId; 919 | assert(entryMap.has(parentEntryId)); 920 | let parentPointer = changetype(entryMap.get(parentEntryId)); 921 | 922 | let childEntryId = entry.childEntryId; 923 | assert(entryMap.has(childEntryId)); 924 | let childPointer = changetype(entryMap.get(childEntryId)); 925 | 926 | __link(parentPointer, childPointer, false); 927 | let parentDataPointer = load(parentPointer, offsetof>("dataStart")) 928 | store(parentDataPointer + (entry.index << alignof()), childPointer); 929 | i += offsetof(); 930 | } 931 | 932 | // Sets and Maps are initialized at this point 933 | i = 0; 934 | while (i < setEntryTableByteLength) { 935 | let entry = setEntryTable.allocate(); 936 | let parentEntryId = entry.parentEntryId; 937 | assert(entryMap.has(parentEntryId)); 938 | let parent = entryMap.get(parentEntryId); 939 | let childEntryId = entry.childEntryId; 940 | assert(entryMap.has(childEntryId)); 941 | let child = entryMap.get(childEntryId); 942 | if (entry.isString) { 943 | let parentStringSet = changetype>(parent); 944 | let childString = changetype(child); 945 | parentStringSet.add(childString); 946 | } else { 947 | changetype>(parent).add(child); 948 | } 949 | 950 | i += offsetof(); 951 | } 952 | 953 | i = 0; 954 | while (i < mapKeyValueEntryTableByteLength) { 955 | let entry = mapKeyValueEntryTable.allocate(); 956 | let parentEntryId = entry.parentEntryId; 957 | assert(entryMap.has(parentEntryId)); 958 | let parent = entryMap.get(parentEntryId); 959 | switch (entry.keyType) { 960 | case MapKeyValueType.Dummy: { 961 | let keyEntryId = load( 962 | changetype(entry), 963 | offsetof("key"), 964 | ); 965 | assert(entryMap.has(keyEntryId)); 966 | let key = entryMap.get(keyEntryId); 967 | if (entry.valueType == MapKeyValueType.Dummy) { 968 | let valueEntryId = load( 969 | changetype(entry), 970 | offsetof("value"), 971 | ); 972 | assert(entryMap.has(valueEntryId)); 973 | changetype>(parent).set( 974 | key, 975 | entryMap.get(valueEntryId), 976 | ); 977 | } else { 978 | switch (entry.valueSize) { 979 | case 1: { 980 | let value = load( 981 | changetype(entry), 982 | offsetof("value"), 983 | ); 984 | changetype>(parent).set(key, value); 985 | break; 986 | } 987 | case 2: { 988 | let value = load( 989 | changetype(entry), 990 | offsetof("value"), 991 | ); 992 | changetype>(parent).set(key, value); 993 | break; 994 | } 995 | case 4: { 996 | let value = load( 997 | changetype(entry), 998 | offsetof("value"), 999 | ); 1000 | changetype>(parent).set(key, value); 1001 | break; 1002 | } 1003 | case 8: { 1004 | let value = load( 1005 | changetype(entry), 1006 | offsetof("value"), 1007 | ); 1008 | changetype>(parent).set(key, value); 1009 | break; 1010 | } 1011 | default: assert(false); 1012 | } 1013 | } 1014 | break; 1015 | } 1016 | case MapKeyValueType.String: { 1017 | let keyEntryId = load( 1018 | changetype(entry), 1019 | offsetof("key"), 1020 | ); 1021 | assert(entryMap.has(keyEntryId)); 1022 | let key = changetype(entryMap.get(keyEntryId)); 1023 | if (entry.valueType == MapKeyValueType.Dummy) { 1024 | let valueEntryId = load( 1025 | changetype(entry), 1026 | offsetof("value"), 1027 | ); 1028 | assert(entryMap.has(valueEntryId)); 1029 | changetype>(parent).set( 1030 | key, 1031 | entryMap.get(valueEntryId), 1032 | ); 1033 | } else { 1034 | switch (entry.valueSize) { 1035 | case 1: { 1036 | let value = load( 1037 | changetype(entry), 1038 | offsetof("value"), 1039 | ); 1040 | changetype>(parent).set(key, value); 1041 | break; 1042 | } 1043 | case 2: { 1044 | let value = load( 1045 | changetype(entry), 1046 | offsetof("value"), 1047 | ); 1048 | changetype>(parent).set(key, value); 1049 | break; 1050 | } 1051 | case 4: { 1052 | let value = load( 1053 | changetype(entry), 1054 | offsetof("value"), 1055 | ); 1056 | changetype>(parent).set(key, value); 1057 | break; 1058 | } 1059 | case 8: { 1060 | let value = load( 1061 | changetype(entry), 1062 | offsetof("value"), 1063 | ); 1064 | changetype>(parent).set(key, value); 1065 | break; 1066 | } 1067 | default: assert(false); 1068 | } 1069 | } 1070 | break; 1071 | } 1072 | case MapKeyValueType.Number: { 1073 | let mapValueEntryId = load(changetype(entry), offsetof("value")); 1074 | assert(entryMap.has(mapValueEntryId)); 1075 | let mapValue = entryMap.get(mapValueEntryId); 1076 | switch (entry.keySize) { 1077 | case 1: { 1078 | if (entry.keyIsSigned) { 1079 | changetype>(parent).set( 1080 | load(changetype(entry), offsetof("key")), 1081 | mapValue, 1082 | ); 1083 | } else { 1084 | changetype>(parent).set( 1085 | load(changetype(entry), offsetof("key")), 1086 | mapValue, 1087 | ); 1088 | } 1089 | break; 1090 | } 1091 | case 2: { 1092 | if (entry.keyIsSigned) { 1093 | changetype>(parent).set( 1094 | load(changetype(entry), offsetof("key")), 1095 | mapValue, 1096 | ); 1097 | } else { 1098 | changetype>(parent).set( 1099 | load(changetype(entry), offsetof("key")), 1100 | mapValue, 1101 | ); 1102 | } 1103 | break; 1104 | } 1105 | case 4: { 1106 | if (entry.keyIsSigned) { 1107 | changetype>(parent).set( 1108 | load(changetype(entry), offsetof("key")), 1109 | mapValue, 1110 | ); 1111 | } else { 1112 | changetype>(parent).set( 1113 | load(changetype(entry), offsetof("key")), 1114 | mapValue, 1115 | ); 1116 | } 1117 | break; 1118 | } 1119 | case 8: { 1120 | if (entry.keyIsSigned) { 1121 | changetype>(parent).set( 1122 | load(changetype(entry), offsetof("key")), 1123 | mapValue, 1124 | ); 1125 | } else { 1126 | changetype>(parent).set( 1127 | load(changetype(entry), offsetof("key")), 1128 | mapValue, 1129 | ); 1130 | } 1131 | break; 1132 | } 1133 | default: assert(false); 1134 | } 1135 | break; 1136 | } 1137 | default: assert(false); 1138 | } 1139 | i += offsetof(); 1140 | } 1141 | 1142 | if (isReference()) { 1143 | // Return the original object, stored in the 0th element of the entryMap. 1144 | return changetype(entryMap.get(0)); 1145 | } else { 1146 | return changetype>(entryMap.get(0)).value; 1147 | } 1148 | } 1149 | } 1150 | 1151 | class Box { constructor(public value: T) {} } 1152 | 1153 | /** 1154 | * Serialize a given value. Numeric values will be `Box`ed for you. 1155 | * 1156 | * @param value - The value to be serialized. 1157 | * @returns {StaticArray} A serialized buffer. 1158 | */ 1159 | export function serialize(value: T): StaticArray { 1160 | let a = new Serializer(); 1161 | return a.serialize(value); 1162 | } 1163 | 1164 | /** 1165 | * Deserialize a given ASON buffer. 1166 | * 1167 | * @param buffer - The buffer to be deserialized. 1168 | * @returns {T} - An object of type `T` 1169 | */ 1170 | export function deserialize(buffer: StaticArray): T { 1171 | let a = new Deserializer(); 1172 | return a.deserialize(buffer); 1173 | } 1174 | 1175 | interface InternalTransformInterface { 1176 | __asonPut(ser: U, entryId: u32): void 1177 | __asonAlignofValueofParameter(): usize 1178 | __asonLength(): i32 1179 | } 1180 | } 1181 | -------------------------------------------------------------------------------- /packages/assembly/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ason/assembly", 3 | "version": "0.11.1", 4 | "description": "AssemblyScript glue code for assemblyscript encoding and decoding", 5 | "types": "index.ts", 6 | "keywords": [ 7 | "assemblyscript", 8 | "glue", 9 | "encoding", 10 | "decoding" 11 | ], 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/jtenner/ason.git" 15 | }, 16 | "bugs": { 17 | "url": "https://github.com/jtenner/ason/issues" 18 | }, 19 | "author": "Joshua Tenner ", 20 | "license": "MIT", 21 | "dependencies": { 22 | "@ason/transform": "^0.11.0" 23 | }, 24 | "devDependencies": { 25 | "assemblyscript": "^0.24.1" 26 | }, 27 | "scripts": { 28 | "asbuild:untouched": "asc assembly/index.ts --target debug", 29 | "asbuild:optimized": "asc assembly/index.ts --target release", 30 | "asbuild": "npm run asbuild:untouched && npm run asbuild:optimized", 31 | "test": "node tests" 32 | }, 33 | "gitHead": "adfca9274be98765f3bc0d5088ee0246f0313592" 34 | } 35 | -------------------------------------------------------------------------------- /packages/assembly/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "experimentalDecorators": true 4 | }, 5 | "extends": "assemblyscript/std/assembly.json", 6 | "include": [ 7 | "./**/*.ts" 8 | ] 9 | } -------------------------------------------------------------------------------- /packages/assembly/util.ts: -------------------------------------------------------------------------------- 1 | @unmanaged 2 | // The first object within the serialized array. 3 | export class ASONHeader { 4 | referenceTableByteLength: usize; 5 | dataSegmentTableByteLength: usize; 6 | arrayTableByteLength: usize; 7 | arrayDataSegmentTableByteLength: usize; 8 | staticReferenceTableByteLength: usize; 9 | customTableByteLength: usize; 10 | linkTableByteLength: usize; 11 | arrayLinkTableByteLength: usize; 12 | setReferenceTableByteLength: usize; 13 | setEntryTableByteLength: usize; 14 | mapReferenceTableByteLength: usize; 15 | mapKeyValueEntryTableByteLength: usize; 16 | } 17 | 18 | // The reference that defines where each object is within the StaticArary 19 | // The base object is assumed to be in the ReferenceEntry table. 20 | // It is defined at entryId = 0, with rtId = typeof 21 | @unmanaged 22 | export class ReferenceEntry { 23 | rtId: u32; // The type Id 24 | entryId: u32; // some kind of unique entry identifier 25 | offset: usize; // and how big it is 26 | } 27 | 28 | // Defines the links between two objects: Defines the entryId of the parent, and the entryId of the child. 29 | @unmanaged 30 | export class LinkEntry { 31 | parentEntryId: u32; 32 | offset: usize; 33 | childEntryId: u32; 34 | } 35 | 36 | // A reference to a Data Segment. 37 | @unmanaged 38 | export class DataSegmentEntry { 39 | rtId: u32; 40 | entryId: u32; 41 | byteLength: usize; 42 | } 43 | 44 | // A reference to an Array Entry. 45 | @unmanaged 46 | export class ArrayEntry { 47 | rtId: u32; 48 | entryId: u32; 49 | length: i32; 50 | } 51 | 52 | // A reference to an Array of Data Segments. 53 | @unmanaged 54 | export class ArrayDataSegmentEntry { 55 | rtId: u32; 56 | entryId: u32; 57 | align: usize; 58 | length: i32; 59 | } 60 | 61 | // A reference to the links within arrays (This needs to be handled separately) 62 | @unmanaged 63 | export class ArrayLinkEntry { 64 | parentEntryId: u32; 65 | index: i32; 66 | childEntryId: u32; 67 | } 68 | 69 | // Set helper: A reference to object values within Set objects. 70 | @unmanaged 71 | export class SetReferenceEntry { 72 | entryId: u32; 73 | rtId: u32; 74 | entrySize: usize; 75 | capacity: i32; 76 | } 77 | 78 | // Set helper: A reference to the keys for Set objects 79 | @unmanaged 80 | export class SetKeyEntry { 81 | parentEntryId: u32; 82 | childEntryId: u32; 83 | isString: bool; 84 | } 85 | 86 | // Map helper: A reference to object values within Map objects 87 | @unmanaged 88 | export class MapReferenceEntry { 89 | entryId: u32; 90 | rtId: u32; 91 | capacity: i32; 92 | entrySize: usize; 93 | } 94 | 95 | // Map helper: Possible types for a Map's keys and/or values. Dummy is a generic object reference. 96 | export const enum MapKeyValueType { 97 | Dummy, 98 | String, 99 | Number, 100 | } 101 | 102 | // Map helper: A reference to a Map's key-value pair. 103 | @unmanaged 104 | export class MapKeyValuePairEntry { 105 | parentEntryId: i32; 106 | keySize: i32; 107 | keyType: MapKeyValueType; 108 | keyIsSigned: bool; 109 | valueSize: i32; 110 | valueType: MapKeyValueType; 111 | key: u64; 112 | value: u64; 113 | } 114 | 115 | // A reference to a Static Reference, such as a String. 116 | @unmanaged 117 | export class StaticReferenceEntry { 118 | entryId: u32; 119 | ptr: usize; 120 | } 121 | 122 | // Used when the user defines their own deserialize function for an object. 123 | @unmanaged 124 | export class CustomEntry { 125 | entryId: u32; 126 | rtId: u32; 127 | offset: usize; 128 | byteLength: i32; 129 | deserializeFuncIndex: i32; 130 | } 131 | 132 | // Generic Table reference, T will be one of the above Entry classes. 133 | export class Table { 134 | data: StaticArray; 135 | index: i32 = 0; 136 | 137 | constructor(defaultSize: usize) { 138 | if (isManaged()) ERROR("Internal class error. T must not be managed."); 139 | this.data = new StaticArray(defaultSize); 140 | } 141 | 142 | ensureSize(newLength: i32): void { 143 | let data = this.data; 144 | let length = data.length; 145 | if (length < newLength) { 146 | let newData = new StaticArray(newLength << 1); 147 | memory.copy(changetype(newData), changetype(data), length); 148 | this.data = newData; 149 | } 150 | } 151 | 152 | // Simple bump allocation 153 | allocate(): T { 154 | let index = this.index; 155 | let nextIndex = index + offsetof(); 156 | this.ensureSize(nextIndex); 157 | let result = changetype(changetype(this.data) + index); 158 | this.index = nextIndex; 159 | return result; 160 | } 161 | 162 | // More complex bump allocation 163 | allocateSegment(length: i32): usize { 164 | let index = this.index; 165 | let nextIndex = index + length; 166 | this.ensureSize(nextIndex); 167 | let result = changetype(this.data) + index; 168 | this.index = nextIndex; 169 | return result; 170 | } 171 | 172 | reset(): void { 173 | this.index = 0; 174 | memory.fill(changetype(this.data), 0, this.data.length); 175 | } 176 | 177 | // Creates a table object of byte length `length`, from a memory address `data`, 178 | public static from(data: usize, length: usize): Table { 179 | let result = __new(offsetof>(), idof>()); 180 | // @ts-ignore: pin prevents garbage collection 181 | __pin(result); 182 | 183 | let dataArray = new StaticArray(length); 184 | 185 | memory.copy(changetype(dataArray), data, length); 186 | let resultRef = changetype>(result); 187 | resultRef.data = dataArray; 188 | // @ts-ignore 189 | __unpin(result); 190 | return resultRef; 191 | } 192 | 193 | // Copy this Table's `data` array to an inputted StaticArray `array`, at position `offset`. 194 | copyTo(array: StaticArray, offset: usize): void { 195 | memory.copy(changetype(array) + offset, changetype(this.data), this.index); 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /packages/test/as-pect.asconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "targets": { 3 | "coverage": { 4 | "lib": ["./node_modules/@as-covers/assembly/index.ts"], 5 | "transform": ["@as-covers/transform", "@as-pect/transform"] 6 | }, 7 | "noCoverage": { 8 | "transform": ["@as-pect/transform"] 9 | } 10 | }, 11 | "options": { 12 | "exportMemory": true, 13 | "outFile": "output.wasm", 14 | "textFile": "output.wat", 15 | "bindings": "raw", 16 | "exportStart": "_start", 17 | "exportRuntime": true, 18 | "use": ["RTRACE=1", "ASON_TRACE=1"], 19 | "debug": true, 20 | "exportTable": true 21 | }, 22 | "extends": "./asconfig.json", 23 | "entries": ["../../node_modules/@as-pect/assembly/assembly/index.ts"] 24 | } -------------------------------------------------------------------------------- /packages/test/as-pect.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | /** 3 | * A set of globs passed to the glob package that qualify typescript files for testing. 4 | */ 5 | entries: ["assembly/__tests__/**/*.spec.ts"], 6 | /** 7 | * A set of globs passed to the glob package that quality files to be added to each test. 8 | */ 9 | include: ["assembly/__tests__/**/*.include.ts"], 10 | /** 11 | * A set of regexp that will disclude source files from testing. 12 | */ 13 | disclude: [/node_modules/], 14 | /** 15 | * Add your required AssemblyScript imports here. 16 | */ 17 | async instantiate(memory, createImports, instantiate, binary) { 18 | let instance; // Imports can reference this 19 | const myImports = { 20 | env: { memory }, 21 | // put your web assembly imports here, and return the module 22 | }; 23 | return instantiate(binary, createImports(myImports)); 24 | }, 25 | /** Enable code coverage. */ 26 | // coverage: ["assembly/**/*.ts"], 27 | /** 28 | * Specify if the binary wasm file should be written to the file system. 29 | */ 30 | outputBinary: false, 31 | }; 32 | -------------------------------------------------------------------------------- /packages/test/asconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "options": { 3 | "binaryFile": "build/index.wasm", 4 | "textFile": "build/index.wat", 5 | "sourceMap": true, 6 | "debug": true, 7 | "use": ["ASC_RTRACE=1"], 8 | "importMemory": true, 9 | "transform": ["../transform/lib/index.js"] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/test/assembly/__tests__/as-pect.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /packages/test/assembly/__tests__/index.spec.ts: -------------------------------------------------------------------------------- 1 | import { OBJECT, TOTAL_OVERHEAD } from "rt/common"; 2 | import { ASON } from "../../../assembly/index"; 3 | import { ASONHeader } from "../../../assembly/util"; 4 | class Vec3 { 5 | constructor( 6 | public x: f32 = 0, 7 | public y: f32 = 0, 8 | public z: f32 = 0, 9 | ) {} 10 | } 11 | 12 | class A { 13 | a: f32 = 1; 14 | b: B = new B(); 15 | c: i64 = -1; 16 | x: u8 = 32; 17 | } 18 | 19 | class B { 20 | a: A | null; 21 | c: i32 = 42; 22 | } 23 | 24 | class Base {} 25 | class Child extends Base { 26 | a: i32 = 0; 27 | } 28 | 29 | describe("ASON test suite", () => { 30 | test("basic vectors", testBasicVectors); 31 | test("complex objects", testComplexObjects); 32 | test("complex circular objects", testComplexCircularObject); 33 | test("data arrays", testDataArrays); 34 | test("reference arrays", testReferenceArrays); 35 | test("serialize null", checkSerializeNull); 36 | test("static array of references", staticArrayOfReferences); 37 | test("static array data", staticArrayData); 38 | test("complex array circular", arrayOfSameReferenceWithCircular); 39 | test("serialize numeric values", serializeNumericValues); 40 | test("set of strings", setOfStrings); 41 | test("set of integers", setOfIntegers); 42 | test("custom", testCustom); 43 | test("customVector", testCustomVectorSerialization); 44 | test("really long static strings", testStaticStrings); 45 | test("typed array", testTypedArray); 46 | test("extension", objectExtension); 47 | test("functions", testCallbacks); 48 | 49 | describe("map", () => { 50 | test("int to int maps", () => { testMap([1, 2, 3], [3, 6, 9]); }); 51 | test("string to int maps", () => { testMap(["one", "two", "three"], [3, 6, 9]); }); 52 | test("different sized int to int maps", () => { testMap([-1384328, 2, -3], [3, 6, 9]); }); 53 | test("float to int maps", () => { testMap([-1.01, 4.0, 341.44], [4, 5, 7]); }); 54 | test("different sized float to float maps", () => { testMap([1.44, -0.00000425, 3334445], [9.8, 756, 0.00000000000000004478]); }); 55 | test("object to int maps", () => { testMap([new Vec3(1, 2, 3), new Vec3(4, 5, 6), new Vec3(7, 8, 9)], [3, 6, 9]); }); 56 | test("float to string maps, with emoji", () => { testMap([2.1, 3.1415926, 2.71828], ["TwoAndABit", "Pi", "🇪"]); }); 57 | test("negative float to string maps", () => { testMap([-11.4, -1.0, 8.000001],["Negative", "Floats", "Work"]); }); 58 | test("int to empty string maps", () => { testMap([1,2,3],["","",""]); }); 59 | test("int to complex object maps", () => { 60 | let a1 = new A(); 61 | a1.a = 0.989; 62 | let a2 = new A(); 63 | testMap([-1, 1], [a1, a2]); 64 | }); 65 | test("int to complex object maps, with multiple and circular references", () => { 66 | let a1 = new A(); 67 | a1.a = 0.989; 68 | let a2 = new A(); 69 | a2.b.a = a1; 70 | testMap([-1, 1, 2], [a1, a2, a1]); 71 | }); 72 | test("int to nullable object maps", () => { 73 | let a1 = new A(); 74 | testMap([4, -1], [null, a1]); 75 | }); 76 | test("infinite floats to float maps", () => { testMap([Infinity],[44.44]); }); 77 | test("virtual deserialization", testVirtual); 78 | }); 79 | test("Major objects that should engage all parts of ASON", testHugeObject); 80 | 81 | test("nullable custom", testNullableCustom); 82 | test("object (de)serialization", testPlainObject); 83 | }); 84 | 85 | function testBasicVectors(): void { 86 | let a = new Vec3(1, 2, 3); 87 | let ser = new ASON.Serializer(); 88 | let buffer = ser.serialize(a); 89 | let des = new ASON.Deserializer(); 90 | let b = des.deserialize(buffer); 91 | 92 | assert(memory.compare(changetype(a), changetype(b), offsetof()) == 0, "Raw values are equal."); 93 | __collect(); 94 | } 95 | 96 | function testComplexObjects(): void { 97 | let myA = new A(); 98 | 99 | myA.c = 3525201; 100 | 101 | let buffer = ASON.serialize(myA); 102 | 103 | let myA2 = ASON.deserialize(buffer); 104 | 105 | assert(myA != myA2, "New object has been created."); 106 | assert(myA.a == myA2.a, "Float value in object is correct."); 107 | assert(myA.x == myA2.x, "Unsigned int8 value in object is correct."); 108 | assert(myA.c == myA2.c, "Signed int64 value in object is correct, and not default.") 109 | assert(myA2.b.c == 42, "Nested int32 value in object is correct."); 110 | __collect(); 111 | } 112 | 113 | function testComplexCircularObject(): void { 114 | let myA = new A(); 115 | 116 | myA.b.a = myA; 117 | 118 | let buffer = ASON.serialize(myA); 119 | 120 | let myA2 = ASON.deserialize(buffer); 121 | 122 | assert(myA != myA2, "New object has been created."); 123 | assert(myA2.b.a == myA2, "Nested object circular reference is preserved.") 124 | assert(myA2.b.a != myA, "Nested object is not the same as the original object"); 125 | __collect(); 126 | } 127 | 128 | function testDataArrays(): void { 129 | let array: Array = [8, 6, 7, 5, 3, 0, 9]; 130 | 131 | let buffer = ASON.serialize(array); 132 | 133 | let array2 = ASON.deserialize>(buffer); 134 | 135 | assert(array != array2, "New int array has been created."); 136 | assert(array.length == array2.length, "New int array is same size as original array"); 137 | let i: i32 = 0; 138 | for (i = 0; i < array.length; i++) { 139 | assert(array[i] == array2[i], "Int value at " + i.toString() + " matches"); 140 | } 141 | 142 | let array3: Array = [8, 6, 7, 5, 3, 0, 9]; 143 | 144 | buffer = ASON.serialize(array3); 145 | 146 | let array4 = ASON.deserialize>(buffer); 147 | 148 | assert(array3 != array4, "New float array has been created."); 149 | assert(array3.length == array4.length, "New float array is same size as original array"); 150 | for (i = 0; i < array.length; i++) { 151 | assert(array3[i] == array4[i], "Float value at " + i.toString() + " matches"); 152 | } 153 | __collect(); 154 | } 155 | 156 | function testReferenceArrays(): void { 157 | let array = new Array(); 158 | array.push(new A()); 159 | array.push(new A()); 160 | array.push(new A()); 161 | array.push(new A()); 162 | 163 | array[0].a = 2.1; 164 | array[3].b.a = array[0]; 165 | 166 | let buffer = ASON.serialize(array); 167 | 168 | let array2 = ASON.deserialize>(buffer); 169 | 170 | assert(array != array2, "New object array has been created."); 171 | assert(array.length == array2.length, "New object array is same size as original."); 172 | assert(array[0] != array2[0], "New objects are not the same as the originals."); 173 | assert(array[0].a == array2[0].a, "Object values have been preserved."); 174 | assert(array2[0].a != array2[1].a, "Object value changes are preserved."); 175 | assert(array2[3].b.a == array2[0], "Circular reference objects are same."); 176 | __collect(); 177 | } 178 | 179 | function checkSerializeNull(): void { 180 | let a: Vec3 | null = null; 181 | let buff = ASON.serialize(a); 182 | assert(buff.length == 0, "Buffer's length should be 0."); 183 | let b = ASON.deserialize(buff); 184 | assert(b == null, "Empty buffer returns null"); 185 | __collect(); 186 | } 187 | 188 | class Box { 189 | constructor(public value: T) {} 190 | } 191 | 192 | function staticArrayOfReferences(): void { 193 | let refs = new StaticArray>(10); 194 | for (let i = 0; i < 10; i++) { 195 | refs[i] = new Box(i); 196 | } 197 | let buff = ASON.serialize(refs); 198 | let result = ASON.deserialize>>(buff); 199 | for (let i = 0; i < 10; i++) { 200 | assert(result[i].value == i); 201 | } 202 | __collect(); 203 | } 204 | 205 | function staticArrayData(): void { 206 | let a = [9, 12, 6, 2, 4, -5] as StaticArray; 207 | let buff = ASON.serialize(a); 208 | let b = ASON.deserialize>(buff); 209 | assert(memory.compare(changetype(a), changetype(b), (a.length << alignof())) == 0); 210 | __collect(); 211 | } 212 | 213 | class ArrayChild { 214 | circular: Array | null; 215 | } 216 | 217 | function arrayOfSameReferenceWithCircular(): void { 218 | let child = new ArrayChild(); 219 | let a = [child, child, child, child, child, child]; 220 | let buff = ASON.serialize(a); 221 | let b = ASON.deserialize>(buff); 222 | 223 | assert(a.length == b.length); 224 | let first = b[0]; 225 | for (let i = 0; i < a.length; i++) { 226 | assert(b[i] == first); 227 | } 228 | __collect(); 229 | } 230 | 231 | function serializeNumericValues(): void { 232 | assert(ASON.deserialize(ASON.serialize(3.14)) == 3.14); 233 | assert(ASON.deserialize(ASON.serialize(64)) == 64); 234 | assert(ASON.deserialize(ASON.serialize(255)) == 255); 235 | __collect(); 236 | } 237 | 238 | function setOfStrings(): void { 239 | let a = new Set(); 240 | a.add("one"); 241 | a.add("two"); 242 | a.add("three"); 243 | 244 | let value = ASON.deserialize>(ASON.serialize(a)); 245 | assert(value); 246 | assert(value.size == 3); 247 | assert(value.has("one")); 248 | assert(value.has("two")); 249 | assert(value.has("three")); 250 | __collect(); 251 | } 252 | 253 | function setOfIntegers(): void { 254 | let a = new Set(); 255 | a.add(1); 256 | a.add(2); 257 | a.add(3); 258 | a.add(42); 259 | 260 | let value = ASON.deserialize>(ASON.serialize(a)); 261 | assert(value); 262 | assert(value.has(1)); 263 | assert(value.has(2)); 264 | assert(value.has(3)); 265 | assert(value.has(42)); 266 | __collect(); 267 | } 268 | 269 | function testMap(keys: StaticArray, values: StaticArray): void { 270 | assert(keys.length == values.length); 271 | let len = keys.length; 272 | let value = new Map(); 273 | for (let i = 0; i < len; i++) { 274 | value.set(keys[i], values[i]); 275 | } 276 | 277 | let result = ASON.deserialize>(ASON.serialize(value)); 278 | 279 | assert(result); 280 | expect(result).toStrictEqual(value); 281 | __collect(); 282 | } 283 | 284 | let serializeCalled: i32 = 0; 285 | let deserializeCalled: i32 = 0; 286 | class CustomSerialize { 287 | __asonSerialize(): StaticArray { 288 | serializeCalled++; 289 | let result = new StaticArray(3); 290 | result[0] = 1; 291 | result[1] = 2; 292 | result[2] = 3; 293 | return result; 294 | } 295 | __asonDeserialize(buffer: StaticArray): void { 296 | deserializeCalled++; 297 | assert(buffer); 298 | assert(buffer.length == 3); 299 | assert(buffer[0] == 1); 300 | assert(buffer[1] == 2); 301 | assert(buffer[2] == 3); 302 | assert(this); 303 | assert(changetype(changetype(this) - TOTAL_OVERHEAD).rtId == idof()); 304 | } 305 | } 306 | 307 | function testCustom(): void { 308 | let result = ASON.deserialize(ASON.serialize(new CustomSerialize())); 309 | assert(result); 310 | assert(serializeCalled == 1); 311 | assert(deserializeCalled == 1); 312 | } 313 | 314 | class CustomVector { 315 | constructor() {} 316 | 317 | x: f32 = 1; 318 | y: f32 = 2; 319 | z: f32 = 3; 320 | __asonSerialize(): StaticArray { 321 | let result = new StaticArray(offsetof()); 322 | memory.copy(changetype(result), changetype(this), offsetof()); 323 | return result; 324 | } 325 | __asonDeserialize(buffer: StaticArray): void { 326 | assert(buffer.length == offsetof()); 327 | memory.copy(changetype(this), changetype(buffer), offsetof()); 328 | } 329 | } 330 | 331 | function testCustomVectorSerialization(): void { 332 | let a = new CustomVector(); 333 | let b = ASON.deserialize(ASON.serialize(a)); 334 | assert(memory.compare(changetype(a), changetype(b), offsetof()) == 0); 335 | } 336 | 337 | function testStaticStrings(): void { 338 | let a = "Some really long static string"; 339 | let serialized = ASON.serialize(a); 340 | let b = ASON.deserialize(serialized); 341 | assert(a == b); 342 | trace("bytelength of serialized static string", 1, (serialized.length - offsetof())); 343 | } 344 | 345 | function testTypedArray(): void { 346 | let a = new Float64Array(15); 347 | for (let i = 0; i < 15; i++) { 348 | a[i] = i; 349 | } 350 | let buffer = ASON.serialize(a); 351 | let b = ASON.deserialize(buffer); 352 | assert(b); 353 | assert(b.length == 15); 354 | for (let i = 0; i < 15; i++) { 355 | assert(b[i] == i); 356 | } 357 | } 358 | 359 | class übermenschObject { 360 | a: A = new A(); 361 | b: B = new B(); 362 | c: Set = new Set(); 363 | d: Map = new Map(); 364 | e: i16[] = []; 365 | f: A[] = []; 366 | g: Array = new Array(); 367 | //h: funcref | null; 368 | } 369 | 370 | function testHugeObject(): void { 371 | let bigobj: übermenschObject = new übermenschObject(); 372 | bigobj.a.a = 0.444444; 373 | bigobj.b.a = bigobj.a; 374 | bigobj.c.add(3.1415); 375 | bigobj.c.add(2.7183); 376 | bigobj.d.set(new Vec3(1,1,1), bigobj.a); 377 | bigobj.d.set(new Vec3(1,2,3), new A()); 378 | let d3: Vec3 = new Vec3(2,3,4); 379 | bigobj.d.set(d3, new A()); 380 | bigobj.e = [-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19]; 381 | bigobj.f.push(bigobj.a); 382 | bigobj.f.push(bigobj.d.get(d3)); 383 | bigobj.g = [ 384 | "I know a song that gets on everybody's nerves", 385 | "Everybody's nerves", 386 | "Everybody's nerves.", 387 | "I know a song that gets on everybody's nerves,", 388 | "And this is how it goes:" 389 | ]; 390 | 391 | let buff = ASON.serialize(bigobj); 392 | let b = ASON.deserialize<übermenschObject>(buff); 393 | 394 | assert(bigobj != b, "New object created"); 395 | expect(bigobj).toStrictEqual(b); 396 | 397 | __collect(); 398 | } 399 | 400 | class ExtendedVector extends Vec3 { 401 | constructor() { 402 | super(); 403 | } 404 | a: f32 = 1; 405 | b: f32 = 2; 406 | c: f32 = 4; 407 | } 408 | 409 | function objectExtension(): void { 410 | let a = new ExtendedVector(); 411 | 412 | let buffer = ASON.serialize(a); 413 | let b = ASON.deserialize(buffer); 414 | expect(a).toStrictEqual(b); 415 | } 416 | 417 | function testCallbacks(): void { 418 | let a = (): void => {}; 419 | let buffer = ASON.serialize(a); 420 | let b = ASON.deserialize<() => void>(buffer); 421 | b(); 422 | } 423 | 424 | function testVirtual(): void { 425 | let child = new Child(); 426 | child.a = 42; 427 | let buffer = ASON.serialize(child); 428 | let result = ASON.deserialize(buffer); 429 | assert(result instanceof Child); 430 | let cast = result; 431 | assert(cast.a == 42); 432 | } 433 | 434 | class NullableCustom { 435 | b: CustomSerialize | null = new CustomSerialize(); 436 | } 437 | 438 | function testNullableCustom(): void { 439 | let actual = new NullableCustom(); 440 | let expected = ASON.deserialize(ASON.serialize(actual)); 441 | expect(actual).toStrictEqual(expected); 442 | expect(actual).not.toBe(expected); 443 | assert(expected instanceof NullableCustom); 444 | } 445 | 446 | function testPlainObject(): void { 447 | const actual = new Vec3(12, 34, 56); 448 | const processed = ASON.deserialize(ASON.serialize(actual)); 449 | assert(processed instanceof Vec3); 450 | 451 | const casted = processed as Vec3; 452 | expect(actual).toStrictEqual(casted); 453 | expect(actual).not.toBe(casted); 454 | } 455 | -------------------------------------------------------------------------------- /packages/test/assembly/as_types.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /packages/test/assembly/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "assemblyscript/std/assembly.json", 3 | "include": ["./**/*.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/test/index.js: -------------------------------------------------------------------------------- 1 | const loader = require("assemblyscript/lib/loader"); 2 | const { Rtrace } = require("assemblyscript/lib/rtrace"); 3 | let memory = new WebAssembly.Memory({ 4 | initial: 100, 5 | }); 6 | let rtrace = new Rtrace({ 7 | getMemory() { 8 | return memory; 9 | }, 10 | }); 11 | const fs = require("fs"); 12 | 13 | const mod = loader.instantiateSync( 14 | fs.readFileSync("./build/index.wasm"), 15 | rtrace.install({ 16 | env: { memory }, 17 | }) 18 | ); 19 | mod.exports._start(); 20 | -------------------------------------------------------------------------------- /packages/test/output.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jtenner/ason/71f583a5882581a45907e58c52c4989258e1705d/packages/test/output.txt -------------------------------------------------------------------------------- /packages/test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ason/test", 3 | "version": "0.11.1", 4 | "private": true, 5 | "description": "AssemblyScript glue code for assemblyscript encoding and decoding", 6 | "type": "module", 7 | "keywords": [ 8 | "assemblyscript", 9 | "glue", 10 | "encoding", 11 | "decoding" 12 | ], 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/jtenner/ason.git" 16 | }, 17 | "bugs": { 18 | "url": "https://github.com/jtenner/ason/issues" 19 | }, 20 | "author": "Joshua Tenner ", 21 | "license": "MIT", 22 | "dependencies": { 23 | "@ason/assembly": "^0.11.1", 24 | "@ason/transform": "^0.11.0" 25 | }, 26 | "devDependencies": { 27 | "@as-pect/cli": "^8.0.0", 28 | "@assemblyscript/loader": "^0.24.1", 29 | "assemblyscript": "^0.24.1" 30 | }, 31 | "scripts": { 32 | "test": "asp --verbose" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/transform/README.md: -------------------------------------------------------------------------------- 1 | # @ason/assembly 2 | 3 | This package is the typescript library for the source file transform that enables reference serialization for the `@ason/assembly` package. To see the full documentation, please visit the [ason](https://github.com/jtenner/ason) github repo. 4 | 5 | # License 6 | 7 | MIT 8 | 9 | ``` 10 | MIT License 11 | 12 | Copyright (c) 2021 Joshua Tenner 13 | 14 | Permission is hereby granted, free of charge, to any person obtaining a copy 15 | of this software and associated documentation files (the "Software"), to deal 16 | in the Software without restriction, including without limitation the rights 17 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | copies of the Software, and to permit persons to whom the Software is 19 | furnished to do so, subject to the following conditions: 20 | 21 | The above copyright notice and this permission notice shall be included in all 22 | copies or substantial portions of the Software. 23 | 24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 | SOFTWARE. 31 | ``` 32 | -------------------------------------------------------------------------------- /packages/transform/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ason/transform", 3 | "version": "0.11.0", 4 | "description": "The transform for enabling AssemblyScript object encoding.", 5 | "type": "module", 6 | "main": "lib/index.js", 7 | "author": "Joshua Tenner ", 8 | "license": "MIT", 9 | "files": [ 10 | "lib" 11 | ], 12 | "scripts": { 13 | "build": "tsc" 14 | }, 15 | "devDependencies": { 16 | "assemblyscript": "^0.24.1", 17 | "typescript": "^4.9.3" 18 | }, 19 | "dependencies": { 20 | "visitor-as": "^0.11.3" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/transform/src/createAsonAlignofValueofMethod.ts: -------------------------------------------------------------------------------- 1 | import {CommonFlags, ClassDeclaration, Node} from "assemblyscript/dist/assemblyscript.js"; 2 | 3 | export function createAsonAlignofValueofMethod({range, isGeneric, members, indexSignature}: ClassDeclaration): void { 4 | members.push( 5 | Node.createMethodDeclaration( 6 | Node.createIdentifierExpression("__asonAlignofValueofParameter", range), 7 | null, 8 | ( 9 | CommonFlags.Public | 10 | CommonFlags.Instance | 11 | (isGeneric ? CommonFlags.GenericContext : 0) 12 | ), 13 | null, 14 | Node.createFunctionType( 15 | [], 16 | Node.createNamedType( 17 | Node.createSimpleTypeName("usize", range), 18 | null, 19 | false, 20 | range 21 | ), 22 | null, 23 | false, 24 | range 25 | ), 26 | Node.createBlockStatement( 27 | [ 28 | Node.createReturnStatement( 29 | indexSignature 30 | ? Node.createCallExpression( 31 | Node.createIdentifierExpression("alignof", range), 32 | [ 33 | Node.createNamedType( 34 | Node.createSimpleTypeName("valueof", range), 35 | [ 36 | Node.createNamedType( 37 | Node.createSimpleTypeName("this", range), 38 | null, 39 | false, 40 | range 41 | ) 42 | ], 43 | false, 44 | range 45 | ) 46 | ], 47 | [], 48 | range 49 | ) 50 | : Node.createIntegerLiteralExpression( 51 | {low: 0, high: 0} as unknown as i64, 52 | range 53 | ), 54 | range 55 | ) 56 | ], 57 | range 58 | ), 59 | range 60 | ) 61 | ); 62 | } -------------------------------------------------------------------------------- /packages/transform/src/createAsonInstanceOfMethod.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ClassDeclaration, 3 | CommonFlags, 4 | Statement, 5 | TypeNode, 6 | ParameterKind, 7 | Token, 8 | } from "assemblyscript/dist/assemblyscript.js"; 9 | 10 | 11 | export function createAsonInstanceOfMethod(classDeclaration: ClassDeclaration): void { 12 | let statements = [ 13 | // if (id == idof()) return true; 14 | TypeNode.createIfStatement( 15 | TypeNode.createBinaryExpression( 16 | Token.Equals_Equals, 17 | TypeNode.createIdentifierExpression("id", classDeclaration.name.range), 18 | TypeNode.createCallExpression( 19 | TypeNode.createIdentifierExpression("idof", classDeclaration.name.range), 20 | [ 21 | TypeNode.createNamedType( 22 | TypeNode.createSimpleTypeName("this", classDeclaration.name.range), 23 | null, 24 | false, 25 | classDeclaration.name.range, 26 | ), 27 | ], 28 | [], 29 | classDeclaration.name.range 30 | ), 31 | classDeclaration.name.range, 32 | ), 33 | TypeNode.createReturnStatement(TypeNode.createTrueExpression(classDeclaration.name.range), classDeclaration.name.range), 34 | null, 35 | classDeclaration.name.range, 36 | ), 37 | ]; 38 | 39 | // if (isDefined(super.__asonInstanceOf)) return super.__asonInstanceOf(id); 40 | statements.push( 41 | TypeNode.createIfStatement( 42 | TypeNode.createCallExpression( 43 | TypeNode.createIdentifierExpression("isDefined", classDeclaration.name.range), 44 | null, 45 | [ 46 | TypeNode.createPropertyAccessExpression( 47 | TypeNode.createSuperExpression(classDeclaration.name.range), 48 | TypeNode.createIdentifierExpression("__asonInstanceOf", classDeclaration.name.range), 49 | classDeclaration.name.range, 50 | ) 51 | ], 52 | classDeclaration.name.range, 53 | ), 54 | TypeNode.createReturnStatement( 55 | TypeNode.createCallExpression( 56 | TypeNode.createPropertyAccessExpression( 57 | TypeNode.createSuperExpression(classDeclaration.name.range), 58 | TypeNode.createIdentifierExpression("__asonInstanceOf", classDeclaration.name.range), 59 | classDeclaration.name.range, 60 | ), 61 | null, 62 | [ 63 | TypeNode.createIdentifierExpression("id", classDeclaration.name.range), 64 | ], 65 | classDeclaration.name.range, 66 | ), 67 | classDeclaration.name.range, 68 | ), 69 | null, 70 | classDeclaration.name.range, 71 | ), 72 | ); 73 | 74 | // return false; 75 | statements.push( 76 | TypeNode.createReturnStatement( 77 | TypeNode.createFalseExpression(classDeclaration.name.range), 78 | classDeclaration.name.range, 79 | ), 80 | ); 81 | 82 | let method = TypeNode.createMethodDeclaration( 83 | TypeNode.createIdentifierExpression("__asonInstanceOf", classDeclaration.name.range), 84 | null, 85 | CommonFlags.Public | 86 | CommonFlags.Instance | 87 | (classDeclaration.isGeneric ? CommonFlags.GenericContext : 0), 88 | null, 89 | TypeNode.createFunctionType( 90 | [ 91 | // ser: Serializer, 92 | TypeNode.createParameter( 93 | ParameterKind.Default, 94 | TypeNode.createIdentifierExpression("id", classDeclaration.name.range), 95 | TypeNode.createNamedType( 96 | TypeNode.createSimpleTypeName("usize", classDeclaration.name.range), 97 | null, 98 | false, 99 | classDeclaration.name.range 100 | ), 101 | null, 102 | classDeclaration.name.range 103 | ), 104 | ], 105 | TypeNode.createNamedType( 106 | TypeNode.createSimpleTypeName("bool", classDeclaration.name.range), 107 | null, 108 | false, 109 | classDeclaration.name.range 110 | ), 111 | null, 112 | false, 113 | classDeclaration.name.range 114 | ), 115 | TypeNode.createBlockStatement(statements, classDeclaration.name.range), 116 | classDeclaration.name.range 117 | ); 118 | 119 | classDeclaration.members.push(method); 120 | } 121 | -------------------------------------------------------------------------------- /packages/transform/src/createAsonLengthMethod.ts: -------------------------------------------------------------------------------- 1 | import {CommonFlags, ClassDeclaration, Statement, Node} from "assemblyscript/dist/assemblyscript.js"; 2 | 3 | export function createAsonLengthMethod({name, range, isGeneric, members}: ClassDeclaration): void { 4 | const statements: Statement[] = [ 5 | Node.createReturnStatement( 6 | Node.createIntegerLiteralExpression( 7 | {low: 0, high: 0} as unknown as i64, 8 | range 9 | ), 10 | range 11 | ) 12 | ]; 13 | 14 | if (name.text !== "Function") { 15 | statements.unshift( 16 | Node.createIfStatement( 17 | Node.createCallExpression( 18 | Node.createIdentifierExpression("isDefined", range), 19 | null, 20 | [ 21 | Node.createPropertyAccessExpression( 22 | Node.createThisExpression(range), 23 | Node.createIdentifierExpression("length", range), 24 | range 25 | ) 26 | ], 27 | range 28 | ), 29 | Node.createReturnStatement( 30 | Node.createPropertyAccessExpression( 31 | Node.createThisExpression(range), 32 | Node.createIdentifierExpression("length", range), 33 | range 34 | ), 35 | range 36 | ), 37 | null, 38 | range 39 | ) 40 | ); 41 | } 42 | 43 | members.push( 44 | Node.createMethodDeclaration( 45 | Node.createIdentifierExpression("__asonLength", range), 46 | null, 47 | ( 48 | CommonFlags.Public | 49 | CommonFlags.Instance | 50 | (isGeneric ? CommonFlags.GenericContext : 0) 51 | ), 52 | null, 53 | Node.createFunctionType( 54 | [], 55 | Node.createNamedType( 56 | Node.createSimpleTypeName("i32", range), 57 | null, 58 | false, 59 | range 60 | ), 61 | null, 62 | false, 63 | range 64 | ), 65 | Node.createBlockStatement(statements, range), 66 | range 67 | ) 68 | ); 69 | } -------------------------------------------------------------------------------- /packages/transform/src/createAsonPutMethod.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ClassDeclaration, 3 | CommonFlags, 4 | NodeKind, 5 | Statement, 6 | FieldDeclaration, 7 | TypeNode, 8 | ParameterKind, 9 | } from "assemblyscript/dist/assemblyscript.js"; 10 | 11 | export function createAsonPutMethod(classDeclaration: ClassDeclaration): void { 12 | const statements = [] as Statement[]; 13 | 14 | for (const member of classDeclaration.members) { 15 | if ( 16 | member.is(CommonFlags.Instance) && 17 | member.kind === NodeKind.FieldDeclaration 18 | ) { 19 | statements.push( 20 | createFieldPutStatement(classDeclaration, member as FieldDeclaration) 21 | ); 22 | } 23 | } 24 | 25 | // if (isDefined(super.__asonPut)) super.__asonPut(ser, entryId) 26 | statements.push(createSuperAsonPutCall(classDeclaration)); 27 | 28 | let method = TypeNode.createMethodDeclaration( 29 | TypeNode.createIdentifierExpression("__asonPut", classDeclaration.name.range), 30 | null, 31 | CommonFlags.Public | 32 | CommonFlags.Instance | 33 | CommonFlags.Generic | 34 | (classDeclaration.isGeneric ? CommonFlags.GenericContext : 0), 35 | [ 36 | TypeNode.createTypeParameter( 37 | TypeNode.createIdentifierExpression("U", classDeclaration.name.range), 38 | null, 39 | null, 40 | classDeclaration.name.range 41 | ), 42 | ], 43 | TypeNode.createFunctionType( 44 | [ 45 | // ser: Serializer, 46 | TypeNode.createParameter( 47 | ParameterKind.Default, 48 | TypeNode.createIdentifierExpression("ser", classDeclaration.name.range), 49 | TypeNode.createNamedType( 50 | TypeNode.createSimpleTypeName("U", classDeclaration.name.range), 51 | null, 52 | false, 53 | classDeclaration.name.range 54 | ), 55 | null, 56 | classDeclaration.name.range 57 | ), 58 | // entryId: u32, 59 | TypeNode.createParameter( 60 | ParameterKind.Default, 61 | TypeNode.createIdentifierExpression( 62 | "entryId", 63 | classDeclaration.name.range 64 | ), 65 | TypeNode.createNamedType( 66 | TypeNode.createSimpleTypeName("u32", classDeclaration.name.range), 67 | null, 68 | false, 69 | classDeclaration.name.range 70 | ), 71 | null, 72 | classDeclaration.name.range 73 | ), 74 | ], 75 | TypeNode.createNamedType( 76 | TypeNode.createSimpleTypeName("void", classDeclaration.name.range), 77 | null, 78 | false, 79 | classDeclaration.name.range 80 | ), 81 | null, 82 | false, 83 | classDeclaration.name.range 84 | ), 85 | TypeNode.createBlockStatement(statements, classDeclaration.name.range), 86 | classDeclaration.name.range 87 | ); 88 | classDeclaration.members.push(method); 89 | } 90 | 91 | function createFieldPutStatement( 92 | classDeclaration: ClassDeclaration, 93 | fieldDeclaration: FieldDeclaration 94 | ): Statement { 95 | // ser.putField(entryId, this.field, offsetof("field")); 96 | return TypeNode.createExpressionStatement( 97 | TypeNode.createCallExpression( 98 | TypeNode.createPropertyAccessExpression( 99 | TypeNode.createIdentifierExpression("ser", fieldDeclaration.range), 100 | TypeNode.createIdentifierExpression( 101 | "putField", 102 | fieldDeclaration.range 103 | ), 104 | fieldDeclaration.range 105 | ), 106 | null, 107 | [ 108 | TypeNode.createIdentifierExpression( 109 | "entryId", 110 | fieldDeclaration.range 111 | ), 112 | TypeNode.createPropertyAccessExpression( 113 | TypeNode.createThisExpression(fieldDeclaration.range), 114 | TypeNode.createIdentifierExpression( 115 | fieldDeclaration.name.text, 116 | fieldDeclaration.range 117 | ), 118 | fieldDeclaration.range 119 | ), 120 | // offsetof("field") 121 | TypeNode.createCallExpression( 122 | TypeNode.createIdentifierExpression( 123 | "offsetof", 124 | fieldDeclaration.range 125 | ), 126 | [ 127 | TypeNode.createNamedType( 128 | TypeNode.createSimpleTypeName( 129 | "this", 130 | fieldDeclaration.range 131 | ), 132 | null, 133 | false, 134 | fieldDeclaration.range 135 | ), 136 | ], 137 | [ 138 | TypeNode.createStringLiteralExpression( 139 | fieldDeclaration.name.text, 140 | fieldDeclaration.range 141 | ), 142 | ], 143 | fieldDeclaration.range 144 | ), 145 | ], 146 | fieldDeclaration.range 147 | ) 148 | ); 149 | } 150 | 151 | function createSuperAsonPutCall( 152 | classDeclaration: ClassDeclaration 153 | ): Statement { 154 | // if (isDefined(super.__asonPut)) 155 | return TypeNode.createIfStatement( 156 | TypeNode.createCallExpression( 157 | TypeNode.createIdentifierExpression("isDefined", classDeclaration.name.range), 158 | null, 159 | [ 160 | TypeNode.createPropertyAccessExpression( 161 | TypeNode.createSuperExpression(classDeclaration.name.range), 162 | TypeNode.createIdentifierExpression( 163 | "__asonPut", 164 | classDeclaration.name.range 165 | ), 166 | classDeclaration.name.range 167 | ), 168 | ], 169 | classDeclaration.name.range 170 | ), 171 | TypeNode.createExpressionStatement( 172 | TypeNode.createCallExpression( 173 | TypeNode.createPropertyAccessExpression( 174 | TypeNode.createSuperExpression(classDeclaration.name.range), 175 | TypeNode.createIdentifierExpression( 176 | "__asonPut", 177 | classDeclaration.name.range 178 | ), 179 | classDeclaration.name.range 180 | ), 181 | null, 182 | [ 183 | TypeNode.createIdentifierExpression("ser", classDeclaration.name.range), 184 | TypeNode.createIdentifierExpression("entryId", classDeclaration.name.range), 185 | ], 186 | classDeclaration.name.range 187 | ) 188 | ), 189 | null, 190 | classDeclaration.name.range 191 | ); 192 | } 193 | -------------------------------------------------------------------------------- /packages/transform/src/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | FunctionPrototype, 3 | ClassPrototype, 4 | ClassDeclaration, 5 | InterfaceDeclaration, 6 | NamespaceDeclaration, 7 | Node, 8 | NodeKind, 9 | Parser, 10 | Statement, 11 | Source, 12 | Program, 13 | ElementKind, 14 | } from "assemblyscript/dist/assemblyscript.js"; 15 | import { 16 | Transform, 17 | } from "assemblyscript/dist/transform.js"; 18 | import { createAsonInstanceOfMethod } from "./createAsonInstanceOfMethod.js"; 19 | 20 | 21 | import { createAsonPutMethod } from "./createAsonPutMethod.js"; 22 | import { createAsonAlignofValueofMethod } from "./createAsonAlignofValueofMethod.js"; 23 | import { createAsonLengthMethod } from "./createAsonLengthMethod.js"; 24 | 25 | const INTERNAL_TRANSFORM_NAME = "InternalTransformInterface" 26 | 27 | export default class ASONTransform extends Transform { 28 | /** 29 | * This method results in a pure AST transform that inserts a strictEquals member 30 | * into each ClassDeclaration. 31 | * 32 | * @param {Parser} parser - The AssemblyScript parser. 33 | */ 34 | afterParse(parser: Parser): void { 35 | // For backwards compatibility 36 | let sources: Source[] = (parser as any).program 37 | ? (parser as any).program.sources 38 | : parser.sources; 39 | // for each program source 40 | for (const source of sources) { 41 | traverseStatements(source.statements); 42 | } 43 | } 44 | 45 | afterInitialize(program: Program): void { 46 | const classes = [...program.elementsByName.values()] 47 | .filter(element => { 48 | return element.kind === ElementKind.ClassPrototype || element.kind === ElementKind.InterfacePrototype 49 | }) as ClassPrototype[]; 50 | 51 | const [internalInterface] = classes.splice( 52 | classes.findIndex(clazz => clazz.internalName.endsWith("ASON." + INTERNAL_TRANSFORM_NAME)), 53 | 1 54 | ); 55 | 56 | const methodNames = ["__asonPut", "__asonAlignofValueofParameter", "__asonLength"]; 57 | const baseMethods = new Map(); 58 | for (const name of methodNames) { 59 | baseMethods.set(name, internalInterface.instanceMembers!.get(name)! as FunctionPrototype); 60 | } 61 | 62 | const {range} = internalInterface.declaration.name; 63 | classes.forEach(clazz => { 64 | clazz.interfacePrototypes ??= []; 65 | clazz.interfacePrototypes.push(internalInterface); 66 | 67 | const declaration = clazz.declaration as ClassDeclaration; 68 | declaration.implementsTypes ??= []; 69 | declaration.implementsTypes.push( 70 | Node.createNamedType( 71 | Node.createSimpleTypeName(INTERNAL_TRANSFORM_NAME, range), 72 | null, 73 | false, 74 | range 75 | ) 76 | ); 77 | 78 | if (clazz.kind === ElementKind.InterfacePrototype) return; 79 | 80 | for (const [name, method] of baseMethods) { 81 | method.unboundOverrides ??= new Set(); 82 | method.unboundOverrides.add( 83 | clazz.instanceMembers!.get(name)! as FunctionPrototype 84 | ); 85 | } 86 | }); 87 | 88 | const resolvedInternalInterface = program.resolver.resolveClass(internalInterface, null)!; 89 | const resolvedClasses = [ 90 | program.objectInstance, 91 | program.stringInstance, 92 | program.arrayBufferInstance, 93 | program.arrayBufferViewInstance 94 | ]; 95 | resolvedClasses.forEach(clazz => clazz.addInterface(resolvedInternalInterface)); 96 | } 97 | }; 98 | 99 | function traverseStatements(statements: Statement[]): void { 100 | // for each statement in the source 101 | for (const statement of statements) { 102 | // find each class declaration 103 | if (statement.kind === NodeKind.ClassDeclaration) { 104 | // cast and create a strictEquals function 105 | const classDeclaration = statement; 106 | createAsonPutMethod(classDeclaration); 107 | createAsonInstanceOfMethod(classDeclaration); 108 | createAsonAlignofValueofMethod(classDeclaration); 109 | createAsonLengthMethod(classDeclaration); 110 | } else if (statement.kind === NodeKind.InterfaceDeclaration) { 111 | const interfaceDeclaration = statement; 112 | 113 | // Don't declare methods on the internal interface 114 | if (interfaceDeclaration.name.text === INTERNAL_TRANSFORM_NAME) continue; 115 | 116 | createAsonAlignofValueofMethod(interfaceDeclaration); 117 | createAsonLengthMethod(interfaceDeclaration); 118 | } else if (statement.kind === NodeKind.NamespaceDeclaration) { 119 | const namespaceDeclaration = statement; 120 | traverseStatements(namespaceDeclaration.members); 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /packages/transform/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 4 | 5 | /* Basic Options */ 6 | // "incremental": true, /* Enable incremental compilation */ 7 | "target": "ESNext" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, 8 | "module": "ESNext" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, 9 | // "lib": [], /* Specify library files to be included in the compilation. */ 10 | // "allowJs": true, /* Allow javascript files to be compiled. */ 11 | // "checkJs": true, /* Report errors in .js files. */ 12 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */ 13 | "declaration": true /* Generates corresponding '.d.ts' file. */, 14 | "declarationMap": true /* Generates a sourcemap for each corresponding '.d.ts' file. */, 15 | "sourceMap": true /* Generates corresponding '.map' file. */, 16 | // "outFile": "./", /* Concatenate and emit output to single file. */ 17 | "outDir": "./lib/" /* Redirect output structure to the directory. */, 18 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 19 | // "composite": true, /* Enable project compilation */ 20 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 21 | // "removeComments": true, /* Do not emit comments to output. */ 22 | // "noEmit": true, /* Do not emit outputs. */ 23 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 24 | "downlevelIteration": true /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */, 25 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 26 | 27 | /* Strict Type-Checking Options */ 28 | "strict": true /* Enable all strict type-checking options. */, 29 | "noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */, 30 | "strictNullChecks": true /* Enable strict null checks. */, 31 | "strictFunctionTypes": true /* Enable strict checking of function types. */, 32 | "strictBindCallApply": true /* Enable strict 'bind', 'call', and 'apply' methods on functions. */, 33 | "strictPropertyInitialization": true /* Enable strict checking of property initialization in classes. */, 34 | "noImplicitThis": true /* Raise error on 'this' expressions with an implied 'any' type. */, 35 | "alwaysStrict": true /* Parse in strict mode and emit "use strict" for each source file. */, 36 | 37 | /* Additional Checks */ 38 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 39 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 40 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 41 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 42 | // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ 43 | // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ 44 | 45 | /* Module Resolution Options */ 46 | "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 47 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 48 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 49 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 50 | // "typeRoots": [], /* List of folders to include type definitions from. */ 51 | // "types": [], /* Type declaration files to be included in compilation. */ 52 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 53 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, 54 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 55 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 56 | 57 | /* Source Map Options */ 58 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 59 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 60 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 61 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 62 | 63 | /* Experimental Options */ 64 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 65 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 66 | 67 | /* Advanced Options */ 68 | "skipLibCheck": true /* Skip type checking of declaration files. */, 69 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ 70 | } 71 | } 72 | --------------------------------------------------------------------------------