├── .gitignore ├── html ├── Makefile ├── aot.png ├── subtypes.png ├── github.css ├── highlight.pack.js ├── whatwg.css └── subtypes.graffle ├── historic ├── def.pdf ├── subtypes.png ├── mathpartir.sty ├── subtypes.graffle └── def.tex ├── lib ├── asm.js ├── env.js ├── report.js ├── fail.js ├── asmAssert.js ├── tables.js ├── types.js └── validate.js ├── package.json ├── README.md ├── test └── index.js └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /html/Makefile: -------------------------------------------------------------------------------- 1 | index.html: index.src.html 2 | anolis index.src.html index.html 3 | -------------------------------------------------------------------------------- /html/aot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/no-problemo/asm.js/master/html/aot.png -------------------------------------------------------------------------------- /historic/def.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/no-problemo/asm.js/master/historic/def.pdf -------------------------------------------------------------------------------- /html/subtypes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/no-problemo/asm.js/master/html/subtypes.png -------------------------------------------------------------------------------- /historic/subtypes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/no-problemo/asm.js/master/historic/subtypes.png -------------------------------------------------------------------------------- /historic/mathpartir.sty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/no-problemo/asm.js/master/historic/mathpartir.sty -------------------------------------------------------------------------------- /lib/asm.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | validate: require('./validate'), 3 | ValidationError: require('./fail').ValidationError, 4 | types: require('./types') 5 | }; -------------------------------------------------------------------------------- /lib/env.js: -------------------------------------------------------------------------------- 1 | var dict = require('dict'); 2 | 3 | function Env(v) { 4 | if (!(this instanceof Env)) 5 | return new Env(v); 6 | this._v = v; 7 | this._dict = dict(); 8 | } 9 | 10 | Env.prototype.lookup = function lookup(x) { 11 | return this._dict.get(x) || null; 12 | }; 13 | 14 | Env.prototype.bind = function bind(x, t, loc) { 15 | if (x === 'arguments' || x === 'eval') 16 | this._v.fail("illegal binding: '" + x + "'", loc); 17 | if (this._dict.has(x)) 18 | this._v.fail("duplicate binding for " + x, loc); 19 | this._dict.set(x, t); 20 | }; 21 | 22 | module.exports = Env; 23 | -------------------------------------------------------------------------------- /lib/report.js: -------------------------------------------------------------------------------- 1 | var ty = require('./types'); 2 | 3 | function Report(globals, exports) { 4 | this._globals = globals; 5 | this._exports = exports; 6 | } 7 | 8 | Report.prototype.getFunction = function(f) { 9 | var global = this._globals.lookup(f); 10 | if (!global || !(global.type instanceof ty.Arrow)) 11 | return null; 12 | return global.type; 13 | }; 14 | 15 | Report.prototype.isSingleExport = function() { 16 | return this._exports.type === 'single'; 17 | }; 18 | 19 | // ( this.isSingleExport => () -> string) 20 | // (!this.isSingleExport => (string) -> string) 21 | Report.prototype.getExport = function(f) { 22 | return this._exports.type === 'single' 23 | ? this._exports.export.name 24 | : this._exports.exports.lookup(f).name; 25 | }; 26 | 27 | module.exports = Report; 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "asm.js", 3 | "description": "Tools for the asm.js subset of JavaScript", 4 | "main": "lib/asm.js", 5 | "version": "0.0.3", 6 | "engines": { 7 | "node": ">=0.8.2" 8 | }, 9 | "author": { 10 | "name": "Dave Herman" 11 | }, 12 | "license": "Apache-2.0", 13 | "dependencies": { 14 | "esprima": "1.0.4", 15 | "dict": "1.4.0", 16 | "array-extended": "0.0.4", 17 | "pattern-match": "0.3.0" 18 | }, 19 | "devDependencies": { 20 | "nodeunit": "0.9.0" 21 | }, 22 | "directories": { 23 | "lib": "./lib" 24 | }, 25 | "repository": { 26 | "type": "git", 27 | "url": "git://github.com/dherman/asm.js.git" 28 | }, 29 | "keywords": [ 30 | "javascript", 31 | "asm.js", 32 | "validator" 33 | ], 34 | "scripts": { 35 | "test": "nodeunit" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/fail.js: -------------------------------------------------------------------------------- 1 | var match = require('pattern-match'); 2 | 3 | function ValidationError(message, stack) { 4 | Error.call(this, message); 5 | this.stack = stack; 6 | this.message = message; 7 | } 8 | 9 | ValidationError.prototype = Object.create(Error.prototype); 10 | 11 | function spaces(n) { 12 | var result = ""; 13 | for (var i = 0; i < n; i++) 14 | result += " "; 15 | return result; 16 | } 17 | 18 | ValidationError.prototype.context = function() { 19 | if (!this.src || !this.loc) 20 | return null; 21 | 22 | var lines = this.src.split(/\r|\n|\r\n/); 23 | var start = this.loc.start; 24 | var line = lines[start.line - 1]; 25 | return line + "\n" + spaces(start.column) + "^"; 26 | }; 27 | 28 | ValidationError.prototype.toString = function() { 29 | return this.context() + "\n\n" + Error.prototype.toString.call(this); 30 | }; 31 | 32 | function fail(message, src, loc) { 33 | message = message + locToString(loc); 34 | // FIXME: V8-specific; make this stack-trace logic more robust 35 | var stack = (new Error).stack 36 | .replace(/[^n]*\n/, "ValidationError: " + message + "\n") 37 | .replace(/\n[^\n]*\n/, "\n"); 38 | var e = new ValidationError(message, stack); 39 | e.src = src; 40 | e.loc = loc; 41 | throw e; 42 | } 43 | 44 | // (Loc) -> str 45 | function locToString(loc) { 46 | return match(loc, function(when) { 47 | when({ 48 | start: { line: match.var('sl'), column: match.var('sc') }, 49 | end: { line: match.var('el'), column: match.var('ec') } 50 | }, function(vars) { 51 | return " at " + vars.sl + ":" + vars.sc + "-" + vars.el + ":" + vars.ec; 52 | }); 53 | 54 | when(match.any, function() { 55 | return ""; 56 | }); 57 | }); 58 | } 59 | 60 | fail.locToString = locToString; 61 | 62 | fail.ValidationError = ValidationError; 63 | 64 | module.exports = fail; 65 | -------------------------------------------------------------------------------- /html/github.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | github.com style (c) Vasily Polovnyov 4 | 5 | */ 6 | 7 | .hljs { 8 | display: block; 9 | overflow-x: auto; 10 | padding: 0.5em; 11 | color: #333; 12 | background: #f8f8f8; 13 | } 14 | 15 | .hljs-comment, 16 | .hljs-template_comment, 17 | .diff .hljs-header, 18 | .hljs-javadoc { 19 | color: #998; 20 | font-style: italic; 21 | } 22 | 23 | .hljs-keyword, 24 | .css .rule .hljs-keyword, 25 | .hljs-winutils, 26 | .javascript .hljs-title, 27 | .nginx .hljs-title, 28 | .hljs-subst, 29 | .hljs-request, 30 | .hljs-status { 31 | color: #333; 32 | font-weight: bold; 33 | } 34 | 35 | .hljs-number, 36 | .hljs-hexcolor, 37 | .ruby .hljs-constant { 38 | color: #099; 39 | } 40 | 41 | .hljs-string, 42 | .hljs-tag .hljs-value, 43 | .hljs-phpdoc, 44 | .tex .hljs-formula { 45 | color: #d14; 46 | } 47 | 48 | .hljs-title, 49 | .hljs-id, 50 | .coffeescript .hljs-params, 51 | .scss .hljs-preprocessor { 52 | color: #900; 53 | font-weight: bold; 54 | } 55 | 56 | .javascript .hljs-title, 57 | .lisp .hljs-title, 58 | .clojure .hljs-title, 59 | .hljs-subst { 60 | font-weight: normal; 61 | } 62 | 63 | .hljs-class .hljs-title, 64 | .haskell .hljs-type, 65 | .vhdl .hljs-literal, 66 | .tex .hljs-command { 67 | color: #458; 68 | font-weight: bold; 69 | } 70 | 71 | .hljs-tag, 72 | .hljs-tag .hljs-title, 73 | .hljs-rules .hljs-property, 74 | .django .hljs-tag .hljs-keyword { 75 | color: #000080; 76 | font-weight: normal; 77 | } 78 | 79 | .hljs-attribute, 80 | .hljs-variable, 81 | .lisp .hljs-body { 82 | color: #008080; 83 | } 84 | 85 | .hljs-regexp { 86 | color: #009926; 87 | } 88 | 89 | .hljs-symbol, 90 | .ruby .hljs-symbol .hljs-string, 91 | .lisp .hljs-keyword, 92 | .tex .hljs-special, 93 | .hljs-prompt { 94 | color: #990073; 95 | } 96 | 97 | .hljs-built_in, 98 | .lisp .hljs-title, 99 | .clojure .hljs-built_in { 100 | color: #0086b3; 101 | } 102 | 103 | .hljs-preprocessor, 104 | .hljs-pragma, 105 | .hljs-pi, 106 | .hljs-doctype, 107 | .hljs-shebang, 108 | .hljs-cdata { 109 | color: #999; 110 | font-weight: bold; 111 | } 112 | 113 | .hljs-deletion { 114 | background: #fdd; 115 | } 116 | 117 | .hljs-addition { 118 | background: #dfd; 119 | } 120 | 121 | .diff .hljs-change { 122 | background: #0086b3; 123 | } 124 | 125 | .hljs-chunk { 126 | color: #aaa; 127 | } 128 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # asm.js 2 | 3 | A Mozilla Research project to specify and develop the extremely optimizable subset of JS targeted by compilers like Emscripten, Mandreel, and LLJS. 4 | 5 | Discussion of the asm.js spec now takes place on Specifiction 6 | [here](http://discourse.specifiction.org/c/asm-js). 7 | 8 | As of this update, this repo hosts the source for 9 | [the current asm.js Working Draft](http://asmjs.org/spec/latest/). 10 | 11 | This repo also hosts JS source code which performs asm.js validation, however 12 | as of this update, this code is not up to date with the latest working draft and is 13 | not extensively tested. Patches to update it or fix bugs are welcome though. 14 | 15 | ## Example 16 | 17 | ```javascript 18 | function mymodule(stdlib, foreign, heap) { 19 | "use asm"; 20 | 21 | // ------------------------------------------------------------------------- 22 | // SECTION 1: globals 23 | 24 | var H32 = new stdlib.Int32Array(heap); 25 | var HU32 = new stdlib.Uint32Array(heap); 26 | var log = foreign.consoleDotLog; 27 | 28 | var g_i = 0; // int global 29 | var g_f = 0.0; // double global 30 | 31 | // ------------------------------------------------------------------------- 32 | // SECTION 2: functions 33 | 34 | function f(x, y) { 35 | // SECTION A: parameter type declarations 36 | x = x|0; // int parameter 37 | y = +y; // double parameter 38 | 39 | // SECTION B: function body 40 | log(x|0); // call into FFI -- must force the sign 41 | log(y); // call into FFI -- already know it's a double 42 | x = (x+3)|0; // signed addition 43 | 44 | // SECTION C: unconditional return 45 | return ((((x+1)|0)>>>0)/(x>>>0))|0; // compound expression 46 | } 47 | 48 | function g() { 49 | g_f = +(g_i|0); // read/write globals 50 | return; 51 | } 52 | 53 | function g2() { 54 | return; 55 | } 56 | 57 | function h(i, x) { 58 | i = i|0; 59 | x = x|0; 60 | H32[i>>2] = x; // shifted by log2(byte count) 61 | ftable_2[(x-2)&1](); // dynamic call of functions in table 2 62 | 63 | // no return necessary when return type is void 64 | } 65 | 66 | // ------------------------------------------------------------------------- 67 | // SECTION 3: function tables 68 | 69 | var ftable_1 = [f]; 70 | var ftable_2 = [g, g2]; // all of the same type 71 | 72 | // ------------------------------------------------------------------------- 73 | // SECTION 4: exports 74 | 75 | return { f_export: f, goop: g }; 76 | } 77 | ``` 78 | 79 | ## License 80 | 81 | [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0). 82 | -------------------------------------------------------------------------------- /lib/asmAssert.js: -------------------------------------------------------------------------------- 1 | var asm = require('../lib/asm'); 2 | var validate = asm.validate; 3 | var ValidationError = asm.ValidationError; 4 | 5 | function explode(test) { 6 | var __EMPTY__ = []; 7 | var __PURE__ = ["var imul = stdlib.Math.imul, sin = stdlib.Math.sin;"]; 8 | var __ALL__ = __PURE__.concat(["var H32 = new stdlib.Int32Array(heap), HU32 = new stdlib.Uint32Array(heap), HF64 = new stdlib.Float64Array(heap);"]); 9 | 10 | var SEP = "\n "; 11 | 12 | return String(test).replace("__EMPTY__", __EMPTY__.join(SEP)) 13 | .replace("__PURE__", __PURE__.join(SEP)) 14 | .replace("__ALL__", __ALL__.join(SEP)); 15 | } 16 | 17 | function asmAssert(msg, f, expect) { 18 | f = explode(f); 19 | var hasOwn = {}.hasOwnProperty; 20 | 21 | if (expect.pass) { 22 | return function(test) { 23 | var report; 24 | test.doesNotThrow(function() { report = validate(f); }, "validation threw"); 25 | try { 26 | if (!report) 27 | return; 28 | var types = expect.types, 29 | singleExport = expect.export, 30 | exports = expect.exports; 31 | if (types) { 32 | for (var key in types) { 33 | if (!hasOwn.call(types, key)) 34 | continue; 35 | var expectedType = types[key], 36 | actualType = report.getFunction(key); 37 | test.ok(actualType, msg + ": function " + key + " not found"); 38 | test.ok(actualType.equals(expectedType), msg + ": " + key + " : " + actualType + ", expected " + expectedType); 39 | } 40 | } 41 | if (singleExport) { 42 | test.ok(report.isSingleExport(), msg + ": expected single export, got multiple"); 43 | var actualExport = report.getExport(); 44 | test.equal(actualExport, singleExport, msg + ": expected single export " + singleExport + ", got " + actualExport); 45 | } 46 | if (exports) { 47 | for (var key in exports) { 48 | if (!hasOwn.call(exports, key)) 49 | continue; 50 | var actualExport = report.getExport(key); 51 | test.ok(actualExport, msg + ": function " + key + " not found"); 52 | test.equal(actualExport, exports[key], msg + ": expected export " + key + " to map to " + exports[key] + ", got " + actualExport); 53 | } 54 | } 55 | } catch (e) { 56 | test.ok(false, msg + ": validation failed: " + e); 57 | } finally { 58 | test.done(); 59 | } 60 | } 61 | } else { 62 | return function(test) { 63 | test.throws(function() { validate(f); }, 64 | function(e) { 65 | return e instanceof ValidationError; 66 | }, 67 | msg + ": should fail to validate"); 68 | test.done(); 69 | } 70 | } 71 | } 72 | 73 | asmAssert.one = function(msg, f, expect) { 74 | return asmAssert(msg, 75 | "function one(stdlib, foreign, heap) {\n 'use asm';\n __ALL__\n " + f + "\n return {};\n}", 76 | expect); 77 | }; 78 | 79 | module.exports = asmAssert; 80 | -------------------------------------------------------------------------------- /lib/tables.js: -------------------------------------------------------------------------------- 1 | var dict = require('dict'); 2 | var ty = require('./types'); 3 | 4 | exports.ROOT_NAMES = ['stdlib', 'foreign', 'heap']; 5 | 6 | exports.HEAP_VIEW_TYPES = dict({ 7 | 'Int8Array': ty.View(1, ty.Intish), 8 | 'Uint8Array': ty.View(1, ty.Intish), 9 | 'Int16Array': ty.View(2, ty.Intish), 10 | 'Uint16Array': ty.View(2, ty.Intish), 11 | 'Int32Array': ty.View(4, ty.Intish), 12 | 'Uint32Array': ty.View(4, ty.Intish), 13 | 'Float32Array': ty.View(4, ty.Doublish), 14 | 'Float64Array': ty.View(8, ty.Doublish) 15 | }); 16 | 17 | var DoublishToDouble = ty.Arrow([ty.Doublish], ty.Double); 18 | 19 | exports.STDLIB_TYPES = dict({ 20 | 'Infinity': ty.Double, 21 | 'NaN': ty.Double 22 | }); 23 | 24 | exports.STDLIB_MATH_TYPES = dict({ 25 | 'acos': DoublishToDouble, 26 | 'asin': DoublishToDouble, 27 | 'atan': DoublishToDouble, 28 | 'cos': DoublishToDouble, 29 | 'sin': DoublishToDouble, 30 | 'tan': DoublishToDouble, 31 | 'ceil': DoublishToDouble, 32 | 'floor': DoublishToDouble, 33 | 'exp': DoublishToDouble, 34 | 'log': DoublishToDouble, 35 | 'sqrt': DoublishToDouble, 36 | 'abs': ty.Overloaded([ 37 | ty.Arrow([ty.Signed], ty.Signed), 38 | DoublishToDouble 39 | ]), 40 | 'atan2': ty.Arrow([ty.Doublish, ty.Doublish], ty.Double), 41 | 'pow': ty.Arrow([ty.Doublish, ty.Doublish], ty.Double), 42 | 'imul': ty.Arrow([ty.Int, ty.Int], ty.Signed), 43 | // FIXME: In the asm.js spec, min and max are variadic. 44 | 'min': ty.Overloaded([ 45 | ty.Arrow([ty.Int, ty.Int], ty.Signed), 46 | ty.Arrow([ty.Double, ty.Double], ty.Double) 47 | ]), 48 | 'max': ty.Overloaded([ 49 | ty.Arrow([ty.Int, ty.Int], ty.Signed), 50 | ty.Arrow([ty.Double, ty.Double], ty.Double) 51 | ]), 52 | 'E': ty.Double, 53 | 'LN10': ty.Double, 54 | 'LN2': ty.Double, 55 | 'LOG2E': ty.Double, 56 | 'LOG10E': ty.Double, 57 | 'PI': ty.Double, 58 | 'SQRT1_2': ty.Double, 59 | 'SQRT2': ty.Double 60 | }); 61 | 62 | var SignedBitwise = ty.Arrow([ty.Intish, ty.Intish], ty.Signed); 63 | 64 | var RelOp = ty.Overloaded([ 65 | ty.Arrow([ty.Signed, ty.Signed], ty.Int), 66 | ty.Arrow([ty.Unsigned, ty.Unsigned], ty.Int), 67 | ty.Arrow([ty.Double, ty.Double], ty.Int) 68 | ]); 69 | 70 | exports.BINOPS = dict({ 71 | '+': ty.Arrow([ty.Double, ty.Double], ty.Double), 72 | '-': ty.Arrow([ty.Doublish, ty.Doublish], ty.Double), 73 | '*': ty.Arrow([ty.Doublish, ty.Doublish], ty.Double), 74 | '/': ty.Overloaded([ 75 | ty.Arrow([ty.Signed, ty.Signed], ty.Intish), 76 | ty.Arrow([ty.Unsigned, ty.Unsigned], ty.Intish), 77 | ty.Arrow([ty.Doublish, ty.Doublish], ty.Double) 78 | ]), 79 | '%': ty.Overloaded([ 80 | ty.Arrow([ty.Signed, ty.Signed], ty.Intish), 81 | ty.Arrow([ty.Unsigned, ty.Unsigned], ty.Intish), 82 | ty.Arrow([ty.Doublish, ty.Doublish], ty.Double) 83 | ]), 84 | '|': SignedBitwise, 85 | '&': SignedBitwise, 86 | '^': SignedBitwise, 87 | '<<': SignedBitwise, 88 | '>>': SignedBitwise, 89 | '>>>': ty.Arrow([ty.Intish, ty.Intish], ty.Unsigned), 90 | '<': RelOp, 91 | '<=': RelOp, 92 | '>': RelOp, 93 | '>=': RelOp, 94 | '==': RelOp, 95 | '!=': RelOp 96 | }); 97 | 98 | exports.UNOPS = dict({ 99 | '+': ty.Overloaded([ 100 | ty.Arrow([ty.Signed], ty.Double), 101 | ty.Arrow([ty.Unsigned], ty.Double), 102 | ty.Arrow([ty.Doublish], ty.Double) 103 | ]), 104 | '-': ty.Overloaded([ 105 | ty.Arrow([ty.Int], ty.Intish), 106 | ty.Arrow([ty.Doublish], ty.Double) 107 | ]), 108 | '~': ty.Arrow([ty.Intish], ty.Signed), 109 | '!': ty.Arrow([ty.Int], ty.Int) 110 | }); 111 | -------------------------------------------------------------------------------- /lib/types.js: -------------------------------------------------------------------------------- 1 | function Type() {} 2 | 3 | // ============================================================================= 4 | // Value Types 5 | // ============================================================================= 6 | 7 | function ValueType(name, supertypes) { 8 | this._name = name; 9 | this._supertypes = supertypes; 10 | }; 11 | 12 | ValueType.prototype = Object.create(Type.prototype); 13 | 14 | ValueType.prototype.equals = function equals(other) { 15 | return this === other; 16 | }; 17 | 18 | ValueType.prototype.subtype = function subtype(other) { 19 | return this.equals(other) || 20 | (this._supertypes && this._supertypes.some(function(sup) { 21 | return sup.subtype(other); 22 | })); 23 | }; 24 | 25 | ValueType.prototype.toString = function toString() { 26 | return this._name; 27 | } 28 | 29 | var Extern = new ValueType("extern", null); 30 | 31 | var Intish = new ValueType("intish", null); 32 | 33 | var Doublish = new ValueType("doublish", null); 34 | 35 | var Unknown = new ValueType("unknown", [Intish, Doublish]); 36 | 37 | var Int = new ValueType("int", [Intish]); 38 | 39 | var Double = new ValueType("double", [Extern, Doublish]); 40 | 41 | var Signed = new ValueType("signed", [Extern, Int]); 42 | 43 | var Unsigned = new ValueType("unsigned", [Extern, Int]); 44 | 45 | var Fixnum = new ValueType("fixnum", [Signed, Unsigned]); 46 | 47 | var Void = new ValueType("void", null); 48 | 49 | // ============================================================================= 50 | // Global Types 51 | // ============================================================================= 52 | 53 | // ([ValueType], ValueType) -> Arrow 54 | function Arrow(params, result) { 55 | if (!(this instanceof Arrow)) 56 | return new Arrow(params, result); 57 | this.params = params; 58 | this.result = result; 59 | } 60 | 61 | Arrow.prototype = Object.create(Type.prototype); 62 | 63 | Arrow.prototype.equals = function equals(other) { 64 | return other instanceof Arrow && 65 | this.params.length === other.params.length && 66 | this.params.every(function(p, i) { return p.equals(other.params[i]) }) && 67 | this.result.equals(other.result); 68 | }; 69 | 70 | Arrow.prototype.toString = function toString() { 71 | return "(" + this.params.join(", ") + ") -> " + this.result; 72 | }; 73 | 74 | // ([Arrow]) -> Overloaded 75 | function Overloaded(alts) { 76 | if (!(this instanceof Overloaded)) 77 | return new Overloaded(alts); 78 | this.alts = alts; 79 | } 80 | 81 | Overloaded.prototype = Object.create(Type.prototype); 82 | 83 | Overloaded.prototype.toString = function toString() { 84 | return this.alts.join(" ^ "); 85 | }; 86 | 87 | // (1|2|4|8, ValueType) -> View 88 | function View(bytes, elementType) { 89 | if (!(this instanceof View)) 90 | return new View(bytes, elementType); 91 | this.bytes = bytes; 92 | this.elementType = elementType; 93 | } 94 | 95 | View.prototype.toString = function toString() { 96 | return "View<" + this.bytes + ", " + this.elementType + ">"; 97 | }; 98 | 99 | // (Arrow, integer) -> Table 100 | function Table(type, length) { 101 | if (!(this instanceof Table)) 102 | return new Table(type, length); 103 | this.type = type; 104 | this.length = length; 105 | } 106 | 107 | Table.prototype = Object.create(Type.prototype); 108 | 109 | Table.prototype.toString = function toString() { 110 | return "(" + this.type + ")[" + this.length + "]"; 111 | }; 112 | 113 | var Function = Object.create(Type.prototype); 114 | 115 | var Module = Object.create(Type.prototype); 116 | 117 | var ModuleParameter = Object.create(Type.prototype); 118 | 119 | Function.toString = function() { 120 | return "Function"; 121 | }; 122 | 123 | exports.ValueType = ValueType; 124 | 125 | exports.Extern = Extern; 126 | exports.Intish = Intish; 127 | exports.Doublish = Doublish; 128 | exports.Unknown = Unknown; 129 | exports.Int = Int; 130 | exports.Double = Double; 131 | exports.Signed = Signed; 132 | exports.Unsigned = Unsigned; 133 | exports.Fixnum = Fixnum; 134 | exports.Void = Void; 135 | 136 | exports.Arrow = Arrow; 137 | exports.Overloaded = Overloaded; 138 | exports.View = View; 139 | exports.Table = Table; 140 | exports.Function = Function; 141 | exports.Module = Module; 142 | exports.ModuleParameter = ModuleParameter; 143 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | var ty = require('../lib/types'); 2 | var asmAssert = require('../lib/asmAssert'); 3 | 4 | exports.testModuloIntish1 = asmAssert.one( 5 | "% doesn't return int", 6 | function f() { 7 | var x = 0, y = 0; 8 | x = (x|0)%(y|0); 9 | }, 10 | { pass: false }); 11 | 12 | exports.testModuleIntish2 = asmAssert.one( 13 | "% returns intish", 14 | function f() { 15 | var x = 0, y = 0; 16 | x = ((x|0)%(y|0))|0; 17 | }, 18 | { pass: true }); 19 | 20 | exports.testIntCoercionRequiresDouble1 = asmAssert.one( 21 | "~~ requires double", 22 | function f() { 23 | var x = 0.0, y = 0; 24 | y = ~~HF64[0]; 25 | }, 26 | { pass: false }); 27 | 28 | exports.testIntCoercionRequiresDouble2 = asmAssert.one( 29 | "~~ requires double", 30 | function f() { 31 | var x = 0.0, y = 0; 32 | y = ~~+HF64[0]; 33 | }, 34 | { pass: true }); 35 | 36 | exports.testNot = asmAssert.one( 37 | "! operator", 38 | function f() { 39 | var x = 0; 40 | x = !((x|0) > 0); 41 | }, 42 | { pass: true }); 43 | 44 | exports.testParamTypes = asmAssert.one( 45 | "different parameter types", 46 | function f(x, y) { 47 | x = x|0; 48 | y = +y; 49 | }, { 50 | pass: true, 51 | types: { 52 | f: ty.Arrow([ty.Int, ty.Double], ty.Void) 53 | } 54 | }); 55 | 56 | exports.testAdd = asmAssert.one( 57 | "addition", 58 | function add1(x) { 59 | x = x|0; 60 | return ((x|0)+1)|0; 61 | }, { 62 | pass: true, 63 | types: { 64 | add1: ty.Arrow([ty.Int], ty.Signed) 65 | } 66 | }); 67 | 68 | exports.testImul = asmAssert.one( 69 | "Math.imul", 70 | function double(x) { 71 | x = x|0; 72 | return imul(x, 2)|0; 73 | }, { 74 | pass: true, 75 | double: ty.Arrow([ty.Int], ty.Signed) 76 | }); 77 | 78 | exports.testLoad = asmAssert.one( 79 | "heap load", 80 | function get(i) { 81 | i = i|0; 82 | return H32[i>>2]|0; 83 | }, { 84 | pass: true, 85 | types: { 86 | get: ty.Arrow([ty.Int], ty.Signed) 87 | } 88 | }); 89 | 90 | exports.testStore = asmAssert.one( 91 | "heap store", 92 | function set(i, x) { 93 | i = i|0; 94 | x = x|0; 95 | H32[i>>2] = x|0; 96 | }, { 97 | pass: true, 98 | types: { 99 | set: ty.Arrow([ty.Int, ty.Int], ty.Void) 100 | } 101 | }); 102 | 103 | exports.testCall1 = asmAssert( 104 | "function call", 105 | function call(stdlib, foreign, heap) { 106 | "use asm"; 107 | __ALL__ 108 | function f(x) { 109 | x = +x; 110 | return +(x + 1.0); 111 | } 112 | function g() { 113 | var x = 0.0; 114 | x = +f(x); 115 | return +x; 116 | } 117 | return {}; 118 | }, { pass: true }); 119 | 120 | exports.testCall2 = asmAssert( 121 | "function call", 122 | function call(stdlib, foreign, heap) { 123 | "use asm"; 124 | __ALL__ 125 | function f(x) { 126 | x = x|0; 127 | return (x + 1)|0; 128 | } 129 | function g() { 130 | var x = 0.0; 131 | x = +f(x); 132 | return +x; 133 | } 134 | return {}; 135 | }, { pass: false }); 136 | 137 | exports.testVoid1 = asmAssert( 138 | "void function call in expression statement", 139 | function void_(stdlib) { 140 | "use asm"; 141 | __PURE__ 142 | function f() { } 143 | function g() { 144 | f(); 145 | } 146 | return {} 147 | }, { pass: true }); 148 | 149 | exports.testVoid2 = asmAssert( 150 | "void function call in comma expression", 151 | function void_(stdlib) { 152 | "use asm"; 153 | __PURE__ 154 | function f() { } 155 | function g() { 156 | var x = 0.0; 157 | x = (f(), f(), 1.0); 158 | } 159 | return {}; 160 | }, { pass: true }); 161 | 162 | exports.testEval1 = asmAssert( 163 | "module named eval", 164 | function eval() { 165 | "use asm"; 166 | __PURE__ 167 | function empty () { } 168 | return empty; 169 | }, { pass: false }); 170 | 171 | exports.testEval2 = asmAssert( 172 | "function named eval", 173 | function m() { 174 | "use asm"; 175 | __PURE__ 176 | function eval() { } 177 | return eval; 178 | }, { pass: false }); 179 | 180 | exports.testEval3 = asmAssert( 181 | "local named eval", 182 | function m() { 183 | "use asm"; 184 | __PURE__ 185 | function f() { 186 | var eval = 0; 187 | } 188 | return f; 189 | }, { pass: false }); 190 | 191 | exports.testAbs = asmAssert( 192 | "abs returns signed", 193 | function call(stdlib, foreign, heap) { 194 | "use asm"; 195 | __PURE__ 196 | var abs = stdlib.Math.abs; 197 | function f() { 198 | return abs(1)|0; 199 | } 200 | return f; 201 | }, { pass: true }); 202 | 203 | exports.testIf = asmAssert( 204 | "if condition is Int", 205 | function m(){ 206 | "use asm"; 207 | function f(x,y) { 208 | x = x|0; 209 | y = y|0; 210 | if (x) { 211 | y = 3; 212 | } 213 | } 214 | return f; 215 | }, { pass: true }); 216 | 217 | exports.testEmpty = asmAssert( 218 | "empty statements", 219 | function m(){ 220 | "use asm"; 221 | function f() { 222 | ; 223 | ; 224 | ; 225 | {}; 226 | if (0); 227 | if (0) {}; 228 | } 229 | return f; 230 | }, { pass: true }); 231 | 232 | exports.testMinMax = asmAssert( 233 | "min and max validate", 234 | function m(stdlib, foreign, heap) { 235 | "use asm"; 236 | var max = stdlib.Math.max; 237 | var min = stdlib.Math.min; 238 | function f(i0, i1, d0, d1) { 239 | i0 = i0|0; 240 | i1 = i1|0; 241 | d0 = +d0; 242 | d1 = +d1; 243 | var ia = 0; 244 | var ib = 0; 245 | var da = 0.0; 246 | var db = 0.0; 247 | ia = max(i0, i1)|0; 248 | ib = min(i0, i1)|0; 249 | da = +max(d0, d1); 250 | db = +min(d0, d1); 251 | return +(+(+(ia + ib|0) + d0) + d1); 252 | } 253 | return f; 254 | }, { pass: true }); 255 | 256 | exports.testMinWrongArgumentType = asmAssert( 257 | "min argument types don't match", 258 | function m(stdlib, foreign, heap) { 259 | "use asm"; 260 | var min = stdlib.Math.min; 261 | function f(i0, d1) { 262 | i0 = i0|0; 263 | d1 = +d1; 264 | min(i0, d1)|0; 265 | } 266 | return f; 267 | }, { pass: false }); 268 | 269 | exports.testMaxWrongArgumentType = asmAssert( 270 | "min argument types don't match", 271 | function m(stdlib, foreign, heap) { 272 | "use asm"; 273 | var max = stdlib.Math.max; 274 | function f(d0, i1) { 275 | d0 = +d0; 276 | i1 = i1|0; 277 | +max(d0, i1); 278 | } 279 | return f; 280 | }, { pass: false }); 281 | 282 | exports.testMaxWrongReturnType = asmAssert( 283 | "min argument types don't match", 284 | function m(stdlib, foreign, heap) { 285 | "use asm"; 286 | var max = stdlib.Math.max; 287 | function f(i0, i1) { 288 | i0 = i0|0; 289 | i1 = i1|0; 290 | +max(i0, i1); 291 | } 292 | return f; 293 | }, { pass: false }); 294 | 295 | exports.testFunctionTables = asmAssert( 296 | "function tables", 297 | function m(stdlib, foreign, heap) { 298 | "use asm" 299 | function f() {} 300 | function g() {} 301 | var x = [f], y = [g], z = [f, g] 302 | return f; 303 | }, { pass: true }); 304 | -------------------------------------------------------------------------------- /html/highlight.pack.js: -------------------------------------------------------------------------------- 1 | var hljs=new function(){function j(v){return v.replace(/&/gm,"&").replace(//gm,">")}function t(v){return v.nodeName.toLowerCase()}function h(w,x){var v=w&&w.exec(x);return v&&v.index==0}function r(w){var v=(w.className+" "+(w.parentNode?w.parentNode.className:"")).split(/\s+/);v=v.map(function(x){return x.replace(/^lang(uage)?-/,"")});return v.filter(function(x){return i(x)||x=="no-highlight"})[0]}function o(x,y){var v={};for(var w in x){v[w]=x[w]}if(y){for(var w in y){v[w]=y[w]}}return v}function u(x){var v=[];(function w(y,z){for(var A=y.firstChild;A;A=A.nextSibling){if(A.nodeType==3){z+=A.nodeValue.length}else{if(t(A)=="br"){z+=1}else{if(A.nodeType==1){v.push({event:"start",offset:z,node:A});z=w(A,z);v.push({event:"stop",offset:z,node:A})}}}}return z})(x,0);return v}function q(w,y,C){var x=0;var F="";var z=[];function B(){if(!w.length||!y.length){return w.length?w:y}if(w[0].offset!=y[0].offset){return(w[0].offset"}function E(G){F+=""}function v(G){(G.event=="start"?A:E)(G.node)}while(w.length||y.length){var D=B();F+=j(C.substr(x,D[0].offset-x));x=D[0].offset;if(D==w){z.reverse().forEach(E);do{v(D.splice(0,1)[0]);D=B()}while(D==w&&D.length&&D[0].offset==x);z.reverse().forEach(A)}else{if(D[0].event=="start"){z.push(D[0].node)}else{z.pop()}v(D.splice(0,1)[0])}}return F+j(C.substr(x))}function m(y){function v(z){return(z&&z.source)||z}function w(A,z){return RegExp(v(A),"m"+(y.cI?"i":"")+(z?"g":""))}function x(D,C){if(D.compiled){return}D.compiled=true;D.k=D.k||D.bK;if(D.k){var z={};var E=function(G,F){if(y.cI){F=F.toLowerCase()}F.split(" ").forEach(function(H){var I=H.split("|");z[I[0]]=[G,I[1]?Number(I[1]):1]})};if(typeof D.k=="string"){E("keyword",D.k)}else{Object.keys(D.k).forEach(function(F){E(F,D.k[F])})}D.k=z}D.lR=w(D.l||/\b[A-Za-z0-9_]+\b/,true);if(C){if(D.bK){D.b="\\b("+D.bK.split(" ").join("|")+")\\b"}if(!D.b){D.b=/\B|\b/}D.bR=w(D.b);if(!D.e&&!D.eW){D.e=/\B|\b/}if(D.e){D.eR=w(D.e)}D.tE=v(D.e)||"";if(D.eW&&C.tE){D.tE+=(D.e?"|":"")+C.tE}}if(D.i){D.iR=w(D.i)}if(D.r===undefined){D.r=1}if(!D.c){D.c=[]}var B=[];D.c.forEach(function(F){if(F.v){F.v.forEach(function(G){B.push(o(F,G))})}else{B.push(F=="self"?D:F)}});D.c=B;D.c.forEach(function(F){x(F,D)});if(D.starts){x(D.starts,C)}var A=D.c.map(function(F){return F.bK?"\\.?("+F.b+")\\.?":F.b}).concat([D.tE,D.i]).map(v).filter(Boolean);D.t=A.length?w(A.join("|"),true):{exec:function(F){return null}};D.continuation={}}x(y)}function c(S,L,J,R){function v(U,V){for(var T=0;T";U+=Z+'">';return U+X+Y}function N(){if(!I.k){return j(C)}var T="";var W=0;I.lR.lastIndex=0;var U=I.lR.exec(C);while(U){T+=j(C.substr(W,U.index-W));var V=E(I,U);if(V){H+=V[1];T+=w(V[0],j(U[0]))}else{T+=j(U[0])}W=I.lR.lastIndex;U=I.lR.exec(C)}return T+j(C.substr(W))}function F(){if(I.sL&&!f[I.sL]){return j(C)}var T=I.sL?c(I.sL,C,true,I.continuation.top):e(C);if(I.r>0){H+=T.r}if(I.subLanguageMode=="continuous"){I.continuation.top=T.top}return w(T.language,T.value,false,true)}function Q(){return I.sL!==undefined?F():N()}function P(V,U){var T=V.cN?w(V.cN,"",true):"";if(V.rB){D+=T;C=""}else{if(V.eB){D+=j(U)+T;C=""}else{D+=T;C=U}}I=Object.create(V,{parent:{value:I}})}function G(T,X){C+=T;if(X===undefined){D+=Q();return 0}var V=v(X,I);if(V){D+=Q();P(V,X);return V.rB?0:X.length}var W=z(I,X);if(W){var U=I;if(!(U.rE||U.eE)){C+=X}D+=Q();do{if(I.cN){D+=""}H+=I.r;I=I.parent}while(I!=W.parent);if(U.eE){D+=j(X)}C="";if(W.starts){P(W.starts,"")}return U.rE?0:X.length}if(A(X,I)){throw new Error('Illegal lexeme "'+X+'" for mode "'+(I.cN||"")+'"')}C+=X;return X.length||1}var M=i(S);if(!M){throw new Error('Unknown language: "'+S+'"')}m(M);var I=R||M;var D="";for(var K=I;K!=M;K=K.parent){if(K.cN){D+=w(K.cN,D,true)}}var C="";var H=0;try{var B,y,x=0;while(true){I.t.lastIndex=x;B=I.t.exec(L);if(!B){break}y=G(L.substr(x,B.index-x),B[0]);x=B.index+y}G(L.substr(x));for(var K=I;K.parent;K=K.parent){if(K.cN){D+=""}}return{r:H,value:D,language:S,top:I}}catch(O){if(O.message.indexOf("Illegal")!=-1){return{r:0,value:j(L)}}else{throw O}}}function e(y,x){x=x||b.languages||Object.keys(f);var v={r:0,value:j(y)};var w=v;x.forEach(function(z){if(!i(z)){return}var A=c(z,y,false);A.language=z;if(A.r>w.r){w=A}if(A.r>v.r){w=v;v=A}});if(w.language){v.second_best=w}return v}function g(v){if(b.tabReplace){v=v.replace(/^((<[^>]+>|\t)+)/gm,function(w,z,y,x){return z.replace(/\t/g,b.tabReplace)})}if(b.useBR){v=v.replace(/\n/g,"
")}return v}function p(z){var y=b.useBR?z.innerHTML.replace(/\n/g,"").replace(/
|
]*>/g,"\n").replace(/<[^>]*>/g,""):z.textContent;var A=r(z);if(A=="no-highlight"){return}var v=A?c(A,y,true):e(y);var w=u(z);if(w.length){var x=document.createElementNS("http://www.w3.org/1999/xhtml","pre");x.innerHTML=v.value;v.value=q(w,u(x),y)}v.value=g(v.value);z.innerHTML=v.value;z.className+=" hljs "+(!A&&v.language||"");z.result={language:v.language,re:v.r};if(v.second_best){z.second_best={language:v.second_best.language,re:v.second_best.r}}}var b={classPrefix:"hljs-",tabReplace:null,useBR:false,languages:undefined};function s(v){b=o(b,v)}function l(){if(l.called){return}l.called=true;var v=document.querySelectorAll("pre code");Array.prototype.forEach.call(v,p)}function a(){addEventListener("DOMContentLoaded",l,false);addEventListener("load",l,false)}var f={};var n={};function d(v,x){var w=f[v]=x(this);if(w.aliases){w.aliases.forEach(function(y){n[y]=v})}}function k(){return Object.keys(f)}function i(v){return f[v]||f[n[v]]}this.highlight=c;this.highlightAuto=e;this.fixMarkup=g;this.highlightBlock=p;this.configure=s;this.initHighlighting=l;this.initHighlightingOnLoad=a;this.registerLanguage=d;this.listLanguages=k;this.getLanguage=i;this.inherit=o;this.IR="[a-zA-Z][a-zA-Z0-9_]*";this.UIR="[a-zA-Z_][a-zA-Z0-9_]*";this.NR="\\b\\d+(\\.\\d+)?";this.CNR="(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)";this.BNR="\\b(0b[01]+)";this.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~";this.BE={b:"\\\\[\\s\\S]",r:0};this.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[this.BE]};this.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[this.BE]};this.PWM={b:/\b(a|an|the|are|I|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such)\b/};this.CLCM={cN:"comment",b:"//",e:"$",c:[this.PWM]};this.CBCM={cN:"comment",b:"/\\*",e:"\\*/",c:[this.PWM]};this.HCM={cN:"comment",b:"#",e:"$",c:[this.PWM]};this.NM={cN:"number",b:this.NR,r:0};this.CNM={cN:"number",b:this.CNR,r:0};this.BNM={cN:"number",b:this.BNR,r:0};this.CSSNM={cN:"number",b:this.NR+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",r:0};this.RM={cN:"regexp",b:/\//,e:/\/[gim]*/,i:/\n/,c:[this.BE,{b:/\[/,e:/\]/,r:0,c:[this.BE]}]};this.TM={cN:"title",b:this.IR,r:0};this.UTM={cN:"title",b:this.UIR,r:0}}();hljs.registerLanguage("javascript",function(a){return{aliases:["js"],k:{keyword:"in if for while finally var new function do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const class",literal:"true false null undefined NaN Infinity",built_in:"eval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent encodeURI encodeURIComponent escape unescape Object Function Boolean Error EvalError InternalError RangeError ReferenceError StopIteration SyntaxError TypeError URIError Number Math Date String RegExp Array Float32Array Float64Array Int16Array Int32Array Int8Array Uint16Array Uint32Array Uint8Array Uint8ClampedArray ArrayBuffer DataView JSON Intl arguments require module console window document"},c:[{cN:"pi",b:/^\s*('|")use strict('|")/,r:10},a.ASM,a.QSM,a.CLCM,a.CBCM,a.CNM,{b:"("+a.RSR+"|\\b(case|return|throw)\\b)\\s*",k:"return throw case",c:[a.CLCM,a.CBCM,a.RM,{b:/;/,r:0,sL:"xml"}],r:0},{cN:"function",bK:"function",e:/\{/,eE:true,c:[a.inherit(a.TM,{b:/[A-Za-z$_][0-9A-Za-z$_]*/}),{cN:"params",b:/\(/,e:/\)/,c:[a.CLCM,a.CBCM],i:/["'\(]/}],i:/\[|%/},{b:/\$[(.]/},{b:"\\."+a.IR,r:0}]}}); -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /html/whatwg.css: -------------------------------------------------------------------------------- 1 | /* WHATWG Green: sRGB #3c790a, rgb(60, 121, 10) */ 2 | 3 | html { margin: 0; padding: 0; color: black; background: white; } 4 | body { margin: 0; padding: 0 1em 2em 8.5em; line-height: 1.35; color: black; background: white top left repeat-y; } 5 | 6 | @media screen { 7 | html { background: #eeeeee; } 8 | body { margin-bottom: 30%; border-bottom: thin solid #3c790a; } 9 | } 10 | 11 | :link { color: #00C; background: transparent } 12 | :visited { color: #609; background: transparent } 13 | :link:active, :visited:active { color: #C00; background: transparent } 14 | :link:hover, :visited:hover { background: #ffa; } 15 | code :link, code :visited { color: inherit; } 16 | 17 | body, th, td { font-family: sans-serif, Droid Sans Fallback; } 18 | 19 | h1, h2, h3, h4, h5, h6 { text-align: left; text-rendering: optimiseLegibility; } 20 | h1, h2, h3 { color: #3c790a; background: transparent; } 21 | h1 { font: 900 200% sans-serif, Droid Sans Fallback; } 22 | h1.allcaps { font: 900 350% sans-serif, Droid Sans Fallback; letter-spacing: 2px; } 23 | h2 { font: 800 140% sans-serif, Droid Sans Fallback; } 24 | h3 { font: 800 125% sans-serif, Droid Sans Fallback; } 25 | h4 { font: 800 110% sans-serif, Droid Sans Fallback; } 26 | h5 { font: 800 100% sans-serif, Droid Sans Fallback; } 27 | h6 { font: 600 italic 100% sans-serif, Droid Sans Fallback; } 28 | 29 | pre { margin-left: 2em; white-space: pre-wrap; } 30 | h2 { margin: 3em 0 1em 0; } 31 | h3 { margin: 2.5em 0 1em 0; } 32 | h4 { margin: 2.5em 0 0.75em 0; } 33 | h5, h6 { margin: 2.5em 0 1em; } 34 | h1 + h2, h2 + h3, h3 + h4, h4 + h5, h5 + h6, h1 + div + h2, h2 + div + h3, h3 + div + h4, h4 + div + h5, h5 + div + h6 { margin-top: 0.5em; } 35 | p { margin: 1em 0; } 36 | hr { display: block; background: none; border: none; padding: 0; margin: 2em 0; height: auto; } 37 | dl, dd { margin-top: 0; margin-bottom: 0; } 38 | dt { margin-top: 0.75em; margin-bottom: 0.25em; clear: left; } 39 | dt + dt { margin-top: 0; } 40 | dd dt { margin-top: 0.25em; margin-bottom: 0; } 41 | dd p, dd ol, dd ol.brief { margin-top: 0; } 42 | dd dl + p { margin-top: 1em; } 43 | dd table + p { margin-top: 1em; } 44 | p + * > li, dd li { margin: 1em 0; } 45 | dt, dfn { font-weight: bold; font-style: normal; } 46 | i, em, dt dfn { font-style: italic; } 47 | pre, code { font-size: inherit; font-family: monospace, Droid Sans Fallback, sans-serif; font-variant: normal; } 48 | pre strong { color: black; font: inherit; font-weight: bold; background: #B7EDEA; } 49 | pre em { font-weight: bolder; font-style: normal; } 50 | @media screen { code { color: orangered; } } 51 | var sub { vertical-align: bottom; font-size: smaller; position: relative; top: 0.1em; } 52 | table { border-collapse: collapse; border-style: hidden hidden none hidden; } 53 | table thead, table tbody { border-bottom: solid; } 54 | table tbody th { text-align: left; } 55 | table tbody th:first-child { border-left: solid; } 56 | table td, table th { border-left: solid; border-right: solid; border-bottom: solid thin; vertical-align: top; padding: 0.2em; } 57 | blockquote { margin: 0 0 0 2em; border: 0; padding: 0; font-style: italic; } 58 | ins { background: green; color: white; /* color: green; border: solid thin lime; padding: 0.3em; line-height: 1.6em; */ text-decoration: none; } 59 | del { background: maroon; color: white; /* color: maroon; border: solid thin red; padding: 0.3em; line-height: 1.6em; */ text-decoration: line-through; } 60 | body ins, body del { display: block; } 61 | body * ins, body * del { display: inline; } 62 | 63 | .toc dfn, h1 dfn, h2 dfn, h3 dfn, h4 dfn, h5 dfn, h6 dfn { font: inherit; } 64 | img.extra, p.overview { float: right; } 65 | hr.bookmark { border: dashed 2em black; background: yellow; } 66 | pre::before { font: bold 0.8em sans-serif; padding: 0.5em; position: absolute; top: auto; margin: -0.703125em 0 0 -3.75em /* 1em/0.8 + 1.5em + 0.5em*2 */ ; width: 1.5em; background: inherit; border: 0.078125em; border-style: solid none solid solid; border-radius: 1em 0 0 1em; } 67 | pre.idl { border: solid 0.0625em; background: #EEEEEE; color: black; padding: 0.5em 1em; } 68 | pre.idl :link, pre.idl :visited { color: inherit; background: transparent; } 69 | pre.idl::before { content: 'IDL'; } 70 | pre.asn { border: solid 0.0625em; background: #EEEEEE; color: black; padding: 0.5em 1em; } 71 | pre.asn :link, pre.asn :visited { color: inherit; background: transparent; } 72 | pre.asn::before { content: 'ASN'; } 73 | pre.css { border: solid 0.0625em; background: #FFFFEE; color: black; padding: 0.5em 1em; } 74 | pre.css:first-line { color: #AAAA50; } 75 | pre.css::before { content: 'CSS'; } 76 | dl.domintro { color: green; margin: 2em 0 2em 0; padding: 0.5em 1em 0.5em 2em; border: none; background: #DDFFDD; } 77 | hr + dl.domintro, div.impl + dl.domintro { margin-top: 2.5em; margin-bottom: 1.5em; } 78 | dl.domintro dt, dl.domintro dt * { color: black; text-decoration: none; } 79 | dl.domintro dd { margin: 0.5em 0 1em 2em; padding: 0; } 80 | dl.domintro dd p { margin: 0.5em 0; } 81 | dl.domintro:before { display: table; margin: -1em -0.5em -0.5em auto; width: auto; content: 'This box is non-normative. Implementation requirements are given below this box.'; color: black; font-style: italic; border: solid 2px; background: white; padding: 0 0.25em; } 82 | dl.switch { padding-left: 2em; } 83 | dl.switch > dt { text-indent: -1.5em; } 84 | dl.switch > dt:before { content: '\21AA'; padding: 0 0.5em 0 0; display: inline-block; width: 1em; text-align: right; line-height: 0.5em; } 85 | dl.triple { padding: 0 0 0 1em; } 86 | dl.triple dt, dl.triple dd { margin: 0; display: inline } 87 | dl.triple dt:after { content: ':'; } 88 | dl.triple dd:after { content: '\A'; white-space: pre; } 89 | .diff-old { text-decoration: line-through; color: silver; background: transparent; } 90 | .diff-chg, .diff-new { text-decoration: underline; color: green; background: transparent; } 91 | a .diff-new { border-bottom: 1px blue solid; } 92 | tr.rare { background: #EEEEEE; color: #333333; } 93 | 94 | figure.diagrams { border: double black; background: white; padding: 1em; } 95 | figure.diagrams img { display: block; margin: 1em auto; } 96 | 97 | h2 { page-break-before: always; } 98 | h1, h2, h3, h4, h5, h6, dt { page-break-after: avoid; } 99 | h1 + h2, hr + h2.no-toc { page-break-before: auto; } 100 | 101 | p > span:not([title=""]):not([class="XXX"]):not([class="impl"]):not([class="note"]), 102 | li > span:not([title=""]):not([class="XXX"]):not([class="impl"]):not([class="note"]) { border-bottom: solid #99CC99; } 103 | 104 | .head { margin: 0 0 1em; padding: 1em 0 0 0; display: block; } 105 | .head p { margin: 0; } 106 | .head h1 { margin: 0; } 107 | .head h2 { margin-top: 0; } 108 | .head .logo { float: right; margin: 0 1em; } 109 | .head .logo img { display: block; margin: 0 0 0 auto; border: none } /* remove border from top image */ 110 | .head dl { margin: 1em 0; } 111 | p.copyright { font-size: 0.6em; font-style: oblique; margin: 0; } 112 | 113 | body > .toc > li { margin-top: 1em; margin-bottom: 1em; } 114 | body > .toc.brief > li { margin-top: 0.35em; margin-bottom: 0.35em; } 115 | body > .toc > li > * { margin-bottom: 0.5em; } 116 | body > .toc > li > * > li > * { margin-bottom: 0.25em; } 117 | .toc, .toc li { list-style: none; } 118 | 119 | .brief { margin-top: 1em; margin-bottom: 1em; line-height: 1.1; } 120 | .brief > li { margin: 0; padding: 0; } 121 | .brief > li > p { margin: 0; padding: 0; } 122 | 123 | .category-list { margin-top: -0.75em; margin-bottom: 1em; line-height: 1.5; } 124 | .category-list::before { content: '\21D2\A0'; font-size: 1.2em; font-weight: 900; } 125 | .category-list li { display: inline; } 126 | .category-list li:not(:last-child)::after { content: ', '; } 127 | .category-list li > span, .category-list li > a { text-transform: lowercase; } 128 | .category-list li * { text-transform: none; } /* don't affect nested in */ 129 | 130 | [title=WIP], [title=TBW] { background: red; color: yellow; padding: 0.1em 0.3em; border: dotted white; margin: 0 0.7em 0 0.2em; } 131 | [title=SCS] { background: green; color: white; padding: 0.1em 0.3em; border-style: none dashed; margin: 0 0.7em 0 0.2em; } 132 | [title=WIP] :link, [title=WIP] :visited, 133 | [title=TBW] :link, [title=TBW] :visited, 134 | [title=SCS] :link, [title=SCS] :visited { background: transparent; color: inherit; } 135 | 136 | .big-issue, .XXX { color: #E50000; background: white; border: solid red; padding: 0.5em; margin: 1em 0; } 137 | .big-issue > :first-child, .XXX > :first-child { margin-top: 0; } 138 | p .big-issue, p .XXX { line-height: 3em; } 139 | .note { color: green; background: transparent; font-family: sans-serif, Droid Sans Fallback; } 140 | .warning { color: red; background: transparent; } 141 | .note, .warning { font-weight: bolder; font-style: italic; } 142 | .note em, .warning em, .note i, .warning i, .note var, .warning var { font-style: normal; } 143 | p.note, div.note { padding: 0.5em 2em; } 144 | span.note { padding: 0 2em; } 145 | .note p:first-child, .warning p:first-child { margin-top: 0; } 146 | .note p:last-child, .warning p:last-child { margin-bottom: 0; } 147 | dd > .note:first-child { margin-bottom: 0; } 148 | .warning:before { font-style: normal; } 149 | 150 | .tablenote { margin: 0.25em 0; } 151 | .tablenote small { font-size: 0.9em; } 152 | 153 | .XXX:before, .XXX:after { content: " ** "; position: absolute; left: 0; width: 8em; text-align: right; } 154 | p.note:before { content: 'Note: '; } 155 | p.warning:before { content: '\26A0 Warning! '; } 156 | 157 | .bookkeeping:before { display: block; content: 'Bookkeeping details'; font-weight: bolder; font-style: italic; } 158 | .bookkeeping { font-size: 0.8em; margin: 2em 0; } 159 | .bookkeeping p { margin: 0.5em 2em; display: list-item; list-style: square; } 160 | .bookkeeping dt { margin: 0.5em 2em 0; } 161 | .bookkeeping dd { margin: 0 3em 0.5em; } 162 | 163 | .critical { margin: 1em; border: double thick red; padding: 1em; background: #FFFFCC; } 164 | .critical > :first-child { margin-top: 0; } 165 | 166 | h4 { position: relative; z-index: 3; } 167 | h4 + .element, h4 + div + .element { margin-top: -2.5em; padding-top: 2em; } 168 | .element { background: #EEFFEE; color: black; margin: 0 0 1em 0.15em; padding: 0 1em 0.25em 0.75em; border-left: solid #99FF99 0.25em; position: relative; z-index: 1; } 169 | .element:before { position: absolute; z-index: 2; top: 0; left: -1.15em; height: 2em; width: 0.9em; background: #EEFFEE; content: ' '; border-style: none none solid solid; border-color: #99FF99; border-width: 0.25em; } 170 | .element:not(:hover) > dt > :link, .element:not(:hover) > dt > :visited { color: inherit; text-decoration: none; } 171 | 172 | table.css-property caption { text-align: left; font: inherit; font-weight: bold; } 173 | table.css-property th { font: inherit; font-style: italic; text-align: left; padding-left: 2em; } 174 | 175 | .example { display: block; color: #222222; background: #FCFCFC; border-left: double; margin-left: 2em; padding-left: 1em; } 176 | td > .example:only-child { margin: 0 0 0 0.1em; } 177 | 178 | td.non-rectangular-cell-continuation { border-left-style: hidden; } 179 | td.non-rectangular-cell-indentation { border-top-style: hidden; min-width: 2em; } 180 | 181 | .hide { display: none } 182 | 183 | body.dfnEnabled dfn { cursor: pointer; } 184 | .dfnPanel { 185 | display: inline; 186 | position: absolute; 187 | z-index: 10; 188 | height: auto; 189 | width: auto; 190 | padding: 0.5em 0.75em; 191 | font: small sans-serif, Droid Sans Fallback; 192 | background: #DDDDDD; 193 | color: black; 194 | border: outset 0.2em; 195 | } 196 | .dfnPanel * { margin: 0; padding: 0; font: inherit; text-indent: 0; } 197 | .dfnPanel :link, .dfnPanel :visited { color: black; } 198 | .dfnPanel p { font-weight: bolder; } 199 | .dfnPanel * + p { margin-top: 0.25em; } 200 | .dfnPanel li { list-style-position: inside; } 201 | 202 | @media aural { 203 | h1, h2, h3 { stress: 20; richness: 90 } 204 | .hide { speak: none } 205 | p.copyright { volume: x-soft; speech-rate: x-fast } 206 | dt { pause-before: 20% } 207 | code, pre { speak-punctuation: code } 208 | } 209 | 210 | @media print { 211 | html { font-size: 8pt; } 212 | @page { margin: 1cm 1cm 1cm 1cm; } 213 | @page :left { 214 | @bottom-left { 215 | font: 6pt sans-serif, Droid Sans Fallback; 216 | content: counter(page); 217 | padding-top: 0em; 218 | vertical-align: top; 219 | } 220 | } 221 | @page :right { 222 | @bottom-right { 223 | font: 6pt sans-serif, Droid Sans Fallback; 224 | content: counter(page); 225 | text-align: right; 226 | vertical-align: top; 227 | padding-top: 0em; 228 | } 229 | } 230 | a[href^="#"]::after { font-size: 0.6em; vertical-align: super; padding: 0 0.15em 0 0.15em; content: "p" target-counter(attr(href), page); } 231 | .toc a::after { font: inherit; vertical-align: baseline; padding: 0; content: leader('.') target-counter(attr(href), page); } 232 | pre a[href^="#"]::after, blockquote a[href^="#"]::after { content: ""; padding: 0; } 233 | table { font-size: smaller; } 234 | :link, :visited { text-decoration: none; color: inherit; background: transparent; } 235 | } 236 | 237 | ul.domTree, ul.domTree ul { padding: 0 0 0 1em; margin: 0; } 238 | ul.domTree li { padding: 0; margin: 0; list-style: none; position: relative; } 239 | ul.domTree li li { list-style: none; } 240 | ul.domTree li:first-child::before { position: absolute; top: 0; height: 0.6em; left: -0.75em; width: 0.5em; border-style: none none solid solid; content: ''; border-width: 0.1em; } 241 | ul.domTree li:not(:last-child)::after { position: absolute; top: 0; bottom: -0.6em; left: -0.75em; width: 0.5em; border-style: none none solid solid; content: ''; border-width: 0.1em; } 242 | ul.domTree span { font-style: italic; font-family: serif, Droid Sans Fallback; } 243 | ul.domTree .t1 code { color: purple; font-weight: bold; } 244 | ul.domTree .t2 { font-style: normal; font-family: monospace, Droid Sans Fallback; } 245 | ul.domTree .t2 .name { color: black; font-weight: bold; } 246 | ul.domTree .t2 .value { color: blue; font-weight: normal; } 247 | ul.domTree .t3 code, .domTree .t4 code, .domTree .t5 code { color: gray; } 248 | ul.domTree .t7 code, .domTree .t8 code { color: green; } 249 | ul.domTree .t10 code { color: teal; } 250 | 251 | :target { 252 | background: #ffa; 253 | -moz-box-shadow: 0 0 25px #ffa; 254 | -webkit-box-shadow: 0 0 150px #ffa; 255 | box-shadow: 0 0 25px #ffa; 256 | } 257 | 258 | /*body:not(.statusEnabled) .head, body:not(.dfnEnabled) .head { background: bottom right url(http://hixie.ch/resources/images/spinner) no-repeat; }*/ 259 | 260 | div.compilation:before { 261 | content: "Compilation"; 262 | font: bold small sans-serif; 263 | /*float: left;*/ 264 | position: absolute; 265 | top: -0.9em; 266 | left: -2.5em; 267 | width: 7.5em; 268 | text-align: center; 269 | line-height: 1em; 270 | color: #F8FFDD; 271 | background: #060; 272 | padding: 0.1em; 273 | border: thin solid #999; 274 | /*margin: -1.3em 0 0.3em -2.5em;*/ 275 | } 276 | div.compilation, pre.compilation { 277 | background: #F8FFDD; 278 | padding: 0.5em; 279 | margin: 1em 0; 280 | border: thin solid #999; 281 | position: relative; 282 | } 283 | pre.compilation { 284 | padding-top: 1.5em; 285 | } 286 | div.compilation { color: #060 } 287 | pre.compilation { color: #060 } 288 | -------------------------------------------------------------------------------- /historic/subtypes.graffle: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ActiveLayerIndex 6 | 0 7 | ApplicationVersion 8 | 9 | com.omnigroup.OmniGraffle 10 | 139.16.0.171715 11 | 12 | AutoAdjust 13 | 14 | BackgroundGraphic 15 | 16 | Bounds 17 | {{0, 0}, {576, 733}} 18 | Class 19 | SolidGraphic 20 | ID 21 | 2 22 | Style 23 | 24 | shadow 25 | 26 | Draws 27 | NO 28 | 29 | stroke 30 | 31 | Draws 32 | NO 33 | 34 | 35 | 36 | BaseZoom 37 | 0 38 | CanvasOrigin 39 | {0, 0} 40 | ColumnAlign 41 | 1 42 | ColumnSpacing 43 | 36 44 | CreationDate 45 | 2012-10-24 05:05:27 +0000 46 | Creator 47 | David Herman 48 | DisplayScale 49 | 1 0/72 in = 1.0000 in 50 | GraphDocumentVersion 51 | 8 52 | GraphicsList 53 | 54 | 55 | Class 56 | LineGraphic 57 | Head 58 | 59 | ID 60 | 107 61 | 62 | ID 63 | 109 64 | Points 65 | 66 | {163.5, 443} 67 | {103, 319} 68 | 69 | Style 70 | 71 | stroke 72 | 73 | HeadArrow 74 | FilledArrow 75 | Legacy 76 | 77 | LineType 78 | 1 79 | TailArrow 80 | 0 81 | 82 | 83 | Tail 84 | 85 | ID 86 | 57 87 | Info 88 | 1 89 | 90 | 91 | 92 | Class 93 | LineGraphic 94 | Head 95 | 96 | ID 97 | 107 98 | Info 99 | 1 100 | 101 | ID 102 | 113 103 | Points 104 | 105 | {195.5, 346} 106 | {103, 319} 107 | 108 | Style 109 | 110 | stroke 111 | 112 | HeadArrow 113 | FilledArrow 114 | Legacy 115 | 116 | LineType 117 | 1 118 | TailArrow 119 | 0 120 | 121 | 122 | Tail 123 | 124 | ID 125 | 7 126 | Info 127 | 1 128 | 129 | 130 | 131 | Bounds 132 | {{68, 249}, {70, 70}} 133 | Class 134 | ShapedGraphic 135 | ID 136 | 107 137 | Magnets 138 | 139 | {0, 0.5} 140 | 141 | Shape 142 | Rectangle 143 | Style 144 | 145 | fill 146 | 147 | Color 148 | 149 | b 150 | 0.901961 151 | g 152 | 0.901961 153 | r 154 | 0.901961 155 | 156 | 157 | 158 | Text 159 | 160 | Text 161 | {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340 162 | \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Menlo-Regular;} 163 | {\colortbl;\red255\green255\blue255;} 164 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc 165 | 166 | \f0\fs24 \cf0 doublish} 167 | 168 | 169 | 170 | Bounds 171 | {{438, 346}, {70, 70}} 172 | Class 173 | ShapedGraphic 174 | ID 175 | 104 176 | Magnets 177 | 178 | {0, -0.5} 179 | 180 | Shape 181 | Rectangle 182 | Style 183 | 184 | fill 185 | 186 | Color 187 | 188 | b 189 | 0.901961 190 | g 191 | 0.901961 192 | r 193 | 0.901961 194 | 195 | 196 | 197 | Text 198 | 199 | Text 200 | {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340 201 | \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Menlo-Regular;} 202 | {\colortbl;\red255\green255\blue255;} 203 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc 204 | 205 | \f0\fs24 \cf0 void} 206 | 207 | 208 | 209 | Class 210 | LineGraphic 211 | Head 212 | 213 | ID 214 | 60 215 | Position 216 | 0.95704245567321777 217 | 218 | ID 219 | 103 220 | Points 221 | 222 | {328.5, 443} 223 | {294.29327151179314, 417.15985208749771} 224 | 225 | Style 226 | 227 | stroke 228 | 229 | HeadArrow 230 | FilledArrow 231 | Legacy 232 | 233 | LineType 234 | 1 235 | TailArrow 236 | 0 237 | 238 | 239 | Tail 240 | 241 | ID 242 | 3 243 | Info 244 | 1 245 | 246 | 247 | 248 | Class 249 | LineGraphic 250 | Head 251 | 252 | ID 253 | 4 254 | Info 255 | 2 256 | 257 | ID 258 | 93 259 | Points 260 | 261 | {381.5, 535} 262 | {434.5, 513} 263 | 264 | Style 265 | 266 | stroke 267 | 268 | HeadArrow 269 | FilledArrow 270 | Legacy 271 | 272 | LineType 273 | 1 274 | TailArrow 275 | 0 276 | 277 | 278 | Tail 279 | 280 | ID 281 | 91 282 | 283 | 284 | 285 | Class 286 | LineGraphic 287 | Head 288 | 289 | ID 290 | 3 291 | Info 292 | 2 293 | 294 | ID 295 | 92 296 | Points 297 | 298 | {381.5, 535} 299 | {328.5, 513} 300 | 301 | Style 302 | 303 | stroke 304 | 305 | HeadArrow 306 | FilledArrow 307 | Legacy 308 | 309 | LineType 310 | 1 311 | TailArrow 312 | 0 313 | 314 | 315 | Tail 316 | 317 | ID 318 | 91 319 | Info 320 | 1 321 | 322 | 323 | 324 | Bounds 325 | {{346.5, 535}, {70, 70}} 326 | Class 327 | ShapedGraphic 328 | ID 329 | 91 330 | Magnets 331 | 332 | {0, -0.5} 333 | {0, 0.5} 334 | 335 | Shape 336 | Rectangle 337 | Text 338 | 339 | Text 340 | {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340 341 | \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Menlo-Regular;} 342 | {\colortbl;\red255\green255\blue255;} 343 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc 344 | 345 | \f0\fs24 \cf0 fixnum} 346 | 347 | 348 | 349 | Class 350 | LineGraphic 351 | Head 352 | 353 | ID 354 | 71 355 | 356 | ID 357 | 112 358 | Points 359 | 360 | {195.5, 346} 361 | {288, 319} 362 | 363 | Style 364 | 365 | stroke 366 | 367 | HeadArrow 368 | FilledArrow 369 | Legacy 370 | 371 | LineType 372 | 1 373 | TailArrow 374 | 0 375 | 376 | 377 | Tail 378 | 379 | ID 380 | 7 381 | Info 382 | 1 383 | 384 | 385 | 386 | Class 387 | LineGraphic 388 | Head 389 | 390 | ID 391 | 71 392 | Info 393 | 1 394 | 395 | ID 396 | 72 397 | Points 398 | 399 | {380.5, 346} 400 | {288, 319} 401 | 402 | Style 403 | 404 | stroke 405 | 406 | HeadArrow 407 | FilledArrow 408 | Legacy 409 | 410 | LineType 411 | 1 412 | TailArrow 413 | 0 414 | 415 | 416 | Tail 417 | 418 | ID 419 | 5 420 | 421 | 422 | 423 | Bounds 424 | {{253, 249}, {70, 70}} 425 | Class 426 | ShapedGraphic 427 | ID 428 | 71 429 | Magnets 430 | 431 | {0, 0.5} 432 | 433 | Shape 434 | Rectangle 435 | Style 436 | 437 | fill 438 | 439 | Color 440 | 441 | b 442 | 0.901961 443 | g 444 | 0.901961 445 | r 446 | 0.901961 447 | 448 | 449 | 450 | Text 451 | 452 | Text 453 | {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340 454 | \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Menlo-Regular;} 455 | {\colortbl;\red255\green255\blue255;} 456 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc 457 | 458 | \f0\fs24 \cf0 intish} 459 | 460 | 461 | 462 | Class 463 | LineGraphic 464 | Head 465 | 466 | ID 467 | 11 468 | 469 | ID 470 | 60 471 | Points 472 | 473 | {434.5, 443} 474 | {288, 416} 475 | 476 | Style 477 | 478 | stroke 479 | 480 | HeadArrow 481 | FilledArrow 482 | Legacy 483 | 484 | LineType 485 | 1 486 | TailArrow 487 | 0 488 | 489 | 490 | Tail 491 | 492 | ID 493 | 4 494 | Info 495 | 1 496 | 497 | 498 | 499 | Class 500 | LineGraphic 501 | Head 502 | 503 | ID 504 | 11 505 | 506 | ID 507 | 58 508 | Points 509 | 510 | {163.5, 443} 511 | {288, 416} 512 | 513 | Style 514 | 515 | stroke 516 | 517 | HeadArrow 518 | FilledArrow 519 | Legacy 520 | 521 | LineType 522 | 1 523 | TailArrow 524 | 0 525 | 526 | 527 | Tail 528 | 529 | ID 530 | 57 531 | Info 532 | 1 533 | 534 | 535 | 536 | Bounds 537 | {{128.5, 443}, {70, 70}} 538 | Class 539 | ShapedGraphic 540 | ID 541 | 57 542 | Magnets 543 | 544 | {0, -0.5} 545 | 546 | Shape 547 | Rectangle 548 | Text 549 | 550 | Text 551 | {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340 552 | \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Menlo-Regular;} 553 | {\colortbl;\red255\green255\blue255;} 554 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc 555 | 556 | \f0\fs24 \cf0 double} 557 | 558 | 559 | 560 | Class 561 | LineGraphic 562 | Head 563 | 564 | ID 565 | 5 566 | 567 | ID 568 | 20 569 | Points 570 | 571 | {434.5, 443} 572 | {380.5, 416} 573 | 574 | Style 575 | 576 | stroke 577 | 578 | HeadArrow 579 | FilledArrow 580 | Legacy 581 | 582 | LineType 583 | 1 584 | TailArrow 585 | 0 586 | 587 | 588 | Tail 589 | 590 | ID 591 | 4 592 | Info 593 | 1 594 | 595 | 596 | 597 | Class 598 | LineGraphic 599 | Head 600 | 601 | ID 602 | 5 603 | Info 604 | 2 605 | 606 | ID 607 | 18 608 | Points 609 | 610 | {328.5, 443} 611 | {380.5, 416} 612 | 613 | Style 614 | 615 | stroke 616 | 617 | HeadArrow 618 | FilledArrow 619 | Legacy 620 | 621 | LineType 622 | 1 623 | TailArrow 624 | 0 625 | 626 | 627 | Tail 628 | 629 | ID 630 | 3 631 | Info 632 | 1 633 | 634 | 635 | 636 | Bounds 637 | {{253, 346}, {70, 70}} 638 | Class 639 | ShapedGraphic 640 | ID 641 | 11 642 | Magnets 643 | 644 | {0, 0.5} 645 | {0, -0.5} 646 | 647 | Shape 648 | Rectangle 649 | Text 650 | 651 | Text 652 | {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340 653 | \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Menlo-Regular;} 654 | {\colortbl;\red255\green255\blue255;} 655 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc 656 | 657 | \f0\fs24 \cf0 extern} 658 | 659 | 660 | 661 | Bounds 662 | {{160.5, 346}, {70, 70}} 663 | Class 664 | ShapedGraphic 665 | ID 666 | 7 667 | Magnets 668 | 669 | {0, -0.5} 670 | {0, 0.49999999999999956} 671 | 672 | Shape 673 | Rectangle 674 | Style 675 | 676 | fill 677 | 678 | Color 679 | 680 | b 681 | 0.901961 682 | g 683 | 0.901961 684 | r 685 | 0.901961 686 | 687 | 688 | 689 | Text 690 | 691 | Text 692 | {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340 693 | \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Menlo-Regular;} 694 | {\colortbl;\red255\green255\blue255;} 695 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc 696 | 697 | \f0\fs24 \cf0 unknown} 698 | 699 | 700 | 701 | Bounds 702 | {{345.5, 346}, {70, 70}} 703 | Class 704 | ShapedGraphic 705 | ID 706 | 5 707 | Magnets 708 | 709 | {0, -0.5} 710 | {0, 0.5} 711 | 712 | Shape 713 | Rectangle 714 | Style 715 | 716 | fill 717 | 718 | Color 719 | 720 | b 721 | 0.901961 722 | g 723 | 0.901961 724 | r 725 | 0.901961 726 | 727 | 728 | 729 | Text 730 | 731 | Text 732 | {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340 733 | \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Menlo-Regular;} 734 | {\colortbl;\red255\green255\blue255;} 735 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc 736 | 737 | \f0\fs24 \cf0 int} 738 | 739 | 740 | 741 | Bounds 742 | {{399.5, 443}, {70, 70}} 743 | Class 744 | ShapedGraphic 745 | ID 746 | 4 747 | Magnets 748 | 749 | {0, -0.5} 750 | {0, 0.5} 751 | 752 | Shape 753 | Rectangle 754 | Text 755 | 756 | Text 757 | {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340 758 | \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Menlo-Regular;} 759 | {\colortbl;\red255\green255\blue255;} 760 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc 761 | 762 | \f0\fs24 \cf0 unsigned} 763 | 764 | 765 | 766 | Bounds 767 | {{293.5, 443}, {70, 70}} 768 | Class 769 | ShapedGraphic 770 | ID 771 | 3 772 | Magnets 773 | 774 | {0, -0.5} 775 | {0, 0.5} 776 | 777 | Shape 778 | Rectangle 779 | Text 780 | 781 | Text 782 | {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340 783 | \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Menlo-Regular;} 784 | {\colortbl;\red255\green255\blue255;} 785 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc 786 | 787 | \f0\fs24 \cf0 signed} 788 | 789 | 790 | 791 | GridInfo 792 | 793 | GuidesLocked 794 | NO 795 | GuidesVisible 796 | YES 797 | HPages 798 | 1 799 | ImageCounter 800 | 1 801 | KeepToScale 802 | 803 | Layers 804 | 805 | 806 | Lock 807 | NO 808 | Name 809 | Layer 1 810 | Print 811 | YES 812 | View 813 | YES 814 | 815 | 816 | LayoutInfo 817 | 818 | Animate 819 | NO 820 | circoMinDist 821 | 18 822 | circoSeparation 823 | 0.0 824 | layoutEngine 825 | dot 826 | neatoSeparation 827 | 0.0 828 | twopiSeparation 829 | 0.0 830 | 831 | LinksVisible 832 | NO 833 | MagnetsVisible 834 | NO 835 | MasterSheets 836 | 837 | ModificationDate 838 | 2013-01-15 05:36:58 +0000 839 | Modifier 840 | David Herman 841 | NotesVisible 842 | NO 843 | Orientation 844 | 2 845 | OriginVisible 846 | NO 847 | PageBreaks 848 | YES 849 | PrintInfo 850 | 851 | NSBottomMargin 852 | 853 | float 854 | 41 855 | 856 | NSHorizonalPagination 857 | 858 | coded 859 | BAtzdHJlYW10eXBlZIHoA4QBQISEhAhOU051bWJlcgCEhAdOU1ZhbHVlAISECE5TT2JqZWN0AIWEASqEhAFxlwCG 860 | 861 | NSLeftMargin 862 | 863 | float 864 | 18 865 | 866 | NSPaperSize 867 | 868 | size 869 | {612, 792} 870 | 871 | NSPrintReverseOrientation 872 | 873 | int 874 | 0 875 | 876 | NSPrinter 877 | 878 | coded 879 | BAtzdHJlYW10eXBlZIHoA4QBQISEhAlOU1ByaW50ZXIAhIQITlNPYmplY3QAhZKEhIQITlNTdHJpbmcBlIQBKw1NVFYyLUh5cGVyaW9uhoY= 880 | 881 | NSPrinterName 882 | 883 | string 884 | MTV2-Hyperion 885 | 886 | NSRightMargin 887 | 888 | float 889 | 18 890 | 891 | NSTopMargin 892 | 893 | float 894 | 18 895 | 896 | 897 | PrintOnePage 898 | 899 | ReadOnly 900 | NO 901 | RowAlign 902 | 1 903 | RowSpacing 904 | 36 905 | SheetTitle 906 | Canvas 1 907 | SmartAlignmentGuidesActive 908 | YES 909 | SmartDistanceGuidesActive 910 | YES 911 | UniqueID 912 | 1 913 | UseEntirePage 914 | 915 | VPages 916 | 1 917 | WindowInfo 918 | 919 | CurrentSheet 920 | 0 921 | ExpandedCanvases 922 | 923 | 924 | name 925 | Canvas 1 926 | 927 | 928 | Frame 929 | {{40, 73}, {1005, 673}} 930 | ListView 931 | 932 | OutlineWidth 933 | 142 934 | RightSidebar 935 | 936 | ShowRuler 937 | 938 | Sidebar 939 | 940 | SidebarWidth 941 | 120 942 | VisibleRegion 943 | {{-147, 138}, {870, 534}} 944 | Zoom 945 | 1 946 | ZoomValues 947 | 948 | 949 | Canvas 1 950 | 1 951 | 0.5 952 | 953 | 954 | 955 | 956 | 957 | -------------------------------------------------------------------------------- /lib/validate.js: -------------------------------------------------------------------------------- 1 | var esprima = require('esprima'); 2 | var dict = require('dict'); 3 | var match = require('pattern-match'); 4 | var array = require('array-extended'); 5 | var env = require('./env'); 6 | var ty = require('./types'); 7 | var fail = require('./fail'); 8 | var tables = require('./tables'); 9 | var Report = require('./report'); 10 | 11 | // ----------------------------------------------------------------------------- 12 | // utilities 13 | // ----------------------------------------------------------------------------- 14 | 15 | var log = Math.log; 16 | 17 | var LOG2 = log(2); 18 | 19 | // (number) -> number 20 | function log2(x) { 21 | return log(x) / LOG2; 22 | } 23 | 24 | // (number) -> boolean 25 | function powerOf2(x) { 26 | return (x & (x - 1)) === 0; 27 | } 28 | 29 | // (Statement) -> boolean 30 | function nonEmpty(s) { 31 | return s.type !== 'EmptyStatement'; 32 | } 33 | 34 | // ([AST], [{ name: string, type: string }]) -> { string: AST, ... } 35 | function split(nodes, filters) { 36 | var result = {}; 37 | var nNodes = nodes.length, nFilters = filters.length; 38 | var iNode = 0; 39 | for (var iFilter = 0; iFilter < nFilters; iFilter++) { 40 | var filter = filters[iFilter]; 41 | var next = []; 42 | while (iNode < nNodes && nodes[iNode].type === filter.type) { 43 | next.push(nodes[iNode]); 44 | iNode++; 45 | } 46 | result[filter.name] = next; 47 | } 48 | return result; 49 | } 50 | 51 | // (string) -> boolean 52 | function hasDot(s) { 53 | return s.indexOf(".") !== -1; 54 | } 55 | 56 | // (string) -> boolean 57 | function dotless(s) { 58 | return !hasDot(s); 59 | } 60 | 61 | // (Expression, Expression) -> [Expression] 62 | function flattenAdditive(left, right) { 63 | var result = []; 64 | 65 | // Since .pop is faster than .shift we'll pop tasks from the end. 66 | var todo = [right, left]; 67 | while (todo.length > 0) { 68 | match(todo.pop(), function(when) { 69 | when({ 70 | type: 'BinaryExpression', 71 | operator: match.some('+', '-'), 72 | left: match.var('left'), 73 | right: match.var('right') 74 | }, function(vars) { 75 | todo.push(vars.right, vars.left); 76 | }); 77 | 78 | when(match.var('operand'), function(vars) { 79 | result.push(vars.operand); 80 | }); 81 | }); 82 | } 83 | 84 | return result; 85 | } 86 | 87 | // ----------------------------------------------------------------------------- 88 | // main methods 89 | // ----------------------------------------------------------------------------- 90 | 91 | function Validator() { 92 | this._roots = { 93 | stdlib: null, 94 | foreign: null, 95 | heap: null 96 | }; 97 | this._globals = env(this); 98 | this._locals = null; 99 | this._src = null; 100 | this._result = null; 101 | } 102 | 103 | var Vp = Validator.prototype; 104 | 105 | // (AST, string[, Function]) -> any 106 | // Delegates to pattern-match library, converting MatchErrors to validation errors. 107 | Vp.match = function(node, desc, body) { 108 | var self = this; 109 | if (body) { 110 | try { 111 | return match(node, body, this); 112 | } catch (e) { 113 | failWithLocation(e); 114 | } 115 | } 116 | 117 | var delayed = match(node); 118 | 119 | return { 120 | when: function(pattern, template) { 121 | try { 122 | return delayed.when(pattern, template, self); 123 | } catch (e) { 124 | failWithLocation(e); 125 | } 126 | } 127 | }; 128 | 129 | function failWithLocation(e) { 130 | if (e instanceof match.MatchError) { 131 | var loc = e && e.actual ? e.actual.loc : null; 132 | self.fail("invalid " + desc, loc); 133 | } else { 134 | throw e; 135 | } 136 | } 137 | }; 138 | 139 | // (string) -> Report 140 | Vp.validate = function validate(src) { 141 | this._src = src; 142 | var module = esprima.parse("(" + src + ")", { raw: true, loc: true }).body[0].expression; 143 | var vars = this.match(module, "asm.js module declaration").when({ 144 | type: 'FunctionExpression', 145 | id: match.var('id', { type: 'Identifier' }), 146 | params: match.var('params', { length: match.range(0, 4) }), 147 | body: { loc: match.var('loc'), body: match.var('body') } 148 | }); 149 | return this.module(vars.params, vars.body.filter(nonEmpty), vars.loc); 150 | }; 151 | 152 | // (string, Loc) -/-> 153 | Vp.fail = function fail_(msg, loc) { 154 | fail(msg, this._src, loc); 155 | }; 156 | 157 | // ([Statement], Loc) -> { 158 | // imports: [VariableDeclaration], 159 | // functions: [FunctionDeclaration], 160 | // tables: [VariableDeclaration], 161 | // exports: Expression 162 | // } 163 | Vp.splitModule = function splitModule(body, loc) { 164 | var sections = split(body, [ 165 | { name: 'directive', type: 'ExpressionStatement' }, 166 | { name: 'globals', type: 'VariableDeclaration' }, 167 | { name: 'functions', type: 'FunctionDeclaration' }, 168 | { name: 'tables', type: 'VariableDeclaration' }, 169 | { name: 'exports', type: 'ReturnStatement' } 170 | ]); 171 | if (sections.directive.length !== 1) 172 | this.fail("expected single \"use asm\" directive, got " + sections.directive.length + " ExpressionStatement nodes", loc); 173 | this.match(sections.directive, "\"use asm\" directive").when([{ 174 | type: 'ExpressionStatement', 175 | expression: { type: 'Literal', value: "use asm" } 176 | }]); 177 | if (sections.exports.length !== 1) 178 | this.fail("expected single exports declaration, got " + sections.exports.length + " ReturnStatement nodes", loc); 179 | return { 180 | globals: sections.globals, 181 | functions: sections.functions, 182 | tables: sections.tables, 183 | exports: sections.exports[0].argument 184 | }; 185 | }; 186 | 187 | // (Identifier, Statement) -> Type 188 | Vp.paramType = function paramType(id, stmt) { 189 | return this.match(stmt, "parameter annotation").when({ 190 | type: 'ExpressionStatement', 191 | expression: { 192 | type: 'AssignmentExpression', 193 | left: { type: 'Identifier', name: id.name }, 194 | right: match.var('right') 195 | } 196 | }, function(vars) { 197 | return this.match(vars.right, "parameter annotation type", function(when) { 198 | when({ 199 | type: 'UnaryExpression', 200 | operator: '+', 201 | argument: { type: 'Identifier', name: id.name } 202 | }, function() { 203 | return ty.Double; 204 | }); 205 | 206 | when({ 207 | type: 'BinaryExpression', 208 | operator: '|', 209 | left: { type: 'Identifier', name: id.name }, 210 | right: { type: 'Literal', value: 0 } 211 | }, function() { 212 | return ty.Int; 213 | }); 214 | }); 215 | }); 216 | }; 217 | 218 | // (Statement) -> Type 219 | Vp.returnType = function returnType(stmt) { 220 | if (stmt.type !== 'ReturnStatement') 221 | return ty.Void; 222 | 223 | return this.match(stmt.argument, "return type annotation", function(when) { 224 | when(null, function() { 225 | return ty.Void; 226 | }); 227 | 228 | when({ 229 | type: 'UnaryExpression', 230 | operator: '+' 231 | }, function() { 232 | return ty.Double; 233 | }); 234 | 235 | when({ 236 | type: 'BinaryExpression', 237 | operator: '|', 238 | right: { type: 'Literal', value: 0 } 239 | }, function() { 240 | return ty.Signed; 241 | }); 242 | 243 | when({ 244 | type: 'Literal', 245 | value: match.number, 246 | raw: hasDot 247 | }, function() { 248 | return ty.Double; 249 | }); 250 | 251 | when({ 252 | type: 'Literal', 253 | value: match.all(match.number, 254 | match.range(-0x80000000, 0x100000000)), 255 | raw: dotless 256 | }, function() { 257 | return ty.Signed; 258 | }); 259 | }); 260 | }; 261 | 262 | // (FunctionDeclaration) -> Type 263 | Vp.functionType = function functionType(funDecl) { 264 | var params = funDecl.params; 265 | var n = params.length; 266 | var body = funDecl.body.body.filter(nonEmpty); 267 | if (body.length < n) 268 | this.fail("not enough annotations for parameters to " + funDecl.id.name, funDecl.body.loc); 269 | var paramTypes = array.zip(params, body.slice(0, n)).map(function(pair) { 270 | return this.paramType(pair[0], pair[1]); 271 | }, this); 272 | var returnType = body.length > 0 ? this.returnType(body[body.length - 1]) : ty.Void; 273 | return ty.Arrow(paramTypes, returnType); 274 | }; 275 | 276 | // (string, Loc) -> Type 277 | Vp.lookup = function lookup(x, loc) { 278 | if (this._locals) { 279 | var t = this._locals.lookup(x); 280 | if (t) 281 | return t; 282 | } 283 | 284 | var mt = this._globals.lookup(x); 285 | if (!mt) 286 | this.fail("unbound variable " + x, loc); 287 | 288 | return mt.type; 289 | }; 290 | 291 | // (string, Loc) -> ValueType 292 | Vp.lookupValueType = function lookupValueType(x, loc) { 293 | var t = this.lookup(x, loc); 294 | if (!(t instanceof ty.ValueType)) 295 | this.fail("expected value type, got " + t, loc); 296 | return t; 297 | }; 298 | 299 | // (string, Expression, Loc) -> { mutable: boolean, type: Type } 300 | Vp.global = function global(x, rhs, loc) { 301 | if (!rhs) 302 | this.fail("global variable missing initializer expression", loc); 303 | 304 | return this.match(rhs, "global declaration", function(when) { 305 | when({ 306 | type: 'Literal', 307 | value: match.var('f', match.number), 308 | raw: match.var('src', hasDot) 309 | }, function(vars) { 310 | return { mutable: true, type: ty.Double }; 311 | }, this); 312 | 313 | when({ 314 | type: 'Literal', 315 | value: match.var('n', match.all(match.integer, 316 | match.range(-0x80000000, 0x100000000))), 317 | raw: match.var('src') 318 | }, function(vars) { 319 | return { mutable: true, type: ty.Int }; 320 | }, this); 321 | 322 | when({ 323 | type: 'MemberExpression', 324 | object: { 325 | type: 'MemberExpression', 326 | object: { 327 | type: 'Identifier', 328 | name: this._roots.stdlib 329 | }, 330 | property: { 331 | type: 'Identifier', 332 | name: 'Math' 333 | } 334 | }, 335 | property: { 336 | type: 'Identifier', 337 | name: match.var('x') 338 | } 339 | }, function(vars) { 340 | if (!tables.STDLIB_MATH_TYPES.has(vars.x)) 341 | this.fail("unknown library: Math." + vars.x, loc); 342 | return { mutable: false, type: tables.STDLIB_MATH_TYPES.get(vars.x) }; 343 | }, this); 344 | 345 | when({ 346 | type: 'MemberExpression', 347 | object: { 348 | type: 'Identifier', 349 | name: this._roots.stdlib 350 | }, 351 | property: match.var('x') 352 | }, function(vars) { 353 | if (!tables.STDLIB_TYPES.has(vars.x)) 354 | this.fail("unknown library: " + vars.x, loc); 355 | return { mutable: false, type: tables.STDLIB_TYPES.get(vars.x) }; 356 | }, this); 357 | 358 | when({ 359 | type: 'MemberExpression', 360 | object: { 361 | type: 'Identifier', 362 | name: this._roots.foreign 363 | }, 364 | property: match.var('x') 365 | }, function(vars) { 366 | return { mutable: false, type: ty.Function }; 367 | }, this); 368 | 369 | when({ 370 | type: 'BinaryExpression', 371 | operator: '|', 372 | left: { 373 | type: 'MemberExpression', 374 | object: { 375 | type: 'Identifier', 376 | name: this._roots.foreign 377 | }, 378 | property: match.var('x') 379 | }, 380 | right: { 381 | type: 'Literal', 382 | value: 0 383 | } 384 | }, function(vars) { 385 | return { mutable: false, type: ty.Int }; 386 | }, this); 387 | 388 | when({ 389 | type: 'UnaryExpression', 390 | operator: '+', 391 | argument: { 392 | type: 'MemberExpression', 393 | object: { 394 | type: 'Identifier', 395 | name: this._roots.foreign 396 | }, 397 | property: match.var('x') 398 | } 399 | }, function(vars) { 400 | return { mutable: false, type: ty.Double }; 401 | }, this); 402 | 403 | when({ 404 | type: 'NewExpression', 405 | callee: { 406 | type: 'MemberExpression', 407 | object: { 408 | type: 'Identifier', 409 | name: this._roots.stdlib 410 | }, 411 | property: { 412 | type: 'Identifier', 413 | name: match.var('view'), 414 | loc: match.var('loc') 415 | } 416 | }, 417 | arguments: match.var('args', [{ type: 'Identifier', name: this._roots.heap }]) 418 | }, function(vars) { 419 | if (vars.args.length !== 1) 420 | this.fail("heap view constructor expects 1 argument, got " + vars.args.length, vars.args[1].loc); 421 | if (!tables.HEAP_VIEW_TYPES.has(vars.view)) 422 | this.fail("unknown typed array type: " + vars.view, vars.loc); 423 | return { mutable: false, type: tables.HEAP_VIEW_TYPES.get(vars.view) }; 424 | }, this); 425 | }, this); 426 | }; 427 | 428 | // (string, Expression, Loc) -> Table 429 | Vp.table = function table(x, rhs, loc) { 430 | this.match(rhs, "function table").when({ 431 | type: 'ArrayExpression', 432 | elements: match.var('elements') 433 | }, function(vars) { 434 | var fs = vars.elements.map(function(element) { 435 | return this.match(element, "function table entry").when(match.var('f', { type: 'Identifier' }), 436 | function(vars) { return vars.f; }, 437 | this); 438 | }, this); 439 | 440 | if (fs.length === 0) 441 | this.fail("empty function table", loc); 442 | if (!powerOf2(fs.length)) 443 | this.fail("function table length must be a power of 2, got " + fs.length, loc); 444 | 445 | var fts = fs.map(function(f) { 446 | var ft = this.lookup(f.name, f.loc); 447 | if (!(ft instanceof ty.Arrow)) 448 | this.fail("non-function " + f.name + " in function table", f.loc); 449 | return ft; 450 | }, this); 451 | 452 | var ft = fts[0]; 453 | 454 | for (var i = 1, n = fts.length; i < n; i++) { 455 | if (!ft.equals(fts[i])) 456 | this.fail("unexpected function type " + fs[i].name + " : " + fts[i] + " in function table", fs[i].loc); 457 | } 458 | 459 | return new ty.Table(ft, fs.length); 460 | }); 461 | }; 462 | 463 | // (string, Expression) -> Type 464 | Vp.local = function local(x, rhs) { 465 | return this.match(rhs, "declaration of local " + x, function(when) { 466 | when({ 467 | type: 'Literal', 468 | value: match.number, 469 | raw: hasDot 470 | }, function(vars) { 471 | return ty.Double; 472 | }, this); 473 | 474 | when({ 475 | type: 'Literal', 476 | value: match.all(match.integer, match.range(-0x80000000, 0x100000000)) 477 | }, function(vars) { 478 | return ty.Int; 479 | }, this); 480 | }); 481 | }; 482 | 483 | // (FunctionDeclaration) -> void 484 | Vp.function = function function_(decl) { 485 | var f = decl.id.name; 486 | var ft = this._globals.lookup(f).type; 487 | var params = decl.params.map(function(id) { return id.name; }); 488 | var paramTypes = ft.params; 489 | var resultType = ft.result; 490 | var body = decl.body.body.filter(nonEmpty); 491 | 492 | try { 493 | this._locals = env(this); 494 | this._result = resultType; 495 | 496 | // Bind the parameters. 497 | params.forEach(function(x, i) { 498 | this._locals.bind(x, paramTypes[i]); 499 | }, this); 500 | 501 | // Bind the locals. 502 | var i = params.length, n = body.length; 503 | while (i < n && body[i].type === 'VariableDeclaration') { 504 | body[i].declarations.forEach(function(dtor) { 505 | var x = dtor.id.name; 506 | this._locals.bind(x, this.local(x, dtor.init)); 507 | }, this); 508 | i++; 509 | } 510 | 511 | // Check the body. 512 | this.statements(body.slice(i)); 513 | } finally { 514 | this._locals = null; 515 | this._result = null; 516 | } 517 | }; 518 | 519 | // (Expression) -> { type: 'single', export: { name: string, type: Arrow } } 520 | // | { type: 'multiple', exports: dict<{ name: string, type: Arrow }> } 521 | Vp.exports = function exports(expr) { 522 | return this.match(expr, "exports declaration", function(when) { 523 | when({ 524 | type: 'Identifier', 525 | name: match.var('f'), 526 | loc: match.var('loc') 527 | }, function(vars) { 528 | var t = this.lookup(vars.f, vars.loc); 529 | if (!(t instanceof ty.Arrow)) 530 | this.fail("expected exported function, got definition of type " + t, vars.loc); 531 | return { type: 'single', export: { name: vars.f, type: t } }; 532 | }, this); 533 | 534 | when({ 535 | type: 'ObjectExpression', 536 | properties: match.var('props') 537 | }, function(vars) { 538 | var table = dict(); 539 | 540 | var self = this; 541 | 542 | function add(internal, external, loc) { 543 | var t = self.lookup(internal, loc); 544 | if (!(t instanceof ty.Arrow)) 545 | self.fail("expected exported function, got definition of type " + t, loc); 546 | table.set(external, { 547 | name: internal, 548 | type: t 549 | }); 550 | } 551 | 552 | vars.props.forEach(function(prop) { 553 | return this.match(prop, "export declaration", function(when) { 554 | when({ 555 | key: { 556 | type: 'Literal', 557 | value: match.var('external', match.string) 558 | }, 559 | value: { 560 | type: 'Identifier', 561 | name: match.var('internal'), 562 | loc: match.var('loc') 563 | }, 564 | kind: 'init' 565 | }, function(vars) { 566 | add(vars.internal, vars.external, vars.loc); 567 | }, this); 568 | 569 | when({ 570 | key: { 571 | type: 'Identifier', 572 | name: match.var('external') 573 | }, 574 | value: { 575 | type: 'Identifier', 576 | name: match.var('internal'), 577 | loc: match.var('loc') 578 | }, 579 | kind: 'init' 580 | }, function(vars) { 581 | add(vars.internal, vars.external, vars.loc); 582 | }, this); 583 | }); 584 | }, this); 585 | 586 | return { type: 'multiple', exports: table }; 587 | }, this); 588 | }); 589 | }; 590 | 591 | // ([Identifier], [Statement], Loc) -> Report 592 | Vp.module = function module(params, body, loc) { 593 | var sections = this.splitModule(body, loc); 594 | 595 | // Bind module parameters. 596 | params.forEach(function(id, i) { 597 | this._roots[tables.ROOT_NAMES[i]] = id.name; 598 | this._globals.bind(id.name, { mutable: false, type: ty.ModuleParameter }, id.loc); 599 | }, this); 600 | 601 | // Bind and check globals. 602 | sections.globals.forEach(function(varDecl) { 603 | varDecl.declarations.forEach(function(decl) { 604 | var x = decl.id.name; 605 | var mt = this.global(x, decl.init, decl.loc); 606 | this._globals.bind(x, mt, decl.id.loc); 607 | }, this); 608 | }, this); 609 | 610 | // Bind function types. 611 | sections.functions.forEach(function(funDecl) { 612 | var id = funDecl.id, f = id.name; 613 | var t = this.functionType(funDecl); 614 | this._globals.bind(f, { mutable: false, type: t }, id.loc); 615 | }, this); 616 | 617 | // Bind and check function tables. 618 | sections.tables.forEach(function(varDecl) { 619 | varDecl.declarations.forEach(function(decl) { 620 | var x = decl.id.name; 621 | var t = this.table(x, decl.init, decl.loc); 622 | this._globals.bind(x, { mutable: false, type: t }, decl.id.loc); 623 | }, this); 624 | }, this); 625 | 626 | // Check functions. 627 | sections.functions.forEach(this.function, this); 628 | 629 | // Check exports. 630 | var exports = this.exports(sections.exports); 631 | 632 | return new Report(this._globals, exports); 633 | }; 634 | 635 | // ----------------------------------------------------------------------------- 636 | // statements 637 | // ----------------------------------------------------------------------------- 638 | 639 | // ([Statement]) -> void 640 | Vp.statements = function statements(ss) { 641 | ss.forEach(this.statement, this); 642 | }; 643 | 644 | // (Statement) -> void 645 | Vp.statement = function statement(s) { 646 | switch (s.type) { 647 | case 'BlockStatement': 648 | return this.statements(s.body); 649 | 650 | case 'ExpressionStatement': 651 | return (s.expression.type === 'CallExpression') 652 | ? this.call(s.expression, ty.Void) 653 | : this.expression(s.expression); 654 | 655 | case 'IfStatement': 656 | return this.ifStatement(s.test, s.consequent, s.alternate); 657 | 658 | case 'ReturnStatement': 659 | return this.returnStatement(s.argument, s.loc); 660 | 661 | case 'WhileStatement': 662 | return this.whileStatement(s.test, s.body); 663 | 664 | case 'DoWhileStatement': 665 | return this.doWhileStatement(s.body, s.test); 666 | 667 | case 'ForStatement': 668 | return this.forStatement(s.init, s.test, s.update, s.body, s.loc); 669 | 670 | case 'BreakStatement': 671 | case 'ContinueStatement': 672 | case "EmptyStatement": 673 | return; 674 | 675 | case 'LabeledStatement': 676 | return this.statement(s.body); 677 | 678 | case 'SwitchStatement': 679 | return this.switchStatement(s.discriminant, s.cases); 680 | 681 | default: 682 | this.fail("illegal " + s.type + " node", s.loc); 683 | } 684 | }; 685 | 686 | // (Type, Type, string, Loc) -> void 687 | Vp.checkSubtype = function checkSubtype(actual, expected, msg, loc) { 688 | if (!(actual instanceof ty.ValueType) || !actual.subtype(expected)) 689 | this.fail("expected " + expected + " in " + msg + ", got " + actual, loc); 690 | }; 691 | 692 | Vp.checkSameType = function checkSameType(actual, expected, msg, loc) { 693 | if (!(actual instanceof ty.ValueType) || !actual.equals(expected)) 694 | this.fail("expected " + expected + " in " + msg + ", got " + actual, loc); 695 | }; 696 | 697 | // ([Type], Type, string, [Loc], Loc) -> Type 698 | Vp.checkArguments = function checkArguments(ts, expected, msg, locs, loc) { 699 | if (expected instanceof ty.Arrow) { 700 | ts.forEach(function(t, i) { 701 | this.checkSubtype(t, expected.params[i], "argument", locs[i]); 702 | }, this); 703 | return expected.result; 704 | } else if (expected instanceof ty.Overloaded) { 705 | var t; 706 | expected.alts.some(function(alt) { 707 | var ss = alt.params; 708 | if (ss.length === ts.length && ss.every(function(s, i) { return ts[i].subtype(s) })) { 709 | t = alt.result; 710 | return true; 711 | } 712 | }, this); 713 | if (!t) 714 | this.fail(msg + ": argument types do not match any overloading", loc); 715 | return t; 716 | } else { 717 | this.fail("expected function type, got " + expected, loc); 718 | } 719 | }; 720 | 721 | // (Expression, Statement, Statement | null) -> void 722 | Vp.ifStatement = function ifStatement(test, cons, alt) { 723 | this.checkSubtype(this.expression(test), ty.Int, "if test", test.loc); 724 | this.statement(cons); 725 | if (alt) 726 | this.statement(alt); 727 | }; 728 | 729 | // (Expression | null, Loc) -> void 730 | Vp.returnStatement = function returnStatement(arg, loc) { 731 | this.checkSubtype(this.optExpression(arg) || ty.Void, this._result, "return argument", loc); 732 | }; 733 | 734 | // (Expression, Statement) -> void 735 | Vp.whileStatement = function whileStatement(test, body, labels) { 736 | this.checkSubtype(this.expression(test), ty.Int, "while loop condition", test.loc); 737 | this.statement(body); 738 | }; 739 | 740 | // (Statement, Expression) -> void 741 | Vp.doWhileStatement = function doWhileStatement(body, test) { 742 | this.statement(body); 743 | this.checkSubtype(this.expression(test), ty.Int, "do-while loop condition", test.loc); 744 | }; 745 | 746 | // (VariableDeclaration | Expression | null, Expression | null, Expression | null, Statement, Loc) -> void 747 | Vp.forStatement = function forStatement(init, test, update, body, loc) { 748 | if (init.type === 'VariableDeclaration') 749 | this.fail("illegal variable declaration in for-head", init.loc); 750 | this.optExpression(init); 751 | this.checkSubtype(this.optExpression(test) || ty.Int, ty.Int, "for loop condition", loc); 752 | this.optExpression(update); 753 | this.statement(body); 754 | }; 755 | 756 | // (Expression, [SwitchCase], labels) -> void 757 | Vp.switchStatement = function switchStatement(disc, cases) { 758 | var s = this.expression(disc); 759 | cases.forEach(function(c) { 760 | this.case(c, s); 761 | }, this); 762 | }; 763 | 764 | // (SwitchCase, Type) -> void 765 | Vp.case = function case_(c, s) { 766 | if (c.test) 767 | this.checkSubtype(this.literal(c.test), s, "case clause expression", c.test.loc); 768 | this.statements(c.consequent); 769 | }; 770 | 771 | // ----------------------------------------------------------------------------- 772 | // expressions 773 | // ----------------------------------------------------------------------------- 774 | 775 | // (Expression | null) -> ValueType | null 776 | Vp.optExpression = function optExpression(expr) { 777 | return expr ? this.expression(expr) : null; 778 | }; 779 | 780 | // (Expression) -> ValueType 781 | Vp.expression = function expression(e) { 782 | return this.match(e, "expression", function(when) { 783 | when({ type: 'Literal', raw: hasDot }, function() { 784 | return ty.Double; 785 | }); 786 | 787 | when({ type: 'Literal', value: match.range(-0x80000000, 0xffffffff) }, function() { 788 | return ty.Fixnum; 789 | }); 790 | 791 | when({ type: 'Identifier' }, function() { 792 | return this.lookupValueType(e.name, e.loc); 793 | }, this); 794 | 795 | when({ 796 | type: 'AssignmentExpression', 797 | left: match.var('left', { type: match.some('Identifier', 'MemberExpression') }), 798 | right: match.var('right') 799 | }, function(vars) { 800 | var s = this.expression(vars.left); 801 | var t = this.expression(vars.right); 802 | this.checkSubtype(t, s, "assignment", e.loc); 803 | return t; 804 | }, this); 805 | 806 | when({ 807 | type: 'MemberExpression', 808 | object: { type: 'Identifier', name: match.var('x'), loc: match.var('loc') }, 809 | property: { type: 'Literal', value: match.range(0, 0x100000000), raw: dotless } 810 | }, function(vars) { 811 | var t = this.lookup(vars.x, vars.loc); 812 | if (!(t instanceof ty.View)) 813 | this.fail("expected view type, got " + t); 814 | return t.elementType; 815 | }, this); 816 | 817 | when({ 818 | type: 'MemberExpression', 819 | object: { type: 'Identifier', name: match.var('x'), loc: match.var('loc') }, 820 | property: { 821 | type: 'BinaryExpression', 822 | operator: '>>', 823 | left: match.var('e'), 824 | right: match.var('n', { 825 | type: 'Literal', 826 | value: match.var('shift', match.number), 827 | raw: dotless 828 | }) 829 | }, 830 | computed: true 831 | }, function(vars) { 832 | var t = this.lookup(vars.x, vars.loc); 833 | if (!(t instanceof ty.View)) 834 | this.fail("expected view type, got " + t, vars.loc); 835 | this.checkSubtype(this.expression(vars.e), ty.Intish, "heap address" , vars.e.loc); 836 | var expectedShift = log2(t.bytes); 837 | if (vars.shift !== expectedShift) 838 | this.fail("expected shift of " + expectedShift + " bits for view type " + t + ", got " + vars.shift, vars.n.loc); 839 | return t.elementType; 840 | }, this); 841 | 842 | when({ 843 | type: 'MemberExpression', 844 | object: { type: 'Identifier', name: match.var('x'), loc: match.var('loc') }, 845 | property: match.var('e'), 846 | computed: true 847 | }, function(vars) { 848 | var t = this.lookup(vars.x, vars.loc); 849 | if (!(t instanceof ty.View)) 850 | this.fail("expected view type, got " + t, vars.loc); 851 | if (t.bytes !== 1) 852 | this.fail("expected view type with element size 1, got " + t, vars.loc); 853 | if (t.elementType !== ty.Intish) 854 | this.fail("expected view type with intish elements, got " + t, vars.loc); 855 | this.checkSubtype(this.expression(vars.e), ty.Int, "heap address", vars.e.loc); 856 | return t.Intish; 857 | }, this); 858 | 859 | when({ 860 | type: 'ConditionalExpression', 861 | test: match.var('test'), 862 | consequent: match.var('cons'), 863 | alternate: match.var('alt') 864 | }, function(vars) { 865 | this.checkSubtype(this.expression(vars.test), ty.Int, "conditional test", vars.test.loc); 866 | var t1 = this.expression(vars.cons); 867 | var t2 = this.expression(vars.alt); 868 | if (t1 !== t2) 869 | this.fail("type mismatch between conditional branches", e.loc); 870 | if (t1 !== ty.Int && t1 !== ty.Double) 871 | this.fail("expected int or double in conditional branch, got " + t1, vars.cons.loc); 872 | return t1; 873 | }, this); 874 | 875 | when({ 876 | type: 'SequenceExpression', 877 | expressions: match.var('es') 878 | }, function(vars) { 879 | var last = vars.es.pop(); 880 | vars.es.forEach(function(e) { 881 | if (e.type === 'CallExpression') 882 | this.call(e, ty.Void); 883 | else 884 | this.expression(e); 885 | }, this); 886 | return this.expression(last); 887 | }, this); 888 | 889 | when({ 890 | type: 'UnaryExpression', 891 | operator: '~', 892 | argument: { 893 | type: 'UnaryExpression', 894 | operator: '~', 895 | argument: match.var('e') 896 | } 897 | }, function(vars) { 898 | this.checkSubtype(this.expression(vars.e), ty.Double, "double->signed coercion", e.loc); 899 | return ty.Signed; 900 | }, this); 901 | 902 | when({ 903 | type: 'UnaryExpression', 904 | operator: '+', 905 | argument: match.var('e', { type: 'CallExpression' }) 906 | }, function(vars) { 907 | this.call(vars.e, ty.Double); 908 | return ty.Double; 909 | }, this); 910 | 911 | when({ 912 | type: 'UnaryExpression', 913 | operator: match.var('op'), 914 | argument: match.var('arg') 915 | }, function(vars) { 916 | var t = tables.UNOPS.get(vars.op); 917 | if (!t) 918 | this.fail("unknown unary operator " + vars.op, e.loc); 919 | return this.checkArguments([this.expression(vars.arg)], t, "unary expression", [vars.op.loc], e.loc); 920 | }, this); 921 | 922 | when({ 923 | type: 'BinaryExpression', 924 | operator: '|', 925 | left: match.var('e', { type: 'CallExpression' }), 926 | right: { type: 'Literal', value: 0, raw: dotless } 927 | }, function(vars) { 928 | this.call(vars.e, ty.Signed); 929 | return ty.Signed; 930 | }, this); 931 | 932 | when({ 933 | type: 'BinaryExpression', 934 | operator: match.some('+', '-'), 935 | left: match.var('left'), 936 | right: match.var('right') 937 | }, function(vars) { 938 | var operands = flattenAdditive(vars.left, vars.right); 939 | var n = operands.length; 940 | var t = this.expression(operands[0]); 941 | if (t.subtype(ty.Double)) { 942 | for (var i = 1; i < n; i++) { 943 | var operand = operands[i]; 944 | this.checkSubtype(this.expression(operand), ty.Double, "additive operand", operand.loc); 945 | } 946 | return ty.Double; 947 | } else if (t.subtype(ty.Int)) { 948 | if (n > 0x100000) 949 | this.fail("too many additive operations without coercion: " + n + " > maximum 2^20", e.loc); 950 | for (var i = 1; i < n; i++) { 951 | var operand = operands[i]; 952 | this.checkSubtype(this.expression(operand), ty.Int, "additive operand", operand.loc); 953 | } 954 | return ty.Intish; 955 | } 956 | this.fail("expected type int or double, got " + t, operands[0].loc); 957 | }, this); 958 | 959 | when({ 960 | type: 'BinaryExpression', 961 | operator: match.var('op'), 962 | left: match.var('left'), 963 | right: match.var('right') 964 | }, function(vars) { 965 | var t = tables.BINOPS.get(vars.op); 966 | if (!t) 967 | this.fail("unknown binary operator " + vars.op, e.loc); 968 | return this.checkArguments([this.expression(vars.left), this.expression(vars.right)], 969 | t, "operator " + vars.op, 970 | [vars.left.loc, vars.right.loc], 971 | e.loc); 972 | }, this); 973 | }); 974 | }; 975 | 976 | // ----------------------------------------------------------------------------- 977 | // call expressions 978 | // ----------------------------------------------------------------------------- 979 | 980 | // (CallExpression, ValueType) -> void 981 | Vp.call = function call(e, t) { 982 | return this.match(e, "function call", function(when) { 983 | when({ 984 | type: 'CallExpression', 985 | callee: { type: 'Identifier', name: match.var('f'), loc: match.var('loc') }, 986 | arguments: match.var('args') 987 | }, function(vars) { 988 | var formalReturnType = this.checkArguments(vars.args.map(this.expression, this), 989 | this.lookup(vars.f, vars.loc), 990 | "function call", 991 | vars.args.map(function(arg) { return arg.loc; }), 992 | e.loc); 993 | this.checkSameType(formalReturnType, t, "function call", e.loc); 994 | }, this); 995 | 996 | when({ 997 | type: 'CallExpression', 998 | callee: { 999 | type: 'MemberExpression', 1000 | object: { type: 'Identifier', name: match.var('f'), loc: match.var('loc') }, 1001 | property: { 1002 | type: 'BinaryExpression', 1003 | operator: '&', 1004 | left: match.var('index'), 1005 | right: { 1006 | type: 'Literal', 1007 | value: match.var('n', match.number), 1008 | raw: dotless, 1009 | loc: match.var('nloc') 1010 | } 1011 | }, 1012 | computed: true 1013 | }, 1014 | arguments: match.var('args') 1015 | }, function(vars) { 1016 | var t = this.lookup(vars.f, vars.loc); 1017 | if (!(t instanceof ty.Table)) 1018 | this.fail("expected function table, got " + vars.f, vars.loc); 1019 | this.checkSubtype(this.expression(vars.index), ty.Intish, "function pointer", vars.index.loc); 1020 | if (t.length !== vars.n + 1) 1021 | this.fail("function table mask should be " + (t.length - 1) + ", got " + vars.n, vars.nloc); 1022 | var formalReturnType = this.checkArguments(vars.args.map(this.expression, this), 1023 | t.type, "function pointer call", 1024 | vars.args.map(function(arg) { return arg.loc; }), 1025 | e.loc); 1026 | this.checkSameType(formalReturnType, t, "function call", e.loc); 1027 | }, this); 1028 | }); 1029 | }; 1030 | 1031 | // ----------------------------------------------------------------------------- 1032 | // front end 1033 | // ----------------------------------------------------------------------------- 1034 | 1035 | // (string) -> Report 1036 | module.exports = function validate(src) { 1037 | return (new Validator).validate(src); 1038 | }; 1039 | -------------------------------------------------------------------------------- /html/subtypes.graffle: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ActiveLayerIndex 6 | 0 7 | ApplicationVersion 8 | 9 | com.omnigroup.OmniGraffle 10 | 139.18.0.187838 11 | 12 | AutoAdjust 13 | 14 | BackgroundGraphic 15 | 16 | Bounds 17 | {{0, 0}, {1152, 733}} 18 | Class 19 | SolidGraphic 20 | ID 21 | 2 22 | Style 23 | 24 | shadow 25 | 26 | Draws 27 | NO 28 | 29 | stroke 30 | 31 | Draws 32 | NO 33 | 34 | 35 | 36 | BaseZoom 37 | 0 38 | CanvasOrigin 39 | {0, 0} 40 | ColumnAlign 41 | 1 42 | ColumnSpacing 43 | 36 44 | CreationDate 45 | 2013-06-07 22:05:55 +0000 46 | Creator 47 | Michael Bebenita 48 | DisplayScale 49 | 1 0/72 in = 1.0000 in 50 | GraphDocumentVersion 51 | 8 52 | GraphicsList 53 | 54 | 55 | Class 56 | LineGraphic 57 | ID 58 | 32 59 | Points 60 | 61 | {140.2999963760376, 271.59995756244513} 62 | {140.2999963760376, 223.19999408721924} 63 | 64 | Style 65 | 66 | stroke 67 | 68 | Color 69 | 70 | b 71 | 0.398864 72 | g 73 | 0.398857 74 | r 75 | 0.398869 76 | 77 | HeadArrow 78 | FilledArrow 79 | Legacy 80 | 81 | LineType 82 | 1 83 | TailArrow 84 | 0 85 | Width 86 | 3 87 | 88 | 89 | 90 | 91 | Bounds 92 | {{79.199997901916504, 180.00000508721922}, {122.40000000000001, 43.199989000000002}} 93 | Class 94 | ShapedGraphic 95 | ID 96 | 30 97 | Shape 98 | Rectangle 99 | Style 100 | 101 | fill 102 | 103 | Color 104 | 105 | b 106 | 0.901961 107 | g 108 | 0.901961 109 | r 110 | 0.901961 111 | 112 | FillType 113 | 2 114 | GradientAngle 115 | 90 116 | GradientColor 117 | 118 | b 119 | 0.6 120 | g 121 | 0.6 122 | r 123 | 0.6 124 | 125 | 126 | shadow 127 | 128 | Draws 129 | NO 130 | 131 | stroke 132 | 133 | Color 134 | 135 | b 136 | 0.4 137 | g 138 | 0.4 139 | r 140 | 0.4 141 | 142 | CornerRadius 143 | 5 144 | Width 145 | 2 146 | 147 | 148 | Text 149 | 150 | Text 151 | {\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf200 152 | \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Calibri;} 153 | {\colortbl;\red255\green255\blue255;} 154 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc 155 | 156 | \f0\b\fs28 \cf0 floatish} 157 | 158 | 159 | 160 | Class 161 | LineGraphic 162 | Head 163 | 164 | ID 165 | 27 166 | 167 | ID 168 | 29 169 | Points 170 | 171 | {140.21845543143303, 366.19997937971277} 172 | {139.82966014768195, 317.79966534952058} 173 | 174 | Style 175 | 176 | stroke 177 | 178 | Color 179 | 180 | b 181 | 0.398864 182 | g 183 | 0.398857 184 | r 185 | 0.398869 186 | 187 | HeadArrow 188 | FilledArrow 189 | Legacy 190 | 191 | LineType 192 | 1 193 | TailArrow 194 | 0 195 | Width 196 | 3 197 | 198 | 199 | Tail 200 | 201 | ID 202 | 28 203 | 204 | 205 | 206 | Bounds 207 | {{79.19999885559082, 367.19994713897705}, {122.40000000000001, 43.199989000000002}} 208 | Class 209 | ShapedGraphic 210 | FontInfo 211 | 212 | Color 213 | 214 | b 215 | 0.298039 216 | g 217 | 0.298039 218 | r 219 | 0.298039 220 | 221 | 222 | ID 223 | 28 224 | Shape 225 | Rectangle 226 | Style 227 | 228 | fill 229 | 230 | Color 231 | 232 | b 233 | 0.901961 234 | g 235 | 0.901961 236 | r 237 | 0.901961 238 | 239 | FillType 240 | 2 241 | GradientAngle 242 | 90 243 | GradientColor 244 | 245 | b 246 | 0.6 247 | g 248 | 0.6 249 | r 250 | 0.6 251 | 252 | 253 | shadow 254 | 255 | Draws 256 | NO 257 | 258 | stroke 259 | 260 | Color 261 | 262 | b 263 | 0.4 264 | g 265 | 0.4 266 | r 267 | 0.4 268 | 269 | CornerRadius 270 | 5 271 | Width 272 | 2 273 | 274 | 275 | Text 276 | 277 | Text 278 | {\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf200 279 | \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Calibri;} 280 | {\colortbl;\red255\green255\blue255;\red0\green0\blue0;} 281 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc 282 | 283 | \f0\b\fs28 \cf2 float} 284 | 285 | 286 | 287 | Bounds 288 | {{79.19999885559082, 273.59999465942383}, {122.40000000000001, 43.199989000000002}} 289 | Class 290 | ShapedGraphic 291 | ID 292 | 27 293 | Shape 294 | Rectangle 295 | Style 296 | 297 | fill 298 | 299 | Color 300 | 301 | b 302 | 0.901961 303 | g 304 | 0.901961 305 | r 306 | 0.901961 307 | 308 | FillType 309 | 2 310 | GradientAngle 311 | 90 312 | GradientColor 313 | 314 | b 315 | 0.6 316 | g 317 | 0.6 318 | r 319 | 0.6 320 | 321 | 322 | shadow 323 | 324 | Draws 325 | NO 326 | 327 | stroke 328 | 329 | Color 330 | 331 | b 332 | 0.4 333 | g 334 | 0.4 335 | r 336 | 0.4 337 | 338 | CornerRadius 339 | 5 340 | Width 341 | 2 342 | 343 | 344 | Text 345 | 346 | Text 347 | {\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf200 348 | \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Calibri;} 349 | {\colortbl;\red255\green255\blue255;} 350 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc 351 | 352 | \f0\b\fs28 \cf0 float\cf0 ?} 353 | 354 | 355 | 356 | Class 357 | LineGraphic 358 | Head 359 | 360 | ID 361 | 8 362 | 363 | ID 364 | 26 365 | Points 366 | 367 | {578.18522311685604, 273.09125784684301} 368 | {494.61476306844685, 223.70871543111116} 369 | 370 | Style 371 | 372 | stroke 373 | 374 | Color 375 | 376 | b 377 | 0.398864 378 | g 379 | 0.398857 380 | r 381 | 0.398869 382 | 383 | HeadArrow 384 | FilledArrow 385 | Legacy 386 | 387 | LineType 388 | 1 389 | TailArrow 390 | 0 391 | Width 392 | 3 393 | 394 | 395 | Tail 396 | 397 | ID 398 | 11 399 | 400 | 401 | 402 | Class 403 | LineGraphic 404 | Head 405 | 406 | ID 407 | 10 408 | 409 | ID 410 | 25 411 | Points 412 | 413 | {517.47714205273303, 460.0366604156489} 414 | {476.12283413256966, 411.16332286230505} 415 | 416 | Style 417 | 418 | stroke 419 | 420 | Color 421 | 422 | b 423 | 0.398864 424 | g 425 | 0.398857 426 | r 427 | 0.398869 428 | 429 | HeadArrow 430 | FilledArrow 431 | Legacy 432 | 433 | LineType 434 | 1 435 | TailArrow 436 | 0 437 | Width 438 | 3 439 | 440 | 441 | Tail 442 | 443 | ID 444 | 15 445 | 446 | 447 | 448 | Class 449 | LineGraphic 450 | Head 451 | 452 | ID 453 | 12 454 | 455 | ID 456 | 24 457 | Points 458 | 459 | {555.32281413256976, 460.0366604156489} 460 | {596.67712205273301, 411.16332286230511} 461 | 462 | Style 463 | 464 | stroke 465 | 466 | Color 467 | 468 | b 469 | 0.398864 470 | g 471 | 0.398857 472 | r 473 | 0.398869 474 | 475 | HeadArrow 476 | FilledArrow 477 | Legacy 478 | 479 | LineType 480 | 1 481 | TailArrow 482 | 0 483 | Width 484 | 3 485 | 486 | 487 | Tail 488 | 489 | ID 490 | 15 491 | 492 | 493 | 494 | Class 495 | LineGraphic 496 | Head 497 | 498 | ID 499 | 11 500 | 501 | ID 502 | 22 503 | Points 504 | 505 | {615.59997809265133, 366.19994713914173} 506 | {615.59997809265133, 317.79997613881233} 507 | 508 | Style 509 | 510 | stroke 511 | 512 | Color 513 | 514 | b 515 | 0.398864 516 | g 517 | 0.398857 518 | r 519 | 0.398869 520 | 521 | HeadArrow 522 | FilledArrow 523 | Legacy 524 | 525 | LineType 526 | 1 527 | TailArrow 528 | 0 529 | Width 530 | 3 531 | 532 | 533 | Tail 534 | 535 | ID 536 | 12 537 | 538 | 539 | 540 | Class 541 | LineGraphic 542 | Head 543 | 544 | ID 545 | 11 546 | 547 | ID 548 | 21 549 | Points 550 | 551 | {494.61477487940653, 366.69121795377214} 552 | {578.18521130589625, 317.30870532418186} 553 | 554 | Style 555 | 556 | stroke 557 | 558 | Color 559 | 560 | b 561 | 0.398864 562 | g 563 | 0.398857 564 | r 565 | 0.398869 566 | 567 | HeadArrow 568 | FilledArrow 569 | Legacy 570 | 571 | LineType 572 | 1 573 | TailArrow 574 | 0 575 | Width 576 | 3 577 | 578 | 579 | Tail 580 | 581 | ID 582 | 10 583 | 584 | 585 | 586 | Class 587 | LineGraphic 588 | Head 589 | 590 | ID 591 | 9 592 | 593 | ID 594 | 20 595 | Points 596 | 597 | {457.20000809265139, 366.19994713914173} 598 | {457.20000809265139, 317.79997613881233} 599 | 600 | Style 601 | 602 | stroke 603 | 604 | Color 605 | 606 | b 607 | 0.398864 608 | g 609 | 0.398857 610 | r 611 | 0.398869 612 | 613 | HeadArrow 614 | FilledArrow 615 | Legacy 616 | 617 | LineType 618 | 1 619 | TailArrow 620 | 0 621 | Width 622 | 3 623 | 624 | 625 | Tail 626 | 627 | ID 628 | 10 629 | 630 | 631 | 632 | Class 633 | LineGraphic 634 | Head 635 | 636 | ID 637 | 9 638 | 639 | ID 640 | 19 641 | Points 642 | 643 | {336.21477455286674, 366.69121799294936} 644 | {419.78521819920854, 317.30870528500463} 645 | 646 | Style 647 | 648 | stroke 649 | 650 | Color 651 | 652 | b 653 | 0.398864 654 | g 655 | 0.398857 656 | r 657 | 0.398869 658 | 659 | HeadArrow 660 | FilledArrow 661 | Legacy 662 | 663 | LineType 664 | 1 665 | TailArrow 666 | 0 667 | Width 668 | 3 669 | 670 | 671 | Tail 672 | 673 | ID 674 | 6 675 | 676 | 677 | 678 | Class 679 | LineGraphic 680 | Head 681 | 682 | ID 683 | 5 684 | 685 | ID 686 | 16 687 | Points 688 | 689 | {298.79999165942382, 366.19994713698696} 690 | {298.79999165942382, 317.79998366176108} 691 | 692 | Style 693 | 694 | stroke 695 | 696 | Color 697 | 698 | b 699 | 0.398864 700 | g 701 | 0.398857 702 | r 703 | 0.398869 704 | 705 | HeadArrow 706 | FilledArrow 707 | Legacy 708 | 709 | LineType 710 | 1 711 | TailArrow 712 | 0 713 | Width 714 | 3 715 | 716 | 717 | Tail 718 | 719 | ID 720 | 6 721 | 722 | 723 | 724 | Bounds 725 | {{475.19997809265135, 460.80004713897705}, {122.40000000000001, 43.199989000000002}} 726 | Class 727 | ShapedGraphic 728 | FontInfo 729 | 730 | Color 731 | 732 | b 733 | 0.298039 734 | g 735 | 0.298039 736 | r 737 | 0.298039 738 | 739 | 740 | ID 741 | 15 742 | Shape 743 | Rectangle 744 | Style 745 | 746 | fill 747 | 748 | Color 749 | 750 | b 751 | 0.901961 752 | g 753 | 0.901961 754 | r 755 | 0.901961 756 | 757 | 758 | shadow 759 | 760 | Draws 761 | NO 762 | 763 | stroke 764 | 765 | Color 766 | 767 | b 768 | 0.4 769 | g 770 | 0.4 771 | r 772 | 0.4 773 | 774 | CornerRadius 775 | 5 776 | Width 777 | 2 778 | 779 | 780 | Text 781 | 782 | Text 783 | {\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf200 784 | \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Calibri;} 785 | {\colortbl;\red255\green255\blue255;\red76\green76\blue76;} 786 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc 787 | 788 | \f0\b\fs28 \cf2 fixnum} 789 | 790 | 791 | 792 | Bounds 793 | {{712.79998809265135, 273.59998713897704}, {122.40000000000001, 43.199989000000002}} 794 | Class 795 | ShapedGraphic 796 | ID 797 | 13 798 | Shape 799 | Rectangle 800 | Style 801 | 802 | fill 803 | 804 | Color 805 | 806 | b 807 | 0.901961 808 | g 809 | 0.901961 810 | r 811 | 0.901961 812 | 813 | FillType 814 | 2 815 | GradientAngle 816 | 90 817 | GradientColor 818 | 819 | b 820 | 0.6 821 | g 822 | 0.6 823 | r 824 | 0.6 825 | 826 | 827 | shadow 828 | 829 | Draws 830 | NO 831 | 832 | stroke 833 | 834 | Color 835 | 836 | b 837 | 0.4 838 | g 839 | 0.4 840 | r 841 | 0.4 842 | 843 | CornerRadius 844 | 5 845 | Width 846 | 2 847 | 848 | 849 | Text 850 | 851 | Text 852 | {\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf200 853 | \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Calibri;} 854 | {\colortbl;\red255\green255\blue255;} 855 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc 856 | 857 | \f0\b\fs28 \cf0 void} 858 | 859 | 860 | 861 | Bounds 862 | {{554.39995809265133, 367.19994713897705}, {122.40000000000001, 43.199989000000002}} 863 | Class 864 | ShapedGraphic 865 | ID 866 | 12 867 | Shape 868 | Rectangle 869 | Style 870 | 871 | fill 872 | 873 | Color 874 | 875 | b 876 | 0.901961 877 | g 878 | 0.901961 879 | r 880 | 0.901961 881 | 882 | FillType 883 | 2 884 | GradientAngle 885 | 90 886 | GradientColor 887 | 888 | b 889 | 0.6 890 | g 891 | 0.6 892 | r 893 | 0.6 894 | 895 | 896 | shadow 897 | 898 | Draws 899 | NO 900 | 901 | stroke 902 | 903 | Color 904 | 905 | b 906 | 0.4 907 | g 908 | 0.4 909 | r 910 | 0.4 911 | 912 | CornerRadius 913 | 5 914 | Width 915 | 2 916 | 917 | 918 | Text 919 | 920 | Text 921 | {\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf200 922 | \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Calibri;} 923 | {\colortbl;\red255\green255\blue255;} 924 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc 925 | 926 | \f0\b\fs28 \cf0 unsigned} 927 | 928 | 929 | 930 | Bounds 931 | {{554.39998809265137, 273.59998713897704}, {122.40000000000001, 43.199989000000002}} 932 | Class 933 | ShapedGraphic 934 | ID 935 | 11 936 | Shape 937 | Rectangle 938 | Style 939 | 940 | fill 941 | 942 | Color 943 | 944 | b 945 | 0.901961 946 | g 947 | 0.901961 948 | r 949 | 0.901961 950 | 951 | FillType 952 | 2 953 | GradientAngle 954 | 90 955 | GradientColor 956 | 957 | b 958 | 0.6 959 | g 960 | 0.6 961 | r 962 | 0.6 963 | 964 | 965 | shadow 966 | 967 | Draws 968 | NO 969 | 970 | stroke 971 | 972 | Color 973 | 974 | b 975 | 0.4 976 | g 977 | 0.4 978 | r 979 | 0.4 980 | 981 | CornerRadius 982 | 5 983 | Width 984 | 2 985 | 986 | 987 | Text 988 | 989 | Text 990 | {\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf200 991 | \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Calibri;} 992 | {\colortbl;\red255\green255\blue255;} 993 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc 994 | 995 | \f0\b\fs28 \cf0 int} 996 | 997 | 998 | 999 | Bounds 1000 | {{395.99999809265137, 367.19994713897705}, {122.40000000000001, 43.199989000000002}} 1001 | Class 1002 | ShapedGraphic 1003 | FontInfo 1004 | 1005 | Color 1006 | 1007 | b 1008 | 0.298039 1009 | g 1010 | 0.298039 1011 | r 1012 | 0.298039 1013 | 1014 | 1015 | ID 1016 | 10 1017 | Shape 1018 | Rectangle 1019 | Style 1020 | 1021 | fill 1022 | 1023 | Color 1024 | 1025 | b 1026 | 0.901961 1027 | g 1028 | 0.901961 1029 | r 1030 | 0.901961 1031 | 1032 | 1033 | shadow 1034 | 1035 | Draws 1036 | NO 1037 | 1038 | stroke 1039 | 1040 | Color 1041 | 1042 | b 1043 | 0.4 1044 | g 1045 | 0.4 1046 | r 1047 | 0.4 1048 | 1049 | CornerRadius 1050 | 5 1051 | Width 1052 | 2 1053 | 1054 | 1055 | Text 1056 | 1057 | Text 1058 | {\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf200 1059 | \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Calibri;} 1060 | {\colortbl;\red255\green255\blue255;\red76\green76\blue76;} 1061 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc 1062 | 1063 | \f0\b\fs28 \cf2 signed} 1064 | 1065 | 1066 | 1067 | Bounds 1068 | {{395.99999809265137, 273.59998713897704}, {122.40000000000001, 43.199989000000002}} 1069 | Class 1070 | ShapedGraphic 1071 | FontInfo 1072 | 1073 | Color 1074 | 1075 | b 1076 | 0.298039 1077 | g 1078 | 0.298039 1079 | r 1080 | 0.298039 1081 | 1082 | 1083 | ID 1084 | 9 1085 | Shape 1086 | Rectangle 1087 | Style 1088 | 1089 | fill 1090 | 1091 | Color 1092 | 1093 | b 1094 | 0.901961 1095 | g 1096 | 0.901961 1097 | r 1098 | 0.901961 1099 | 1100 | 1101 | shadow 1102 | 1103 | Draws 1104 | NO 1105 | 1106 | stroke 1107 | 1108 | Color 1109 | 1110 | b 1111 | 0.4 1112 | g 1113 | 0.4 1114 | r 1115 | 0.4 1116 | 1117 | CornerRadius 1118 | 5 1119 | Width 1120 | 2 1121 | 1122 | 1123 | Text 1124 | 1125 | Text 1126 | {\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf200 1127 | \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Calibri;} 1128 | {\colortbl;\red255\green255\blue255;\red76\green76\blue76;} 1129 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc 1130 | 1131 | \f0\b\fs28 \cf2 extern} 1132 | 1133 | 1134 | 1135 | Bounds 1136 | {{395.99999809265137, 179.99999713897705}, {122.40000000000001, 43.199989000000002}} 1137 | Class 1138 | ShapedGraphic 1139 | ID 1140 | 8 1141 | Shape 1142 | Rectangle 1143 | Style 1144 | 1145 | fill 1146 | 1147 | Color 1148 | 1149 | b 1150 | 0.901961 1151 | g 1152 | 0.901961 1153 | r 1154 | 0.901961 1155 | 1156 | FillType 1157 | 2 1158 | GradientAngle 1159 | 90 1160 | GradientColor 1161 | 1162 | b 1163 | 0.6 1164 | g 1165 | 0.6 1166 | r 1167 | 0.6 1168 | 1169 | 1170 | shadow 1171 | 1172 | Draws 1173 | NO 1174 | 1175 | stroke 1176 | 1177 | Color 1178 | 1179 | b 1180 | 0.4 1181 | g 1182 | 0.4 1183 | r 1184 | 0.4 1185 | 1186 | CornerRadius 1187 | 5 1188 | Width 1189 | 2 1190 | 1191 | 1192 | Text 1193 | 1194 | Text 1195 | {\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf200 1196 | \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Calibri;} 1197 | {\colortbl;\red255\green255\blue255;} 1198 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc 1199 | 1200 | \f0\b\fs28 \cf0 intish} 1201 | 1202 | 1203 | 1204 | Bounds 1205 | {{237.59999465942383, 367.19994713897705}, {122.40000000000001, 43.199989000000002}} 1206 | Class 1207 | ShapedGraphic 1208 | FontInfo 1209 | 1210 | Color 1211 | 1212 | b 1213 | 0.298039 1214 | g 1215 | 0.298039 1216 | r 1217 | 0.298039 1218 | 1219 | 1220 | ID 1221 | 6 1222 | Shape 1223 | Rectangle 1224 | Style 1225 | 1226 | fill 1227 | 1228 | Color 1229 | 1230 | b 1231 | 0.901961 1232 | g 1233 | 0.901961 1234 | r 1235 | 0.901961 1236 | 1237 | 1238 | shadow 1239 | 1240 | Draws 1241 | NO 1242 | 1243 | stroke 1244 | 1245 | Color 1246 | 1247 | b 1248 | 0.4 1249 | g 1250 | 0.4 1251 | r 1252 | 0.4 1253 | 1254 | CornerRadius 1255 | 5 1256 | Width 1257 | 2 1258 | 1259 | 1260 | Text 1261 | 1262 | Text 1263 | {\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf200 1264 | \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Calibri;} 1265 | {\colortbl;\red255\green255\blue255;\red76\green76\blue76;} 1266 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc 1267 | 1268 | \f0\b\fs28 \cf2 double} 1269 | 1270 | 1271 | 1272 | Bounds 1273 | {{237.59999465942383, 273.59999465942383}, {122.40000000000001, 43.199989000000002}} 1274 | Class 1275 | ShapedGraphic 1276 | ID 1277 | 5 1278 | Shape 1279 | Rectangle 1280 | Style 1281 | 1282 | fill 1283 | 1284 | Color 1285 | 1286 | b 1287 | 0.901961 1288 | g 1289 | 0.901961 1290 | r 1291 | 0.901961 1292 | 1293 | FillType 1294 | 2 1295 | GradientAngle 1296 | 90 1297 | GradientColor 1298 | 1299 | b 1300 | 0.6 1301 | g 1302 | 0.6 1303 | r 1304 | 0.6 1305 | 1306 | 1307 | shadow 1308 | 1309 | Draws 1310 | NO 1311 | 1312 | stroke 1313 | 1314 | Color 1315 | 1316 | b 1317 | 0.4 1318 | g 1319 | 0.4 1320 | r 1321 | 0.4 1322 | 1323 | CornerRadius 1324 | 5 1325 | Width 1326 | 2 1327 | 1328 | 1329 | Text 1330 | 1331 | Text 1332 | {\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf200 1333 | \cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Calibri;} 1334 | {\colortbl;\red255\green255\blue255;} 1335 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc 1336 | 1337 | \f0\b\fs28 \cf0 double?} 1338 | 1339 | 1340 | 1341 | GridInfo 1342 | 1343 | GridSpacing 1344 | 7.1999998092651367 1345 | MajorGridSpacing 1346 | 10 1347 | ShowsGrid 1348 | YES 1349 | SnapsToGrid 1350 | YES 1351 | 1352 | GuidesLocked 1353 | NO 1354 | GuidesVisible 1355 | YES 1356 | HPages 1357 | 2 1358 | ImageCounter 1359 | 1 1360 | KeepToScale 1361 | 1362 | Layers 1363 | 1364 | 1365 | Lock 1366 | NO 1367 | Name 1368 | Layer 1 1369 | Print 1370 | YES 1371 | View 1372 | YES 1373 | 1374 | 1375 | LayoutInfo 1376 | 1377 | Animate 1378 | NO 1379 | circoMinDist 1380 | 18 1381 | circoSeparation 1382 | 0.0 1383 | layoutEngine 1384 | dot 1385 | neatoSeparation 1386 | 0.0 1387 | twopiSeparation 1388 | 0.0 1389 | 1390 | LinksVisible 1391 | NO 1392 | MagnetsVisible 1393 | NO 1394 | MasterSheets 1395 | 1396 | ModificationDate 1397 | 2014-06-30 23:06:41 +0000 1398 | Modifier 1399 | David Herman 1400 | NotesVisible 1401 | NO 1402 | Orientation 1403 | 2 1404 | OriginVisible 1405 | NO 1406 | PageBreaks 1407 | YES 1408 | PrintInfo 1409 | 1410 | NSBottomMargin 1411 | 1412 | float 1413 | 41 1414 | 1415 | NSHorizonalPagination 1416 | 1417 | int 1418 | 0 1419 | 1420 | NSLeftMargin 1421 | 1422 | float 1423 | 18 1424 | 1425 | NSPaperSize 1426 | 1427 | size 1428 | {612, 792} 1429 | 1430 | NSPrintReverseOrientation 1431 | 1432 | int 1433 | 0 1434 | 1435 | NSRightMargin 1436 | 1437 | float 1438 | 18 1439 | 1440 | NSTopMargin 1441 | 1442 | float 1443 | 18 1444 | 1445 | 1446 | PrintOnePage 1447 | 1448 | ReadOnly 1449 | NO 1450 | RowAlign 1451 | 1 1452 | RowSpacing 1453 | 36 1454 | SheetTitle 1455 | Canvas 1 1456 | SmartAlignmentGuidesActive 1457 | YES 1458 | SmartDistanceGuidesActive 1459 | YES 1460 | UniqueID 1461 | 1 1462 | UseEntirePage 1463 | 1464 | VPages 1465 | 1 1466 | WindowInfo 1467 | 1468 | CurrentSheet 1469 | 0 1470 | ExpandedCanvases 1471 | 1472 | 1473 | name 1474 | Canvas 1 1475 | 1476 | 1477 | Frame 1478 | {{40, 4}, {1175, 742}} 1479 | ListView 1480 | 1481 | OutlineWidth 1482 | 142 1483 | RightSidebar 1484 | 1485 | ShowRuler 1486 | 1487 | Sidebar 1488 | 1489 | SidebarWidth 1490 | 120 1491 | VisibleRegion 1492 | {{0, 0}, {1040, 603}} 1493 | Zoom 1494 | 1 1495 | ZoomValues 1496 | 1497 | 1498 | Canvas 1 1499 | 1 1500 | 2 1501 | 1502 | 1503 | 1504 | 1505 | 1506 | -------------------------------------------------------------------------------- /historic/def.tex: -------------------------------------------------------------------------------- 1 | \documentclass{article} 2 | 3 | \usepackage{mathpartir} 4 | \usepackage{amssymb} 5 | \usepackage{graphicx} 6 | \RequirePackage{algorithm} 7 | \RequirePackage[noend]{algpseudocode} 8 | 9 | \newcommand{\mod}{\mathrel{\mathrm{mod}}} 10 | \newcommand{\imm}{\mathtt{imm}} 11 | \newcommand{\mut}{\mathtt{mut}} 12 | \newcommand{\funcall}[2]{{#1}\mathjs{(}{#2}\mathjs{)}} 13 | \newcommand{\paren}[1]{\mathjs{(}{#1}\mathjs{)}} 14 | \newcommand{\dom}{\mathit{dom}} 15 | \newcommand{\funtype}{\mathit{fun}\mbox{-}\mathit{type}} 16 | \newcommand{\vartype}{\mathit{var}\mbox{-}\mathit{type}} 17 | \newcommand{\rettype}{\mathit{return}\mbox{-}\mathit{type}} 18 | \newcommand{\imptype}{\mathit{import}\mbox{-}\mathit{type}} 19 | \newcommand{\stdtype}{\mathit{std}\mbox{-}\mathit{type}} 20 | \newcommand{\funty}[2]{({#1}) \rightarrow {#2}} 21 | \newcommand{\seq}[1]{\overline{{#1}}} 22 | \newcommand{\mathjs}[1]{\mbox{\texttt{{#1}}}} 23 | \newcommand{\mathjssm}[1]{\mbox{\texttt{\scriptsize {#1}}}} 24 | \newcommand{\return}[1]{\mathjs{return }{#1}\mathjs{;}} 25 | \newcommand{\fun}[3]{\mathjs{function }{#1}\mathjs{(}{#2}\mathjs{) \char123{} }{#3}\mathjs{ \char125{}}} 26 | \newcommand{\ternary}[3]{{#1}\,\mathjs{?}\,{#2}\,\mathjs{:}{#3}} 27 | \newcommand{\afun}[2]{\mathjs{function}\mathjs{(}{#1}\mathjs{) \char123{} }{#2}\mathjs{ \char125{}}} 28 | \newcommand{\var}[1]{\mathjs{var }{#1}\mathjs{;}} 29 | \newcommand{\rel}[1]{\scriptsize [\textsc{#1}]} 30 | \newcommand\defeq{\stackrel{\mbox{\tiny def}}{=}} 31 | \newcommand{\while}[2]{\mathjs{while (}{#1}\mathjs{) }{#2}} 32 | \newcommand{\dowhile}[2]{\mathjs{do }{#1}\mathjs{ while (}{#2}\mathjs{);}} 33 | \newcommand{\for}[4]{\mathjs{for (}{#1}\mathjs{; }{#2}\mathjs{; }{#3}\mathjs{) }{#4}} 34 | \newcommand{\switch}[2]{\mathjs{switch (}{#1}\mathjs{) \char123{} }{#2}\mathjs{ \char125{}}} 35 | \newcommand{\switchdef}[3]{\mathjs{switch (}{#1}\mathjs{) \char123{} }{#2}\mathjs{ default:}\,{#3}\mathjs{ \char125{}}} 36 | \newcommand{\brk}{\mathjs{break;}} 37 | \newcommand{\brkl}[1]{\mathjs{break }{#1}\mathjs{;}} 38 | \newcommand{\cont}{\mathjs{continue;}} 39 | \newcommand{\contl}[1]{\mathjs{continue }{#1}\mathjs{;}} 40 | \newcommand{\lab}[2]{{#1}\mathjs{:}\,{#2}} 41 | \newcommand{\ifone}[2]{\mathjs{if (}{#1}\mathjs{) }{#2}} 42 | \newcommand{\iftwo}[3]{\mathjs{if (}{#1}\mathjs{) }{#2}\mathjs{ else }{#3}} 43 | \newcommand{\block}[1]{\mathjs{\char123{} }{#1}\mathjs{ \char125{}}} 44 | \newcommand{\ok}{\mathrm{\mathbf{ok}}} 45 | \newcommand{\rulebreak}{\vspace{.1in}\\} 46 | \newcommand{\bit}{\mathtt{bit}} 47 | \newcommand{\unsigned}{\mathtt{unsigned}} 48 | \newcommand{\intsm}{\mathjssm{intish}} 49 | \newcommand{\doublesm}{\mathjssm{doublish}} 50 | \newcommand{\signed}{\mathtt{signed}} 51 | \newcommand{\fixnum}{\mathtt{fixnum}} 52 | \newcommand{\double}{\mathtt{double}} 53 | \newcommand{\view}[2]{\mathtt{view}^{#1}_{#2}} 54 | \newcommand{\extern}{\mathtt{extern}} 55 | \newcommand{\unk}{\mathtt{unknown}} 56 | \newcommand{\str}{\mathtt{string}} 57 | \newcommand{\undef}{\mathtt{undefined}} 58 | \newcommand{\void}{\mathtt{void}} 59 | \newcommand{\nul}{\mathtt{null}} 60 | \newcommand{\num}{\mathtt{number}} 61 | \newcommand{\obj}{\mathtt{object}} 62 | \newcommand{\mustret}{\mathsf{return}} 63 | \newcommand{\seqcomp}{\mathrel{;}} 64 | \newcommand{\getprop}[2]{{#1}\mathjs{[}{#2}\mathjs{]}} 65 | \newcommand{\getpropsm}[2]{{#1}\mathjssm{[}{#2}\mathjssm{]}} 66 | \newcommand{\longlong}[2]{\mathjs{[}{#1},{#2}\mathjs{]}} 67 | \newcommand{\toint}[1]{\mathjs{\~{}\~{}}{#1}} 68 | \newcommand{\todouble}[1]{\mathjs{+}{#1}} 69 | \renewcommand{\int}{\mathtt{int}} 70 | \newcommand{\dword}{\mathtt{bits64}} 71 | \newcommand{\function}{\mathtt{function}} 72 | \newcommand{\union}[2]{{#1}\mathrel{|}{#2}} 73 | \newcommand{\boolish}{\mathtt{boolish}} 74 | \newcommand{\floor}{\mathtt{floor}} 75 | \newcommand{\imul}{\mathtt{imul}} 76 | \newcommand{\intish}{\mathtt{intish}} 77 | \newcommand{\doublish}{\mathtt{doublish}} 78 | \newcommand{\Fun}{\mathtt{Function}} 79 | 80 | \newcommand{\progjudge}[1]{\vdash {#1}\ \ok} 81 | \newcommand{\impjudge}[5]{{#1};{#2};{#3};{#4} \vdash {#5}\ \ok} 82 | \newcommand{\iejudge}[4]{{#1};{#2} \vdash {#3} : {#4}} 83 | \newcommand{\fnjudge}[2]{{#1} \vdash {#2}\ \ok} 84 | \newcommand{\expjudge}[2]{{#1} \vdash {#2}\ \ok} 85 | \newcommand{\stmtjudge}[5]{{#1};{#2} \vdash {#3} : {#4} / {#5}} 86 | \newcommand{\exprjudge}[4]{{#1};{#2} \vdash {#3} : {#4}} 87 | \newcommand{\stmtretjudge}[2]{\vdash {#1} \hookrightarrow {#2}} 88 | \newcommand{\sjudge}[4]{{#1};{#2};{#3} \vdash {#4}\ \ok} 89 | 90 | \newcommand{\returns}{\mathit{returns}} 91 | \newcommand{\breaks}{\mathit{breaks}} 92 | 93 | \begin{document} 94 | 95 | \title{\texttt{asm.js}: a High-Performance Subset of JavaScript} 96 | \author{Dave Herman, Luke Wagner, and Alon Zakai} 97 | \maketitle 98 | 99 | \section{Introduction} 100 | 101 | This document describes a formal definition of a subset of the 102 | JavaScript programming language that can be used as a high-performance 103 | compiler target language. This sublanguage or dialect, which we call 104 | $\mathjs{asm.js}$, effectively describes a safe virtual machine for 105 | memory-unsafe languages such as C and C++. 106 | 107 | Because $\mathjs{asm.js}$ is a proper subset of JavaScript, both 108 | syntactically and semantically, the language is fully defined by a 109 | static {\it validation} judgment, which yields a predicate that 110 | determines whether a given JavaScript program is or is not in the 111 | subset. No specification of a dynamic semantics is needed, since the 112 | behavior of an $\mathjs{asm.js}$ program is simply defined by its 113 | behavior as a JavaScript program. 114 | 115 | \subsection{Overview} 116 | 117 | The unit of compilation/validation of $\mathjs{asm.js}$ is the 118 | $\mathjs{asm.js}$ {\it module}, which takes the form of a closed 119 | JavaScript function beginning with the {\it prologue 120 | directive}~\cite{es5}: 121 | \[ 122 | \mathjs{"use asm";} 123 | \] 124 | The presence of this directive serves two purposes. First, it allows 125 | JavaScript engines that wish to provide specialized optimizations for 126 | $\mathjs{asm.js}$ to efficiently recognize that the module should be 127 | validated as an $\mathjs{asm.js}$, without the need for complex, 128 | heuristic or concurrent recognition logic. (Since validation requires 129 | a non-trivial traversal of the body of the module, it is likely too 130 | expensive to speculatively validate {\it all} JavaScript code during 131 | JIT compilation.) Second, by requiring the programmer or code 132 | generator to state the intention explicitly that the code should be 133 | recognized as $\mathjs{asm.js}$, it allows user agents to report 134 | validation errors or performance faults to developer consoles. 135 | 136 | An $\mathjs{asm.js}$ module takes three optional parameters: a global 137 | object, containing standard ECMAScript libraries, an {\it FFI object}, 138 | containing custom imported functions and constants from external 139 | JavaScript, and a JavaScript $\mathjs{ArrayBuffer}$ representing a 140 | virtualized memory. The module can provide different views of the 141 | buffer by using typed array wrappers imported from the environment: 142 | \begin{verbatim} 143 | function mod(global, foreign, buffer) { 144 | "use asm"; 145 | 146 | var HEAP_I32 = new global.Int32Array(buffer); 147 | var HEAP_F64 = new global.Float64Array(buffer); 148 | // ... 149 | } 150 | \end{verbatim} 151 | 152 | The body of an $\mathjs{asm.js}$ module consists of any number of 153 | function definitions, followed by an {\it export clause}: 154 | \[ 155 | \return{\mathjs{\char123{} foo:\,f, bar:\,g \char125{}}} 156 | \] 157 | If a module only exports a single function, it can do so directly, 158 | without the object literal: 159 | \[ 160 | \return{\mathjs{foo}} 161 | \] 162 | 163 | \subsection{Types} 164 | 165 | The $\mathjs{asm.js}$ language is statically typed: every function, 166 | variable, and expression has a statically predictable type, according 167 | to a type hierarchy covering a subset of JavaScript values (see 168 | Section~\ref{sec:types}). Variables, parameters, and functions are 169 | provided with an explicit type bound through a stylized use of 170 | JavaScript coercions. This technique was pioneered by the Emscripten 171 | compiler~\cite{emscripten}, and is now used by a number of compilers 172 | that target JavaScript~\cite{mandreel,lljs}. 173 | 174 | For example, the following is a simple function from integers to 175 | integers: 176 | \begin{verbatim} 177 | function id(x) { 178 | x = x|0; 179 | return x|0; 180 | } 181 | \end{verbatim} 182 | Even though JavaScript provides only double-precision floating-point 183 | numbers (doubles) in its data model, the $\mathjs{asm.js}$ type system 184 | enforces that 32-bit integer values---a strict subset of 185 | doubles---never overflow to larger doubles. This allows optimizing 186 | compilers to represent these values as unboxed integers in 32-bit 187 | registers or memory. 188 | 189 | Again following the practice established by Emscripten, it is possible 190 | to do integer operations such as arithmetic and conditionals by means 191 | of coercions: 192 | \begin{verbatim} 193 | function add1(x) { 194 | x = x|0; 195 | return ((x|0)+1)|0; 196 | } 197 | \end{verbatim} 198 | While the JavaScript semantics dictates that the addition may overflow 199 | to a larger number than a 32-bit integer, the outer coercion ensures 200 | that the entire expression results in a 32-bit integer---the same 201 | integer that would be produced by a signed, 32-bit addition in a 202 | typical assembly language. The $\mathjs{asm.js}$ type system thus 203 | ensures that integer operations can be efficiently compiled by 204 | optimizing JavaScript engines to predictable machine instructions. 205 | 206 | \subsection{Validation, linking, and execution} 207 | 208 | The $\mathjs{asm.js}$ validator is defined as a static type system, 209 | which can be performed by an optimizing JavaScript engine at the time 210 | the module is parsed by the JavaScript engine. (If compilation time is 211 | a concern, it can be delayed to runtime by hiding the source code in a 212 | string and passed to $\mathjs{eval}$ or the $\mathjs{Function}$ 213 | constructor.) During this phase, any static validation errors can be 214 | reported to a developer console. 215 | 216 | After a $\mathjs{asm.js}$ module is compiled, its evaluation produces 217 | a closure with an empty lexical environment. The module can be {\it 218 | linked} by calling the function with an object representing the 219 | imported environment and an optional buffer: 220 | \begin{verbatim} 221 | function mod(global, foreign, buffer) { 222 | "use asm"; 223 | // ... 224 | return { f: foo, g: bar }; 225 | } 226 | 227 | var foreign = { 228 | consoleDotLog: console.log, 229 | // ... 230 | }; 231 | 232 | var buffer = new ArrayBuffer(0x100000); 233 | 234 | // link the module 235 | var m = mod(window, foreign, buffer); 236 | \end{verbatim} 237 | 238 | This linking phase may need to perform additional, dynamic 239 | validation. In particular, dynamic validation can fail if, for 240 | example, the $\mathjs{Int32Array}$ function passed in through the 241 | environment does not turn out to construct a proper typed array 242 | (thereby defeating typed array-based optimizations). 243 | 244 | The resulting module object provides access to the exported 245 | $\mathjs{asm.js}$ functions, which have been fully validated (both 246 | statically and dynamically) and optimized. 247 | 248 | \subsection{Notation conventions} 249 | 250 | The following notation conventions are used in this document. Optional 251 | items in a grammar are presented in $[\mbox{square 252 | brackets}]$. Sequences are presented with a $\seq{\mbox{horizontal 253 | overbar}}$. The empty sequence is denoted by $\epsilon$. Integers and 254 | integer literals are ranged over by the metavariables $i, j$; 255 | these may also serve as sequence indices, in which case they are 256 | natural numbers. Natural numbers are otherwise ranged over by the 257 | metavariables $m, n$. Floating-point literals are ranged over by the 258 | metavariable $r$. 259 | 260 | \subsection{Document outline} 261 | 262 | The remainder of this document proceeds as follows. 263 | %% FIXME: outline 264 | 265 | \section{Abstract syntax} 266 | 267 | This section specifies the abstract syntax of $\mathjs{asm.js}$. The 268 | grammar is presented with concrete syntax for conciseness and 269 | readability, but should be read as describing the subset of abstract 270 | syntax trees produced by a standard JavaScript parser. 271 | 272 | We make the following assumptions about canonicalization of 273 | $\mathjs{asm.js}$ abstract syntax trees: 274 | \begin{enumerate} 275 | \item Parentheses are ignored in the AST. This allows parentheses to 276 | be left out of any of the formal definitions of this spec. 277 | 278 | \item Empty statements ($\mathjs{;}$) are ignored in the AST. This 279 | allows empty statements to be left out of any of the formal 280 | definitions of this spec. 281 | 282 | \item The identifiers $\mathjs{arguments}$ and $\mathjs{eval}$ do not 283 | appear in $\mathjs{asm.js}$ programs. If either of these identifiers 284 | appears anywhere, static validation must fail. 285 | \end{enumerate} 286 | 287 | In various places in this document, the meta-variables $f$, $g$, $x$, 288 | $y$, and $z$ are used to range over JavaScript identifiers. 289 | 290 | We syntactically distinguish integer literals $i, j$ from 291 | floating-point literals $r$. A floating-point literal is a JavaScript 292 | number literal that contains a $\mathjs{.}$ character. 293 | 294 | \subsection{Modules} 295 | 296 | An $\mathjs{asm.js}$ module has the following syntax: 297 | \[ 298 | \begin{array}{rcl} 299 | \mathit{mod} & ::= & \mathjs{function }[f]\mathjs{(}[\mathit{global}[, \mathit{foreign}[, \mathit{buffer}]]]\mathjs{) \char123{}} \\ 300 | & & \qquad\mathjs{"use asm";} \\ 301 | & & \qquad\seq{\var{\seq{x \mathjs{ = } \mathit{imp}}}} \\ 302 | & & \qquad\seq{\mathit{fn}_g} \\ 303 | & & \qquad\seq{\var{\seq{y \mathjs{ = } v}}} \\ 304 | & & \qquad\mathit{exp} \\ 305 | & & \mathjs{\char125{}} \\ 306 | %% \fun{[m]}{[g[, e[, b]]]}{\mathjs{"use asm"; } \seq{\mathit{imp}_x}\ \seq{\mathit{fn}_f}\ \seq{\var{\seq{y \mathjs{ = } v}}}\ \mathit{exp}} 307 | \end{array} 308 | \] 309 | The syntax consists of: 310 | \begin{enumerate} 311 | \item an optional module name; 312 | \item up to three optional parameters; 313 | \item a $\mathjs{"use asm";}$ prologue directive; 314 | \item a sequence of import statements; 315 | \item a sequence of function declarations; 316 | \item a sequence of global variable declarations; and 317 | \item a single export statement. 318 | \end{enumerate} 319 | 320 | An import expression is either an FFI function binding, a 321 | type-annotated (coerced) FFI value, or a heap view: 322 | \[ 323 | \begin{array}{rcl} 324 | \mathit{imp} & ::= & \mathit{global}\mathjs{.}x \\ 325 | & | & \mathit{global}\mathjs{.Math.}x \\ 326 | & | & \mathjs{new }\funcall{\mathit{global}\mathjs{.}x}{\mathit{buffer}} \\ 327 | & | & \mathit{foreign}\mathjs{.}x\mathjs{|0} \\ 328 | & | & \mathjs{+}\mathit{foreign}\mathjs{.}x \\ 329 | & | & \mathit{foreign}\mathjs{.}x \\ 330 | \end{array} 331 | \] 332 | (We assume that the metavariables $\mathit{global}$, 333 | $\mathit{foreign}$, and $\mathit{buffer}$ stand for fixed variables 334 | names used consistently throughout the imports.) 335 | 336 | A function declaration has the following syntax: 337 | \[ 338 | \mathit{fn}_f ::= \fun{f}{\seq{x}}{\seq{x\mathjs{ = }\mathit{ann}_x\mathjs{;}}\ \seq{\var{\seq{y\mathjs{ = }v}}}\ \mathit{ss}} 339 | \] 340 | The syntax consists of type annotations for the parameters, a sequence 341 | of local variable declarations, and a sequence of statements. 342 | 343 | Type annotations are either $\int$ or $\double$ coercions: 344 | \[ 345 | \mathit{ann}_x ::= x\mathjs{|0} ~|~ \todouble{x} 346 | \] 347 | 348 | An export statement returns either a single function or an object 349 | literal containing multiple functions: 350 | \[ 351 | \begin{array}{rcl} 352 | \mathit{exp} & ::= & \return{f} \\ 353 | & | & \return{\mathjs{\char123{} } \seq{x \mathjs{:} f} \mathjs{ \char125{}}} \\ 354 | \end{array} 355 | \] 356 | 357 | \subsection{Statements} 358 | 359 | The set of legal statements in $\mathjs{asm.js}$ includes blocks, 360 | expression statements, conditionals, returns, loops, $\mathjs{switch}$ 361 | blocks, $\mathjs{break}$ and $\mathjs{continue}$, and labeled 362 | statements: 363 | \[ 364 | \begin{array}{rcl} 365 | s & ::= & \block{\mathit{ss}} \\ 366 | & | & e\mathjs{;} \\ 367 | & | & \ifone{e}{s} \\ 368 | & | & \iftwo{e}{s}{s} \\ 369 | & | & \return{[\mathit{re}]} \\ 370 | & | & \while{e}{s} \\ 371 | & | & \dowhile{s}{e} \\ 372 | & | & \for{[e]}{[e]}{[e]}{s} \\ 373 | & | & \switch{e}{\seq{c}\ [d]} \\ 374 | & | & \brkl{[\mathit{lab}]} \\ 375 | & | & \contl{[\mathit{lab}]} \\ 376 | & | & \lab{\mathit{lab}}{s} \\ 377 | \\ 378 | \mathit{ss} & ::= & \seq{s} \\ 379 | \end{array} 380 | \] 381 | 382 | Return arguments always have their type explicitly manifest: either a 383 | $\signed$ or $\double$ coercion or a literal: 384 | \[ 385 | \mathit{re} ::= e\mathjs{|0} ~|~ \todouble{e} ~|~ v 386 | \] 387 | 388 | The contents of $\mathjs{switch}$ blocks are restricted: in addition 389 | to requiring the (optional) $\mathjs{default}$ clause to be the last 390 | clause, each $\mathjs{case}$ clause is syntactically restricted to 391 | contain only literal values: 392 | \[ 393 | \begin{array}{rcl} 394 | c & ::= & \mathjs{case }v\mathjs{:}\,\mathit{ss} \\ 395 | d & ::= & \mathjs{default:}\,\mathit{ss} \\ 396 | \mathit{cd} & ::= & c ~|~ d \\ 397 | \end{array} 398 | \] 399 | 400 | \subsection{Expressions} 401 | 402 | Expressions include literals, lvalues, assignments, function calls, 403 | unary expressions, binary expressions, and sequence expressions: 404 | \[ 405 | \begin{array}{rcl} 406 | e & ::= & v \\ 407 | & | & \mathit{lval} \\ 408 | & | & \mathit{lval}\mathjs{ = }e \\ 409 | & | & \funcall{f}{\seq{e}} \\ 410 | & | & \mathit{unop}\ e \\ 411 | & | & e\ \mathit{binop}\ e \\ 412 | & | & \ternary{e}{e}{e} \\ 413 | & | & \paren{\seq{e}} \\ 414 | \\ 415 | \mathit{unop} & ::= & \mathjs{+} ~|~ \mathjs{\~{}} ~|~ \mathjs{!} \\ 416 | \\ 417 | \mathit{binop} & ::= & \mathjs{+} ~|~ \mathjs{-} ~|~ \mathjs{*} ~|~ \mathjs{/} ~|~ \mathjs{\%} \\ 418 | & | & \mathjs{|} ~|~ \mathjs{\&} ~|~ \mathjs{\^{}} ~|~ \mathjs{<<} ~|~ \mathjs{>>} ~|~ \mathjs{>>>} \\ 419 | & | & \mathjs{<} ~|~ \mathjs{<=} ~|~ \mathjs{>} ~|~ \mathjs{>=} ~|~ \mathjs{!=} ~|~ \mathjs{==} \\ 420 | \end{array} 421 | \] 422 | 423 | Literals are either doubles or integers: 424 | \[ 425 | v ::= r ~|~ i 426 | \] 427 | 428 | Lvalues are either variables or typed array dereference 429 | expressions. The latter requires a mask to force the byte offset into 430 | a valid range and a shift to convert the offset into a proper index 431 | for the size of the typed array. 432 | \[ 433 | \mathit{lval} ::= x ~|~ \getprop{x}{i} ~|~ \getprop{x}{e\mathjs{ \& }\mathit{mask}} ~|~ \getprop{x}{\paren{e\mathjs{ \& }\mathit{mask}}\mathjs{ >> }i} 434 | \] 435 | The $\mathit{mask}$ is a non-negative integer $2^k - 1$ where $k \in 436 | [3, 31]$. The same $\mathit{mask}$ must be used consistently 437 | throughout the program. 438 | 439 | \section{Types} 440 | \label{sec:types} 441 | 442 | The $\mathjs{asm.js}$ validator relies on a static type system that 443 | classifies and constraints the syntax beyond the grammar. 444 | 445 | \subsection{Expression types} 446 | 447 | The set of {\it expression types} classifies the results of expression 448 | evaluation and constrains the allowable values of variables. 449 | \[ 450 | \begin{array}{rcl} 451 | \sigma, \tau & ::= & \fixnum ~|~ \signed ~|~ \unsigned ~|~ \int ~|~ \intish \\ 452 | & | & \double ~|~ \doublish ~|~ \extern ~|~ \unk ~|~ \void \\ 453 | %% \sigma, \tau & ::= & \double ~|~ \signed ~|~ \unsigned ~|~ \fixnum ~|~ \extern \\ 454 | %% & | & \bit ~|~ \int ~|~ \boolish ~|~ \intish ~|~ \void ~|~ \unk \\ 455 | \end{array} 456 | \] 457 | 458 | These types are arranged in a subtyping hierarchy, defined by: 459 | \[ 460 | \begin{array}{rcl} 461 | \fixnum & <: & \signed, \unsigned \\ 462 | \signed, \unsigned & <: & \int, \extern \\ 463 | \double & <: & \extern, \doublish \\ 464 | \unk & <: & \intish, \doublish \\ 465 | \int & <: & \intish \\ 466 | \end{array} 467 | \] 468 | 469 | Figure~\ref{fig:subtypes} depicts this subtyping hierarchy 470 | visually. Note that some types are presented in white boxes and others 471 | in gray boxes. The white boxes represent types that may escape into 472 | external JavaScript; the gray types are internal to the 473 | $\mathjs{asm.js}$ module and cannot escape. This allows an optimizing 474 | implementation to use unboxed representations that would otherwise be 475 | illegal. What follows is an explanation of each type. 476 | 477 | \begin{figure}[tb] 478 | \centering 479 | \includegraphics[scale=0.5]{subtypes} 480 | \caption{The hierarchy of expression types.} 481 | \label{fig:subtypes} 482 | \end{figure} 483 | 484 | \subsubsection*{The $\extern$ type} 485 | 486 | This abstract type represents the root of all types that can escape 487 | back into ordinary JavaScript. Any type that is a subtype of $\extern$ 488 | must carry enough information in its internal representation in an 489 | optimizing virtual machine to faithfully convert back into a dynamic 490 | JavaScript value. 491 | 492 | \subsubsection*{The $\double$ type} 493 | 494 | This is the type of double-precision floating-point values. An 495 | optimizing engine can represent these as unboxed 64-bit floats. If 496 | they escape into external JavaScript they must of course be wrapped 497 | back up as JavaScript values according to the JavaScript engine's 498 | value representation. 499 | 500 | \subsubsection*{The $\signed$ and $\unsigned$ types} 501 | 502 | These are the types of signed and unsigned 32-bit integers, 503 | respectively. For an optimizing engine, their representation can be 504 | the same: an unboxed 32-bit integer. If a value escapes into external 505 | JavaScript, the sign is used to determine which JavaScript value it 506 | represents. For example, the bit pattern $\mathjs{0xffffffff}$ 507 | represents either the JavaScript value $\mathjs{-1}$ or 508 | $\mathjs{4294967295}$, depending on the signedness of the type. 509 | 510 | \subsubsection*{The $\fixnum$ type} 511 | 512 | This type represents integers in the range $[0, 2^{31})$, which are 513 | both valid signed and unsigned integers. Literals in this range are 514 | given the type $\fixnum$ rather than $\signed$ or $\unsigned$, since 515 | either way they have the same representation as an unboxed 32-bit 516 | integer. 517 | 518 | \subsubsection*{The $\int$ type} 519 | 520 | This is the type of 32-bit integers whose sign is not known. Again, 521 | optimizing engines can represent them as unboxed 32-bit integers. But 522 | because the sign is not known, they cannot be allowed to escape to 523 | external JavaScript, as it is impossible to determine exactly which 524 | JavaScript value they represent. While this might not seem like a very 525 | useful type, the JavaScript bitwise coercions can be used to force an 526 | $\int$ value back to $\signed$ or $\unsigned$ without any loss of 527 | data. 528 | 529 | The conditional operators also produce the $\int$ type, even though, 530 | according to the JavaScript semantics, they produce boolean 531 | values. Since boolean values convert to the integer values 532 | $\mathjs{0}$ and $\mathjs{1}$, it is sound to represent the boolean 533 | values in an optimizing engine as integers, even while allowing them 534 | to be stored in integer values and passed to integer 535 | arithmetic. Similarly, integer values can be treated as ``boolish'' in 536 | JavaScript when used in conditional contexts, so it is safe to store 537 | boolean values as unboxed integers even when being used in the test 538 | expression of an $\mathjs{if}$ statement and the like. 539 | 540 | \subsubsection*{The $\intish$ type} 541 | 542 | The JavaScript arithmetic operations can be performed on 32-bit 543 | integer values, but their results may produce non-integer values. For 544 | example, addition and subtraction can overflow to large numbers that 545 | exceed the 32-bit integer range, and integer division can produce a 546 | non-integer value. However, if the result is coerced back to an 547 | integer, the resulting arithmetic operation behaves identically to the 548 | typical corresponding machine operation (i.e., integer addition, 549 | subtraction, or division). The $\intish$ type represents the result of 550 | a JavaScript integer artihmetic operation that must be coerced back to 551 | integer with an explicit coercion. Because this type can only be used 552 | as an argument to a coercion (or silently ignored in an expression 553 | statement), $\mathjs{asm.js}$ integer arithmetic can always be 554 | implemented in an optimizing engine by the machine integer artithmetic 555 | operations. 556 | 557 | The one arithmetic operation that does not quite fit into this story 558 | is multiplication. Multiplying two large integers can results in a 559 | large enough double that some lower bits of precision are lost, so 560 | that coercing the result back to integer does {\it not} behave 561 | identically to the machine operation. The use of the proposed ES6 562 | $\mathjs{Math.imul}$ function~\cite{imul} as an FFI function is 563 | recommended as the proper means of implementing integer 564 | multiplication. 565 | 566 | In short: 567 | 568 | \begin{quote} 569 | The $\intish$ type represents the result of integer operations that 570 | must be dropped or immediately coerced via $\mathit{ToInt32}$ or 571 | $\mathit{ToUint32}$. 572 | \end{quote} 573 | 574 | \subsubsection*{The $\doublish$ type} 575 | 576 | Similarly, the $\doublish$ type represents operations that are 577 | expected to produce a $\double$ but may produce additional junk that 578 | must be coerced back to a number via $\mathit{ToNumber}$. In 579 | particular, reading from the typed array may produce 580 | $\mathjs{undefined}$, and calling FFI functions may produce an 581 | arbitrary JavaScript value. Thus: 582 | 583 | \begin{quote} 584 | The $\doublish$ type represents the result of numeric operations that 585 | must be dropped or immediately coerced via $\mathit{ToNumber}$. 586 | \end{quote} 587 | 588 | \subsubsection*{The $\unk$ type} 589 | 590 | Calling an external JavaScript function through the FFI results in an 591 | arbitrary JavaScript value. Because $\mathjs{asm.js}$ is designed to 592 | avoid dealing with general values, the result must be coerced to one 593 | of the other types before it can be used. The $\unk$ type represents 594 | one of these result values before being coerced to an integer or 595 | double. 596 | 597 | \subsubsection*{The $\void$ type} 598 | 599 | A function that returns $\mathjs{undefined}$ is considered to have the 600 | $\void$ result type. The $\mathjs{undefined}$ value is not actually a 601 | first-class value in $\mathjs{asm.js}$. It can only be ignored via an 602 | expression statement. This avoids having to represent it at all as 603 | data. 604 | 605 | \subsection{Global types} 606 | 607 | Validation tracks the types not only of expressions and local 608 | variables but also global variables, FFI imports, and functions. In 609 | addition to variables of expression type, globals may also be typed 610 | array views on the module's buffer, imported FFI constants and 611 | functions, and functions defined in the $\mathjs{asm.js}$ module. 612 | \[ 613 | \gamma ::= \tau ~|~ \view{n}{\tau} ~|~ (\funty{\seq{\sigma}}{\tau}) \land \cdots \land (\funty{\seq{\sigma'}}{\tau'}) ~|~ \Fun 614 | \] 615 | 616 | The type of a typed array tracks the number of bytes per element and 617 | the elements' value type ($\intish$ or $\doublish$). A function type 618 | may be overloaded to allow different parameter types, potentially 619 | providing different result types for each overloading. 620 | 621 | \subsection{Operator types} 622 | 623 | Every operator, unary or binary, has an overloaded function type. 624 | This overloading corresponds to the different machine operations used 625 | in an optimizing engine to implement the various cases. Whereas 626 | JavaScript generally needs to choose the behavior of operators 627 | dynamically, $\mathjs{asm.js}$ makes it possible to resolve the 628 | overloaded operators statically based on the types of the operands. 629 | 630 | The types of the arithmetic operators are as follows: 631 | \[ 632 | \begin{array}{rcc@{\ }l} 633 | \mathjs{+} & : & & \funty{\double, \double}{\double} \\ 634 | % & & \land & \funty{\int, \int}{\intish} \\ 635 | \mathjs{-} & : & & \funty{\doublish, \doublish}{\double} \\ 636 | % & & \land & \funty{\int, \int}{\intish} \\ 637 | \mathjs{*} & : & & \funty{\doublish, \doublish}{\double} \\ 638 | \mathjs{/} & : & & \funty{\doublish, \doublish}{\double} \\ 639 | & & \land & \funty{\signed, \signed}{\intish} \\ 640 | & & \land & \funty{\unsigned, \unsigned}{\intish} \\ 641 | \mathjs{\%} & : & & \funty{\doublish, \doublish}{\double} \\ 642 | & & \land & \funty{\signed, \signed}{\int} \\ 643 | & & \land & \funty{\unsigned, \unsigned}{\int} \\ 644 | \end{array} 645 | \] 646 | %% Note that some operations produce the type $\intish$, indicating that 647 | %% their result must be immediately coerced back to an integer. Note also 648 | %% that some operations accept $\doublish$ arguments, because they coerce 649 | %% them immediately with $\mathit{ToNumber}$. (This is not the case for 650 | %% the binary $\mathjs{+}$ operator, which therefore requires its 651 | %% argument to be a true $\double$.) 652 | The type of addition and subtraction only deals with floating-point 653 | arithmetic; for integer addition and subtraction see 654 | Section~\ref{sec:exprjudge}. Similarly, the $\mathjs{*}$ operator 655 | provides only floating-point multiplication; integer multiplication 656 | can be implemented via the $\imul$ function. The $\mathjs{/}$ operator 657 | does provide both floating-point and integer division; for the latter 658 | it produces the type $\intish$, which requires a coercion via the 659 | bitwise operators to produce the proper integer result. The 660 | $\mathjs{\%}$ operator works as either a floating-point or integer 661 | operator and produces the correct result without any need for a 662 | coercion. 663 | 664 | The bitwise operators are one of only two places in the language that 665 | can consume an $\intish$ expression. Every one of these operators can 666 | be composed with the arithmetic operators to produce the correct 667 | behavior of integer arithmetic. 668 | \[ 669 | \begin{array}{rcc@{\ }l} 670 | \mathjs{|}, \mathjs{\&}, \mathjs{\^{}}, \mathjs{<<}, \mathjs{>>} 671 | & : & & \funty{\intish, \intish}{\signed} \\ 672 | \mathjs{>>>} & : & & \funty{\intish, \intish}{\unsigned} \\ 673 | \end{array} 674 | \] 675 | 676 | The conditional operators rely on the sign of their input and produce 677 | a boolean result: 678 | \[ 679 | \begin{array}{rcc@{\ }l} 680 | \mathjs{<}, \mathjs{<=}, \mathjs{>}, \mathjs{>=}, \mathjs{==}, \mathjs{!=} 681 | & : & & \funty{\signed, \signed}{\int} \\ 682 | & & \land & \funty{\unsigned, \unsigned}{\int} \\ 683 | & & \land & \funty{\double, \double}{\int} \\ 684 | \end{array} 685 | \] 686 | 687 | Finally, the unary operators have function types as well; unary 688 | operations can also serve as coercions: the $\mathjs{+}$ operator 689 | converts integers and the results of typed array reads and FFI calls 690 | to $\double$, and the $\mathjs{\~{}}$ operator can be used to coerce 691 | $\intish$ expressions, similar to the two-argument bitwise operators. 692 | \[ 693 | \begin{array}{rcc@{\ }l} 694 | \mathjs{+} & : & & \funty{\signed}{\double} \\ 695 | & & \land & \funty{\unsigned}{\double} \\ 696 | & & \land & \funty{\doublish}{\double} \\ 697 | \mathjs{-} & : & & \funty{\int}{\intish} \\ 698 | & & \land & \funty{\doublish}{\double} \\ 699 | \mathjs{\~{}} & : & & \funty{\intish}{\signed} \\ 700 | \end{array} 701 | \] 702 | 703 | \section{Validation} 704 | 705 | This section describes the $\mathjs{asm.js}$ validation process, which 706 | is essentially a static type system. 707 | 708 | \subsection{Standard libraries} 709 | 710 | The JavaScript $\mathjs{Math}$ API is recognized as a typed standard 711 | library; most of its functions are allowed as imports with the same 712 | name and are given appropriate function types. These functions must be 713 | passed into the import environment under their respective names (e.g., 714 | $\mathjs{Math.sin}$ under the name $g\mathjs{.Math.sin}$). Building in 715 | support for these standard functions allows optimizing engines to 716 | build special support for them---particularly the $\imul$ operation, 717 | which can be implemented with hardware multiplication instructions. 718 | \[ 719 | \begin{array}{rcl} 720 | M(\mathtt{acos}), M(\mathtt{asin}), M(\mathtt{atan}) & = & \funty{\doublish}{\double} \\ 721 | M(\mathtt{cos}), M(\mathtt{sin}), M(\mathtt{tan}) & = & \funty{\doublish}{\double} \\ 722 | M(\mathtt{ceil}), M(\mathtt{floor}) & = & \funty{\doublish}{\double} \\ 723 | M(\mathtt{exp}), M(\mathtt{log}), M(\mathtt{sqrt}) & = & \funty{\doublish}{\double} \\ 724 | M(\mathtt{abs}) & = & \funty{\signed}{\unsigned} \\ 725 | & \land & \funty{\doublish}{\double} \\ 726 | M(\mathtt{atan2}), M(\mathtt{pow}) & = & \funty{\doublish, \doublish}{\double} \\ 727 | M(\imul) & = & \funty{\int, \int}{\signed} \\ 728 | M(\mathtt{random}) & = & \funty{}{\double} \\ 729 | M(\mathtt{E}) & = & \double \\ 730 | M(\mathtt{LN10}), M(\mathtt{LN2}), M(\mathtt{LOG2E}), M(\mathtt{LOG10E}) & = & \double \\ 731 | M(\mathtt{PI}) & = & \double \\ 732 | M(\mathtt{SQRT1\_2}), M(\mathtt{SQRT2}) & = & \double \\ 733 | \end{array} 734 | \] 735 | 736 | \subsection{View types} 737 | 738 | The typed array constructors are also recognized as imports in the 739 | import environment, each under its standard name. Calling the various 740 | typed arrays constructors on the buffer produces typed array views of 741 | various types. 742 | \[ 743 | \begin{array}{rcl} 744 | V(\mathtt{Uint8Array}), V(\mathtt{Int8Array}) & = & \view{1}{\intsm} \\ 745 | V(\mathtt{Uint16Array}), V(\mathtt{Int16Array}) & = & \view{2}{\intsm} \\ 746 | V(\mathtt{Uint32Array}), V(\mathtt{Int32Array}) & = & \view{4}{\intsm} \\ 747 | V(\mathtt{Float32Array}) & = & \view{4}{\doublesm} \\ 748 | V(\mathtt{Float64Array}) & = & \view{8}{\doublesm} \\ 749 | \end{array} 750 | \] 751 | 752 | \subsection{Global constants} 753 | 754 | \[ 755 | \begin{array}{rcl} 756 | G(\mathtt{Infinity}), G(\mathtt{NaN}) & = & \double \\ 757 | \end{array} 758 | \] 759 | 760 | \subsection{Annotations} 761 | 762 | Type annotations are provided in the form of explicit 763 | coercions. Variables in $\mathjs{asm.js}$ are always taken to have the 764 | type $\double$ or $\int$, never $\signed$ or $\unsigned$. This is 765 | because they are intended to be representable as unboxed 32-bit words 766 | in memory or registers, which are agnostic about what sign to 767 | interpret the bits with. 768 | 769 | \[ 770 | \begin{array}{rcl} 771 | \vartype(\todouble{x}), \vartype(r) & = & \double \\ 772 | \vartype(x\mathjs{|0}), \vartype(i) & = & \int \mbox{ $(-2^{31} \leq i < 2^{32})$} 773 | \end{array} 774 | \] 775 | 776 | Function return types are determined by explicit coercions in their 777 | return statements. Function return types are always explicit about 778 | their sign so that they can be exported to external JavaScript. 779 | 780 | \[ 781 | \begin{array}{rcl} 782 | \rettype(\todouble{e}), \rettype(r) & = & \double \\ 783 | \rettype(e\mathjs{|0}), \rettype(i) & = & \signed \mbox{ $(-2^{31} \leq i < 2^{31})$} \\ 784 | \rettype(\epsilon) & = & \void \\ 785 | \end{array} 786 | \] 787 | 788 | The type of a function can be extracted by looking at its parameter 789 | annotations and return statements. 790 | 791 | \[ 792 | \begin{array}{l} 793 | \funtype(\fun{f}{\seq{x}}{\seq{x\mathjs{ = }\mathit{ann}_x\mathjs{;}}\ \seq{\var{\seq{y\mathjs{ = }v}}}\ \mathit{ss}\ \return{[\mathit{re}]}}) = \funty{\seq{\sigma}}{\tau} \\ 794 | \qquad \mbox{where } \forall i . \vartype(\mathit{ann}_{x_i}) = \sigma_i \\ 795 | \qquad \mbox{and } \rettype([\mathit{re}]) = \tau \\ 796 | \funtype(\fun{f}{\seq{x}}{\seq{x\mathjs{ = }\mathit{ann}_x\mathjs{;}}\ \seq{\var{\seq{y\mathjs{ = }v}}}\ \mathit{ss}\ s}) = \funty{\seq{\sigma}}{\void} \\ 797 | \qquad \mbox{where } \forall i . \vartype(\mathit{ann}_{x_i}) = \sigma_i \\ 798 | \qquad \mbox{and } s \not= \return{[\mathit{re}]} \\ 799 | \funtype(\fun{f}{\seq{x}}{\seq{x\mathjs{ = }\mathit{ann}_x\mathjs{;}}\ \seq{\var{\seq{y\mathjs{ = }v}}}\ \epsilon}) = \funty{\seq{\sigma}}{\void} \\ 800 | \qquad \mbox{where } \forall i . \vartype(\mathit{ann}_{x_i}) = \sigma_i \\ 801 | \end{array} 802 | \] 803 | 804 | The types of import expressions are determined by the standard library 805 | metafunctions $G$, $M$, and $V$ or by their coercion. A foreign import 806 | with no coercion is assumed to be a function. 807 | 808 | \[ 809 | \begin{array}{l} 810 | \imptype(\mathit{global}\mathjs{.}x) = G(x) \\ 811 | \imptype(\mathit{global}\mathjs{.Math.}x) = M(x) \\ 812 | \imptype(\mathjs{new }\funcall{\mathit{global}\mathjs{.}x}{\mathit{buffer}}) = V(y) \\ 813 | \imptype(\mathit{foreign}\mathjs{.}x\mathjs{|0}) = \signed \\ 814 | \imptype(\mathjs{+}\mathit{foreign}\mathjs{.}x) = \double \\ 815 | \imptype(\mathit{foreign}\mathjs{.}x) = \Fun \\ 816 | \end{array} 817 | \] 818 | 819 | \subsection{Module validation} 820 | 821 | Module validation takes an $\mathjs{asm.js}$ module and constructs a 822 | {\it global environment} $\Delta$, which is used to track the types of 823 | all globals: imports, buffer views, global variables, and functions. 824 | \[ 825 | \begin{array}{rcl} 826 | \mu & ::= & \mut ~|~ \imm \\ 827 | \Delta & ::= & \{ \seq{x : \mu\,\gamma} \} \\ 828 | \end{array} 829 | \] 830 | Validation then uses this environment to check the imports, exports, 831 | and function definitions. 832 | 833 | \newsavebox{\modjudge} 834 | \begin{lrbox}{\modjudge} 835 | \begin{minipage}[t]{3in} 836 | \vspace{-.25in} 837 | \[ 838 | \begin{array}{rll} 839 | & \mathjs{function }[f]\mathjs{(}[\mathit{global}[, \mathit{foreign}[, \mathit{buffer}]]]\mathjs{) \char123{}} \\ 840 | & \qquad\mathjs{"use asm";} \\ 841 | & \qquad\seq{\var{\seq{x \mathjs{ = } \mathit{imp}}}} \\ 842 | \vdash & \qquad\seq{\mathit{fn}_g} & \ok \\ 843 | & \qquad\seq{\var{\seq{y \mathjs{ = } v}}} \\ 844 | & \qquad\mathit{exp} \\ 845 | & \mathjs{\char125{}} \\ 846 | \end{array} 847 | \] 848 | \end{minipage} 849 | \end{lrbox} 850 | 851 | \[ 852 | \begin{array}{c} 853 | \multicolumn{1}{r}{\fbox{$\progjudge{\mathit{mod}}$}} 854 | \rulebreak 855 | \inferrule* [lab=\rel{T-Module}] 856 | {\Delta = \{ \seq{x : \imm\,\imptype(\mathit{imp})}, \seq{g : \imm\,\funtype(\mathit{fn}_g)}, \seq{y : \mut\,\vartype(v)} \} \\\\ 857 | %%\forall i . \impjudge{[g]}{[e]}{[b]}{\Delta}{\mathit{imp}_x} \\ 858 | \seq{x}, \seq{y}, \seq{g}, [f], [\mathit{global}], [\mathit{foreign}], [\mathit{buffer}]\ \mbox{distinct} \\ 859 | \forall i . \fnjudge{\Delta}{\mathit{fn}_f} \\ 860 | \forall i . \expjudge{\Delta}{\mathit{exp}}} 861 | {\usebox{\modjudge}} 862 | % {\progjudge{\fun{[m]}{[g[, e[, b]]}{\mathjs{"use asm";}\ \seq{\mathit{imp}_x}\ \seq{\mathit{fn}_f}\ \seq{\var{\seq{y \mathjs{ = } v}}}\ \mathit{exp}}}} 863 | \end{array} 864 | \] 865 | 866 | For simplicity of the specification, we leave as an assumption that 867 | the same $\mathit{global}$, $\mathit{foreign}$, and $\mathit{buffer}$ 868 | variables are used consistently throughout the module. An actual 869 | validator must check this assumption. Similarly, we assume the 870 | existence of a single $\mathit{mask}$ constant used throughout the 871 | module, which a real validator would have to check. 872 | 873 | %% \subsection{Import validation} 874 | 875 | %% Declarations in the import section of an $\mathjs{asm.js}$ module can 876 | %% be known library functions, unknown FFI functions, typed array views, 877 | %% or imported constants. 878 | 879 | %% \[ 880 | %% \begin{array}{c} 881 | %% \multicolumn{1}{r}{\fbox{$\impjudge{[g]}{[e]}{[b]}{\Delta}{\mathit{imp}}$}} 882 | %% \rulebreak 883 | %% \inferrule* [lab=\rel{T-ImportStd}] 884 | %% {\iejudge{[g]}{[e]}{\mathit{ie}}{\Delta(x)}} 885 | %% {\impjudge{[g]}{[e]}{[b]}{\Delta}{\var{x \mathjs{ = } \mathit{ie}}}} 886 | %% \qquad 887 | %% \inferrule* [lab=\rel{T-ImportFFI}] 888 | %% {\iejudge{[g]}{[e]}{\mathit{ie}}{\epsilon} \\\\ 889 | %% \Delta(x) = \Fun} 890 | %% {\impjudge{[g]}{[e]}{[b]}{\Delta}{\var{x \mathjs{ = } \mathit{ie}}}} 891 | %% \\ \\ 892 | %% \inferrule* [lab=\rel{T-ImportInt}] 893 | %% {\iejudge{[g]}{[e]}{\mathit{ie}}{\tau} \\\\ 894 | %% \tau <: \intish \\ 895 | %% \Delta(x) = \signed} 896 | %% {\impjudge{[g]}{[e]}{[b]}{\Delta}{\var{x \mathjs{ = } \mathit{ie}\mathjs{|0}}}} 897 | %% \qquad 898 | %% \inferrule* [lab=\rel{T-ImportDouble}] 899 | %% {\iejudge{[g]}{[e]}{\mathit{ie}}{\tau} \\\\ 900 | %% \tau <: \doublish \\ 901 | %% \Delta(x) = \double} 902 | %% {\impjudge{[g]}{[e]}{[b]}{\Delta}{\var{x \mathjs{ = +}\mathit{ie}}}} 903 | %% \\ \\ 904 | %% \inferrule* [lab=\rel{T-ImportView}] 905 | %% {\Delta(x) = V(y)} 906 | %% {\impjudge{g}{[e]}{b}{\Delta}{\var{x \mathjs{ = new } g\mathjs{.}y(b)}}} 907 | %% %% \\ \\ 908 | %% %% \inferrule* [lab=\rel{T-ImportStd}] 909 | %% %% {\Delta(x) = M(y)} 910 | %% %% {\impjudge{[g]}{[e]}{[b]}{\Delta}{\var{x \mathjs{ = } e\mathjs{.}y}}} 911 | %% %% \qquad 912 | %% %% \inferrule* [lab=\rel{T-ImportFFI}] 913 | %% %% {y \not\in\dom(M), \dom(A) \\\\ 914 | %% %% \Delta(x) = \Fun} 915 | %% %% {\impjudge{[g]}{e}{[b]}{\Delta}{\var{x \mathjs{ = } c\mathjs{.}y}}} 916 | %% %% \\ \\ 917 | %% %% \inferrule* [lab=\rel{T-View}] 918 | %% %% {\Delta(x) = \view{n}{A(y)}} 919 | %% %% {\impjudge{[g]}{e}{b}{\Delta}{\var{x \mathjs{ = new } c\mathjs{.}y(b)}}} 920 | %% %% \\ \\ 921 | %% %% \inferrule* [lab=\rel{T-ImportInt}] 922 | %% %% {\Delta(x) = \int} 923 | %% %% {\impjudge{[g]}{e}{[b]}{\Delta}{\var{x \mathjs{ = }c\mathjs{.}y\mathjs{|0}}}} 924 | %% %% \qquad 925 | %% %% \inferrule* [lab=\rel{T-ImportDouble}] 926 | %% %% {\Delta(x) = \double} 927 | %% %% {\impjudge{[g]}{e}{[b]}{\Delta}{\var{x \mathjs{ = +}c\mathjs{.}y}}} 928 | %% \end{array} 929 | %% \] 930 | 931 | %% \[ 932 | %% \begin{array}{c} 933 | %% \multicolumn{1}{r}{\fbox{$\iejudge{[g]}{[e]}{\mathit{ie}}{[\gamma]}$}} 934 | %% \rulebreak 935 | %% \inferrule* [lab=\rel{T-Global}] 936 | %% {y \in \dom(G)} 937 | %% {\iejudge{g}{[e]}{g\mathjs{.}y}{G(y)}} 938 | %% \qquad 939 | %% \inferrule* [lab=\rel{T-Math}] 940 | %% {y \in \dom(M)} 941 | %% {\iejudge{g}{[e]}{g\mathjs{.Math.}y}{M(y)}} 942 | %% \qquad 943 | %% \inferrule* [lab=\rel{T-FFI}] 944 | %% { } 945 | %% {\iejudge{[g]}{e}{e\mathjs{.}y}{\epsilon}} 946 | %% \end{array} 947 | %% \] 948 | 949 | \subsection{Function validation} 950 | 951 | Function validation proceeds in several steps. First, a local 952 | environment is constructed with bindings for the parameters and local 953 | variables, based on their annotations and initializers, 954 | respectively. Next, the body of the function is checked in this 955 | environment. Finally, if the function has a non-$\void$ return type, 956 | the body is checked to ensure that all control flow paths definitely 957 | return a value (see Section~\ref{sec:cfa}). 958 | 959 | \[ 960 | \begin{array}{c} 961 | \multicolumn{1}{r}{\fbox{$\fnjudge{\Delta}{\mathit{fn}}$}} 962 | \rulebreak 963 | \inferrule* [lab=\rel{T-Function}] 964 | {\seq{x}, \seq{y}\ \mbox{distinct} \\ 965 | \Delta(f) = \imm\,\funty{\seq{\sigma}}{\tau} \\ 966 | \seq{\sigma} = \seq{\vartype(\mathit{ann}_x)} \\\\ 967 | \sjudge{\Delta}{\{ \seq{x : \sigma}, \seq{y : \vartype(v)} \}}{\tau}{\mathit{ss}}} 968 | {\fnjudge{\Delta}{\fun{f}{\seq{x}}{\seq{x\mathjs{ = }\mathit{ann}_x\mathjs{;}}\ \seq{\var{\seq{y\mathjs{ = }v}}}\ \mathit{ss}}}} 969 | \end{array} 970 | \] 971 | 972 | \subsection{Export validation} 973 | 974 | Export validation ensures that all exports are functions. 975 | 976 | \[ 977 | \begin{array}{c} 978 | \multicolumn{1}{r}{\fbox{$\expjudge{\Delta}{\mathit{exp}}$}} 979 | \rulebreak 980 | \inferrule* [lab=\rel{T-Singleton}] 981 | {\Delta(f) = \imm\,\funty{\seq{\sigma}}{\tau}} 982 | {\expjudge{\Delta}{\return{f}}} 983 | \qquad 984 | \inferrule* [lab=\rel{T-Module}] 985 | {\forall f . \Delta(f) = \imm\,\funty{\seq{\sigma}}{\tau}} 986 | {\expjudge{\Delta}{\return{\mathjs{\char123{} } \seq{x \mathjs{:} f} \mathjs{ \char125{}}}}} 987 | \end{array} 988 | \] 989 | 990 | \subsection{Statement list validation} 991 | 992 | \[ 993 | \begin{array}{c} 994 | \multicolumn{1}{r}{\fbox{$\sjudge{\Delta}{\Gamma}{\tau}{\mathit{ss}}$}} 995 | \rulebreak 996 | \inferrule* [lab=\rel{T-Statements}] 997 | {\forall i . \sjudge{\Delta}{\Gamma}{\tau}{s_i}} 998 | {\sjudge{\Delta}{\Gamma}{\tau}{\seq{s}}} 999 | \end{array} 1000 | \] 1001 | 1002 | 1003 | \subsection{Statement validation} 1004 | 1005 | \newsavebox{\switchcontrol} 1006 | \begin{lrbox}{\switchcontrol} 1007 | \begin{minipage}[t]{2.87in} 1008 | \vspace{-.25in} 1009 | \[ 1010 | \varepsilon = \left\{ \begin{array}{ll} 1011 | \mustret & \mbox{if}\ \varepsilon_n = \mustret \land \forall i . \varepsilon_i \cup \emptyset = \emptyset \\ 1012 | \bigcup \varepsilon_i - \{ \epsilon \} & \mbox{otherwise} 1013 | \end{array} \right. 1014 | \] 1015 | \end{minipage} 1016 | \end{lrbox} 1017 | 1018 | \[ 1019 | \begin{array}{c} 1020 | \multicolumn{1}{r}{\fbox{$\sjudge{\Delta}{\Gamma}{\tau}{s}$}} 1021 | \rulebreak 1022 | \inferrule* [lab=\rel{T-Block}] 1023 | {\sjudge{\Delta}{\Gamma}{\tau}{\mathit{ss}}} 1024 | {\sjudge{\Delta}{\Gamma}{\tau}{\block{\mathit{ss}}}} 1025 | \qquad 1026 | \inferrule* [lab=\rel{T-ExprStmt}] 1027 | {\exprjudge{\Delta}{\Gamma}{e}{\sigma}} 1028 | {\sjudge{\Delta}{\Gamma}{\tau}{e\mathjs{;}}} 1029 | \qquad 1030 | \inferrule* [lab=\rel{T-EmptyStatement}] 1031 | { } 1032 | {\sjudge{\Delta}{\Gamma}{\tau}{\mathjs{;}}} 1033 | \\ \\ 1034 | \inferrule* [lab=\rel{T-If}] 1035 | {\exprjudge{\Delta}{\Gamma}{e}{\boolish} \\\\ 1036 | \sjudge{\Delta}{\Gamma}{\tau}{s}} 1037 | {\sjudge{\Delta}{\Gamma}{\tau}{\ifone{e}{s}}} 1038 | \qquad 1039 | \inferrule* [lab=\rel{T-IfElse}] 1040 | {\exprjudge{\Delta}{\Gamma}{e}{\boolish} \\\\ 1041 | \sjudge{\Delta}{\Gamma}{\tau}{s_1} \\ 1042 | \sjudge{\Delta}{\Gamma}{\tau}{s_2}} 1043 | {\sjudge{\Delta}{\Gamma}{\tau}{\iftwo{e}{s_1}{s_2}}} 1044 | \\ \\ 1045 | \inferrule* [lab=\rel{T-ReturnExpr}] 1046 | {\exprjudge{\Delta}{\Gamma}{\mathit{re}}{\tau} \\\\ 1047 | \rettype(\mathit{re}) = \tau} 1048 | {\sjudge{\Delta}{\Gamma}{\tau}{\return{\mathit{re}}}} 1049 | %% \qquad 1050 | %% \inferrule* [lab=\rel{T-ReturnDouble}] 1051 | %% {\exprjudge{\Delta}{\Gamma}{e}{\double}} 1052 | %% {\sjudge{\Delta}{\Gamma}{\double}{\return{\mathjs{+}e}}} 1053 | \qquad 1054 | \inferrule* [lab=\rel{T-ReturnVoid}] 1055 | { } 1056 | {\sjudge{\Delta}{\Gamma}{\void}{\mathtt{return;}}} 1057 | \\ \\ 1058 | \inferrule* [lab=\rel{T-While}] 1059 | {\exprjudge{\Delta}{\Gamma}{e}{\int} \\\\ 1060 | \sjudge{\Delta}{\Gamma}{\tau}{s}} 1061 | {\sjudge{\Delta}{\Gamma}{\tau}{\while{e}{s}}} 1062 | \qquad 1063 | \inferrule* [lab=\rel{T-DoWhile}] 1064 | {\sjudge{\Delta}{\Gamma}{\tau}{s} \\\\ 1065 | \exprjudge{\Delta}{\Gamma}{e}{\int}} 1066 | {\sjudge{\Delta}{\Gamma}{\tau}{\dowhile{s}{e}}} 1067 | \\ \\ 1068 | \inferrule* [lab=\rel{T-For}] 1069 | {[\exprjudge{\Delta}{\Gamma}{e_1}{\sigma_1}] \\ 1070 | [\exprjudge{\Delta}{\Gamma}{e_2}{\int}] \\ 1071 | [\exprjudge{\Delta}{\Gamma}{e_3}{\sigma_3}] \\\\ 1072 | \sjudge{\Delta}{\Gamma}{\tau}{s}} 1073 | {\sjudge{\Delta}{\Gamma}{\tau}{\for{[e_1]}{[e_2]}{[e_3]}{s}}} 1074 | \\ \\ 1075 | \inferrule* [lab=\rel{T-Break}] 1076 | { } 1077 | {\sjudge{\Delta}{\Gamma}{\tau}{\brkl{[\mathit{lab}]}}} 1078 | \qquad 1079 | \inferrule* [lab=\rel{T-Continue}] 1080 | { } 1081 | {\sjudge{\Delta}{\Gamma}{\tau}{\contl{[\mathit{lab}]}}} 1082 | \qquad 1083 | \inferrule* [lab=\rel{T-Label}] 1084 | {\sjudge{\Delta}{\Gamma}{\tau}{s}} 1085 | {\sjudge{\Delta}{\Gamma}{\tau}{\lab{\mathit{lab}}{s}}} 1086 | \\ \\ 1087 | \inferrule* [lab=\rel{T-Switch}] 1088 | {\exprjudge{\Delta}{\Gamma}{e}{\sigma} \\ 1089 | \sigma \in \{ \signed, \unsigned \} \\ 1090 | \forall i . \exprjudge{\Delta}{\Gamma}{v_i}{\sigma} \\\\ 1091 | \forall i . \sjudge{\Delta}{\Gamma}{\tau}{\mathit{ss}_i} \\ 1092 | [\sjudge{\Delta}{\Gamma}{\tau}{\mathit{ss}}]} 1093 | {\sjudge{\Delta}{\Gamma}{\tau}{\switch{e}{\seq{\mathjs{case }v_i\mathjs{:}\,\mathit{ss}_i}\ [\mathjs{default:}\,\mathit{ss}]}}} 1094 | \end{array} 1095 | \] 1096 | 1097 | \subsection{Case validation} 1098 | 1099 | \[ 1100 | \begin{array}{c} 1101 | \multicolumn{1}{r}{\fbox{$\sjudge{\Delta}{\Gamma}{\tau}{\mathit{cd}}$}} 1102 | \rulebreak 1103 | \inferrule* [lab=\rel{T-Case}] 1104 | {\sjudge{\Delta}{\Gamma}{\tau}{\mathit{ss}}} 1105 | {\sjudge{\Delta}{\Gamma}{\tau}{\mathjs{case }v\mathjs{:}\,\mathit{ss}}} 1106 | \qquad 1107 | \inferrule* [lab=\rel{T-Default}] 1108 | {\sjudge{\Delta}{\Gamma}{\tau}{\mathit{ss}}} 1109 | {\sjudge{\Delta}{\Gamma}{\tau}{\mathjs{default:}\,\mathit{ss}}} 1110 | \end{array} 1111 | \] 1112 | 1113 | %% \subsection{Must-return analysis} 1114 | %% \label{sec:cfa} 1115 | 1116 | %% \[ 1117 | %% \begin{array}{rcl} 1118 | %% \breaks(\seq{s}) & = & \bigcup_i \breaks(s_i) \\ 1119 | %% \breaks(\block{\mathit{ss}}) & = & \breaks(\mathit{ss}) \\ 1120 | %% \breaks(\ifone{e}{s}) & = & \breaks(s) \\ 1121 | %% \breaks(\iftwo{e}{s_1}{s_2}) & = & \breaks(s_1) \cup \breaks(s_2) \\ 1122 | %% \breaks(\while{e}{s}) & = & \breaks(s) - \{ \epsilon \} \\ 1123 | %% \breaks(\dowhile{s}{e}) & = & \breaks(s) - \{ \epsilon \} \\ 1124 | %% \breaks(\for{[e_1]}{[e_2]}{[e_3]}{s}) & = & \breaks(s) - \{ \epsilon \} \\ 1125 | %% \breaks(\brk) & = & \{ \epsilon \} \\ 1126 | %% \breaks(\brkl{\mathit{lab}}) & = & \{ \mathit{lab} \} \\ 1127 | %% \breaks(\lab{\mathit{lab}}{s}) & = & \breaks(s) - \{ \mathit{lab} \} \\ 1128 | %% \breaks(\switch{e}{\seq{\mathit{cd}}}) & = & \bigcup_i \breaks(\mathit{cd}_i) - \{ \epsilon \} \\ 1129 | %% \breaks(s) \mbox{ (otherwise)} & = & \emptyset \\ 1130 | %% \breaks(\mathjs{case }v\mathjs{:}\,\mathit{ss}) & = & \breaks(\mathit{ss}) \\ 1131 | %% \breaks(\mathjs{default:}\,\mathit{ss}) & = & \breaks(\mathit{ss}) \\ 1132 | %% \end{array} 1133 | %% \] 1134 | 1135 | %% \[ 1136 | %% \begin{array}{l} 1137 | %% \returns(\seq{s}) \\ 1138 | %% \qquad \mbox{if } \returns(s_m) \land \forall i < m . \breaks(s_m) = \emptyset \mbox{ for some } m \\ 1139 | %% \returns(\block{\mathit{ss}}) \\ 1140 | %% \qquad \mbox{if } \returns(ss) \\ 1141 | %% \returns(\iftwo{e}{s_1}{s_2}) \\ 1142 | %% \qquad \mbox{if } \returns(s_1) \land \returns(s_2) \\ 1143 | %% \returns(\dowhile{s}{e}) \\ 1144 | %% \qquad \mbox{if } \returns(s) \\ 1145 | %% \returns(\switch{e}{\seq{\mathit{cd}}}) \\ 1146 | %% \qquad \mbox{if } \returns(cd_n) \land \forall i . \breaks(cd_i) = \emptyset \\ 1147 | %% \returns(\mathjs{case }v\mathjs{:}\,\mathit{ss}) \\ 1148 | %% \qquad \mbox{if } \returns(\mathit{ss}) \\ 1149 | %% \returns(\mathjs{default:}\,\mathit{ss}) \\ 1150 | %% \qquad \mbox{if } \returns(\mathit{ss}) 1151 | %% \end{array} 1152 | %% \] 1153 | 1154 | \subsection{Expression validation} 1155 | \label{sec:exprjudge} 1156 | 1157 | \[ 1158 | (\Delta\cdot\Gamma)(x) = \left\{\begin{array}{ll} 1159 | \Gamma(x) & \mbox{if}\ x \in\dom(\Gamma) \\ 1160 | \gamma & \mbox{if}\ \Delta(x) = \mu\,\gamma \\ 1161 | \end{array} \right. 1162 | \] 1163 | 1164 | \[ 1165 | \begin{array}{lr} 1166 | \mbox{Expression validation} & \hfil \fbox{$\exprjudge{\Delta}{\Gamma}{e}{\tau}$} 1167 | \rulebreak 1168 | \multicolumn{2}{c}{ 1169 | \begin{array}{c} 1170 | \inferrule* [lab=\rel{T-Signed}] 1171 | {-2^{31} \leq i < 0} 1172 | {\exprjudge{\Delta}{\Gamma}{i}{\signed}} 1173 | \qquad 1174 | \inferrule* [lab=\rel{T-Fixnum}] 1175 | {0 \leq i < 2^{31}} 1176 | {\exprjudge{\Delta}{\Gamma}{i}{\fixnum}} 1177 | \qquad 1178 | \inferrule* [lab=\rel{T-Unsigned}] 1179 | {2^{31} \leq i < 2^{32}} 1180 | {\exprjudge{\Delta}{\Gamma}{i}{\unsigned}} 1181 | \\ \\ 1182 | \inferrule* [lab=\rel{T-Double}] 1183 | { } 1184 | {\exprjudge{\Delta}{\Gamma}{r}{\double}} 1185 | \qquad 1186 | \inferrule* [lab=\rel{T-VarRef}] 1187 | {(\Delta\cdot\Gamma)(x) = \tau} 1188 | {\exprjudge{\Delta}{\Gamma}{x}{\tau}} 1189 | \\ \\ 1190 | \inferrule* [lab=\rel{T-SetLocal}] 1191 | {\exprjudge{\Delta}{\Gamma}{e}{\tau} \\ 1192 | \tau <: \Gamma(x)} 1193 | {\exprjudge{\Delta}{\Gamma}{x\mathjs{ = }e}{\tau}} 1194 | \qquad 1195 | \inferrule* [lab=\rel{T-SetGlobal}] 1196 | {x \not\in\dom(\Gamma) \\ 1197 | \Delta(x) = \mut\,\sigma \\\\ 1198 | \exprjudge{\Delta}{\Gamma}{e}{\tau} \\ 1199 | \tau <: \sigma} 1200 | {\exprjudge{\Delta}{\Gamma}{x\mathjs{ = }e}{\tau}} 1201 | \\ \\ 1202 | \inferrule* [lab=\rel{T-LoadImm}] 1203 | {(\Delta\cdot\Gamma)(x) = \view{n}{\tau} \\\\ 1204 | i \equiv 0 \mod n \\ 1205 | 0 \leq i \leq \mathit{mask} \\\\ 1206 | \exprjudge{\Delta}{\Gamma}{e}{\intish}} 1207 | {\exprjudge{\Delta}{\Gamma}{\getprop{x}{i}}{\tau}} 1208 | \qquad 1209 | \inferrule* [lab=\rel{T-StoreImm}] 1210 | {(\Delta\cdot\Gamma)(x) = \view{n}{\tau} \\\\ 1211 | i \equiv 0 \mod n \\ 1212 | 0 \leq i \leq \mathit{mask} \\\\ 1213 | \exprjudge{\Delta}{\Gamma}{e_1}{\intish} \\ 1214 | \exprjudge{\Delta}{\Gamma}{e_2}{\tau}} 1215 | {\exprjudge{\Delta}{\Gamma}{\getprop{x}{i}\mathjs{ = }e_2}{\tau}} 1216 | \\ \\ 1217 | \inferrule* [lab=\rel{T-LoadByte}] 1218 | {(\Delta\cdot\Gamma)(x) = \view{1}{\intsm} \\\\ 1219 | \exprjudge{\Delta}{\Gamma}{e}{\intish}} 1220 | {\exprjudge{\Delta}{\Gamma}{\getprop{x}{e\mathjs{ \& } \mathit{mask}}}{\tau}} 1221 | \qquad 1222 | \inferrule* [lab=\rel{T-StoreByte}] 1223 | {(\Delta\cdot\Gamma)(x) = \view{1}{\intsm} \\\\ 1224 | \exprjudge{\Delta}{\Gamma}{e_1}{\intish} \\ 1225 | \exprjudge{\Delta}{\Gamma}{e_2}{\tau}} 1226 | {\exprjudge{\Delta}{\Gamma}{\getprop{x}{e_1\mathjs{ \& } \mathit{mask}}\mathjs{ = }e_2}{\tau}} 1227 | \\ \\ 1228 | \inferrule* [lab=\rel{T-Load}] 1229 | {(\Delta\cdot\Gamma)(x) = \view{n}{\tau} \\\\ 1230 | \mathit{shift} = \log_2(n) \\ 1231 | n > 1 \\\\ 1232 | \exprjudge{\Delta}{\Gamma}{e}{\intish}} 1233 | {\exprjudge{\Delta}{\Gamma}{\getprop{x}{\paren{e\mathjs{ \& } \mathit{mask}}\mathjs{ >> } \mathit{shift}}}{\tau}} 1234 | \qquad 1235 | \inferrule* [lab=\rel{T-Store}] 1236 | {(\Delta\cdot\Gamma)(x) = \view{n}{\tau} \\\\ 1237 | \mathit{shift} = \log_2(n) \\ 1238 | n > 1 \\\\ 1239 | \exprjudge{\Delta}{\Gamma}{e_1}{\intish} \\ 1240 | \exprjudge{\Delta}{\Gamma}{e_2}{\tau}} 1241 | {\exprjudge{\Delta}{\Gamma}{\getprop{x}{\paren{e_1\mathjs{ \& } \mathit{mask}}\mathjs{ >> } \mathit{shift}}\mathjs{ = }e_2}{\tau}} 1242 | \end{array} 1243 | } 1244 | \end{array} 1245 | \] 1246 | 1247 | \[ 1248 | \begin{array}{lr} 1249 | \mbox{Expression validation (cont'd)} & \hfil \fbox{$\exprjudge{\Delta}{\Gamma}{e}{\tau}$} 1250 | \rulebreak 1251 | \multicolumn{2}{c}{ 1252 | \begin{array}{c} 1253 | \inferrule* [lab=\rel{T-FunCall}] 1254 | {(\Delta\cdot\Gamma)(f) = \_ \land \funty{\seq{\sigma}}{\tau} \land \_ \\\\ 1255 | \forall i . \exprjudge{\Delta}{\Gamma}{e_i}{\sigma_i}} 1256 | {\exprjudge{\Delta}{\Gamma}{\funcall{f}{\seq{e}}}{\tau}} 1257 | \qquad 1258 | \inferrule* [lab=\rel{T-FFICall}] 1259 | {(\Delta\cdot\Gamma)(f) = \Fun \\\\ 1260 | %(\Delta\cdot\Gamma)(f) = \_ \land \funty{\ldots\sigma}{\tau} \land \_ \\\\ 1261 | \forall i . \exprjudge{\Delta}{\Gamma}{e_i}{\extern}} 1262 | {\exprjudge{\Delta}{\Gamma}{\funcall{f}{\seq{e}}}{\unk}} 1263 | \\ \\ 1264 | \inferrule* [lab=\rel{T-Unop}] 1265 | {\mathit{unop} : \_ \land \funty{\sigma}{\tau} \land \_ \\\\ 1266 | \exprjudge{\Delta}{\Gamma}{e}{\sigma}} 1267 | {\exprjudge{\Delta}{\Gamma}{\mathit{unop}\ e}{\tau}} 1268 | \qquad 1269 | \inferrule* [lab=\rel{T-Binop}] 1270 | {\mathit{binop} : \_ \land \funty{\sigma_1, \sigma_2}{\tau} \land \_ \\\\ 1271 | \exprjudge{\Delta}{\Gamma}{e_1}{\sigma_1} \\ 1272 | \exprjudge{\Delta}{\Gamma}{e_2}{\sigma_2}} 1273 | {\exprjudge{\Delta}{\Gamma}{e_1\ \mathit{binop}\ e_2}{\tau}} 1274 | \\ \\ 1275 | \inferrule* [lab=\rel{T-Multiary}] 1276 | {\forall i < n . \oplus_i \in \{ +, - \} \\ 1277 | n \leq 2^{20} \\\\ 1278 | \forall i \leq n . \exprjudge{\Delta}{\Gamma}{e_i}{\int}} 1279 | {\exprjudge{\Delta}{\Gamma}{e_1 \oplus_1 \ldots \oplus_{n-1} e_n}{\intish}} 1280 | \qquad 1281 | \inferrule* [lab=\rel{T-Cond}] 1282 | {\tau \in \{ \int, \double \} \\\\ 1283 | \exprjudge{\Delta}{\Gamma}{e_1}{\int} \\\\ 1284 | \exprjudge{\Delta}{\Gamma}{e_2}{\tau} \\ 1285 | \exprjudge{\Delta}{\Gamma}{e_3}{\tau}} 1286 | {\exprjudge{\Delta}{\Gamma}{\ternary{e_1}{e_2}{e_3}}{\tau}} 1287 | \\ \\ 1288 | \inferrule* [lab=\rel{T-Paren}] 1289 | {\forall i \leq n . \exprjudge{\Delta}{\Gamma}{e_i}{\tau_i}} 1290 | {\exprjudge{\Delta}{\Gamma}{\paren{\seq{e}}}{\tau_n}} 1291 | \qquad 1292 | \inferrule* [lab=\rel{T-Sub}] 1293 | {\exprjudge{\Delta}{\Gamma}{e}{\sigma} \\ 1294 | \sigma <: \tau} 1295 | {\exprjudge{\Delta}{\Gamma}{e}{\tau}} 1296 | \qquad 1297 | \inferrule* [lab=\rel{T-Cast}] 1298 | {\exprjudge{\Delta}{\Gamma}{e}{\double}} 1299 | {\exprjudge{\Delta}{\Gamma}{\mathjs{\~{}\~{}}e}{\signed}} 1300 | \end{array} 1301 | } 1302 | \end{array} 1303 | \] 1304 | 1305 | \end{document} 1306 | --------------------------------------------------------------------------------