├── test ├── builtin │ ├── .fs_fixtures │ │ └── .dummy │ ├── .require_fixtures │ │ ├── foo.js │ │ ├── baz │ │ │ ├── baz.js │ │ │ └── index.js │ │ ├── module.js │ │ ├── package │ │ │ ├── lib │ │ │ │ └── foo.js │ │ │ └── package.json │ │ └── bar │ │ │ └── index.json │ ├── jeph_modules │ │ └── foo.js │ ├── node_modules │ │ ├── bar.js │ │ └── foo.js │ ├── error.js │ ├── php.js │ ├── require.js │ ├── global.js │ ├── boolean.js │ ├── date.js │ ├── math.js │ ├── number.js │ ├── json.js │ ├── buffer.js │ ├── function.js │ ├── path.js │ ├── regexp.js │ ├── crypto.js │ ├── native.js │ ├── string.js │ ├── array.js │ ├── object.js │ └── fs.js ├── twostroke │ ├── README │ ├── regexp.js │ ├── semicolon_insertion.js │ ├── boolean.js │ ├── internals.js │ ├── object.js │ ├── array.js │ ├── etc.js │ ├── function.js │ ├── math.js │ ├── date.js │ ├── exceptions.js │ ├── number.js │ ├── string.js │ └── misc_syntax.js └── compiler │ ├── for.js │ ├── do.js │ ├── while.js │ ├── php.js │ ├── this.js │ ├── if.js │ ├── var.js │ ├── function.js │ ├── array.js │ ├── break.js │ ├── continue.js │ ├── switch.js │ ├── arithmetic.js │ ├── for_in.js │ ├── with.js │ ├── logical.js │ ├── unary.js │ ├── exception.js │ ├── assign.js │ └── comparison.js ├── util ├── jake.d │ ├── main.js │ └── prelude.js ├── shrink.php └── jtest.d │ └── main.js ├── src ├── image │ ├── 99_epilogue.js │ ├── 06_boolean.js │ ├── 12_json.js │ ├── 13_templates.js │ ├── 14_php.js │ ├── 17_crypto.js │ ├── 08_math.js │ ├── 01_global.js │ ├── 11_error.js │ ├── 18_path.js │ ├── 10_regexp.js │ ├── 03_function.js │ ├── 20_buffer.js │ ├── 16_console.js │ ├── 07_number.js │ ├── 15_require.js │ ├── 09_date.js │ ├── 02_object.js │ ├── 05_string.js │ └── 04_array.js ├── JSInterpreter.php └── JSDumper.phptw └── Jakefile /test/builtin/.fs_fixtures/.dummy: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/builtin/.require_fixtures/foo.js: -------------------------------------------------------------------------------- 1 | exports = "foo"; 2 | -------------------------------------------------------------------------------- /test/builtin/.require_fixtures/baz/baz.js: -------------------------------------------------------------------------------- 1 | exports = "baz"; 2 | -------------------------------------------------------------------------------- /test/builtin/jeph_modules/foo.js: -------------------------------------------------------------------------------- 1 | exports = "Jeph's foo"; 2 | -------------------------------------------------------------------------------- /test/builtin/node_modules/bar.js: -------------------------------------------------------------------------------- 1 | exports = "Node's bar"; 2 | -------------------------------------------------------------------------------- /test/builtin/node_modules/foo.js: -------------------------------------------------------------------------------- 1 | exports = "Node's foo"; 2 | -------------------------------------------------------------------------------- /test/builtin/.require_fixtures/module.js: -------------------------------------------------------------------------------- 1 | module.exports = "foo"; 2 | -------------------------------------------------------------------------------- /test/builtin/.require_fixtures/package/lib/foo.js: -------------------------------------------------------------------------------- 1 | module.exports = "foo"; 2 | -------------------------------------------------------------------------------- /test/builtin/.require_fixtures/bar/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "hello": "world" 3 | } 4 | -------------------------------------------------------------------------------- /test/builtin/.require_fixtures/package/package.json: -------------------------------------------------------------------------------- 1 | { "name": "package", 2 | "main": "./lib/foo.js" } 3 | -------------------------------------------------------------------------------- /test/twostroke/README: -------------------------------------------------------------------------------- 1 | These test are taken from project https://github.com/charliesome/twostroke. 2 | Slightly modified 3 | -------------------------------------------------------------------------------- /test/builtin/.require_fixtures/baz/index.js: -------------------------------------------------------------------------------- 1 | exports = { 2 | baz: function () { 3 | return require("./baz"); 4 | } 5 | }; 6 | -------------------------------------------------------------------------------- /test/compiler/for.js: -------------------------------------------------------------------------------- 1 | test("for", function () { 2 | for (var i = 0, j = 5; j > 0; ++i, --j); 3 | 4 | assert(i === 5); 5 | assert(j === 0); 6 | }); 7 | -------------------------------------------------------------------------------- /test/compiler/do.js: -------------------------------------------------------------------------------- 1 | test("do", function () { 2 | var i = 0, j = 5; 3 | 4 | do { 5 | i++; 6 | --j; 7 | } while (j); 8 | 9 | assert(i === 5); 10 | assert(j === 0); 11 | }); 12 | -------------------------------------------------------------------------------- /test/compiler/while.js: -------------------------------------------------------------------------------- 1 | test("while", function () { 2 | var i = 0, j = 5; 3 | 4 | while (j) { 5 | ++i; 6 | j--; 7 | } 8 | 9 | assert(i === 5); 10 | assert(j === 0); 11 | }); 12 | -------------------------------------------------------------------------------- /test/compiler/php.js: -------------------------------------------------------------------------------- 1 | test("php", function () { 2 | var ok = true, fail = true; 3 | 4 | @@ `fail = FALSE; @@ 5 | 6 | assert(@@ TRUE @@); 7 | assert(@@ `ok @@); 8 | assert(!fail); 9 | }); 10 | -------------------------------------------------------------------------------- /util/jake.d/main.js: -------------------------------------------------------------------------------- 1 | var args = @@ JS::fromNative($_SERVER['argv']) @@, 2 | jakefilePath = PHP.fn("getcwd")() + "/Jakefile"; 3 | 4 | if (args.length === 1) { 5 | jake.displayTasks(jakefilePath); 6 | 7 | } else { 8 | jake.runTask.call(global, args[1], args.slice(2)); 9 | } 10 | -------------------------------------------------------------------------------- /test/compiler/this.js: -------------------------------------------------------------------------------- 1 | test("this", function () { 2 | function F() { 3 | this.foo = "bar"; 4 | } 5 | 6 | F(); 7 | 8 | assert(foo === "bar"); 9 | 10 | var f = new F; 11 | assert(f.foo === "bar"); 12 | 13 | var o = { f: F }; 14 | o.f(); 15 | assert(o.foo === "bar"); 16 | }); 17 | -------------------------------------------------------------------------------- /test/compiler/if.js: -------------------------------------------------------------------------------- 1 | test("if", function () { 2 | var foo, bar; 3 | 4 | if (true) { 5 | foo = "foo"; 6 | } else { 7 | foo = "bar"; 8 | } 9 | 10 | if (false) { 11 | bar = "foo"; 12 | } else { 13 | bar = "bar"; 14 | } 15 | 16 | assert(foo === "foo"); 17 | assert(bar === "bar"); 18 | }); 19 | -------------------------------------------------------------------------------- /test/compiler/var.js: -------------------------------------------------------------------------------- 1 | test("var", function () { 2 | var 3 | u = undefined, 4 | n = null, 5 | t = true, 6 | i = 5, 7 | z = 0, 8 | s = "foo", 9 | e = ""; 10 | 11 | assert(!u); 12 | assert(!n); 13 | assert(t); 14 | assert(i); 15 | assert(!z); 16 | assert(s); 17 | assert(!e); 18 | }); 19 | -------------------------------------------------------------------------------- /test/compiler/function.js: -------------------------------------------------------------------------------- 1 | test("function", function () { 2 | assert(foo() === "foo"); 3 | assert(undef() === undefined); 4 | assert(add(2, 3) === 5); 5 | 6 | function foo() { 7 | return "foo"; 8 | } 9 | 10 | function undef() {} 11 | 12 | function add(a, b) { 13 | return a + b; 14 | } 15 | }); 16 | -------------------------------------------------------------------------------- /test/compiler/array.js: -------------------------------------------------------------------------------- 1 | test("array", function () { 2 | var a = [ 1, 2, 3 ]; 3 | 4 | assertEqual(Object.keys(a).join(), "0,1,2"); 5 | 6 | a[4] = 5; 7 | assertEqual(Object.keys(a).join(), "0,1,2,4"); 8 | 9 | a.length = 7; 10 | assertEqual(Object.keys(a).join(), "0,1,2,4"); 11 | 12 | a.length = 2; 13 | assertEqual(Object.keys(a).join(), "0,1"); 14 | }); 15 | -------------------------------------------------------------------------------- /test/compiler/break.js: -------------------------------------------------------------------------------- 1 | test("break", function () { 2 | var i; 3 | 4 | for (i = 0; ; ++i) { 5 | if (i === 42) { break; } 6 | } 7 | 8 | assert(i === 42); 9 | 10 | i = 0; 11 | while (true) { 12 | if (i === 42) { break; } 13 | i++; 14 | } 15 | 16 | assert(i === 42); 17 | 18 | i = 0; 19 | do { 20 | if (i === 42) { break; } 21 | ++i; 22 | } while (true); 23 | 24 | assert(i === 42); 25 | }); 26 | -------------------------------------------------------------------------------- /test/compiler/continue.js: -------------------------------------------------------------------------------- 1 | test("continue", function () { 2 | var count, i; 3 | 4 | for (count = 0, i = 0; i < 10; ++i) { 5 | if (i & 1) { continue; } 6 | count++; 7 | } 8 | 9 | assert(count === 5); 10 | 11 | count = i = 0; 12 | while (i < 10) { 13 | if (i++ & 1) { continue; } 14 | count++; 15 | } 16 | 17 | assert(count === 5); 18 | 19 | count = i = 0; 20 | do { 21 | if (i++ & 1) { continue; } 22 | count++; 23 | } while (i < 9); 24 | 25 | assert(count === 5); 26 | }); 27 | -------------------------------------------------------------------------------- /test/compiler/switch.js: -------------------------------------------------------------------------------- 1 | test("switch", function () { 2 | function word(n) { 3 | var foo; 4 | 5 | switch (n) { 6 | case 1: 7 | foo = "one"; 8 | break; 9 | 10 | case 2: 11 | foo = "two"; 12 | break; 13 | 14 | case 3: 15 | foo = "three"; 16 | break; 17 | 18 | default: 19 | foo = "more"; 20 | } 21 | 22 | return foo; 23 | } 24 | 25 | assert(word(1) === "one"); 26 | assert(word(2) === "two"); 27 | assert(word(3) === "three"); 28 | assert(word(4) === "more"); 29 | assert(word(42) === "more"); 30 | }); 31 | -------------------------------------------------------------------------------- /test/compiler/arithmetic.js: -------------------------------------------------------------------------------- 1 | test("arithmetic", function () { 2 | assert((1 + 2) === 3); 3 | assert((2 - 1) === 1); 4 | 5 | assert((2 * 3 / 2) === 3); 6 | assert((16 % 5) === 1); 7 | 8 | assert((5 + 5 * 5) === 30); 9 | 10 | assert((1 | 2 | 4) === 7); 11 | assert((3 & 1) === 1); 12 | assert((1 << 1) === 2); 13 | assert((1 >> 1) === 0); 14 | 15 | assert(1 < 2); 16 | assert(2 > 1); 17 | assert(1 <= 2); 18 | assert(1 <= 1); 19 | assert(2 >= 1); 20 | assert(2 >= 2); 21 | 22 | assert("1" + 1 === "11"); 23 | assert(1 + "1" === "11"); 24 | assert(1 + 1 === 2); 25 | }); 26 | -------------------------------------------------------------------------------- /src/image/99_epilogue.js: -------------------------------------------------------------------------------- 1 | global = @@ JS::$global @@; 2 | var f; 3 | 4 | for (var k in global) { 5 | if (typeof global[k] === "function") { 6 | f = global[k]; 7 | @@ `f->scope = JS::$global; @@ 8 | } 9 | } 10 | 11 | delete f; 12 | delete k; 13 | delete global; 14 | 15 | @@ 16 | JS::$wrappedObjectTemplates = JS::$wrappedObjects = array(); 17 | JS::$global = $scope; 18 | JS::$global->prototype = JS::$global->up = NULL; 19 | JS::$global->class = 'Global'; 20 | JS::$global->extensible = TRUE; 21 | unset(JS::$global->trace, JS::$global->trace_sp, JS::$global->scope, JS::$global->scope_sp); 22 | @@ 23 | -------------------------------------------------------------------------------- /test/compiler/for_in.js: -------------------------------------------------------------------------------- 1 | test("for in", function () { 2 | var o = {a: "a", b: "b", c: "c"}; 3 | 4 | for (var k in o) { 5 | assert(k === o[k]); 6 | } 7 | 8 | function F() { 9 | this.a = "F.a"; 10 | this.b = "F.b"; 11 | } 12 | 13 | F.prototype.a = "F.prototype.a"; 14 | F.prototype.b = "F.prototype.b"; 15 | F.prototype.c = "F.prototype.c"; 16 | 17 | var o = new F, props = [], values = []; 18 | 19 | for (var k in o) { 20 | props.push(k); 21 | values.push(o[k]); 22 | } 23 | 24 | assertEqual(props.toString(), "a,b,c"); 25 | assertEqual(values.toString(), "F.a,F.b,F.prototype.c"); 26 | }); 27 | -------------------------------------------------------------------------------- /test/builtin/error.js: -------------------------------------------------------------------------------- 1 | test("Error()", function () { 2 | assert(Error() instanceof Error, "Error(): 1"); 3 | assert((new Error) instanceof Error, "Error(): 2"); 4 | }); 5 | 6 | test("Error.name, Error.message", function () { 7 | var e = new Error("a message"); 8 | assert(e.name === "Error", "Error(): wrong name"); 9 | assert(e.message === "a message", "Error(): wrong message"); 10 | }); 11 | 12 | test("Error.prototype.toString()", function () { 13 | assert(Error().toString() === "Error", "Error.prototype.toString(): 1"); 14 | assert(Error("a message").toString() === "Error: a message", "Error.prototype.toString(): 2"); 15 | assert(TypeError("a message").toString() === "TypeError: a message", "Error.prototype.toString(): 3"); 16 | }); 17 | -------------------------------------------------------------------------------- /test/builtin/php.js: -------------------------------------------------------------------------------- 1 | test("PHP.fn()", function () { 2 | var substr = PHP.fn("substr"); 3 | 4 | assertEqual(typeof substr, "function"); 5 | assertEqual(substr("fooo!", 1), "ooo!"); 6 | assertEqual(substr("fooo!", 1, 1), "o"); 7 | 8 | assertEqual(substr, PHP.fn("substr")); 9 | }); 10 | 11 | test("PHP.cls()", function () { 12 | var ReflectionClass = PHP.cls("ReflectionClass"), 13 | o = new ReflectionClass("ReflectionClass"); 14 | 15 | assertEqual(typeof ReflectionClass, "function"); 16 | assertEqual(typeof o, "object"); 17 | 18 | assertEqual(o.name, "ReflectionClass"); 19 | assertEqual(o.constructor.name, "__construct"); 20 | assertEqual(o.constructor.numberOfParameters, 1); 21 | 22 | assertEqual(ReflectionClass, PHP.cls("ReflectionClass")); 23 | }); 24 | -------------------------------------------------------------------------------- /test/builtin/require.js: -------------------------------------------------------------------------------- 1 | test("require()", function () { 2 | assertEqual(require(__dirname + "/.require_fixtures/foo.js"), "foo"); 3 | assertEqual(require("./.require_fixtures/foo.js"), "foo"); 4 | assertEqual(require("../builtin/.require_fixtures/foo.js"), "foo"); 5 | 6 | assertEqual(require("./.require_fixtures/module.js"), "foo"); 7 | 8 | var bar = require("./.require_fixtures/bar"); 9 | assertEqual(typeof bar, "object"); 10 | assertEqual(Object.keys(bar).toString(), "hello"); 11 | assertEqual(bar.hello, "world"); 12 | 13 | assertEqual(require("./.require_fixtures/package"), "foo"); 14 | 15 | assertEqual(require("foo"), "Jeph's foo"); 16 | assertEqual(require("bar"), "Node's bar"); 17 | 18 | assertEqual(require("./.require_fixtures/baz").baz(), "baz"); 19 | }); 20 | -------------------------------------------------------------------------------- /test/compiler/with.js: -------------------------------------------------------------------------------- 1 | test("with", function () { 2 | try { 3 | with (undefined) {} 4 | assert(false, "did not throw on undefined"); 5 | 6 | } catch (e) { 7 | assert(e instanceof TypeError, "TypeError should be thrown"); 8 | } 9 | 10 | try { 11 | with (null) {} 12 | assert(false, "did not throw on null"); 13 | 14 | } catch (e) { 15 | assert(e instanceof TypeError, "TypeError should be thrown"); 16 | } 17 | 18 | var a = undefined; 19 | 20 | try { 21 | with (a || {}) {} 22 | } catch (e) { 23 | assert(false, "should not throw"); 24 | } 25 | 26 | var o = { one: 1, two: 2 }; 27 | 28 | with (o) { 29 | assertEqual(one, 1); 30 | assertEqual(two, 2); 31 | three = 3; 32 | } 33 | 34 | assertEqual(three, 3); 35 | assertEqual(o.three, undefined); 36 | }); 37 | -------------------------------------------------------------------------------- /test/builtin/global.js: -------------------------------------------------------------------------------- 1 | test("NaN, Infinity, undefined, parseInt(), parseFloat(), isNaN(), isFinite()", function () { 2 | assert(@@ is_nan(`NaN) @@, "NaN is not NaN"); 3 | 4 | assert(@@ is_infinite(`Infinity) @@, "Infinity is finite"); 5 | 6 | assert(undefined === undefined, "undefined is not undefined"); 7 | 8 | assert(parseInt("42") === 42); 9 | assert(parseInt("-87") === -87); 10 | 11 | assert(parseFloat("5.8") === 5.8); 12 | assert(parseFloat("-9.3") === -9.3); 13 | 14 | assert(isNaN(NaN)); 15 | assert(!isNaN(5)); 16 | 17 | assert(isFinite(5)); 18 | assert(!isFinite(Infinity)); 19 | }); 20 | 21 | test("eval()", function() { 22 | assertEqual(eval(" return 42; "), 42); 23 | 24 | var ok = false; 25 | assertEqual(eval("ok = true;"), undefined); 26 | assert(ok, "eval(): cannot change variables from enclosing scope"); 27 | }); 28 | -------------------------------------------------------------------------------- /test/compiler/logical.js: -------------------------------------------------------------------------------- 1 | test("logical", function () { 2 | var ok; 3 | 4 | function okIfRunReturnTrue() { 5 | ok = true; 6 | return true; 7 | } 8 | 9 | function okIfRunReturnFalse() { 10 | ok = true; 11 | return false; 12 | } 13 | 14 | function failIfRun() { 15 | ok = false; 16 | } 17 | 18 | ok = false; 19 | assert((okIfRunReturnTrue() && okIfRunReturnTrue()) === true); 20 | assert(ok); 21 | 22 | ok = false; 23 | assert((okIfRunReturnFalse() && failIfRun()) === false); 24 | assert(ok); 25 | 26 | ok = false; 27 | assert((okIfRunReturnTrue() || failIfRun()) === true); 28 | assert(ok); 29 | 30 | ok = false; 31 | assert((okIfRunReturnFalse() || okIfRunReturnTrue()) === true); 32 | assert(ok); 33 | 34 | ok = false; 35 | assert((okIfRunReturnFalse() || okIfRunReturnFalse()) === false); 36 | assert(ok); 37 | 38 | assert((undefined || 42) === 42); 39 | assert((42 || undefined) === 42); 40 | }); 41 | -------------------------------------------------------------------------------- /test/builtin/boolean.js: -------------------------------------------------------------------------------- 1 | test("Boolean()", function () { 2 | assert(Boolean(undefined) === false, "Boolean(): undefined"); 3 | assert(Boolean(null) === false, "Boolean(): null"); 4 | assert(Boolean(true) === true, "Boolean(): true"); 5 | assert(Boolean(false) === false, "Boolean(): false"); 6 | assert(Boolean("") === false, "Boolean(): empty string"); 7 | assert(Boolean("abc") === true, "Boolean(): string"); 8 | assert(Boolean({}) === true, "Boolean(): object"); 9 | }); 10 | 11 | test("Boolean.prototype.toString()", function () { 12 | assert(true.toString() === "true", "Boolean.prototype.toString(): 1"); 13 | assert(false.toString() === "false", "Boolean.prototype.toString(): 2"); 14 | }); 15 | 16 | test("Boolean.prototype.valueOf()", function () { 17 | assert((new Boolean(true)).valueOf() === true, "Boolean.prototype.valueOf(): 1"); 18 | assert((new Boolean(false)).valueOf() === false, "Boolean.prototype.valueOf(): 2"); 19 | }); 20 | -------------------------------------------------------------------------------- /test/builtin/date.js: -------------------------------------------------------------------------------- 1 | test("Date()", function () { 2 | assert(typeof Date(1992, 9, 21) === "string", "Date(): 1"); 3 | 4 | var d1 = new Date(Date.parse("1992-09-21 02:53:03")), 5 | d2 = new Date(1992, 9, 21, 2, 53, 3), 6 | d = d1; 7 | 8 | assert(d1.valueOf() === d2.valueOf()); 9 | 10 | 11 | assert(d.getFullYear() === 1992); 12 | assert(d.getMonth() === 9); 13 | assert(d.getDate() === 21); 14 | assert(d.getHours() === 2); 15 | assert(d.getMinutes() === 53); 16 | assert(d.getSeconds() === 3); 17 | assert(d.getMilliseconds() === 0); 18 | 19 | d.setFullYear(2012); 20 | d.setMonth(7); 21 | d.setDate(11); 22 | d.setHours(2); 23 | d.setMinutes(35); 24 | d.setSeconds(59); 25 | d.setMilliseconds(736); 26 | 27 | assert(d.getFullYear() === 2012); 28 | assert(d.getMonth() === 7); 29 | assert(d.getDate() === 11); 30 | assert(d.getHours() === 2); 31 | assert(d.getMinutes() === 35); 32 | assert(d.getSeconds() === 59); 33 | assert(d.getMilliseconds() === 736); 34 | }); 35 | -------------------------------------------------------------------------------- /test/twostroke/regexp.js: -------------------------------------------------------------------------------- 1 | test("simple regexps", function() { 2 | assertEqual(true, /hello/.test("hello world")); 3 | assertEqual(false, /goodbye/.test("hello world")); 4 | }); 5 | 6 | test("exec", function() { 7 | assert("123,1,2,3" == /(\d)(\d)(\d)/.exec("123")); 8 | assert("123,123" == /(\d+)/.exec("123")); 9 | assert(null == /\s+/.exec("123")); 10 | assert("b,,b" == /(a)?(b)/.exec("b")); 11 | }); 12 | 13 | test("exec additional attributes", function() { 14 | assertEqual("hello", /./.exec("hello").input); 15 | assertEqual(0, /./.exec("hello").index); 16 | assertEqual("hello", /l/.exec("hello").input); 17 | assertEqual(2, /l/.exec("hello").index); 18 | }); 19 | 20 | test("toString", function() { 21 | assertEqual("/abc/i", /abc/i.toString()); 22 | }); 23 | 24 | test("\\c[A-Z]", function() { 25 | assert(/\cA/.test("\x01")); 26 | assert(/\ca/.test("\x01")); 27 | assert(/\cZ/.test(String.fromCharCode(26))); 28 | assert(/\cz/.test(String.fromCharCode(26))); 29 | }); 30 | -------------------------------------------------------------------------------- /test/builtin/math.js: -------------------------------------------------------------------------------- 1 | 2 | // only wrappers for PHP functions: 3 | // 4 | // abs, acos, asin, atan, atan2, ceil, cos, exp, floor, log, 5 | // pow, round, sin, sqrt, tan 6 | // 7 | // won't be tested 8 | 9 | test("Math.random()", function () { 10 | var r = Math.random(); 11 | assert(r >= 0 && r <= 1, "Math.random()"); 12 | }); 13 | 14 | test("Math.max()", function () { 15 | assert(Math.max() === -Infinity, "Math.max(): 1"); 16 | assert(Math.max(0) === 0, "Math.max(): 2"); 17 | assert(Math.max(1, 2) === 2, "Math.max(): 3"); 18 | assert(Math.max(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) === 10, "Math.max(): 4"); 19 | assert(isNaN(Math.max(0, NaN)), "Math.max(): 5"); 20 | }); 21 | 22 | test("Math.min()", function () { 23 | assert(Math.min() === +Infinity, "Math.min(): 1"); 24 | assert(Math.min(0) === 0, "Math.min(): 2"); 25 | assert(Math.min(1, 2) === 1, "Math.min(): 3"); 26 | assert(Math.min(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) === 1, "Math.min(): 4"); 27 | assert(isNaN(Math.min(0, NaN)), "Math.min(): 5"); 28 | }); 29 | -------------------------------------------------------------------------------- /test/twostroke/semicolon_insertion.js: -------------------------------------------------------------------------------- 1 | // semiconol insertion not fully functional 2 | /* 3 | test("return is a restricted production", function() { 4 | assertEqual(undefined, (function() { 5 | return 6 | 1 7 | })()); 8 | }); 9 | 10 | test("throw is a restricted production", function() { 11 | try { 12 | eval("throw\n1;") 13 | } catch(e) { 14 | assert(e instanceof SyntaxError); 15 | } 16 | }); 17 | 18 | test("break is a restricted production", function() { 19 | a:while(true) { 20 | while(true) { 21 | break 22 | a; 23 | } 24 | assert(true); 25 | return; 26 | } 27 | assert(false); 28 | }); 29 | 30 | test("fragmented statement parses", function() { 31 | var x = { a: 1 }; 32 | eval("delete\nx.a"); 33 | assert(1 !== x.a); 34 | }); 35 | 36 | test("fragmented statement parses", function() { 37 | var x = { a: 1 }; 38 | eval("delete x.a\n"); 39 | assert(1 !== x.a); 40 | }); 41 | */ 42 | -------------------------------------------------------------------------------- /test/compiler/unary.js: -------------------------------------------------------------------------------- 1 | test("unary", function () { 2 | var theAnswerToLifeTheUniverseAndEverything = 42; 3 | 4 | assert(typeof undefined === "undefined"); 5 | assert(typeof null === "object"); 6 | assert(typeof true === "boolean"); 7 | assert(typeof theAnswerToLifeTheUniverseAndEverything === "number"); 8 | assert(typeof "foo" === "string"); 9 | assert(typeof {} === "object"); 10 | assert(typeof assert === "function"); 11 | 12 | assert(void(theAnswerToLifeTheUniverseAndEverything) === undefined); 13 | 14 | assert(theAnswerToLifeTheUniverseAndEverything === 42); 15 | delete theAnswerToLifeTheUniverseAndEverything; 16 | try { 17 | assert(theAnswerToLifeTheUniverseAndEverything === undefined); 18 | } catch (e) { 19 | assert(e instanceof ReferenceError); 20 | } 21 | 22 | var n = 5; 23 | 24 | assert(++n === 6); 25 | assert(--n === 5); 26 | assert(n++ === 5); 27 | assert(n === 6); 28 | assert(n-- === 6); 29 | assert(n === 5); 30 | 31 | assert(+n === 5); 32 | assert(-n === -5); 33 | assert(!!n === true); 34 | assert(~0 === -1); 35 | }); 36 | -------------------------------------------------------------------------------- /test/twostroke/boolean.js: -------------------------------------------------------------------------------- 1 | test("toString", function() { 2 | assertEqual("true", true.toString()); 3 | assertEqual("false", false.toString()); 4 | try { 5 | Boolean.prototype.toString.call(123); 6 | assert(false, "did not throw"); 7 | } catch(e) { 8 | assert(/generic/.test(e.toString())); 9 | } 10 | }); 11 | 12 | test("valueOf", function() { 13 | assertEqual(true, true.valueOf()); 14 | assertEqual(false, false.valueOf()); 15 | try { 16 | Boolean.prototype.valueOf.call(123); 17 | assert(false, "did not throw"); 18 | } catch(e) { 19 | assert(/generic/.test(e.toString())); 20 | } 21 | }); 22 | 23 | test("String coercion", function() { 24 | assertEqual("true", String(true)); 25 | assertEqual("false", String(false)); 26 | assertEqual("true", String(new Boolean(true))); 27 | assertEqual("false", String(new Boolean(false))); 28 | }); 29 | 30 | test("Number coercion", function() { 31 | assertEqual(1, Number(true)); 32 | assertEqual(0, Number(false)); 33 | assertEqual(1, Number(new Boolean(true))); 34 | assertEqual(0, Number(new Boolean(false))); 35 | }); 36 | -------------------------------------------------------------------------------- /test/compiler/exception.js: -------------------------------------------------------------------------------- 1 | test("exception", function () { 2 | var ok = false; 3 | 4 | try { 5 | throw "foo"; 6 | } catch (e) { 7 | assert(e === "foo"); 8 | } finally { 9 | ok = true; 10 | } 11 | 12 | assert(ok); 13 | 14 | try { 15 | ok = false; 16 | } finally { 17 | ok = true; 18 | } 19 | 20 | assert(ok); 21 | 22 | ok = false; 23 | try { 24 | @@ throw new Exception('foo'); @@ 25 | 26 | } catch (e) { 27 | assert(!(e instanceof Error)); 28 | assertEqual(Object.prototype.toString.call(e), "[object Exception]"); 29 | assertEqual(e.message, "foo"); 30 | ok = true; 31 | } 32 | 33 | assert(ok); 34 | }); 35 | 36 | test("exception - rethrowing", function () { 37 | try { 38 | try { 39 | throw new Error; // line 39, column 4 40 | } catch (e) { 41 | throw e; 42 | } 43 | } catch (e) { 44 | assert(e.file, __filename); 45 | assert(e.line, 39); 46 | assert(e.column, 4); 47 | } 48 | }); 49 | 50 | test("exception - no catch block", function () { 51 | try { 52 | try { 53 | ok = false; 54 | throw "foo"; 55 | } finally { 56 | ok = true; 57 | } 58 | } catch (e) { 59 | assertEqual(e, "foo"); 60 | assert(ok); 61 | } 62 | }); 63 | -------------------------------------------------------------------------------- /src/image/06_boolean.js: -------------------------------------------------------------------------------- 1 | function Boolean(value) { 2 | if (this === @@ $global @@) { 3 | return @@ JS::toBoolean(`value, $global) @@; 4 | } 5 | 6 | var b = {}; 7 | @@ `b->prototype = `Boolean->properties['prototype']; @@ 8 | @@ `b->class = 'Boolean'; @@ 9 | @@ `b->value = JS::toBoolean(`value, $global); @@ 10 | @@ `b->extensible = TRUE; @@ 11 | 12 | return b; 13 | } 14 | 15 | Boolean.prototype = {}; 16 | @@ `Boolean->properties['prototype']->prototype = `Object->properties['prototype']; @@ 17 | @@ `Boolean->properties['prototype']->class = 'Boolean'; @@ 18 | @@ `Boolean->properties['prototype']->extensible = TRUE; @@ 19 | Boolean.prototype.constructor = Boolean; 20 | 21 | Boolean.prototype.toString = function () { 22 | if (@@ !isset($leThis->class) || $leThis->class !== 'Boolean' @@) { 23 | throw new TypeError("Boolean.prototype.toString(): not generic"); 24 | } 25 | 26 | if (@@ $leThis->value @@) { 27 | return "true"; 28 | } 29 | 30 | return "false"; 31 | }; 32 | 33 | Boolean.prototype.valueOf = function () { 34 | if (@@ !isset($leThis->class) || $leThis->class !== 'Boolean' @@) { 35 | throw new TypeError("Boolean.prototype.valueOf(): not generic"); 36 | } 37 | 38 | return @@ $leThis->value @@; 39 | }; 40 | -------------------------------------------------------------------------------- /test/compiler/assign.js: -------------------------------------------------------------------------------- 1 | test("assign", function () { 2 | var a = "foo"; 3 | 4 | assert(a === "foo"); 5 | 6 | a = "bar"; 7 | 8 | assert(a === "bar"); 9 | 10 | var b = true, c = false, d, e; 11 | 12 | d = b ? "b is true" : "b is false"; 13 | e = c ? "c is true" : "c is false"; 14 | 15 | assert(d === "b is true"); 16 | assert(e === "c is false"); 17 | 18 | var o = {}; 19 | o.undefined = undefined; 20 | o.null = null; 21 | o.bool = true; 22 | o.number = 42; 23 | o.string = "foo"; 24 | o.f = function () { 25 | this.number /= 2; 26 | }; 27 | o[[ 1, 2, 3 ]] = "x"; 28 | o[{}] = "y"; 29 | 30 | assert(o["undefined"] === undefined); 31 | assert(o["null"] === null); 32 | assert(o["bool"] === true); 33 | assert(o["number"] === 42); 34 | assert(o["string"] === "foo"); 35 | assert(typeof o["f"] === "function"); 36 | o["f"](); 37 | assert(o["number"] === 21); 38 | assertEqual(o["1,2,3"], "x"); 39 | assertEqual(o["[object Object]"], "y"); 40 | assertEqual(o[[ 1, 2, 3 ]], "x"); 41 | assertEqual(o[{}], "y"); 42 | 43 | o[undefined] = "z"; 44 | assertEqual(o["undefined"], "z"); 45 | assertEqual(o[undefined], "z"); 46 | 47 | o[null] = "z"; 48 | assertEqual(o["null"], "z"); 49 | assertEqual(o[null], "z"); 50 | }); 51 | -------------------------------------------------------------------------------- /src/image/12_json.js: -------------------------------------------------------------------------------- 1 | var JSON = { 2 | parse: function (text, reviver) { 3 | var ret = @@ JS::fromNative(json_decode(`text)) @@; 4 | 5 | if (typeof reviver === "function") { 6 | var revive = function revive(o, index) { 7 | var v = o[index]; 8 | 9 | if (typeof v === "object") { 10 | var newItem; 11 | 12 | if (Array.isArray(v)) { 13 | for (var i = 0, l = v.length; i < l; ++i) { 14 | if ((newItem = revive(v, String(i))) === undefined) { 15 | delete v[i]; 16 | } else { 17 | v[i] = newItem; 18 | } 19 | } 20 | 21 | } else { 22 | for (var k in v) { 23 | if ((newItem = revive(v, k)) === undefined) { 24 | delete v[k]; 25 | } else { 26 | v[k] = newItem; 27 | } 28 | } 29 | } 30 | } 31 | 32 | return reviver.call(o, index, v); 33 | }; 34 | 35 | ret = revive({"": ret}, ""); 36 | } 37 | 38 | return ret; 39 | }, 40 | 41 | stringify: function (value, replacer, space) { 42 | if (arguments.length > 1) { 43 | throw new NotImplementedError("JSON.stringify(): replacer and space arguments not supported."); 44 | } 45 | 46 | return @@ str_replace("\\/", "/", json_encode(JS::toNative(`value))) @@; 47 | } 48 | }; 49 | 50 | @@ `JSON->class = 'JSON'; @@ 51 | -------------------------------------------------------------------------------- /test/twostroke/internals.js: -------------------------------------------------------------------------------- 1 | test("undefined variable throws ReferenceError", function() { 2 | try { 3 | whatever; 4 | assert(false, "did not throw"); 5 | } catch(e) { 6 | assertEqual(e.name, "ReferenceError"); 7 | } 8 | }); 9 | 10 | test("dereferencing null throws TypeError", function() { 11 | try { 12 | var x = null; 13 | x.foo; 14 | assert(false, "did not throw"); 15 | } catch(e) { 16 | assertEqual(e.name, "TypeError"); 17 | } 18 | }); 19 | 20 | test("if toString is not defined, falls back to valueOf", function() { 21 | assertEqual("123", String({ toString: null, valueOf: function() { return 123; } })); 22 | }); 23 | 24 | test("if valueOf is not defined, falls back to toString", function() { 25 | assertEqual(123, Number({ toString: function() { return "123"; }, valueOf: null })); 26 | }); 27 | 28 | test("throws if toString returns non-primitive", function() { 29 | try { 30 | String({ toString: function() { return this; } }); 31 | assert(false, "did not throw"); 32 | } catch(e) { 33 | assert(e instanceof TypeError); 34 | } 35 | }); 36 | 37 | test("throws if valueOf returns non-primitive", function() { 38 | try { 39 | Number({ valueOf: function() { return this; }, toString: null }); 40 | assert(false, "did not throw"); 41 | } catch(e) { 42 | assert(e instanceof TypeError); 43 | } 44 | }); 45 | 46 | test("null == undefined", function() { 47 | assert(null == undefined); 48 | assert(undefined == null); 49 | assert(null !== undefined); 50 | assert(undefined !== null); 51 | }); 52 | -------------------------------------------------------------------------------- /test/builtin/number.js: -------------------------------------------------------------------------------- 1 | test("Number()", function () { 2 | assert(isNaN(Number(undefined)), "Number(): undefined"); 3 | assert(Number(null) === 0, "Number(): null"); 4 | assert(Number(true) === 1, "Number(): true"); 5 | assert(Number(false) === 0, "Number(): false"); 6 | assert(Number(42) === 42, "Number(): number"); 7 | assert(Number("42") === 42, "Number(): string"); 8 | }); 9 | 10 | test("Number.prototype.toString()", function () { 11 | assert((42).toString() === "42", "Number.prototype.toString(): 1"); 12 | assert((42).toString(16) === "2a", "Number.prototype.toString(): 2"); 13 | }); 14 | 15 | test("Number.prototype.valueOf()", function () { 16 | assert((new Number(42)).valueOf() === 42, "Number.prototype.valueOf(): 1"); 17 | }); 18 | 19 | test("Number.prototype.toFixed()", function () { 20 | assert((42).toFixed() === "42", "Number.prototype.toFixed(): 1"); 21 | assert((42).toFixed(3) === "42.000", "Number.prototype.toFixed(): 2"); 22 | }); 23 | 24 | test("Number.prototype.toExponential()", function () { 25 | assert((42).toExponential() === "4.2e+1", "Number.prototype.toExponential(): 1"); 26 | assert((42).toExponential(3) === "4.200e+1", "Number.prototype.toExponential(): 2"); 27 | }); 28 | 29 | test("Number.prototype.toPrecision()", function () { 30 | assert((42.42).toPrecision() === "42.42", "Number.prototype.toPrecision(): 1"); 31 | assert((42.42).toPrecision(1) === "4e+1", "Number.prototype.toPrecision(): 2"); 32 | assert((42.42).toPrecision(2) === "42", "Number.prototype.toPrecision(): 3"); 33 | assert((42.42).toPrecision(3) === "42.4", "Number.prototype.toPrecision(): 4"); 34 | assert((42.42).toPrecision(4) === "42.42", "Number.prototype.toPrecision(): 5"); 35 | }); 36 | -------------------------------------------------------------------------------- /test/twostroke/object.js: -------------------------------------------------------------------------------- 1 | test("hasOwnProperty", function() { 2 | assertEqual(console.hasOwnProperty("log"), true); 3 | assertEqual("test".hasOwnProperty("length"), true); 4 | 5 | // not implemented 6 | //assertEqual("test".hasOwnProperty(0), true); 7 | //assertEqual("test".hasOwnProperty(99), false); 8 | assertEqual([1].hasOwnProperty(0), true); 9 | assertEqual([1].hasOwnProperty(99), false); 10 | assertEqual({ a: 1 }.hasOwnProperty("a"), true); 11 | }); 12 | 13 | test("isPrototypeOf", function() { 14 | assertEqual(Object.prototype.isPrototypeOf(console), true); 15 | assertEqual(Object.isPrototypeOf(console), false); 16 | assertEqual(Number.prototype.isPrototypeOf(new Number(1)), true); 17 | assertEqual(Number.prototype.isPrototypeOf(1), false); 18 | assertEqual(String.prototype.isPrototypeOf(new String("hi")), true); 19 | assertEqual(String.prototype.isPrototypeOf("hi"), false); 20 | }); 21 | 22 | test("propertyIsEnumerable", function() { 23 | assertEqual(true, ({a:1}).propertyIsEnumerable("a")); 24 | assertEqual(false, ({b:1}).propertyIsEnumerable("a")); 25 | assertEqual(false, Object.prototype.propertyIsEnumerable("propertyIsEnumerable")); 26 | }); 27 | 28 | test("toString", function() { 29 | var to_s = Object.prototype.toString; 30 | assertEqual("[object Object]", to_s.call({})); 31 | assertEqual("[object Number]", to_s.call(123)); 32 | assertEqual("[object Boolean]", to_s.call(true)); 33 | assertEqual("[object String]", to_s.call("hi")); 34 | assertEqual("[object String]", to_s.call(new String("hi"))); 35 | }); 36 | 37 | test("typeof", function() { 38 | assertEqual("object", typeof null); 39 | assertEqual("object", typeof {}); 40 | assertEqual("object", typeof new String("hi")); 41 | }); 42 | -------------------------------------------------------------------------------- /src/image/13_templates.js: -------------------------------------------------------------------------------- 1 | @@ 2 | foreach (array( 3 | `Object, `Object->properties['prototype'], 4 | `Function, `Function->properties['prototype'], 5 | `Array, `Array->properties['prototype'], 6 | `String, `String->properties['prototype'], 7 | `Boolean, `Boolean->properties['prototype'], 8 | `Number, `Number->properties['prototype'], 9 | `Math, 10 | `Date, `Date->properties['prototype'], 11 | `RegExp, `RegExp->properties['prototype'], 12 | `Error, `Error->properties['prototype'], 13 | `JSON 14 | ) as $o) 15 | { 16 | foreach ($o->attributes as $k => $v) { 17 | $o->attributes[$k] = $v & ~JS::ENUMERABLE; 18 | } 19 | } 20 | 21 | JS::$objectTemplate = (object) array( 22 | 'properties' => array(), 23 | 'attributes' => array(), 24 | 'getters' => array(), 25 | 'setters' => array(), 26 | 'prototype' => `Object->properties['prototype'], 27 | 'extensible' => TRUE, 28 | 'class' => 'Object', 29 | ); 30 | 31 | JS::$functionTemplate = clone JS::$objectTemplate; 32 | JS::$functionTemplate->prototype = `Function->properties['prototype']; 33 | JS::$functionTemplate->class = 'Function'; 34 | JS::$functionTemplate->loaded = FALSE; 35 | 36 | JS::$argumentsTemplate = clone JS::$objectTemplate; 37 | JS::$argumentsTemplate->class = 'Arguments'; 38 | JS::$argumentsTemplate->attributes['length'] = JS::WRITABLE | JS::CONFIGURABLE; 39 | @@ 40 | 41 | var a = new Array(), 42 | s = new String(), 43 | b = new Boolean(), 44 | n = new Number(), 45 | r = new RegExp(), 46 | x = {}; 47 | 48 | @@ 49 | JS::$arrayTemplate = `a; 50 | JS::$stringTemplate = `s; 51 | JS::$booleanTemplate = `b; 52 | JS::$numberTemplate = `n; 53 | JS::$regexpTemplate = `r; 54 | @@ 55 | 56 | delete a; 57 | delete s; 58 | delete b; 59 | delete n; 60 | delete r; 61 | delete x; 62 | -------------------------------------------------------------------------------- /test/builtin/json.js: -------------------------------------------------------------------------------- 1 | test("JSON.parse()", function () { 2 | assert(JSON.parse("{}") instanceof Object, "JSON.parse(): 1"); 3 | assert(JSON.parse("[]") instanceof Array, "JSON.parse(): 2"); 4 | assert(JSON.parse("null") === null, "JSON.parse(): 3"); 5 | assert(JSON.parse("true") === true, "JSON.parse(): 4"); 6 | assert(JSON.parse("false") === false, "JSON.parse(): 5"); 7 | assert(JSON.parse("42") === 42, "JSON.parse(): 6"); 8 | assert(JSON.parse('"hello"') === "hello", "JSON.parse(): 7"); 9 | 10 | var o = JSON.parse('{ "a": "b", "c": "d" }'); 11 | assert(o.a === "b" && o.c === "d", "JSON.parse(): 8"); 12 | 13 | var a = JSON.parse('[1, 2, 3]'); 14 | assert(a.length === 3 && a[0] === 1 && a[1] === 2 && a[2] === 3, "JSON.parse(): 9"); 15 | 16 | var o = JSON.parse('{ "a": "b", "c": "d" }', function (k, v) { 17 | if (k !== "") { 18 | return undefined; 19 | } 20 | 21 | return v; 22 | }); 23 | 24 | assert(o.a === undefined, "JSON.parse(): 10"); 25 | assert(o.b === undefined, "JSON.parse(): 11"); 26 | }); 27 | 28 | test("JSON.stringify()", function () { 29 | assert(JSON.stringify({}) === "{}", "JSON.stringify(): 1"); 30 | assert(JSON.stringify([]) === "[]", "JSON.stringify(): 2"); 31 | assert(JSON.stringify(null) === "null", "JSON.stringify(): 3"); 32 | assert(JSON.stringify(true) === "true", "JSON.stringify(): 4"); 33 | assert(JSON.stringify(false) === "false", "JSON.stringify(): 5"); 34 | assert(JSON.stringify(42) === "42", "JSON.stringify(): 6"); 35 | assert(JSON.stringify("hello") === '"hello"', "JSON.stringify(): 7"); 36 | assert(JSON.stringify("/") === '"/"', "JSON.stringify(): 8"); 37 | assert(JSON.stringify({ a: "b", c: "d" }) === '{"a":"b","c":"d"}', "JSON.stringify(): 9"); 38 | assert(JSON.stringify([ 1, 2, 3, 4, 5 ]) === "[1,2,3,4,5]", "JSON.stringify(): 10"); 39 | }); 40 | -------------------------------------------------------------------------------- /test/builtin/buffer.js: -------------------------------------------------------------------------------- 1 | var Buffer = require("buffer").Buffer; 2 | 3 | test("Buffer()", function () { 4 | assertEqual((new Buffer(3)).length, 3); 5 | assertEqual((new Buffer("abc")).length, 3); 6 | assertEqual((new Buffer([0x61, 0x62, 0x63])).length, 3); 7 | 8 | try { 9 | new Buffer({}); 10 | assert(false, "did not throw"); 11 | } catch (e) {} 12 | }); 13 | 14 | test("Buffer.isBuffer()", function () { 15 | assert(Buffer.isBuffer(new Buffer(5))); 16 | assert(!Buffer.isBuffer(5)); 17 | }); 18 | 19 | test("Buffer.byteLength()", function () { 20 | assertEqual(Buffer.byteLength("abc"), 3); 21 | }); 22 | 23 | test("Buffer.concat()", function () { 24 | assertEqual(Buffer.concat([]).length, 0); 25 | var b = new Buffer(5); 26 | assertEqual(Buffer.concat([b]), b); 27 | assertEqual(Buffer.concat([new Buffer(5), new Buffer(5)]).length, 10); 28 | assertEqual(Buffer.concat([new Buffer(5), new Buffer(5)], 8).length, 8); 29 | }); 30 | 31 | test("Buffer.prototype.write()", function () { 32 | var b = new Buffer(3); 33 | b.write("abc"); 34 | assertEqual(b.toString(), "abc"); 35 | b.write("abc", 1); 36 | assertEqual(b.toString(), "aab"); 37 | b.write("bbb", 0, 1); 38 | assertEqual(b.toString(), "bab"); 39 | }); 40 | 41 | test("Buffer.prototype.toString()", function () { 42 | assertEqual((new Buffer("abc")).toString(), "abc"); 43 | }); 44 | 45 | test("Buffer.prototype.copy()", function () { 46 | var b1 = new Buffer("abc"), b2 = new Buffer(3); 47 | b1.copy(b2); 48 | assertEqual(b2.toString(), "abc"); 49 | }); 50 | 51 | test("Buffer.prototype.slice()", function () { 52 | var b = new Buffer("aahojky"); 53 | assertEqual(b.slice(1, 5).toString(), "ahoj"); 54 | }); 55 | 56 | test("Buffer.prototype.fill()", function () { 57 | var b = new Buffer(5); 58 | b.fill("a"); 59 | assertEqual(b.toString(), "aaaaa"); 60 | }); 61 | -------------------------------------------------------------------------------- /util/shrink.php: -------------------------------------------------------------------------------- 1 | ?@[\]^`{|}'; 14 | $set = array_flip(preg_split('//',$set)); 15 | $inFor = FALSE; $forParenLevel = 0; 16 | 17 | foreach (token_get_all($input) as $token) { 18 | if (!is_array($token)) { $token = array(0, $token); } 19 | 20 | switch ($token[0]) { 21 | case T_COMMENT: 22 | case T_ML_COMMENT: 23 | case T_DOC_COMMENT: 24 | case T_WHITESPACE: 25 | $space = ' '; 26 | break; 27 | 28 | case T_FOR: 29 | case $token[1] === '(': 30 | case $token[1] === ')': 31 | if ($token[1] === 'for') { 32 | $inFor = TRUE; 33 | $forParenLevel = 0; 34 | } 35 | 36 | if ($token[1] === '(' && $inFor) { ++$forParenLevel; } 37 | 38 | if ($token[1] === ')' && $inFor) { 39 | if (--$forParenLevel <= 0) { $inFor = FALSE; } 40 | } 41 | 42 | default: 43 | if ($token[1] === ';' && !$inFor && 44 | (substr($output, -1) === ';' || substr($output, -1) === '}')) 45 | { 46 | continue; 47 | } 48 | 49 | if ($token[1][0] === "'" && substr($output, -2) === "'." || 50 | $token[1][0] === '"' && substr($output, -2) === '".') 51 | { 52 | $output = substr($output, 0, -2) . substr($token[1], 1); 53 | continue; 54 | } 55 | 56 | if (isset($set[substr($output, -1)]) || isset($set[$token[1]{0}])) { $space = ''; } 57 | $lastc = ord(substr($output, -1)); 58 | if ($token[1] === '.' && $lastc >= 0x30 && $lastc <= 0x39) { 59 | $space = ' '; 60 | } 61 | 62 | $output .= $space . $token[1] . ($token[0] === T_END_HEREDOC ? PHP_EOL : ''); 63 | $space = ''; 64 | } 65 | } 66 | return $output; 67 | } 68 | -------------------------------------------------------------------------------- /src/JSInterpreter.php: -------------------------------------------------------------------------------- 1 | code = $code; 25 | $this->file = $file; 26 | } 27 | 28 | /** 29 | * Compiles code and returns it 30 | * @return string 31 | */ 32 | public function compile() 33 | { 34 | if ($this->compiled === NULL) { 35 | $parser = new JSParser; 36 | list($ok, $result, $error) = $parser->__invoke($this->code, array('file' => $this->file)); 37 | 38 | if (!$ok) { 39 | throw new Exception( 40 | "Syntax error " . ($this->file !== NULL ? " in {$this->file} " : "") . 41 | "on {$error->line}:{$error->column}, expected " . 42 | implode(', ', $error->expected) 43 | ); 44 | } 45 | 46 | $compiler = new JSCompiler; 47 | $this->compiled = $compiler->__invoke($result); 48 | } 49 | 50 | return $this->compiled; 51 | } 52 | 53 | /** 54 | * Runs the script, returns what script returns 55 | * @param array values from array will be added to global object 56 | * @return mixed 57 | */ 58 | public function run(array $vars = array()) 59 | { 60 | $this->compile(); 61 | 62 | if (!class_exists('JS')) { 63 | require_once __DIR__ . '/image.php'; 64 | } 65 | 66 | if ($this->entryPoint === NULL) { 67 | $this->entryPoint = eval($this->compiled); 68 | } 69 | 70 | $global = JS::$global; // FIXME: no copy 71 | 72 | foreach ($vars as $k => $v) { 73 | $global->properties[$k] = JS::fromNative($v); 74 | $global->attributes[$k] = JS::WRITABLE | JS::ENUMERABLE | JS::CONFIGURABLE; 75 | } 76 | 77 | return JS::toNative(call_user_func($this->entryPoint, $global)); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /test/builtin/function.js: -------------------------------------------------------------------------------- 1 | test("Function()", function () { 2 | var F = new Function(); 3 | assertEqual(F(), undefined); 4 | 5 | var n = 1, F = new Function("n++"); 6 | F(); 7 | assertEqual(n, 2, "function does not get current scope"); 8 | 9 | var F = new Function("a", "b", "c", "return a + b + c;"); 10 | assert(isNaN(F())); 11 | assertEqual(F("a"), "aundefinedundefined"); 12 | assertEqual(F("a", "b"), "abundefined"); 13 | assertEqual(F("a", "b", "c"), "abc"); 14 | 15 | var F = new Function("a,b,c", "return a + b + c;"); 16 | assert(isNaN(F())); 17 | assertEqual(F("a"), "aundefinedundefined"); 18 | assertEqual(F("a", "b"), "abundefined"); 19 | assertEqual(F("a", "b", "c"), "abc"); 20 | 21 | var F = new Function("a,b", "c", "return a + b + c;"); 22 | assert(isNaN(F())); 23 | assertEqual(F("a"), "aundefinedundefined"); 24 | assertEqual(F("a", "b"), "abundefined"); 25 | assertEqual(F("a", "b", "c"), "abc"); 26 | }); 27 | 28 | function F(that, msg) { 29 | assert(this === that, msg); 30 | } 31 | 32 | var o = {}; 33 | 34 | test("Function.prototype.toString()", function () { 35 | var FString = F.toString(); 36 | assert(/^function F/.test(FString)); 37 | assert(/\(.*that.*\)/.test(FString)); 38 | assert(/\(.*msg.*\)/.test(FString)); 39 | assert(/assert/.test(FString)); 40 | 41 | var G = function () { return 42; }, 42 | GString = G.toString(), 43 | GNew = eval("return " + GString + ";"); 44 | 45 | assertEqual(G(), GNew()); 46 | assertEqual(G.toString(), GNew.toString()); 47 | }); 48 | 49 | test("Function.prototype.apply()", function () { 50 | F.apply(o, [o, "Function.prototype.apply(): wrong this"]); 51 | }); 52 | 53 | test("Function.prototype.call()", function () { 54 | F.call(o, o, "Function.prototype.call(): wrong this"); 55 | }); 56 | 57 | test("Function.prototype.bind()", function () { 58 | G = F.bind(o, o, "Function.prototype.bind(): wrong this"); 59 | G(); 60 | var F = function () { return this; }, G = F.bind({ hello: "world" }); 61 | assert((new G).hello === undefined, "Function.prototype.bind(): should not be bound when called as constructor"); 62 | }); 63 | -------------------------------------------------------------------------------- /test/builtin/path.js: -------------------------------------------------------------------------------- 1 | var path = require("path"); 2 | 3 | test("path.normalize()", function () { 4 | assertEqual(path.normalize("a//b/c//d///"), "a/b/c/d"); 5 | assertEqual(path.normalize("//a//b/c//d///"), "/a/b/c/d"); 6 | assertEqual(path.normalize("/../../.."), "/"); 7 | assertEqual(path.normalize("./a/./b/./c/./d///"), "a/b/c/d"); 8 | assertEqual(path.normalize("a/../b/../c/../d"), "d"); 9 | }); 10 | 11 | test("path.join()", function () { 12 | assertEqual(path.join("a/", "/b", "/c", "//d///"), "a/b/c/d"); 13 | assertEqual(path.join("//a", "//b", "/c//d", "///"), "/a/b/c/d"); 14 | assertEqual(path.join("/../", "..", ".."), "/"); 15 | assertEqual(path.join("./", "a/./b/./c", "./d///"), "a/b/c/d"); 16 | assertEqual(path.join("a/../b", "../c/../d"), "d"); 17 | }); 18 | 19 | test("path.resolve()", function () { 20 | assertEqual(path.resolve("/foo/bar", "./baz"), "/foo/bar/baz"); 21 | assertEqual(path.resolve("/foo/bar", "/tmp/file"), "/tmp/file"); 22 | assertEqual(path.resolve("wwwroot", "static_files/png/", "../gif/image.gif"), 23 | PHP.fn("getcwd")() + "/wwwroot/static_files/gif/image.gif"); 24 | }); 25 | 26 | test("path.relative()", function () { 27 | assertEqual(path.relative("/", "/home/jakub/js2php"), "home/jakub/js2php"); 28 | assertEqual(path.relative("/a/b/c", "/a/b/c"), ""); 29 | assertEqual(path.relative("/a/b/c", "/"), "../../.."); 30 | assertEqual(path.relative("/data/orandea/test/aaa", "/data/orandea/impl/bbb"), "../../impl/bbb"); 31 | }); 32 | 33 | test("path.dirname()", function () { 34 | assertEqual(path.dirname("a/b/c"), "a/b"); 35 | assertEqual(path.dirname(".."), "."); 36 | assertEqual(path.dirname("../.."), ".."); 37 | assertEqual(path.dirname(""), "."); 38 | }); 39 | 40 | test("path.basename()", function () { 41 | assertEqual(path.basename("a/b/c.html"), "c.html"); 42 | assertEqual(path.basename("a/b/c.html", ".html"), "c"); 43 | }); 44 | 45 | test("path.extname()", function () { 46 | assertEqual(path.extname("index.html"), ".html"); 47 | assertEqual(path.extname("index."), "."); 48 | assertEqual(path.extname("index"), ""); 49 | assertEqual(path.extname(".."), ""); 50 | assertEqual(path.extname("."), ""); 51 | assertEqual(path.extname(".htaccess"), ""); 52 | }); 53 | -------------------------------------------------------------------------------- /test/twostroke/array.js: -------------------------------------------------------------------------------- 1 | test("pop", function() { 2 | var x = [1,2]; 3 | assertEqual(x.pop(), 2); 4 | assertEqual(x.pop(), 1); 5 | assertEqual(x.pop(), undefined); 6 | }); 7 | 8 | test("shift", function() { 9 | var x = [1,2]; 10 | assertEqual(x.shift(), 1); 11 | assertEqual(x.shift(), 2); 12 | assertEqual(x.shift(), undefined); 13 | }); 14 | 15 | test("push", function() { 16 | var x = [1]; 17 | assertEqual(x.push("foo"), 2); 18 | assertEqual(x[0], 1); 19 | assertEqual(x[1], "foo"); 20 | assertEqual(x.push("bar"), 3); 21 | assertEqual(x[0], 1); 22 | assertEqual(x[1], "foo"); 23 | assertEqual(x[2], "bar"); 24 | }); 25 | 26 | test("unshift", function() { 27 | var x = [1]; 28 | assertEqual(x.unshift("foo"), 2); 29 | assertEqual(x[0], "foo"); 30 | assertEqual(x[1], 1); 31 | assertEqual(x.unshift("bar"), 3); 32 | assertEqual(x[0], "bar"); 33 | assertEqual(x[1], "foo"); 34 | assertEqual(x[2], 1); 35 | }); 36 | 37 | test("constructor", function() { 38 | assert("1,2,3,4" == new Array(1,2,3,4)); 39 | assert("1,2,3,4" == Array(1,2,3,4)); 40 | assert("" == new Array()); 41 | }); 42 | 43 | test("splice", function() { 44 | var a = [1,2,3,4,5]; 45 | assert("2,3" == a.splice(1, 2, 7)); 46 | assert("1,7,4,5" == a); 47 | assert("4,5" == a.splice(2)); 48 | assert("1,7" == a); 49 | }); 50 | 51 | test("length=", function() { 52 | var a = [1,2,3,4,5,6,7]; 53 | a.length = 4; 54 | assert("1,2,3,4" == a); 55 | }); 56 | 57 | test("join", function() { 58 | assertEqual("", [].join("")); 59 | assertEqual("", [].join(",")); 60 | assertEqual("12", [1,2].join("")); 61 | assertEqual("1,2", [1,2].join(",")); 62 | assertEqual("1,2", [1,2].join()); 63 | assertEqual(",,,,", Array(5).join(), "join sparse array"); 64 | }); 65 | 66 | test("concat", function() { 67 | var a = []; 68 | 69 | assert(a.concat(1,2,3) == "1,2,3"); 70 | assert(a == "", "concat mutated array"); 71 | 72 | assert(a.concat([1,2,3],4,[5]) == "1,2,3,4,5"); 73 | assert(a == "", "concat mutated array"); 74 | 75 | var b = [1,2,3]; 76 | assert(b.concat() == "1,2,3"); 77 | 78 | assertNotEqual(b.concat(), b); 79 | 80 | assert(Array.prototype.concat.call({ length: 2, 0: 1, 1: 2 }, [3, 4]) == "1,2,3,4"); 81 | }); 82 | -------------------------------------------------------------------------------- /src/image/14_php.js: -------------------------------------------------------------------------------- 1 | var PHP = { 2 | _cachedFns: {}, 3 | _cachedClasses: {}, 4 | 5 | fn: function fn(name) { 6 | if (this._cachedFns[name] !== undefined) { 7 | return this._cachedFns[name]; 8 | } 9 | 10 | @@ 11 | $fn = clone JS::$functionTemplate; 12 | $reflection = new ReflectionFunction(JS::toString(`name, $global)); 13 | 14 | $fn->call = 'JSWrappedFunction'; 15 | 16 | $fn->parameters = array(); 17 | foreach ($reflection->getParameters() as $p) { 18 | $fn->parameters[] = $p->getName(); 19 | } 20 | 21 | $fn->name = $reflection->getName(); 22 | 23 | $fn->properties['prototype'] = clone JS::$objectTemplate; 24 | $fn->attributes['prototype'] = 0; 25 | $fn->properties['prototype']->properties['constructor'] = $fn; 26 | $fn->properties['prototype']->attributes['constructor'] = 0; 27 | $fn->properties['length'] = count($fn->parameters); 28 | $fn->attributes['length'] = 0; 29 | @@ 30 | 31 | this._cachedFns[name] = @@ $fn @@; 32 | 33 | @@ return $fn; @@ 34 | }, 35 | 36 | cls: function cls(name) { 37 | if (this._cachedClasses[name] !== undefined) { 38 | return this._cachedClasses[name]; 39 | } 40 | 41 | @@ 42 | $fn = clone JS::$functionTemplate; 43 | $reflection = new ReflectionClass(JS::toString(`name, $global)); 44 | 45 | $fn->call = 'JSWrappedConstructor'; 46 | 47 | $fn->parameters = array(); 48 | if (($ctor = $reflection->getConstructor()) !== NULL) { 49 | foreach ($ctor->getParameters() as $p) { 50 | $fn->parameters[] = $p->getName(); 51 | } 52 | } 53 | 54 | $fn->name = $reflection->getName(); 55 | 56 | $fn->properties['prototype'] = clone JS::$objectTemplate; 57 | $fn->attributes['prototype'] = 0; 58 | $fn->properties['prototype']->properties['constructor'] = $fn; 59 | $fn->properties['prototype']->attributes['constructor'] = 0; 60 | $fn->properties['length'] = count($fn->parameters); 61 | $fn->attributes['length'] = 0; 62 | @@ 63 | 64 | this._cachedClasses[name] = @@ $fn @@; 65 | 66 | @@ return $fn; @@ 67 | } 68 | }; 69 | 70 | @@ 71 | `PHP->attributes['_cachedFns'] &= ~JS::ENUMERABLE; 72 | `PHP->attributes['_cachedClasses'] &= ~JS::ENUMERABLE; 73 | `PHP->attributes['fn'] &= ~JS::ENUMERABLE; 74 | `PHP->attributes['cls'] &= ~JS::ENUMERABLE; 75 | @@ 76 | -------------------------------------------------------------------------------- /test/compiler/comparison.js: -------------------------------------------------------------------------------- 1 | test("comparison", function () { 2 | assert(undefined === undefined); 3 | assert(undefined !== null); 4 | assert(undefined !== true); 5 | assert(undefined !== false); 6 | assert(undefined !== 42); 7 | assert(undefined !== "hello, world"); 8 | assert(undefined !== {}); 9 | assert(undefined !== []); 10 | 11 | assert(null === null); 12 | assert(null !== true); 13 | assert(null !== false); 14 | assert(null !== 42); 15 | assert(null !== "hello, world"); 16 | assert(null !== {}); 17 | assert(null !== []); 18 | 19 | assert(true === true); 20 | assert(true !== false); 21 | assert(true !== 42); 22 | assert(true !== "hello, world"); 23 | assert(true !== {}); 24 | assert(true !== []); 25 | 26 | assert(false === false); 27 | assert(false !== 42); 28 | assert(false !== "hello, world"); 29 | assert(false !== {}); 30 | assert(false !== []); 31 | 32 | assert(42 === 42); 33 | assert(42 !== "hello, world"); 34 | assert(42 !== {}); 35 | assert(42 !== []); 36 | 37 | assert("hello, world" === "hello, world"); 38 | assert("hello, world" !== {}); 39 | assert("hello, world" !== []); 40 | 41 | assert({} !== {}); 42 | var o = {}; 43 | assert(o === o); 44 | 45 | 46 | assert(undefined == undefined); 47 | assert(undefined == null); 48 | assert(undefined != true); 49 | assert(undefined != false); 50 | assert(undefined != 42); 51 | assert(undefined != "hello, world"); 52 | assert(undefined != {}); 53 | assert(undefined != []); 54 | 55 | assert(null == null); 56 | assert(null != true); 57 | assert(null != false); 58 | assert(null != 42); 59 | assert(null != "hello, world"); 60 | assert(null != {}); 61 | assert(null != []); 62 | 63 | assert(true == true); 64 | assert(true != false); 65 | assert(true != 42); 66 | assert(true != "hello, world"); 67 | assert(true != {}); 68 | assert(true != []); 69 | 70 | assert(false == false); 71 | assert(false != 42); 72 | assert(false != "hello, world"); 73 | assert(false != {}); 74 | assert(false != []); 75 | 76 | assert(42 == 42); 77 | assert(42 != "hello, world"); 78 | assert(42 != {}); 79 | assert(42 != []); 80 | 81 | assert("hello, world" == "hello, world"); 82 | assert("hello, world" != {}); 83 | assert("hello, world" != []); 84 | 85 | assert({} != {}); 86 | var o = {}; 87 | assert(o == o); 88 | }); 89 | -------------------------------------------------------------------------------- /test/twostroke/etc.js: -------------------------------------------------------------------------------- 1 | test("Error constructor works", function() { 2 | var e = new Error(); 3 | assertEqual(e.name, "Error"); 4 | assertEqual(e.message, ""); 5 | 6 | var f = new Error("test"); 7 | assertEqual(f.name, "Error"); 8 | assertEqual(f.message, "test"); 9 | }); 10 | 11 | test("json parser works with simple values", function() { 12 | assertEqual(123, JSON.parse("123")); 13 | assertEqual(123.456, JSON.parse("123.456")); 14 | assertEqual(-123456, JSON.parse("-123.456e3")); 15 | assertEqual(true, JSON.parse("true")); 16 | assertEqual(false, JSON.parse("false")); 17 | assertEqual(null, JSON.parse("null")); 18 | assertEqual("foo", JSON.parse('"foo"')); 19 | assertEqual("", JSON.parse('""')); 20 | }); 21 | 22 | test("json parser works with arrays", function() { 23 | var a = JSON.parse("[1,2,3]"); 24 | assertEqual(3, a.length); 25 | assertEqual(1, a[0]); 26 | assertEqual(2, a[1]); 27 | assertEqual(3, a[2]); 28 | 29 | var a = JSON.parse("[[[[1]]]]"); 30 | assertEqual(1, a.length); 31 | assertEqual(1, a[0][0][0][0]); 32 | }); 33 | 34 | test("json parser works with object", function() { 35 | var o = JSON.parse('{ "a": 1, "b": [2] }'); 36 | assertEqual(1, o.a); 37 | assertEqual(1, o.b.length); 38 | assertEqual(2, o.b[0]); 39 | 40 | var o = JSON.parse('{"a":{"b":{"a":{"b":123}}}}'); 41 | assertEqual(123, o.a.b.a.b); 42 | }); 43 | 44 | test("json stringifer", function() { 45 | assertEqual("123", JSON.stringify(123)); 46 | assertEqual("123.456", JSON.stringify(123.456)); 47 | assertEqual("-123456", JSON.stringify(-123.456e3)); 48 | assertEqual("true", JSON.stringify(true)); 49 | assertEqual("false", JSON.stringify(false)); 50 | assertEqual("null", JSON.stringify(null)); 51 | assertEqual('"foo"', JSON.stringify("foo")); 52 | assertEqual('""', JSON.stringify("")); 53 | }); 54 | 55 | test("json parser works with arrays", function() { 56 | assertEqual("[1,2,3]", JSON.stringify([1,2,3])); 57 | assertEqual("[[[[1]]]]", JSON.stringify([[[[1]]]])); 58 | }); 59 | 60 | test("json parser works with object", function() { 61 | assertEqual('{"a":1,"b":[2]}', JSON.stringify({ a: 1, b: [2] })); 62 | assertEqual('{"a":{"b":{"a":{"b":123}}}}', JSON.stringify({a:{b:{a:{b:123}}}})); 63 | }); 64 | -------------------------------------------------------------------------------- /src/image/17_crypto.js: -------------------------------------------------------------------------------- 1 | require.modules.crypto = { 2 | exports: { 3 | createHash: function createHash(algorithm) { 4 | var hash = { 5 | update: function update(data, inputEncoding) { 6 | if (inputEncoding) { 7 | throw new NotImplementedError("Hash.update(): inputEncoding ignored"); 8 | } 9 | 10 | @@ hash_update($leThis->ctx, JS::toString(`data, $global)); @@ 11 | 12 | return this; 13 | }, 14 | 15 | digest: function digest(encoding) { 16 | var output = @@ hash_final($leThis->ctx, TRUE); @@ 17 | encoding = encoding || "binary"; 18 | 19 | switch (encoding) { 20 | case "binary": 21 | return output; 22 | 23 | case "hex": 24 | return @@ bin2hex(`output) @@; 25 | 26 | case "base64": 27 | return @@ base64_encode(`output) @@; 28 | 29 | default: 30 | throw new Error("Hash.digest(): unknown encoding " + encoding); 31 | } 32 | } 33 | }; 34 | 35 | @@ `hash->ctx = hash_init(`algorithm); @@ 36 | 37 | return hash; 38 | }, 39 | 40 | createHmac: function createHmac(algorithm, key) { 41 | var hash = { 42 | update: function update(data, inputEncoding) { 43 | if (inputEncoding) { 44 | throw new NotImplementedError("Hmac.update(): inputEncoding ignored"); 45 | } 46 | 47 | @@ hash_update($leThis->ctx, JS::toString(`data, $global)); @@ 48 | 49 | return this; 50 | }, 51 | 52 | digest: function digest(encoding) { 53 | var output = @@ hash_final($leThis->ctx, TRUE); @@ 54 | encoding = encoding || "binary"; 55 | 56 | switch (encoding) { 57 | case "binary": 58 | return output; 59 | 60 | case "hex": 61 | return @@ bin2hex(`output) @@; 62 | 63 | case "base64": 64 | return @@ base64_encode(`output) @@; 65 | 66 | default: 67 | throw new Error("Hmac.digest(): unknown encoding " + encoding); 68 | } 69 | } 70 | }; 71 | 72 | @@ `hash->ctx = hash_init(`algorithm, HASH_HMAC, `key); @@ 73 | 74 | return hash; 75 | }, 76 | 77 | md5: function (data) { 78 | return require.modules.crypto.exports.createHash("md5").update(data).digest("hex"); 79 | }, 80 | 81 | sha1: function (data) { 82 | return require.modules.crypto.exports.createHash("sha1").update(data).digest("hex"); 83 | } 84 | } 85 | }; 86 | -------------------------------------------------------------------------------- /test/twostroke/function.js: -------------------------------------------------------------------------------- 1 | test("typeof", function() { 2 | assertEqual(typeof function() {}, "function"); 3 | }); 4 | 5 | test("undefined arguments", function() { 6 | function foo(a, b, c) { 7 | assertEqual(a, 1); 8 | assertEqual(b, undefined); 9 | assertEqual(c, undefined); 10 | } 11 | foo(1); 12 | }); 13 | 14 | test("apply", function() { 15 | (function() { 16 | assertEqual(typeof this, "object"); 17 | assertEqual(+this, 1); 18 | }).apply(1); 19 | 20 | (function(a, b, c) { 21 | assertEqual(typeof this, "object"); 22 | assertEqual(Boolean(this), true); 23 | assertEqual(a, 1); 24 | assertEqual(b, "test"); 25 | assertEqual(c, false); 26 | }).apply(true, [1, "test", false]); 27 | }); 28 | 29 | test("toString", function() { 30 | assert(/function foo/.test((function foo() { }).toString())); 31 | }); 32 | 33 | test("new with member access", function() { 34 | var x = { 35 | y: { 36 | z: function() { 37 | assert(true); 38 | return true; 39 | } 40 | } 41 | }; 42 | (new x.y.z()) || assert(false); 43 | }); 44 | 45 | test("new with array index", function() { 46 | var x = { 47 | y: { 48 | z: function() { 49 | assert(true); 50 | return true; 51 | } 52 | } 53 | }; 54 | (new x["y"]["z"]()) || assert(false); 55 | }); 56 | 57 | test("instanceof", function() { 58 | assert(!("hi" instanceof String)); 59 | assert(new String("hi") instanceof String); 60 | var x = String; 61 | assert(new x("hi") instanceof String); 62 | assert(new String("hi") instanceof x); 63 | assert(Function instanceof Function); 64 | try { 65 | String instanceof new String("hi"); 66 | assert(false, "did not throw"); 67 | } catch(e) { 68 | assert(/instanceof/.test(e.toString())); 69 | } 70 | }); 71 | 72 | test("function constructor", function() { 73 | var f = new Function("return 123;"); 74 | assertEqual(123, f()); 75 | }); 76 | 77 | test("function constructor arguments", function() { 78 | var f = new Function("a", "b", "return a + b;"); 79 | assertEqual(3, f(1,2)); 80 | }); 81 | 82 | test("function constructor comma separated arguments", function() { 83 | var f = new Function("a,b", "return a + b;"); 84 | assertEqual(3, f(1,2)); 85 | 86 | var f = new Function("a,b", "c,d", "return a + b + c + d;"); 87 | assertEqual(10, f(1,2,3,4)); 88 | }); 89 | -------------------------------------------------------------------------------- /src/image/08_math.js: -------------------------------------------------------------------------------- 1 | var Math = { 2 | E: 2.7182818284590452354, 3 | LN10: 2.302585092994046, 4 | LN2: 0.6931471805599453, 5 | LOG2E: 1.4426950408889634, 6 | LOG10E: 0.4342944819032518, 7 | PI: 3.1415926535897932, 8 | SQRT1_2: 0.7071067811865476, 9 | SQRT2: 1.4142135623730951, 10 | 11 | abs: function (x) { 12 | return @@ abs(JS::toNumber(`x, $global)) @@; 13 | }, 14 | 15 | acos: function (x) { 16 | return @@ acos(JS::toNumber(`x, $global)) @@; 17 | }, 18 | 19 | asin: function (x) { 20 | return @@ asin(JS::toNumber(`x, $global)) @@; 21 | }, 22 | 23 | atan: function (x) { 24 | return @@ atan(JS::toNumber(`x, $global)) @@; 25 | }, 26 | 27 | atan2: function (x) { 28 | return @@ atan2(JS::toNumber(`x, $global)) @@; 29 | }, 30 | 31 | ceil: function (x) { 32 | return @@ ceil(JS::toNumber(`x, $global)) @@; 33 | }, 34 | 35 | cos: function (x) { 36 | return @@ cos(JS::toNumber(`x, $global)) @@; 37 | }, 38 | 39 | exp: function (x) { 40 | return @@ exp(JS::toNumber(`x, $global)) @@; 41 | }, 42 | 43 | floor: function (x) { 44 | return @@ floor(JS::toNumber(`x, $global)) @@; 45 | }, 46 | 47 | log: function (x) { 48 | return @@ log(JS::toNumber(`x, $global)) @@; 49 | }, 50 | 51 | max: function (a, b) { 52 | for (var i = 0, l = arguments.length, max = -Infinity; i < l; ++i) { 53 | if (isNaN(arguments[i])) { 54 | return NaN; 55 | } 56 | 57 | if (arguments[i] > max) { 58 | max = arguments[i]; 59 | } 60 | } 61 | 62 | return max; 63 | }, 64 | 65 | min: function (a, b) { 66 | for (var i = 0, l = arguments.length, min = +Infinity; i < l; ++i) { 67 | if (isNaN(arguments[i])) { 68 | return NaN; 69 | } 70 | 71 | if (arguments[i] < min) { 72 | min = arguments[i]; 73 | } 74 | } 75 | 76 | return min; 77 | }, 78 | 79 | pow: function (x, y) { 80 | return @@ pow(JS::toNumber(`x, $global), JS::toNumber(`y, $global)) @@; 81 | }, 82 | 83 | random: function () { 84 | return @@ mt_rand() / mt_getrandmax() @@; 85 | }, 86 | 87 | round: function (x) { 88 | return @@ round(JS::toNumber(`x, $global)) @@; 89 | }, 90 | 91 | sin: function (x) { 92 | return @@ sin(JS::toNumber(`x, $global)) @@; 93 | }, 94 | 95 | sqrt: function (x) { 96 | return @@ sqrt(JS::toNumber(`x, $global)) @@; 97 | }, 98 | 99 | tan: function (x) { 100 | return @@ tan(JS::toNumber(`x, $global)) @@; 101 | } 102 | }; 103 | 104 | @@ `Math->class = "Math"; @@ 105 | -------------------------------------------------------------------------------- /test/twostroke/math.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | function assert_feq(a, b) { 4 | assert(Math.abs(a - b) < 0.00000001); 5 | } 6 | 7 | test("sin", function() { 8 | assert_feq(0, Math.sin(0)); 9 | assert_feq(1, Math.sin(Math.PI / 2)); 10 | assert_feq(0, Math.sin(Math.PI)); 11 | assert_feq(-1, Math.sin(Math.PI * 3 / 2)); 12 | assert_feq(0, Math.sin(Math.PI * 2)); 13 | }); 14 | 15 | test("cos", function() { 16 | assert_feq(1, Math.cos(0)); 17 | assert_feq(0, Math.cos(Math.PI / 2)); 18 | assert_feq(-1, Math.cos(Math.PI)); 19 | assert_feq(0, Math.cos(Math.PI * 3 / 2)); 20 | assert_feq(1, Math.cos(Math.PI * 2)); 21 | }); 22 | 23 | test("tan", function() { 24 | assert_feq(0, Math.tan(0)); 25 | }); 26 | 27 | test("sqrt", function() { 28 | assert_feq(0, Math.sqrt(0)); 29 | assert_feq(4, Math.sqrt(16)); 30 | assert_feq(Math.PI, Math.sqrt(Math.PI * Math.PI)); 31 | assert(isNaN(Math.sqrt(-1))); 32 | }); 33 | 34 | test("max", function() { 35 | assertEqual(-Infinity, Math.max()); 36 | assertEqual(10, Math.max(10)); 37 | assertEqual(20, Math.max(0.5, 20)); 38 | assert(isNaN(Math.max(1, 2, NaN, 3, 4))); 39 | }); 40 | 41 | test("min", function() { 42 | assertEqual(Infinity, Math.min()); 43 | assertEqual(10, Math.min(10)); 44 | assertEqual(0.5, Math.min(0.5, 20)); 45 | assert(isNaN(Math.min(1, 2, NaN, 3, 4))); 46 | }); 47 | 48 | test("parseInt", function() { 49 | assertEqual(123, parseInt("123")); 50 | assertEqual(123, parseInt("123", 10)); 51 | assertEqual(123, parseInt("0123", 10)); 52 | assertEqual(123, parseInt("0173")); 53 | assertEqual(123, parseInt("173", 8)); 54 | assertEqual(123, parseInt("0x7b")); 55 | assertEqual(123, parseInt("0X7B")); 56 | assertEqual(123, parseInt("7b", 16)); 57 | 58 | assert(isNaN(parseInt("123", 1))); 59 | assert(isNaN(parseInt("123", 37))); 60 | assert(isNaN(parseInt("f00"))); 61 | }); 62 | 63 | test("parseFloat", function() { 64 | assertEqual(3.14, parseFloat("3.14")); 65 | assertEqual(3.14, parseFloat("314e-2")); 66 | assertEqual(3.14, parseFloat("0.0314E+2")); 67 | assertEqual(3.14, parseFloat("3.14more non-digit characters")) 68 | assertEqual(3.14, parseFloat(" \t3.14")); 69 | assert(isNaN(parseFloat("what"))); 70 | assert(isNaN(parseFloat(""))); 71 | assert(isNaN(parseFloat("FF2"))); 72 | }); 73 | 74 | test("pow", function() { 75 | assertEqual(27, Math.pow(3, 3)); 76 | assertEqual(4, Math.pow(16, 0.5)); 77 | assertEqual(1024, Math.pow(16, 2.5)); 78 | assert(isNaN(Math.pow(-1, 0.5))); 79 | }) 80 | 81 | })(); 82 | -------------------------------------------------------------------------------- /src/image/01_global.js: -------------------------------------------------------------------------------- 1 | var NaN = @@ NAN @@; 2 | var Infinity = @@ INF @@; 3 | var undefined = @@ JS::$undefined @@; 4 | 5 | function eval(x) { 6 | @@ 7 | $parser = new JSParser; 8 | 9 | list($ok, $ast, $error) = $parser->__invoke(JS::toString(`x, $global), array('file' => '')); 10 | 11 | if (!$ok) { @@ 12 | throw new SyntaxError("eval(): syntax error at " + 13 | @@ $error->line @@ + ":" + @@ $error->column @@ + 14 | ", expected " + @@ implode(', ', $error->expected) @@); 15 | @@ } 16 | 17 | $compiler = new JSCompiler; 18 | $code = $compiler->__invoke($ast, array('loader' => JS::$loader)); 19 | $entryPoint = eval($code); 20 | return $entryPoint($global, $global->scope[$global->scope_sp - 1]); 21 | @@ 22 | } 23 | 24 | function parseInt(string, radix) { 25 | if (radix !== undefined && (radix < 2 || radix > 36)) { 26 | return NaN; 27 | } 28 | 29 | if (radix !== undefined) { 30 | return @@ intval(JS::toString(`string, $global), (int) JS::toNumber(`radix, $global)) @@; 31 | } 32 | 33 | string = @@ JS::toString(`string, $global) @@; 34 | 35 | if (@@ empty(`string) @@) { 36 | return NaN; 37 | } 38 | 39 | if (@@ `string[0] === '0' && isset(`string[1]) && (`string[1] === 'x' || `string[1] === 'X') @@) { 40 | return parseInt(@@ substr(`string, 2) @@, 16); 41 | } 42 | 43 | if (@@ `string[0] === '0' && isset(`string[1]) @@) { 44 | return @@ intval(`string, 8) @@; 45 | } 46 | 47 | var i = @@ intval(`string) @@; 48 | 49 | if (@@ ((string) `i) !== `string @@) { 50 | return NaN; 51 | } 52 | 53 | return i; 54 | } 55 | 56 | function parseFloat(string) { 57 | string = @@ ltrim(JS::toString(`string, $global)) @@; 58 | var f = @@ floatval(`string) @@; 59 | 60 | if (@@ empty(`string) || ((string) `f) === '0' && `string[0] !== '0' @@) { 61 | return NaN; 62 | } 63 | 64 | return f; 65 | } 66 | 67 | function isNaN(number) { 68 | return @@ is_nan(JS::toNumber(`number, $global)) @@; 69 | } 70 | 71 | function isFinite(number) { 72 | return @@ is_finite(JS::toNumber(`number, $global)) @@; 73 | } 74 | 75 | function decodeURI(encodedURI) { 76 | throw new NotImplementedError("decodeURI(): not implemented."); 77 | } 78 | 79 | function decodeURIComponent(encodedURIComponent) { 80 | throw new NotImplementedError("decodeURIComponent(): not implemented."); 81 | } 82 | 83 | function encodeURI(uri) { 84 | throw new NotImplementedError("encodedURI(): not implemented."); 85 | } 86 | 87 | function encodeURIComponent(uriComponent) { 88 | throw new NotImplementedError("encodeURIComponent(): not implemented."); 89 | } 90 | -------------------------------------------------------------------------------- /src/image/11_error.js: -------------------------------------------------------------------------------- 1 | function Error(message) { 2 | var e = {}; 3 | 4 | if (message !== undefined) { 5 | e.message = message; 6 | } 7 | 8 | @@ `e->prototype = `Error->properties['prototype']; @@ 9 | @@ `e->class = 'Error'; @@ 10 | @@ `e->extensible = TRUE; @@ 11 | 12 | return e; 13 | } 14 | 15 | Error.prototype = {}; 16 | @@ `Error->properties['prototype']->prototype = `Object->properties['prototype']; @@ 17 | @@ `Error->properties['prototype']->class = 'Error'; @@ 18 | @@ `Error->properties['prototype']->extensible = TRUE; @@ 19 | Error.prototype.constructor = Error; 20 | 21 | Error.prototype.name = "Error"; 22 | 23 | Error.prototype.message = ""; 24 | 25 | Error.prototype.toString = function () { 26 | var name = this.name, msg = this.message; 27 | 28 | if (name === undefined) { 29 | name = ""; 30 | } 31 | 32 | if (msg === undefined) { 33 | msg = ""; 34 | } 35 | 36 | if (name === "" && msg === "") { 37 | return "Error"; 38 | } 39 | 40 | if (name === "") { 41 | return msg; 42 | } 43 | 44 | if (msg === "") { 45 | return name; 46 | } 47 | 48 | return name + ": " + msg; 49 | }; 50 | 51 | function RangeError(message) { 52 | var e = new Error(message); 53 | e.name = "RangeError"; 54 | @@ `e->prototype = `RangeError->properties['prototype']; @@ 55 | return e; 56 | } 57 | 58 | @@ `RangeError->properties['prototype']->prototype = `Error->properties['prototype']; @@ 59 | 60 | function ReferenceError(message) { 61 | var e = new Error(message); 62 | e.name = "ReferenceError"; 63 | @@ `e->prototype = `ReferenceError->properties['prototype']; @@ 64 | return e; 65 | } 66 | 67 | @@ `ReferenceError->properties['prototype']->prototype = `Error->properties['prototype']; @@ 68 | 69 | function SyntaxError(message) { 70 | var e = new Error(message); 71 | e.name = "SyntaxError"; 72 | @@ `e->prototype = `SyntaxError->properties['prototype']; @@ 73 | return e; 74 | } 75 | 76 | @@ `SyntaxError->properties['prototype']->prototype = `Error->properties['prototype']; @@ 77 | 78 | function TypeError(message) { 79 | var e = new Error(message); 80 | e.name = "TypeError"; 81 | @@ `e->prototype = `TypeError->properties['prototype']; @@ 82 | return e; 83 | } 84 | 85 | @@ `TypeError->properties['prototype']->prototype = `Error->properties['prototype']; @@ 86 | 87 | // non-standard 88 | function NotImplementedError(message) { 89 | var e = new Error(message); 90 | e.name = "NotImplementedError"; 91 | @@ `e->prototype = `NotImplementedError->properties['prototype']; @@ 92 | return e; 93 | } 94 | 95 | @@ `NotImplementedError->properties['prototype']->prototype = `Error->properties['prototype']; @@ 96 | -------------------------------------------------------------------------------- /test/twostroke/date.js: -------------------------------------------------------------------------------- 1 | test("new Date", function() { 2 | assertEqual(1970, new Date(9876543210).getFullYear()); 3 | }); 4 | 5 | test("string coercion", function() { 6 | // !!! specification does not say it has to have this format 7 | // 8 | // assert(/^Sat Apr 25 1970 /.test(String(new Date(9876543210))), "string did not start with what was expected, it is: " + String(new Date(9876543210))); 9 | }); 10 | 11 | test("number coercion", function() { 12 | //assertEqual(9876543210, Number(new Date(9876543210))); 13 | }); 14 | 15 | test("calling date without new gives current date as string", function() { 16 | assertEqual("string", typeof Date()); 17 | assertNotEqual("Sat Apr 25 1970 17:29:03 GMT+1000 (EST)", String(Date(9876543210))); 18 | }); 19 | 20 | test("date prefers to be coerced to a number", function() { 21 | // this is true: 22 | // String(new Date(9876543211)) < String(new Date(0)) 23 | // because that ends up as: 24 | // "Sat Apr 25 1970 17:29:03 GMT+1000 (EST)" < "Thu Jan 01 1970 10:00:00 GMT+1000 (EST)" 25 | // which is true in the string sense. 26 | // implicit number corecion should go the other way 27 | assert(new Date(9876543211) > new Date(0), "implicit (to number) coercion goes the other way"); 28 | }); 29 | 30 | test("getDate", function() { 31 | assertEqual(25, new Date(9876543210).getDate()); 32 | assertEqual(12, new Date(1350000000000).getDate()); 33 | }); 34 | 35 | test("getDay", function() { 36 | assertEqual(6, new Date(9876543210).getDay()); 37 | assertEqual(5, new Date(1350000000000).getDay()); 38 | }); 39 | 40 | test("getFullYear", function() { 41 | assertEqual(1970, new Date(9876543210).getFullYear()); 42 | assertEqual(2012, new Date(1350000000000).getFullYear()); 43 | }); 44 | 45 | test("getYear", function() { 46 | assertEqual(70, new Date(9876543210).getYear()); 47 | assertEqual(112, new Date(1350000000000).getYear()); 48 | }); 49 | 50 | test("getMilliseconds", function() { 51 | assertEqual(210, new Date(9876543210).getMilliseconds()); 52 | assertEqual(0, new Date(1350000000000).getMilliseconds()); 53 | }); 54 | 55 | test("getMinutes", function() { 56 | assertEqual(29, new Date(9876543210).getMinutes()); 57 | assertEqual(0, new Date(1350000000000).getMinutes()); 58 | }); 59 | 60 | test("getTime", function() { 61 | assertEqual(9876543210, new Date(9876543210).getTime()); 62 | assertEqual(1350000000000, new Date(1350000000000).getTime()); 63 | }); 64 | 65 | test("UTC", function() { 66 | // not implemented 67 | /* 68 | assertEqual(776307723004, Date.UTC(1994, 7, 8, 1, 2, 3, 4)); 69 | assertEqual(775699200000, Date.UTC(1994, 7)); 70 | assert(isNaN(Date.UTC())); 71 | */ 72 | }); 73 | -------------------------------------------------------------------------------- /test/builtin/regexp.js: -------------------------------------------------------------------------------- 1 | test("RegExp()", function () { 2 | var r = RegExp("a"); 3 | assert(r instanceof RegExp, "RegExp(): wrong instance"); 4 | assert(r.source === "a", "RegExp(): wrong source"); 5 | assert(r.global === false, "RegExp(): wrong global"); 6 | assert(r.ignoreCase === false, "RegExp(): wrong ignore case"); 7 | assert(r.multiline === false, "RegExp(): wrong multiline"); 8 | 9 | var r = new RegExp("b", "gim"); 10 | assert(r instanceof RegExp, "RegExp(): wrong instance 2"); 11 | assert(r.source === "b", "RegExp(): wrong source 2"); 12 | assert(r.global === true, "RegExp(): wrong global 2"); 13 | assert(r.ignoreCase === true, "RegExp(): wrong ignore case 2"); 14 | assert(r.multiline === true, "RegExp(): wrong multiline 2"); 15 | 16 | var r = /c/g; 17 | assert(r instanceof RegExp, "RegExp(): wrong instance 3"); 18 | assert(r.source === "c", "RegExp(): wrong source 3"); 19 | assert(r.global === true, "RegExp(): wrong global 3"); 20 | assert(r.ignoreCase === false, "RegExp(): wrong ignore case 3"); 21 | assert(r.multiline === false, "RegExp(): wrong multiline 3"); 22 | }); 23 | 24 | test("RegExp.prototype.exec()", function () { 25 | var r = /c/g; 26 | assert(r.exec("baba") === null, "RegExp.prototype.exec(): should not match"); 27 | 28 | var r = /a/g, m = r.exec("baba"); 29 | assertNotEqual(m, null, "RegExp.prototype.exec(): should match"); 30 | assert(m.length === 1 && m[0] === "a", "RegExp.prototype.exec(): wrong match"); 31 | assert(m.index === 1, "RegExp.prototype.exec(): wrong index"); 32 | assert(m.input === "baba", "RegExp.prototype.exec(): wrong input"); 33 | assert(r.lastIndex === 2, "RegExp.prototype.exec(): wrong regexp.lastIndex"); 34 | 35 | var r = /a/, m = r.exec("baba"); 36 | assert(r.lastIndex === 0, "RegExp.prototype.exec(): should not change lastIndex"); 37 | 38 | var r = /(a)(b)/, m = r.exec("abab"); 39 | assert(m.length === 3 && m[0] === "ab" && m[1] === "a" && m[2] === "b", 40 | "RegExp.prototype.exec(): wrong match"); 41 | }); 42 | 43 | test("RegExp.prototype.test()", function () { 44 | assert(/a/.test("abab") === true, "RegExp.prototype.test(): 1"); 45 | assert(/b/.test("abab") === true, "RegExp.prototype.test(): 2"); 46 | assert(/c/.test("abab") === false, "RegExp.prototype.test(): 3"); 47 | }); 48 | 49 | test("RegExp.prototype.toString()", function () { 50 | assert(/a/.toString() === "/a/", "RegExp.prototype.toString(): 1"); 51 | assert(/a/g.toString() === "/a/g", "RegExp.prototype.toString(): 2"); 52 | assert(/a/i.toString() === "/a/i", "RegExp.prototype.toString(): 3"); 53 | assert(/a/m.toString() === "/a/m", "RegExp.prototype.toString(): 4"); 54 | assert(/a/gim.toString() === "/a/gim", "RegExp.prototype.toString(): 5"); 55 | }); 56 | -------------------------------------------------------------------------------- /src/image/18_path.js: -------------------------------------------------------------------------------- 1 | require.modules.path = { 2 | exports: { 3 | normalize: function normalize(path) { 4 | @@ 5 | `path = JS::toString(`path, $global); 6 | $absolute = `path[0] === DIRECTORY_SEPARATOR; 7 | $newPath = array(); 8 | 9 | foreach(explode(DIRECTORY_SEPARATOR, `path) as $part) { 10 | if (!empty($part)) { 11 | switch ($part) { 12 | case '.': break; 13 | 14 | case '..': 15 | array_pop($newPath); 16 | break; 17 | 18 | default: 19 | $newPath[] = $part; 20 | } 21 | } 22 | } 23 | 24 | return ($absolute ? DIRECTORY_SEPARATOR : '') . 25 | implode(DIRECTORY_SEPARATOR, $newPath); 26 | @@ 27 | }, 28 | 29 | join: function join() { 30 | var path = []; 31 | for (var i = 0, l = arguments.length; i < l; ++i) { 32 | if (typeof arguments[i] === "string") { 33 | path.push(arguments[i]); 34 | } 35 | } 36 | 37 | return require.modules.path.exports.normalize(path.join(@@ DIRECTORY_SEPARATOR @@)); 38 | }, 39 | 40 | resolve: function resolve(to) { 41 | var path = [@@ getcwd() @@]; 42 | for (var i = 0, l = arguments.length, arg; i < l; ++i) { 43 | arg = String(arguments[i]); 44 | 45 | if (arg.charAt(0) === @@ DIRECTORY_SEPARATOR @@) { 46 | path = [arg]; 47 | 48 | } else { 49 | path.push(arg); 50 | } 51 | } 52 | 53 | return require.modules.path.exports.normalize(path.join(@@ DIRECTORY_SEPARATOR @@)); 54 | }, 55 | 56 | relative: function relative(from, to) { 57 | from = require.modules.path.exports.resolve(from).split(@@ DIRECTORY_SEPARATOR @@); 58 | to = require.modules.path.exports.resolve(to).split(@@ DIRECTORY_SEPARATOR @@); 59 | 60 | while (from[0] === to[0] && from.length && to.length) { 61 | from.shift(); 62 | to.shift(); 63 | } 64 | 65 | var newPath = Array(from.length + 1).join(".." + @@ DIRECTORY_SEPARATOR @@) + 66 | to.join(@@ DIRECTORY_SEPARATOR @@); 67 | 68 | if (newPath.charAt(newPath.length - 1) === "/") { 69 | return newPath.substring(0, newPath.length - 1); 70 | } 71 | 72 | return newPath; 73 | }, 74 | 75 | dirname: function dirname(p) { 76 | if (p === "") { return "."; } 77 | return @@ dirname(JS::toString(`p, $global)) @@; 78 | }, 79 | 80 | basename: function basename(p, ext) { 81 | if (ext !== undefined) { 82 | return @@ basename(JS::toString(`p, $global), JS::toString(`ext, $global)) @@; 83 | } 84 | 85 | return @@ basename(JS::toString(`p, $global)) @@; 86 | }, 87 | 88 | extname: function extname(p) { 89 | p = String(p); 90 | 91 | if (p.lastIndexOf(@@ DIRECTORY_SEPARATOR @@) !== -1) { 92 | p = p.substring(p.lastIndexOf(@@ DIRECTORY_SEPARATOR @@) + 1); 93 | } 94 | 95 | if (p === "." || p === ".." || 96 | p.lastIndexOf(".") === 0 || p.lastIndexOf(".") === -1) { return ""; } 97 | 98 | return p.substring(p.lastIndexOf(".")); 99 | }, 100 | 101 | sep: @@ DIRECTORY_SEPARATOR @@ 102 | } 103 | }; 104 | -------------------------------------------------------------------------------- /test/builtin/crypto.js: -------------------------------------------------------------------------------- 1 | var crypto = require("crypto"); 2 | 3 | test("crypto.createHash()", function () { 4 | assertEqual(crypto.createHash("md5").update("a").digest("hex"), 5 | "0cc175b9c0f1b6a831c399e269772661"); 6 | 7 | assertEqual(crypto.createHash("md5").update("a").digest("base64"), 8 | "DMF1ucDxtqgxw5niaXcmYQ=="); 9 | 10 | assertEqual(PHP.fn("base64_encode")(crypto.createHash("md5").update("a").digest()), 11 | "DMF1ucDxtqgxw5niaXcmYQ=="); 12 | 13 | assertEqual(crypto.createHash("md5").update("a").update("b").update("c").digest("hex"), 14 | "900150983cd24fb0d6963f7d28e17f72"); 15 | 16 | assertEqual(crypto.createHash("md5").update("a").update("b").update("c").digest("base64"), 17 | "kAFQmDzST7DWlj99KOF/cg=="); 18 | 19 | assertEqual(PHP.fn("base64_encode")(crypto.createHash("md5").update("a").update("b").update("c").digest()), 20 | "kAFQmDzST7DWlj99KOF/cg=="); 21 | 22 | assertEqual(crypto.createHash("sha1").update("a").digest("hex"), 23 | "86f7e437faa5a7fce15d1ddcb9eaeaea377667b8"); 24 | 25 | assertEqual(crypto.createHash("sha1").update("a").digest("base64"), 26 | "hvfkN/qlp/zhXR3cuerq6jd2Z7g="); 27 | 28 | assertEqual(PHP.fn("base64_encode")(crypto.createHash("sha1").update("a").digest()), 29 | "hvfkN/qlp/zhXR3cuerq6jd2Z7g="); 30 | 31 | try { 32 | crypto.createHash("md5").digest("bleh"); 33 | assert(false, "did not throw"); 34 | } catch (e) { 35 | } 36 | }); 37 | 38 | test("crypto.createHmac()", function () { 39 | assertEqual(crypto.createHmac("md5", "key").update("a").digest("hex"), 40 | "80ea44ccea14cc2263889567cfacccd7"); 41 | 42 | assertEqual(crypto.createHmac("md5", "key").update("a").digest("base64"), 43 | "gOpEzOoUzCJjiJVnz6zM1w=="); 44 | 45 | assertEqual(PHP.fn("base64_encode")(crypto.createHmac("md5", "key").update("a").digest()), 46 | "gOpEzOoUzCJjiJVnz6zM1w=="); 47 | 48 | assertEqual(crypto.createHmac("md5", "key").update("a").update("b").update("c").digest("hex"), 49 | "d2fe98063f876b03193afb49b4979591"); 50 | 51 | assertEqual(crypto.createHmac("md5", "key").update("a").update("b").update("c").digest("base64"), 52 | "0v6YBj+HawMZOvtJtJeVkQ=="); 53 | 54 | assertEqual(PHP.fn("base64_encode")(crypto.createHmac("md5", "key").update("a").update("b").update("c").digest()), 55 | "0v6YBj+HawMZOvtJtJeVkQ=="); 56 | 57 | assertEqual(crypto.createHmac("sha1", "key").update("a").digest("hex"), 58 | "0db21f05052f323e714ef9bf1f7b000ffe97e8a0"); 59 | 60 | assertEqual(crypto.createHmac("sha1", "key").update("a").digest("base64"), 61 | "DbIfBQUvMj5xTvm/H3sAD/6X6KA="); 62 | 63 | assertEqual(PHP.fn("base64_encode")(crypto.createHmac("sha1", "key").update("a").digest()), 64 | "DbIfBQUvMj5xTvm/H3sAD/6X6KA="); 65 | 66 | try { 67 | crypto.createHmac("md5", "key").digest("bleh"); 68 | assert(false, "did not throw"); 69 | } catch (e) { 70 | } 71 | }); 72 | 73 | test("crypto.md5()", function () { 74 | assertEqual(crypto.md5(""), "d41d8cd98f00b204e9800998ecf8427e"); 75 | }); 76 | 77 | test("crypto.sha1()", function () { 78 | assertEqual(crypto.sha1(""), "da39a3ee5e6b4b0d3255bfef95601890afd80709"); 79 | }); 80 | -------------------------------------------------------------------------------- /test/twostroke/exceptions.js: -------------------------------------------------------------------------------- 1 | test("simple throw/catch", function() { 2 | try { 3 | throw 1; 4 | } catch(e) { 5 | assert(e === 1); 6 | } 7 | }); 8 | 9 | test("code after throw is not executed", function() { 10 | try { 11 | throw 1; 12 | assert(false); 13 | } catch(e) { 14 | assert(true); 15 | } 16 | }); 17 | 18 | test("throw/catch across function", function() { 19 | function foo() { 20 | throw 1; 21 | } 22 | try { 23 | foo(); 24 | } catch(e) { 25 | assert(e === 1); 26 | } 27 | }); 28 | 29 | test("throw/catch across many functions", function() { 30 | function foo(i) { 31 | if(i > 10) { 32 | throw 1; 33 | } else { 34 | foo(i + 1); 35 | } 36 | } 37 | try { 38 | foo(0); 39 | } catch(e) { 40 | assert(e === 1); 41 | } 42 | }); 43 | 44 | test("catch runs before finally", function() { 45 | var x = 0; 46 | try { 47 | assert(x === 0); 48 | throw 1; 49 | } catch(e) { 50 | assert(++x === 1); 51 | } finally { 52 | assert(++x === 2); 53 | } 54 | }); 55 | 56 | test("finally's return value overrides catch's", function() { 57 | function foo() { 58 | try { 59 | throw 1; 60 | } catch(e) { 61 | return "catch"; 62 | } finally { 63 | return "finally"; 64 | } 65 | } 66 | assert(foo() === "finally"); 67 | }); 68 | 69 | test("finally still executes if catch throws an exception", function() { 70 | var x = false; 71 | function foo() { 72 | try { 73 | throw 1; 74 | } catch(e) { 75 | throw 2; 76 | } finally { 77 | x = true; 78 | } 79 | } 80 | try { 81 | foo(); 82 | } catch(e) { 83 | } 84 | assert(x); 85 | }); 86 | 87 | test("finally still executes if catch throws an exception and the exception keeps bubbling up", function() { 88 | var x = false, y = false, z = false; 89 | try { 90 | try { 91 | throw 1; 92 | } catch(e) { 93 | x = (e == 1); 94 | throw 2; 95 | } finally { 96 | y = true; 97 | } 98 | } catch(e) { 99 | z = (e == 2); 100 | } 101 | assert(x && y && z); 102 | }); 103 | 104 | test("finally's throw overrides catch's", function() { 105 | function foo() { 106 | try { 107 | throw 1; 108 | } catch(e) { 109 | throw 2; 110 | } finally { 111 | throw 3; 112 | } 113 | } 114 | try { 115 | foo(); 116 | } catch(e) { 117 | assert(e === 3); 118 | } 119 | }); 120 | 121 | test("nested try/catches work", function() { 122 | try { 123 | try { 124 | throw 1; 125 | } catch(e) { 126 | assert(e === 1); 127 | throw 2; 128 | } 129 | } catch(e) { 130 | assert(e === 2); 131 | } 132 | }); 133 | -------------------------------------------------------------------------------- /src/image/10_regexp.js: -------------------------------------------------------------------------------- 1 | function RegExp(pattern, flags) { 2 | if (@@ is_object(`pattern) && isset(`pattern->class) && `pattern->class === 'RegExp' @@) { 3 | return pattern; 4 | } 5 | 6 | if (pattern === undefined) { 7 | pattern = ""; 8 | } 9 | 10 | if (flags === undefined) { 11 | flags = ""; 12 | } 13 | 14 | var r = { 15 | source: @@ JS::toString(`pattern, $global) @@, 16 | global: false, 17 | ignoreCase: false, 18 | multiline: false, 19 | lastIndex: 0 20 | }; 21 | 22 | @@ `r->class = 'RegExp'; @@ 23 | @@ `r->prototype = `RegExp->properties['prototype']; @@ 24 | 25 | r.source = @@ JS::toString(`pattern, $global) @@; 26 | 27 | for (var i = 0, l = @@ strlen(`flags) @@, c; i < l; ++i) { 28 | @@ `c = substr(`flags, `i, 1); @@ 29 | 30 | if (c === 'g') { 31 | r.global = true; 32 | } else if (c === 'i') { 33 | r.ignoreCase = true; 34 | } else if (c === 'm') { 35 | r.multiline = true; 36 | } else { 37 | throw new SyntaxError("RegExp(): Unknow regexp flag " + c + "."); 38 | } 39 | } 40 | 41 | @@ foreach (`r->attributes as $a => $_) { `r->attributes[$a] = 0; } @@ 42 | @@ `r->attributes['lastIndex'] = JS::WRITABLE; @@ 43 | 44 | return r; 45 | } 46 | 47 | RegExp.prototype = { 48 | source: "", 49 | global: false, 50 | ignoreCase: false, 51 | multiline: false, 52 | lastIndex: 0 53 | }; 54 | @@ `RegExp->properties['prototype']->prototype = `Object->properties['prototype']; @@ 55 | @@ `RegExp->properties['prototype']->class = 'RegExp'; @@ 56 | @@ `RegExp->properties['prototype']->extensible = TRUE; @@ 57 | RegExp.prototype.constructor = RegExp; 58 | 59 | RegExp.prototype.exec = function (string) { 60 | string = @@ JS::toString(`string, $global) @@; 61 | var length = string.length, i = this.lastIndex; 62 | 63 | if (!this.global) { 64 | i = 0; 65 | } 66 | 67 | if (i < 0 || i > length) { 68 | this.lastIndex = 0; 69 | return null; 70 | } 71 | 72 | var pattern = this.source, flags = "", result; 73 | 74 | if (this.ignoreCase) { 75 | flags += "i"; 76 | } 77 | if (this.multiline) { 78 | flags += "m"; 79 | } 80 | 81 | result = @@ preg_match( 82 | '/' . `pattern . '/u' . `flags, 83 | JS::toString(`string, $global), 84 | $matches, 85 | PREG_OFFSET_CAPTURE, 86 | `i 87 | ) @@; 88 | 89 | if (!result) { 90 | this.lastIndex = 0; 91 | return null; 92 | } 93 | 94 | var returnArray = []; 95 | 96 | @@ foreach ($matches as $I => $match) { @@ 97 | @@ if ($match[0] === '' && $I !== 0) { @@ 98 | returnArray.push(undefined); 99 | @@ } else { @@ 100 | returnArray.push(@@ $match[0] @@); 101 | @@ } @@ 102 | @@ } @@ 103 | 104 | if (this.global) { 105 | this.lastIndex = @@ $matches[0][1] + strlen($matches[0][0]) @@; 106 | } 107 | 108 | returnArray.index = @@ $matches[0][1] @@; 109 | returnArray.input = @@ JS::toString(`string, $global) @@; 110 | 111 | return returnArray; 112 | }; 113 | 114 | RegExp.prototype.test = function (string) { 115 | return this.exec(string) !== null; 116 | }; 117 | 118 | RegExp.prototype.toString = function () { 119 | var flags = ""; 120 | 121 | if (this.global) { 122 | flags += "g"; 123 | } 124 | if (this.ignoreCase) { 125 | flags += "i"; 126 | } 127 | if (this.multiline) { 128 | flags += "m"; 129 | } 130 | return "/" + this.source + "/" + flags; 131 | }; 132 | -------------------------------------------------------------------------------- /src/image/03_function.js: -------------------------------------------------------------------------------- 1 | function Function() { 2 | var args = [], body = "", fn; 3 | 4 | if (arguments.length) { 5 | for (var i = 0; i < arguments.length - 1; ++i) { 6 | args.push(arguments[i]); 7 | } 8 | 9 | body = arguments[arguments.length - 1]; 10 | } 11 | 12 | args = args.join(); 13 | fn = "return function(" + args + "){" + body + "};"; 14 | 15 | @@ 16 | $parser = new JSParser; 17 | 18 | list($ok, $ast, $error) = $parser->__invoke(JS::toString(`fn, $global), array('file' => '')); 19 | 20 | if (!$ok) { @@ 21 | throw new SyntaxError("Function(): syntax error at " + 22 | @@ $error->line @@ + ":" + @@ $error->column @@ + 23 | ", expected " + @@ implode(', ', $error->expected) @@); 24 | @@ } 25 | 26 | $compiler = new JSCompiler; 27 | $code = $compiler->__invoke($ast, array('loader' => JS::$loader)); 28 | 29 | $entryPoint = eval($code); 30 | $fn = $entryPoint(); 31 | 32 | $fn->scope = $global->scope[$global->scope_sp - 1]; 33 | 34 | return $fn; 35 | @@ 36 | } 37 | 38 | @@ `Function->class = 'Function'; @@ 39 | 40 | Function.prototype = @@ `Function->prototype @@; 41 | @@ `Function->properties['prototype']->prototype = `Object->properties['prototype']; @@ 42 | @@ `Function->properties['prototype']->class = 'Function'; @@ 43 | @@ `Function->properties['prototype']->extensible = TRUE; @@ 44 | Function.prototype.constructor = Function; 45 | 46 | function F() { 47 | return undefined; 48 | } 49 | 50 | @@ `Function->prototype->call = `F->call; @@ 51 | 52 | delete F; 53 | 54 | Function.prototype.toString = function () { 55 | if (@@ isset($leThis->string) @@) { 56 | return @@ $leThis->string @@; 57 | } 58 | 59 | return @@ 60 | "function " . (isset($leThis->name) ? $leThis->name : "") . 61 | "(" . implode(",", $leThis->parameters) . "){ [native code] }" @@; 62 | }; 63 | 64 | Function.prototype.apply = function (thisArg, argArray) { 65 | var fn = this, call = @@ `fn->call @@; 66 | 67 | if (thisArg === undefined) { 68 | thisArg = @@ $global @@; 69 | } else if (typeof thisArg !== "object" && typeof thisArg !== "function") { 70 | thisArg = @@ JS::toObject(`thisArg, $global) @@; 71 | } 72 | 73 | argArray = argArray || []; 74 | @@ $passArgs = array(); @@ 75 | 76 | for (var i = 0, l = argArray.length, arg; i < l; ++i) { 77 | arg = argArray[i]; 78 | @@ $passArgs[] = `arg; @@ 79 | } 80 | 81 | @@ if (JS::$loader !== NULL && !$leThis->loaded) { $l = JS::$loader; $l($leThis, $global); } @@ 82 | return @@ `call($global, `thisArg, $leThis, $passArgs) @@; 83 | }; 84 | 85 | Function.prototype.call = function (thisArg) { 86 | var fn = this, call = @@ `fn->call @@; 87 | 88 | if (thisArg === undefined) { 89 | thisArg = @@ $global @@; 90 | } else if (typeof thisArg !== "object" && typeof thisArg !== "function") { 91 | thisArg = @@ JS::toObject(`thisArg, $global) @@; 92 | } 93 | 94 | @@ $passArgs = array(); @@ 95 | 96 | for (var i = 1, l = arguments.length, arg; i < l; ++i) { 97 | arg = arguments[i]; 98 | @@ $passArgs[] = `arg; @@ 99 | } 100 | 101 | @@ if (JS::$loader !== NULL && !$leThis->loaded) { $l = JS::$loader; $l($leThis, $global); } @@ 102 | return @@ `call($global, `thisArg, $leThis, $passArgs) @@; 103 | }; 104 | 105 | Function.prototype.bind = function (thisArg) { 106 | var newFn = @@ clone $leThis @@; 107 | 108 | @@ `newFn->boundThis = `thisArg; @@ 109 | 110 | @@ $boundArgs = isset(`newFn->boundArgs) ? `newFn->boundArgs : array(); @@ 111 | for (var i = 1, l = arguments.length, arg; i < l; ++i) { 112 | arg = arguments[i]; 113 | @@ $boundArgs[] = `arg; @@ 114 | --newFn.length; 115 | } 116 | @@ `newFn->boundArgs = $boundArgs; @@ 117 | 118 | return newFn; 119 | }; 120 | -------------------------------------------------------------------------------- /test/twostroke/number.js: -------------------------------------------------------------------------------- 1 | test("toString", function() { 2 | assertEqual((123).toString(), "123"); 3 | assertEqual((123.0).toString(), "123"); 4 | assertEqual((123.456).toString(), "123.456"); 5 | assertEqual(Number.POSITIVE_INFINITY.toString(), "Infinity"); 6 | assertEqual(Number.NEGATIVE_INFINITY.toString(), "-Infinity"); 7 | assertEqual(Number.NaN.toString(), "NaN"); 8 | }); 9 | 10 | test("toString throws on non-number", function() { 11 | try { 12 | Number.prototype.toString.call(""); 13 | assert(false); 14 | } catch(e) { 15 | assert(true); 16 | } 17 | }); 18 | 19 | test("toExponential", function() { 20 | assertEqual((1).toExponential(), "1e+0"); 21 | assertEqual((1.0).toExponential(), "1e+0"); 22 | assertEqual((123).toExponential(), "1.23e+2"); 23 | assertEqual((0.00123).toExponential(), "1.23e-3"); 24 | assertEqual("Infinity", Infinity.toExponential()); 25 | assertEqual("-Infinity", (-Infinity).toExponential()); 26 | assertEqual("NaN", NaN.toExponential()); 27 | assertEqual(123456789..toExponential(3), "1.235e+8"); 28 | assertEqual(123456789..toExponential(NaN), "1e+8"); 29 | 30 | try { 31 | assertEqual(123456789..toExponential(Infinity), "1e+8"); 32 | assert(false, "did not throw"); 33 | } catch (e) { 34 | assert(e instanceof RangeError); 35 | } 36 | 37 | try { 38 | assertEqual(123456789..toExponential(-Infinity), "1e+8"); 39 | assert(false, "did not throw"); 40 | } catch (e) { 41 | assert(e instanceof RangeError); 42 | } 43 | }); 44 | 45 | test("toFixed", function() { 46 | assertEqual("1235", 1234.5.toFixed()); 47 | assertEqual("12.35", 12.345678.toFixed(2)); 48 | assertEqual("1235", 1234.56789.toFixed(NaN)); 49 | 50 | try { 51 | assertEqual("1235", 1234.56789.toFixed(Infinity)); 52 | assert(false, "did not throw"); 53 | } catch (e) { 54 | assert(e instanceof RangeError); 55 | } 56 | 57 | try { 58 | assertEqual("1235", 1234.56789.toFixed(-Infinity)); 59 | assert(false, "did not throw"); 60 | } catch (e) { 61 | assert(e instanceof RangeError); 62 | } 63 | }); 64 | 65 | test("toPrecision", function() { 66 | assertEqual("5.123456", 5.123456.toPrecision()); 67 | assertEqual("5.1235", 5.123456.toPrecision(5)); 68 | assertEqual("5.1", 5.123456.toPrecision(2)); 69 | assertEqual("5", 5.123456.toPrecision(1)); 70 | try { 71 | 123..toPrecision(NaN); 72 | assert(false, "did not throw"); 73 | } catch(e) { 74 | assert(e instanceof RangeError); 75 | } 76 | }); 77 | 78 | test("typeof", function() { 79 | assertEqual(typeof 1, "number"); 80 | assertEqual(typeof 1.0, "number"); 81 | assertEqual(typeof Number(1), "number"); 82 | assertEqual(typeof Number(1.0), "number"); 83 | assertEqual(typeof new Number(1), "object"); 84 | assertEqual(typeof new Number(1.0), "object"); 85 | }); 86 | 87 | test("Number", function() { 88 | assert(isNaN(Number(undefined))); 89 | assertEqual(0, Number(null)); 90 | assertEqual(0, Number(false)); 91 | assertEqual(1, Number(true)); 92 | assertEqual(0, Number("")); 93 | assertEqual(0, Number(" ")); 94 | assertEqual(123, Number("123")); 95 | assertEqual(123.456, Number("123.456")); 96 | assertEqual(5, Number({ toString: function() { return "5"; } })); 97 | }); 98 | 99 | test("int32", function() { 100 | assertEqual(5, 5 >> NaN); 101 | assertEqual(5, 5 << NaN); 102 | assertEqual(5, 5 | NaN); 103 | 104 | assertEqual(4, 5 & 4); 105 | assertEqual(4, 5.5 & 4.7816516); 106 | 107 | assertEqual(-8, ~7); 108 | assertEqual(-9, ~8.9923456789123456789123456789); 109 | }); 110 | 111 | test("==", function() { 112 | assert("5" == 5); 113 | assert(5 == "5"); 114 | }); 115 | 116 | // not implemented 117 | /* 118 | test("^", function() { 119 | assertEqual(6, 3 ^ 5); 120 | }); 121 | 122 | test(">>>", function() { 123 | assertEqual(32589925, -123456789 >>> 7); 124 | assertEqual(964506, 123456789 >>> 7); 125 | }); 126 | */ 127 | 128 | test(">>", function() { 129 | assertEqual(-964507, -123456789 >> 7); 130 | assertEqual(964506, 123456789 >> 7); 131 | }); 132 | -------------------------------------------------------------------------------- /src/image/20_buffer.js: -------------------------------------------------------------------------------- 1 | require.modules.buffer = { 2 | exports: { 3 | Buffer: function Buffer(x) { 4 | var buf = Object.create(Buffer.prototype); 5 | @@ `buf->class = 'Buffer'; @@ 6 | 7 | if (typeof x === "number") { 8 | @@ `buf->value = str_repeat(chr(0), `x); @@ 9 | 10 | } else if (typeof x === "string") { 11 | @@ `buf->value = `x; @@ 12 | 13 | } else if (Array.isArray(x)) { 14 | @@ `buf->value = ""; @@ 15 | for (var i = 0, l = x.length, octet; i < l; ++i) { 16 | octet = x[i]; 17 | @@ `buf->value .= chr(`octet); @@ 18 | } 19 | 20 | } else { 21 | throw new TypeError("Given argument is not a number, a string, or an array"); 22 | } 23 | 24 | buf.length = @@ strlen(`buf->value) @@; 25 | @@ `buf->attributes['length'] = 0; @@ 26 | 27 | return buf; 28 | } 29 | } 30 | }; 31 | 32 | require.modules.buffer.exports.Buffer.isBuffer = function isBuffer(buf) { 33 | return @@ isset(`buf->class) && `buf->class === 'Buffer' @@; 34 | }; 35 | 36 | require.modules.buffer.exports.Buffer.byteLength = function byteLength(string) { 37 | return @@ strlen(JS::toString(`string, $global)) @@; 38 | }; 39 | 40 | require.modules.buffer.exports.Buffer.concat = function concat(list, totalLength) { 41 | if (!Array.isArray(list)) { 42 | throw new TypeError("Given list is not an array"); 43 | } 44 | 45 | if (list.length === 0 || totalLength === 0) { 46 | return new require.modules.buffer.exports.Buffer(0); 47 | 48 | } else if (list.length === 1) { 49 | return list[0]; 50 | 51 | } else { 52 | if (totalLength === undefined) { 53 | totalLength = 0; 54 | for (var i = 0, l = list.length; i < l; ++i) { 55 | totalLength += list[i].length; 56 | } 57 | } 58 | 59 | var buf = new require.modules.buffer.exports.Buffer(totalLength); 60 | 61 | for (var i = 0, l = list.length, offset = 0; i < l; ++i) { 62 | list[i].copy(buf, offset); 63 | offset += list[i].length; 64 | } 65 | 66 | return buf; 67 | } 68 | }; 69 | 70 | require.modules.buffer.exports.Buffer.prototype.write = function write(string, offset, length, encoding) { 71 | if (encoding !== undefined) { 72 | throw new NotImplementedError("encoding argument not implemented"); 73 | } 74 | 75 | offset = offset || 0; 76 | length = length || this.length - offset; 77 | string = @@ JS::toString(`string, $global) @@; 78 | maxlen = this.length; 79 | 80 | @@ $leThis->value = substr(substr($leThis->value, 0, `offset) . substr(`string, 0, `length) . 81 | substr($leThis->value, `offset + `length), 0, `maxlen); @@ 82 | 83 | return length; 84 | }; 85 | 86 | require.modules.buffer.exports.Buffer.prototype.toString = function toString() { 87 | return @@ $leThis->value @@; 88 | }; 89 | 90 | require.modules.buffer.exports.Buffer.prototype.copy = function copy(targetBuffer, targetStart, sourceStart, sourceEnd) { 91 | if (!require.modules.buffer.exports.Buffer.isBuffer(targetBuffer)) { 92 | throw new TypeError("Given first argument is not a buffer"); 93 | } 94 | 95 | targetStart = targetStart || 0; 96 | sourceStart = sourceStart || 0; 97 | sourceEnd = sourceEnd || this.length; 98 | 99 | targetBuffer.write(@@ substr($leThis->value, `sourceStart, `sourceEnd - `sourceStart) @@, targetStart); 100 | }; 101 | 102 | require.modules.buffer.exports.Buffer.prototype.slice = function slice(start, end) { 103 | // not the same as Node.JS', modification of new buffer does not modify the old 104 | start = @@ JS::toNumber(`start, $global) @@; 105 | end = @@ JS::toNumber(`end, $global) @@; 106 | return new require.modules.buffer.exports.Buffer(@@ substr($leThis->value, `start, `end - `start) @@); 107 | }; 108 | 109 | // read* methods omitted 110 | 111 | require.modules.buffer.exports.Buffer.prototype.fill = function fill(value, offset, end) { 112 | value = @@ JS::toString(`value, $global) @@; 113 | value = value.substring(0, 1); 114 | offset = offset || 0; 115 | end = end || this.length; 116 | 117 | for (var i = offset; i < end; ++i) { 118 | @@ $leThis->value[`i] = `value; @@ 119 | } 120 | }; 121 | -------------------------------------------------------------------------------- /src/image/16_console.js: -------------------------------------------------------------------------------- 1 | var console = { 2 | p: function (fd, s) { 3 | @@ fwrite(`fd, `s . "\n"); @@ 4 | }, 5 | 6 | log: function (data) { 7 | @@ static $out; if ($out === NULL) { $out = defined('STDOUT') ? STDOUT : fopen('php://output', 'w'); } @@ 8 | 9 | if (arguments.length > 1) { 10 | this.p(@@ $out @@, PHP.fn("vsprintf")(data, Array.prototype.slice.call(arguments, 1))); 11 | } else { 12 | this.p(@@ $out @@, this.inspect(data)); 13 | } 14 | }, 15 | 16 | error: function (data) { 17 | @@ static $err; if ($err === NULL) { $err = defined('STDERR') ? STDERR : fopen('php://output', 'w'); } @@ 18 | 19 | if (arguments.length > 1) { 20 | this.p(@@ $err @@, PHP.fn("vsprintf")(data, Array.prototype.slice.call(arguments, 1))); 21 | } else { 22 | this.p(@@ $err @@, this.inspect(data)); 23 | } 24 | }, 25 | 26 | dir: function (object) { 27 | return this.log(object); 28 | }, 29 | 30 | _times: {}, 31 | 32 | time: function (label) { 33 | this._times[label] = @@ microtime(TRUE) @@; 34 | }, 35 | 36 | timeEnd: function (label) { 37 | var t = @@ microtime(TRUE) @@; 38 | 39 | if (this._times[label] === undefined) { 40 | throw new Error("Label " + label + " not defined."); 41 | } 42 | 43 | this.log("%s: %f", label, t - this._times[label]); 44 | delete this._times[label]; 45 | }, 46 | 47 | inspect: function (object) { 48 | var dumper = function dumper(v, indent) { 49 | indent = indent || ""; 50 | if (v === undefined) { 51 | return { multiline: false, dump: "undefined" }; 52 | 53 | } else if (v === null || typeof v === "boolean") { 54 | return { multiline: false, dump: @@ json_encode(`v) @@ }; 55 | 56 | } else if (typeof v === "number") { 57 | return { multiline: false, dump: v.toString() }; 58 | 59 | } else if (typeof v === "string") { 60 | return { multiline: false, dump: @@ str_replace("\\/", "/", json_encode(`v)) @@ }; 61 | 62 | } else if (@@ isset(`v->class) && `v->class === 'Date' @@) { 63 | return { multiline: false, dump: "[date " + v.toString() + "]" }; 64 | 65 | } else if (@@ isset(`v->call) @@) { 66 | var o = {}; 67 | 68 | for (var k in v) { 69 | o[k] = v[k]; 70 | } 71 | 72 | if (Object.getOwnPropertyNames(o).length > 0) { 73 | o = dumper(o); 74 | o.dump = " " + o.dump; 75 | 76 | } else { 77 | o = { multiline: false, dump: "" }; 78 | } 79 | 80 | return { multiline: o.multiline, 81 | dump: 82 | "[function" + 83 | @@ (isset(`v->name) ? " " . `v->name : "") @@ + 84 | o.dump + "]" }; 85 | 86 | } else { 87 | var multiline = false, i, k, d, isArray = @@ isset(`v->class) && `v->class === "Array" @@; 88 | 89 | @@ $values = array(); @@ 90 | 91 | if (isArray) { 92 | for (i = 0; i < v.length; ++i) { 93 | if ((d = dumper(v[i], indent + " ")).multiline || d.dump.length > 40) { 94 | multiline = true; 95 | } 96 | 97 | d = d.dump; 98 | 99 | @@ $values[] = `d; @@ 100 | } 101 | 102 | } 103 | 104 | for (k in v) { 105 | if (isArray && String(Number(k)) === String(k)) { continue; } 106 | 107 | if ((d = dumper(v[k], indent + " ")).multiline || d.dump.length > 40) { 108 | multiline = true; 109 | } 110 | 111 | d = d.dump; 112 | 113 | @@ $values[] = (preg_match("~^[A-Za-z][A-Za-z0-9]*$~", `k) 114 | ? `k 115 | : str_replace("\\/", "/", json_encode(`k))) . ": " . `d; @@ 116 | } 117 | 118 | if (@@ empty($values) @@) { 119 | return { multiline: false, dump: isArray ? "[]" : "{}" }; 120 | } 121 | 122 | if (@@ count($values) > 5 @@) { 123 | multiline = true; 124 | } 125 | 126 | return { multiline: multiline, 127 | dump: (isArray ? "[ " : "{ ") + 128 | @@ implode(!`multiline ? ", " : ",\n" . `indent . " ", $values) @@ + 129 | (isArray ? " ]" : " }") }; 130 | } 131 | }; 132 | 133 | return dumper(object).dump; 134 | } 135 | }; 136 | 137 | console.info = console.log; 138 | console.warn = console.error; 139 | -------------------------------------------------------------------------------- /util/jake.d/prelude.js: -------------------------------------------------------------------------------- 1 | var _tasks = {}, 2 | _level = 0; 3 | 4 | function task(name, description, action) { 5 | var actionsStartIndex = 2, 6 | desc = description; 7 | 8 | if (typeof desc !== "string") { 9 | desc = undefined; 10 | actionsStartIndex = 1; 11 | } 12 | 13 | _tasks[name] = { 14 | description: desc, 15 | running: false, 16 | alreadyRun: false, 17 | results: [], 18 | actions: [] 19 | }; 20 | 21 | Array.prototype.slice.call(arguments, actionsStartIndex).forEach(function (dep) { 22 | if (typeof dep === "object") { 23 | _tasks[name].results.push(dep.result); 24 | 25 | } else { 26 | _tasks[name].actions.push(dep); 27 | } 28 | }); 29 | } 30 | 31 | function result(file) { 32 | if (file.charAt(0) !== "/") { 33 | file = PHP.fn("getcwd")() + "/" + file; 34 | } 35 | 36 | return { result: file }; 37 | } 38 | 39 | function run(cmd) { 40 | var ret; 41 | cmd = Array.prototype.join.call(arguments, " "); 42 | puts("[ " + cmd + " ]"); 43 | @@ passthru(`cmd, `ret); @@ 44 | 45 | if (ret !== 0) { 46 | throw new Error("command " + cmd + " failed"); 47 | } 48 | } 49 | 50 | function fail(msg) { 51 | throw new Error(msg); 52 | } 53 | 54 | String.prototype.times = function (n) { 55 | var s = ""; 56 | 57 | while (n-- > 0) { 58 | s += this; 59 | } 60 | 61 | return s; 62 | }; 63 | 64 | function runTask(name, args) { 65 | args = args || []; 66 | 67 | if (typeof _tasks[name] === "undefined") { 68 | throw new Error("no task " + name); 69 | } 70 | 71 | if (!needRun(name)) { return; } 72 | 73 | if (_level++ !== 0) { 74 | puts("=".times(_level - 1) + "> " + name); 75 | } 76 | 77 | _tasks[name].running = true; 78 | 79 | for (var i = 0, l = _tasks[name].actions.length, action; i < l; ++i) { 80 | action = _tasks[name].actions[i]; 81 | 82 | if (typeof action === "string") { 83 | if (action.charAt(0) !== "/") { runTask(action); } 84 | 85 | } else { 86 | action.apply(global, args); 87 | } 88 | } 89 | 90 | _tasks[name].running = false; 91 | _tasks[name].alreadyRun = true; 92 | 93 | if (_level > 1) { 94 | puts("=".times(--_level) + "> ok"); 95 | } 96 | } 97 | 98 | function needRun(name, beingSearched) { 99 | beingSearched = beingSearched || []; 100 | 101 | if (beingSearched.indexOf(name) !== -1) { 102 | return true; 103 | } 104 | 105 | if (_tasks[name].running || _tasks[name].alreadyRun) { 106 | return false; 107 | } 108 | 109 | if (_tasks[name].results.length < 1) { 110 | return true; 111 | } 112 | 113 | for (var i = 0, l = _tasks[name].actions.length, action; i < l; ++i) { 114 | action = _tasks[name].actions[i]; 115 | 116 | if (typeof action === "string") { 117 | if (action.charAt(0) === "/") { 118 | if (PHP.fn("glob")(action).some(function (f) { 119 | if (_tasks[name].results.some(function (g) { 120 | g = PHP.fn("glob")(g); 121 | 122 | if (!g || !g.length || g.some(function (h) { 123 | return PHP.fn("filemtime")(f) > PHP.fn("filemtime")(h); 124 | })) 125 | { 126 | return true; 127 | } 128 | })) 129 | { 130 | return true; 131 | } 132 | })) 133 | { 134 | return true; 135 | } 136 | 137 | } else { 138 | if (needRun(action, beingSearched.concat(name))) { 139 | return true; 140 | } 141 | } 142 | } 143 | } 144 | 145 | return false; 146 | } 147 | 148 | function puts() { 149 | var line = Array.prototype.join.call(arguments, ""); 150 | @@ echo `line, "\n"; @@ 151 | } 152 | 153 | function displayTasks(jakefilePath) { 154 | var nameMax = Object.keys(_tasks).map(function (n) { return n.length }).reduce(Math.max); 155 | 156 | puts(jakefilePath + " defines following tasks:\n"); 157 | for (var name in _tasks) { 158 | puts(" $ jake " + name + 159 | (_tasks[name].description 160 | ? " ".times(nameMax - name.length) + " # " + _tasks[name].description 161 | : "")); 162 | } 163 | puts(); 164 | } 165 | 166 | global.task = task; 167 | global.run = run; 168 | global.fail = fail; 169 | global.puts = puts; 170 | global.result = result; 171 | global.jake = { runTask: runTask, displayTasks: displayTasks }; 172 | -------------------------------------------------------------------------------- /test/builtin/native.js: -------------------------------------------------------------------------------- 1 | test("JS::fromNative()", function () { 2 | assertEqual(@@ JS::fromNative(NULL) @@, null); 3 | assertEqual(@@ JS::fromNative(TRUE) @@, true); 4 | assertEqual(@@ JS::fromNative(FALSE) @@, false); 5 | assertEqual(@@ JS::fromNative(42) @@, 42); 6 | assertEqual(@@ JS::fromNative(5.8) @@, 5.8); 7 | assertEqual(@@ JS::fromNative("abc") @@, "abc"); 8 | 9 | var a = @@ JS::fromNative(array(1, 2, 3)) @@; 10 | assert(Array.isArray(a)); 11 | assertEqual(a.toString(), "1,2,3"); 12 | 13 | var o = @@ JS::fromNative(array("hello" => "JS")) @@; 14 | assert(!Array.isArray(o)); 15 | assertEqual(o.toString(), "[object Object]"); 16 | assertEqual(o.hello, "JS"); 17 | 18 | var o = @@ JS::fromNative((object) array("hello" => "JS")) @@; 19 | assertEqual(Object(o), o); 20 | assertEqual(o.hello, "JS"); 21 | 22 | 23 | var ok; 24 | 25 | @@ 26 | class JSFromNativeTestAncestor 27 | { 28 | private $foo = NULL; 29 | 30 | public function getFoo() 31 | { 32 | return $this->foo; 33 | } 34 | 35 | public function setFoo($foo) 36 | { 37 | $this->foo = $foo; 38 | return $this; 39 | } 40 | 41 | public function __toString() 42 | { 43 | return "hello, world!"; 44 | } 45 | } 46 | 47 | class JSFromNativeTest extends JSFromNativeTestAncestor 48 | { 49 | static $ok; 50 | 51 | public function doSomething() 52 | { 53 | self::$ok = TRUE; 54 | return TRUE; 55 | } 56 | 57 | static function staticMethod() 58 | { 59 | } 60 | 61 | protected function protectedMethod() 62 | { 63 | } 64 | 65 | private function privateMethod() 66 | { 67 | } 68 | } 69 | 70 | class JSFromNativeTestInvokable 71 | { 72 | public function __invoke($who) 73 | { 74 | return "hello, $who!"; 75 | } 76 | } 77 | 78 | JSFromNativeTest::$ok =& `ok; 79 | @@ 80 | 81 | var o = @@ JS::fromNative(new JSFromNativeTest) @@; 82 | 83 | assertEqual(Object.getOwnPropertyNames(o).sort().toString(), "doSomething,foo,getFoo,setFoo,toString"); 84 | assertEqual(o.getFoo(), null); 85 | assertEqual(o.setFoo("bar"), o); 86 | assertEqual(o.getFoo(), "bar"); 87 | assertEqual(o.foo, "bar"); 88 | o.foo = "FOO!!!"; 89 | assertEqual(o.foo, "FOO!!!"); 90 | assertEqual(o.toString(), "hello, world!"); 91 | assertEqual(Object.prototype.toString.call(o), "[object JSFromNativeTest]"); 92 | assertEqual("" + o, "hello, world!"); 93 | 94 | ok = false; 95 | assertEqual(@@ JSFromNativeTest::$ok @@, false); 96 | o.doSomething(); 97 | assert(ok); 98 | 99 | o.newProperty = "new"; 100 | assertEqual(o.newProperty, undefined); 101 | assert(Object.isFrozen(o)); 102 | 103 | var f = @@ JS::fromNative(new JSFromNativeTestInvokable) @@; 104 | 105 | assertEqual(typeof f, "function"); 106 | assertEqual(f.__invoke, undefined); 107 | assertEqual(f("world"), "hello, world!"); 108 | assertEqual(Object.prototype.toString.call(f), "[object JSFromNativeTestInvokable]"); 109 | 110 | 111 | var o = @@ JS::fromNative(stream_context_create()) @@; 112 | assertEqual(o.toString(), "[object Native]"); 113 | 114 | o.newProperty = "new"; 115 | assertEqual(o.newProperty, undefined); 116 | assert(Object.isFrozen(o)); 117 | }); 118 | 119 | test("JS::toNative()", function () { 120 | var u = undefined, 121 | n = null, 122 | t = true, 123 | f = false, 124 | theAnswerToLifeTheUniverseAndEverything = 42, 125 | d = 5.8, 126 | s = "hello", 127 | a = [1, 2, 3], 128 | o = { foo: "bar" }; 129 | 130 | assert(@@ JS::toNative(`u) === NULL @@); 131 | assert(@@ JS::toNative(`n) === NULL @@); 132 | assert(@@ JS::toNative(`t) === TRUE @@); 133 | assert(@@ JS::toNative(`f) === FALSE @@); 134 | assert(@@ JS::toNative(`theAnswerToLifeTheUniverseAndEverything) === 42 @@); 135 | assert(@@ JS::toNative(`d) === 5.8 @@); 136 | assert(@@ JS::toNative(`s) === 'hello' @@); 137 | assert(@@ JS::toNative(`a) === array(1, 2, 3) @@); 138 | assert(@@ JS::toNative(`o) == ((object) array("foo" => "bar")) @@); // intentionally == 139 | 140 | @@ 141 | class JSToNativeTest {} 142 | @@ 143 | 144 | @@ $o = new JSToNativeTest; @@; 145 | var o = @@ JS::fromNative($o) @@; 146 | assert(@@ JS::toNative(`o) === $o @@); 147 | }); 148 | -------------------------------------------------------------------------------- /src/image/07_number.js: -------------------------------------------------------------------------------- 1 | function Number(value) { 2 | if (this === @@ $global @@) { 3 | return @@ JS::toNumber(`value, $global) @@; 4 | } 5 | 6 | var n = {}; 7 | @@ `n->prototype = `Number->properties['prototype']; @@ 8 | @@ `n->class = 'Number'; @@ 9 | @@ `n->value = JS::toNumber(`value, $global); @@ 10 | @@ `n->extensible = TRUE; @@ 11 | 12 | return n; 13 | } 14 | 15 | // FIXME 16 | Number.MAX_VALUE = @@ PHP_INT_MAX @@; 17 | Number.MIN_VALUE = @@ (int) PHP_INT_MAX + 1 @@; 18 | Number.NaN = NaN; 19 | Number.NEGATIVE_INFINITY = -Infinity; 20 | Number.POSITIVE_INFINITY = Infinity; 21 | 22 | Number.prototype = {}; 23 | @@ `Number->properties['prototype']->prototype = `Object->properties['prototype']; @@ 24 | @@ `Number->properties['prototype']->class = 'Number'; @@ 25 | @@ `Number->properties['prototype']->extensible = TRUE; @@ 26 | Number.prototype.constructor = Number; 27 | 28 | Number.prototype.toString = function (radix) { 29 | if (@@ isset($leThis->class) && $leThis->class !== 'Number' @@) { 30 | throw new TypeError("Number.prototype.toString(): not generic"); 31 | } 32 | 33 | if (isNaN(@@ $leThis->value @@)) { 34 | return "NaN"; 35 | } 36 | 37 | if (@@ $leThis->value === INF @@) { 38 | return "Infinity"; 39 | } 40 | 41 | if (@@ $leThis->value === -INF @@) { 42 | return "-Infinity"; 43 | } 44 | 45 | if (@@ is_float($leThis->value) @@) { 46 | return @@ (string) $leThis->value @@; 47 | } 48 | 49 | radix = radix || 10; 50 | 51 | if (radix < 2 || radix > 36) { 52 | throw new RangeError("Number.prototype.toString(): radix is not between 2 and 36."); 53 | } 54 | 55 | return @@ base_convert((string) $leThis->value, 10, `radix) @@; 56 | }; 57 | 58 | Number.prototype.toLocaleString = Number.prototype.toString; 59 | 60 | Number.prototype.valueOf = function () { 61 | return @@ $leThis->value @@; 62 | }; 63 | 64 | Number.prototype.toFixed = function (fractionDigits) { 65 | if (fractionDigits !== undefined && (fractionDigits < 0 || fractionDigits > 20)) { 66 | throw new RangeError("Number.prototype.toFixed(): argument must be between 0 and 20"); 67 | } 68 | 69 | if (@@ is_nan($leThis->value) @@) { 70 | return "NaN"; 71 | } 72 | 73 | fractionDigits = @@ JS::toNumber(`fractionDigits, $global) @@; 74 | 75 | if (isNaN(fractionDigits)) { 76 | return @@ (string) intval(round($leThis->value)) @@; 77 | } 78 | 79 | return @@ sprintf('%.' . `fractionDigits . 'f', $leThis->value) @@; 80 | 81 | return @@ sprintf('%f', $leThis->value) @@; 82 | }; 83 | 84 | Number.prototype.toExponential = function (fractionDigits) { 85 | if (fractionDigits !== undefined && (fractionDigits < 0 || fractionDigits > 20)) { 86 | throw new RangeError("Number.prototype.toExponential(): argument must be between 0 and 20"); 87 | } 88 | 89 | if (@@ is_nan($leThis->value) @@) { 90 | return "NaN"; 91 | } 92 | 93 | if (@@ $leThis->value === INF @@) { 94 | return "Infinity"; 95 | } 96 | 97 | if (@@ $leThis->value === -INF @@) { 98 | return "-Infinity"; 99 | } 100 | 101 | if (fractionDigits !== undefined) { 102 | var d = @@ JS::toNumber(`fractionDigits, $global) @@; 103 | 104 | if (isNaN(d)) { 105 | d = 0; 106 | } 107 | 108 | return @@ sprintf('%.' . `d . 'e', $leThis->value) @@; 109 | } 110 | 111 | return @@ preg_replace('/[.]?0+e/', 'e', sprintf('%e', $leThis->value)) @@; 112 | }; 113 | 114 | Number.prototype.toPrecision = function (precision) { 115 | if (precision === undefined) { 116 | return this.toString(); 117 | } 118 | 119 | if (precision !== undefined && (precision < 1 || precision > 21) || isNaN(precision)) { 120 | throw new RangeError("Number.prototype.toPrecision(): precision must be between 1 and 21"); 121 | } 122 | 123 | if (@@ is_nan($leThis->value) @@) { 124 | return "NaN"; 125 | } 126 | 127 | if (@@ $leThis->value === INF @@) { 128 | return "Infinity"; 129 | } 130 | 131 | if (@@ $leThis->value === -INF @@) { 132 | return "-Infinity"; 133 | } 134 | 135 | precision = @@ JS::toNumber(`precision, $global) @@; 136 | 137 | return @@ preg_replace('/[.]?0+e/', 'e', sprintf('%.' . JS::toString(`precision, $global) . 'g', 138 | round($leThis->value, `precision - strlen((string) intval($leThis->value))))) @@; 139 | }; 140 | -------------------------------------------------------------------------------- /util/jtest.d/main.js: -------------------------------------------------------------------------------- 1 | var assertionsCount, currentTest; 2 | 3 | function AssertionFailed(message) { 4 | var e = new Error(message); 5 | e.name = "AssertionFailed"; 6 | @@ `e->prototype = `AssertionFailed->properties["prototype"]; @@ 7 | return e; 8 | } 9 | 10 | @@ `AssertionFailed->properties["prototype"]->prototype = `Error->properties["prototype"]; @@ 11 | 12 | function assert(assertion, message) { 13 | if (!assertion) { 14 | throw new AssertionFailed(message); 15 | } 16 | 17 | ++assertionsCount; 18 | } 19 | 20 | function assertEqual(a, b, message) { 21 | if (a !== b) { 22 | var neq = "<" + a + "> !== <" + b + ">"; 23 | if (message) { 24 | throw new AssertionFailed(message + ", " + neq); 25 | } else { 26 | throw new AssertionFailed(neq); 27 | } 28 | } 29 | 30 | ++assertionsCount; 31 | } 32 | 33 | function assertNotEqual(a, b, message) { 34 | if (a === b) { 35 | var eq = "<" + a + "> === <" + b + ">"; 36 | if (message) { 37 | throw new AssertionFailed(message + ", " + eq); 38 | } else { 39 | throw new AssertionFailed(eq); 40 | } 41 | } 42 | 43 | ++assertionsCount; 44 | } 45 | 46 | function test(name, callback) { 47 | currentTest = name; 48 | assertionsCount = 1; 49 | callback(); 50 | } 51 | 52 | function puts() { 53 | var s = Array.prototype.join.call(arguments, ""); 54 | @@ echo `s, "\n"; @@ 55 | } 56 | 57 | function putc(c) { 58 | @@ echo `c; @@ 59 | } 60 | 61 | global.assert = assert; 62 | global.assertEqual = assertEqual; 63 | global.assertNotEqual = assertNotEqual; 64 | global.test = test; 65 | 66 | var args = @@ JS::fromNative($_SERVER['argv']) @@; 67 | 68 | if (args.length < 2 || args[1] === '-h' || args[1] === '--help') { 69 | puts("usage: jtest [ -h | --help ] "); 70 | @@ die(); @@ 71 | } 72 | 73 | var parse = PHP.cls("JSParser")(), compile = PHP.cls("JSCompiler")(), 74 | ast, code, call, 75 | files = PHP.fn("is_dir")(args[1]) 76 | ? [].concat(PHP.fn("glob")(args[1] + "/*.js"), PHP.fn("glob")(args[1] + "/*/*.js"), 77 | PHP.fn("glob")(args[1] + "/*/*/*.js"), PHP.fn("glob")(args[1] + "/*/*/*/*.js")) 78 | : [ args[1] ], 79 | failedCompile = 0, failedCompiled = 0, failedAssert = 0, passed = 0; 80 | 81 | try { 82 | files.forEach(function (f) { 83 | ast = parse(PHP.fn("file_get_contents")(f), { file: PHP.fn("realpath")(f) }); 84 | 85 | if (!ast[0]) { 86 | puts(">>>"); 87 | puts(">>> syntax error in " + f + "@" + ast[2].line + ":" + ast[2].column + 88 | ", expected " + ast[2].expected.join(", ")); 89 | puts(">>>\n"); 90 | 91 | ++failedCompile; 92 | return; 93 | } 94 | 95 | code = compile(ast[1]); 96 | call = @@ eval(`code) @@; 97 | 98 | if (call === false) { 99 | var tmpFile = PHP.fn("sys_get_temp_dir")() + "/js2php.last.php"; 100 | puts(">>>"); 101 | puts(">>> " + f + " compiled badly, dumping contents to " + tmpFile); 102 | puts(">>>\n"); 103 | 104 | PHP.fn("file_put_contents")(tmpFile, code); 105 | ++failedCompiled; 106 | return; 107 | } 108 | 109 | try { 110 | @@ 111 | $trace = $global->trace; 112 | $trace_sp = $global->trace_sp; 113 | `call($global); 114 | $global->trace = $trace; 115 | $global->trace_sp = $trace_sp; 116 | @@ 117 | ++passed; 118 | putc("."); 119 | 120 | } catch (e) { 121 | @@ $global->trace = $trace; @@ 122 | if (e instanceof AssertionFailed) { 123 | puts("\n>>>"); 124 | puts(">>> FILE: " + f + ", TEST: " + currentTest); 125 | puts(">>>"); 126 | puts(">>> assertion #" + assertionsCount + " failed: " + e.message); 127 | puts(">>>"); 128 | puts(">>>"); 129 | 130 | ++failedAssert; 131 | 132 | } else { 133 | throw e; 134 | } 135 | } 136 | }); 137 | 138 | } catch (e) { 139 | puts(); 140 | puts(e.name + ": " + e.message + " in " + e.file + "@" + e.line + ":" + e.column); 141 | var trace = @@ JS::fromNative($global->trace) @@; 142 | trace.reverse().forEach(function (t) { 143 | puts(" " + t[0] + (t[1] !== null ? "@" + t[1] + ":" + t[2] : "")); 144 | }); 145 | puts(); 146 | 147 | } finally { 148 | puts("\n>>>"); 149 | puts(">>> " + passed + " passed, " + failedCompile + " failed to compile, " + 150 | failedCompiled + " compiled code failed, " + failedAssert + " failed assert"); 151 | puts(">>>"); 152 | } 153 | -------------------------------------------------------------------------------- /test/twostroke/string.js: -------------------------------------------------------------------------------- 1 | test("toString", function() { 2 | assertEqual("foo", "foo".toString()); 3 | assertEqual("", "".toString()); 4 | }); 5 | 6 | test("toString is not generic", function() { 7 | try { 8 | String.prototype.toString.call(123); 9 | } catch(e) { 10 | if(e instanceof TypeError) { 11 | assert(true); 12 | return; 13 | } 14 | assert(false, "exception not TypeError"); 15 | } 16 | assert(false, "did not throw"); 17 | }); 18 | 19 | test("split", function() { 20 | assert("hello,world" == "hello world".split(" ")); 21 | assert("this,is,a test" == "this is a test".split(" ", 3)); 22 | assert("what" == "what".split("jjjj")); 23 | assert("w,h,a,t" == "what".split("")); 24 | assert("what" == "what".split()); 25 | }); 26 | 27 | test("slice", function() { 28 | assertEqual("hello", "hello".slice()); 29 | assertEqual("llo", "hello".slice(2)); 30 | assertEqual("el", "hello".slice(1,3)); 31 | assertEqual("ell", "hello".slice(1,-1)); 32 | }); 33 | 34 | test("match", function() { 35 | assert("fo,ob,ar" == "foobar".match(/../g)); 36 | assert("fo" == "foobar".match(/../)); 37 | 38 | assert("fo,ob,ar" == "foobar".match(/.(.)/g)); 39 | assert("fo,o" == "foobar".match(/.(.)/)); 40 | }); 41 | 42 | test("indexOf", function() { 43 | assertEqual(3, "foobar".indexOf("bar")); 44 | assertEqual(2, "bbaaaaa".indexOf("a")); 45 | }); 46 | 47 | test("lastIndexOf", function() { 48 | assertEqual(3, "foobar".lastIndexOf("bar")); 49 | assertEqual(6, "bbaaaaa".lastIndexOf("a")); 50 | }); 51 | 52 | // not implemented 53 | /* 54 | test("enumerable properties", function() { 55 | var i = 0; 56 | var s = "hello"; 57 | for(var x in s) { 58 | if(s.hasOwnProperty(x)) { 59 | i++; 60 | } 61 | } 62 | assertEqual(5, i); 63 | }); 64 | 65 | test("[]", function() { 66 | assertEqual("a", "abc"[0]); 67 | assertEqual("o", "zzo"[2]); 68 | assertEqual(undefined, "what"[-1]); 69 | assertEqual(undefined, "what"[4]); 70 | }); 71 | 72 | test("in", function() { 73 | assert(0 in "hi"); 74 | assert(1 in "hi"); 75 | assert(!(2 in "hi")); 76 | assert("length" in "hi"); 77 | assert(!("foo" in "hi")); 78 | }); 79 | 80 | */ 81 | 82 | test("charAt", function() { 83 | assertEqual("a", "abc".charAt(0)); 84 | assertEqual("o", "zzo".charAt(2)); 85 | assertEqual("", "what".charAt(-1)); 86 | assertEqual("", "what".charAt(4)); 87 | }); 88 | 89 | test("charCodeAt", function() { 90 | assertEqual(97, "a".charCodeAt(0)); 91 | assertEqual(111, "zzo".charCodeAt(2)); 92 | assert(isNaN("what".charCodeAt(-1))); 93 | assert(isNaN("what".charCodeAt(4))); 94 | }); 95 | 96 | test("toUpperCase", function() { 97 | assertEqual("FOO", "foo".toUpperCase()); 98 | assertEqual("FOO", "FoO".toUpperCase()); 99 | assertEqual("", "".toUpperCase()); 100 | }); 101 | 102 | test("toLowerCase", function() { 103 | assertEqual("foo", "FOO".toLowerCase()); 104 | assertEqual("foo", "FoO".toLowerCase()); 105 | assertEqual("", "".toLowerCase()); 106 | }); 107 | 108 | test("fromCharCode", function() { 109 | assertEqual("", String.fromCharCode()); 110 | assertEqual("a", String.fromCharCode(97)); 111 | assertEqual("abcd", String.fromCharCode(97,98,99,100)); 112 | }); 113 | 114 | test("primitive value", function() { 115 | assertEqual("foobar", String(new String("foobar"))); 116 | assertEqual("", String(new String(""))); 117 | }); 118 | 119 | test("escape codes", function() { 120 | assertEqual(-1, "\b".indexOf("b")); 121 | assertEqual(-1, "\n".indexOf("n")); 122 | assertEqual(-1, "\f".indexOf("f")); 123 | assertEqual(-1, "\v".indexOf("v")); 124 | assertEqual(-1, "\r".indexOf("r")); 125 | assertEqual(-1, "\t".indexOf("t")); 126 | assertEqual("hi", "h\ 127 | i"); 128 | 129 | // not implemented 130 | //assertEqual(" ", "\x20"); 131 | //assertEqual(" ", "\u0020"); 132 | //assertEqual(" ", "\40"); 133 | }); 134 | 135 | test("substring", function() { 136 | assertEqual("foobar", "foobar".substring()); 137 | assertEqual("foobar", "foobar".substring(0, 6)); 138 | assertEqual("ar", "foobar".substring(-2, 6)); 139 | assertEqual("ob", "foobar".substring(2, 4)); 140 | assertEqual("bar", "foobar".substring(3)); 141 | }); 142 | 143 | test("comparison", function() { 144 | assert("hello" > "a"); 145 | assert("a" < "b"); 146 | assert("a" <= "a"); 147 | assert("A" < "a"); 148 | }); 149 | -------------------------------------------------------------------------------- /src/JSDumper.phptw: -------------------------------------------------------------------------------- 1 | -invoke(ast) -> WALK($ast) 2 | 3 | program(ast) -> WALK($ast) 4 | 5 | // STATEMENTS 6 | 7 | block(statements) -> implode("", WALKEACH($statements)) 8 | 9 | php_statement(parts) -> "@@" . implode("", WALKEACH($parts)) . "@@" 10 | 11 | function(name, parameters_list, body) -> "function" . ($name !== NULL ? " " : "") . "$name(" . implode(",", $parameters_list) . "){" . WALK($body) . "}" 12 | 13 | var(declarations_list) { 14 | $ret = array(); 15 | 16 | foreach ($declarations_list as $declaration) { 17 | list($varname, $expr) = $declaration; 18 | $ret[] = "$varname" . ($expr ? "=" . WALK($expr) : ""); 19 | } 20 | 21 | return "var " . implode(",", $ret) . ";"; 22 | } 23 | 24 | if(cond_expr, statement, else_statement) { 25 | return "if(" . WALK($cond_expr) . "){" . WALK($statement) . "}" . 26 | ($else_statement ? "else{" . WALK($else_statement) . "}" : ""); 27 | } 28 | 29 | do(cond_expr, statement) -> "do{" . WALK($statement) . "}while(" . WALK($cond_expr) . ");"; 30 | 31 | while(cond_expr, statement) -> "while(" . WALK($cond_expr) . "){" . WALK($statement) . "}" 32 | 33 | for_in(assignment_expr, in_expr, statement) -> "for(" . rtrim(WALK($assignment_expr), ";") . " in " . WALK($in_expr) . "){" . WALK($statement) . "}" 34 | 35 | for(init_expr, cond_expr, iter_expr, statement) { 36 | $init = $cond = $iter = ''; 37 | 38 | if ($init_expr) { $init = rtrim(WALK($init_expr), ";"); } 39 | if ($cond_expr) { $cond = WALK($cond_expr); } 40 | if ($iter_expr) { $iter = WALK($iter_expr); } 41 | 42 | return "for($init;$cond;$iter){" . WALK($statement) . "}"; 43 | } 44 | 45 | continue(label) -> "continue $label;" 46 | 47 | break(label) -> "break $label;" 48 | 49 | return(expr) -> "return " . WALK($expr) . ";" 50 | 51 | with(expr, statement) -> "with(" . WALK($expr) . "){" . WALK($statement) . "}" 52 | 53 | labelled(label, statement) -> "$label:" . WALK($statement) 54 | 55 | switch(expr, clauses_list) -> "switch(" . WALK($expr) . "){" . implode("", WALKEACH($clauses_list)) . "}" 56 | 57 | case(expr, statement) -> "case " . WALK($expr) . ":" . WALK($statement) 58 | 59 | default(statement) -> "default:" . WALK($statement) 60 | 61 | throw(expr) -> "throw " . WALK($expr) . ";" 62 | 63 | try(try_block, catch_var, catch_block, finally_block) { 64 | return "try{" . WALK($try_block) . "}" . 65 | ($catch_block ? "catch($catch_var){" . WALK($catch_block) . "}" : "") . 66 | ($finally_block ? "finally{" . WALK($finally_block) . "}": ""); 67 | } 68 | 69 | discard(expr) -> rtrim(WALK($expr), ";") . ";" 70 | 71 | 72 | // EXPRESSIONS 73 | 74 | exprs(exprs) -> implode(",", WALKEACH($exprs)) 75 | 76 | assignment(op, lhs_expr, rhs_expr) -> WALK($lhs_expr) . $op . WALK($rhs_expr) 77 | 78 | cond(cond_expr, iftrue_expr, iffalse_expr) -> WALK($cond_expr) . "?" . WALK($iftrue_expr) . ":" . WALK($iffalse_expr) 79 | 80 | binary(op, left_expr, right_expr) -> "(" . WALK($left_expr) . ")$op(" . WALK($right_expr) . ")" 81 | 82 | delete(expr) -> "delete " . WALK($expr) 83 | 84 | void(expr) -> "void " . WALK($expr) 85 | 86 | typeof(expr) -> "typeof " . WALK($expr) 87 | 88 | preinc(expr) -> "++" . WALK($expr) 89 | 90 | predec(expr) -> "--" . WALK($expr) 91 | 92 | positive(expr) -> "+" . WALK($expr) 93 | 94 | negative(expr) -> "-" . WALK($expr) 95 | 96 | inverse(expr) -> "~" . WALK($expr) 97 | 98 | not(expr) -> "!" . WALK($expr) 99 | 100 | postinc(expr) -> WALK($expr) . "++" 101 | 102 | postdec(expr) -> WALK($expr) . "--" 103 | 104 | call(fn_expr, arguments) -> WALK($fn_expr) . "(" . implode(",", WALKEACH($arguments)) . ")" 105 | 106 | index(base, index_expr) -> WALK($base) . (($index_expr[0] === "string" && preg_match("/^[a-zA-Z_$][a-zA-Z0-9_$]*$/", $index_expr[1])) ? "." . $index_expr[1] : "[" . WALK($index_expr) . "]") 107 | 108 | new(expr, arguments_exprs_list) -> "new " . WALK($expr) . "(" . implode(",", WALKEACH($arguments_exprs_list)) . ")" 109 | 110 | this() -> "this" 111 | 112 | null() -> "null" 113 | 114 | true() -> "true" 115 | 116 | false() -> "false" 117 | 118 | undefined() -> "undefined" 119 | 120 | number(n) -> json_encode($n) 121 | 122 | string(s) -> str_replace("\\/", "/", json_encode($s)) 123 | 124 | regexp(regexp, flags) -> "/$regexp/$flags" 125 | 126 | identifier(identifier) -> $identifier 127 | 128 | array(elements_list) -> "[" . implode(",", WALKEACH($elements_list)) . "]" 129 | 130 | object(properties_list) { 131 | $ret = array(); 132 | 133 | foreach ($properties_list as $property) { 134 | list($name, $value) = $property; 135 | $ret[] = WALK(array("id_", $name)) . ":" . WALK($value); 136 | } 137 | 138 | return "{" . implode(",", $ret) . "}"; 139 | } 140 | 141 | id_(name) -> preg_match("/^[a-zA-Z_$][a-zA-Z0-9_$]*$|^\d+$/", $name) ? $name : WALK(array("string", $name)); 142 | 143 | php(parts) -> "@@" . implode("", WALKEACH($parts)) . "@@" 144 | 145 | raw(code) -> trim($code) 146 | -------------------------------------------------------------------------------- /test/builtin/string.js: -------------------------------------------------------------------------------- 1 | test("String()", function () { 2 | assert(String(undefined) === "undefined", "String(): 1"); 3 | assert(String(null) === "null", "String(): 2"); 4 | assert(String(true) === "true", "String(): 3"); 5 | assert(String(false) === "false", "String(): 4"); 6 | assert(String(5) === "5", "String(): 5"); 7 | assert(String(NaN) === "NaN", "String(): 6"); 8 | assert(String("foo") === "foo", "String(): 7"); 9 | assert(new String() instanceof String, "String(): 8"); 10 | }); 11 | 12 | test("String.fromCharCode()", function () { 13 | assert(String.fromCharCode(0x61, 0x62, 0x63) === "abc", "String.fromCharCode(): 1"); 14 | }); 15 | 16 | test("String.prototype.toString()", function () { 17 | assert("foo".toString() === "foo", "String.prototype.toString(): 1"); 18 | }); 19 | 20 | test("String.prototype.valueOf()", function () { 21 | assert("foo".valueOf() === "foo", "String.prototype.valueOf(): 1"); 22 | }); 23 | 24 | test("String.prototypeo.charAt()", function () { 25 | assert("abc".charAt(0) === "a", "String.prototype.charAt(): 1"); 26 | assert("abc".charAt(1) === "b", "String.prototype.charAt(): 2"); 27 | assert("abc".charAt(2) === "c", "String.prototype.charAt(): 3"); 28 | }); 29 | 30 | test("String.prototype.charCodeAt()", function() { 31 | assert("abc".charCodeAt(0) === 0x61, "String.prototype.charCodeAt(): 1"); 32 | assert("abc".charCodeAt(1) === 0x62, "String.prototype.charCodeAt(): 2"); 33 | assert("abc".charCodeAt(2) === 0x63, "String.prototype.charCodeAt(): 3"); 34 | }); 35 | 36 | test("String.prototype.concat()", function () { 37 | assert("abc".concat("def") === "abcdef", "String.prototype.concat(): 1"); 38 | assert("abc".concat("def", "ghi") === "abcdefghi", "String.prototype.concat(): 2"); 39 | }); 40 | 41 | test("String.prototype.indexOf()", function () { 42 | assert("abc".indexOf("b") === 1, "String.prototype.indexOf(): 1"); 43 | assert("abc".indexOf("d") === -1, "String.prototype.indexOf(): 2"); 44 | assert("abc".indexOf("a", 1) === -1, "String.prototype.indexOf(): 3"); 45 | assert("abc".indexOf("a", -2) === 0, "String.prototype.indexOf(): 4"); 46 | }); 47 | 48 | test("String.prototype.lastIndexOf()", function () { 49 | assert("abc".lastIndexOf("b") === 1, "String.prototype.lastIndexOf(): 1"); 50 | assert("abc".lastIndexOf("d") === -1, "String.prototype.lastIndexOf(): 2"); 51 | assert("abc".lastIndexOf("a", 1) === -1, "String.prototype.lastIndexOf(): 3"); 52 | assert("abc".lastIndexOf("a", -2) === 0, "String.prototype.lastIndexOf(): 4"); 53 | assert("aba".lastIndexOf("a") === 2, "String.prototype.lastIndexOf(): 5"); 54 | assert("aba".lastIndexOf("a", 1) === 2, "String.prototype.lastIndexOf(): 6"); 55 | }); 56 | 57 | test("String.prototype.match()", function () { 58 | assert("abcd".match(/bc/).index === 1, "String.prototype.match(): 1"); 59 | assert("abcd".match(/ef/) === null, "String.prototype.match(): 2"); 60 | assert("abab".match(/ab/g).length === 2, "String.prototype.match(): 3"); 61 | }); 62 | 63 | test("String.prototype.replace()", function () { 64 | assert("abab".replace("a", "b") === "bbab", "String.prototype.replace(): 1"); 65 | assert("abab".replace(/a/, "b") === "bbab", "String.prototype.replace(): 2"); 66 | assert("abab".replace(/(a)b/, "$1") === "aab", "String.prototype.replace(): 3"); 67 | assert("abab".replace(/(a)b/, "$$$1") === "$aab", "String.prototype.replace(): 4"); 68 | assert("abab".replace(/(a)(b)/, function (_, a, b) { return b + a; }) === "baab", 69 | "String.prototype.replace(): 5"); 70 | assert("abab".replace(/a/g, "b") === "bbbb", "String.prototype.replace(): 6"); 71 | assert("abab".replace(/(a)b/g, "$1") === "aa", "String.prototype.replace(): 7"); 72 | assert("abab".replace(/(a)b/g, "$$$1") === "$a$a", "String.prototype.replace(): 8"); 73 | assert("abab".replace(/(a)(b)/g, function (_, a, b) { return b + a; }) === "baba", 74 | "String.prototype.replace(): 9"); 75 | }); 76 | 77 | test("String.prototype.search()", function () { 78 | assert("abcd".search("a") === 0, "String.prototype.search(): 1"); 79 | assert("abcd".search("e") === -1, "String.prototype.search(): 2"); 80 | assert("abcd".search(/a/) === 0, "String.prototype.search(): 3"); 81 | assert("abcd".search(/e/) === -1, "String.prototype.search(): 4"); 82 | }); 83 | 84 | test("String.prototype.slice()", function () { 85 | assert("abcd".slice(1, 3) === "bc", "String.prototype.slice(): 1"); 86 | assert("abcd".slice(-3, -1) === "bc", "String.prototype.slice(): 2"); 87 | assert("abcd".slice(-2) === "cd", "String.prototype.slice(): 3"); 88 | }); 89 | 90 | test("String.prototype.split()", function () { 91 | var a = "abcd".split(); 92 | assert(a.length === 1 && a[0] === "abcd", "String.prototype.split(): 1"); 93 | a = "abcd".split(""); 94 | assert(a.length === 4 && a[0] === "a" && a[1] === "b" && a[2] === "c" && a[3] === "d", 95 | "String.prototype.split(): 2"); 96 | a = "a,b,c,d".split(","); 97 | assert(a.length === 4 && a[0] === "a" && a[1] === "b" && a[2] === "c" && a[3] === "d", 98 | "String.prototype.split(): 3"); 99 | a = "a,b,c,d".split(/,/); 100 | assert(a.length === 4 && a[0] === "a" && a[1] === "b" && a[2] === "c" && a[3] === "d", 101 | "String.prototype.split(): 4"); 102 | a = "ab".split(/a*?/); 103 | assert(a.length === 2 && a[0] === "a" && a[1] === "b", "String.prototype.split(): 5"); 104 | a = "ab".split(/a*/); 105 | assert(a.length === 2 && a[0] === "" && a[1] === "b", "String.prototype.split(): 6"); 106 | a = "ace".split(/<(\/)?([^<>]+)>/); 107 | assert(a.length === 7 && a[0] === "a" && a[1] === undefined && a[2] === "b" && a[3] === "c" && 108 | a[4] === "/" && a[5] === "b" && a[6] === "e", 109 | "String.prototype.split(): 7"); 110 | }); 111 | 112 | test("String.prototype.substring()", function () { 113 | assert("abcd".substring(1, 3) === "bc", "String.prototype.substring(): 1"); 114 | assert("abcd".substring(-3, -1) === "bc", "String.prototype.substring(): 2"); 115 | assert("abcd".substring(0) === "abcd", "String.prototype.substring(): 3"); 116 | assert("abcd".substring(-1) === "d", "String.prototype.substring(): 4"); 117 | }); 118 | 119 | test("String.prototype.substr()", function () { 120 | assert("abcd".substr(1, 2) === "bc", "String.prototype.substr(): 1"); 121 | assert("abcd".substr(-3, 2) === "bc", "String.prototype.substr(): 2"); 122 | assert("abcd".substr(0) === "abcd", "String.prototype.substr(): 3"); 123 | assert("abcd".substr(-1) === "d", "String.prototype.substr(): 4"); 124 | }); 125 | 126 | 127 | test("String.prototype.toLowerCase()", function () { 128 | assert("ABCD".toLowerCase() === "abcd", "String.prototype.toLowerCase(): 1"); 129 | }); 130 | 131 | test("String.prototype.toUpperCase()", function () { 132 | assert("abcd".toUpperCase() === "ABCD", "String.prototype.toUpperCase(): 1"); 133 | }); 134 | 135 | test("String.prototype.trim()", function () { 136 | assert(" abc ".trim() === "abc", "String.prototype.trim(): 1"); 137 | }); 138 | -------------------------------------------------------------------------------- /src/image/15_require.js: -------------------------------------------------------------------------------- 1 | function require(path) { 2 | if (require.modules[path] !== undefined) { 3 | return require.modules[path].exports; 4 | } 5 | 6 | path = @@ JS::toString(`path, $global) @@; 7 | 8 | if (@@ (strncmp(`path, './', 2) === 0 || strncmp(`path, '../', 3) === 0) @@) { 9 | path = require["."] + "/" + path; 10 | 11 | } else if (path.charAt(0) !== "/") { 12 | var ok = false; 13 | 14 | for (var dir = require["."], root = false; dir !== "/" || !root; dir = @@ dirname(`dir) @@) { 15 | if (dir === "/") { root = true; } 16 | 17 | if (@@ file_exists(`dir . '/jeph_modules/' . `path) @@) { 18 | ok = true; 19 | path = dir + "/jeph_modules/" + path; 20 | break; 21 | 22 | } else if (@@ file_exists(`dir . '/node_modules/' . `path) @@) { 23 | ok = true; 24 | path = dir + "/node_modules/" + path; 25 | break; 26 | 27 | } else { 28 | for (var ext in require.extensions) { 29 | if (@@ file_exists(`dir . '/jeph_modules/' . `path . `ext) @@) { 30 | ok = true; 31 | path = dir + "/jeph_modules/" + path + ext; 32 | break; 33 | 34 | } else if (@@ file_exists(`dir . '/node_modules/' . `path . `ext) @@) { 35 | ok = true; 36 | path = dir + "/node_modules/" + path + ext; 37 | break; 38 | } 39 | } 40 | 41 | if (ok) { break; } 42 | } 43 | } 44 | } 45 | 46 | if (!@@ (file_exists(`path)) @@) { 47 | var ok = false; 48 | 49 | for (var ext in require.extensions) { 50 | if (@@ file_exists(`path . `ext) @@) { 51 | ok = true; 52 | path += ext; 53 | break; 54 | } 55 | } 56 | 57 | if (!ok) { 58 | throw new Error("File " + path + " does not exist"); 59 | } 60 | } 61 | 62 | if (!@@ (is_file(`path)) @@) { 63 | if (@@ file_exists(`path . '/package.json') @@) { 64 | var data = @@ @file_get_contents(`path . '/package.json') @@; 65 | 66 | if (data === false) { 67 | throw new Error("Cannot open file " + path + "/package.json"); 68 | } 69 | var pkg = JSON.parse(data) 70 | 71 | if (pkg.main === undefined) { 72 | throw new Error("Package " + path + 73 | "/package.json does not export entry point"); 74 | } 75 | 76 | if (pkg.main.indexOf("../") !== -1 || pkg.main.indexOf("/..") !== -1) { 77 | throw new Error("Package " + path + 78 | "/package.json's main tries to go out of package directory"); 79 | } 80 | 81 | path = path + "/" + pkg.main; 82 | 83 | } else { 84 | var ok = false; 85 | for (var ext in require.extensions) { 86 | if (@@ file_exists(`path . '/index' . `ext) @@) { 87 | ok = true; 88 | path += "/index" + ext; 89 | break; 90 | } 91 | } 92 | 93 | if (!ok) { 94 | throw new Error(path + 95 | " cannot be loaded, no index"); 96 | } 97 | } 98 | } 99 | 100 | path = @@ realpath(`path) @@; 101 | 102 | if (require.modules[path] !== undefined) { 103 | return require.modules[path].exports; 104 | } 105 | 106 | require.modules[path] = { exports: {} }; 107 | 108 | return require.modules[path].exports = 109 | require.extensions[path.substring(path.lastIndexOf("."))](path); 110 | } 111 | 112 | require.saveCompiled = false; 113 | require.compiled = {}; 114 | require.compiledCache = undefined; 115 | require.loader = undefined; 116 | require.modules = {}; 117 | 118 | require.extensions = { 119 | ".js": function (path) { 120 | var compiled, 121 | cacheFile = require.compiledCache 122 | ? require.compiledCache + "/" + @@ md5(`path) @@ 123 | : undefined; 124 | 125 | if (cacheFile !== undefined && @@ (file_exists(`cacheFile) && 126 | filemtime(`cacheFile) >= filemtime(`path)) @@) 127 | { 128 | var data = @@ @file_get_contents(`cacheFile) @@; 129 | 130 | if (data === false) { 131 | throw new Error("Cannot open file " + path); 132 | } 133 | 134 | compiled = @@ unserialize(`data) @@; 135 | 136 | } else { 137 | @@ 138 | $parser = new JSParser; 139 | $compiler = new JSCompiler; 140 | $data = @file_get_contents(`path); 141 | 142 | if ($data === FALSE) { @@ 143 | throw new Error("Cannot open file " + path); 144 | @@ } 145 | 146 | list($ok, $ast, $error) = $parser->__invoke($data, array('file' => `path)); 147 | 148 | if (!$ok) { @@ 149 | throw new SyntaxError("Syntax error in " + path + "@" + 150 | @@ $error->line @@ + ":" + @@ $error->column @@ + "; expected " + 151 | @@ implode(', ', $error->expected) @@); 152 | @@ } @@ 153 | 154 | var loader = require.loader; 155 | compiled = @@ JS::fromNative($compiler->__invoke($ast, array( 156 | 'force' => TRUE, 157 | 'generate' => 'object', 158 | 'loader' => `loader !== JS::$undefined ? `loader : NULL 159 | ))) @@; @@ 160 | 161 | @file_put_contents(`cacheFile, serialize(JS::toNative(`compiled))); 162 | @@ 163 | } 164 | 165 | if (require.saveCompiled) { 166 | require.compiled[path] = compiled; 167 | } 168 | 169 | var code = "", 170 | main = compiled.main, 171 | savedCurrentDirectory; 172 | 173 | for (var k in compiled.functions) { 174 | code += compiled.functions[k]; 175 | } 176 | 177 | try { 178 | savedCurrentDirectory = require["."]; 179 | var moduleRequire = (function (__dirname) { 180 | return function (path) { 181 | var savedCurrentDirectory = require["."], ret; 182 | try { 183 | @@ `require->properties['.'] = `__dirname; @@ 184 | ret = require(path); 185 | } finally { 186 | @@ `require->properties['.'] = `savedCurrentDirectory; @@ 187 | } 188 | 189 | return ret; 190 | }; 191 | })(@@ dirname(`path) @@); 192 | 193 | @@ 194 | $newScope = clone JS::$emptyScope; 195 | $newScope->properties['global'] = $global; 196 | $newScope->properties['__dirname'] = dirname(`path); 197 | $newScope->properties['__filename'] = `path; 198 | $newScope->properties['module'] = clone JS::$objectTemplate; 199 | $newScope->properties['module']->properties['exports'] = clone JS::$objectTemplate; 200 | $newScope->properties['module']->attributes['exports'] = JS::WRITABLE | JS::CONFIGURABLE; 201 | $newScope->properties['module']->properties['require'] = `moduleRequire; 202 | $newScope->properties['module']->attributes['require'] = 0; 203 | $newScope->properties['module']->up = $global; 204 | $newScope->up = $newScope->properties['module']; 205 | @@ 206 | require.modules[path] = @@ $newScope->properties['module'] @@; 207 | @@ 208 | $savedTrace = $global->trace; 209 | $savedTraceSP = $global->trace_sp; 210 | eval(`code); `main($global, $newScope); 211 | @@ 212 | 213 | } finally { 214 | @@ 215 | $global->trace = $savedTrace; 216 | $global->trace_sp = $savedTraceSP; 217 | $global->properties['require']->properties['.'] = `savedCurrentDirectory; 218 | @@ 219 | } 220 | 221 | return require.modules[path].exports; 222 | }, 223 | 224 | ".json": function (path) { 225 | var data = @@ @file_get_contents(`path) @@; 226 | 227 | if (data === false) { 228 | throw new Error("Could not read file " + path); 229 | } 230 | 231 | return JSON.parse(data); 232 | } 233 | }; 234 | -------------------------------------------------------------------------------- /test/twostroke/misc_syntax.js: -------------------------------------------------------------------------------- 1 | test("do while", function() { 2 | do { 3 | assert(true); 4 | return; 5 | } while(false); 6 | assert(false, "did not execute"); 7 | }); 8 | 9 | test("do while and continue", function() { 10 | var flag = false; 11 | do { 12 | if(flag) { 13 | assert(false, "loop executed again after continue in do..while(false)"); 14 | } 15 | flag = true; 16 | continue; 17 | } while(false); 18 | assert(true); 19 | }); 20 | 21 | test("with", function() { 22 | var b = 0; 23 | var x = { a: 1 }; 24 | with(x) { 25 | assertEqual("number", typeof a); 26 | assertEqual(1, a); 27 | var y = 2; 28 | b = 3; 29 | a = 4; 30 | } 31 | assertEqual(4, x.a); 32 | assertEqual(2, y); 33 | assertEqual("undefined", typeof x.b); 34 | assertEqual(3, b); 35 | }); 36 | 37 | test("default in switch", function() { 38 | switch(1) { 39 | case 1: assert(true); break; 40 | default: assert(false); break; 41 | } 42 | 43 | switch(2) { 44 | case 1: assert(false); break; 45 | default: assert(true); break; 46 | } 47 | 48 | switch(1) { 49 | default: assert(false); break; 50 | case 1: assert(true); break; 51 | } 52 | 53 | switch(2) { 54 | default: assert(true); break; 55 | case 1: assert(false); break; 56 | } 57 | }); 58 | 59 | test("combo-assignment-arithmetic on member", function() { 60 | var x = { a: 1 }; 61 | assertEqual(3, x.a += 2); 62 | assertEqual(3, x.a); 63 | }); 64 | 65 | test("combo-assignment-arithmetic on index", function() { 66 | var x = { a: 1 }; 67 | assertEqual(3, x["a"] += 2); 68 | assertEqual(3, x["a"]); 69 | }); 70 | 71 | test("assignment non-lval throws", function() { 72 | try { 73 | eval("hi() = 123"); 74 | assert(false, "didn't throw"); 75 | } catch(e) { 76 | assert(e instanceof ReferenceError); 77 | } 78 | }); 79 | 80 | test("combo-assignment-arithmetic on non-lval throws", function() { 81 | try { 82 | eval("hi() += 123"); 83 | assert(false, "didn't throw"); 84 | } catch(e) { 85 | assert(e instanceof ReferenceError); 86 | } 87 | }); 88 | 89 | test("post-increment", function() { 90 | var i = 0; 91 | var a = [1]; 92 | var o = { x: 2 }; 93 | assertEqual(0, i++); 94 | assertEqual(1, a[0]++); 95 | assertEqual(2, o.x++); 96 | assertEqual(1, i); 97 | assertEqual(2, a[0]); 98 | assertEqual(3, o.x); 99 | }); 100 | 101 | test("pre-increment", function() { 102 | var i = 0; 103 | var a = [1]; 104 | var o = { x: 2 }; 105 | assertEqual(1, ++i); 106 | assertEqual(2, ++a[0]); 107 | assertEqual(3, ++o.x); 108 | assertEqual(1, i); 109 | assertEqual(2, a[0]); 110 | assertEqual(3, o.x); 111 | }); 112 | 113 | test("post/pre increment on non-lval throws", function() { 114 | try { 115 | eval("hi()++;"); 116 | assert(false, "didn't throw"); 117 | } catch(e) { 118 | assert(e instanceof ReferenceError); 119 | } 120 | try { 121 | eval("++hi();"); 122 | assert(false, "didn't throw"); 123 | } catch(e) { 124 | assert(e instanceof ReferenceError); 125 | } 126 | }); 127 | 128 | test("calling", function() { 129 | var x = { a: function() { return this; } }; 130 | assertEqual(x, x["a"]()); 131 | assertEqual(x, x.a()); 132 | assertEqual(123, Number(x.a.call(123))); 133 | function closure() { 134 | var fn = x.a; 135 | assertEqual(this, fn()); 136 | } 137 | closure(); 138 | }); 139 | 140 | test("delete", function() { 141 | global.x = 123; 142 | var y = { a:1, b:2, c:3 }; 143 | var z = [1,2,3,4]; 144 | 145 | assertEqual("number", typeof x); 146 | delete x; 147 | assertEqual("undefined", typeof x); 148 | 149 | assertEqual("number", typeof y.b); 150 | delete y.b; 151 | assertEqual("undefined", typeof y.b); 152 | 153 | assertEqual("number", typeof y.c); 154 | with(y) delete c; 155 | assertEqual("undefined", typeof y.c); 156 | 157 | assert("1,2,3,4" == z); 158 | delete z[2]; 159 | assert("1,2,,4" == z); 160 | 161 | var fncalled = false; 162 | function fn() { fncalled = true; } 163 | assertEqual(true, delete fn()); 164 | assertEqual("function", typeof fn); 165 | assertEqual("undefined", typeof fn()); 166 | assertEqual(true, fncalled); 167 | 168 | function Fn() { } 169 | Fn.prototype.hi = 123; 170 | assertEqual(false, delete Fn.prototype); 171 | assertEqual(123, Fn.prototype.hi); 172 | }); 173 | 174 | // not implemented 175 | /* 176 | test("break label", function() { 177 | br: while(true) { 178 | while(true) { 179 | break br; 180 | assert(false, "did not break"); 181 | } 182 | assert(false, "did not escape second loop"); 183 | } 184 | assert(true); 185 | }); 186 | 187 | test("can't continue to non existant label", function() { 188 | try { 189 | eval('while(false) continue foo;'); 190 | } catch(e) { 191 | assert(e instanceof SyntaxError); 192 | assert(/foo/.test(e.toString())); 193 | } 194 | }); 195 | 196 | test("labelled continue while", function() { 197 | var x = false; 198 | a:while(true) { 199 | if(x) { 200 | assert(true); 201 | return; 202 | } 203 | while(true) { 204 | x = true; 205 | continue a; 206 | assert(false, "did not continue"); 207 | } 208 | } 209 | }); 210 | 211 | test("labelled continue do-while", function() { 212 | a:do { 213 | do { 214 | continue a; 215 | assert(false); 216 | } while(false); 217 | assert(false); 218 | } while(false); 219 | assert(true); 220 | }); 221 | 222 | test("labelled continue for-loop", function() { 223 | var x = false; 224 | function init() { 225 | if(x) { 226 | assert(false, "reran initializer"); 227 | } 228 | x = true; 229 | } 230 | var i = 0; 231 | a: for(init(); i < 10; i++) { 232 | assert(true); 233 | do { 234 | continue a; 235 | assert(false); 236 | } while(false); 237 | assert(false); 238 | } 239 | assert(true); 240 | }); 241 | 242 | test("labelled continue for-in", function() { 243 | var a = 0, c = [1, 2, 3, 4, 5]; 244 | l: for(var x in c) { 245 | if(c.hasOwnProperty(x)) { 246 | do { 247 | a += c[x]; 248 | continue l; 249 | assert(false); 250 | } while(false); 251 | assert(false); 252 | } 253 | } 254 | assertEqual(15, a); 255 | }); 256 | 257 | test("labelled block", function() { 258 | eval("a: { };"); 259 | assert(true); 260 | }); 261 | */ 262 | 263 | test("floating point is used for large numbers but not for small numbers", function() { 264 | assertEqual(99999999999999999999999999999999999999, 99999999999999999999999999999999999999 + 1); 265 | assertNotEqual(999999, 999999 + 1); 266 | }); 267 | 268 | test("redeclaring a variable", function() { 269 | assertEqual(123, (function(a) { 270 | var a; 271 | return a; 272 | })(123)); 273 | }); 274 | -------------------------------------------------------------------------------- /test/builtin/array.js: -------------------------------------------------------------------------------- 1 | test("Array()", function () { 2 | var a = Array(3); 3 | assert(a.length === 3, "Array(): wrong length"); 4 | assert(a[0] === undefined && a[1] === undefined && a[2] === undefined, "Array(): wrong values"); 5 | try { 6 | Array(-5); 7 | assert(false, "Array(): should throw when negative length"); 8 | } catch (e) { 9 | assert(e instanceof RangeError, "Array(): should throw RangeError"); 10 | } 11 | 12 | 13 | var a = Array(1, 2, 3); 14 | assert(a.length === 3, "Array(): wrong length 2"); 15 | assert(a[0] === 1 && a[1] === 2 && a[2] === 3, "Array(): wrong values 2"); 16 | 17 | 18 | var a = new Array(1, 2, 3); 19 | assert(a.length === 3, "Array(): wrong length 2"); 20 | assert(a[0] === 1 && a[1] === 2 && a[2] === 3, "Array(): wrong values 2"); 21 | }); 22 | 23 | test("Array.isArray()", function () { 24 | assert(Array.isArray([]), "Array.isArray(): 1"); 25 | assert(!Array.isArray(undefined), "Array.isArray(): 2"); 26 | assert(!Array.isArray(null), "Array.isArray(): 3"); 27 | assert(!Array.isArray(true), "Array.isArray(): 4"); 28 | assert(!Array.isArray(42), "Array.isArray(): 5"); 29 | assert(!Array.isArray("hello, world!"), "Array.isArray(): 6"); 30 | assert(!Array.isArray({}), "Array.isArray(): 7"); 31 | }); 32 | 33 | test("Array.prototype.toString()", function () { 34 | assert([1, 2, 3].toString() === "1,2,3", "Array.prototype.toString(): 1"); 35 | assert([].toString() === "", "Array.prototype.toString(): 2"); 36 | assert([1, 2, 3, undefined].toString() === "1,2,3,", "Array.prototype.toString(): 3"); 37 | assert([1, 2, 3, [4]].toString() === "1,2,3,4", "Array.prototype.toString(): 4"); 38 | }); 39 | 40 | test("Array.prototype.concat()", function () { 41 | var a = []; 42 | assert(a.length === 0, "Array.prototype.concat(): 0"); 43 | a = a.concat(1); 44 | assert(a.length === 1 && a[0] === 1, "Array.prototype.concat(): 1"); 45 | a = a.concat([2]); 46 | assert(a.length === 2 && a[1] === 2, "Array.prototype.concat(): 2"); 47 | a = a.concat(3, 4); 48 | assert(a.length === 4 && a[2] === 3 && a[3] === 4, "Array.prototoype.concat(): 3"); 49 | a = a.concat([5], [6], [7]); 50 | assert(a.length === 7 && a[4] === 5 && a[5] === 6 && a[6] === 7, "Array.prototype.concat(): 4"); 51 | }); 52 | 53 | test("Array.prototype.join()", function () { 54 | assert([1, 2, 3, 4, 5].join() === "1,2,3,4,5", "Array.prototype.join(): 1"); 55 | assert([1, 2, 3, 4, 5].join(" !! ") === "1 !! 2 !! 3 !! 4 !! 5", "Array.prototype.join(): 2"); 56 | }); 57 | 58 | test("Array.prototype.pop()", function () { 59 | var a = [1, 2]; 60 | assert(a.pop() === 2, "Array.prototype.pop(): 1"); 61 | assert(a.pop() === 1, "Array.prototype.pop(): 2"); 62 | assert(a.pop() === undefined, "Array.prototype.pop(): 3"); 63 | assert(a.length === 0, "Array.prototype.pop(): 4"); 64 | }); 65 | 66 | test("Array.prototype.push()", function () { 67 | var a = []; 68 | a.push(1); 69 | assert(a.length === 1 && a[0] === 1, "Array.prototype.push(): 1"); 70 | a.push(2, 3); 71 | assert(a.length === 3 && a[1] === 2 && a[2] === 3, "Array.prototype.push(): 2"); 72 | a.push(undefined); 73 | assert(a.length === 4 && a[3] === undefined, "Array.prototype.push(): 3"); 74 | }); 75 | 76 | test("Array.prototype.reverse()", function () { 77 | var a = [1, 2, 3].reverse(); 78 | assert(a.length === 3 && a[0] === 3 && a[1] === 2 && a[2] === 1, "Array.prototype.reverse(): 1"); 79 | }); 80 | 81 | test("Array.prototype.unshift()", function () { 82 | var a = [1, 2]; 83 | assert(a.shift() === 1, "Array.prototype.shift(): 1"); 84 | assert(a.shift() === 2, "Array.prototype.shift(): 2"); 85 | assert(a.shift() === undefined, "Array.prototype.shift(): 3"); 86 | assert(a.length === 0, "Array.prototype.shift(): 4"); 87 | }); 88 | 89 | test("Array.prototype.slice()", function () { 90 | var a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], b; 91 | b = a.slice(0, 1); 92 | assert(b.length === 1 && b[0] === 1, "Array.prototype.slice(): 1"); 93 | b = a.slice(1, 3); 94 | assert(b.length === 2 && b[0] === 2 && b[1] === 3, "Array.prototype.slice(): 2"); 95 | b = a.slice(9); 96 | assert(b.length === 1 && b[0] === 10, "Array.prototype.slice(): 3"); 97 | b = a.slice(-1); 98 | assert(b.length === 1 && b[0] === 10, "Array.prototype.slice(): 4"); 99 | b = a.slice(-3, -1); 100 | assert(b.length === 2 && b[0] === 8 && b[1] === 9, "Array.prototype.slice(): 5"); 101 | }); 102 | 103 | test("Array.prototype.sort()", function () { 104 | var a = [2, 4, 3, 5, 1], b; 105 | b = a.sort(); 106 | assert(b.length === 5 && b[0] === 1 && b[1] === 2 && b[2] === 3 && b[3] === 4 && b[4] === 5, 107 | "Array.prototype.sort(): 1"); 108 | }); 109 | 110 | test("Array.prototype.splice()", function () { 111 | var a = [1, 2, 3], b; 112 | b = a.splice(0, 3); 113 | assert(b.length === 3 && b[0] === 1 && b[1] === 2 && b[2] === 3 && a.length === 0, 114 | "Array.prototype.splice(): 1"); 115 | a = b.splice(0, 1, 4); 116 | assert(a.length === 1 && a[0] === 1 && b.length === 3 && b[0] === 4, "Array.prototype.splice(): 2"); 117 | }); 118 | 119 | test("Array.prototype.unshift()", function () { 120 | var a = []; 121 | a.unshift(1); 122 | assert(a.length === 1 && a[0] === 1, "Array.prototype.unshift(): 1"); 123 | a.unshift(2, 3); 124 | assert(a.length === 3 && a[0] === 2 && a[1] === 3 && a[2] === 1, "Array.prototype.unshift(): 2"); 125 | var a = []; 126 | a.unshift(1, 2, 3); 127 | assert(a.length === 3 && a[0] === 1 && a[1] === 2 && a[2] === 3, "Array.prototype.unshift(): 3"); 128 | }); 129 | 130 | test("Array.prototype.indexOf()", function () { 131 | var a = [1, 2, 3, 4, 5]; 132 | assert(a.indexOf(2) === 1, "Array.prototype.indexOf(): 1"); 133 | assert(a.indexOf(2, 1) === 1, "Array.prototype.indexOf(): 2"); 134 | assert(a.indexOf(2, 2) === -1, "Array.prototype.indexOf(): 3"); 135 | assert(a.indexOf(5, -1) === 4, "Array.prototype.indexOf(): 4"); 136 | assert(a.indexOf(5, -6) === 4, "Array.prototype.indexOf(): 5"); 137 | }); 138 | 139 | test("Array.prototype.lastIndexOf()", function () { 140 | var a = [1, 2, 3, 2, 1]; 141 | assert(a.lastIndexOf(2) === 3, "Array.prototype.lastIndexOf(): 1"); 142 | assert(a.lastIndexOf(2, 3) === 3, "Array.prototype.lastIndexOf(): 2"); 143 | assert(a.lastIndexOf(2, 4) === -1, "Array.prototype.lastIndexOf(): 3"); 144 | assert(a.lastIndexOf(2, -1) === -1, "Array.prototype.lastIndexOf(): 4"); 145 | assert(a.lastIndexOf(2, -2) === 3, "Array.prototype.lastIndexOf(): 5"); 146 | }); 147 | 148 | test("Array.prototype.every()", function () { 149 | assert([true, true, true].every(function (x) { return x; }), "Array.prototype.every(): 1"); 150 | assert(![true, false, true].every(function (x) { return x; }), "Array.prototype.every(): 2"); 151 | }); 152 | 153 | test("Array.prototype.some()", function () { 154 | assert([true, false, false].some(function (x) { return x; }), "Array.prototype.some(): 1"); 155 | assert(![false, false, false].some(function (x) { return x; }), "Array.prototype.some(): 2"); 156 | }); 157 | 158 | test("Array.prototype.forEach()", function () { 159 | var n = 0; 160 | [1, 2, 3, 4].forEach(function (x) { n += x; }) 161 | assert(n === 1 + 2 + 3 + 4, "Array.prototype.forEach(): 1"); 162 | }); 163 | 164 | test("Array.prototype.map()", function () { 165 | var a = [1, 2, 3].map(function (x) { return x * x; }); 166 | assert(a.length === 3 && a[0] === 1 && a[1] === 4 && a[2] === 9, "Array.prototype.map(): 1"); 167 | }); 168 | 169 | test("Array.prototype.filter()", function () { 170 | var a = [1, 2, 3, 4].filter(function (x) { return x & 1; }); 171 | assert(a.length === 2 && a[0] === 1 && a[1] === 3, "Array.prototype.filter(): 1"); 172 | }); 173 | 174 | test("Array.prototype.reduce()", function () { 175 | assert([1, 2, 3, 4].reduce(function (a, b) { return a + b; }) === 10, "Array.prototype.reduce(): 1"); 176 | assert([2, 3, 4].reduce(function (a, b) { return a + b; }, 1) === 10, "Array.prototype.reduce(): 2"); 177 | assert([8, 4, 2].reduce(function (a, b) { return a / b; }) === 1, "Array.prototype.reduce(): 3"); 178 | }); 179 | 180 | test("Array.prototype.reduceRight()", function() { 181 | assert([1, 2, 3, 4].reduceRight(function (a, b) { return a + b; }) === 10, "Array.prototype.reduceRight(): 1"); 182 | assert([1, 2, 3].reduceRight(function (a, b) { return a + b; }, 4) === 10, "Array.prototype.reduceRight(): 2"); 183 | assert([2, 4, 8].reduceRight(function (a, b) { return a / b; }) === 1, "Array.prototype.reduceRight(): 3"); 184 | }); 185 | -------------------------------------------------------------------------------- /test/builtin/object.js: -------------------------------------------------------------------------------- 1 | test("Object()", function () { 2 | assert(Object(undefined) instanceof Object, "bad conversion undefined -> object"); 3 | assert(Object(null) instanceof Object, "bad conversion null -> object"); 4 | assert(Object(true) instanceof Boolean, "bad conversion boolean -> object"); 5 | assert(Object(42) instanceof Number, "bad conversion number -> object"); 6 | assert(Object("hello, world!") instanceof String, "bad conversion string -> object"); 7 | assert(Object(Object) === Object, "bad conversion object -> object"); 8 | }); 9 | 10 | test("Object.getPrototypeOf()", function () { 11 | assert(Object.getPrototypeOf({}) === Object.prototype, "Object.getPrototypeOf(): Object.prototype"); 12 | assert(Object.getPrototypeOf(Object.prototype) === null, "Object.getPrototypeOf(): Object.prototype.prototype"); 13 | }); 14 | 15 | test("Object.getOwnPropertyDescriptor()", function () { 16 | var o = { foo: "bar" }, d = Object.getOwnPropertyDescriptor(o, "foo"); 17 | assert(Object(d) === d, "Object.getOwnPropertyDescriptor(): not an object"); 18 | assert(d.value === "bar", "Object.getOwnPropertyDescriptor(): wrong value"); 19 | assert(d.writable === true, "Object.getOwnPropertyDescriptor(): wrong writable"); 20 | assert(d.enumerable === true, "Object.getOwnPropertyDescriptor(): wrong enumerable"); 21 | assert(d.configurable === true, "Object.getOwnPropertyDescriptor(): wrong configurable"); 22 | }); 23 | 24 | test("Object.getOwnPropertyNames()", function () { 25 | var o = { one: 1, two: 2 }, n = Object.getOwnPropertyNames(o); 26 | assert(@@ `n->class === "Array" @@, "Object.getOwnPropertyNames(): not an array"); 27 | assert(n.length === 2, "Object.getOwnPropertyNames(): wrong array length"); 28 | assert((n[0] === "one" && n[1] === "two") || (n[0] === "two" && n[1] === "one"), 29 | "Object.getOwnPropertyNames(): wrong names"); 30 | }); 31 | 32 | test("Object.create()", function () { 33 | var o = Object.create(null, { one: { value: 1 }, two: { value: 2 } }); 34 | assert(@@ `o->prototype @@ === null, "Object.create(): wrong prototype"); 35 | assert(o.one === 1 && o.two === 2, "Object.create(): wrong properties"); 36 | var o = Object.create(null); 37 | assert(@@ `o->prototype @@ === null, "Object.create(): wrong prototype 2"); 38 | assert(o.one === undefined && o.two === undefined, "Object.create(): wrong properties 2"); 39 | }); 40 | 41 | test("Object.defineProperty()", function () { 42 | var o = {}; 43 | Object.defineProperty(o, "one", { value: 1 }); 44 | assert(o.one === 1, "Object.defineProperty(): wrong property value"); 45 | var d = Object.getOwnPropertyDescriptor(o, "one"); 46 | assert(d.writable === false && d.enumerable === false && d.configurable === false, 47 | "Object.defineProperty(): wrong property attributes"); 48 | 49 | Object.defineProperty(o, "two", { value: 2, writable: true, enumerable: true, configurable: true }); 50 | assert(o.two === 2, "Object.defineProperty(): wrong property value 2"); 51 | var d = Object.getOwnPropertyDescriptor(o, "two"); 52 | assert(d.writable === true && d.enumerable === true && d.configurable === true, 53 | "Object.defineProperty(): wrong property attributes 2"); 54 | 55 | var three = 3; 56 | Object.defineProperty(o, "three", { 57 | get: function () { return three; }, 58 | set: function (v) { three = v; }, 59 | enumerable: true 60 | }); 61 | assert(o.three === 3, "Object.defineProperty(): getter returned wrong value"); 62 | o.three = 4; 63 | assert(o.three === 4, "Object.defineProperty(): setter set wrong value"); 64 | 65 | Object.defineProperty(o, "four", { value: "" }); 66 | assertEqual(o.four, ""); 67 | }); 68 | 69 | test("Object.defineProperties()", function() { 70 | var o = {}, three = 3; 71 | Object.defineProperties(o, { 72 | one: { value: 1 }, 73 | two: { value: 2, writable: true, enumerable: true, configurable: true }, 74 | three: { 75 | get: function () { return three; }, 76 | set: function (v) { three = v; } 77 | } 78 | }); 79 | 80 | assert(o.one === 1, "Object.defineProperties(): wrong property value"); 81 | var d = Object.getOwnPropertyDescriptor(o, "one"); 82 | assert(d.writable === false && d.enumerable === false && d.configurable === false, 83 | "Object.defineProperties(): wrong property attributes"); 84 | 85 | 86 | assert(o.two === 2, "Object.defineProperties(): wrong property value 2"); 87 | var d = Object.getOwnPropertyDescriptor(o, "two"); 88 | assert(d.writable === true && d.enumerable === true && d.configurable === true, 89 | "Object.defineProperties(): wrong property attributes 2"); 90 | 91 | assert(o.three === 3, "Object.defineProperties(): getter returned wrong value"); 92 | o.three = 4; 93 | assert(o.three === 4, "Object.defineProperties(): setter set wrong value"); 94 | }); 95 | 96 | test("Object.seal(), Object.isSealed()", function () { 97 | var o = { one: 1, two: 2 }; 98 | assert(!Object.isSealed(o), "Object.isSealed(): should not be"); 99 | Object.seal(o); 100 | assert(Object.isSealed(o), "Object.seal(): not sealed"); 101 | delete o.one; 102 | delete o.two; 103 | assert(o.one === 1 && o.two === 2, "Object.seal(): properties can be deleted"); 104 | o.one = 11; 105 | o.two = 12; 106 | assert(o.one === 11 && o.two === 12, "Object.seal(): properties can't be changed"); 107 | o.three = 3; 108 | assert(o.three === undefined, "Object.seal(): properties can be added"); 109 | }); 110 | 111 | test("Object.freeze(), Object.isFrozen()", function () { 112 | var o = { one: 1, two: 2 }; 113 | assert(!Object.isFrozen(o), "Object.isFrozen(): should not be"); 114 | Object.freeze(o); 115 | assert(Object.isFrozen(o), "Object.freeze(): not frozen"); 116 | delete o.one; 117 | delete o.two; 118 | assert(o.one === 1 && o.two === 2, "Object.freeze(): properties can be deleted"); 119 | o.one = 11; 120 | o.two = 12; 121 | assert(o.one === 1 && o.two === 2, "Object.freeze(): properties can be changed"); 122 | o.three = 3; 123 | assert(o.three === undefined, "Object.freeze(): properties can be added"); 124 | }); 125 | 126 | test("Object.preventExtensions(), Object.isExtensible()", function () { 127 | var o = { one: 1, two: 2 }; 128 | assert(Object.isExtensible(o), "Object.isExtensible(): should be"); 129 | Object.preventExtensions(o); 130 | assert(!Object.isExtensible(o), "Object.preventExtensions(): still extensible"); 131 | delete o.one; 132 | delete o.two; 133 | assert(o.one === undefined && o.two === undefined, "Object.preventExtensions(): properties can't be deleted"); 134 | o.one = 1; 135 | o.two = 1; 136 | assert(o.one === undefined && o.two === undefined, "Object.preventExtensions(): properties can be added"); 137 | }); 138 | 139 | test("Object.keys()", function () { 140 | assert(Object.keys({}).length === 0, "Object.keys(): wrong for empty object"); 141 | var o = { one: 1, two: 2 }, k = Object.keys(o); 142 | assert(@@ `k->class === "Array" @@, "Object.keys(): returned not an array"); 143 | assert(k.length === 2, "Object.keys(): wrong array length"); 144 | assert(k[0] === "one" && k[1] === "two" || k[0] === "two" && k[1] === "one", "Object.keys(): wrong names"); 145 | }); 146 | 147 | test("Object.prototype.toString()", function () { 148 | assert({}.toString() === "[object Object]", "Object.prototype.toString(): wrong for Object"); 149 | }); 150 | 151 | test("Object.prototype.valueOf()", function () { 152 | var o = {}; 153 | assert(o.valueOf() === o, "Object.prototype.valueOf(): wrong value"); 154 | }); 155 | 156 | test("Object.prototype.hasOwnProperty()", function () { 157 | var o1 = { one: 1 }, o2 = Object.create(o1); 158 | o2.two = 2; 159 | assert(o1.hasOwnProperty("one"), "Object.prototype.hasOwnProperty(): 1"); 160 | assert(o2.hasOwnProperty("two"), "Object.prototype.hasOwnProperty(): 2"); 161 | assert(!o2.hasOwnProperty("one"), "Object.prototype.hasOwnProperty(): 3"); 162 | }); 163 | 164 | test("Object.prototype.isPrototypeOf()", function () { 165 | var o1 = {}, o2 = Object.create(o1); 166 | assert(Object.prototype.isPrototypeOf(o1), "Object.prototype.isPrototypeOf(): 1"); 167 | assert(o1.isPrototypeOf(o2), "Object.prototype.isPrototypeOf(): 2"); 168 | assert(!o2.isPrototypeOf(o1), "Object.prototype.isPrototypeOf(): 3"); 169 | }); 170 | 171 | test("Object.prototype.propertyIsEnumerable()", function () { 172 | var o = Object.create(Object.prototype, { 173 | one: { value: 1, enumerable: true }, 174 | two: { value: 2, enumerable: false } 175 | }); 176 | assert(o.propertyIsEnumerable("one"), "Object.prototype.propertyIsEnumerable(): 1"); 177 | assert(!o.propertyIsEnumerable("two"), "Object.prototype.propertyIsEnumerable(): 2"); 178 | }); 179 | -------------------------------------------------------------------------------- /src/image/09_date.js: -------------------------------------------------------------------------------- 1 | function Date(year, month, date, hours, minutes, seconds, ms) { 2 | if (this === @@ $global @@) { 3 | return (new Date(year, month, date, hours, minutes, seconds, ms)).toString(); 4 | } 5 | 6 | var d = {}; 7 | @@ `d->prototype = `Date->properties['prototype']; @@ 8 | @@ `d->class = 'Date'; @@ 9 | @@ `d->extensible = TRUE; @@ 10 | 11 | if (year === undefined) { 12 | @@ `d->value = time(); @@ 13 | 14 | } else if (month === undefined) { 15 | var value = arguments[0]; 16 | if (typeof value === "object" && value !== null) { 17 | if (typeof value.valueOf === "function") { 18 | value = value.valueOf(); 19 | 20 | } else if (typeof value.toString === "function") { 21 | value = value.toString(); 22 | 23 | } else { 24 | throw new TypeError("Date(): cannot get value of object argument given"); 25 | } 26 | } else { 27 | value = @@ JS::toPrimitive(`value, $global) @@; 28 | } 29 | 30 | if (typeof value === "string") { 31 | value = Date.parse(value); 32 | } 33 | 34 | @@ `d->value = JS::toNumber(`value, $global) / 1000; @@ 35 | 36 | } else { 37 | if (year >= 0 && year <= 99) { 38 | year += 1900; 39 | } 40 | 41 | date = date || 1; 42 | hours = hours || 0; 43 | minutes = minutes || 0; 44 | seconds = seconds || 0; 45 | ms = ms || 0; 46 | 47 | @@ `d->value = mktime(`hours, `minutes, `seconds, `month, `date, `year) + `ms / 1000; @@ 48 | } 49 | 50 | return d; 51 | } 52 | 53 | Date.parse = function (string) { 54 | var i = @@ strtotime(`string) @@; 55 | 56 | if (i === false) { 57 | return NaN; 58 | } 59 | 60 | return @@ `i * 1000 @@; 61 | }; 62 | 63 | Date.UTC = function (year, month, date, hours, minutes, seconds, ms) { 64 | throw new NotImplementedError("UTC functions not implemented."); 65 | }; 66 | 67 | Date.now = function () { 68 | return new Date(@@ time() * 1000 @@); 69 | }; 70 | 71 | Date.prototype = {}; 72 | @@ `Date->properties['prototype']->prototype = `Object->properties['prototype']; @@ 73 | @@ `Date->properties['prototype']->class = 'Date'; @@ 74 | @@ `Date->properties['prototype']->extensible = TRUE; @@ 75 | @@ `Date->properties['prototype']->value = NAN; @@ 76 | Date.prototype.constructor = Date; 77 | 78 | Date.prototype.toString = function () { 79 | return @@ date('Y-m-d H:i:s', $leThis->value) @@; 80 | }; 81 | 82 | Date.prototype.toDateString = function () { 83 | return @@ date('Y-m-d', $leThis->value) @@; 84 | }; 85 | 86 | Date.prototype.toTimeString = function () { 87 | return @@ date('H:i:s', $leThis->value) @@; 88 | }; 89 | 90 | Date.prototype.toLocaleString = Date.prototype.toString; 91 | Date.prototype.toLocaleDateString = Date.prototype.toDateString; 92 | Date.prototype.toLocaleTimeString = Date.prototype.toTimeString; 93 | 94 | Date.prototype.valueOf = function () { 95 | return @@ $leThis->value * 1000 @@; 96 | }; 97 | 98 | Date.prototype.getTime = Date.prototype.valueOf; 99 | 100 | Date.prototype.getFullYear = function () { 101 | return @@ (int) date('Y', $leThis->value) @@; 102 | }; 103 | 104 | Date.prototype.getUTCFullYear = function () { 105 | throw new NotImplementedError("UTC functions not implemented."); 106 | }; 107 | 108 | Date.prototype.getYear = function () { 109 | return this.getFullYear() - 1900; 110 | }; 111 | 112 | Date.prototype.getUTCYear = Date.prototype.getUTCFullYear; 113 | 114 | Date.prototype.getMonth = function () { 115 | return @@ (int) date('n', $leThis->value) @@; 116 | }; 117 | 118 | Date.prototype.getUTCMonth = Date.prototype.getUTCFullYear; 119 | 120 | Date.prototype.getDate = function () { 121 | return @@ (int) date('j', $leThis->value) @@; 122 | }; 123 | 124 | Date.prototype.getUTCDate = Date.prototype.getUTCFullYear; 125 | 126 | Date.prototype.getDay = function () { 127 | return @@ (int) date('w', $leThis->value) @@; 128 | }; 129 | 130 | Date.prototype.getUTCDay = Date.prototype.getUTCFullYear; 131 | 132 | Date.prototype.getHours = function () { 133 | return @@ (int) date('G', $leThis->value) @@; 134 | }; 135 | 136 | Date.prototype.getUTCHours = Date.prototype.getUTCFullYear; 137 | 138 | Date.prototype.getMinutes = function () { 139 | return @@ (int) date('i', $leThis->value) @@; 140 | }; 141 | 142 | Date.prototype.getUTCMinutes = Date.prototype.getUTCFullYear; 143 | 144 | Date.prototype.getSeconds = function () { 145 | return @@ (int) date('s', $leThis->value) @@; 146 | }; 147 | 148 | Date.prototype.getUTCSeconds = Date.prototype.getUTCFullYear; 149 | 150 | Date.prototype.getMilliseconds = function () { 151 | return @@ (int) (($leThis->value - (int) $leThis->value) * 1000) @@; 152 | }; 153 | 154 | Date.prototype.getUTCMilliseconds = Date.prototype.getUTCFullYear; 155 | 156 | Date.prototype.getTimezoneOffset = function () { 157 | return @@ date('Z', $leThis->value) / 60 @@; 158 | }; 159 | 160 | Date.prototype.setTime = function (time) { 161 | @@ $leThis->value = JS::toNumber(`time / 1000, $global); @@ 162 | return @@ $leThis->value * 1000 @@; 163 | }; 164 | 165 | Date.prototype.setMilliseconds = function (ms) { 166 | var newDate = new Date( 167 | this.getFullYear(), this.getMonth(), this.getDate(), 168 | this.getHours(), this.getMinutes(), this.getSeconds(), ms 169 | ); 170 | 171 | @@ $leThis->value = `newDate->value; @@ 172 | 173 | return @@ $leThis->value @@; 174 | }; 175 | 176 | Date.prototype.setUTCMilliseconds = Date.prototype.getUTCFullYear; 177 | 178 | Date.prototype.setSeconds = function (seconds, ms) { 179 | if (ms === undefined) { 180 | ms = this.getMilliseconds(); 181 | } 182 | 183 | var newDate = new Date( 184 | this.getFullYear(), this.getMonth(), this.getDate(), 185 | this.getHours(), this.getMinutes(), seconds, ms 186 | ); 187 | 188 | @@ $leThis->value = `newDate->value; @@ 189 | 190 | return @@ $leThis->value @@; 191 | }; 192 | 193 | Date.prototype.setUTCSeconds = Date.prototype.getUTCFullYear; 194 | 195 | Date.prototype.setMinutes = function (minutes, seconds, ms) { 196 | if (seconds === undefined) { 197 | seconds = this.getSeconds(); 198 | } 199 | 200 | if (ms === undefined) { 201 | ms = this.getMilliseconds(); 202 | } 203 | 204 | var newDate = new Date( 205 | this.getFullYear(), this.getMonth(), this.getDate(), 206 | this.getHours(), minutes, seconds, ms 207 | ); 208 | 209 | @@ $leThis->value = `newDate->value; @@ 210 | 211 | return @@ $leThis->value @@; 212 | }; 213 | 214 | Date.prototype.setUTCMinutes = Date.prototype.getUTCFullYear; 215 | 216 | Date.prototype.setHours = function (hours, minutes, seconds, ms) { 217 | if (minutes === undefined) { 218 | minutes = this.getMinutes(); 219 | } 220 | 221 | if (seconds === undefined) { 222 | seconds = this.getSeconds(); 223 | } 224 | 225 | if (ms === undefined) { 226 | ms = this.getMilliseconds(); 227 | } 228 | 229 | var newDate = new Date( 230 | this.getFullYear(), this.getMonth(), this.getDate(), 231 | hours, minutes, seconds, ms 232 | ); 233 | 234 | @@ $leThis->value = `newDate->value; @@ 235 | 236 | return @@ $leThis->value @@; 237 | }; 238 | 239 | Date.prototype.setUTCHours = Date.prototype.getUTCFullYear; 240 | 241 | Date.prototype.setDate = function (date) { 242 | var newDate = new Date( 243 | this.getFullYear(), this.getMonth(), date, 244 | this.getHours(), this.getMinutes(), this.getSeconds(), this.getMilliseconds() 245 | ); 246 | 247 | @@ $leThis->value = `newDate->value; @@ 248 | 249 | return @@ $leThis->value @@; 250 | }; 251 | 252 | Date.prototype.setUTCDate = Date.prototype.getUTCFullYear; 253 | 254 | Date.prototype.setMonth = function (month, date) { 255 | if (date === undefined) { 256 | date = this.getDate(); 257 | } 258 | 259 | var newDate = new Date( 260 | this.getFullYear(), month, date, 261 | this.getHours(), this.getMinutes(), this.getSeconds(), this.getMilliseconds() 262 | ); 263 | 264 | @@ $leThis->value = `newDate->value; @@ 265 | 266 | return @@ $leThis->value @@; 267 | }; 268 | 269 | Date.prototype.setUTCMonth = Date.prototype.getUTCFullYear; 270 | 271 | Date.prototype.setFullYear = function (year, month, date) { 272 | if (month === undefined) { 273 | month = this.getMonth(); 274 | } 275 | 276 | if (date === undefined) { 277 | date = this.getDate(); 278 | } 279 | 280 | var newDate = new Date( 281 | year, month, date, 282 | this.getHours(), this.getMinutes(), this.getSeconds(), this.getMilliseconds() 283 | ); 284 | 285 | @@ $leThis->value = `newDate->value; @@ 286 | 287 | return @@ $leThis->value @@; 288 | }; 289 | 290 | Date.prototype.setUTCFullYear = Date.prototype.getUTCFullYear; 291 | 292 | Date.prototype.toUTCString = Date.prototype.getUTCFullYear; 293 | 294 | Date.prototype.toISOString = function () { 295 | return @@ date('Y-m-d', $leThis->value) . 296 | 'T' . date('H:i:s', $leThis->value) . 297 | '.' . ((int) (($leThis->value - (int) $leThis->value) * 1000)) . 298 | date('P', $leThis->value) @@; 299 | }; 300 | 301 | Date.prototype.toJSON = function (key) { 302 | return this.toISOString(); 303 | }; 304 | -------------------------------------------------------------------------------- /src/image/02_object.js: -------------------------------------------------------------------------------- 1 | function Object(value) { 2 | if (value === undefined || value === null) { 3 | return {}; 4 | } 5 | 6 | return @@ JS::toObject(`value, $global) @@; 7 | } 8 | 9 | Object.getPrototypeOf = function (o) { 10 | if (o === null || typeof o !== "object" && typeof o !== "function") { 11 | throw new TypeError("Object.getPrototyeOf(): Cannot get prototype of non-object."); 12 | } 13 | 14 | return @@ `o->prototype @@; 15 | }; 16 | 17 | Object.getOwnPropertyDescriptor = function (o, p) { 18 | if (o === null || typeof o !== "object" && typeof o !== "function") { 19 | throw new TypeError("Object.getOwnPropertyDescriptor(): Cannot get property descriptor of non-object."); 20 | } 21 | 22 | if (!(@@ array_key_exists(`p, `o->properties) || array_key_exists(`p, `o->attributes) @@)) { 23 | return undefined; 24 | } 25 | 26 | var desc = {}; 27 | 28 | if (@@ array_key_exists(`p, `o->properties) @@) { 29 | desc.value = o[p]; 30 | desc.writable = @@ JS::toBoolean(`o->attributes[`p] & JS::WRITABLE, $global) @@; 31 | 32 | } else { 33 | if (@@ `o->attributes[`p] & JS::HAS_GETTER @@) { 34 | desc.get = @@ `o->getters[`p] @@; 35 | } 36 | 37 | if (@@ `o->attributes[`p] & JS::HAS_SETTER @@) { 38 | desc.set = @@ `o->setters[`p] @@; 39 | } 40 | } 41 | 42 | desc.enumerable = @@ JS::toBoolean(`o->attributes[`p] & JS::ENUMERABLE, $global) @@; 43 | desc.configurable = @@ JS::toBoolean(`o->attributes[`p] & JS::CONFIGURABLE, $global) @@; 44 | 45 | return desc; 46 | }; 47 | 48 | Object.getOwnPropertyNames = function (o) { 49 | if (o === null || typeof o !== "object" && typeof o !== "function") { 50 | throw new TypeError("Object.getOwnPropertyNames(): Cannot get property names of non-object."); 51 | } 52 | 53 | var names = []; 54 | 55 | @@ foreach (array_unique(array_merge(array_keys(`o->properties), array_keys(`o->attributes))) as $i => $name) { @@ 56 | names.push(@@ $name @@); 57 | @@ } @@ 58 | 59 | return names; 60 | }; 61 | 62 | Object.create = function (o, properties) { 63 | if (typeof o !== "object" && typeof o !== "function") { 64 | throw new TypeError("Object.create(): Cannot create object with non-object prototype."); 65 | } 66 | 67 | var newObject = {}; 68 | 69 | @@ `newObject->prototype = `o; @@ 70 | 71 | if (@@ is_object(`properties) && `properties !== JS::$undefined @@) { 72 | Object.defineProperties(newObject, properties); 73 | } 74 | 75 | return newObject; 76 | }; 77 | 78 | Object.defineProperty = function (o, p, attributes) { 79 | if (o === null || typeof o !== "object" && typeof o !== "function") { 80 | throw new TypeError("Object.defineProperty(): Cannot define property on non-object."); 81 | } 82 | 83 | if (attributes.get !== undefined && typeof attributes.get !== "function") { 84 | throw new TypeError("Object.defineProperty(): Given getter is not a function."); 85 | } 86 | 87 | if (attributes.set !== undefined && typeof attributes.set !== "function") { 88 | throw new TypeError("Object.defineProperty(): Given setter is not a function."); 89 | } 90 | 91 | attributes = attributes || {}; 92 | p = @@ JS::toString(`p, $global) @@; 93 | 94 | var value = attributes.value, 95 | get = attributes.get, 96 | set = attributes.set, 97 | attrs = 0; 98 | 99 | if (typeof value !== "undefined") { 100 | @@ `o->properties[`p] = `value; @@ 101 | } 102 | 103 | if (get) { 104 | @@ `o->getters[`p] = `get; @@ 105 | attrs |= @@ JS::HAS_GETTER @@; 106 | } 107 | 108 | if (set) { 109 | @@ `o->setters[`p] = `set; @@ 110 | attrs |= @@ JS::HAS_SETTER @@; 111 | } 112 | 113 | if (attributes.writable) { 114 | attrs |= @@ JS::WRITABLE @@; 115 | } 116 | 117 | if (attributes.enumerable) { 118 | attrs |= @@ JS::ENUMERABLE @@; 119 | } 120 | 121 | if (attributes.configurable) { 122 | attrs |= @@ JS::CONFIGURABLE @@; 123 | } 124 | 125 | @@ `o->attributes[`p] = `attrs; @@ 126 | 127 | return o; 128 | }; 129 | 130 | Object.defineProperties = function (o, properties) { 131 | if (o === null || typeof o !== "object" && typeof o !== "function") { 132 | throw new TypeError("Object.defineProperties(): Cannot define properties on non-object."); 133 | } 134 | 135 | if (typeof properties !== "object") { 136 | throw new TypeError("Object.defineProperties(): Given properties argument is not an object."); 137 | } 138 | 139 | for (var p in properties) { 140 | Object.defineProperty(o, p, properties[p]); 141 | } 142 | 143 | return o; 144 | }; 145 | 146 | Object.seal = function (o) { 147 | if (o === null || typeof o !== "object" && typeof o !== "function") { 148 | throw new TypeError("Object.seal(): Cannot seal non-object."); 149 | } 150 | 151 | @@ 152 | foreach (array_keys(`o->attributes) as $property) { 153 | `o->attributes[$property] &= ~JS::CONFIGURABLE; 154 | } 155 | 156 | `o->extensible = FALSE; 157 | @@ 158 | 159 | return o; 160 | }; 161 | 162 | Object.freeze = function (o) { 163 | if (o === null || typeof o !== "object" && typeof o !== "function") { 164 | throw new TypeError("Object.freeze(): Cannot freeze non-object."); 165 | } 166 | 167 | @@ 168 | foreach (array_keys(`o->attributes) as $property) { 169 | `o->attributes[$property] &= ~JS::WRITABLE; 170 | `o->attributes[$property] &= ~JS::CONFIGURABLE; 171 | } 172 | 173 | `o->extensible = FALSE; 174 | @@ 175 | 176 | return o; 177 | }; 178 | 179 | Object.preventExtensions = function (o) { 180 | if (o === null || typeof o !== "object" && typeof o !== "function") { 181 | throw new TypeError("Object.preventExtensions(): Cannot prevent extensions on non-object."); 182 | } 183 | 184 | @@ `o->extensible = FALSE; @@ 185 | 186 | return o; 187 | }; 188 | 189 | Object.isSealed = function (o) { 190 | if (o === null || typeof o !== "object" && typeof o !== "function") { 191 | throw new TypeError("Object.isSealed(): Cannot return if sealed, non-object given."); 192 | } 193 | 194 | @@ 195 | foreach (array_keys(`o->attributes) as $property) { 196 | if (`o->attributes[$property] & JS::CONFIGURABLE) { 197 | return FALSE; 198 | } 199 | } 200 | 201 | return !`o->extensible; 202 | @@ 203 | }; 204 | 205 | Object.isFrozen = function (o) { 206 | if (o === null || typeof o !== "object" && typeof o !== "function") { 207 | throw new TypeError("Object.isFrozen(): Cannot return if frozen, non-object given."); 208 | } 209 | 210 | @@ 211 | foreach (array_keys(`o->attributes) as $property) { 212 | if (`o->attributes[$property] & (JS::CONFIGURABLE | JS::WRITABLE)) { 213 | return FALSE; 214 | } 215 | } 216 | 217 | return !`o->extensible; 218 | @@ 219 | }; 220 | 221 | Object.isExtensible = function (o) { 222 | if (o === null || typeof o !== "object" && typeof o !== "function") { 223 | throw new TypeError("Object.isExtensible(): Cannot return if extensible, non-object given."); 224 | } 225 | 226 | @@ return `o->extensible; @@ 227 | }; 228 | 229 | Object.keys = function (o) { 230 | if (o === null || typeof o !== "object" && typeof o !== "function") { 231 | throw new TypeError("Object.keys(): Cannot return keys, non-object given."); 232 | } 233 | 234 | var keys = []; 235 | 236 | for (var k in o) { 237 | keys.push(k); 238 | } 239 | 240 | return keys; 241 | }; 242 | 243 | Object.prototype = {}; 244 | @@ `Object->properties['prototype']->prototype = NULL; @@ 245 | @@ `Object->properties['prototype']->class = 'Object'; @@ 246 | @@ `Object->properties['prototype']->extensible = TRUE; @@ 247 | Object.prototype.constructor = Object; 248 | 249 | Object.prototype.toString = function () { 250 | if (this === undefined) { 251 | return "[object Undefined]"; 252 | 253 | } else if (this === null) { 254 | return "[object Null]"; 255 | 256 | } else { 257 | var o = @@ JS::toObject($leThis, $global) @@; 258 | 259 | if (@@ !isset(`o->class) @@) { 260 | return "[object]"; 261 | } 262 | 263 | return @@ "[object " . `o->class . "]" @@; 264 | } 265 | }; 266 | 267 | Object.prototype.toLocaleString = function () { 268 | return this.toString(); 269 | }; 270 | 271 | Object.prototype.valueOf = function () { 272 | if (this === undefined || this === null) { 273 | throw new TypeError("Object.prototype.valueOf(): this is undefined or null"); 274 | } 275 | 276 | return @@ JS::toObject($leThis, $global) @@; 277 | }; 278 | 279 | Object.prototype.hasOwnProperty = function (p) { 280 | return @@ array_key_exists(`p, $leThis->properties) || isset($leThis->attributes[`p]) @@; 281 | }; 282 | 283 | Object.prototype.isPrototypeOf = function (v) { 284 | if (v === null || typeof v !== "object" && typeof v !== "function") { 285 | return false; 286 | } 287 | 288 | @@ 289 | for (`v = `v->prototype; `v; `v = `v->prototype) { 290 | if (`v === $leThis) { 291 | return TRUE; 292 | } 293 | } 294 | 295 | @@ 296 | 297 | return false; 298 | }; 299 | 300 | Object.prototype.propertyIsEnumerable = function (p) { 301 | return @@ JS::toBoolean(isset($leThis->attributes[`p]) && ($leThis->attributes[`p] & JS::ENUMERABLE), $global) @@; 302 | }; 303 | -------------------------------------------------------------------------------- /test/builtin/fs.js: -------------------------------------------------------------------------------- 1 | var fs = require("fs"), 2 | Buffer = require("buffer").Buffer, 3 | fixturesDir = __dirname + "/.fs_fixtures"; 4 | a = __dirname + "/.fs_fixtures/a", 5 | b = __dirname + "/.fs_fixtures/b"; 6 | 7 | 8 | test("fs.rename()", function () { 9 | PHP.fn("touch")(a); 10 | fs.renameSync(a, b); 11 | 12 | PHP.fn("touch")(a); 13 | fs.rename(a, b, function (err) { 14 | assert(!err); 15 | assert(!PHP.fn("file_exists")(a)); 16 | assert(PHP.fn("file_exists")(b)); 17 | PHP.fn("unlink")(b); 18 | }); 19 | }); 20 | 21 | test("fs.truncate()", function () { 22 | // TODO 23 | }); 24 | 25 | test("fs.chown()", function () { 26 | // must be run as root 27 | }); 28 | 29 | test("fs.chmod()", function () { 30 | PHP.fn("touch")(a); 31 | fs.chmodSync(a, 0); 32 | @@ clearstatcache(); @@ 33 | assertEqual(PHP.fn("stat")(a).mode & 0777, 0); 34 | 35 | fs.chmod(a, 0777, function (err) { 36 | assert(!err); 37 | @@ clearstatcache(); @@ 38 | assertEqual(PHP.fn("stat")(a).mode & 0777, 0777); 39 | PHP.fn("unlink")(a); 40 | }); 41 | }); 42 | 43 | test("fs.stat()", function () { 44 | assert(fs.statSync(fixturesDir).isDirectory()); 45 | 46 | PHP.fn("touch")(a) 47 | fs.statSync(a, function (err, stats) { 48 | assert(!err); 49 | assert(stats.isFile()); 50 | PHP.fn("unlink")(a); 51 | }); 52 | 53 | assert(fs.statSync("/dev/zero").isCharacterDevice()); 54 | assert(fs.statSync("/dev/fd0").isBlockDevice()); 55 | PHP.fn("symlink")(a, b); 56 | assert(fs.statSync(a).isSymbolicLink()); 57 | PHP.fn("unlink")(a); 58 | PHP.fn("unlink")(b); 59 | assert(fs.statSync("/dev/log").isSocket()); 60 | }); 61 | 62 | test("fs.link()", function () { 63 | PHP.fn("file_put_contents")(a, "a"); 64 | fs.linkSync(a, b); 65 | assert(PHP.fn("file_exists")(b)); 66 | assertEqual(PHP.fn("file_get_contents")(a), PHP.fn("file_get_contents")(b)); 67 | PHP.fn("unlink")(a); 68 | PHP.fn("unlink")(b); 69 | 70 | PHP.fn("file_put_contents")(a, "a"); 71 | fs.link(a, b, function (err) { 72 | assert(!err); 73 | assert(PHP.fn("file_exists")(b)); 74 | assertEqual(PHP.fn("file_get_contents")(a), PHP.fn("file_get_contents")(b)); 75 | PHP.fn("unlink")(a); 76 | PHP.fn("unlink")(b); 77 | }); 78 | }); 79 | 80 | test("fs.symlink()", function () { 81 | PHP.fn("file_put_contents")(a, "a"); 82 | fs.symlinkSync(a, b); 83 | assert(PHP.fn("file_exists")(b)); 84 | assertEqual(PHP.fn("file_get_contents")(a), PHP.fn("file_get_contents")(b)); 85 | PHP.fn("unlink")(a); 86 | PHP.fn("unlink")(b); 87 | 88 | PHP.fn("file_put_contents")(a, "a"); 89 | fs.symlink(a, b, function (err) { 90 | assert(!err); 91 | assert(PHP.fn("file_exists")(b)); 92 | assertEqual(PHP.fn("file_get_contents")(a), PHP.fn("file_get_contents")(b)); 93 | PHP.fn("unlink")(a); 94 | PHP.fn("unlink")(b); 95 | }); 96 | }); 97 | 98 | test("fs.readlink()", function () { 99 | PHP.fn("touch")(a); 100 | PHP.fn("symlink")(a, b); 101 | assertEqual(fs.readlinkSync(b), a); 102 | fs.readlink(b, function (err, file) { 103 | assert(!err); 104 | assertEqual(file, a); 105 | PHP.fn("unlink")(a); 106 | PHP.fn("unlink")(b); 107 | }); 108 | }); 109 | 110 | test("fs.realpath()", function () { 111 | PHP.fn("touch")(a); 112 | PHP.fn("symlink")(a, b); 113 | assertEqual(fs.realpathSync(b), a); 114 | fs.realpath(b, function (err, path) { 115 | assert(!err); 116 | assertEqual(path, a); 117 | PHP.fn("unlink")(a); 118 | PHP.fn("unlink")(b); 119 | }); 120 | }); 121 | 122 | test("fs.unlink()", function () { 123 | PHP.fn("touch")(a); 124 | assert(PHP.fn("file_exists")(a)); 125 | fs.unlinkSync(a); 126 | assert(!PHP.fn("file_exists")(a)); 127 | 128 | PHP.fn("touch")(a); 129 | assert(PHP.fn("file_exists")(a)); 130 | fs.unlink(a, function (err) { 131 | assert(!err); 132 | assert(!PHP.fn("file_exists")(a)); 133 | }); 134 | }); 135 | 136 | test("fs.rmdir()", function () { 137 | PHP.fn("mkdir")(a); 138 | assert(PHP.fn("file_exists")(a)); 139 | fs.rmdirSync(a); 140 | assert(!PHP.fn("file_exists")(a)); 141 | 142 | PHP.fn("mkdir")(a); 143 | assert(PHP.fn("file_exists")(a)); 144 | fs.rmdir(a, function (err) { 145 | assert(!err); 146 | assert(!PHP.fn("file_exists")(a)); 147 | }); 148 | }); 149 | 150 | test("fs.mkdir()", function () { 151 | assert(!PHP.fn("file_exists")(a)); 152 | fs.mkdirSync(a); 153 | assert(PHP.fn("file_exists")(a)); 154 | assert(PHP.fn("is_dir")(a)); 155 | PHP.fn("rmdir")(a); 156 | 157 | assert(!PHP.fn("file_exists")(a)); 158 | fs.mkdir(a, function (err) { 159 | assert(!err); 160 | assert(PHP.fn("file_exists")(a)); 161 | assert(PHP.fn("is_dir")(a)); 162 | PHP.fn("rmdir")(a); 163 | }); 164 | }); 165 | 166 | test("fs.readdir()", function () { 167 | PHP.fn("touch")(a); 168 | PHP.fn("touch")(b); 169 | assertEqual(fs.readdirSync(fixturesDir).sort().toString(), ".dummy,a,b"); 170 | fs.readdir(fixturesDir, function (err, files) { 171 | assert(!err); 172 | assertEqual(files.sort().toString(), ".dummy,a,b"); 173 | PHP.fn("unlink")(a); 174 | PHP.fn("unlink")(b); 175 | }); 176 | }); 177 | 178 | test("fs.utimes()", function () { 179 | PHP.fn("touch")(a); 180 | fs.utimesSync(a, 0, 0); 181 | fs.utimes(a, 0, 0, function (err) { 182 | assert(!err); 183 | PHP.fn("unlink")(a); 184 | }); 185 | }); 186 | 187 | test("fs.open(), fs.close()", function () { 188 | assert(!PHP.fn("file_exists")(a)); 189 | try { 190 | fs.openSync(a, "r"); 191 | assert(false, "did not throw"); 192 | } catch (e) {} 193 | assert(!PHP.fn("file_exists")(a)); 194 | 195 | assert(!PHP.fn("file_exists")(a)); 196 | var fd = fs.openSync(a, "w"); 197 | fs.closeSync(fd); 198 | assert(PHP.fn("file_exists")(a)); 199 | PHP.fn("unlink")(a); 200 | 201 | assert(!PHP.fn("file_exists")(a)); 202 | var fd = fs.openSync(a, "a"); 203 | fs.closeSync(fd); 204 | assert(PHP.fn("file_exists")(a)); 205 | PHP.fn("unlink")(a); 206 | 207 | assert(!PHP.fn("file_exists")(a)); 208 | fs.open(a, "r", function (err, fd) { 209 | assert(err); 210 | assert(!fd); 211 | }); 212 | 213 | assert(!PHP.fn("file_exists")(a)); 214 | fs.open(a, "w", function (err, fd) { 215 | assert(!err); 216 | assert(fd); 217 | fs.close(fd, function (err) { 218 | assert(!err); 219 | assert(PHP.fn("file_exists")(a)); 220 | PHP.fn("unlink")(a); 221 | }); 222 | }); 223 | 224 | assert(!PHP.fn("file_exists")(a)); 225 | fs.open(a, "a", function (err, fd) { 226 | assert(!err); 227 | assert(fd); 228 | fs.close(fd, function (err) { 229 | assert(!err); 230 | assert(PHP.fn("file_exists")(a)); 231 | PHP.fn("unlink")(a); 232 | }); 233 | }); 234 | }); 235 | 236 | test("fs.write()", function () { 237 | var buf = new Buffer("abc"); 238 | 239 | fs.open(a, "w", function (err, fd) { 240 | assert(!err); 241 | assertEqual(fs.writeSync(fd, buf, 0, 3, null), 3); 242 | fs.closeSync(fd); 243 | assertEqual(PHP.fn("file_get_contents")(a), "abc"); 244 | PHP.fn("unlink")(a); 245 | }); 246 | 247 | fs.open(a, "w", function (err, fd) { 248 | assert(!err); 249 | fs.write(fd, buf, 0, 3, null, function (err, bytesWritten) { 250 | assert(!err); 251 | assertEqual(bytesWritten, 3); 252 | fs.closeSync(fd); 253 | assertEqual(PHP.fn("file_get_contents")(a), "abc"); 254 | PHP.fn("unlink")(a); 255 | }); 256 | }); 257 | }); 258 | 259 | test("fs.read()", function () { 260 | PHP.fn("file_put_contents")(a, "abc"); 261 | 262 | fs.open(a, "r", function (err, fd) { 263 | assert(!err); 264 | var buf = new Buffer("cde"); 265 | assertEqual(fs.readSync(fd, buf, 0, 3, null), 3); 266 | assertEqual(buf.toString(), "abc"); 267 | 268 | var buf = new Buffer("cde"); 269 | fs.read(fd, buf, 0, 3, 0, function (err, bytesRead, buf) { 270 | assert(!err); 271 | assertEqual(bytesRead, 3); 272 | assertEqual(buf.toString(), "abc"); 273 | fs.closeSync(fd); 274 | PHP.fn("unlink")(a); 275 | }); 276 | }); 277 | }); 278 | 279 | test("fs.readFile()", function () { 280 | PHP.fn("file_put_contents")(a, "readFile"); 281 | assertEqual(fs.readFileSync(a).toString(), "readFile"); 282 | assertEqual(fs.readFileSync(a, "utf8"), "readFile"); 283 | try { fs.readlinkSync(a, "cp1250"); assert(false, "did not throw"); } catch (e) {} 284 | fs.readFile(a, function (err, data) { 285 | assert(!err); 286 | assertEqual(data.toString(), "readFile"); 287 | PHP.fn("unlink")(a); 288 | }); 289 | }); 290 | 291 | test("fs.writeFile()", function () { 292 | fs.writeFileSync(a, "writeFileSync"); 293 | assertEqual(PHP.fn("file_get_contents")(a), "writeFileSync"); 294 | 295 | fs.writeFile(a, "writeFile", function (err) { 296 | assert(!err); 297 | assertEqual(PHP.fn("file_get_contents")(a), "writeFile"); 298 | PHP.fn("unlink")(a); 299 | }); 300 | }); 301 | 302 | test("fs.appendFile()", function () { 303 | fs.appendFile(a, "append"); 304 | assertEqual(PHP.fn("file_get_contents")(a), "append"); 305 | 306 | fs.appendFile(a, "append", function (err) { 307 | assert(!err); 308 | assertEqual(PHP.fn("file_get_contents")(a), "appendappend"); 309 | PHP.fn("unlink")(a); 310 | }); 311 | }); 312 | 313 | test("fs.exits()", function () { 314 | assert(!fs.existsSync(a)); 315 | PHP.fn("touch")(a); 316 | fs.exists(a, function (exists) { 317 | assert(exists); 318 | PHP.fn("unlink")(a); 319 | }); 320 | }); 321 | -------------------------------------------------------------------------------- /src/image/05_string.js: -------------------------------------------------------------------------------- 1 | function String(value) { 2 | if (this === @@ $global @@) { 3 | return @@ JS::toString(`value, $global) @@; 4 | } 5 | 6 | var s = {}; 7 | 8 | @@ `s->prototype = `String->properties['prototype']; @@ 9 | @@ `s->class = 'String'; @@ 10 | @@ `s->extensible = true; @@ 11 | @@ `s->value = JS::toString(`value, $global); @@ 12 | 13 | return s; 14 | } 15 | 16 | String.fromCharCode = function (c) { 17 | var s = ""; 18 | 19 | for (var i = 0, l = arguments.length, arg; i < l; ++i) { 20 | arg = arguments[i]; 21 | s += @@ chr(JS::toNumber(`arg, $global)) @@; 22 | } 23 | 24 | return s; 25 | }; 26 | 27 | String.prototype = {}; 28 | @@ `String->properties['prototype']->prototype = `Object->properties['prototype']; @@ 29 | @@ `String->properties['prototype']->class = 'String'; @@ 30 | @@ `String->properties['prototype']->extensible = TRUE; @@ 31 | String.prototype.constructor = String; 32 | 33 | String.prototype.toString = function () { 34 | if (@@ !isset($leThis->class) || $leThis->class !== 'String' @@) { 35 | throw new TypeError("String.prototype.toString(): not generic"); 36 | } 37 | 38 | return @@ $leThis->value @@; 39 | }; 40 | 41 | String.prototype.valueOf = String.prototype.toString; 42 | 43 | String.prototype.charAt = function (pos) { 44 | pos = @@ JS::toNumber(`pos, $global) @@; 45 | 46 | if (pos < 0 || pos >= this.length) { 47 | return ""; 48 | } 49 | 50 | return @@ (string) substr($leThis->value, `pos, 1) @@; 51 | }; 52 | 53 | String.prototype.charCodeAt = function (pos) { 54 | pos = @@ JS::toNumber(`pos, $global) @@; 55 | 56 | if (pos < 0 || pos >= this.length) { 57 | return NaN; 58 | } 59 | 60 | var c = this.charAt(pos); 61 | 62 | return @@ ord(`c) @@; 63 | }; 64 | 65 | String.prototype.concat = function (string) { 66 | var returnString = @@ $leThis->value @@; 67 | 68 | for (var i = 0, l = arguments.length; i < l; ++i) { 69 | returnString += arguments[i]; 70 | } 71 | 72 | return returnString; 73 | }; 74 | 75 | String.prototype.indexOf = function (search, position) { 76 | if (position === undefined) { 77 | position = 0; 78 | } 79 | 80 | var offset = Math.min(Math.max(position, 0), @@ strlen($leThis->value) @@), 81 | ret = @@ strpos($leThis->value, JS::toString(`search, $global), `offset) @@; 82 | 83 | if (ret === false) { 84 | return -1; 85 | } 86 | 87 | return ret; 88 | }; 89 | 90 | String.prototype.lastIndexOf = function (search, position) { 91 | if (position === undefined) { 92 | position = 0; 93 | } 94 | 95 | var offset = Math.min(Math.max(position, 0), @@ strlen($leThis->value) @@), 96 | ret = @@ strrpos($leThis->value, JS::toString(`search, $global), `offset) @@; 97 | 98 | if (ret === false) { 99 | return -1; 100 | } 101 | 102 | return ret; 103 | }; 104 | 105 | String.prototype.localeCompare = function (that) { 106 | return @@ $leThis->value === JS::toString(`that, $global) @@; 107 | }; 108 | 109 | String.prototype.match = function (regexp) { 110 | if (!@@ (is_object(`regexp) && isset(`regexp->class) && `regexp->class === 'RegExp') @@) { 111 | regexp = new RegExp(regexp); 112 | } 113 | 114 | if (!regexp.global) { 115 | return regexp.exec(@@ $leThis->value @@); 116 | } 117 | 118 | var returnArray = [], n = 0, match, currentLastIndex; 119 | 120 | regexp.lastIndex = 0; 121 | 122 | for (var previousLastIndex = 0, lastMatch = true; lastMatch; ++n) { 123 | match = regexp.exec(@@ $leThis->value @@); 124 | lastMatch = !!match; 125 | 126 | if (match) { 127 | if (regexp.lastIndex === previousLastIndex) { 128 | previousLastIndex = regexp.lastIndex = regexp.lastIndex + 1; 129 | } else { 130 | previousLastIndex = regexp.lastIndex; 131 | } 132 | 133 | returnArray.push(match[0]); 134 | } 135 | } 136 | 137 | if (n === 0) { 138 | return null; 139 | } 140 | 141 | return returnArray; 142 | }; 143 | 144 | String.prototype.replace = function (search, replace) { 145 | if (@@ !is_object(`search) || !isset(`search->class) || `search->class !== 'RegExp' @@) { 146 | search = new RegExp(@@ preg_quote(JS::toString(`search, $global), '/') @@); 147 | } 148 | 149 | var match, index, lastIndex = 0, 150 | thisString = @@ $leThis->value @@, newString = "", s; 151 | 152 | while ((match = search.exec(thisString)) !== null) { 153 | index = match.index; 154 | newString += s = thisString.substring(lastIndex, index); 155 | lastIndex = index + match[0].length; 156 | 157 | if (typeof replace === "function") { 158 | match.push(match.index); 159 | match.push(match.input); 160 | delete match.index; 161 | delete match.input; 162 | 163 | newString += s = String(replace.apply(undefined, match)); 164 | 165 | } else { 166 | newString += replace.replace(/\$([$&]|[0-9]|[0-9][0-9])/g, function (_, c) { 167 | if (c === "$") { 168 | return "$"; 169 | 170 | } else if (c === "&") { 171 | return match[0]; 172 | 173 | } else { 174 | return match[Number(c)]; 175 | } 176 | }); 177 | } 178 | 179 | if (!search.global) { 180 | break; 181 | } 182 | } 183 | 184 | newString += thisString.substring(lastIndex, thisString.length); 185 | 186 | return newString; 187 | }; 188 | 189 | String.prototype.search = function (search) { 190 | if (!@@ (is_object(`search) && isset(`search->class) && `search->class === 'RegExp') @@) { 191 | search = new RegExp(search); 192 | } 193 | 194 | var savedLastIndex = search.lastIndex, 195 | match = search.exec(@@ $leThis->value @@); 196 | 197 | search.lastIndexOf = savedLastIndex; 198 | 199 | if (match) { 200 | return match.index; 201 | } 202 | 203 | return -1; 204 | }; 205 | 206 | String.prototype.slice = function (start, end) { 207 | var length = @@ strlen($leThis->value) @@; 208 | 209 | if (start === undefined) { 210 | start = 0; 211 | } 212 | 213 | if (end === undefined) { 214 | end = length; 215 | } 216 | 217 | if (start < 0) { 218 | start = Math.max(start + length, 0); 219 | } 220 | 221 | if (end < 0) { 222 | end = Math.max(end + length, 0); 223 | } 224 | 225 | return @@ (string) substr($leThis->value, `start, max(`end - `start, 0)) @@; 226 | }; 227 | 228 | String.prototype.split = function (separator, limit) { 229 | if (separator === undefined) { 230 | return [@@ $leThis->value @@]; 231 | } 232 | 233 | if (separator === "") { 234 | var returnArray = []; 235 | 236 | for (var i = 0, l = this.length; i < l; ++i) { 237 | returnArray.push(@@ (string) substr($leThis->value, `i, 1) @@); 238 | } 239 | 240 | return returnArray; 241 | } 242 | 243 | if (typeof separator === "string") { 244 | separator = new RegExp(@@ preg_quote(JS::toString(`separator, $global), '/') @@, 'g'); 245 | } 246 | 247 | var returnArray = [], match, lastIndex = 0, index, thisString = @@ $leThis->value @@, 248 | savedLastIndex = separator.lastIndex, savedGlobal = separator.global; 249 | 250 | @@ `separator->properties['global'] = TRUE; @@ 251 | 252 | if (limit === undefined) { 253 | limit = Infinity; 254 | } 255 | 256 | while ((match = separator.exec(thisString)) && --limit > 0 && lastIndex < this.length) { 257 | if (match[0] === '' && match.index === lastIndex) { 258 | returnArray.push(this.substring(lastIndex, lastIndex + 1)); 259 | ++lastIndex; 260 | ++separator.lastIndex; 261 | 262 | } else { 263 | returnArray.push(this.substring(lastIndex, match.index)); 264 | 265 | for (var i = 1, l = match.length; i < l; ++i) { 266 | returnArray.push(match[i]); 267 | } 268 | 269 | lastIndex = match.index + match[0].length; 270 | } 271 | } 272 | 273 | if (lastIndex < this.length) { 274 | returnArray.push(this.substring(lastIndex, this.length)); 275 | } 276 | 277 | @@ `separator->properties['global'] = `savedGlobal; @@ 278 | separator.lastIndex = savedLastIndex; 279 | 280 | return returnArray; 281 | }; 282 | 283 | String.prototype.substring = function (start, end) { 284 | var length = @@ strlen($leThis->value) @@; 285 | 286 | if (start === undefined) { 287 | start = 0; 288 | } 289 | 290 | if (end === undefined) { 291 | end = length; 292 | } 293 | 294 | if (start < 0) { 295 | start = Math.max(start + length, 0); 296 | } 297 | 298 | if (end < 0) { 299 | end = Math.max(end + length, 0); 300 | } 301 | 302 | start = Math.min(start, length); 303 | end = Math.min(end, length); 304 | 305 | var from = Math.min(start, end), to = Math.max(start, end); 306 | 307 | return @@ (string) substr($leThis->value, `from, `to - `from) @@; 308 | }; 309 | 310 | // non-standard 311 | String.prototype.substr = function (start, length) { 312 | if (length === undefined) { 313 | return @@ substr($leThis->value, `start) @@; 314 | } 315 | 316 | return @@ (string) substr($leThis->value, `start, `length) @@; 317 | }; 318 | 319 | String.prototype.toLowerCase = function () { 320 | return @@ strtolower($leThis->value) @@; 321 | }; 322 | 323 | String.prototype.toLocaleLowerCase = String.prototype.toLowerCase; 324 | 325 | String.prototype.toUpperCase = function () { 326 | return @@ strtoupper($leThis->value) @@; 327 | }; 328 | 329 | String.prototype.toLocaleUpperCase = String.prototype.toUpperCase; 330 | 331 | String.prototype.trim = function () { 332 | return @@ trim($leThis->value) @@; 333 | }; 334 | -------------------------------------------------------------------------------- /src/image/04_array.js: -------------------------------------------------------------------------------- 1 | function Array() { 2 | var array = {}; 3 | 4 | @@ `array->prototype = `Array->properties['prototype']; @@ 5 | @@ `array->class = 'Array'; @@ 6 | 7 | array.length = 0; 8 | 9 | @@ `array->attributes['length'] = JS::WRITABLE; @@ 10 | 11 | if (arguments.length === 1 && typeof arguments[0] === "number") { 12 | var len = arguments[0]; 13 | 14 | if (@@ (((string) intval(`len)) !== ((string) `len)) || `len < 0 @@) { 15 | throw new RangeError("Array(): Given array length is not legal."); 16 | } 17 | 18 | array.length = len; 19 | 20 | return array; 21 | } 22 | 23 | for (var i = 0, l = arguments.length; i < l; ++i) { 24 | array[i] = arguments[i]; 25 | } 26 | 27 | return array; 28 | } 29 | 30 | Array.isArray = function (arg) { 31 | if (arg === null || typeof arg !== "object") { 32 | return false; 33 | } 34 | 35 | return @@ isset(`arg->class) && `arg->class === 'Array' @@; 36 | }; 37 | 38 | Array.prototype = {}; 39 | @@ `Array->prototype = `Array->properties['prototype']; @@ 40 | @@ `Array->properties['prototype']->prototype = `Object->properties['prototype']; @@ 41 | @@ `Array->properties['prototype']->class = 'Array'; @@ 42 | @@ `Array->properties['prototype']->extensible = TRUE; @@ 43 | Array.prototype.constructor = Array; 44 | Array.prototype.length = 0; 45 | 46 | Array.prototype.toString = function () { 47 | return this.join(); 48 | }; 49 | 50 | Array.prototype.toLocaleString = Array.prototype.toString; 51 | 52 | Array.prototype.concat = function (item) { 53 | var newArray = []; 54 | 55 | for (var i = 0, l = this.length; i < l; ++i) { 56 | newArray.push(this[i]); 57 | } 58 | 59 | for (var i = 0, l = arguments.length; i < l; ++i) { 60 | if (Array.isArray(arguments[i])) { 61 | for (var j = 0, m = arguments[i].length; j < m; ++j) { 62 | newArray.push(arguments[i][j]); 63 | } 64 | 65 | } else { 66 | newArray.push(arguments[i]); 67 | } 68 | } 69 | 70 | return newArray; 71 | }; 72 | 73 | Array.prototype.join = function (separator) { 74 | if (separator === undefined) { 75 | separator = ","; 76 | } 77 | 78 | var string = ""; 79 | 80 | if (this.length < 1) { 81 | return ""; 82 | } 83 | 84 | for (var i = 0, l = this.length, item; i < l; ++i) { 85 | if (i !== 0) { 86 | string += separator; 87 | } 88 | 89 | item = this[i]; 90 | 91 | if (item !== undefined && item !== null) { 92 | string += @@ JS::toString(`item, $global) @@; 93 | } 94 | } 95 | 96 | return string; 97 | }; 98 | 99 | Array.prototype.pop = function () { 100 | if (this.length < 1) { 101 | return undefined; 102 | } 103 | 104 | var item = this[this.length - 1], len = this.length; 105 | delete this[this.length - 1]; 106 | this.length = len - 1; 107 | 108 | return item; 109 | }; 110 | 111 | Array.prototype.push = function (item) { 112 | for (var i = 0, l = arguments.length; i < l; ++i) { 113 | this[this.length] = arguments[i]; 114 | } 115 | 116 | return this.length; 117 | }; 118 | 119 | Array.prototype.reverse = function () { 120 | for (var l = 0, r = this.length - 1; l < r; ++l, --r) { 121 | var tmp = this[l]; 122 | this[l] = this[r]; 123 | this[r] = tmp; 124 | } 125 | 126 | return this; 127 | }; 128 | 129 | Array.prototype.shift = function () { 130 | if (this.length < 1) { 131 | return undefined; 132 | } 133 | 134 | var item = this[0], l = this.length; 135 | 136 | for (var i = 0; i < l; ++i) { 137 | this[i] = this[i + 1]; 138 | } 139 | 140 | this.length = l - 1; 141 | 142 | return item; 143 | }; 144 | 145 | Array.prototype.slice = function (start, end) { 146 | if (start === undefined) { 147 | start = 0; 148 | } 149 | 150 | if (start < 0) { 151 | start = Math.max(this.length + start, 0); 152 | } 153 | 154 | if (end === undefined) { 155 | end = this.length; 156 | } 157 | 158 | if (end < 0) { 159 | end = this.length + end; 160 | } 161 | 162 | var newArray = []; 163 | 164 | for (; start < end; ++start) { 165 | newArray.push(this[start]); 166 | } 167 | 168 | return newArray; 169 | }; 170 | 171 | Array.prototype.sort = function (compareFn) { 172 | compareFn = compareFn || function (a, b) { 173 | if (typeof a === "string" || typeof b === "string") { 174 | a = @@ JS::toString(`a, $global) @@; 175 | b = @@ JS::toString(`b, $global) @@; 176 | 177 | if (a < b) { 178 | return -1; 179 | } else if (a > b) { 180 | return 1; 181 | } else { 182 | return 0; 183 | } 184 | 185 | } else if (typeof a === "number" || typeof b === "number") { 186 | a = @@ JS::toNumber(`a, $global) @@; 187 | b = @@ JS::toNumber(`b, $global) @@; 188 | 189 | return a - b; 190 | } 191 | 192 | throw new TypeError("Unsortable values in array."); 193 | }; 194 | 195 | if (this.length < 2) { 196 | return this; 197 | } 198 | 199 | var pivot, smaller = [], larger = []; 200 | 201 | pivot = this[0]; 202 | 203 | for (var i = 1, l = this.length; i < l; ++i) { 204 | if (compareFn(this[i], pivot) < 0) { 205 | smaller.push(this[i]); 206 | } else { 207 | larger.push(this[i]); 208 | } 209 | } 210 | 211 | var sorted = smaller.sort(compareFn).concat([pivot], larger.sort(compareFn)); 212 | 213 | for (var i = 0, l = sorted.length; i < l; ++i) { 214 | this[i] = sorted[i]; 215 | } 216 | 217 | return this; 218 | }; 219 | 220 | Array.prototype.splice = function (start, deleteCount) { 221 | var i, l, item, returnArray = []; 222 | 223 | @@ $A = array(); @@ 224 | 225 | for (i = 0, l = this.length, item; i < l; ++i) { 226 | item = this[i]; 227 | @@ $A[] = `item; @@ 228 | delete this[i]; 229 | } 230 | 231 | if (deleteCount === undefined) { 232 | @@ $C = array_splice($A, `start); @@ 233 | } else { 234 | @@ $B = array(); @@ 235 | 236 | for (i = 2, l = arguments.length; i < l; ++i) { 237 | item = arguments[i]; 238 | @@ $B[] = `item; @@ 239 | } 240 | 241 | @@ $C = array_splice($A, `start, `deleteCount, $B); @@ 242 | } 243 | 244 | this.length = 0; 245 | 246 | @@ foreach ($A as $V) { @@ 247 | this.push(@@ $V @@); 248 | @@ } @@ 249 | 250 | @@ foreach ($C as $I => $V) { @@ 251 | returnArray.push(@@ $V @@); 252 | @@ } @@ 253 | 254 | return returnArray; 255 | }; 256 | 257 | Array.prototype.unshift = function (item) { 258 | for (var i = this.length - 1; i >= 0; --i) { 259 | this[i + arguments.length] = this[i]; 260 | } 261 | 262 | for (var i = 0, l = arguments.length; i < l; ++i) { 263 | this[i] = arguments[i]; 264 | } 265 | 266 | return this.length; 267 | }; 268 | 269 | Array.prototype.indexOf = function (search, from) { 270 | from = from || 0; 271 | 272 | if (from < 0) { 273 | from = Math.max(this.length + from, 0); 274 | } 275 | 276 | for (var i = from, l = this.length; i < l; ++i) { 277 | if (this[i] === search) { 278 | return i; 279 | } 280 | } 281 | 282 | return -1; 283 | }; 284 | 285 | Array.prototype.lastIndexOf = function (search, from) { 286 | from = from || 0; 287 | 288 | if (from < 0) { 289 | from = Math.max(this.length + from, 0); 290 | } 291 | 292 | if (this.length < 1 || from >= this.length) { 293 | return -1; 294 | } 295 | 296 | for (var i = this.length - 1; i >= from; --i) { 297 | if (this[i] == search) { 298 | return i; 299 | } 300 | } 301 | 302 | return -1; 303 | }; 304 | 305 | Array.prototype.every = function (callbackFn) { 306 | var thisArg; 307 | 308 | if (arguments.length > 1) { 309 | thisArg = arguments[1]; 310 | } 311 | 312 | for (var i = 0, l = this.length; i < l; ++i) { 313 | if (!callbackFn.call(thisArg, this[i], i, this)) { 314 | return false; 315 | } 316 | } 317 | 318 | return true; 319 | }; 320 | 321 | Array.prototype.some = function (callbackFn) { 322 | var thisArg; 323 | 324 | if (arguments.length > 1) { 325 | thisArg = arguments[1]; 326 | } 327 | 328 | for (var i = 0, l = this.length; i < l; ++i) { 329 | if (callbackFn.call(thisArg, this[i], i, this)) { 330 | return true; 331 | } 332 | } 333 | 334 | return false; 335 | }; 336 | 337 | Array.prototype.forEach = function (callbackFn) { 338 | var thisArg; 339 | 340 | if (arguments.length > 1) { 341 | thisArg = arguments[1]; 342 | } 343 | 344 | for (var i = 0, l = this.length; i < l; ++i) { 345 | callbackFn.call(thisArg, this[i], i, this); 346 | } 347 | }; 348 | 349 | Array.prototype.map = function (callbackFn) { 350 | var thisArg, returnArray = []; 351 | 352 | if (arguments.length > 1) { 353 | thisArg = arguments[1]; 354 | } 355 | 356 | for (var i = 0, l = this.length; i < l; ++i) { 357 | returnArray.push(callbackFn.call(thisArg, this[i], i, this)); 358 | } 359 | 360 | return returnArray; 361 | }; 362 | 363 | Array.prototype.filter = function (callbackFn) { 364 | var thisArg, returnArray = []; 365 | 366 | if (arguments.length > 1) { 367 | thisArg = arguments[1]; 368 | } 369 | 370 | for (var i = 0, l = this.length; i < l; ++i) { 371 | if (callbackFn.call(thisArg, this[i], i, this)) { 372 | returnArray.push(this[i]); 373 | } 374 | } 375 | 376 | return returnArray; 377 | }; 378 | 379 | Array.prototype.reduce = function (callbackFn) { 380 | if (this.length < 1 && arguments.length < 2) { 381 | throw new TypeError("Array.prototype.reduce(): Array empty, no initial value."); 382 | } 383 | 384 | var value, i; 385 | 386 | if (arguments.length > 1) { 387 | value = arguments[1]; 388 | i = 0; 389 | 390 | } else { 391 | for (i = 0; i < this.length && this[i] === undefined; ++i); 392 | value = this[i]; 393 | i = i + 1; 394 | } 395 | 396 | for (var l = this.length; i < l; ++i) { 397 | if (this[i] !== undefined) { 398 | value = callbackFn(value, this[i], i, this); 399 | } 400 | } 401 | 402 | return value; 403 | }; 404 | 405 | Array.prototype.reduceRight = function (callbackFn) { 406 | if (this.length < 1 && arguments.length < 2) { 407 | throw new TypeError("Array.prototype.reduceRight(): Array empty, no initial value."); 408 | } 409 | 410 | var value, i; 411 | 412 | if (arguments.length > 1) { 413 | value = arguments[1]; 414 | i = this.length - 1; 415 | 416 | } else { 417 | for (i = this.length - 1; i >= 0 && this[i] === undefined; --i); 418 | value = this[i]; 419 | i = i - 1; 420 | } 421 | 422 | for (; i >= 0; --i) { 423 | if (this[i] !== undefined) { 424 | value = callbackFn(value, this[i], i, this); 425 | } 426 | } 427 | 428 | return value; 429 | }; 430 | -------------------------------------------------------------------------------- /Jakefile: -------------------------------------------------------------------------------- 1 | var buildDir = __dirname + "/build", 2 | srcDir = __dirname + "/src", 3 | testDir = __dirname + "/test"; 4 | utilDir = __dirname + "/util"; 5 | 6 | task("all", "clean, build image, binary, jake, jtest, and run tests", 7 | "clean", "build", "build:binary", "build:jake", "build:jtest", "test"); 8 | 9 | task("build", "build parser, compiler and image", 10 | "build:image", 11 | result(buildDir + "/*.php"), 12 | srcDir + "/JSInterpreter.php", 13 | function () { 14 | run("cp " + srcDir + "/JSInterpreter.php " + buildDir + "/"); 15 | }); 16 | 17 | task("build:parser", "build JSParser from phpeg source; argument , parser for PHP 5.2", 18 | result(buildDir + "/JSParser.php"), 19 | srcDir + "/JSParser.phpeg", 20 | utilDir + "/phpeg", 21 | function (usePHPGenerator) { 22 | run(utilDir + "/phpeg " + (usePHPGenerator ? "-t php " : "") + 23 | srcDir + "/JSParser.phpeg " + buildDir + "/JSParser.php"); 24 | }); 25 | 26 | task("build:compiler", "build JSCompiler from phptw source", 27 | result(buildDir + "/JSCompiler.php"), 28 | srcDir + "/JSCompiler.phptw", 29 | utilDir + "/phptwc", 30 | function () { 31 | run(utilDir + "/phptwc " + srcDir + "/JSCompiler.phptw " + buildDir + "/JSCompiler.php"); 32 | }); 33 | 34 | task("build:dumper", "build JSDumper from phptw source", 35 | result(buildDir + "/JSDumper.php"), 36 | srcDir + "/JSDumper.phptw", 37 | utilDir + "/phptwc", 38 | function () { 39 | run(utilDir + "/phptwc " + srcDir + "/JSDumper.phptw " + buildDir + "/JSDumper.php"); 40 | }); 41 | 42 | task("build:image", "build image.php from src/image/*.js; argument , image will be built with it", 43 | "build:parser", "build:compiler", "build:dumper", 44 | result(buildDir + "/image.php"), 45 | srcDir + "/image/*.js", 46 | __filename, 47 | function (loader) { 48 | @@ require_once `utilDir . "/shrink.php"; @@ 49 | 50 | var parse = PHP.cls("JSParser")(), compile = PHP.cls("JSCompiler")(), 51 | compileOptions = { force: true, generate: "object", dumpFunctions: false } 52 | code = " array(), 'attributes' => array(), " + 55 | "'getters' => array(), 'setters' => array(), 'prototype' => NULL, 'up' => NULL, " + 56 | "'trace' => array());\n" + 57 | "$lines = explode(\"\\n\", file_get_contents(__FILE__));\n" + 58 | "echo \" PHP.fn("filemtime")(f)) 71 | { 72 | code += PHP.fn("file_get_contents")(buildFile + ".php"); 73 | mains += PHP.fn("file_get_contents")(buildFile + ".main.php"); 74 | return; 75 | } 76 | 77 | puts("[ COMPILING " + f + " ]"); 78 | 79 | var ast = parse(PHP.fn("file_get_contents")(f), { file: "/" + PHP.fn("basename")(f) }); 80 | 81 | if (!ast[0]) { 82 | fail("syntax error in " + f + "@" + ast[2].line + ":" + ast[2].column + 83 | ", expected " + ast[2].expected.join(", ")); 84 | } 85 | 86 | var compiled = compile(ast[1], compileOptions); 87 | 88 | for (var k in compiled.functions) { 89 | fileCode += compiled.functions[k] + "\n"; 90 | 91 | if (k !== compiled.main) { 92 | fileCode += "echo " + PHP.fn("var_export")( 93 | PHP.fn("shrink")("getStartLine() - 1, $r->getEndLine() - $r->getStartLine() + 1))), 6)) . \"\\n\"; }\n"; 120 | 121 | code += "$newFunctions = get_defined_functions(); $newFunctions = $newFunctions['user'];\n"; 122 | code += "foreach (array_diff($newFunctions, $functions) as $fn) {" + 123 | "$r = new ReflectionFunction($fn); echo ltrim(substr(shrink('getStartLine() - 1, $r->getEndLine() - $r->getStartLine() + 1))), 6)) . \"\\n\"; }\n"; 125 | 126 | if (loader) { 127 | code += "JS::$loader = " + PHP.fn("var_export")(loader, true) + ";\n"; 128 | code += "JS::$functionTemplate->loaded = FALSE;\n"; 129 | } 130 | 131 | code += "$r = new ReflectionClass('JS');\n" + 132 | "echo 'foreach (unserialize(' . var_export(serialize($r->getStaticProperties()), TRUE) . ') " + 133 | "as $k => $v) {JS::$$k = $v;}', \"\\n\";\n"; 134 | 135 | PHP.fn("file_put_contents")(buildDir + "/mkimage.php", code); 136 | 137 | run("php -l " + buildDir + "/mkimage.php"); 138 | run("php " + buildDir + "/mkimage.php > " + buildDir + "/newimage.php"); 139 | run("php -l " + buildDir + "/newimage.php"); 140 | 141 | puts("[ CHECKING NEW IMAGE VALIDITY ]"); 142 | var lastLine = false; 143 | PHP.fn("file_get_contents")(buildDir + "/newimage.php").split(/\n/).forEach(function (line, n) { 144 | if (line === "" || line.match(/^(function|class|foreach \(unserialize|<\?php)/) !== null) { 145 | if (line.match(/^foreach/) && lastLine) { 146 | fail("there can be only one unserialization of image, second one on line #" + 147 | (n + 1) + ", " + buildDir + "/newimage.php is not valid"); 148 | } 149 | 150 | if (line.match(/^foreach/)) { 151 | lastLine = true; 152 | } 153 | 154 | return; 155 | } 156 | 157 | fail("line #" + (n + 1) + " of " + buildDir + "/newimage.php is not valid"); 158 | }); 159 | 160 | run("mv " + buildDir + "/newimage.php " + buildDir + "/image.php"); 161 | run("rm " + buildDir + "/mkimage.php"); 162 | }); 163 | 164 | task("build:binary", "build interpreter", 165 | "build", 166 | result(utilDir + "/js2php-interpreter"), 167 | buildDir + "/*", 168 | __filename, 169 | function () { 170 | var code = "#!/usr/bin/php\n 2 || ($argv === 2 && ($argv[1] === '-h' || $argv[1] === '--help'))) {\n" + 177 | " die(\"usage: {$argv[0]} [ -h ] [ ]\");\n" + 178 | "}\n" + 179 | "\n" + 180 | "$file = 'php://stdin';\n" + 181 | "if (isset($argv[1])) {\n" + 182 | " $file = $argv[1];\n" + 183 | "}\n" + 184 | "\n" + 185 | "$interpreter = new JSInterpreter(file_get_contents($file), $file);\n" + 186 | "$interpreter->run();\n"; 187 | 188 | PHP.fn("file_put_contents")(utilDir + "/js2php-interpreter", code); 189 | run("chmod +x " + utilDir + "/js2php-interpreter"); 190 | }); 191 | 192 | task("build:jake", "build jake util binary", 193 | "build", 194 | result(utilDir + "/jake"), 195 | utilDir + "/jake.d/*.js", 196 | buildDir + "/*.php", 197 | __filename, 198 | function () { 199 | var code = "#!/usr/bin/php\n/prelude.js" }); 212 | if (!ast[0]) { 213 | fail("syntax error in prelude.js@" + ast[2].line + ":" + ast[2].column + 214 | ", expected " + ast[2].expected.join(", ")); 215 | } 216 | code += "function prelude() {" + compile(ast[1], { force: true }) + "} call_user_func(prelude(), JS::$global);"; 217 | 218 | code += "$i = new JSInterpreter(file_get_contents(getcwd() . '/Jakefile'), getcwd() . '/Jakefile'); $i->run();\n"; 219 | 220 | puts("[ COMPILING main.js ]"); 221 | 222 | ast = parse(PHP.fn("file_get_contents")(utilDir + "/jake.d/main.js"), { file: "/main.js" }); 223 | if (!ast[0]) { 224 | fail("syntax error in main.js@" + ast[2].line + ":" + ast[2].column + 225 | ", expected " + ast[2].expected.join(", ")); 226 | } 227 | code += "function main() {" + compile(ast[1], { force: true }) + "} call_user_func(main(), JS::$global);"; 228 | 229 | code += "} catch (JSException $e) {\n" + 230 | "if (isset($e->value->properties)) {\n" + 231 | "echo (isset($e->value->properties['name']) ? $e->value->properties['name'] . \": \" : \"\"), $e->value->properties['message'], ' in ', $e->value->properties['file'], '@', $e->value->properties['line'], ':', $e->value->properties['column'], \"\\n\";\n" + 232 | "} else {\n" + 233 | "echo $e->value->getMessage(), \"\\n\";\n" + 234 | "}\n" + 235 | "echo implode(\"\\n\", array_map(function ($t) { return ' ' . $t[0] . '@' . $t[1] . ':' . $t[2]; }, array_reverse(JS::$global->trace))), \"\\n\";\n" + 236 | "}\n"; 237 | 238 | PHP.fn("file_put_contents")(utilDir + "/jake", code); 239 | run("chmod +x " + utilDir + "/jake"); 240 | }); 241 | 242 | task("build:jtest", "build jtest util binary", 243 | "build", 244 | result(utilDir + "/jtest"), 245 | utilDir + "/jtest.d/*.js", 246 | buildDir + "/*.php", 247 | __filename, 248 | function () { 249 | var code = "#!/usr/bin/php\n/main.js" }); 259 | if (!ast[0]) { 260 | fail("syntax error in main.js@" + ast[2].line + ":" + ast[2].column + 261 | ", expected " + ast[2].expected.join(", ")); 262 | } 263 | 264 | code += "JS::$global->trace = array(array('/main.js', NULL, NULL)); JS::$global->trace_sp = 0;"; 265 | code += "function main() {" + compile(ast[1], { force: true }) + "} call_user_func(main(), JS::$global);"; 266 | 267 | PHP.fn("file_put_contents")(utilDir + "/jtest", code); 268 | run("chmod +x " + utilDir + "/jtest"); 269 | }); 270 | 271 | task("clean", "clean build/", 272 | function () { 273 | PHP.fn("glob")(buildDir + "/*.php").forEach(function (f) { 274 | run("rm " + f); 275 | }); 276 | }); 277 | 278 | task("test", "run tests", 279 | "build", "build:jtest", 280 | function () { 281 | run(utilDir + "/jtest " + testDir); 282 | }); 283 | --------------------------------------------------------------------------------