Safe Scripting Syntax: Difference between revisions
Adamcolton (talk | contribs) |
Adamcolton (talk | contribs) No edit summary |
||
| Line 15: | Line 15: | ||
Memory is use bounded. So is computation. | Memory is use bounded. So is computation. | ||
== VM Definition == | |||
A program has 3 top level memory spaces: read-only, read-write and executable. There are 3 registry spaces: floating, int and reference. Values can be moved between floating and int, but no reference math is allowed. Problem; values can be written to memory and recalled, so you could write a reference to memory then recall it as an int. Maybe I could have each memory block actually zero out those locations and hold references someplace else so that this will always return a 0 as an int. Same in the other direction. | |||
There is also a call stack. Values in the call stack are mapped to their register. | |||
== Simplicity == | == Simplicity == | ||
| Line 20: | Line 25: | ||
== Assembly == | == Assembly == | ||
The assembly starts by expressing the expected arguments and requesting memory. It then requests which language extensions and modules it needs. | The assembly starts by expressing the expected arguments and requesting memory. It then requests which language extensions and modules it needs. The assembler needs a VM definition; a list of the commands and modules. | ||
I like the idea of memory pages, but it may make more sense to tag pages as read-only, read-write or execute. | I like the idea of memory pages, but it may make more sense to tag pages as read-only, read-write or execute. | ||
| Line 55: | Line 60: | ||
I'll write the compiler as a package and include a command line so it can be used anywhere. | I'll write the compiler as a package and include a command line so it can be used anywhere. | ||
== GC == | |||
The GC will actually be a language extension, but a core one. It has to be configured at runtime. It only runs against read-write memory blocks. | |||
The header of each block contains a mark-and-sweep flag and the number of references. So it just does a mark and sweep. | |||
== Memoization == | == Memoization == | ||
A detail, but interesting. The base language should be fully deterministic. Language extensions can set a non-deterministic bit. This way, we can run a deterministic script and record the starting state and the output and memoize the whole process. | A detail, but interesting. The base language should be fully deterministic. Language extensions can set a non-deterministic bit. This way, we can run a deterministic script and record the starting state and the output and memoize the whole process. | ||
== UTF-8 Style Numeric encoding == | |||
* 127: 1 byte 0xxxxxxx | |||
* 16,384: 2 bytes 10xxxxxx xxxxxxxx | |||
* 2,097,152 : 3 bytes 110xxxxx xxxxxxxx xxxxxxxx | |||
and so on. So the information density is 7/8 bits. | |||
Revision as of 17:07, 20 July 2018
I've already done a first pass of this with the dist.ribut.us VM but the idea fascinates me so I may try and do a more advanced version.
Being able to take arbitrary commands and execute them safely and bounded is interesting. The language should have no means to escape it's bounds. It should have base constructs that are guaranteed, language extension modules that can be provided by the environment and library modules.
I think the correct approach is a byte code. The lowest level is primitive but can be run fast.
A level up is a scripting language.
- Data structures
- associative array
- array
VM Safety
This is kind of the whole point, so it's important. Most critical is that the script is fully sandboxed - no access to disk or network and memory access is controlled.
Memory is use bounded. So is computation.
VM Definition
A program has 3 top level memory spaces: read-only, read-write and executable. There are 3 registry spaces: floating, int and reference. Values can be moved between floating and int, but no reference math is allowed. Problem; values can be written to memory and recalled, so you could write a reference to memory then recall it as an int. Maybe I could have each memory block actually zero out those locations and hold references someplace else so that this will always return a 0 as an int. Same in the other direction.
There is also a call stack. Values in the call stack are mapped to their register.
Simplicity
I would eventually like this implemented in many languages. For that reason, it should be easy to implement.
Assembly
The assembly starts by expressing the expected arguments and requesting memory. It then requests which language extensions and modules it needs. The assembler needs a VM definition; a list of the commands and modules.
I like the idea of memory pages, but it may make more sense to tag pages as read-only, read-write or execute.
At the assembly level commands are named and scoped
IaddVal $0, 100 foo.bar $0, 400, 3.1415
When compiling, these are reduced to bytes. Module commands are replaced with Calls. Language extensions add commands. The total number of commands is fixed, so that determines how many bytes a call requires.
Module Loading
There should be standard collections of packages. A server can quickly download an index. When a script requests that a module be included the server can go get it. The risk this avoids is being able to define arbitrary package names and having the runtime make a network request searching for that which becomes identifiable.
Scripting
- Data Structs
- array
- associative array
- base types
- signed int
- unsigned int
- byte
- 64bit float
- string
Script is strongly typed. Probably OO very similar to Go.
Operators
- if
- for : continue, break
- func : return
- exit
The exit operator causes the script to terminate. I think I'm going to leave it up the context to determine if the script was successful or not.
I'll write the compiler as a package and include a command line so it can be used anywhere.
GC
The GC will actually be a language extension, but a core one. It has to be configured at runtime. It only runs against read-write memory blocks.
The header of each block contains a mark-and-sweep flag and the number of references. So it just does a mark and sweep.
Memoization
A detail, but interesting. The base language should be fully deterministic. Language extensions can set a non-deterministic bit. This way, we can run a deterministic script and record the starting state and the output and memoize the whole process.
UTF-8 Style Numeric encoding
- 127: 1 byte 0xxxxxxx
- 16,384: 2 bytes 10xxxxxx xxxxxxxx
- 2,097,152 : 3 bytes 110xxxxx xxxxxxxx xxxxxxxx
and so on. So the information density is 7/8 bits.