From ccabd98e5444cab7fd498b9e3ca180e35f6cd528 Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Mon, 4 Feb 2019 15:41:22 -0500 Subject: [PATCH 01/52] Infrastructure to pass options to js2php --- index.js | 3 ++- js2php | 7 ++++--- test/generate.js | 5 +++-- test/suite.js | 5 +++-- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/index.js b/index.js index 05e73ce..148b536 100644 --- a/index.js +++ b/index.js @@ -3,7 +3,8 @@ var core = require('./core'), utils = require('./utils'), espree = require('espree'); -module.exports = function(code) { +module.exports = function(code, options) { + options = options || {}; var ast = espree.parse(code, { loc : true, range : true, diff --git a/js2php b/js2php index 8a424fd..03d089f 100755 --- a/js2php +++ b/js2php @@ -1,7 +1,8 @@ #!/usr/bin/env node var fs = require('fs'), - js2php = require('./index.js'); + js2php = require('./index.js'), + options = {}; if (process.stdin.isTTY) { @@ -9,7 +10,7 @@ if (process.stdin.isTTY) { console.log(require('./package.json').version); } else { - console.log( js2php(fs.readFileSync(process.argv[2]).toString()) ); + console.log( js2php(fs.readFileSync(process.argv[2], 'utf8'), options) ); } } else { @@ -17,6 +18,6 @@ if (process.stdin.isTTY) { process.stdin.resume(); process.stdin.on('data', function(data) { code += data; }); process.stdin.on('end', function() { - console.log( js2php(code) ); + console.log( js2php(code, options) ); }); } diff --git a/test/generate.js b/test/generate.js index db67f3e..165a102 100644 --- a/test/generate.js +++ b/test/generate.js @@ -24,7 +24,8 @@ if (!target) { for(var i=0;i '" + e.message + "'", e.stack); diff --git a/test/suite.js b/test/suite.js index 8492883..de8938e 100644 --- a/test/suite.js +++ b/test/suite.js @@ -14,9 +14,10 @@ describe('js2php', function(){ (function(i) { it(sources[i][0], function(){ var js_source = fs.readFileSync(sources[i][0]).toString(), - php_source = fs.readFileSync(sources[i][1]).toString().trim(); + php_source = fs.readFileSync(sources[i][1]).toString().trim(), + options = {}; - assert.equal(js2php(js_source).trim(), php_source); + assert.equal(js2php(js_source, options).trim(), php_source); }) })(i) } From c1d6bed45db38c49de39a9248b3617524a2d750a Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Thu, 31 Jan 2019 08:41:26 -0800 Subject: [PATCH 02/52] Handle command-line arguments to js2php better --- js2php | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/js2php b/js2php index 03d089f..b866f91 100755 --- a/js2php +++ b/js2php @@ -4,13 +4,20 @@ var fs = require('fs'), js2php = require('./index.js'), options = {}; -if (process.stdin.isTTY) { - +while (/^-/.test(process.argv[2])) { if (process.argv[2]=="-v" || process.argv[2]=="--version") { - console.log(require('./package.json').version); - + console.warn(require('./package.json').version); + return; + } else { + console.warn("Unknown option:", process.argv.shift()); + } +} +if (process.stdin.isTTY || process.argv.length > 2) { + var result = js2php(fs.readFileSync(process.argv[2], 'utf8'), options); + if (process.argv.length < 4) { + console.log( result ); } else { - console.log( js2php(fs.readFileSync(process.argv[2], 'utf8'), options) ); + fs.writeFileSync(process.argv[3], result, 'utf8'); } } else { From 5ab3159a51aef2b56fd550d4553169b6366f82e7 Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Mon, 4 Feb 2019 15:57:04 -0500 Subject: [PATCH 03/52] Add option to use concise array syntax. --- index.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 148b536..be4f22b 100644 --- a/index.js +++ b/index.js @@ -5,6 +5,7 @@ var core = require('./core'), module.exports = function(code, options) { options = options || {}; + var useConciseArrays = !!options.conciseArrays; var ast = espree.parse(code, { loc : true, range : true, @@ -337,14 +338,20 @@ module.exports = function(code, options) { for (var i=0; i < node.properties.length; i++) { properties.push( visit(node.properties[i], node) ) } - content = "array(" + properties.join(", ") + ")"; + content = + (useConciseArrays ? "[" : "array(") + + properties.join(", ") + + (useConciseArrays ? "]" : ")"); } else if (node.type == "ArrayExpression") { var elements = []; for (var i=0; i < node.elements.length; i++) { elements.push( visit(node.elements[i], node) ) } - content = "array(" + elements.join(", ") + ")"; + content = + (useConciseArrays ? "[" : "array(") + + elements.join(", ") + + (useConciseArrays ? "]" : ")"); } else if (node.type == "Property") { var property = (node.key.type == 'Identifier') ? node.key.name : node.key.value; From 0ed2bbdb0d7a348768b855fd99c3370134118012 Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Mon, 28 Jan 2019 17:18:15 -0800 Subject: [PATCH 04/52] Refactor to match line breaks in original JS. --- core/array.js | 44 +- core/string.js | 7 +- index.js | 630 +++++++++++++++++++-------- test/fixtures/anonymous_function.php | 21 +- test/fixtures/array_index.php | 2 +- test/fixtures/arrow_functions.php | 10 +- test/fixtures/class.php | 120 +++-- test/fixtures/class_inheritance.php | 47 +- test/fixtures/closures.php | 15 +- test/fixtures/concat.php | 2 +- test/fixtures/conditionals.php | 29 +- test/fixtures/core_array.php | 8 +- test/fixtures/core_function.php | 30 +- test/fixtures/core_json.php | 21 +- test/fixtures/core_math.php | 1 + test/fixtures/core_number.php | 2 +- test/fixtures/core_string.php | 22 +- test/fixtures/date.php | 2 +- test/fixtures/exceptions.js | 4 +- test/fixtures/exceptions.php | 15 +- test/fixtures/expression.js | 3 +- test/fixtures/expression.php | 3 +- test/fixtures/failures.php | 7 +- test/fixtures/for_of.php | 13 +- test/fixtures/function.js | 4 +- test/fixtures/function.php | 14 +- test/fixtures/function_super.php | 18 +- test/fixtures/global_functions.php | 4 +- test/fixtures/iife.php | 14 +- test/fixtures/loops.php | 43 +- test/fixtures/namespaces.php | 21 +- test/fixtures/namespaces_use.php | 3 +- test/fixtures/regexp.php | 12 +- test/fixtures/simple.php | 7 +- test/fixtures/static_call.php | 15 +- test/fixtures/string_template.php | 16 +- 36 files changed, 836 insertions(+), 393 deletions(-) diff --git a/core/array.js b/core/array.js index e87a9b7..6772486 100644 --- a/core/array.js +++ b/core/array.js @@ -7,6 +7,8 @@ module.exports = { unshift: function(node) { var args = utils.clone(node.parent.arguments); node.parent.arguments = false; + if (args.length === 1) { args[0].suppressParens = true; } + args.unshift(node.parent.callee.object); return { type: 'CallExpression', @@ -14,7 +16,7 @@ module.exports = { type: 'Identifier', name: 'array_unshift', }, - arguments: [node.parent.callee.object, args[0]] + arguments: args, }; }, @@ -44,6 +46,8 @@ module.exports = { push: function(node) { var args = utils.clone(node.parent.arguments); node.parent.arguments = false; + if (args.length === 1) { args[0].suppressParens = true; } + args.unshift(node.parent.callee.object); return { type: 'CallExpression', @@ -51,7 +55,7 @@ module.exports = { type: 'Identifier', name: 'array_push', }, - arguments: [ node.parent.callee.object, args[0] ] + arguments: args, }; }, @@ -69,6 +73,7 @@ module.exports = { join: function(node) { var args = utils.clone(node.parent.arguments); node.parent.arguments = false; + args[0].suppressParens = true; return { type: 'CallExpression', @@ -80,6 +85,40 @@ module.exports = { }; }, + map: function(node) { + var args = utils.clone(node.parent.arguments); + node.parent.arguments = false; + args[0].suppressParens = true; + + return { + type: 'CallExpression', + callee: { + type: 'Identifier', + name: 'array_map', + }, + arguments: [ node.parent.callee.object, args[0] ] + }; + }, + + slice: function(node) { + // Second argument to array_slice is very different from Array#slice + if (node.parent.arguments.length !== 1) { return node; } + var args = utils.clone(node.parent.arguments); + args[0].suppressParens = true; + args.unshift(node.parent.callee.object); + + node.parent.arguments = false; + + return { + type: 'CallExpression', + callee: { + type: 'Identifier', + name: 'array_slice', + }, + arguments: args + }; + }, + splice: function(node) { var args = utils.clone(node.parent.arguments); args.unshift(node.parent.callee.object); @@ -101,6 +140,7 @@ module.exports = { args = utils.clone(node.parent.arguments); node.parent.arguments = false; + args[0].suppressParens = true; var targetDefinition = scope.get(node).getDefinition(node.parent.callee.object); if (utils.isString(node.parent.callee.object) || (targetDefinition && targetDefinition.dataType == "String")) { diff --git a/core/string.js b/core/string.js index 26c0bea..b135147 100644 --- a/core/string.js +++ b/core/string.js @@ -129,6 +129,8 @@ module.exports = { else if (args[0].value === '') { method = "str_split"; args = [args[1]]; + } else if (args.length == 2) { + args[0].suppressParens = true; } return { @@ -143,9 +145,9 @@ module.exports = { substr: function(node) { var args = utils.clone(node.parent.arguments); - args.unshift(node.parent.callee.object); - node.parent.arguments = false; + if (args.length === 1) { args[0].suppressParens = true; } + args.unshift(node.parent.callee.object); return { type: 'CallExpression', @@ -159,6 +161,7 @@ module.exports = { match: function(node) { var args = utils.clone(node.parent.arguments); + args[0].suppressParens = true; args.push(node.parent.callee.object); if(args[0].type === 'Literal') { diff --git a/index.js b/index.js index be4f22b..8634fbe 100644 --- a/index.js +++ b/index.js @@ -5,12 +5,13 @@ var core = require('./core'), module.exports = function(code, options) { options = options || {}; - var useConciseArrays = !!options.conciseArrays; + var useConciseArrays = (options.conciseArrays === false) ? false : true; var ast = espree.parse(code, { loc : true, range : true, tokens : true, comment : true, + attachComment: true, ecmaFeatures: { arrowFunctions: true, // enable parsing of arrow functions blockBindings: true, // enable parsing of let/const @@ -39,40 +40,205 @@ module.exports = function(code, options) { experimentalObjectRestSpread: true // allow experimental object rest/spread } }); + var tokenStartMap = Object.create(null), tokenEndMap = Object.create(null); + var locToKey = function(loc) { + return loc.line + '-' + loc.column; + }; + (function() { + var lines = code.split(/\n/g); + // slideFwd and slideBck skip over whitespace + var slideFwd = function(loc) { + loc = {line: loc.line, column: loc.column}; + while (true) { + var l = lines[loc.line - 1]; + var c = l[loc.column]; + if (!/[ \t\r\n]/.test(c)) break; + loc.column++; + if (loc.column >= l.length) { loc.column = 0; loc.line++; } + } + return loc; + }; + var slideBck = function(loc) { + loc = {line: loc.line, column: loc.column}; + while (true) { + var l = lines[loc.line - 1]; + var c = l[loc.column - 1]; + if (!/[ \t\r\n]/.test(c)) break; + loc.column--; + if (loc.column < 0) { + loc.line--; + loc.column = lines[loc.line - 1].length - 1; + } + } + return loc; + }; + ast.tokens.forEach(function(t) { + tokenStartMap[locToKey(slideBck(t.loc.start))] = t; + tokenEndMap[locToKey(slideFwd(t.loc.end))] = t; + }); + })(); var rootScope = scope.create(ast, scope.KIND_ROOT); + function Emitter() { + this.buffer = ''; + this.line = 1; + this.insertionPoints = []; + this.indentLevel = 0; + } + Emitter.prototype.toString = function() { return this.buffer; }; + Emitter.prototype.emit = function(str) { + this.buffer += str; + }; + Emitter.prototype.nl = function() { + this.buffer = this.buffer.replace(/[ \t]+$/, '') + '\n'; + for (var i = 0; i < this.indentLevel; i++) { + this.buffer += '\t'; + } + this.line++; + } + Emitter.prototype.block = function(open, f, close) { + var firstline = this.line; + this.emit(open); + this.incrIndent(); + f(); + if (this.line !== firstline) { this.ensureNl(); } + this.decrIndent(); + this.emit(close); + }; + Emitter.prototype.incrIndent = function() { + this.indentLevel++; + if (/\t$/.test(this.buffer)) { this.emit('\t'); } + } + Emitter.prototype.decrIndent = function() { + this.indentLevel--; + if (/\t$/.test(this.buffer)) { + this.buffer = this.buffer.slice(0, this.buffer.length - 1); + } + } + Emitter.prototype.locStart = function(node) { + if (node.leadingComments && node.leadingComments.length) { + node.leadingComments.forEach(function(c) { + this.emitComment(c); + }, this); + } + if (node.type === 'Program') { return; } + if (node && node.loc) { + while (node.loc.start.line > this.line) { + this.nl(); + } + this.line = node.loc.start.line; + } + // Hack for preserving parentheses from the original + if (!(node && node.suppressParens)) { + var startT = node && node.loc && tokenEndMap[locToKey(node.loc.start)]; + var endT = node && node.loc && tokenStartMap[locToKey(node.loc.end)]; + if ( + startT && startT.type==='Punctuator' && startT.value === '(' && + endT && endT.type==='Punctuator' && endT.value === ')' + ) { + this.emit('('); + } + } + }; + Emitter.prototype.locEnd = function(node) { + // Hack for preserving parentheses from the original + if (!(node && node.suppressParens)) { + var startT = node && node.loc && tokenEndMap[locToKey(node.loc.start)]; + var endT = node && node.loc && tokenStartMap[locToKey(node.loc.end)]; + if ( + startT && startT.type==='Punctuator' && startT.value === '(' && + endT && endT.type==='Punctuator' && endT.value === ')' + ) { + this.emit(')'); + } + } + if (node && node.loc) { + while (node.loc.end.line > this.line) { + this.nl(); + } + this.line = node.loc.end.line; + } + if (node.trailingComments && node.trailingComments.length) { + node.trailingComments.forEach(function(c) { + this.emitComment(c); + }, this); + } + }; + Emitter.prototype.emitComment = function(c) { + if (c.emitted) { return; } + this.locStart(c); + if (c.type==='Block') { + this.emit('/*'); + c.value.split(/\n/).forEach(function(l, idx) { + if (idx > 0) { this.nl(); } + this.emit(l.replace(/^\t+/, '')); + }, this); + this.emit('*/'); + } else { + this.emit('//' + c.value); this.nl(); + } + c.emitted = true; + }; + Emitter.prototype.isSemiLast = function() { + return this.buffer.match(/;\n?[ ]*$/); + } + Emitter.prototype.ensureSemi = function() { + if (!emitter.isSemiLast()) { this.emit(';'); } + }; + Emitter.prototype.ensureNl = function() { + if (!/\n[ \t]*$/.test(this.buffer)) { + this.nl(); + } + }; + Emitter.prototype.pushInsertionPoint = function() { + this.insertionPoints.push(this.buffer.length); + } + Emitter.prototype.popInsertionPoint = function() { + this.insertionPoints.pop(); + } + Emitter.prototype.insertAt = function(depth, str) { + var idx = this.insertionPoints.length - depth - 1; + var at = this.insertionPoints[idx]; + this.buffer = this.buffer.slice(0, at) + str + this.buffer.slice(at); + while (++idx < this.insertionPoints.length) { + this.insertionPoints[idx] += str.length; + } + } + var emitter = new Emitter(); function visit(node, parent) { - var content = "", semicolon = false; + var semicolon = false; // set parent node if (parent) { node.parent = parent; } + if (!node.suppressLoc) { emitter.locStart(node); } if (node.type == "Program" || node.type == "BlockStatement" || node.type == "ClassBody") { for (var i=0,length = node.body.length;i 0 && node.parent.type !== "Program") { - content += "use (" + using.map(function(identifier) { - return "&$" + identifier; - }).join(', ') + ") "; } + emitter.decrIndent(); + emitter.emit(') '); + emitter.pushInsertionPoint(); + emitter.block('{', function() { + emitter.pushInsertionPoint(); + + visit(node.body, node); /* function contents */ + var using = scope.get(node).using + // XXX I don't understand why I have to do this: + .filter(function(u) { return u!==undefined;}); + + // try to use parent's variables + // http://php.net/manual/pt_BR/functions.anonymous.php + if (using.length > 0 && node.parent.type !== "Program") { + emitter.insertAt(1, "use (" + using.map(function(identifier) { + return "&$" + identifier; + }).join(', ') + ") "); + } - content += "{\n"; - - // workaround when scope doesn't allow to have the `use` keyword. - if (node.parent.type === "Program") { - content += using.map(function(identifier) { - return `global $${identifier};`; - }).join("\n"); - } + // workaround when scope doesn't allow to have the `use` keyword. + if (node.parent.type === "Program") { + emitter.insertAt(0, using.map(function(identifier) { + return `\n\tglobal $${identifier};`; + }).join('')); + } - if (node.body.type === 'BinaryExpression') { - // x => x * 2 - content += "return " + func_contents + ";\n"; - } else { - content += func_contents; - } - content += "}\n"; + if (node.body.type === 'BinaryExpression') { + // x => x * 2 + emitter.insertAt(0, 'return '); + emitter.emit(';'); + } + }, '}'); + emitter.popInsertionPoint(); + emitter.popInsertionPoint(); } else if (node.type == "ObjectExpression") { - var properties = []; - for (var i=0; i < node.properties.length; i++) { - properties.push( visit(node.properties[i], node) ) - } - content = - (useConciseArrays ? "[" : "array(") + - properties.join(", ") + - (useConciseArrays ? "]" : ")"); + emitter.block(useConciseArrays ? '[' : 'array(', function() { + for (var i=0; i < node.properties.length; i++) { + visit(node.properties[i], node); + if ((i+1) < node.properties.length) { emitter.emit(', '); } + } + }, useConciseArrays ? ']' : ')'); } else if (node.type == "ArrayExpression") { - var elements = []; - for (var i=0; i < node.elements.length; i++) { - elements.push( visit(node.elements[i], node) ) - } - content = - (useConciseArrays ? "[" : "array(") + - elements.join(", ") + - (useConciseArrays ? "]" : ")"); + emitter.block(useConciseArrays ? '[' : 'array(', function() { + for (var i=0; i < node.elements.length; i++) { + visit(node.elements[i], node); + if ((i+1) < node.elements.length) { emitter.emit(', '); } + } + }, useConciseArrays ? ']' : ')'); } else if (node.type == "Property") { var property = (node.key.type == 'Identifier') ? node.key.name : node.key.value; - content = '"'+property+'" => ' + visit(node.value, node); + emitter.emit('"'+property+'" => '); + visit(node.value, node); } else if (node.type == "ReturnStatement") { semicolon = true; - content = "return"; + emitter.emit('return'); if (node.argument) { - content += " " + visit(node.argument, node); + emitter.emit(' '); + visit(node.argument, node); } } else if (node.type == "ClassDeclaration") { - content = "class " + node.id.name + emitter.emit("class " + node.id.name + " "); if (node.superClass) { - content += " extends " + node.superClass.name; + emitter.emit("extends " + node.superClass.name + " "); } var s = scope.create(node); - content += "\n{\n"; - content += visit(node.body, node); + emitter.emit('{'); emitter.incrIndent(); + visit(node.body, node); if (s.getters.length > 0) { - content += "function __get($_property) {\n"; - for (var i=0;i 0) { - content += "function __set($_property, $value) {\n"; - for (var i=0;i $___)"; - content += "{" + visit(node.body, node) + "}"; + emitter.emit("foreach "); + emitter.block('(', function() { + visit(node.right, node); + emitter.emit(" as "); + visit(node.left, node); + emitter.emit(" => $___"); + }, ')'); + emitter.emit(' '); + emitter.block('{', function() { visit(node.body, node); }, '}'); } else if (node.type == "UpdateExpression") { if (node.prefix) { - content += node.operator; + emitter.emit(node.operator); } - content += visit(node.argument, node); + visit(node.argument, node); if (!node.prefix) { - content += node.operator; + emitter.emit(node.operator); } } else if (node.type == "SwitchStatement") { - content = "switch (" + visit(node.discriminant, node) + ")"; - content += "{"; - for (var i=0; i < node.cases.length; i++) { - content += visit(node.cases[i], node) + "\n"; - } - content += "}"; + emitter.emit("switch "); + emitter.block('(', function() { node.discriminant.suppressParens = true; visit(node.discriminant, node); }, ')'); + emitter.emit(' '); + emitter.block('{', function() { + for (var i=0; i < node.cases.length; i++) { + visit(node.cases[i], node); emitter.nl(); + } + }, '}'); } else if (node.type == "SwitchCase") { if (node.test) { - content += "case " + visit(node.test, node) + ":\n"; + emitter.emit("case "); + visit(node.test, node); + emitter.emit(":"); } else { - content = "default:\n"; + emitter.emit("default:"); } + emitter.nl(); for (var i=0; i < node.consequent.length; i++) { - content += visit(node.consequent[i], node); + visit(node.consequent[i], node); } } else if (node.type == "BreakStatement") { - content = "break;"; + emitter.emit("break;"); } else if (node.type == "ContinueStatement") { - content = "continue;"; + emitter.emit("continue;"); } else if (node.type == "NewExpression") { // re-use CallExpression for NewExpression's var newNode = utils.clone(node); newNode.type = "CallExpression"; - return "new " + visit(newNode, node); + emitter.emit('new '); + visit(newNode, node); + return; } else if (node.type == "FunctionExpression") { @@ -541,30 +785,30 @@ module.exports = function(code, options) { node.type = "FunctionDeclaration"; node.id = { name: node.id || "" }; - content = visit(node, node.parent); + visit(node, node.parent); // Modules & Export (http://wiki.ecmascript.org/doku.php?id=harmony:modules_examples) } else if (node.type == "ModuleDeclaration") { - content = "namespace " + utils.classize(node.id.value) + ";\n"; - content += visit(node.body, node); + emitter.emit("namespace " + utils.classize(node.id.value) + ";"); + visit(node.body, node); } else if (node.type == "ExportNamedDeclaration") { - content = visit(node.declaration, node); + visit(node.declaration, node); } else if (node.type == "ImportDeclaration") { for (var i=0,length = node.specifiers.length;i_something = $something; -} -public function toArray($model, $array) { -return $array; -} -public function hello() { -return "Hello world"; -} -public function creating($model) { -Mail::send(array("body" => Module::template('signup-confirmation')->compile(array("base_url" => AppConfig::get("retrieve.email.url"))), "subject" => "Sign-up confirmation", "to" => $model->email, "from" => "somebody@example.com")); -} -public function updating($model) { -if ($model->isDirty('status') && $model->status == 1) { -Mail::send(array("body" => Module::template('signup-approved')->compile(array("BASE_URL" => AppConfig::get("retrieve.email.url"))), "subject" => "Approved!", "to" => $model->email, "from" => "somebody@example.com")); -}} -function __get($_property) { -if ($_property === 'something') { -return "Something: " + $this->_something; -} -} -function __set($_property, $value) { -if ($_property === 'something') { -$this->_something = $value + 10; -} -} +class ClassExample { + + public function __construct($something) { + $this->_something = $something; + } + public $_something; + + + public function toArray($model, $array) { + return $array; + } + + public function hello() { + return "Hello world"; + } + + + + + + + + + public function creating($model) { + Mail::send([ + "body" => Module::template('signup-confirmation')->compile([ + "base_url" => AppConfig::get("retrieve.email.url") + ]), + "subject" => "Sign-up confirmation", + "to" => $model->email, + "from" => "somebody@example.com" + ]); + } + + public function updating($model) { + if ($model->isDirty('status') && $model->status == 1) { + + Mail::send([ + "body" => Module::template('signup-approved')->compile([ + "BASE_URL" => AppConfig::get("retrieve.email.url") + ]), + "subject" => "Approved!", + "to" => $model->email, + "from" => "somebody@example.com" + ]); + + } + } + + function __get($_property) { + if ($_property === 'something') { + return "Something: " + $this->_something; + } + } + function __set($_property, $value) { + if ($_property === 'something') { + $this->_something = $value + 10; + } + } } + + + + + + + + + + + + + + + + + + + + + + + + + + + +// Instantiate class and call getter method $example = new ClassExample("awesome"); -var_dump(call_user_func_array(array($example, 'hello'), array())); -var_dump($example->hello()); +var_dump(call_user_func_array([$example, 'hello'], [])); +var_dump($example->hello()); \ No newline at end of file diff --git a/test/fixtures/class_inheritance.php b/test/fixtures/class_inheritance.php index a5e1ed4..5eaf407 100644 --- a/test/fixtures/class_inheritance.php +++ b/test/fixtures/class_inheritance.php @@ -1,29 +1,30 @@ title = $title; -$this->body = $body; -} -public function getDescription() { -return "{$this->title}: {$this->body}"; -} +class Page { + public function __construct($title, $body) { + $this->title = $title; + $this->body = $body; + } + public $title; + public $body; + + public function getDescription() { + return "{$this->title}: {$this->body}"; + } } -class Article extends Page -{ -public $author; -public function __construct($title, $body, $author) { -parent::__construct($title, $body); -$this->author = $author; -} -public function getDescription() { -$descrition = $parent->getDescription(); -return "{$description} by {$this->author}"; -} +class Article extends Page { + public function __construct($title, $body, $author) { + parent::__construct($title, $body); + $this->author = $author; + } + public $author; + + public function getDescription() { + $descrition = $parent->getDescription(); + return "{$description} by {$this->author}"; + } } + $article = new Article("Wonderful article", "Yada Yada Yada", "Bob Loblaw"); -var_dump($article->getDescription()); +var_dump($article->getDescription()); \ No newline at end of file diff --git a/test/fixtures/closures.php b/test/fixtures/closures.php index 989e09a..f67df9e 100644 --- a/test/fixtures/closures.php +++ b/test/fixtures/closures.php @@ -1,12 +1,11 @@ "Three"), $items)); +echo(array_search(["name" => "Three"], $items)); echo(join(", ", $items)); + diff --git a/test/fixtures/core_function.php b/test/fixtures/core_function.php index 460ca13..42c06fb 100644 --- a/test/fixtures/core_function.php +++ b/test/fixtures/core_function.php @@ -1,22 +1,24 @@ 5, "string" => "hey", "nested" => array("objects" => array("here" => "yey")))); -var_dump(json_encode(array("integer" => 5, "string" => "hey", "nested" => array("objects" => array("here" => "yey"))))); +json_encode([ + "integer" => 5, + "string" => "hey", + "nested" => ["objects" => ["here" => "yey"]] + ]); + + + +; + +var_dump(json_encode([ + "integer" => 5, + "string" => "hey", + "nested" => ["objects" => ["here" => "yey"]] + ]) + + + +); \ No newline at end of file diff --git a/test/fixtures/core_math.php b/test/fixtures/core_math.php index 0b87138..ab1e4b8 100644 --- a/test/fixtures/core_math.php +++ b/test/fixtures/core_math.php @@ -1,4 +1,5 @@ "one"), array("name" => "two"), array("name" => "three")); -foreach ($items as $item => $___){echo($item['name']); -continue;} \ No newline at end of file +$items = [ + ["name" => "one"], + ["name" => "two"], + ["name" => "three"] +]; + +foreach ($items as $item => $___) { + echo($item['name']); + continue; +} \ No newline at end of file diff --git a/test/fixtures/function.js b/test/fixtures/function.js index 9d08f20..f05e86c 100644 --- a/test/fixtures/function.js +++ b/test/fixtures/function.js @@ -12,6 +12,6 @@ function hello(a, b) { } var_dump(hello.apply(hello, [5,6])) -var_dump(hello(5, 6)) +var_dump(hello(5, 6)); -// var_dump(something(5), sum(1,2)) +// var_dump(something(5), sum(1,2)); diff --git a/test/fixtures/function.php b/test/fixtures/function.php index 4d3c119..8869eec 100644 --- a/test/fixtures/function.php +++ b/test/fixtures/function.php @@ -1,12 +1,18 @@ read()); diff --git a/test/fixtures/global_functions.php b/test/fixtures/global_functions.php index c5fd181..e15949b 100644 --- a/test/fixtures/global_functions.php +++ b/test/fixtures/global_functions.php @@ -1,8 +1,10 @@ 'one', "two" => 'two', "three" => 'three'); -foreach ($obj as $i => $___){var_dump($i, $obj[$i]); -}$j = 10; -while ($j > 0) {$j--; -var_dump($j); -}$xxx = 10; -do {--$xxx; -} while ($xxx > 0); +$items = [1, 2, 3, 4, 5]; + +for ($i = 0; $i < count($items); $i++) { + var_dump($items[$i]); +} + +for ($i = 0, $j = 10; $i < $j; $i++) { + var_dump($i); +} + +$obj = [ + "one" => 'one', + "two" => 'two', + "three" => 'three' +]; + +foreach ($obj as $i => $___) { + var_dump($i, $obj[$i]); +} + +$j = 10; +while ( $j > 0 ) { + $j--; + var_dump($j); +} + +$xxx = 10; + +do { + --$xxx; +} while ($xxx > 0); \ No newline at end of file diff --git a/test/fixtures/namespaces.php b/test/fixtures/namespaces.php index ddd5f02..5ec955d 100644 --- a/test/fixtures/namespaces.php +++ b/test/fixtures/namespaces.php @@ -1,18 +1,17 @@ log($x, $y); $console->log($z); -var_dump(array("a" => 1, "b-c" => 'c', "d-e-fh" => g(0), "hi" => "hi")); -$obj = array("key" => 'value', "key2" => 'value2'); + +var_dump(["a" => 1, "b-c" => 'c', "d-e-fh" => g(0), "hi" => "hi"]); + +$obj = ["key" => 'value', "key2" => 'value2']; unset($obj['key']); var_dump(isset($something['key2'])); var_dump(gettype($something) !== NULL); diff --git a/test/fixtures/static_call.php b/test/fixtures/static_call.php index b4dde0d..db0fa6e 100644 --- a/test/fixtures/static_call.php +++ b/test/fixtures/static_call.php @@ -1,5 +1,14 @@ json(array("success" => Mail::send(array("body" => Module::template('signup-confirmation')->compile(array("base_url" => AppConfig::get("retrieve.email.url"))), "subject" => "Sign-up confirmation", "to" => $model->email, "from" => "somebody@example.com")))); -} -); + return $this->json([ + "success" => Mail::send([ + "body" => Module::template('signup-confirmation')->compile([ + "base_url" => AppConfig::get("retrieve.email.url") + ]), + "subject" => "Sign-up confirmation", + "to" => $model->email, + "from" => "somebody@example.com" + ]) + ]); + }); \ No newline at end of file diff --git a/test/fixtures/string_template.php b/test/fixtures/string_template.php index 01f7d44..70c62ba 100644 --- a/test/fixtures/string_template.php +++ b/test/fixtures/string_template.php @@ -4,15 +4,15 @@ $n1 = M_PI; $n2 = 50; $func = function () { -return "from function"; -} -; -class Item -{ -public function method() { -return "from method"; -} + return "from function"; +}; + +class Item { + public function method() { + return "from method"; + } } $item = new Item(); + echo("{$str1}, {$str2}, {$n1}, {$n2}, {$item->method()}, {$func()}"); From 410d96a3ec7181b4fb29a972b4a60edc70f00d0f Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Tue, 29 Jan 2019 12:10:25 -0800 Subject: [PATCH 05/52] Improvements to RegExp/String method translation. --- core.js | 15 ++++++++-- core/array.js | 15 ++++++++-- core/regexp.js | 27 ++++++++++++++++++ core/string.js | 60 +++++++++++++++++++++++++++++++++++++++- test/fixtures/regexp.js | 5 +++- test/fixtures/regexp.php | 9 ++++-- 6 files changed, 121 insertions(+), 10 deletions(-) create mode 100644 core/regexp.js diff --git a/core.js b/core.js index 4d32632..f3eb921 100644 --- a/core.js +++ b/core.js @@ -5,6 +5,7 @@ var utils = require('./utils'), _date = require('./core/date'), _function = require('./core/function'), _json = require('./core/json'), + _regexp = require('./core/regexp'), _string = require('./core/string'), _math = require('./core/math'), _number = require('./core/number'); @@ -14,10 +15,20 @@ module.exports = { evaluate: function(node) { var handler = undefined; - if (node.property) { + if (node.object && node.object.type === 'MemberExpression') { + var newNode = module.exports.evaluate(node.object); + if (newNode !== node.object) { + node.object = newNode; + } + } + if (node.object && node.object.type === 'Literal') { + var method = node.property.name; + handler = node.object.regex ? _regexp[method] : _string[method]; + } else if (node.property) { var method = node.property.name; + // _array should be before _string here so we pick up the correct + // multitype version of #length and #indexOf handler = _array[method] || _date[method] || _function[method] || _json[method] || _string[method] || _math[method] || _number[method]; - } else if (node.callee) { handler = _global[node.callee.name]; } diff --git a/core/array.js b/core/array.js index 6772486..f14e5c7 100644 --- a/core/array.js +++ b/core/array.js @@ -101,10 +101,19 @@ module.exports = { }, slice: function(node) { - // Second argument to array_slice is very different from Array#slice - if (node.parent.arguments.length !== 1) { return node; } var args = utils.clone(node.parent.arguments); - args[0].suppressParens = true; + if (node.parent.arguments.length > 1) { + // Second argument to array_slice is very different from Array#slice + // unless it is negative. + if (args[1].type === 'UnaryExpression' && args[1].operator==='-' && + args[1].argument.type==='Literal') { + /* this is okay */ + } else { + args[1].trailingComments = [{ type: 'Block', value: 'CHECK THIS'}]; + } + } else { + args[0].suppressParens = true; + } args.unshift(node.parent.callee.object); node.parent.arguments = false; diff --git a/core/regexp.js b/core/regexp.js new file mode 100644 index 0000000..fe09916 --- /dev/null +++ b/core/regexp.js @@ -0,0 +1,27 @@ +var utils = require('../utils'); + +module.exports = { + source: function(node) { + node.object.value = node.object.value.source; + node.object.raw = JSON.stringify(node.object.value); + node.object.regex = undefined; + return node.object; + }, + + test: function(node) { + var args = utils.clone(node.parent.arguments); + node.parent.arguments = false; + args[0].suppressParens = true; + var lit = module.exports.source(node.parent.callee); + lit.value = '/' + lit.value + '/'; + lit.raw = JSON.stringify(lit.value); + return { + type: 'CallExpression', + callee: { + type: 'Identifier', + name: 'preg_match', + }, + arguments: [ node.parent.callee.object, args[0] ], + }; + }, +}; diff --git a/core/string.js b/core/string.js index b135147..0d1a0c8 100644 --- a/core/string.js +++ b/core/string.js @@ -5,6 +5,33 @@ module.exports = { // // string methods // + indexOf: function(node) { + var args = utils.clone(node.parent.arguments); + node.parent.arguments = false; + if (args.length === 1) { args[0].suppressParens = true; } + args.unshift(node.parent.callee.object); + + return { + type: 'CallExpression', + callee: { + type: 'Identifier', + name: 'strpos', + }, + arguments: args + }; + }, + + length: function(node) { + var object = (node.parent.callee && node.parent.callee.object) || node.object; + return { + type: 'CallExpression', + callee: { + type: 'Identifier', + name: 'strlen', + }, + arguments: [object], + }; + }, replace: function(node) { var method = "str_replace"; @@ -13,7 +40,7 @@ module.exports = { node.parent.arguments = false; - if(args[0].type !== 'Identifier'){ + if(args[0].type === 'Literal'){ var regexpData = args[0].raw.match(/^\/([^\/]+)\/([gimy])?$/), regex = regexpData && regexpData[1], flags = regexpData && regexpData[2] || "", @@ -43,6 +70,34 @@ module.exports = { }; }, + slice: function(node) { + var args = utils.clone(node.parent.arguments); + if (node.parent.arguments.length > 1) { + // Second argument to substr is very different from String#slice + // unless it is negative. + if (args[1].type === 'UnaryExpression' && args[1].operator==='-' && + args[1].argument.type==='Literal') { + /* this is okay */ + } else { + args[1].trailingComments = [{ type: 'Block', value: 'CHECK THIS'}]; + } + } else { + args[0].suppressParens = true; + } + args.unshift(node.parent.callee.object); + + node.parent.arguments = false; + + return { + type: 'CallExpression', + callee: { + type: 'Identifier', + name: 'substr', + }, + arguments: args + }; + }, + trim: function(node) { node.parent.arguments = false; @@ -124,6 +179,9 @@ module.exports = { method = "preg_split"; args[0].raw = "'/" + regex + "/" + flags.replace("g", "") + "'"; args[0].type = "Literal"; + if (args.length === 2) { + args[0].suppressParens = true; + } } // If splitting with a blank delimiter, use str_split. else if (args[0].value === '') { diff --git a/test/fixtures/regexp.js b/test/fixtures/regexp.js index 3cc23d4..57c9a79 100644 --- a/test/fixtures/regexp.js +++ b/test/fixtures/regexp.js @@ -20,4 +20,7 @@ var_dump("hey hey hey".replace(/y/g, "llo")); var string = 'hello'; var regex = 'ello'; -var nonliteral = string.match(regex); \ No newline at end of file +var nonliteral = string.match(regex); + +var_dump(/abc\n[/]/.test('def')); +var_dump(/[\n]/.source.slice(1,-1)); diff --git a/test/fixtures/regexp.php b/test/fixtures/regexp.php index fe7b238..ba55160 100644 --- a/test/fixtures/regexp.php +++ b/test/fixtures/regexp.php @@ -8,10 +8,10 @@ $splitted = explode(",", "one, two, three"); var_dump($splitted); -$splitted = preg_split(('/,/'), "one, two, three"); +$splitted = preg_split('/,/', "one, two, three"); var_dump($splitted); -$g_splitted = preg_split(('/,/'), "one, two, three"); +$g_splitted = preg_split('/,/', "one, two, three"); var_dump($g_splitted); var_dump(str_replace("y", "llo", "hey")); @@ -21,4 +21,7 @@ $string = 'hello'; $regex = 'ello'; -$nonliteral = preg_match($regex, $string); \ No newline at end of file +$nonliteral = preg_match($regex, $string); + +var_dump(preg_match("/abc\\n[\\/]/", 'def')); +var_dump(substr("[\\n]", 1, -1)); From aa6402d77767846909cfbf4409ef5c6fd93ce3ff Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Wed, 30 Jan 2019 15:13:32 -0800 Subject: [PATCH 06/52] More improvements to mapping of standard library functions. --- core.js | 5 +++++ core/array.js | 30 ++++++++++++++++++++++++++++++ core/console.js | 17 +++++++++++++++++ core/object.js | 15 +++++++++++++++ index.js | 9 +++++++++ test/fixtures/assert.js | 2 ++ test/fixtures/assert.php | 3 +++ test/fixtures/core_array.js | 9 +++++++++ test/fixtures/core_array.php | 13 ++++++++++++- test/fixtures/core_object.js | 3 +++ test/fixtures/core_object.php | 4 ++++ 11 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 core/console.js create mode 100644 core/object.js create mode 100644 test/fixtures/assert.js create mode 100644 test/fixtures/assert.php create mode 100644 test/fixtures/core_object.js create mode 100644 test/fixtures/core_object.php diff --git a/core.js b/core.js index f3eb921..df7f6d0 100644 --- a/core.js +++ b/core.js @@ -2,9 +2,11 @@ var utils = require('./utils'), scope = require('./scope'), _global = require('./core/global'), _array = require('./core/array'), + _console = require('./core/console'), _date = require('./core/date'), _function = require('./core/function'), _json = require('./core/json'), + _object = require('./core/object'), _regexp = require('./core/regexp'), _string = require('./core/string'), _math = require('./core/math'), @@ -24,6 +26,9 @@ module.exports = { if (node.object && node.object.type === 'Literal') { var method = node.property.name; handler = node.object.regex ? _regexp[method] : _string[method]; + } else if (node.object && node.object.type === 'Identifier' && /^(Array|Object|console)$/.test(node.object.name)) { + var longName = node.object.name + '_' + node.property.name; + handler = _array[longName] || _object[longName] || _console[longName]; } else if (node.property) { var method = node.property.name; // _array should be before _string here so we pick up the correct diff --git a/core/array.js b/core/array.js index f14e5c7..b5f8d7c 100644 --- a/core/array.js +++ b/core/array.js @@ -3,6 +3,18 @@ var utils = require('../utils'), string = require('./string'); module.exports = { + Array_isArray: function(node) { + var args = utils.clone(node.parent.arguments); + node.parent.arguments = false; + return { + type: 'CallExpression', + callee: { + type: 'Identifier', + name: 'is_array', + }, + arguments: args, + }; + }, unshift: function(node) { var args = utils.clone(node.parent.arguments); @@ -100,6 +112,24 @@ module.exports = { }; }, + reduce: function(node) { + var args = utils.clone(node.parent.arguments); + node.parent.arguments = false; + if (args.length === 1) { + args[0].suppressParens = true; + } + args.unshift(node.parent.callee.object); + + return { + type: 'CallExpression', + callee: { + type: 'Identifier', + name: 'array_reduce', + }, + arguments: args, + }; + }, + slice: function(node) { var args = utils.clone(node.parent.arguments); if (node.parent.arguments.length > 1) { diff --git a/core/console.js b/core/console.js new file mode 100644 index 0000000..540f9a5 --- /dev/null +++ b/core/console.js @@ -0,0 +1,17 @@ +var utils = require('../utils'); + +module.exports = { + console_assert: function(node) { + return { + type: 'MemberExpression', + object: { + type: 'Identifier', + name: 'Assert', + }, + property: { + type: 'Identifier', + name: 'invariant', + } + }; + } +}; diff --git a/core/object.js b/core/object.js new file mode 100644 index 0000000..0f1b352 --- /dev/null +++ b/core/object.js @@ -0,0 +1,15 @@ +var utils = require('../utils'); + +module.exports = { + Object_create: function(node) { + var args = utils.clone(node.parent.arguments); + if (args.length === 1 && args[0].type === 'Literal' && args[0].value === null) { + node.parent.arguments = false; + return { + type: 'ObjectExpression', + properties: [], + }; + }; + return node; + } +}; diff --git a/index.js b/index.js index 8634fbe..2397d4b 100644 --- a/index.js +++ b/index.js @@ -374,6 +374,15 @@ module.exports = function(code, options) { node.callee.isCallee = (!calleeDefined || calleeDefined && (calleeDefined.type != "Identifier" && calleeDefined.type != "VariableDeclarator")); + if (node.parent && node.parent.arguments === false && node.parent.parent.type === 'ExpressionStatement' && node.callee.type === 'Identifier' && node.callee.name === 'array_push' && node.arguments.length === 2) { + // Special case syntax + visit(node.arguments[0], node); + emitter.emit('[] = '); + visit(node.arguments[1], node); + emitter.locEnd(node); + return; + } + if (node.callee.type === 'Super') { emitter.emit('parent::__construct'); } else { diff --git a/test/fixtures/assert.js b/test/fixtures/assert.js new file mode 100644 index 0000000..4bf959d --- /dev/null +++ b/test/fixtures/assert.js @@ -0,0 +1,2 @@ +console.assert(1 !== 2); +console.assert("a" === "a", "This is a string"); diff --git a/test/fixtures/assert.php b/test/fixtures/assert.php new file mode 100644 index 0000000..0c9f74b --- /dev/null +++ b/test/fixtures/assert.php @@ -0,0 +1,3 @@ + "Three"], $items)); echo(join(", ", $items)); +echo("\n"); +$count = array_reduce($items, function ($curr, $string) { + return $curr + strlen($string); + }, 0) + +; +var_dump($count); + +var_dump(is_array($items)); +var_dump(is_array($count)); \ No newline at end of file diff --git a/test/fixtures/core_object.js b/test/fixtures/core_object.js new file mode 100644 index 0000000..d79ab63 --- /dev/null +++ b/test/fixtures/core_object.js @@ -0,0 +1,3 @@ +var a = Object.create(null); +a["foo"] = "bar"; +var_dump(a); diff --git a/test/fixtures/core_object.php b/test/fixtures/core_object.php new file mode 100644 index 0000000..cede6d3 --- /dev/null +++ b/test/fixtures/core_object.php @@ -0,0 +1,4 @@ + Date: Wed, 30 Jan 2019 15:15:29 -0800 Subject: [PATCH 07/52] Fix translation of array functions with expression values like `()=>3` --- index.js | 2 +- test/fixtures/arrow_functions.js | 4 ++++ test/fixtures/arrow_functions.php | 6 +++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 2397d4b..6a26151 100644 --- a/index.js +++ b/index.js @@ -531,7 +531,7 @@ module.exports = function(code, options) { }).join('')); } - if (node.body.type === 'BinaryExpression') { + if (node.expression) { // x => x * 2 emitter.insertAt(0, 'return '); emitter.emit(';'); diff --git a/test/fixtures/arrow_functions.js b/test/fixtures/arrow_functions.js index 7d6fcd8..4bd333f 100644 --- a/test/fixtures/arrow_functions.js +++ b/test/fixtures/arrow_functions.js @@ -2,3 +2,7 @@ var something = (a, b = 2, c = 4) => { return a * b * c; } var something2 = (a, b = 2, c = 4) => a * b * c; + +var something3 = () => 5; + +var_dump(something3()); diff --git a/test/fixtures/arrow_functions.php b/test/fixtures/arrow_functions.php index 21799e6..d4f9759 100644 --- a/test/fixtures/arrow_functions.php +++ b/test/fixtures/arrow_functions.php @@ -2,4 +2,8 @@ $something = function ($a, $b = 2, $c = 4) { return $a * $b * $c; }; -$something2 = function ($a, $b = 2, $c = 4) {return $a * $b * $c;}; \ No newline at end of file +$something2 = function ($a, $b = 2, $c = 4) {return $a * $b * $c;}; + +$something3 = function () {return 5;}; + +var_dump($something3()); \ No newline at end of file From cfc8754c428c014236d581a7c376f788b0479f04 Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Thu, 31 Jan 2019 08:39:58 -0800 Subject: [PATCH 08/52] Support rest and spread arguments --- index.js | 6 ++++++ test/fixtures/rest_spread.js | 5 +++++ test/fixtures/rest_spread.php | 6 ++++++ 3 files changed, 17 insertions(+) create mode 100644 test/fixtures/rest_spread.js create mode 100644 test/fixtures/rest_spread.php diff --git a/index.js b/index.js index 6a26151..836a08c 100644 --- a/index.js +++ b/index.js @@ -863,6 +863,12 @@ module.exports = function(code, options) { emitter.emit("throw "); visit(node.argument, node); semicolon = true; + } else if (node.type === "RestElement") { + emitter.emit('...'); + visit(node.argument, node); + } else if (node.type === "SpreadElement") { + emitter.emit('...'); + visit(node.argument, node); } else { throw new Error("'" + node.type + "' not implemented: " + JSON.stringify(node)); } diff --git a/test/fixtures/rest_spread.js b/test/fixtures/rest_spread.js new file mode 100644 index 0000000..dc28fab --- /dev/null +++ b/test/fixtures/rest_spread.js @@ -0,0 +1,5 @@ +var x = (...args) => args.length; +var y = (z) => x(...z); + +var_dump(x(1, 2, 3, 4)); +var_dump(y([1, 2, 3])); diff --git a/test/fixtures/rest_spread.php b/test/fixtures/rest_spread.php new file mode 100644 index 0000000..e8687d9 --- /dev/null +++ b/test/fixtures/rest_spread.php @@ -0,0 +1,6 @@ + Date: Tue, 29 Jan 2019 12:11:03 -0800 Subject: [PATCH 09/52] Add namespace option; nominally support import statements using require. --- index.js | 52 ++++++++++++++++++++++++++++ js2php | 3 ++ test/fixtures/namespaces_require.js | 5 +++ test/fixtures/namespaces_require.php | 7 ++++ test/generate.js | 3 ++ test/suite.js | 3 ++ 6 files changed, 73 insertions(+) create mode 100644 test/fixtures/namespaces_require.js create mode 100644 test/fixtures/namespaces_require.php diff --git a/index.js b/index.js index 836a08c..5984e84 100644 --- a/index.js +++ b/index.js @@ -206,6 +206,33 @@ module.exports = function(code, options) { } var emitter = new Emitter(); + function handleImport(node) { + node.declarations.forEach(function(d) { + if (d.type !== 'VariableDeclarator') { return; } + // the RHS is require('..some string..') but we're going to ignore that + if (d.id.type === 'ObjectPattern') { + emitter.locStart(d); + d.id.properties.forEach(function(p, idx) { + if (idx > 0) { emitter.nl(); } + var name = utils.classize(p.key.name); + if (options.namespace) { name = options.namespace + "\\" + name; } + emitter.emit("use " + name); + if (p.key.name !== p.value.name) { + emitter.emit(" as " + utils.classize(p.value.name)); + } + emitter.emit(";"); + }); + emitter.locEnd(d); + } else if (d.id.type === 'Identifier') { + var name = d.id.name; + if (options.namespace) { name = options.namespace + "\\" + name; } + emitter.locStart(d); + emitter.emit("use " + name + ";"); + emitter.locEnd(d); + } + }); + } + function visit(node, parent) { var semicolon = false; @@ -214,6 +241,31 @@ module.exports = function(code, options) { if (!node.suppressLoc) { emitter.locStart(node); } if (node.type == "Program" || node.type == "BlockStatement" || node.type == "ClassBody") { + // Skip strictness declaration + if (node.body[0] && node.body[0].type === 'ExpressionStatement' && + node.body[0].expression.type === 'Literal' && + node.body[0].expression.raw.match(/^["']use strict["']$/)) { + emitter.locStart(node.body[0]); // flush leading comment + node.body.shift(); + } + if (node.type === 'Program') { + if (options.namespace) { + // Add optional namespace. + emitter.emit(`namespace ${options.namespace};`); + emitter.nl(); + } + + // Look for require declarations + while (node.body[0] && node.body[0].type === 'VariableDeclaration' && + node.body[0].declarations[0] && + node.body[0].declarations[0].type === 'VariableDeclarator' && + node.body[0].declarations[0].init && + node.body[0].declarations[0].init.type === 'CallExpression' && + node.body[0].declarations[0].init.callee.type === 'Identifier' && + node.body[0].declarations[0].init.callee.name === 'require') { + handleImport(node.body.shift()); + } + } for (var i=0,length = node.body.length;i Date: Tue, 29 Jan 2019 12:11:27 -0800 Subject: [PATCH 10/52] Parsoid-specific: Skip `require('core-update.js')` --- index.js | 8 ++++++++ test/fixtures/parsoid.js | 5 +++++ test/fixtures/parsoid.php | 7 +++++++ 3 files changed, 20 insertions(+) create mode 100644 test/fixtures/parsoid.js create mode 100644 test/fixtures/parsoid.php diff --git a/index.js b/index.js index 5984e84..7090efd 100644 --- a/index.js +++ b/index.js @@ -255,6 +255,14 @@ module.exports = function(code, options) { emitter.nl(); } + // skip core update require + while (node.body[0] && node.body[0].type === 'ExpressionStatement' && + node.body[0].expression.type === 'CallExpression' && + node.body[0].expression.callee.type === 'Identifier' && + node.body[0].expression.callee.name === 'require') { + node.body.shift(); // discard this + } + // Look for require declarations while (node.body[0] && node.body[0].type === 'VariableDeclaration' && node.body[0].declarations[0] && diff --git a/test/fixtures/parsoid.js b/test/fixtures/parsoid.js new file mode 100644 index 0000000..43b1986 --- /dev/null +++ b/test/fixtures/parsoid.js @@ -0,0 +1,5 @@ +'use strict'; +require('core-update.js'); +class Foo { + constructor() { this.x = 1; } +} diff --git a/test/fixtures/parsoid.php b/test/fixtures/parsoid.php new file mode 100644 index 0000000..cfff7ca --- /dev/null +++ b/test/fixtures/parsoid.php @@ -0,0 +1,7 @@ +x = 1;} + public $x; +} \ No newline at end of file From 0bfeeeab93836329093c1214af0f2f91d517c7db Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Thu, 31 Jan 2019 08:40:49 -0800 Subject: [PATCH 11/52] Parsoid-specific: handle Promise.async(... yield ...) --- core.js | 5 +++-- core/promise.js | 15 +++++++++++++++ index.js | 4 ++++ test/fixtures/parsoid.js | 6 ++++++ test/fixtures/parsoid.php | 8 ++++++++ 5 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 core/promise.js diff --git a/core.js b/core.js index df7f6d0..5ed4e5b 100644 --- a/core.js +++ b/core.js @@ -7,6 +7,7 @@ var utils = require('./utils'), _function = require('./core/function'), _json = require('./core/json'), _object = require('./core/object'), + _promise = require('./core/promise'), _regexp = require('./core/regexp'), _string = require('./core/string'), _math = require('./core/math'), @@ -26,9 +27,9 @@ module.exports = { if (node.object && node.object.type === 'Literal') { var method = node.property.name; handler = node.object.regex ? _regexp[method] : _string[method]; - } else if (node.object && node.object.type === 'Identifier' && /^(Array|Object|console)$/.test(node.object.name)) { + } else if (node.object && node.object.type === 'Identifier' && /^(Array|Object|Promise|console)$/.test(node.object.name)) { var longName = node.object.name + '_' + node.property.name; - handler = _array[longName] || _object[longName] || _console[longName]; + handler = _array[longName] || _object[longName] || _promise[longName] || _console[longName]; } else if (node.property) { var method = node.property.name; // _array should be before _string here so we pick up the correct diff --git a/core/promise.js b/core/promise.js new file mode 100644 index 0000000..a0de34b --- /dev/null +++ b/core/promise.js @@ -0,0 +1,15 @@ +var utils = require('../utils'); + +module.exports = { + Promise_async: function(node) { + var args = utils.clone(node.parent.arguments); + if (args.length === 1) { + node.parent.arguments = false; + args[0].suppressParens = true; + args[0].leadingComments = args[0].leadingComments || []; + args[0].leadingComments.push({ type: 'Block', value: ' async ' }); + return args[0]; + }; + return node; + } +}; diff --git a/index.js b/index.js index 7090efd..6ffe979 100644 --- a/index.js +++ b/index.js @@ -929,6 +929,10 @@ module.exports = function(code, options) { } else if (node.type === "SpreadElement") { emitter.emit('...'); visit(node.argument, node); + } else if (node.type === "YieldExpression") { + // Parsoid-specific: ignore yield expression. + emitter.emit("/* await */ "); + visit(node.argument, node); } else { throw new Error("'" + node.type + "' not implemented: " + JSON.stringify(node)); } diff --git a/test/fixtures/parsoid.js b/test/fixtures/parsoid.js index 43b1986..5224581 100644 --- a/test/fixtures/parsoid.js +++ b/test/fixtures/parsoid.js @@ -1,5 +1,11 @@ 'use strict'; require('core-update.js'); +const { Bat } = require('some/thing'); + +var bar = Promise.async(function *(x) { + return (yield Bat.bat()); +}); + class Foo { constructor() { this.x = 1; } } diff --git a/test/fixtures/parsoid.php b/test/fixtures/parsoid.php index cfff7ca..e778bf0 100644 --- a/test/fixtures/parsoid.php +++ b/test/fixtures/parsoid.php @@ -1,6 +1,14 @@ x = 1;} public $x; From a45dbd46d4bf61c109d5c5f9a414ae35a90f3a61 Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Mon, 4 Feb 2019 17:17:40 -0500 Subject: [PATCH 12/52] Parsoid-specific: Add support for a watermark at the top of the file. --- index.js | 3 +++ js2php | 3 +++ 2 files changed, 6 insertions(+) diff --git a/index.js b/index.js index 6ffe979..3d69d1e 100644 --- a/index.js +++ b/index.js @@ -945,6 +945,9 @@ module.exports = function(code, options) { } emitter.emit(" Date: Tue, 5 Feb 2019 10:39:34 -0500 Subject: [PATCH 13/52] Bug-fix to for loops with multiple initializers Multiple initializers should be comma-separated. --- index.js | 10 +++++++--- test/fixtures/loops.js | 7 +++++++ test/fixtures/loops.php | 9 ++++++++- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index 3d69d1e..91a1f44 100644 --- a/index.js +++ b/index.js @@ -190,6 +190,9 @@ module.exports = function(code, options) { this.nl(); } }; + Emitter.prototype.replaceSemiWithComma = function() { + this.buffer = this.buffer.replace(/;([ \t]*)$/, ', $1'); + }; Emitter.prototype.pushInsertionPoint = function() { this.insertionPoints.push(this.buffer.length); } @@ -287,6 +290,9 @@ module.exports = function(code, options) { } else if (node.type == "VariableDeclarator") { scope.get(node).register(node); + var isForStatement = node.parent && node.parent.parent && + /^For(In|Of|)Statement$/.test(node.parent.parent.type); + if (isForStatement) { emitter.replaceSemiWithComma(); } // declaration of one variable emitter.emit('$' + node.id.name); @@ -295,9 +301,7 @@ module.exports = function(code, options) { emitter.emit(' = '); visit(node.init, node); semicolon = true; - } else if (node.parent.parent.type !== "ForInStatement" && - node.parent.parent.type !== "ForStatement" && - node.parent.parent.type !== "ForOfStatement") { + } else if (!isForStatement || node.parent.parent==='ForStatement') { emitter.emit(' = null'); semicolon = true; } diff --git a/test/fixtures/loops.js b/test/fixtures/loops.js index 6389f2b..733b7cb 100644 --- a/test/fixtures/loops.js +++ b/test/fixtures/loops.js @@ -29,3 +29,10 @@ var xxx = 10; do { --xxx; } while (xxx > 0); + +for (i=0, j=2; i 0); \ No newline at end of file +} while ($xxx > 0); + +for ($i = 0, $j = 2; $i < $j; $i++) { + echo($i); +} +for ($k = 0, $count = 2; $k < $count; $k++) { + echo($k); +} \ No newline at end of file From ed5b6475e140d33c4d4196c01a49de885c7fd5fa Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Tue, 5 Feb 2019 10:53:46 -0500 Subject: [PATCH 14/52] Fix regular expression matching in preg_replace() --- core/string.js | 2 +- test/fixtures/regexp.js | 2 ++ test/fixtures/regexp.php | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/core/string.js b/core/string.js index 0d1a0c8..9bd217e 100644 --- a/core/string.js +++ b/core/string.js @@ -41,7 +41,7 @@ module.exports = { node.parent.arguments = false; if(args[0].type === 'Literal'){ - var regexpData = args[0].raw.match(/^\/([^\/]+)\/([gimy])?$/), + var regexpData = args[0].raw.match(/^\/((?:[^\/]|\\.)+)\/([gimy])?$/), regex = regexpData && regexpData[1], flags = regexpData && regexpData[2] || "", isGroup = flags.indexOf('g') >= 0; diff --git a/test/fixtures/regexp.js b/test/fixtures/regexp.js index 57c9a79..84d4f1c 100644 --- a/test/fixtures/regexp.js +++ b/test/fixtures/regexp.js @@ -24,3 +24,5 @@ var nonliteral = string.match(regex); var_dump(/abc\n[/]/.test('def')); var_dump(/[\n]/.source.slice(1,-1)); + +var_dump("./a/b/c".replace(/\.\//, '')); diff --git a/test/fixtures/regexp.php b/test/fixtures/regexp.php index ba55160..918cc32 100644 --- a/test/fixtures/regexp.php +++ b/test/fixtures/regexp.php @@ -25,3 +25,5 @@ var_dump(preg_match("/abc\\n[\\/]/", 'def')); var_dump(substr("[\\n]", 1, -1)); + +var_dump(preg_replace('/\.\//', '', "./a/b/c", 1)); \ No newline at end of file From ea8ac249615007057f5f76f06956dfd8aeb8f562 Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Tue, 5 Feb 2019 11:54:57 -0500 Subject: [PATCH 15/52] Improve support for imports using `require` --- index.js | 35 ++++++++++++++++++++++------ test/fixtures/namespaces_require.js | 9 +++++++ test/fixtures/namespaces_require.php | 15 +++++++++--- test/fixtures/parsoid.php | 2 +- 4 files changed, 50 insertions(+), 11 deletions(-) diff --git a/index.js b/index.js index 91a1f44..da01178 100644 --- a/index.js +++ b/index.js @@ -209,18 +209,25 @@ module.exports = function(code, options) { } var emitter = new Emitter(); - function handleImport(node) { + function handleImport(parent, node) { node.declarations.forEach(function(d) { if (d.type !== 'VariableDeclarator') { return; } + scope.get(parent).register(d); + d.isImport = true; // the RHS is require('..some string..') but we're going to ignore that if (d.id.type === 'ObjectPattern') { emitter.locStart(d); d.id.properties.forEach(function(p, idx) { + scope.get(parent).register({ + type: 'VariableDeclarator', + id: { type: 'Identifier', name: p.value.name }, + isImport: true, + }); if (idx > 0) { emitter.nl(); } var name = utils.classize(p.key.name); if (options.namespace) { name = options.namespace + "\\" + name; } emitter.emit("use " + name); - if (p.key.name !== p.value.name) { + if (p.key.name !== p.value.name || options.namespace) { emitter.emit(" as " + utils.classize(p.value.name)); } emitter.emit(";"); @@ -228,9 +235,12 @@ module.exports = function(code, options) { emitter.locEnd(d); } else if (d.id.type === 'Identifier') { var name = d.id.name; - if (options.namespace) { name = options.namespace + "\\" + name; } emitter.locStart(d); - emitter.emit("use " + name + ";"); + if (options.namespace) { + emitter.emit(`use ${options.namespace}\\${name} as ${name};`); + } else { + emitter.emit(`use ${name};`); + } emitter.locEnd(d); } }); @@ -274,7 +284,7 @@ module.exports = function(code, options) { node.body[0].declarations[0].init.type === 'CallExpression' && node.body[0].declarations[0].init.callee.type === 'Identifier' && node.body[0].declarations[0].init.callee.name === 'require') { - handleImport(node.body.shift()); + handleImport(node, node.body.shift()); } } @@ -310,8 +320,12 @@ module.exports = function(code, options) { var identifier = (node.name || node.value); if (!node.static && !node.isCallee && !node.isMemberExpression) { - scope.get(node).getDefinition(node); - emitter.emit('$' + identifier); + var targetDefinition = scope.get(node).getDefinition(node); + if (targetDefinition && targetDefinition.isImport) { + emitter.emit(identifier + '::class'); + } else { + emitter.emit('$' + identifier); + } } else { emitter.emit(identifier); } @@ -514,6 +528,13 @@ module.exports = function(code, options) { object.static = (object.name || object.value || "").match(/^[A-Z]/); property.static = String(property.name || property.value || "").match(/^[A-Z]/); + var targetDefinition = scope.get(node).getDefinition(object); + if ( + node.object.type === 'Identifier' && + targetDefinition && targetDefinition.isImport + ) { + object.static = true; + } var accessor; if (node.property.static && object.static) { diff --git a/test/fixtures/namespaces_require.js b/test/fixtures/namespaces_require.js index f1860c9..9043589 100644 --- a/test/fixtures/namespaces_require.js +++ b/test/fixtures/namespaces_require.js @@ -3,3 +3,12 @@ const { Foo, Bar } = require('./some/path/here.js'); const Bat = require('bat'); +const semver = require('semver'); + +Foo.do_something(); +Bar.do_something(); +Bat.do_something(); +Bat.do_something().do_something_else(); +echo(semver.satisfies(1,2)); +echo(semver.satisfies(1,2).something_else()); +echo({ Foo, Bar }); diff --git a/test/fixtures/namespaces_require.php b/test/fixtures/namespaces_require.php index 80b12d4..ac08073 100644 --- a/test/fixtures/namespaces_require.php +++ b/test/fixtures/namespaces_require.php @@ -2,6 +2,15 @@ /* Initial comment */ namespace NameTest; -use NameTest\Foo; -use NameTest\Bar; -use NameTest\Bat; \ No newline at end of file +use NameTest\Foo as Foo; +use NameTest\Bar as Bar; +use NameTest\Bat as Bat; +use NameTest\semver as semver; + +Foo::do_something(); +Bar::do_something(); +Bat::do_something(); +Bat::do_something()->do_something_else(); +echo(semver::satisfies(1, 2)); +echo(semver::satisfies(1, 2)->something_else()); +echo(["Foo" => Foo::class, "Bar" => Bar::class]); \ No newline at end of file diff --git a/test/fixtures/parsoid.php b/test/fixtures/parsoid.php index e778bf0..a559ccb 100644 --- a/test/fixtures/parsoid.php +++ b/test/fixtures/parsoid.php @@ -3,7 +3,7 @@ use Bat; -$bar = /* async */function ($x) { +$bar = /* async */function ($x) use (&$Bat) { return (/* await */ Bat::bat()); } From 7e37a83dde9bd226c0ba87311ee082074db5b5f1 Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Tue, 5 Feb 2019 12:46:47 -0500 Subject: [PATCH 16/52] Fix Function#apply with non-literal argument --- core/function.js | 12 ++++++++---- test/fixtures/function.js | 3 +++ test/fixtures/function.php | 3 +++ 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/core/function.js b/core/function.js index fb0b214..a8647b6 100644 --- a/core/function.js +++ b/core/function.js @@ -25,10 +25,14 @@ function apply(node, isCall) { } else { // .apply use call_user_func_array method = "call_user_func_array"; - arguments.push({ - type: "ArrayExpression", - elements: (node.parent.arguments[0] || {elements:[]}).elements - }); + if (node.parent.arguments[0]) { + arguments.push(node.parent.arguments[0]); + } else { + arguments.push({ + type: "ArrayExpression", + elements: [], + }); + } } node.parent.arguments = false; diff --git a/test/fixtures/function.js b/test/fixtures/function.js index f05e86c..16b48be 100644 --- a/test/fixtures/function.js +++ b/test/fixtures/function.js @@ -14,4 +14,7 @@ function hello(a, b) { var_dump(hello.apply(hello, [5,6])) var_dump(hello(5, 6)); +var args = [5, 6]; +var_dump(sum.apply(sum, args)); + // var_dump(something(5), sum(1,2)); diff --git a/test/fixtures/function.php b/test/fixtures/function.php index 8869eec..db84e8e 100644 --- a/test/fixtures/function.php +++ b/test/fixtures/function.php @@ -15,4 +15,7 @@ function hello($a, $b) { var_dump(call_user_func_array('hello', [5, 6])); var_dump(hello(5, 6)); +$args = [5, 6]; +var_dump(call_user_func_array('sum', $args)); + // var_dump(something(5), sum(1,2)); From 761bcdc524b02d2d3907ba5bc1808083251d6129 Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Tue, 5 Feb 2019 12:47:15 -0500 Subject: [PATCH 17/52] Stub support for RegExp#exec --- core/regexp.js | 25 +++++++++++++++++++++++++ test/fixtures/regexp.js | 2 ++ test/fixtures/regexp.php | 4 +++- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/core/regexp.js b/core/regexp.js index fe09916..648725f 100644 --- a/core/regexp.js +++ b/core/regexp.js @@ -1,6 +1,31 @@ var utils = require('../utils'); module.exports = { + exec: function(node) { + var args = utils.clone(node.parent.arguments); + node.parent.arguments = false; + args[0].suppressParens = true; + var regexpData = node.parent.callee.object.raw + .match(/^\/((?:[^\/]|\\.)+)\/([gimy])?$/); + var flags = (regexpData && regexpData[2]) || ""; + var isGroup = flags.indexOf('g') >= 0; + var lit = module.exports.source(node.parent.callee); + lit.value = '/' + lit.value + '/' + flags.replace(/g/g, ""); + lit.raw = JSON.stringify(lit.value); + return { + type: 'CallExpression', + callee: { + type: 'Identifier', + name: isGroup ? 'preg_match_all' : 'preg_match', + }, + arguments: [ node.parent.callee.object, args[0], { + type: 'Identifier', + name: 'FIXME', + }], + leadingComments: [{ type: 'Block', value: 'RegExp#exec' }], + }; + }, + source: function(node) { node.object.value = node.object.value.source; node.object.raw = JSON.stringify(node.object.value); diff --git a/test/fixtures/regexp.js b/test/fixtures/regexp.js index 84d4f1c..1b6c503 100644 --- a/test/fixtures/regexp.js +++ b/test/fixtures/regexp.js @@ -26,3 +26,5 @@ var_dump(/abc\n[/]/.test('def')); var_dump(/[\n]/.source.slice(1,-1)); var_dump("./a/b/c".replace(/\.\//, '')); + +var matches = /[abc]/gi.exec("LA la black sheep"); diff --git a/test/fixtures/regexp.php b/test/fixtures/regexp.php index 918cc32..8b25bd1 100644 --- a/test/fixtures/regexp.php +++ b/test/fixtures/regexp.php @@ -26,4 +26,6 @@ var_dump(preg_match("/abc\\n[\\/]/", 'def')); var_dump(substr("[\\n]", 1, -1)); -var_dump(preg_replace('/\.\//', '', "./a/b/c", 1)); \ No newline at end of file +var_dump(preg_replace('/\.\//', '', "./a/b/c", 1)); + +$matches = /*RegExp#exec*/preg_match("/[abc]/", "LA la black sheep", $FIXME); \ No newline at end of file From 02a15c13c9e87a988364e612e0a1e23f5dd93bcb Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Tue, 5 Feb 2019 13:04:57 -0500 Subject: [PATCH 18/52] Avoid unwanted aliases when looking up core library functions This prevents matching 'hasOwnProperty' and other properties of object. --- core.js | 12 ++++++++---- test/fixtures/core_object.js | 4 ++++ test/fixtures/core_object.php | 6 +++++- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/core.js b/core.js index 5ed4e5b..48270bb 100644 --- a/core.js +++ b/core.js @@ -17,6 +17,10 @@ module.exports = { evaluate: function(node) { var handler = undefined; + var get = function(obj, name) { + if (obj.hasOwnProperty(name)) { return obj[name]; } + return undefined; + }; if (node.object && node.object.type === 'MemberExpression') { var newNode = module.exports.evaluate(node.object); @@ -26,17 +30,17 @@ module.exports = { } if (node.object && node.object.type === 'Literal') { var method = node.property.name; - handler = node.object.regex ? _regexp[method] : _string[method]; + handler = node.object.regex ? get(_regexp, method) : get(_string, method); } else if (node.object && node.object.type === 'Identifier' && /^(Array|Object|Promise|console)$/.test(node.object.name)) { var longName = node.object.name + '_' + node.property.name; - handler = _array[longName] || _object[longName] || _promise[longName] || _console[longName]; + handler = get(_array, longName) || get(_object, longName) || get(_promise, longName) || get(_console, longName); } else if (node.property) { var method = node.property.name; // _array should be before _string here so we pick up the correct // multitype version of #length and #indexOf - handler = _array[method] || _date[method] || _function[method] || _json[method] || _string[method] || _math[method] || _number[method]; + handler = get(_array, method) || get(_date, method) || get(_function, method) || get(_json, method) || get(_string, method) || get(_math, method) || get(_number, method); } else if (node.callee) { - handler = _global[node.callee.name]; + handler = get(_global, node.callee.name); } if (handler) { diff --git a/test/fixtures/core_object.js b/test/fixtures/core_object.js index d79ab63..1dc99b9 100644 --- a/test/fixtures/core_object.js +++ b/test/fixtures/core_object.js @@ -1,3 +1,7 @@ var a = Object.create(null); a["foo"] = "bar"; var_dump(a); + +// Avoid a crash when looking up a method named hasOwnProperty +var b = {}; +var_dump(b.hasOwnProperty('foo')); diff --git a/test/fixtures/core_object.php b/test/fixtures/core_object.php index cede6d3..a687c1c 100644 --- a/test/fixtures/core_object.php +++ b/test/fixtures/core_object.php @@ -1,4 +1,8 @@ hasOwnProperty('foo')); \ No newline at end of file From d48896ee5d4ee7f17f68599e75872fa52d802d99 Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Tue, 5 Feb 2019 13:05:52 -0500 Subject: [PATCH 19/52] Bug fix to binary operators when variable wasn't initialized --- index.js | 3 ++- test/fixtures/concat.js | 8 +++++++- test/fixtures/concat.php | 8 +++++++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index da01178..f8f26d5 100644 --- a/index.js +++ b/index.js @@ -362,7 +362,8 @@ module.exports = function(code, options) { if (leftDefinition && rightDefinition) { if (leftDefinition.type == "VariableDeclarator" && rightDefinition.type == "VariableDeclarator") { - if (utils.isString(leftDefinition.init) && utils.isString(rightDefinition.init)) { + if (leftDefinition.init && utils.isString(leftDefinition.init) && + rightDefinition.init && utils.isString(rightDefinition.init)) { node.operator = node.operator.replace('+', '.'); } } diff --git a/test/fixtures/concat.js b/test/fixtures/concat.js index 4bbb3c6..08e7300 100644 --- a/test/fixtures/concat.js +++ b/test/fixtures/concat.js @@ -1,4 +1,10 @@ var string1 = "string1"; var string2 = "string2"; var string3 = string1 + string2; -var_dump(string1+string2); \ No newline at end of file +var string4; + +var_dump(string1+string2); + +// Avoid a crash if/when one side of the operator is undefined at init +string4 = "later"; +var_dump(string1+string4); diff --git a/test/fixtures/concat.php b/test/fixtures/concat.php index 50ffce5..33d7375 100644 --- a/test/fixtures/concat.php +++ b/test/fixtures/concat.php @@ -2,4 +2,10 @@ $string1 = "string1"; $string2 = "string2"; $string3 = $string1 . $string2; -var_dump($string1 . $string2); \ No newline at end of file +$string4 = null; + +var_dump($string1 . $string2); + +// Avoid a crash if/when one side of the operator is undefined at init +$string4 = "later"; +var_dump($string1 + $string4); \ No newline at end of file From 22d00678ae7638365e1ef46c0a4713b73c3b70e6 Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Tue, 5 Feb 2019 17:04:35 -0500 Subject: [PATCH 20/52] Support SequenceExpression and ObjectPattern assignments --- index.js | 110 +++++++++++++++++++++++++++---- scope.js | 17 ++++- test/fixtures/object_pattern.js | 5 ++ test/fixtures/object_pattern.php | 6 ++ 4 files changed, 124 insertions(+), 14 deletions(-) create mode 100644 test/fixtures/object_pattern.js create mode 100644 test/fixtures/object_pattern.php diff --git a/index.js b/index.js index f8f26d5..ab8f79d 100644 --- a/index.js +++ b/index.js @@ -133,8 +133,9 @@ module.exports = function(code, options) { var startT = node && node.loc && tokenEndMap[locToKey(node.loc.start)]; var endT = node && node.loc && tokenStartMap[locToKey(node.loc.end)]; if ( - startT && startT.type==='Punctuator' && startT.value === '(' && - endT && endT.type==='Punctuator' && endT.value === ')' + node.forceParens || + (startT && startT.type==='Punctuator' && startT.value === '(' && + endT && endT.type==='Punctuator' && endT.value === ')') ) { this.emit('('); } @@ -145,9 +146,9 @@ module.exports = function(code, options) { if (!(node && node.suppressParens)) { var startT = node && node.loc && tokenEndMap[locToKey(node.loc.start)]; var endT = node && node.loc && tokenStartMap[locToKey(node.loc.end)]; - if ( - startT && startT.type==='Punctuator' && startT.value === '(' && - endT && endT.type==='Punctuator' && endT.value === ')' + if (node.forceParens || + (startT && startT.type==='Punctuator' && startT.value === '(' && + endT && endT.type==='Punctuator' && endT.value === ')') ) { this.emit(')'); } @@ -298,6 +299,27 @@ module.exports = function(code, options) { visit(node.declarations[i], node); } + } else if (node.type == "VariableDeclarator" && node.id.type === 'ObjectPattern') { + var tmpName = scope.get(node).getTmpName(); + visit({ + type: 'VariableDeclarator', + id: { type: 'Identifier', name: tmpName }, + init: node.init, + }, node); + node.id.properties.forEach(function(p) { + visit({ + type: 'VariableDeclarator', + id: p.value, + init: { + type: 'MemberExpression', + object: { + type: 'Identifier', + name: tmpName, + }, + property: p.key, + }, + }, node); + }); } else if (node.type == "VariableDeclarator") { scope.get(node).register(node); var isForStatement = node.parent && node.parent.parent && @@ -375,6 +397,38 @@ module.exports = function(code, options) { emitter.decrIndent(); } + } else if (node.type == "AssignmentExpression" && node.left.type == "ObjectPattern") { + var tmpName = scope.get(node).getTmpName(); + var expressions = [ + { + type: 'AssignmentExpression', + operator: '=', + left: { + type: 'Identifier', + name: tmpName, + }, + right: node.right, + }, + ]; + node.left.properties.forEach(function(p) { + expressions.push({ + type: 'AssignmentExpression', + operator: '=', + left: p.value, + right: { + type: 'MemberExpression', + object: { + type: 'Identifier', + name: tmpName, + }, + property: p.key, + }, + }); + }); + visit({ + type: 'SequenceExpression', + expressions + }, node); } else if (node.type == "AssignmentExpression" || node.type == "AssignmentPattern") { scope.get(node).register(node.left); @@ -480,9 +534,11 @@ module.exports = function(code, options) { } else if (node.parent.type == "AssignmentExpression") { identifier = node.parent.left.name; } - emitter.emit(";"); - emitter.nl(); - emitter.emit("$" + identifier + " = " + "$" + identifier); + if (!node.callee.isSequence) { + emitter.emit(";"); + emitter.nl(); + emitter.emit("$" + identifier + " = " + "$" + identifier); + } } if (node.arguments) { @@ -776,15 +832,47 @@ module.exports = function(code, options) { } } - } else if (node.type == "SequenceExpression") { + } else if (node.type == "SequenceExpression" && node.parent.type === 'ForStatement') { + // PHP doesn't really have a true SequenceExpression (aka comma operator) + // This version works iff the sequence expression is in a for loop: for (var i=0;i 'foo', "bar" => 42]; +$temp0 = $x;$foo = $temp0->foo;$bar = $temp0->bar; +$y = 'not foo'; +(((function () use (&$x) {$temp1 = $x;return $y = $temp1->foo;}))()); +var_dump("{$foo} {$bar} {$y}"); \ No newline at end of file From 97b1cdd35150566b633e5200ea4499d175747eec Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Tue, 5 Feb 2019 17:23:31 -0500 Subject: [PATCH 21/52] Improvements to regular expression pattern for core library functions --- core/string.js | 5 +++-- test/fixtures/core_string.js | 5 +++++ test/fixtures/core_string.php | 5 +++++ utils.js | 2 +- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/core/string.js b/core/string.js index 9bd217e..ae03872 100644 --- a/core/string.js +++ b/core/string.js @@ -170,7 +170,8 @@ module.exports = { node.parent.arguments = false; - var regexpData = args[0].raw.match(/^\/([^\/]+)\/([gimy])?$/), + var regexpData = args[0].type==='Literal' && + args[0].raw.match(/^\/((?:[^\/]|\\.)+)\/([gimy])?$/), regex = regexpData && regexpData[1], flags = regexpData && regexpData[2] || ""; @@ -224,7 +225,7 @@ module.exports = { if(args[0].type === 'Literal') { - var regexpData = args[0].raw.match(/^\/([^\/]+)\/([gimy])?$/), + var regexpData = args[0].raw.match(/^\/((?:[^\/]|\\.)+)\/([gimy])?$/), regex = regexpData && regexpData[1], flags = regexpData && regexpData[2] || "", isGroup = flags.indexOf('g') >= 0; diff --git a/test/fixtures/core_string.js b/test/fixtures/core_string.js index a89628b..6df119e 100644 --- a/test/fixtures/core_string.js +++ b/test/fixtures/core_string.js @@ -29,6 +29,11 @@ echo(strArray[0]); var strArray2 = str.split(''); echo(strArray2[0]); +// shouldn't crash with identifier as argument. +var xyz = 'll'; +var strArray3 = str.split(xyz); +echo(strArray3[0]); + var_dump('testing'.length); var test_length = 'testing_string_variable'; diff --git a/test/fixtures/core_string.php b/test/fixtures/core_string.php index a62cdc8..d5f5d96 100644 --- a/test/fixtures/core_string.php +++ b/test/fixtures/core_string.php @@ -30,6 +30,11 @@ $strArray2 = str_split($str); echo($strArray2[0]); +// shouldn't crash with identifier as argument. +$xyz = 'll'; +$strArray3 = explode($xyz, $str); +echo($strArray3[0]); + var_dump(strlen('testing')); $test_length = 'testing_string_variable'; diff --git a/utils.js b/utils.js index 8e198db..22edd3a 100644 --- a/utils.js +++ b/utils.js @@ -47,7 +47,7 @@ module.exports = { value = value.substr(1, node.raw.length-2); } - var isRegExp = value.match(/^\/[^\/]+\/[gimy]?$/); + var isRegExp = value.match(/^\/(?:[^\/]|\\.)+\/[gimy]?$/); if (isRegExp) { node.raw.value = "'" + value + "'"; From afc9702608c90002f7fb00b81560ff5e2e23531d Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Tue, 5 Feb 2019 17:41:58 -0500 Subject: [PATCH 22/52] Bug fix to for loops when initializer, test, or update expression is missing --- index.js | 8 +++++--- test/fixtures/loops.js | 4 ++++ test/fixtures/loops.php | 4 ++++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index ab8f79d..c4606b0 100644 --- a/index.js +++ b/index.js @@ -892,9 +892,11 @@ module.exports = function(code, options) { } else if (node.type == "ForStatement") { emitter.emit("for "); emitter.block('(', function() { - visit(node.init, node); emitter.ensureSemi(); emitter.emit(' '); - visit(node.test, node); emitter.ensureSemi(); emitter.emit(' '); - visit(node.update, node); + if (node.init) { visit(node.init, node); } + emitter.ensureSemi(); emitter.emit(' '); + if (node.test) { visit(node.test, node); } else { emitter.emit('true'); } + emitter.ensureSemi(); emitter.emit(' '); + if (node.update) { visit(node.update, node); } }, ')'); emitter.emit(' '); emitter.block('{', function() { visit(node.body, node); }, '}'); diff --git a/test/fixtures/loops.js b/test/fixtures/loops.js index 733b7cb..78503ad 100644 --- a/test/fixtures/loops.js +++ b/test/fixtures/loops.js @@ -36,3 +36,7 @@ for (i=0, j=2; i5) break; +} diff --git a/test/fixtures/loops.php b/test/fixtures/loops.php index b3a15d1..6851984 100644 --- a/test/fixtures/loops.php +++ b/test/fixtures/loops.php @@ -36,4 +36,8 @@ } for ($k = 0, $count = 2; $k < $count; $k++) { echo($k); +} +for (; true; ) { + $i++; + if ($i > 5) {break;} } \ No newline at end of file From b4bd92a35dfd10f07b7b2a45605f13da91b935e3 Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Mon, 11 Feb 2019 16:29:50 -0500 Subject: [PATCH 23/52] Tweak to match the Wikimedia PHP code style better (in particular, whitespace) --- index.js | 140 +++++++++++++++------------ test/fixtures/anonymous_function.php | 16 +-- test/fixtures/array_index.php | 6 +- test/fixtures/arrow_functions.php | 8 +- test/fixtures/assert.php | 4 +- test/fixtures/class.php | 44 +++++---- test/fixtures/class_inheritance.php | 10 +- test/fixtures/closures.php | 8 +- test/fixtures/concat.php | 4 +- test/fixtures/conditionals.php | 30 +++--- test/fixtures/core_array.php | 33 ++++--- test/fixtures/core_function.php | 10 +- test/fixtures/core_json.php | 16 +-- test/fixtures/core_math.php | 6 +- test/fixtures/core_number.php | 4 +- test/fixtures/core_object.php | 6 +- test/fixtures/core_string.php | 56 +++++------ test/fixtures/date.php | 2 +- test/fixtures/exceptions.php | 14 +-- test/fixtures/expression.php | 4 +- test/fixtures/failures.php | 6 +- test/fixtures/for_of.php | 12 +-- test/fixtures/function.php | 16 +-- test/fixtures/function_super.php | 12 +-- test/fixtures/global_functions.php | 12 +-- test/fixtures/iife.php | 12 +-- test/fixtures/loops.php | 32 +++--- test/fixtures/namespaces.php | 6 +- test/fixtures/namespaces_require.php | 6 +- test/fixtures/namespaces_use.php | 2 +- test/fixtures/object_pattern.php | 8 +- test/fixtures/parsoid.php | 8 +- test/fixtures/regexp.php | 38 ++++---- test/fixtures/rest_spread.php | 8 +- test/fixtures/simple.php | 14 +-- test/fixtures/static_call.php | 22 +++-- test/fixtures/string_template.php | 2 +- 37 files changed, 331 insertions(+), 306 deletions(-) diff --git a/index.js b/index.js index c4606b0..e3968f4 100644 --- a/index.js +++ b/index.js @@ -98,10 +98,18 @@ module.exports = function(code, options) { } Emitter.prototype.block = function(open, f, close) { var firstline = this.line; - this.emit(open); + this.emit(open); this.emit(' '); + var checkpoint = this.buffer.length; this.incrIndent(); f(); - if (this.line !== firstline) { this.ensureNl(); } + if (this.line > firstline) { + this.ensureNl(); + } else if (this.buffer.length !== checkpoint) { + this.emit(' '); + } else { + // no content in block => no space + this.buffer = this.buffer.replace(/[\t ]+$/, ''); + } this.decrIndent(); this.emit(close); }; @@ -137,7 +145,7 @@ module.exports = function(code, options) { (startT && startT.type==='Punctuator' && startT.value === '(' && endT && endT.type==='Punctuator' && endT.value === ')') ) { - this.emit('('); + this.emit('( '); } } }; @@ -149,7 +157,8 @@ module.exports = function(code, options) { if (node.forceParens || (startT && startT.type==='Punctuator' && startT.value === '(' && endT && endT.type==='Punctuator' && endT.value === ')') - ) { + ) { + if (!this.endsInNl()) this.emit(' '); this.emit(')'); } } @@ -180,14 +189,17 @@ module.exports = function(code, options) { } c.emitted = true; }; + Emitter.prototype.endsInNl = function() { + return /\n[ \t]*$/.test(this.buffer); + }; Emitter.prototype.isSemiLast = function() { return this.buffer.match(/;\n?[ ]*$/); } Emitter.prototype.ensureSemi = function() { - if (!emitter.isSemiLast()) { this.emit(';'); } + if (!emitter.isSemiLast()) { this.emit('; '); } }; Emitter.prototype.ensureNl = function() { - if (!/\n[ \t]*$/.test(this.buffer)) { + if (!this.endsInNl()) { this.nl(); } }; @@ -437,10 +449,11 @@ module.exports = function(code, options) { visit(node.right, node); } else if (node.type == "ConditionalExpression") { - emitter.emit('('); - node.test.suppressParens = true; - visit(node.test, node); - emitter.emit(') ? '); + emitter.block('(', function() { + node.test.suppressParens = true; + visit(node.test, node); + }, ')'); + emitter.emit(' ? '); visit(node.consequent, node); emitter.emit(' : '); visit(node.alternate, node); @@ -492,7 +505,7 @@ module.exports = function(code, options) { if (isIIFE) { node.expression.isIIFE = true; node.expression.suppressParens = true; - iife = "call_user_func("; + iife = "call_user_func( "; } emitter.emit(iife); @@ -544,19 +557,17 @@ module.exports = function(code, options) { if (node.arguments) { var arguments = []; - if (node.isIIFE) { - if (node.arguments.length) emitter.emit(','); - } else { - emitter.emit('('); - emitter.incrIndent(); - } - for (var i=0, length = node.arguments.length; i < length; i++) { - if (node.arguments.length===1) { node.arguments[i].suppressParens=true; } - visit(node.arguments[i], node); - if ((i+1) < length) { emitter.emit(', '); } - } - emitter.decrIndent(); - emitter.emit(')'); + emitter.block( + node.isIIFE ? (node.arguments.length ? ',' : '') : '(', function() { + for (var i=0, length = node.arguments.length; i < length; i++) { + if (node.arguments.length===1) { + node.arguments[i].suppressParens=true; + } + visit(node.arguments[i], node); + if ((i+1) < length) { emitter.emit(', '); } + } + }, ')' + ); } // allow semicolon if parent node isn't MemberExpression or Property @@ -619,39 +630,40 @@ module.exports = function(code, options) { node.type == "ArrowFunctionExpression") { var defaults = node.defaults || []; - emitter.emit("function " + ((node.id) ? node.id.name : "") + "("); - emitter.incrIndent(); - - // function declaration creates a new scope - scope.create(node); + emitter.emit("function " + ((node.id) ? node.id.name : "")); + emitter.block('(', function() { - // compute function params - for (var i=0; i < node.params.length; i++) { - if (defaults[i]) { - visit({ - type: "BinaryExpression", - left: node.params[i], - operator: '=', - right: defaults[i] - }, node); - } else { - if (node.params.length===1) { node.params[i].suppressParens=true; } - visit(node.params[i], node) - } - if ((i+1) < node.params.length) { - emitter.emit(', '); - } + // function declaration creates a new scope + scope.create(node); + + // compute function params + for (var i=0; i < node.params.length; i++) { + if (defaults[i]) { + visit({ + type: "BinaryExpression", + left: node.params[i], + operator: '=', + right: defaults[i] + }, node); + } else { + if (node.params.length===1) { node.params[i].suppressParens=true; } + visit(node.params[i], node) + } + if ((i+1) < node.params.length) { + emitter.emit(', '); + } - // register parameter identifiers - if (scope.get(node).parent) { - scope.get(node).register(node.params[i]); + // register parameter identifiers + if (scope.get(node).parent) { + scope.get(node).register(node.params[i]); + } } - } - emitter.decrIndent(); - emitter.emit(') '); + }, ')'); + emitter.emit(' '); emitter.pushInsertionPoint(); - emitter.block('{', function() { - emitter.pushInsertionPoint(); + emitter.emit('{'); + emitter.pushInsertionPoint(); + emitter.block('', function() { visit(node.body, node); /* function contents */ var using = scope.get(node).using @@ -661,9 +673,9 @@ module.exports = function(code, options) { // try to use parent's variables // http://php.net/manual/pt_BR/functions.anonymous.php if (using.length > 0 && node.parent.type !== "Program") { - emitter.insertAt(1, "use (" + using.map(function(identifier) { + emitter.insertAt(1, "use ( " + using.map(function(identifier) { return "&$" + identifier; - }).join(', ') + ") "); + }).join(', ') + " ) "); } // workaround when scope doesn't allow to have the `use` keyword. @@ -733,6 +745,7 @@ module.exports = function(code, options) { visit(s.getters[i].value.body, node); }, '}'); } + emitter.nl(); }, '}'); emitter.nl(); } @@ -796,7 +809,7 @@ module.exports = function(code, options) { emitter.emit('public '); definitions[i].property.suppressLoc = true; visit(definitions[i].property, null); - emitter.emit(";"); + emitter.emit("; "); //emitter.locEnd(definitions[i].property); } } @@ -877,7 +890,7 @@ module.exports = function(code, options) { } else if (node.type == "WhileStatement") { emitter.emit("while "); - emitter.block('( ', function() { node.test.suppressParens = true; visit(node.test, node); }, ' )'); + emitter.block('(', function() { node.test.suppressParens = true; visit(node.test, node); }, ')'); emitter.emit(' '); emitter.block('{', function() { visit(node.body, node); }, '}'); @@ -950,10 +963,10 @@ module.exports = function(code, options) { } } else if (node.type == "BreakStatement") { - emitter.emit("break;"); + emitter.emit("break; "); } else if (node.type == "ContinueStatement") { - emitter.emit("continue;"); + emitter.emit("continue; "); } else if (node.type == "NewExpression") { // re-use CallExpression for NewExpression's @@ -975,7 +988,7 @@ module.exports = function(code, options) { // Modules & Export (http://wiki.ecmascript.org/doku.php?id=harmony:modules_examples) } else if (node.type == "ModuleDeclaration") { - emitter.emit("namespace " + utils.classize(node.id.value) + ";"); + emitter.emit("namespace " + utils.classize(node.id.value) + "; "); visit(node.body, node); } else if (node.type == "ExportNamedDeclaration") { @@ -1029,11 +1042,11 @@ module.exports = function(code, options) { } } else if (node.type === "CatchClause") { - emitter.emit(' catch (Exception '); + emitter.emit(' catch ( Exception '); scope.create(node.param, node); node.param.suppressParens = true; visit(node.param, node); - emitter.emit(") "); + emitter.emit(" ) "); emitter.block('{', function() { visit(node.body, node); }, '}'); } else if (node.type === "ThrowStatement") { emitter.emit("throw "); @@ -1065,5 +1078,6 @@ module.exports = function(code, options) { emitter.emit(`/* ${options.watermark} */\n`); } visit(ast); - return emitter.toString(); + emitter.ensureNl(); + return emitter.toString().replace(/[ \t]+\n/g, '\n'); // remove trailing space } diff --git a/test/fixtures/anonymous_function.php b/test/fixtures/anonymous_function.php index a214ac1..5830ee3 100644 --- a/test/fixtures/anonymous_function.php +++ b/test/fixtures/anonymous_function.php @@ -1,14 +1,14 @@ _something = $something; } public $_something; - public function toArray($model, $array) { + public function toArray( $model, $array ) { return $array; } @@ -23,28 +23,32 @@ public function hello() { - public function creating($model) { - Mail::send([ - "body" => Module::template('signup-confirmation')->compile([ - "base_url" => AppConfig::get("retrieve.email.url") - ]), + public function creating( $model ) { + Mail::send( [ + "body" => Module::template( 'signup-confirmation' )->compile( [ + "base_url" => AppConfig::get( "retrieve.email.url" ) + ] + ), "subject" => "Sign-up confirmation", "to" => $model->email, "from" => "somebody@example.com" - ]); + ] + ); } - public function updating($model) { - if ($model->isDirty('status') && $model->status == 1) { + public function updating( $model ) { + if ( $model->isDirty( 'status' ) && $model->status == 1 ) { - Mail::send([ - "body" => Module::template('signup-approved')->compile([ - "BASE_URL" => AppConfig::get("retrieve.email.url") - ]), + Mail::send( [ + "body" => Module::template( 'signup-approved' )->compile( [ + "BASE_URL" => AppConfig::get( "retrieve.email.url" ) + ] + ), "subject" => "Approved!", "to" => $model->email, "from" => "somebody@example.com" - ]); + ] + ); } } @@ -52,8 +56,8 @@ public function updating($model) { function __get($_property) { if ($_property === 'something') { return "Something: " + $this->_something; - } - } + } + } function __set($_property, $value) { if ($_property === 'something') { $this->_something = $value + 10; @@ -88,6 +92,6 @@ function __set($_property, $value) { // Instantiate class and call getter method -$example = new ClassExample("awesome"); -var_dump(call_user_func_array([$example, 'hello'], [])); -var_dump($example->hello()); \ No newline at end of file +$example = new ClassExample( "awesome" ); +var_dump( call_user_func_array( [ $example, 'hello' ], [] ) ); +var_dump( $example->hello() ); diff --git a/test/fixtures/class_inheritance.php b/test/fixtures/class_inheritance.php index 5eaf407..02fe9e0 100644 --- a/test/fixtures/class_inheritance.php +++ b/test/fixtures/class_inheritance.php @@ -1,6 +1,6 @@ title = $title; $this->body = $body; } @@ -13,8 +13,8 @@ public function getDescription() { } } class Article extends Page { - public function __construct($title, $body, $author) { - parent::__construct($title, $body); + public function __construct( $title, $body, $author ) { + parent::__construct( $title, $body ); $this->author = $author; } public $author; @@ -26,5 +26,5 @@ public function getDescription() { } } -$article = new Article("Wonderful article", "Yada Yada Yada", "Bob Loblaw"); -var_dump($article->getDescription()); \ No newline at end of file +$article = new Article( "Wonderful article", "Yada Yada Yada", "Bob Loblaw" ); +var_dump( $article->getDescription() ); diff --git a/test/fixtures/closures.php b/test/fixtures/closures.php index f67df9e..8b4e4b4 100644 --- a/test/fixtures/closures.php +++ b/test/fixtures/closures.php @@ -1,11 +1,11 @@ "Three"], $items)); -echo(join(", ", $items)); -echo("\n"); +echo( count( $items ) ); +echo( array_search( [ "name" => "Three" ], $items ) ); +echo( join( ", ", $items ) ); +echo( "\n" ); -$count = array_reduce($items, function ($curr, $string) { - return $curr + strlen($string); - }, 0) +$count = array_reduce( $items, function ( $curr, $string ) { + return $curr + strlen( $string ); + }, 0 +) ; -var_dump($count); +var_dump( $count ); -var_dump(is_array($items)); -var_dump(is_array($count)); \ No newline at end of file +var_dump( is_array( $items ) ); +var_dump( is_array( $count ) ); diff --git a/test/fixtures/core_function.php b/test/fixtures/core_function.php index 42c06fb..d9ecbe9 100644 --- a/test/fixtures/core_function.php +++ b/test/fixtures/core_function.php @@ -4,7 +4,7 @@ public function without_params() { return "I'm a method without params."; } - public function with_default_params($param1 = "default") { + public function with_default_params( $param1 = "default" ) { return $param1; } } @@ -18,7 +18,7 @@ function global_function_with_params() { } $klass = new Klass(); -var_dump(call_user_func_array([$klass, 'without_params'], [])); -var_dump(call_user_func_array([$klass, 'with_default_params'], [])); -var_dump(call_user_func_array([$klass, 'with_default_params'], ["Hey!"])); -var_dump(call_user_func([$klass, 'with_default_params'], "Hey!")); +var_dump( call_user_func_array( [ $klass, 'without_params' ], [] ) ); +var_dump( call_user_func_array( [ $klass, 'with_default_params' ], [] ) ); +var_dump( call_user_func_array( [ $klass, 'with_default_params' ], [ "Hey!" ] ) ); +var_dump( call_user_func( [ $klass, 'with_default_params' ], "Hey!" ) ); diff --git a/test/fixtures/core_json.php b/test/fixtures/core_json.php index 5feec6e..9467a9b 100644 --- a/test/fixtures/core_json.php +++ b/test/fixtures/core_json.php @@ -1,20 +1,22 @@ 5, "string" => "hey", - "nested" => ["objects" => ["here" => "yey"]] - ]); + "nested" => [ "objects" => [ "here" => "yey" ] ] + ] +); ; -var_dump(json_encode([ +var_dump( json_encode( [ "integer" => 5, "string" => "hey", - "nested" => ["objects" => ["here" => "yey"]] - ]) + "nested" => [ "objects" => [ "here" => "yey" ] ] + ] + ) -); \ No newline at end of file +); diff --git a/test/fixtures/core_math.php b/test/fixtures/core_math.php index ab1e4b8..fe000b1 100644 --- a/test/fixtures/core_math.php +++ b/test/fixtures/core_math.php @@ -1,5 +1,5 @@ hasOwnProperty('foo')); \ No newline at end of file +var_dump( $b->hasOwnProperty( 'foo' ) ); diff --git a/test/fixtures/core_string.php b/test/fixtures/core_string.php index d5f5d96..4c3f7c4 100644 --- a/test/fixtures/core_string.php +++ b/test/fixtures/core_string.php @@ -1,55 +1,55 @@ "one"], - ["name" => "two"], - ["name" => "three"] + [ "name" => "one" ], + [ "name" => "two" ], + [ "name" => "three" ] ]; -foreach ($items as $item => $___) { - echo($item['name']); +foreach ( $items as $item => $___ ) { + echo( $item[ 'name' ] ); continue; -} \ No newline at end of file +} diff --git a/test/fixtures/function.php b/test/fixtures/function.php index db84e8e..9324d83 100644 --- a/test/fixtures/function.php +++ b/test/fixtures/function.php @@ -1,21 +1,21 @@ read()); +echo( $article->read() ); diff --git a/test/fixtures/global_functions.php b/test/fixtures/global_functions.php index e15949b..dbe503a 100644 --- a/test/fixtures/global_functions.php +++ b/test/fixtures/global_functions.php @@ -1,10 +1,10 @@ 'three' ]; -foreach ($obj as $i => $___) { - var_dump($i, $obj[$i]); +foreach ( $obj as $i => $___ ) { + var_dump( $i, $obj[ $i ] ); } $j = 10; while ( $j > 0 ) { $j--; - var_dump($j); + var_dump( $j ); } $xxx = 10; do { --$xxx; -} while ($xxx > 0); +} while ( $xxx > 0 ); -for ($i = 0, $j = 2; $i < $j; $i++) { - echo($i); +for ( $i = 0, $j = 2; $i < $j; $i++ ) { + echo( $i ); } -for ($k = 0, $count = 2; $k < $count; $k++) { - echo($k); +for ( $k = 0, $count = 2; $k < $count; $k++ ) { + echo( $k ); } -for (; true; ) { +for ( ; true; ) { $i++; - if ($i > 5) {break;} -} \ No newline at end of file + if ( $i > 5 ) { break; } +} diff --git a/test/fixtures/namespaces.php b/test/fixtures/namespaces.php index 5ec955d..1381b13 100644 --- a/test/fixtures/namespaces.php +++ b/test/fixtures/namespaces.php @@ -4,14 +4,14 @@ public function __construct() { } public static function do_something() { - var_dump("Something!"); + var_dump( "Something!" ); } } class Hello { } -function sum($x, $y) { +function sum( $x, $y ) { return $x + $y; } -$pi = 3.141593; \ No newline at end of file +$pi = 3.141593; diff --git a/test/fixtures/namespaces_require.php b/test/fixtures/namespaces_require.php index ac08073..839154e 100644 --- a/test/fixtures/namespaces_require.php +++ b/test/fixtures/namespaces_require.php @@ -11,6 +11,6 @@ Bar::do_something(); Bat::do_something(); Bat::do_something()->do_something_else(); -echo(semver::satisfies(1, 2)); -echo(semver::satisfies(1, 2)->something_else()); -echo(["Foo" => Foo::class, "Bar" => Bar::class]); \ No newline at end of file +echo( semver::satisfies( 1, 2 ) ); +echo( semver::satisfies( 1, 2 )->something_else() ); +echo( [ "Foo" => Foo::class, "Bar" => Bar::class ] ); diff --git a/test/fixtures/namespaces_use.php b/test/fixtures/namespaces_use.php index ddae0df..ee3c555 100644 --- a/test/fixtures/namespaces_use.php +++ b/test/fixtures/namespaces_use.php @@ -3,4 +3,4 @@ use \Ns\Hello as Hello; Formula1::do_something(); -Hello::do_something(); \ No newline at end of file +Hello::do_something(); diff --git a/test/fixtures/object_pattern.php b/test/fixtures/object_pattern.php index e0c3a5d..70242f5 100644 --- a/test/fixtures/object_pattern.php +++ b/test/fixtures/object_pattern.php @@ -1,6 +1,6 @@ 'foo', "bar" => 42]; -$temp0 = $x;$foo = $temp0->foo;$bar = $temp0->bar; +$x = [ "foo" => 'foo', "bar" => 42 ]; +$temp0 = $x; $foo = $temp0->foo; $bar = $temp0->bar; $y = 'not foo'; -(((function () use (&$x) {$temp1 = $x;return $y = $temp1->foo;}))()); -var_dump("{$foo} {$bar} {$y}"); \ No newline at end of file +( ( ( function () use ( &$x ) { $temp1 = $x; return $y = $temp1->foo; } ) )() ); +var_dump( "{$foo} {$bar} {$y}" ); diff --git a/test/fixtures/parsoid.php b/test/fixtures/parsoid.php index a559ccb..9746cbc 100644 --- a/test/fixtures/parsoid.php +++ b/test/fixtures/parsoid.php @@ -3,13 +3,13 @@ use Bat; -$bar = /* async */function ($x) use (&$Bat) { - return (/* await */ Bat::bat()); +$bar = /* async */function ( $x ) use ( &$Bat ) { + return ( /* await */ Bat::bat() ); } ; class Foo { - public function __construct() {$this->x = 1;} + public function __construct() { $this->x = 1; } public $x; -} \ No newline at end of file +} diff --git a/test/fixtures/regexp.php b/test/fixtures/regexp.php index 8b25bd1..272c8c8 100644 --- a/test/fixtures/regexp.php +++ b/test/fixtures/regexp.php @@ -1,31 +1,31 @@ log($x, $y); -$console->log($z); +$console->log( $x, $y ); +$console->log( $z ); -var_dump(["a" => 1, "b-c" => 'c', "d-e-fh" => g(0), "hi" => "hi"]); +var_dump( [ "a" => 1, "b-c" => 'c', "d-e-fh" => g( 0 ), "hi" => "hi" ] ); -$obj = ["key" => 'value', "key2" => 'value2']; -unset($obj['key']); -var_dump(isset($something['key2'])); -var_dump(gettype($something) !== NULL); +$obj = [ "key" => 'value', "key2" => 'value2' ]; +unset( $obj[ 'key' ] ); +var_dump( isset( $something[ 'key2' ] ) ); +var_dump( gettype( $something ) !== NULL ); diff --git a/test/fixtures/static_call.php b/test/fixtures/static_call.php index db0fa6e..6400004 100644 --- a/test/fixtures/static_call.php +++ b/test/fixtures/static_call.php @@ -1,14 +1,18 @@ json([ - "success" => Mail::send([ - "body" => Module::template('signup-confirmation')->compile([ - "base_url" => AppConfig::get("retrieve.email.url") - ]), +Module\Http\Router::get( '/send', function () { + return $this->json( [ + "success" => Mail::send( [ + "body" => Module::template( 'signup-confirmation' )->compile( [ + "base_url" => AppConfig::get( "retrieve.email.url" ) + ] + ), "subject" => "Sign-up confirmation", "to" => $model->email, "from" => "somebody@example.com" - ]) - ]); - }); \ No newline at end of file + ] + ) + ] + ); + } +); diff --git a/test/fixtures/string_template.php b/test/fixtures/string_template.php index 70c62ba..405b31d 100644 --- a/test/fixtures/string_template.php +++ b/test/fixtures/string_template.php @@ -15,4 +15,4 @@ public function method() { } $item = new Item(); -echo("{$str1}, {$str2}, {$n1}, {$n2}, {$item->method()}, {$func()}"); +echo( "{$str1}, {$str2}, {$n1}, {$n2}, {$item->method()}, {$func()}" ); From 4085195135757d2e48a35f9edb92f2343c8bee72 Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Tue, 12 Feb 2019 09:58:22 -0500 Subject: [PATCH 24/52] Factor out utils.isType() and utils.isId() to simplify core.js --- core.js | 6 +++--- utils.js | 12 +++++++++++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/core.js b/core.js index 48270bb..5891277 100644 --- a/core.js +++ b/core.js @@ -22,16 +22,16 @@ module.exports = { return undefined; }; - if (node.object && node.object.type === 'MemberExpression') { + if (utils.isType(node.object, 'MemberExpression')) { var newNode = module.exports.evaluate(node.object); if (newNode !== node.object) { node.object = newNode; } } - if (node.object && node.object.type === 'Literal') { + if (utils.isType(node.object, 'Literal')) { var method = node.property.name; handler = node.object.regex ? get(_regexp, method) : get(_string, method); - } else if (node.object && node.object.type === 'Identifier' && /^(Array|Object|Promise|console)$/.test(node.object.name)) { + } else if (utils.isId(node.object, /^(Array|Object|Promise|console)$/)) { var longName = node.object.name + '_' + node.property.name; handler = get(_array, longName) || get(_object, longName) || get(_promise, longName) || get(_console, longName); } else if (node.property) { diff --git a/utils.js b/utils.js index 22edd3a..b7beade 100644 --- a/utils.js +++ b/utils.js @@ -55,5 +55,15 @@ module.exports = { } return isRegExp; - } + }, + + isType: function(node, type) { + return node && + (typeof(type)==='string' ? (type===node.type) : type.test(node.type)); + }, + + isId: function(node, id) { + return module.exports.isType(node, 'Identifier') && + (typeof(id)==='string' ? (id===node.name) : id.test(node.name)); + }, } From 59646b7a688880bf2dc654a9bcc23967cf73262f Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Tue, 12 Feb 2019 10:37:23 -0500 Subject: [PATCH 25/52] Use new utils.isType() and utils.isId() where appropriate --- core.js | 2 +- core/array.js | 2 +- index.js | 41 ++++++++++++++++++++--------------------- 3 files changed, 22 insertions(+), 23 deletions(-) diff --git a/core.js b/core.js index 5891277..5c0a9ab 100644 --- a/core.js +++ b/core.js @@ -34,7 +34,7 @@ module.exports = { } else if (utils.isId(node.object, /^(Array|Object|Promise|console)$/)) { var longName = node.object.name + '_' + node.property.name; handler = get(_array, longName) || get(_object, longName) || get(_promise, longName) || get(_console, longName); - } else if (node.property) { + } else if (utils.isType(node.property, 'Identifier')) { var method = node.property.name; // _array should be before _string here so we pick up the correct // multitype version of #length and #indexOf diff --git a/core/array.js b/core/array.js index b5f8d7c..4b64a95 100644 --- a/core/array.js +++ b/core/array.js @@ -209,7 +209,7 @@ module.exports = { var targetDefinition = scope.get(node).getDefinition(object); if (!isString && targetDefinition) { - if (targetDefinition.type == "Identifier" && targetDefinition.name == "string") { + if (utils.isId(targetDefinition, "string")) { isString = true; } else if (targetDefinition.dataType == "String") { isString = true; diff --git a/index.js b/index.js index e3968f4..ce195fa 100644 --- a/index.js +++ b/index.js @@ -142,8 +142,8 @@ module.exports = function(code, options) { var endT = node && node.loc && tokenStartMap[locToKey(node.loc.end)]; if ( node.forceParens || - (startT && startT.type==='Punctuator' && startT.value === '(' && - endT && endT.type==='Punctuator' && endT.value === ')') + (utils.isType(startT, 'Punctuator') && startT.value === '(' && + utils.isType(endT, 'Punctuator') && endT.value === ')') ) { this.emit('( '); } @@ -155,8 +155,8 @@ module.exports = function(code, options) { var startT = node && node.loc && tokenEndMap[locToKey(node.loc.start)]; var endT = node && node.loc && tokenStartMap[locToKey(node.loc.end)]; if (node.forceParens || - (startT && startT.type==='Punctuator' && startT.value === '(' && - endT && endT.type==='Punctuator' && endT.value === ')') + (utils.isType(startT, 'Punctuator') && startT.value === '(' && + utils.isType(endT, 'Punctuator') && endT.value === ')') ) { if (!this.endsInNl()) this.emit(' '); this.emit(')'); @@ -266,10 +266,10 @@ module.exports = function(code, options) { if (parent) { node.parent = parent; } if (!node.suppressLoc) { emitter.locStart(node); } - if (node.type == "Program" || node.type == "BlockStatement" || node.type == "ClassBody") { + if (utils.isType(node, /^(Program|BlockStatement|ClassBody)$/)) { // Skip strictness declaration - if (node.body[0] && node.body[0].type === 'ExpressionStatement' && - node.body[0].expression.type === 'Literal' && + if (utils.isType(node.body[0], 'ExpressionStatement') && + utils.isType(node.body[0].expression, 'Literal') && node.body[0].expression.raw.match(/^["']use strict["']$/)) { emitter.locStart(node.body[0]); // flush leading comment node.body.shift(); @@ -282,21 +282,17 @@ module.exports = function(code, options) { } // skip core update require - while (node.body[0] && node.body[0].type === 'ExpressionStatement' && - node.body[0].expression.type === 'CallExpression' && - node.body[0].expression.callee.type === 'Identifier' && - node.body[0].expression.callee.name === 'require') { + while (utils.isType(node.body[0], 'ExpressionStatement') && + utils.isType(node.body[0].expression, 'CallExpression') && + utils.isId(node.body[0].expression.callee, 'require')) { node.body.shift(); // discard this } // Look for require declarations - while (node.body[0] && node.body[0].type === 'VariableDeclaration' && - node.body[0].declarations[0] && - node.body[0].declarations[0].type === 'VariableDeclarator' && - node.body[0].declarations[0].init && - node.body[0].declarations[0].init.type === 'CallExpression' && - node.body[0].declarations[0].init.callee.type === 'Identifier' && - node.body[0].declarations[0].init.callee.name === 'require') { + while (utils.isType(node.body[0], 'VariableDeclaration') && + utils.isType(node.body[0].declarations[0], 'VariableDeclarator') && + utils.isType(node.body[0].declarations[0].init, 'CallExpression') && + utils.isId(node.body[0].declarations[0].init.callee, 'require')) { handleImport(node, node.body.shift()); } } @@ -334,8 +330,8 @@ module.exports = function(code, options) { }); } else if (node.type == "VariableDeclarator") { scope.get(node).register(node); - var isForStatement = node.parent && node.parent.parent && - /^For(In|Of|)Statement$/.test(node.parent.parent.type); + var isForStatement = node.parent && + utils.isType(node.parent.parent, /^For(In|Of|)Statement$/); if (isForStatement) { emitter.replaceSemiWithComma(); } // declaration of one variable @@ -520,7 +516,10 @@ module.exports = function(code, options) { node.callee.isCallee = (!calleeDefined || calleeDefined && (calleeDefined.type != "Identifier" && calleeDefined.type != "VariableDeclarator")); - if (node.parent && node.parent.arguments === false && node.parent.parent.type === 'ExpressionStatement' && node.callee.type === 'Identifier' && node.callee.name === 'array_push' && node.arguments.length === 2) { + if (node.parent && node.parent.arguments === false && + utils.isType(node.parent.parent, 'ExpressionStatement') && + utils.isId(node.callee, 'array_push') && + node.arguments.length === 2) { // Special case syntax visit(node.arguments[0], node); emitter.emit('[] = '); From 68835b187b7ad48fe57a6ff5cf77b8d004e2226b Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Tue, 12 Feb 2019 09:45:01 -0500 Subject: [PATCH 26/52] Disambiguate property access from method access in core library support Property names are encoded with a leading dot, since method names are the most common case. This allows us to handle `Array.prototype.slice.call(...)` and similar constructs in a subsequent patch. --- core.js | 13 ++++-- core/array.js | 4 +- core/console.js | 2 +- core/math.js | 78 ++++++++++++++++++------------------ core/object.js | 2 +- core/promise.js | 2 +- core/regexp.js | 6 +-- core/string.js | 2 +- test/fixtures/core_array.js | 4 ++ test/fixtures/core_array.php | 4 ++ 10 files changed, 66 insertions(+), 51 deletions(-) diff --git a/core.js b/core.js index 5c0a9ab..71236f5 100644 --- a/core.js +++ b/core.js @@ -26,16 +26,23 @@ module.exports = { var newNode = module.exports.evaluate(node.object); if (newNode !== node.object) { node.object = newNode; + newNode.parent = node; } } if (utils.isType(node.object, 'Literal')) { var method = node.property.name; + if (!(utils.isType(node.parent, 'CallExpression') && node === node.parent.callee)) { + method = '.' + method; + } handler = node.object.regex ? get(_regexp, method) : get(_string, method); - } else if (utils.isId(node.object, /^(Array|Object|Promise|console)$/)) { - var longName = node.object.name + '_' + node.property.name; - handler = get(_array, longName) || get(_object, longName) || get(_promise, longName) || get(_console, longName); + } else if (utils.isId(node.object, /^(Array|Math|Object|Promise|console)$/)) { + var longName = node.object.name + '.' + node.property.name; + handler = get(_array, longName) || get(_math, longName) || get(_object, longName) || get(_promise, longName) || get(_console, longName); } else if (utils.isType(node.property, 'Identifier')) { var method = node.property.name; + if (!(utils.isType(node.parent, 'CallExpression') && node === node.parent.callee)) { + method = '.' + method; + } // _array should be before _string here so we pick up the correct // multitype version of #length and #indexOf handler = get(_array, method) || get(_date, method) || get(_function, method) || get(_json, method) || get(_string, method) || get(_math, method) || get(_number, method); diff --git a/core/array.js b/core/array.js index 4b64a95..b21cbfa 100644 --- a/core/array.js +++ b/core/array.js @@ -3,7 +3,7 @@ var utils = require('../utils'), string = require('./string'); module.exports = { - Array_isArray: function(node) { + 'Array.isArray': function(node) { var args = utils.clone(node.parent.arguments); node.parent.arguments = false; return { @@ -201,7 +201,7 @@ module.exports = { }, - length: function(node) { + '.length': function(node) { var method, object = (node.parent.callee && node.parent.callee.object) || node.object, isString = utils.isString(object); diff --git a/core/console.js b/core/console.js index 540f9a5..90c8c49 100644 --- a/core/console.js +++ b/core/console.js @@ -1,7 +1,7 @@ var utils = require('../utils'); module.exports = { - console_assert: function(node) { + 'console.assert': function(node) { return { type: 'MemberExpression', object: { diff --git a/core/math.js b/core/math.js index cefe08b..6bef73e 100644 --- a/core/math.js +++ b/core/math.js @@ -13,7 +13,7 @@ function constant(node, name) { } function method(node, name) { - if (isMathClass(node)) { + if (isMathClass(node) && node.parent.type === 'CallExpression') { var args = utils.clone(node.parent.arguments); node.parent.arguments = false; return { type: 'CallExpression', callee: { type: 'Identifier', name: name, }, arguments: args }; @@ -25,50 +25,50 @@ function method(node, name) { module.exports = { // constants - E: function(node) { return constant(node, 'M_E'); }, - LN2: function(node) { return constant(node, 'M_LN2'); }, - LN10: function(node) { return constant(node, 'M_LN10'); }, - LOG2E: function(node) { return constant(node, 'M_LOG2E'); }, - LOG10E: function(node) { return constant(node, 'M_LOG10E'); }, - PI: function(node) { return constant(node, 'M_PI'); }, - SQRT2: function(node) { return constant(node, 'M_SQRT2'); }, - SQRT1_2: function(node) { return constant(node, 'M_SQRT1_2'); }, + 'Math.E': function(node) { return constant(node, 'M_E'); }, + 'Math.LN2': function(node) { return constant(node, 'M_LN2'); }, + 'Math.LN10': function(node) { return constant(node, 'M_LN10'); }, + 'Math.LOG2E': function(node) { return constant(node, 'M_LOG2E'); }, + 'Math.LOG10E': function(node) { return constant(node, 'M_LOG10E'); }, + 'Math.PI': function(node) { return constant(node, 'M_PI'); }, + 'Math.SQRT2': function(node) { return constant(node, 'M_SQRT2'); }, + 'Math.SQRT1_2': function(node) { return constant(node, 'M_SQRT1_2'); }, // methods - abs: function(node) { return method(node, 'abs'); }, - acos: function(node) { return method(node, 'acos'); }, - acosh: function(node) { return method(node, 'acosh'); }, - asin: function(node) { return method(node, 'asin'); }, - asinh: function(node) { return method(node, 'asinh'); }, - atan: function(node) { return method(node, 'atan'); }, - atanh: function(node) { return method(node, 'atanh'); }, - atan2: function(node) { return method(node, 'atan2'); }, - cbrt: function(node) { return method(node, 'cbrt'); }, - ceil: function(node) { return method(node, 'ceil'); }, - clz32: function(node) { return method(node, 'clz32'); }, - cos: function(node) { return method(node, 'cos'); }, - cosh: function(node) { return method(node, 'cosh'); }, - exp: function(node) { return method(node, 'exp'); }, - expm1: function(node) { return method(node, 'expm1'); }, - floor: function(node) { return method(node, 'floor'); }, + 'Math.abs': function(node) { return method(node, 'abs'); }, + 'Math.acos': function(node) { return method(node, 'acos'); }, + 'Math.acosh': function(node) { return method(node, 'acosh'); }, + 'Math.asin': function(node) { return method(node, 'asin'); }, + 'Math.asinh': function(node) { return method(node, 'asinh'); }, + 'Math.atan': function(node) { return method(node, 'atan'); }, + 'Math.atanh': function(node) { return method(node, 'atanh'); }, + 'Math.atan2': function(node) { return method(node, 'atan2'); }, + 'Math.cbrt': function(node) { return method(node, 'cbrt'); }, + 'Math.ceil': function(node) { return method(node, 'ceil'); }, + 'Math.clz32': function(node) { return method(node, 'clz32'); }, + 'Math.cos': function(node) { return method(node, 'cos'); }, + 'Math.cosh': function(node) { return method(node, 'cosh'); }, + 'Math.exp': function(node) { return method(node, 'exp'); }, + 'Math.expm1': function(node) { return method(node, 'expm1'); }, + 'Math.floor': function(node) { return method(node, 'floor'); }, // fround: function(node) { return method(node, 'fround'); }, - hypot: function(node) { return method(node, 'hypot'); }, + 'Math.hypot': function(node) { return method(node, 'hypot'); }, // imul: function(node) { return method(node, 'imul'); }, - log: function(node) { return method(node, 'log'); }, - log1p: function(node) { return method(node, 'log1p'); }, - log10: function(node) { return method(node, 'log10'); }, + 'Math.log': function(node) { return method(node, 'log'); }, + 'Math.log1p': function(node) { return method(node, 'log1p'); }, + 'Math.log10': function(node) { return method(node, 'log10'); }, // log2: function(node) { return method(node, 'log2'); }, - max: function(node) { return method(node, 'max'); }, - min: function(node) { return method(node, 'min'); }, - pow: function(node) { return method(node, 'pow'); }, - random: function(node) { return method(node, 'rand'); }, - round: function(node) { return method(node, 'round'); }, + 'Math.max': function(node) { return method(node, 'max'); }, + 'Math.min': function(node) { return method(node, 'min'); }, + 'Math.pow': function(node) { return method(node, 'pow'); }, + 'Math.random': function(node) { return method(node, 'rand'); }, + 'Math.round': function(node) { return method(node, 'round'); }, // sign: function(node) { return method(node, 'sign'); }, - sin: function(node) { return method(node, 'sin'); }, - sinh: function(node) { return method(node, 'sinh'); }, - sqrt: function(node) { return method(node, 'sqrt'); }, - tan: function(node) { return method(node, 'tan'); }, - tanh: function(node) { return method(node, 'tanh'); }, + 'Math.sin': function(node) { return method(node, 'sin'); }, + 'Math.sinh': function(node) { return method(node, 'sinh'); }, + 'Math.sqrt': function(node) { return method(node, 'sqrt'); }, + 'Math.tan': function(node) { return method(node, 'tan'); }, + 'Math.tanh': function(node) { return method(node, 'tanh'); }, // trunc: function(node) { return method(node, 'trunc'); }, diff --git a/core/object.js b/core/object.js index 0f1b352..f6d3cfd 100644 --- a/core/object.js +++ b/core/object.js @@ -1,7 +1,7 @@ var utils = require('../utils'); module.exports = { - Object_create: function(node) { + 'Object.create': function(node) { var args = utils.clone(node.parent.arguments); if (args.length === 1 && args[0].type === 'Literal' && args[0].value === null) { node.parent.arguments = false; diff --git a/core/promise.js b/core/promise.js index a0de34b..a8003ab 100644 --- a/core/promise.js +++ b/core/promise.js @@ -1,7 +1,7 @@ var utils = require('../utils'); module.exports = { - Promise_async: function(node) { + 'Promise.async': function(node) { var args = utils.clone(node.parent.arguments); if (args.length === 1) { node.parent.arguments = false; diff --git a/core/regexp.js b/core/regexp.js index 648725f..d2736ab 100644 --- a/core/regexp.js +++ b/core/regexp.js @@ -9,7 +9,7 @@ module.exports = { .match(/^\/((?:[^\/]|\\.)+)\/([gimy])?$/); var flags = (regexpData && regexpData[2]) || ""; var isGroup = flags.indexOf('g') >= 0; - var lit = module.exports.source(node.parent.callee); + var lit = module.exports['.source'](node.parent.callee); lit.value = '/' + lit.value + '/' + flags.replace(/g/g, ""); lit.raw = JSON.stringify(lit.value); return { @@ -26,7 +26,7 @@ module.exports = { }; }, - source: function(node) { + '.source': function(node) { node.object.value = node.object.value.source; node.object.raw = JSON.stringify(node.object.value); node.object.regex = undefined; @@ -37,7 +37,7 @@ module.exports = { var args = utils.clone(node.parent.arguments); node.parent.arguments = false; args[0].suppressParens = true; - var lit = module.exports.source(node.parent.callee); + var lit = module.exports['.source'](node.parent.callee); lit.value = '/' + lit.value + '/'; lit.raw = JSON.stringify(lit.value); return { diff --git a/core/string.js b/core/string.js index ae03872..4488c4c 100644 --- a/core/string.js +++ b/core/string.js @@ -21,7 +21,7 @@ module.exports = { }; }, - length: function(node) { + '.length': function(node) { var object = (node.parent.callee && node.parent.callee.object) || node.object; return { type: 'CallExpression', diff --git a/test/fixtures/core_array.js b/test/fixtures/core_array.js index c969c14..639d11e 100644 --- a/test/fixtures/core_array.js +++ b/test/fixtures/core_array.js @@ -20,3 +20,7 @@ var_dump(count); var_dump(Array.isArray(items)); var_dump(Array.isArray(count)); + +/* This might not work, but it shouldn't crash! */ +var a = Array.prototype.slice.call([1,2,3], 1); +var_dump(a); diff --git a/test/fixtures/core_array.php b/test/fixtures/core_array.php index 5522d49..b8c6423 100644 --- a/test/fixtures/core_array.php +++ b/test/fixtures/core_array.php @@ -24,3 +24,7 @@ var_dump( is_array( $items ) ); var_dump( is_array( $count ) ); + +/* This might not work, but it shouldn't crash! */ +$a = call_user_func( [ Array::prototype, 'slice' ], 1 ); +var_dump( $a ); From 74730de010db721f1b2c9b97d2075ece42e46a42 Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Tue, 12 Feb 2019 13:17:46 -0500 Subject: [PATCH 27/52] JSON.stringify / JSON.parse are static methods --- core.js | 4 ++-- core/json.js | 58 ++++++++++++++++++++++------------------------------ 2 files changed, 27 insertions(+), 35 deletions(-) diff --git a/core.js b/core.js index 71236f5..7f95a96 100644 --- a/core.js +++ b/core.js @@ -35,9 +35,9 @@ module.exports = { method = '.' + method; } handler = node.object.regex ? get(_regexp, method) : get(_string, method); - } else if (utils.isId(node.object, /^(Array|Math|Object|Promise|console)$/)) { + } else if (utils.isId(node.object, /^(Array|JSON|Math|Object|Promise|console)$/)) { var longName = node.object.name + '.' + node.property.name; - handler = get(_array, longName) || get(_math, longName) || get(_object, longName) || get(_promise, longName) || get(_console, longName); + handler = get(_array, longName) || get(_json, longName) || get(_math, longName) || get(_object, longName) || get(_promise, longName) || get(_console, longName); } else if (utils.isType(node.property, 'Identifier')) { var method = node.property.name; if (!(utils.isType(node.parent, 'CallExpression') && node === node.parent.callee)) { diff --git a/core/json.js b/core/json.js index 56f7a79..3e2fdcd 100644 --- a/core/json.js +++ b/core/json.js @@ -6,41 +6,33 @@ function isJSONClass(node) { module.exports = { - stringify: function(node) { - if (isJSONClass(node)) { - var args = utils.clone(node.parent.arguments); - node.parent.arguments = false; + 'JSON.stringify': function(node) { + var args = utils.clone(node.parent.arguments); + node.parent.arguments = false; - return { - type: 'CallExpression', - callee: { - type: 'Identifier', - name: 'json_encode', - }, - arguments: args - }; - } else { - return node; - } + return { + type: 'CallExpression', + callee: { + type: 'Identifier', + name: 'json_encode', + }, + arguments: args + }; }, - parse: function(node) { - if (isJSONClass(node)) { - var args = utils.clone(node.parent.arguments); - node.parent.arguments = false; + 'JSON.parse': function(node) { + var args = utils.clone(node.parent.arguments); + node.parent.arguments = false; - return { - type: 'CallExpression', - callee: { - type: 'Identifier', - name: 'json_decode', - }, - arguments: args, - forceSkip: true - }; - } else { - return node; - } - } + return { + type: 'CallExpression', + callee: { + type: 'Identifier', + name: 'json_decode', + }, + arguments: args, + forceSkip: true + }; + }, -} +}; From 42c4aeff6284e05e1699141f0bd02ccfa36dd257 Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Tue, 12 Feb 2019 13:40:28 -0500 Subject: [PATCH 28/52] Date.now() is a static method --- core.js | 4 ++-- core/date.js | 30 +++++++++++------------------- test/fixtures/core_date.js | 4 ++++ test/fixtures/core_date.php | 5 +++++ 4 files changed, 22 insertions(+), 21 deletions(-) create mode 100644 test/fixtures/core_date.js create mode 100644 test/fixtures/core_date.php diff --git a/core.js b/core.js index 7f95a96..6c6a7d1 100644 --- a/core.js +++ b/core.js @@ -35,9 +35,9 @@ module.exports = { method = '.' + method; } handler = node.object.regex ? get(_regexp, method) : get(_string, method); - } else if (utils.isId(node.object, /^(Array|JSON|Math|Object|Promise|console)$/)) { + } else if (utils.isId(node.object, /^(Array|Date|JSON|Math|Object|Promise|console)$/)) { var longName = node.object.name + '.' + node.property.name; - handler = get(_array, longName) || get(_json, longName) || get(_math, longName) || get(_object, longName) || get(_promise, longName) || get(_console, longName); + handler = get(_array, longName) || get(_date, longName) || get(_json, longName) || get(_math, longName) || get(_object, longName) || get(_promise, longName) || get(_console, longName); } else if (utils.isType(node.property, 'Identifier')) { var method = node.property.name; if (!(utils.isType(node.parent, 'CallExpression') && node === node.parent.callee)) { diff --git a/core/date.js b/core/date.js index f17ecf6..e4d1568 100644 --- a/core/date.js +++ b/core/date.js @@ -1,27 +1,19 @@ var utils = require('../utils'); -function isDateClass(node) { - return node.object.name == "Date"; -} - module.exports = { - now: function(node) { - if (isDateClass(node)) { - var args = utils.clone(node.parent.arguments); - node.parent.arguments = false; + 'Date.now': function(node) { + var args = utils.clone(node.parent.arguments); + node.parent.arguments = false; - return { - type: 'CallExpression', - callee: { - type: 'Identifier', - name: 'time', - }, - arguments: [] - }; - } else { - return node; - } + return { + type: 'CallExpression', + callee: { + type: 'Identifier', + name: 'time', + }, + arguments: [] + }; } } diff --git a/test/fixtures/core_date.js b/test/fixtures/core_date.js new file mode 100644 index 0000000..372a35d --- /dev/null +++ b/test/fixtures/core_date.js @@ -0,0 +1,4 @@ +var start = Date.now(); +doSomethingExpensive(); +var end = Date.now(); +var_dump(end-start); diff --git a/test/fixtures/core_date.php b/test/fixtures/core_date.php new file mode 100644 index 0000000..ce182b3 --- /dev/null +++ b/test/fixtures/core_date.php @@ -0,0 +1,5 @@ + Date: Tue, 12 Feb 2019 13:18:22 -0500 Subject: [PATCH 29/52] Handle .prototype..call(...) --- core.js | 30 ++++++++++++++++++++++++++++++ core/array.js | 4 +++- core/console.js | 2 ++ core/date.js | 4 +++- core/function.js | 6 +++++- core/global.js | 2 +- core/json.js | 2 ++ core/math.js | 4 +++- core/number.js | 4 +++- core/object.js | 2 ++ core/promise.js | 2 ++ core/regexp.js | 2 ++ core/string.js | 4 +++- test/fixtures/core_array.php | 2 +- utils.js | 8 ++++++++ 15 files changed, 70 insertions(+), 8 deletions(-) diff --git a/core.js b/core.js index 6c6a7d1..74d82df 100644 --- a/core.js +++ b/core.js @@ -29,6 +29,36 @@ module.exports = { newNode.parent = node; } } + + // Handle Array.prototype.slice.call(...) + if (utils.isId(node.property, 'call') && + utils.isType(node.object, 'MemberExpression') && + utils.isType(node.object.object, 'MemberExpression') && + utils.isId(node.object.object.object, /^(Array)$/) && + utils.isId(node.object.object.property, 'prototype')) { + var type = node.object.object.object.name; + var method = node.object.property.name; + var longName = type + '#' + method; + handler = get(_array, longName); + if (handler) { + // move the 1st argument to be the reciever + var args = utils.clone(node.parent.arguments); + var thisArg = args.shift(); + node = { + type: 'CallExpression', + callee: { + type: 'MemberExpression', + object: thisArg, + property: node.object.property, + isCallee: true, + }, + arguments: args, + parent: node.parent, + }; + node.parent.arguments = false; + } + } + if (utils.isType(node.object, 'Literal')) { var method = node.property.name; if (!(utils.isType(node.parent, 'CallExpression') && node === node.parent.callee)) { diff --git a/core/array.js b/core/array.js index b21cbfa..ea2e446 100644 --- a/core/array.js +++ b/core/array.js @@ -234,4 +234,6 @@ module.exports = { }; }, -} +}; + +utils.coreAddHash(module.exports, 'Array'); diff --git a/core/console.js b/core/console.js index 90c8c49..d4b1783 100644 --- a/core/console.js +++ b/core/console.js @@ -15,3 +15,5 @@ module.exports = { }; } }; + +utils.coreAddHash(module.exports, 'console'); diff --git a/core/date.js b/core/date.js index e4d1568..464365c 100644 --- a/core/date.js +++ b/core/date.js @@ -16,4 +16,6 @@ module.exports = { }; } -} +}; + +utils.coreAddHash(module.exports, 'Date'); diff --git a/core/function.js b/core/function.js index a8647b6..3111631 100644 --- a/core/function.js +++ b/core/function.js @@ -1,3 +1,5 @@ +var utils = require('../utils'); + function apply(node, isCall) { var method, arguments = []; @@ -57,4 +59,6 @@ module.exports = { return apply(node, false) }, -} +}; + +utils.coreAddHash(module.exports, 'Function'); diff --git a/core/global.js b/core/global.js index 515011e..0cdeac0 100644 --- a/core/global.js +++ b/core/global.js @@ -14,4 +14,4 @@ module.exports = { return newNode; } -} +}; diff --git a/core/json.js b/core/json.js index 3e2fdcd..240f609 100644 --- a/core/json.js +++ b/core/json.js @@ -36,3 +36,5 @@ module.exports = { }, }; + +utils.coreAddHash(module.exports, 'JSON'); diff --git a/core/math.js b/core/math.js index 6bef73e..7d88829 100644 --- a/core/math.js +++ b/core/math.js @@ -72,4 +72,6 @@ module.exports = { // trunc: function(node) { return method(node, 'trunc'); }, -} +}; + +utils.coreAddHash(module.exports, 'Math'); diff --git a/core/number.js b/core/number.js index d4662a5..5039a9a 100644 --- a/core/number.js +++ b/core/number.js @@ -27,4 +27,6 @@ module.exports = { isInteger: function(node) { return method(node, 'is_int'); }, isFinite: function(node) { return method(node, 'is_finite'); }, -} \ No newline at end of file +}; + +utils.coreAddHash(module.exports, 'Number'); diff --git a/core/object.js b/core/object.js index f6d3cfd..03d468e 100644 --- a/core/object.js +++ b/core/object.js @@ -13,3 +13,5 @@ module.exports = { return node; } }; + +utils.coreAddHash(module.exports, 'Object'); diff --git a/core/promise.js b/core/promise.js index a8003ab..0ca139f 100644 --- a/core/promise.js +++ b/core/promise.js @@ -13,3 +13,5 @@ module.exports = { return node; } }; + +utils.coreAddHash(module.exports, 'Promise'); diff --git a/core/regexp.js b/core/regexp.js index d2736ab..2ac44a8 100644 --- a/core/regexp.js +++ b/core/regexp.js @@ -50,3 +50,5 @@ module.exports = { }; }, }; + +utils.coreAddHash(module.exports, 'RegExp'); diff --git a/core/string.js b/core/string.js index 4488c4c..b114b0e 100644 --- a/core/string.js +++ b/core/string.js @@ -252,4 +252,6 @@ module.exports = { }, -} +}; + +utils.coreAddHash(module.exports, 'String'); diff --git a/test/fixtures/core_array.php b/test/fixtures/core_array.php index b8c6423..76875a9 100644 --- a/test/fixtures/core_array.php +++ b/test/fixtures/core_array.php @@ -26,5 +26,5 @@ var_dump( is_array( $count ) ); /* This might not work, but it shouldn't crash! */ -$a = call_user_func( [ Array::prototype, 'slice' ], 1 ); +$a = array_slice( [ 1, 2, 3 ], 1 ); var_dump( $a ); diff --git a/utils.js b/utils.js index b7beade..a0fbac6 100644 --- a/utils.js +++ b/utils.js @@ -66,4 +66,12 @@ module.exports = { return module.exports.isType(node, 'Identifier') && (typeof(id)==='string' ? (id===node.name) : id.test(node.name)); }, + + // utility function for core library definitions + coreAddHash: function(exports, className) { + Object.keys(exports).forEach(function(name) { + if (/[#.]/.test(name)) { return; } + exports[className + '#' + name] = exports[name]; + }); + }, } From 859c78ba259523330c9e278786ee6453432cfb39 Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Tue, 12 Feb 2019 14:47:44 -0500 Subject: [PATCH 30/52] Improve support for regexp literals --- core.js | 2 +- core/regexp.js | 91 ++++++++++++++++++++++++---------------- core/string.js | 9 ++-- index.js | 9 ++-- test/fixtures/regexp.php | 2 +- utils.js | 2 +- 6 files changed, 70 insertions(+), 45 deletions(-) diff --git a/core.js b/core.js index 74d82df..4eee85c 100644 --- a/core.js +++ b/core.js @@ -75,7 +75,7 @@ module.exports = { } // _array should be before _string here so we pick up the correct // multitype version of #length and #indexOf - handler = get(_array, method) || get(_date, method) || get(_function, method) || get(_json, method) || get(_string, method) || get(_math, method) || get(_number, method); + handler = get(_array, method) || get(_date, method) || get(_function, method) || get(_json, method) || get(_regexp, method) || get(_string, method) || get(_math, method) || get(_number, method); } else if (node.callee) { handler = get(_global, node.callee.name); } diff --git a/core/regexp.js b/core/regexp.js index 2ac44a8..cb58bb0 100644 --- a/core/regexp.js +++ b/core/regexp.js @@ -3,51 +3,70 @@ var utils = require('../utils'); module.exports = { exec: function(node) { var args = utils.clone(node.parent.arguments); - node.parent.arguments = false; - args[0].suppressParens = true; - var regexpData = node.parent.callee.object.raw - .match(/^\/((?:[^\/]|\\.)+)\/([gimy])?$/); - var flags = (regexpData && regexpData[2]) || ""; - var isGroup = flags.indexOf('g') >= 0; - var lit = module.exports['.source'](node.parent.callee); - lit.value = '/' + lit.value + '/' + flags.replace(/g/g, ""); - lit.raw = JSON.stringify(lit.value); - return { - type: 'CallExpression', - callee: { - type: 'Identifier', - name: isGroup ? 'preg_match_all' : 'preg_match', - }, - arguments: [ node.parent.callee.object, args[0], { - type: 'Identifier', - name: 'FIXME', - }], - leadingComments: [{ type: 'Block', value: 'RegExp#exec' }], - }; + if (utils.isType(node.parent.callee.object, "Literal")) { + node.parent.arguments = false; + args[0].suppressParens = true; + var regexpData = node.parent.callee.object.raw + .match(/^\/((?:[^\/]|\\.)+)\/([gimy]+)?$/); + var flags = (regexpData && regexpData[2]) || ""; + var isGroup = flags.indexOf('g') >= 0; + var lit = module.exports['.source'](node.parent.callee); + lit.value = '/' + lit.value + '/' + flags.replace(/g/g, ""); + lit.raw = JSON.stringify(lit.value); + return { + type: 'CallExpression', + callee: { + type: 'Identifier', + name: isGroup ? 'preg_match_all' : 'preg_match', + }, + arguments: [ node.parent.callee.object, args[0], { + type: 'Identifier', + name: 'FIXME', + }], + leadingComments: [{ type: 'Block', value: 'RegExp#exec' }], + }; + } else { + return node; + } }, '.source': function(node) { - node.object.value = node.object.value.source; - node.object.raw = JSON.stringify(node.object.value); - node.object.regex = undefined; - return node.object; + if (utils.isType(node.object, "Literal")) { + node.object.value = node.object.value.source; + node.object.raw = JSON.stringify(node.object.value); + node.object.regex = undefined; + return node.object; + } else { + return node; + } }, test: function(node) { var args = utils.clone(node.parent.arguments); node.parent.arguments = false; args[0].suppressParens = true; - var lit = module.exports['.source'](node.parent.callee); - lit.value = '/' + lit.value + '/'; - lit.raw = JSON.stringify(lit.value); - return { - type: 'CallExpression', - callee: { - type: 'Identifier', - name: 'preg_match', - }, - arguments: [ node.parent.callee.object, args[0] ], - }; + if (utils.isType(node.parent.callee.object, "Literal")) { + var lit = module.exports['.source'](node.parent.callee); + lit.value = '/' + lit.value + '/'; + lit.raw = JSON.stringify(lit.value); + return { + type: 'CallExpression', + callee: { + type: 'Identifier', + name: 'preg_match', + }, + arguments: [ node.parent.callee.object, args[0] ], + }; + } else { + return { + type: 'CallExpression', + callee: { + type: 'Identifier', + name: 'preg_match', + }, + arguments: [ node.parent.callee.object, args[0] ], + }; + } }, }; diff --git a/core/string.js b/core/string.js index b114b0e..e93330e 100644 --- a/core/string.js +++ b/core/string.js @@ -41,7 +41,7 @@ module.exports = { node.parent.arguments = false; if(args[0].type === 'Literal'){ - var regexpData = args[0].raw.match(/^\/((?:[^\/]|\\.)+)\/([gimy])?$/), + var regexpData = args[0].raw.match(/^\/((?:[^\/]|\\.)+)\/([gimy]+)?$/), regex = regexpData && regexpData[1], flags = regexpData && regexpData[2] || "", isGroup = flags.indexOf('g') >= 0; @@ -51,6 +51,7 @@ module.exports = { method = "preg_replace"; args[0].raw = "'/" + regex + "/" + flags.replace("g", "") + "'"; args[0].type = "Literal"; + args[0].regex = false; // fill '$limit' param with only 1 replacement // http://php.net/manual/en/function.preg-replace.php @@ -171,7 +172,7 @@ module.exports = { node.parent.arguments = false; var regexpData = args[0].type==='Literal' && - args[0].raw.match(/^\/((?:[^\/]|\\.)+)\/([gimy])?$/), + args[0].raw.match(/^\/((?:[^\/]|\\.)+)\/([gimy]+)?$/), regex = regexpData && regexpData[1], flags = regexpData && regexpData[2] || ""; @@ -180,6 +181,7 @@ module.exports = { method = "preg_split"; args[0].raw = "'/" + regex + "/" + flags.replace("g", "") + "'"; args[0].type = "Literal"; + args[0].regex = false; if (args.length === 2) { args[0].suppressParens = true; } @@ -225,7 +227,7 @@ module.exports = { if(args[0].type === 'Literal') { - var regexpData = args[0].raw.match(/^\/((?:[^\/]|\\.)+)\/([gimy])?$/), + var regexpData = args[0].raw.match(/^\/((?:[^\/]|\\.)+)\/([gimy]+)?$/), regex = regexpData && regexpData[1], flags = regexpData && regexpData[2] || "", isGroup = flags.indexOf('g') >= 0; @@ -236,6 +238,7 @@ module.exports = { args[0].raw = "'" + regex + "'"; args[0].type = "Literal"; + args[0].regex = false; } diff --git a/index.js b/index.js index ce195fa..c013776 100644 --- a/index.js +++ b/index.js @@ -364,9 +364,12 @@ module.exports = function(code, options) { emitter.emit(node.value); } else if (node.type == "Literal") { - var value = (node.raw.match(/^["']undefined["']$/)) ? "NULL" : node.raw; - emitter.emit(value); - + if (node.regex) { + emitter.emit("/* RegExp */ " + JSON.stringify(node.raw)); + } else { + var value = (node.raw.match(/^["']undefined["']$/)) ? "NULL" : node.raw; + emitter.emit(value); + } } else if (node.type == "BinaryExpression" || node.type == "LogicalExpression") { if (node.operator == 'in') { diff --git a/test/fixtures/regexp.php b/test/fixtures/regexp.php index 272c8c8..8c92285 100644 --- a/test/fixtures/regexp.php +++ b/test/fixtures/regexp.php @@ -28,4 +28,4 @@ var_dump( preg_replace( '/\.\//', '', "./a/b/c", 1 ) ); -$matches = /*RegExp#exec*/preg_match( "/[abc]/", "LA la black sheep", $FIXME ); +$matches = /*RegExp#exec*/preg_match_all( "/[abc]/i", "LA la black sheep", $FIXME ); diff --git a/utils.js b/utils.js index a0fbac6..f17a1bd 100644 --- a/utils.js +++ b/utils.js @@ -47,7 +47,7 @@ module.exports = { value = value.substr(1, node.raw.length-2); } - var isRegExp = value.match(/^\/(?:[^\/]|\\.)+\/[gimy]?$/); + var isRegExp = value.match(/^\/(?:[^\/]|\\.)+\/[gimy]+?$/); if (isRegExp) { node.raw.value = "'" + value + "'"; From 5a55da106c54216d4654a3ce90ab206867510b67 Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Tue, 12 Feb 2019 14:48:01 -0500 Subject: [PATCH 31/52] Emit `elseif` where appropriate --- index.js | 9 +++++++-- test/fixtures/conditionals.php | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index c013776..781ed81 100644 --- a/index.js +++ b/index.js @@ -835,9 +835,14 @@ module.exports = function(code, options) { }, '}'); if (node.alternate) { - emitter.emit(" else "); + emitter.emit(" else"); + if (utils.isType(node.alternate, "IfStatement") && !node.alternate.leadingComments) { + /* suppress the space to create `elseif` token */ + } else { + emitter.emit(" "); + } - if (node.alternate.type == "BlockStatement") { + if (utils.isType(node.alternate, "BlockStatement")) { emitter.block('{', function() { visit(node.alternate, node); }, '}'); diff --git a/test/fixtures/conditionals.php b/test/fixtures/conditionals.php index 5ca124e..c6ce226 100644 --- a/test/fixtures/conditionals.php +++ b/test/fixtures/conditionals.php @@ -9,7 +9,7 @@ if ( $x < $y ) { echo( "x < y" ); -} else if ( $x == 2 ) { +} elseif ( $x == 2 ) { echo( "x==2" ); } else { echo( "else..." ); From 79e81e7e889d8a36e7820646fa30aa2fe4c3d374 Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Tue, 12 Feb 2019 15:32:04 -0500 Subject: [PATCH 32/52] Ensure callee is marked as 'used' in scope when rearranging parameters --- core/array.js | 15 +++++++++++++-- core/regexp.js | 4 +++- core/string.js | 17 +++++++++++++---- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/core/array.js b/core/array.js index ea2e446..1741add 100644 --- a/core/array.js +++ b/core/array.js @@ -21,6 +21,7 @@ module.exports = { node.parent.arguments = false; if (args.length === 1) { args[0].suppressParens = true; } args.unshift(node.parent.callee.object); + scope.get(node).getDefinition(node.parent.callee.object); return { type: 'CallExpression', @@ -34,6 +35,7 @@ module.exports = { shift: function(node) { node.parent.arguments = false; + scope.get(node).getDefinition(node.parent.callee.object); return { type: 'CallExpression', callee: { @@ -45,6 +47,8 @@ module.exports = { }, reverse: function(node) { + node.parent.arguments = false; + scope.get(node).getDefinition(node.parent.callee.object); return { type: 'CallExpression', callee: { @@ -58,6 +62,7 @@ module.exports = { push: function(node) { var args = utils.clone(node.parent.arguments); node.parent.arguments = false; + scope.get(node).getDefinition(node.parent.callee.object); if (args.length === 1) { args[0].suppressParens = true; } args.unshift(node.parent.callee.object); @@ -72,6 +77,8 @@ module.exports = { }, pop: function(node) { + node.parent.arguments = false; + scope.get(node).getDefinition(node.parent.callee.object); return { type: 'CallExpression', callee: { @@ -85,6 +92,7 @@ module.exports = { join: function(node) { var args = utils.clone(node.parent.arguments); node.parent.arguments = false; + scope.get(node).getDefinition(node.parent.callee.object); args[0].suppressParens = true; return { @@ -100,6 +108,7 @@ module.exports = { map: function(node) { var args = utils.clone(node.parent.arguments); node.parent.arguments = false; + scope.get(node).getDefinition(node.parent.callee.object); args[0].suppressParens = true; return { @@ -115,6 +124,7 @@ module.exports = { reduce: function(node) { var args = utils.clone(node.parent.arguments); node.parent.arguments = false; + scope.get(node).getDefinition(node.parent.callee.object); if (args.length === 1) { args[0].suppressParens = true; } @@ -145,8 +155,8 @@ module.exports = { args[0].suppressParens = true; } args.unshift(node.parent.callee.object); - node.parent.arguments = false; + scope.get(node).getDefinition(node.parent.callee.object); return { type: 'CallExpression', @@ -161,8 +171,8 @@ module.exports = { splice: function(node) { var args = utils.clone(node.parent.arguments); args.unshift(node.parent.callee.object); - node.parent.arguments = false; + scope.get(node).getDefinition(node.parent.callee.object); return { type: 'CallExpression', @@ -179,6 +189,7 @@ module.exports = { args = utils.clone(node.parent.arguments); node.parent.arguments = false; + scope.get(node).getDefinition(node.parent.callee.object); args[0].suppressParens = true; var targetDefinition = scope.get(node).getDefinition(node.parent.callee.object); diff --git a/core/regexp.js b/core/regexp.js index cb58bb0..b429ec8 100644 --- a/core/regexp.js +++ b/core/regexp.js @@ -1,4 +1,5 @@ -var utils = require('../utils'); +var utils = require('../utils'), + scope = require('../scope'); module.exports = { exec: function(node) { @@ -44,6 +45,7 @@ module.exports = { test: function(node) { var args = utils.clone(node.parent.arguments); node.parent.arguments = false; + scope.get(node).getDefinition(node.parent.callee.object); args[0].suppressParens = true; if (utils.isType(node.parent.callee.object, "Literal")) { var lit = module.exports['.source'](node.parent.callee); diff --git a/core/string.js b/core/string.js index e93330e..38b181e 100644 --- a/core/string.js +++ b/core/string.js @@ -1,4 +1,5 @@ -var utils = require('../utils'); +var utils = require('../utils'), + scope = require('../scope'); module.exports = { @@ -8,6 +9,7 @@ module.exports = { indexOf: function(node) { var args = utils.clone(node.parent.arguments); node.parent.arguments = false; + scope.get(node).getDefinition(node.parent.callee.object); if (args.length === 1) { args[0].suppressParens = true; } args.unshift(node.parent.callee.object); @@ -37,8 +39,8 @@ module.exports = { var method = "str_replace"; var args = utils.clone(node.parent.arguments); args.push(node.parent.callee.object) - node.parent.arguments = false; + scope.get(node).getDefinition(node.parent.callee.object); if(args[0].type === 'Literal'){ var regexpData = args[0].raw.match(/^\/((?:[^\/]|\\.)+)\/([gimy]+)?$/), @@ -86,8 +88,8 @@ module.exports = { args[0].suppressParens = true; } args.unshift(node.parent.callee.object); - node.parent.arguments = false; + scope.get(node).getDefinition(node.parent.callee.object); return { type: 'CallExpression', @@ -101,6 +103,7 @@ module.exports = { trim: function(node) { node.parent.arguments = false; + scope.get(node).getDefinition(node.parent.callee.object); return { type: 'CallExpression', @@ -114,6 +117,7 @@ module.exports = { trimRight: function(node) { node.parent.arguments = false; + scope.get(node).getDefinition(node.parent.callee.object); return { type: 'CallExpression', @@ -127,6 +131,7 @@ module.exports = { trimLeft: function(node) { node.parent.arguments = false; + scope.get(node).getDefinition(node.parent.callee.object); return { type: 'CallExpression', @@ -140,6 +145,7 @@ module.exports = { toUpperCase: function(node) { node.parent.arguments = false; + scope.get(node).getDefinition(node.parent.callee.object); return { type: 'CallExpression', @@ -153,6 +159,7 @@ module.exports = { toLowerCase: function(node) { node.parent.arguments = false; + scope.get(node).getDefinition(node.parent.callee.object); return { type: 'CallExpression', @@ -168,8 +175,8 @@ module.exports = { var method = "explode"; var args = utils.clone(node.parent.arguments); args.push(node.parent.callee.object); - node.parent.arguments = false; + scope.get(node).getDefinition(node.parent.callee.object); var regexpData = args[0].type==='Literal' && args[0].raw.match(/^\/((?:[^\/]|\\.)+)\/([gimy]+)?$/), @@ -207,6 +214,7 @@ module.exports = { substr: function(node) { var args = utils.clone(node.parent.arguments); node.parent.arguments = false; + scope.get(node).getDefinition(node.parent.callee.object); if (args.length === 1) { args[0].suppressParens = true; } args.unshift(node.parent.callee.object); @@ -243,6 +251,7 @@ module.exports = { } node.parent.arguments = false; + scope.get(node).getDefinition(node.parent.callee.object); return { type: 'CallExpression', From 2f58fde08a93cdf20cb9d45fec7cf90bea4d2755 Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Tue, 12 Feb 2019 15:37:33 -0500 Subject: [PATCH 33/52] Use PHP `implode` rather than `join` --- core/array.js | 2 +- test/fixtures/core_array.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/array.js b/core/array.js index 1741add..57d8161 100644 --- a/core/array.js +++ b/core/array.js @@ -99,7 +99,7 @@ module.exports = { type: 'CallExpression', callee: { type: 'Identifier', - name: 'join', + name: 'implode', }, arguments: [ args[0], node.parent.callee.object ] }; diff --git a/test/fixtures/core_array.php b/test/fixtures/core_array.php index 76875a9..ea388bb 100644 --- a/test/fixtures/core_array.php +++ b/test/fixtures/core_array.php @@ -7,11 +7,11 @@ array_push( $items, "Four", "Five" ); var_dump( $items ); -echo( join( ", ", $items ) ); +echo( implode( ", ", $items ) ); echo( count( $items ) ); echo( array_search( [ "name" => "Three" ], $items ) ); -echo( join( ", ", $items ) ); +echo( implode( ", ", $items ) ); echo( "\n" ); $count = array_reduce( $items, function ( $curr, $string ) { From 8c5b523a222b98bb9f515a4b46ced43f1ab01c99 Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Tue, 12 Feb 2019 15:50:50 -0500 Subject: [PATCH 34/52] Catch some obvious cases of string concatenation and use the dot operator --- index.js | 17 +++++++++++++++-- test/fixtures/class.php | 2 +- test/fixtures/core_string.js | 4 +++- test/fixtures/core_string.php | 4 ++++ utils.js | 3 ++- 5 files changed, 25 insertions(+), 5 deletions(-) diff --git a/index.js b/index.js index 781ed81..cc3f609 100644 --- a/index.js +++ b/index.js @@ -397,15 +397,28 @@ module.exports = function(code, options) { if (leftDefinition.type == "VariableDeclarator" && rightDefinition.type == "VariableDeclarator") { if (leftDefinition.init && utils.isString(leftDefinition.init) && rightDefinition.init && utils.isString(rightDefinition.init)) { - node.operator = node.operator.replace('+', '.'); + if (/\+/.test(node.operator)) { + node.operator = node.operator.replace('+', '.'); + node.dataType = 'String'; + } } } } visit(node.left, node); - emitter.emit(" " + node.operator + " "); + emitter.emit(" "); + emitter.pushInsertionPoint(); + emitter.emit(" "); emitter.incrIndent(); visit(node.right, node); emitter.decrIndent(); + if (utils.isString(node.left) || utils.isString(node.right)) { + if (/\+/.test(node.operator)) { + node.operator = node.operator.replace('+', '.'); + node.dataType = 'String'; + } + } + emitter.insertAt(0, node.operator); + emitter.popInsertionPoint(); } } else if (node.type == "AssignmentExpression" && node.left.type == "ObjectPattern") { diff --git a/test/fixtures/class.php b/test/fixtures/class.php index 7ebff8b..082aeae 100644 --- a/test/fixtures/class.php +++ b/test/fixtures/class.php @@ -55,7 +55,7 @@ public function updating( $model ) { function __get($_property) { if ($_property === 'something') { - return "Something: " + $this->_something; + return "Something: " . $this->_something; } } function __set($_property, $value) { diff --git a/test/fixtures/core_string.js b/test/fixtures/core_string.js index 6df119e..2c01e7e 100644 --- a/test/fixtures/core_string.js +++ b/test/fixtures/core_string.js @@ -53,4 +53,6 @@ if (str.match(/endel/)) { var_dump(str); } - +var x = "foo" + foo(); +var y = bar() + 'bar'; +var z = foo() + "bar" + baz(); diff --git a/test/fixtures/core_string.php b/test/fixtures/core_string.php index 4c3f7c4..1a9d236 100644 --- a/test/fixtures/core_string.php +++ b/test/fixtures/core_string.php @@ -53,3 +53,7 @@ function lengthTest2( $string ) { if ( preg_match( '/endel/', $str ) ) { var_dump( $str ); } + +$x = "foo" . foo(); +$y = bar() . 'bar'; +$z = foo() . "bar" . baz(); diff --git a/utils.js b/utils.js index f17a1bd..41ed2f3 100644 --- a/utils.js +++ b/utils.js @@ -37,7 +37,8 @@ module.exports = { }, isString: function(node) { - return node.type == "Literal" && node.raw.match(/^['|"]/); + return (node.type == "Literal" && node.raw.match(/^['|"]/)) || + node.dataType === 'String'; }, isRegExp: function(node) { From 6834267e8b51a8332c07b22183c44262c6df3e73 Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Tue, 12 Feb 2019 17:50:07 -0500 Subject: [PATCH 35/52] Handle EmptyStatement --- index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/index.js b/index.js index cc3f609..e59b48b 100644 --- a/index.js +++ b/index.js @@ -1082,6 +1082,8 @@ module.exports = function(code, options) { // Parsoid-specific: ignore yield expression. emitter.emit("/* await */ "); visit(node.argument, node); + } else if (node.type === 'EmptyStatement') { + /* do nothing */ } else { throw new Error("'" + node.type + "' not implemented: " + JSON.stringify(node.loc)); } From 9fb16d58fd9b000c1c6e771910d61a591eb0b49e Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Tue, 12 Feb 2019 17:55:48 -0500 Subject: [PATCH 36/52] Don't return a value when evaluating a object pattern assignment --- index.js | 6 ++++++ test/fixtures/object_pattern.php | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index e59b48b..5c46a30 100644 --- a/index.js +++ b/index.js @@ -449,6 +449,12 @@ module.exports = function(code, options) { }, }); }); + // Don't return an assignment expression when visiting the sequence + expressions.push({ + type: "Literal", + value: null, + raw: "null", + }); visit({ type: 'SequenceExpression', expressions diff --git a/test/fixtures/object_pattern.php b/test/fixtures/object_pattern.php index 70242f5..72123a3 100644 --- a/test/fixtures/object_pattern.php +++ b/test/fixtures/object_pattern.php @@ -2,5 +2,5 @@ $x = [ "foo" => 'foo', "bar" => 42 ]; $temp0 = $x; $foo = $temp0->foo; $bar = $temp0->bar; $y = 'not foo'; -( ( ( function () use ( &$x ) { $temp1 = $x; return $y = $temp1->foo; } ) )() ); +( ( ( function () use ( &$x ) { $temp1 = $x; $y = $temp1->foo; return null; } ) )() ); var_dump( "{$foo} {$bar} {$y}" ); From 94b4a16aa50d357ae629f610c254f800ce63b50c Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Tue, 12 Feb 2019 18:51:24 -0500 Subject: [PATCH 37/52] Fix function name translation --- index.js | 10 +++++++++- test/fixtures/function.js | 2 ++ test/fixtures/function.php | 2 ++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 5c46a30..d555998 100644 --- a/index.js +++ b/index.js @@ -651,7 +651,15 @@ module.exports = function(code, options) { node.type == "ArrowFunctionExpression") { var defaults = node.defaults || []; - emitter.emit("function " + ((node.id) ? node.id.name : "")); + emitter.emit("function "); + if (node.id) { + if (typeof node.id.name === 'string') { + emitter.emit(node.id.name); + } else if (utils.isType(node.id.name, 'Identifier')) { + // PHP doesn't support an identifier here, so suppress it. + emitter.emit("/* " + node.id.name.name + " */"); + } + } emitter.block('(', function() { // function declaration creates a new scope diff --git a/test/fixtures/function.js b/test/fixtures/function.js index 16b48be..dc34c63 100644 --- a/test/fixtures/function.js +++ b/test/fixtures/function.js @@ -17,4 +17,6 @@ var_dump(hello(5, 6)); var args = [5, 6]; var_dump(sum.apply(sum, args)); +var foo = function bar() { }; + // var_dump(something(5), sum(1,2)); diff --git a/test/fixtures/function.php b/test/fixtures/function.php index 9324d83..f0314a4 100644 --- a/test/fixtures/function.php +++ b/test/fixtures/function.php @@ -18,4 +18,6 @@ function hello( $a, $b ) { $args = [ 5, 6 ]; var_dump( call_user_func_array( 'sum', $args ) ); +$foo = function /* bar */() {}; + // var_dump(something(5), sum(1,2)); From 9e03fa193c745ce525afb21d66da4c7a0b907b42 Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Tue, 12 Feb 2019 19:52:46 -0500 Subject: [PATCH 38/52] Emit efficient single-quoted PHP strings where possible --- core/regexp.js | 6 +++--- core/string.js | 9 ++++++--- index.js | 7 +++++-- test/fixtures/assert.php | 2 +- test/fixtures/class.php | 26 +++++++++++++------------- test/fixtures/class_inheritance.php | 2 +- test/fixtures/concat.php | 6 +++--- test/fixtures/conditionals.php | 14 +++++++------- test/fixtures/core_array.php | 14 +++++++------- test/fixtures/core_function.php | 6 +++--- test/fixtures/core_json.php | 12 ++++++------ test/fixtures/core_object.php | 2 +- test/fixtures/core_string.php | 18 +++++++++--------- test/fixtures/exceptions.php | 8 ++++---- test/fixtures/for_of.php | 6 +++--- test/fixtures/function.php | 4 ++-- test/fixtures/function_super.php | 4 ++-- test/fixtures/global_functions.php | 4 ++-- test/fixtures/iife.php | 4 ++-- test/fixtures/loops.php | 6 +++--- test/fixtures/namespaces.php | 2 +- test/fixtures/namespaces_require.php | 2 +- test/fixtures/object_pattern.php | 2 +- test/fixtures/regexp.php | 26 +++++++++++++------------- test/fixtures/simple.php | 4 ++-- test/fixtures/static_call.php | 12 ++++++------ test/fixtures/string_template.php | 8 ++++---- utils.js | 8 ++++++++ 28 files changed, 119 insertions(+), 105 deletions(-) diff --git a/core/regexp.js b/core/regexp.js index b429ec8..bc50200 100644 --- a/core/regexp.js +++ b/core/regexp.js @@ -13,7 +13,7 @@ module.exports = { var isGroup = flags.indexOf('g') >= 0; var lit = module.exports['.source'](node.parent.callee); lit.value = '/' + lit.value + '/' + flags.replace(/g/g, ""); - lit.raw = JSON.stringify(lit.value); + lit.raw = utils.stringify(lit.value); return { type: 'CallExpression', callee: { @@ -34,7 +34,7 @@ module.exports = { '.source': function(node) { if (utils.isType(node.object, "Literal")) { node.object.value = node.object.value.source; - node.object.raw = JSON.stringify(node.object.value); + node.object.raw = utils.stringify(node.object.value); node.object.regex = undefined; return node.object; } else { @@ -50,7 +50,7 @@ module.exports = { if (utils.isType(node.parent.callee.object, "Literal")) { var lit = module.exports['.source'](node.parent.callee); lit.value = '/' + lit.value + '/'; - lit.raw = JSON.stringify(lit.value); + lit.raw = utils.stringify(lit.value); return { type: 'CallExpression', callee: { diff --git a/core/string.js b/core/string.js index 38b181e..0f467b5 100644 --- a/core/string.js +++ b/core/string.js @@ -51,7 +51,8 @@ module.exports = { // check for RegExp for preg_replace if (regexpData) { method = "preg_replace"; - args[0].raw = "'/" + regex + "/" + flags.replace("g", "") + "'"; + args[0].value = "/" + regex + "/" + flags.replace("g", ""); + args[0].raw = utils.stringify(args[0].value); args[0].type = "Literal"; args[0].regex = false; @@ -186,7 +187,8 @@ module.exports = { // check for RegExp for preg_replace if (regexpData) { method = "preg_split"; - args[0].raw = "'/" + regex + "/" + flags.replace("g", "") + "'"; + args[0].value = "/" + regex + "/" + flags.replace("g", ""); + args[0].raw = utils.stringify(args[0].value); args[0].type = "Literal"; args[0].regex = false; if (args.length === 2) { @@ -244,7 +246,8 @@ module.exports = { if (isGroup) { flags = flags.replace("g", ""); } regex = "/" + regex + "/" + flags; - args[0].raw = "'" + regex + "'"; + args[0].value = regex; + args[0].raw = utils.stringify(args[0].value); args[0].type = "Literal"; args[0].regex = false; diff --git a/index.js b/index.js index d555998..757a2f1 100644 --- a/index.js +++ b/index.js @@ -365,7 +365,9 @@ module.exports = function(code, options) { } else if (node.type == "Literal") { if (node.regex) { - emitter.emit("/* RegExp */ " + JSON.stringify(node.raw)); + emitter.emit("/* RegExp */ " + utils.stringify(node.raw)); + } else if (typeof node.value === 'string' && node.value !== 'undefined') { + emitter.emit(utils.stringify(node.value)); } else { var value = (node.raw.match(/^["']undefined["']$/)) ? "NULL" : node.raw; emitter.emit(value); @@ -741,7 +743,8 @@ module.exports = function(code, options) { } else if (node.type == "Property") { var property = (node.key.type == 'Identifier') ? node.key.name : node.key.value; - emitter.emit('"'+property+'" => '); + if (typeof(property)==='string') { property = utils.stringify(property); } + emitter.emit(String(property) + ' => '); visit(node.value, node); } else if (node.type == "ReturnStatement") { diff --git a/test/fixtures/assert.php b/test/fixtures/assert.php index 6b79d97..34e9953 100644 --- a/test/fixtures/assert.php +++ b/test/fixtures/assert.php @@ -1,3 +1,3 @@ Module::template( 'signup-confirmation' )->compile( [ - "base_url" => AppConfig::get( "retrieve.email.url" ) + 'body' => Module::template( 'signup-confirmation' )->compile( [ + 'base_url' => AppConfig::get( 'retrieve.email.url' ) ] ), - "subject" => "Sign-up confirmation", - "to" => $model->email, - "from" => "somebody@example.com" + 'subject' => 'Sign-up confirmation', + 'to' => $model->email, + 'from' => 'somebody@example.com' ] ); } @@ -40,13 +40,13 @@ public function updating( $model ) { if ( $model->isDirty( 'status' ) && $model->status == 1 ) { Mail::send( [ - "body" => Module::template( 'signup-approved' )->compile( [ - "BASE_URL" => AppConfig::get( "retrieve.email.url" ) + 'body' => Module::template( 'signup-approved' )->compile( [ + 'BASE_URL' => AppConfig::get( 'retrieve.email.url' ) ] ), - "subject" => "Approved!", - "to" => $model->email, - "from" => "somebody@example.com" + 'subject' => 'Approved!', + 'to' => $model->email, + 'from' => 'somebody@example.com' ] ); @@ -55,7 +55,7 @@ public function updating( $model ) { function __get($_property) { if ($_property === 'something') { - return "Something: " . $this->_something; + return 'Something: ' . $this->_something; } } function __set($_property, $value) { @@ -92,6 +92,6 @@ function __set($_property, $value) { // Instantiate class and call getter method -$example = new ClassExample( "awesome" ); +$example = new ClassExample( 'awesome' ); var_dump( call_user_func_array( [ $example, 'hello' ], [] ) ); var_dump( $example->hello() ); diff --git a/test/fixtures/class_inheritance.php b/test/fixtures/class_inheritance.php index 02fe9e0..ef38d35 100644 --- a/test/fixtures/class_inheritance.php +++ b/test/fixtures/class_inheritance.php @@ -26,5 +26,5 @@ public function getDescription() { } } -$article = new Article( "Wonderful article", "Yada Yada Yada", "Bob Loblaw" ); +$article = new Article( 'Wonderful article', 'Yada Yada Yada', 'Bob Loblaw' ); var_dump( $article->getDescription() ); diff --git a/test/fixtures/concat.php b/test/fixtures/concat.php index be5976d..2bf8d25 100644 --- a/test/fixtures/concat.php +++ b/test/fixtures/concat.php @@ -1,11 +1,11 @@ "Three" ], $items ) ); -echo( implode( ", ", $items ) ); +echo( array_search( [ 'name' => 'Three' ], $items ) ); +echo( implode( ', ', $items ) ); echo( "\n" ); $count = array_reduce( $items, function ( $curr, $string ) { diff --git a/test/fixtures/core_function.php b/test/fixtures/core_function.php index d9ecbe9..1ee37c5 100644 --- a/test/fixtures/core_function.php +++ b/test/fixtures/core_function.php @@ -4,7 +4,7 @@ public function without_params() { return "I'm a method without params."; } - public function with_default_params( $param1 = "default" ) { + public function with_default_params( $param1 = 'default' ) { return $param1; } } @@ -20,5 +20,5 @@ function global_function_with_params() { $klass = new Klass(); var_dump( call_user_func_array( [ $klass, 'without_params' ], [] ) ); var_dump( call_user_func_array( [ $klass, 'with_default_params' ], [] ) ); -var_dump( call_user_func_array( [ $klass, 'with_default_params' ], [ "Hey!" ] ) ); -var_dump( call_user_func( [ $klass, 'with_default_params' ], "Hey!" ) ); +var_dump( call_user_func_array( [ $klass, 'with_default_params' ], [ 'Hey!' ] ) ); +var_dump( call_user_func( [ $klass, 'with_default_params' ], 'Hey!' ) ); diff --git a/test/fixtures/core_json.php b/test/fixtures/core_json.php index 9467a9b..6cf44bf 100644 --- a/test/fixtures/core_json.php +++ b/test/fixtures/core_json.php @@ -1,8 +1,8 @@ 5, - "string" => "hey", - "nested" => [ "objects" => [ "here" => "yey" ] ] + 'integer' => 5, + 'string' => 'hey', + 'nested' => [ 'objects' => [ 'here' => 'yey' ] ] ] ); @@ -11,9 +11,9 @@ ; var_dump( json_encode( [ - "integer" => 5, - "string" => "hey", - "nested" => [ "objects" => [ "here" => "yey" ] ] + 'integer' => 5, + 'string' => 'hey', + 'nested' => [ 'objects' => [ 'here' => 'yey' ] ] ] ) diff --git a/test/fixtures/core_object.php b/test/fixtures/core_object.php index 83aa72c..729e67c 100644 --- a/test/fixtures/core_object.php +++ b/test/fixtures/core_object.php @@ -1,6 +1,6 @@ "one" ], - [ "name" => "two" ], - [ "name" => "three" ] + [ 'name' => 'one' ], + [ 'name' => 'two' ], + [ 'name' => 'three' ] ]; foreach ( $items as $item => $___ ) { diff --git a/test/fixtures/function.php b/test/fixtures/function.php index f0314a4..9780737 100644 --- a/test/fixtures/function.php +++ b/test/fixtures/function.php @@ -1,6 +1,6 @@ 'one', - "two" => 'two', - "three" => 'three' + 'one' => 'one', + 'two' => 'two', + 'three' => 'three' ]; foreach ( $obj as $i => $___ ) { diff --git a/test/fixtures/namespaces.php b/test/fixtures/namespaces.php index 1381b13..3eb66fa 100644 --- a/test/fixtures/namespaces.php +++ b/test/fixtures/namespaces.php @@ -4,7 +4,7 @@ public function __construct() { } public static function do_something() { - var_dump( "Something!" ); + var_dump( 'Something!' ); } } diff --git a/test/fixtures/namespaces_require.php b/test/fixtures/namespaces_require.php index 839154e..4ef9485 100644 --- a/test/fixtures/namespaces_require.php +++ b/test/fixtures/namespaces_require.php @@ -13,4 +13,4 @@ Bat::do_something()->do_something_else(); echo( semver::satisfies( 1, 2 ) ); echo( semver::satisfies( 1, 2 )->something_else() ); -echo( [ "Foo" => Foo::class, "Bar" => Bar::class ] ); +echo( [ 'Foo' => Foo::class, 'Bar' => Bar::class ] ); diff --git a/test/fixtures/object_pattern.php b/test/fixtures/object_pattern.php index 72123a3..fe5f952 100644 --- a/test/fixtures/object_pattern.php +++ b/test/fixtures/object_pattern.php @@ -1,5 +1,5 @@ 'foo', "bar" => 42 ]; +$x = [ 'foo' => 'foo', 'bar' => 42 ]; $temp0 = $x; $foo = $temp0->foo; $bar = $temp0->bar; $y = 'not foo'; ( ( ( function () use ( &$x ) { $temp1 = $x; $y = $temp1->foo; return null; } ) )() ); diff --git a/test/fixtures/regexp.php b/test/fixtures/regexp.php index 8c92285..8b40b45 100644 --- a/test/fixtures/regexp.php +++ b/test/fixtures/regexp.php @@ -1,31 +1,31 @@ log( $x, $y ); $console->log( $z ); -var_dump( [ "a" => 1, "b-c" => 'c', "d-e-fh" => g( 0 ), "hi" => "hi" ] ); +var_dump( [ 'a' => 1, 'b-c' => 'c', 'd-e-fh' => g( 0 ), 'hi' => 'hi' ] ); -$obj = [ "key" => 'value', "key2" => 'value2' ]; +$obj = [ 'key' => 'value', 'key2' => 'value2' ]; unset( $obj[ 'key' ] ); var_dump( isset( $something[ 'key2' ] ) ); var_dump( gettype( $something ) !== NULL ); diff --git a/test/fixtures/static_call.php b/test/fixtures/static_call.php index 6400004..3aedf24 100644 --- a/test/fixtures/static_call.php +++ b/test/fixtures/static_call.php @@ -2,14 +2,14 @@ Module\Http\Router::get( '/send', function () { return $this->json( [ - "success" => Mail::send( [ - "body" => Module::template( 'signup-confirmation' )->compile( [ - "base_url" => AppConfig::get( "retrieve.email.url" ) + 'success' => Mail::send( [ + 'body' => Module::template( 'signup-confirmation' )->compile( [ + 'base_url' => AppConfig::get( 'retrieve.email.url' ) ] ), - "subject" => "Sign-up confirmation", - "to" => $model->email, - "from" => "somebody@example.com" + 'subject' => 'Sign-up confirmation', + 'to' => $model->email, + 'from' => 'somebody@example.com' ] ) ] diff --git a/test/fixtures/string_template.php b/test/fixtures/string_template.php index 405b31d..65412fd 100644 --- a/test/fixtures/string_template.php +++ b/test/fixtures/string_template.php @@ -1,16 +1,16 @@ Date: Tue, 12 Feb 2019 19:52:52 -0500 Subject: [PATCH 39/52] Emit NULL for JS undefined --- index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/index.js b/index.js index 757a2f1..2f8efda 100644 --- a/index.js +++ b/index.js @@ -346,6 +346,8 @@ module.exports = function(code, options) { semicolon = true; } + } else if (utils.isId(node, "undefined")) { + emitter.emit("null"); } else if (node.type == "Identifier") { var identifier = (node.name || node.value); From 87d881832704751352dd96f56d8c24c211683354 Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Tue, 12 Feb 2019 21:29:23 -0500 Subject: [PATCH 40/52] Fixes to regular expression literals --- core/regexp.js | 22 ++++++++++++++-------- core/string.js | 18 ++++++++++-------- test/fixtures/regexp.js | 19 +++++++++++++++++++ test/fixtures/regexp.php | 23 +++++++++++++++++++++-- 4 files changed, 64 insertions(+), 18 deletions(-) diff --git a/core/regexp.js b/core/regexp.js index bc50200..0a5d4ac 100644 --- a/core/regexp.js +++ b/core/regexp.js @@ -4,16 +4,20 @@ var utils = require('../utils'), module.exports = { exec: function(node) { var args = utils.clone(node.parent.arguments); - if (utils.isType(node.parent.callee.object, "Literal")) { + if (utils.isType(node.parent.callee.object, "Literal") && + node.parent.callee.object.regex) { node.parent.arguments = false; args[0].suppressParens = true; var regexpData = node.parent.callee.object.raw .match(/^\/((?:[^\/]|\\.)+)\/([gimy]+)?$/); + var pattern = (regexpData && regexpData[1]); var flags = (regexpData && regexpData[2]) || ""; var isGroup = flags.indexOf('g') >= 0; - var lit = module.exports['.source'](node.parent.callee); - lit.value = '/' + lit.value + '/' + flags.replace(/g/g, ""); + var lit = node.parent.callee.object; + if (isGroup) { flags = flags.replace(/g/g, ''); } + lit.value = '/' + pattern + '/' + flags; lit.raw = utils.stringify(lit.value); + lit.regex = undefined; return { type: 'CallExpression', callee: { @@ -32,8 +36,8 @@ module.exports = { }, '.source': function(node) { - if (utils.isType(node.object, "Literal")) { - node.object.value = node.object.value.source; + if (utils.isType(node.object, "Literal") && node.object.regex) { + node.object.value = node.object.regex.pattern; node.object.raw = utils.stringify(node.object.value); node.object.regex = undefined; return node.object; @@ -47,10 +51,12 @@ module.exports = { node.parent.arguments = false; scope.get(node).getDefinition(node.parent.callee.object); args[0].suppressParens = true; - if (utils.isType(node.parent.callee.object, "Literal")) { - var lit = module.exports['.source'](node.parent.callee); - lit.value = '/' + lit.value + '/'; + if (utils.isType(node.parent.callee.object, "Literal") && + node.parent.callee.object.regex) { + var lit = node.parent.callee.object; + lit.value = lit.raw; lit.raw = utils.stringify(lit.value); + lit.regex = undefined; return { type: 'CallExpression', callee: { diff --git a/core/string.js b/core/string.js index 0f467b5..8e16dd8 100644 --- a/core/string.js +++ b/core/string.js @@ -237,24 +237,26 @@ module.exports = { if(args[0].type === 'Literal') { - var regexpData = args[0].raw.match(/^\/((?:[^\/]|\\.)+)\/([gimy]+)?$/), - regex = regexpData && regexpData[1], + var regexpData = args[0].regex && + args[0].raw.match(/^\/((?:[^\/]|\\.)+)\/([gimy]+)?$/), + pattern = regexpData && regexpData[1], flags = regexpData && regexpData[2] || "", isGroup = flags.indexOf('g') >= 0; // remove unsupported /g from regexp, to use preg_match_all - if (isGroup) { flags = flags.replace("g", ""); } - regex = "/" + regex + "/" + flags; - - args[0].value = regex; + if (isGroup) { flags = flags.replace(/g/g, ''); } + args[0].value = '/' + pattern + '/' + flags; args[0].raw = utils.stringify(args[0].value); args[0].type = "Literal"; - args[0].regex = false; - + args[0].regex = undefined; } node.parent.arguments = false; scope.get(node).getDefinition(node.parent.callee.object); + if (isGroup) { + // results come back in this out-argument + args.push({ type: 'Identifier', name: 'FIXME' }); + } return { type: 'CallExpression', diff --git a/test/fixtures/regexp.js b/test/fixtures/regexp.js index 1b6c503..a9085e4 100644 --- a/test/fixtures/regexp.js +++ b/test/fixtures/regexp.js @@ -28,3 +28,22 @@ var_dump(/[\n]/.source.slice(1,-1)); var_dump("./a/b/c".replace(/\.\//, '')); var matches = /[abc]/gi.exec("LA la black sheep"); + +var firstA = unknownValue(); +if (firstA && /^#/.test(firstA.getAttribute('href'))) { + var_dump("whoo"); +} + +// Tricky escape cases. +var x = /abc\n'"\/\\/i.source; // should be "abc\\n'\"\\/\\\\" +var y = /abc\n'"\/\\/i.test("ABC\n'\"/\\"); // should be true +// This only returns one match, but it uses the lastIndex property: +var z1 = /abc\n'"\/\\/ig.exec("abc\n'\"/\\xyzABC\n'\"/\\"); +var z2 = "Qabc\n'\"/\\xyzABC\n'\"/\\Q".split(/abc\n'"\/\\/ig); +// z2 should be [ 'Q', 'xyz', 'Q' ] +var z3 = "Qabc\n'\"/\\xyzABC\n'\"/\\Q".match(/abc\n'"\/\\/ig); +// z3 should be [ 'abc\n\'"/\\', 'ABC\n\'"/\\' ] +var z4 = "Qabc\n'\"/\\xyzABC\n'\"/\\Q".replace(/abc\n'"\/\\/ig, "x"); +// z4 should be 'QxxyzxQ' +var z5 = /^text\/html;/.test("text/html; quality=2"); +// z5 should be true diff --git a/test/fixtures/regexp.php b/test/fixtures/regexp.php index 8b40b45..f3fad8d 100644 --- a/test/fixtures/regexp.php +++ b/test/fixtures/regexp.php @@ -2,7 +2,7 @@ $one = preg_match( '/llo/', 'Hello' ); var_dump( $one ); -$two = preg_match_all( '/llo/', 'Hello' ); +$two = preg_match_all( '/llo/', 'Hello', $FIXME ); var_dump( $two ); $splitted = explode( ',', 'one, two, three' ); @@ -23,9 +23,28 @@ $regex = 'ello'; $nonliteral = preg_match( $regex, $string ); -var_dump( preg_match( '/abc\\n[\\/]/', 'def' ) ); +var_dump( preg_match( '/abc\\n[/]/', 'def' ) ); var_dump( substr( '[\\n]', 1, -1 ) ); var_dump( preg_replace( '/\\.\\//', '', './a/b/c', 1 ) ); $matches = /*RegExp#exec*/preg_match_all( '/[abc]/i', 'LA la black sheep', $FIXME ); + +$firstA = unknownValue(); +if ( $firstA && preg_match( '/^#/', $firstA->getAttribute( 'href' ) ) ) { + var_dump( 'whoo' ); +} + +// Tricky escape cases. +$x = "abc\\n'\"\\/\\\\"; // should be "abc\\n'\"\\/\\\\" +$y = preg_match( "/abc\\n'\"\\/\\\\/i", "ABC\n'\"/\\" ); // should be true +// This only returns one match, but it uses the lastIndex property: +$z1 = /*RegExp#exec*/preg_match_all( "/abc\\n'\"\\/\\\\/i", "abc\n'\"/\\xyzABC\n'\"/\\", $FIXME ); +$z2 = preg_split( "/abc\\n'\"\\/\\\\/i", "Qabc\n'\"/\\xyzABC\n'\"/\\Q" ); +// z2 should be [ 'Q', 'xyz', 'Q' ] +$z3 = preg_match_all( "/abc\\n'\"\\/\\\\/i", "Qabc\n'\"/\\xyzABC\n'\"/\\Q", $FIXME ); +// z3 should be [ 'abc\n\'"/\\', 'ABC\n\'"/\\' ] +$z4 = preg_replace( "/abc\\n'\"\\/\\\\/i", 'x', "Qabc\n'\"/\\xyzABC\n'\"/\\Q" ); +// z4 should be 'QxxyzxQ' +$z5 = preg_match( '/^text\\/html;/', 'text/html; quality=2' ); +// z5 should be true From 08abce15f94b63e78353b84068bddccbcfa9c41c Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Tue, 12 Feb 2019 22:59:20 -0500 Subject: [PATCH 41/52] String#match can be called with a plain string as argument --- core/string.js | 7 +++++++ test/fixtures/core_string.js | 3 +++ test/fixtures/core_string.php | 3 +++ 3 files changed, 13 insertions(+) diff --git a/core/string.js b/core/string.js index 8e16dd8..bb6e07b 100644 --- a/core/string.js +++ b/core/string.js @@ -243,6 +243,13 @@ module.exports = { flags = regexpData && regexpData[2] || "", isGroup = flags.indexOf('g') >= 0; + // String#match can be called with a raw string as well. + if ((!args[0].regex) && typeof args[0].value === 'string') { + var r = new RegExp(args[0].value); + pattern = r.source; + flags = ''; + isGroup = false; + } // remove unsupported /g from regexp, to use preg_match_all if (isGroup) { flags = flags.replace(/g/g, ''); } args[0].value = '/' + pattern + '/' + flags; diff --git a/test/fixtures/core_string.js b/test/fixtures/core_string.js index 2c01e7e..6364858 100644 --- a/test/fixtures/core_string.js +++ b/test/fixtures/core_string.js @@ -56,3 +56,6 @@ if (str.match(/endel/)) { var x = "foo" + foo(); var y = bar() + 'bar'; var z = foo() + "bar" + baz(); + +var typeOf = node.getAttribute('typeof'); +var_dump(typeOf.match('begin')); diff --git a/test/fixtures/core_string.php b/test/fixtures/core_string.php index bd2d644..60e4b43 100644 --- a/test/fixtures/core_string.php +++ b/test/fixtures/core_string.php @@ -57,3 +57,6 @@ function lengthTest2( $string ) { $x = 'foo' . foo(); $y = bar() . 'bar'; $z = foo() . 'bar' . baz(); + +$typeOf = $node->getAttribute( 'typeof' ); +var_dump( preg_match( '/begin/', $typeOf ) ); From 0956fa0f63b0b6b18047ef81bcef81833e9e25fe Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Tue, 12 Feb 2019 23:12:23 -0500 Subject: [PATCH 42/52] More tweaks to string and regular expression literals --- core/regexp.js | 6 +++++- test/fixtures/regexp.php | 10 +++++----- utils.js | 2 +- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/core/regexp.js b/core/regexp.js index 0a5d4ac..c87e9b1 100644 --- a/core/regexp.js +++ b/core/regexp.js @@ -54,7 +54,11 @@ module.exports = { if (utils.isType(node.parent.callee.object, "Literal") && node.parent.callee.object.regex) { var lit = node.parent.callee.object; - lit.value = lit.raw; + if (lit.value.source) { + lit.value = '/' + lit.value.source + '/'; + } else { + lit.value = lit.raw; + } lit.raw = utils.stringify(lit.value); lit.regex = undefined; return { diff --git a/test/fixtures/regexp.php b/test/fixtures/regexp.php index f3fad8d..a8339a3 100644 --- a/test/fixtures/regexp.php +++ b/test/fixtures/regexp.php @@ -23,10 +23,10 @@ $regex = 'ello'; $nonliteral = preg_match( $regex, $string ); -var_dump( preg_match( '/abc\\n[/]/', 'def' ) ); -var_dump( substr( '[\\n]', 1, -1 ) ); +var_dump( preg_match( '/abc\n[\/]/', 'def' ) ); +var_dump( substr( '[\n]', 1, -1 ) ); -var_dump( preg_replace( '/\\.\\//', '', './a/b/c', 1 ) ); +var_dump( preg_replace( '/\.\//', '', './a/b/c', 1 ) ); $matches = /*RegExp#exec*/preg_match_all( '/[abc]/i', 'LA la black sheep', $FIXME ); @@ -37,7 +37,7 @@ // Tricky escape cases. $x = "abc\\n'\"\\/\\\\"; // should be "abc\\n'\"\\/\\\\" -$y = preg_match( "/abc\\n'\"\\/\\\\/i", "ABC\n'\"/\\" ); // should be true +$y = preg_match( "/abc\\n'\"\\/\\\\/", "ABC\n'\"/\\" ); // should be true // This only returns one match, but it uses the lastIndex property: $z1 = /*RegExp#exec*/preg_match_all( "/abc\\n'\"\\/\\\\/i", "abc\n'\"/\\xyzABC\n'\"/\\", $FIXME ); $z2 = preg_split( "/abc\\n'\"\\/\\\\/i", "Qabc\n'\"/\\xyzABC\n'\"/\\Q" ); @@ -46,5 +46,5 @@ // z3 should be [ 'abc\n\'"/\\', 'ABC\n\'"/\\' ] $z4 = preg_replace( "/abc\\n'\"\\/\\\\/i", 'x', "Qabc\n'\"/\\xyzABC\n'\"/\\Q" ); // z4 should be 'QxxyzxQ' -$z5 = preg_match( '/^text\\/html;/', 'text/html; quality=2' ); +$z5 = preg_match( '/^text\/html;/', 'text/html; quality=2' ); // z5 should be true diff --git a/utils.js b/utils.js index 01b889b..e3a96f5 100644 --- a/utils.js +++ b/utils.js @@ -22,7 +22,7 @@ module.exports = { stringify: function(string) { if (/^[ -&\(-~]*$/.test(string)) { /* can use an efficient single-quoted string */ - return "'" + string.replace(/['\\]/g, '\\$&') + "'"; + return "'" + string.replace(/'|(?:\\(?=[\\']))/g, '\\$&') + "'"; } return JSON.stringify(string); // double-quoted string }, From 485566ad34ac0848354aba71ce144ed5959eae53 Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Wed, 13 Feb 2019 00:03:55 -0500 Subject: [PATCH 43/52] Fix to parenthesized `new` expressions --- index.js | 2 +- test/fixtures/class.js | 2 ++ test/fixtures/class.php | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 2f8efda..79441e5 100644 --- a/index.js +++ b/index.js @@ -1011,10 +1011,10 @@ module.exports = function(code, options) { // re-use CallExpression for NewExpression's var newNode = utils.clone(node); newNode.type = "CallExpression"; + newNode.suppressParens = true; emitter.emit('new '); visit(newNode, node); - return; } else if (node.type == "FunctionExpression") { diff --git a/test/fixtures/class.js b/test/fixtures/class.js index 076727c..9aaa4a6 100644 --- a/test/fixtures/class.js +++ b/test/fixtures/class.js @@ -52,3 +52,5 @@ class ClassExample { example = new ClassExample("awesome"); var_dump(example.hello.apply()); var_dump(example.hello()); + +var c = (new ClassExample("cool")).hello(); diff --git a/test/fixtures/class.php b/test/fixtures/class.php index ac9d424..308f74d 100644 --- a/test/fixtures/class.php +++ b/test/fixtures/class.php @@ -95,3 +95,5 @@ function __set($_property, $value) { $example = new ClassExample( 'awesome' ); var_dump( call_user_func_array( [ $example, 'hello' ], [] ) ); var_dump( $example->hello() ); + +$c = ( new ClassExample( 'cool' ) )->hello(); From 4235ef1b619f3fffa9c1a34a8ee60685e9f2b8a4 Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Wed, 13 Feb 2019 01:12:53 -0500 Subject: [PATCH 44/52] Fixes to string template expressions containing funny characters --- index.js | 2 +- test/fixtures/core_string.js | 8 ++++++-- test/fixtures/core_string.php | 8 ++++++-- test/fixtures/function_super.php | 3 +-- test/fixtures/string_template.js | 2 ++ test/fixtures/string_template.php | 2 ++ utils.js | 6 +++--- 7 files changed, 21 insertions(+), 10 deletions(-) diff --git a/index.js b/index.js index 79441e5..da7d9da 100644 --- a/index.js +++ b/index.js @@ -1058,7 +1058,7 @@ module.exports = function(code, options) { emitter.emit('"'); for (var i=0; igetAttribute( 'typeof' ); var_dump( preg_match( '/begin/', $typeOf ) ); diff --git a/test/fixtures/function_super.php b/test/fixtures/function_super.php index 168e659..cba4d78 100644 --- a/test/fixtures/function_super.php +++ b/test/fixtures/function_super.php @@ -1,8 +1,7 @@ method()}, {$func()}" ); + +echo( "funny characters like \" and \n should be fine {1 + 2}" ); diff --git a/utils.js b/utils.js index e3a96f5..50afa4a 100644 --- a/utils.js +++ b/utils.js @@ -19,12 +19,12 @@ module.exports = { } }, - stringify: function(string) { - if (/^[ -&\(-~]*$/.test(string)) { + stringify: function(string, forceDoubleQuote) { + if (/^[ -&\(-~]*$/.test(string) && !forceDoubleQuote) { /* can use an efficient single-quoted string */ return "'" + string.replace(/'|(?:\\(?=[\\']))/g, '\\$&') + "'"; } - return JSON.stringify(string); // double-quoted string + return JSON.stringify(string).replace(/\$/g, '\\$'); // double-quoted string }, clone: function(obj) { From a09497e0caeb49090cda6d4bcea0a8cdd61af730 Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Wed, 13 Feb 2019 01:38:31 -0500 Subject: [PATCH 45/52] Fix class reference in `new` expressions --- index.js | 5 ++++- test/fixtures/namespaces_require.js | 4 ++++ test/fixtures/namespaces_require.php | 4 ++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index da7d9da..9a57ce6 100644 --- a/index.js +++ b/index.js @@ -353,7 +353,10 @@ module.exports = function(code, options) { if (!node.static && !node.isCallee && !node.isMemberExpression) { var targetDefinition = scope.get(node).getDefinition(node); - if (targetDefinition && targetDefinition.isImport) { + if (utils.isType(node.parent && node.parent.parent, "NewExpression") && + node.parent.callee === node) { + emitter.emit(identifier); + } else if (targetDefinition && targetDefinition.isImport) { emitter.emit(identifier + '::class'); } else { emitter.emit('$' + identifier); diff --git a/test/fixtures/namespaces_require.js b/test/fixtures/namespaces_require.js index 9043589..d8ff34f 100644 --- a/test/fixtures/namespaces_require.js +++ b/test/fixtures/namespaces_require.js @@ -4,6 +4,7 @@ const { Foo, Bar } = require('./some/path/here.js'); const Bat = require('bat'); const semver = require('semver'); +var Negotiator = require('negotiator'); Foo.do_something(); Bar.do_something(); @@ -12,3 +13,6 @@ Bat.do_something().do_something_else(); echo(semver.satisfies(1,2)); echo(semver.satisfies(1,2).something_else()); echo({ Foo, Bar }); + +var foo = 3; +var n = new Negotiator( foo ); diff --git a/test/fixtures/namespaces_require.php b/test/fixtures/namespaces_require.php index 4ef9485..d69555f 100644 --- a/test/fixtures/namespaces_require.php +++ b/test/fixtures/namespaces_require.php @@ -6,6 +6,7 @@ use NameTest\Bar as Bar; use NameTest\Bat as Bat; use NameTest\semver as semver; +use NameTest\Negotiator as Negotiator; Foo::do_something(); Bar::do_something(); @@ -14,3 +15,6 @@ echo( semver::satisfies( 1, 2 ) ); echo( semver::satisfies( 1, 2 )->something_else() ); echo( [ 'Foo' => Foo::class, 'Bar' => Bar::class ] ); + +$foo = 3; +$n = new Negotiator( $foo ); From 74295f979a1ba656bc7e614d87d90e43334a79ae Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Wed, 13 Feb 2019 02:01:00 -0500 Subject: [PATCH 46/52] Fix to backslash corner case in single-quoted string literals --- test/fixtures/core_string.js | 1 + test/fixtures/core_string.php | 1 + utils.js | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/test/fixtures/core_string.js b/test/fixtures/core_string.js index b4bb7fa..6c2d2cf 100644 --- a/test/fixtures/core_string.js +++ b/test/fixtures/core_string.js @@ -54,6 +54,7 @@ if (str.match(/endel/)) { } var_dump("Strings with funny characters like \n and $foo and {$foo}"); +var_dump('\\\\'); function foo() { return "x"; } diff --git a/test/fixtures/core_string.php b/test/fixtures/core_string.php index 003b335..5c6dd7d 100644 --- a/test/fixtures/core_string.php +++ b/test/fixtures/core_string.php @@ -55,6 +55,7 @@ function lengthTest2( $string ) { } var_dump( "Strings with funny characters like \n and \$foo and {\$foo}" ); +var_dump( '\\\\' ); function foo() { return 'x'; } diff --git a/utils.js b/utils.js index 50afa4a..7265d71 100644 --- a/utils.js +++ b/utils.js @@ -22,7 +22,7 @@ module.exports = { stringify: function(string, forceDoubleQuote) { if (/^[ -&\(-~]*$/.test(string) && !forceDoubleQuote) { /* can use an efficient single-quoted string */ - return "'" + string.replace(/'|(?:\\(?=[\\']))/g, '\\$&') + "'"; + return "'" + string.replace(/'|(?:\\(?=[\\']|$))/g, '\\$&') + "'"; } return JSON.stringify(string).replace(/\$/g, '\\$'); // double-quoted string }, From ec00f212f045053f58e83171b440b0a65e9cd8e4 Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Wed, 13 Feb 2019 02:01:31 -0500 Subject: [PATCH 47/52] Suppress unnecessary `use` clauses in method definitions --- index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 9a57ce6..02fc67a 100644 --- a/index.js +++ b/index.js @@ -708,7 +708,8 @@ module.exports = function(code, options) { // try to use parent's variables // http://php.net/manual/pt_BR/functions.anonymous.php - if (using.length > 0 && node.parent.type !== "Program") { + if (using.length > 0 && + !utils.isType(node.parent, /^(Program|MethodDefinition)$/)) { emitter.insertAt(1, "use ( " + using.map(function(identifier) { return "&$" + identifier; }).join(', ') + " ) "); From 8e32151f74b803fff1cfeda2a311d04082f171e4 Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Wed, 13 Feb 2019 11:17:00 -0500 Subject: [PATCH 48/52] Handle calls to Array#slice with no arguments This is common in JS, which passes arrays by reference, but is generally not useful in PHP, which passes arrays by value. --- core/array.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/array.js b/core/array.js index 57d8161..bf132b2 100644 --- a/core/array.js +++ b/core/array.js @@ -151,6 +151,8 @@ module.exports = { } else { args[1].trailingComments = [{ type: 'Block', value: 'CHECK THIS'}]; } + } else if (node.parent.arguments.length === 0) { + args.unshift({ type: 'Literal', value: 0, raw: '0' }); } else { args[0].suppressParens = true; } From 0c26ebe8501678d3c60363341d8debfc0221a09e Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Wed, 13 Feb 2019 11:17:44 -0500 Subject: [PATCH 49/52] Ensure token-boundary code works at the beginning/start of the string --- index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 02fc67a..7e66e06 100644 --- a/index.js +++ b/index.js @@ -49,7 +49,7 @@ module.exports = function(code, options) { // slideFwd and slideBck skip over whitespace var slideFwd = function(loc) { loc = {line: loc.line, column: loc.column}; - while (true) { + while (loc.line <= lines.length) { var l = lines[loc.line - 1]; var c = l[loc.column]; if (!/[ \t\r\n]/.test(c)) break; @@ -60,7 +60,7 @@ module.exports = function(code, options) { }; var slideBck = function(loc) { loc = {line: loc.line, column: loc.column}; - while (true) { + while (loc.line >= 1) { var l = lines[loc.line - 1]; var c = l[loc.column - 1]; if (!/[ \t\r\n]/.test(c)) break; From da730e8bef61448d66efcb6291f71429b60e4a10 Mon Sep 17 00:00:00 2001 From: "C. Scott Ananian" Date: Wed, 13 Feb 2019 11:18:29 -0500 Subject: [PATCH 50/52] Parsoid-specific: add special case code for parsing pegjs files --- index.js | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++--- js2php | 3 +++ 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 7e66e06..ca8728f 100644 --- a/index.js +++ b/index.js @@ -5,6 +5,49 @@ var core = require('./core'), module.exports = function(code, options) { options = options || {}; + if (options.braced) { + // special option to handle code inside braced regions only + var nest = 0, quote = null, outside = '', inside = ''; + var noptions = Object.assign({}, options, { + braced: false, watermark: false, noOpenTag: true, indent: 1, + }); + if (options.watermark) { outside += `/* ${options.watermark} */\n`; } + code.split( + // /([{}]|\/\*(?:(?!\*\/).)*\*\/|\/\/[^\n]*(?:\n|$)|"[^"]*"|'[^']'|\[[^\]]*\])/mg + /([{}'"\[\]]|\/\*|\*\/|\/\/|\n)/g + ).forEach(function(chunk) { + var isStartQuote = (nest === 0) && + (chunk==='"' || chunk==="'" || chunk==='[' || chunk==='/*' || chunk==='//'); + if (quote || isStartQuote) { + var trailingBackslash = outside.endsWith('\\'); + outside += chunk; + if (chunk === quote && !trailingBackslash) { quote = null; } + else if (!quote) { + switch (chunk) { + case '/*' : quote = '*/'; break; + case '//': quote = '\n'; break; + case '[': quote = ']'; break; + default: quote = chunk; break; + } + } + return; + } + if (chunk==='}') { nest--; } + if (nest <= 0) { + if (inside) { + outside += module.exports(inside, noptions); + inside = ''; + } + outside += chunk; + nest = 0; + } else { + inside += chunk; + } + if (chunk==='{') { nest++; } + }); + if (inside) { outside += module.exports(inside, noptions); } + return outside; + } var useConciseArrays = (options.conciseArrays === false) ? false : true; var ast = espree.parse(code, { loc : true, @@ -221,6 +264,7 @@ module.exports = function(code, options) { } } var emitter = new Emitter(); + if (options.indent) { emitter.indentLevel = options.indent; } function handleImport(parent, node) { node.declarations.forEach(function(d) { @@ -275,7 +319,7 @@ module.exports = function(code, options) { node.body.shift(); } if (node.type === 'Program') { - if (options.namespace) { + if (options.namespace && !options.noOpenTag) { // Add optional namespace. emitter.emit(`namespace ${options.namespace};`); emitter.nl(); @@ -1118,11 +1162,15 @@ module.exports = function(code, options) { if (!node.suppressLoc) { emitter.locEnd(node); } } - emitter.emit(" Date: Tue, 19 Mar 2019 15:25:26 -0400 Subject: [PATCH 51/52] Improve handling of String#substring and String#substr --- core/string.js | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/core/string.js b/core/string.js index bb6e07b..34f8fa5 100644 --- a/core/string.js +++ b/core/string.js @@ -102,6 +102,50 @@ module.exports = { }; }, + substr: function(node) { + // This method is deprecated/discouraged in JS, but maps best to the PHP + // function. + var args = utils.clone(node.parent.arguments); + if (node.parent.arguments.length === 1) { + args[0].suppressParens = true; + } + args.unshift(node.parent.callee.object); + node.parent.arguments = false; + scope.get(node).getDefinition(node.parent.callee.object); + + return { + type: 'CallExpression', + callee: { + type: 'Identifier', + name: 'substr', + }, + arguments: args + }; + }, + + substring: function(node) { + var args = utils.clone(node.parent.arguments); + if (node.parent.arguments.length > 1) { + // Second argument to substr is very different from String#substring + // (And String#substring is almost-but-not-quite-like String#slice) + args[1].trailingComments = [{ type: 'Block', value: 'CHECK THIS'}]; + } else { + args[0].suppressParens = true; + } + args.unshift(node.parent.callee.object); + node.parent.arguments = false; + scope.get(node).getDefinition(node.parent.callee.object); + + return { + type: 'CallExpression', + callee: { + type: 'Identifier', + name: 'substr', + }, + arguments: args + }; + }, + trim: function(node) { node.parent.arguments = false; scope.get(node).getDefinition(node.parent.callee.object); From 4ca1b380895fe545e8fd9fba5a5368d53684accc Mon Sep 17 00:00:00 2001 From: zaoqi Date: Sat, 4 May 2019 20:09:02 +0800 Subject: [PATCH 52/52] $ npm audit fix --- package-lock.json | 695 ++++++++++++++++++++++++---------------------- package.json | 6 +- 2 files changed, 362 insertions(+), 339 deletions(-) diff --git a/package-lock.json b/package-lock.json index f00c137..31afa86 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,45 +1,49 @@ { "name": "js2php", - "version": "0.1.2", + "version": "0.1.7", "lockfileVersion": 1, "requires": true, "dependencies": { "JSONStream": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.4.tgz", - "integrity": "sha512-Y7vfi3I5oMOYIr+WxV8NZxDSwcbNgzdKYsTNInmycOq9bUYwGg9ryu57Wg5NLmCjqdFPNUmpMBo3kSJN9tCbXg==", + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", "dev": true, "requires": { - "jsonparse": "1.3.1", - "through": "2.3.8" + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" } }, "acorn": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.2.tgz", - "integrity": "sha512-cJrKCNcr2kv8dlDnbw+JPUGjHZzo4myaxOLmpOX8a+rgX94YeTcTMv/LFJUSByRpc+i4GgVnnhLxvMu/2Y+rqw==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", + "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==", "dev": true }, "acorn-dynamic-import": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-3.0.0.tgz", - "integrity": "sha512-zVWV8Z8lislJoOKKqdNMOB+s6+XV5WERty8MnKBeFgwA+19XJjJHs2RP5dzM57FftIs+jQnRToLiWazKr6sSWg==", - "dev": true, - "requires": { - "acorn": "5.7.2" - } + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz", + "integrity": "sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==", + "dev": true }, "acorn-node": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.5.2.tgz", - "integrity": "sha512-krFKvw/d1F17AN3XZbybIUzEY4YEPNiGo05AfP3dBlfVKrMHETKpgjpuZkSF8qDNt9UkQcqj7am8yJLseklCMg==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.6.2.tgz", + "integrity": "sha512-rIhNEZuNI8ibQcL7ANm/mGyPukIaZsRNX9psFNQURyJW0nu6k8wjSDld20z6v2mDBWqX13pIEnk9gGZJHIlEXg==", "dev": true, "requires": { - "acorn": "5.7.2", - "acorn-dynamic-import": "3.0.0", - "xtend": "4.0.1" + "acorn": "^6.0.2", + "acorn-dynamic-import": "^4.0.0", + "acorn-walk": "^6.1.0", + "xtend": "^4.0.1" } }, + "acorn-walk": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.1.tgz", + "integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==", + "dev": true + }, "array-filter": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz", @@ -64,9 +68,9 @@ "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", "dev": true, "requires": { - "bn.js": "4.11.8", - "inherits": "2.0.1", - "minimalistic-assert": "1.0.1" + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" } }, "assert": { @@ -102,7 +106,7 @@ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { - "balanced-match": "1.0.0", + "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, @@ -118,12 +122,12 @@ "integrity": "sha512-erYug8XoqzU3IfcU8fUgyHqyOXqIE4tUTTQ+7mqUjQlvnXkOO6OlT9c/ZoJVHYoAaqGxr09CN53G7XIsO4KtWA==", "dev": true, "requires": { - "JSONStream": "1.3.4", - "combine-source-map": "0.8.0", - "defined": "1.0.0", - "safe-buffer": "5.1.2", - "through2": "2.0.3", - "umd": "3.0.3" + "JSONStream": "^1.0.3", + "combine-source-map": "~0.8.0", + "defined": "^1.0.0", + "safe-buffer": "^5.1.1", + "through2": "^2.0.0", + "umd": "^3.0.0" } }, "browser-resolve": { @@ -150,59 +154,59 @@ "dev": true }, "browserify": { - "version": "16.2.2", - "resolved": "https://registry.npmjs.org/browserify/-/browserify-16.2.2.tgz", - "integrity": "sha512-fMES05wq1Oukts6ksGUU2TMVHHp06LyQt0SIwbXIHm7waSrQmNBZePsU0iM/4f94zbvb/wHma+D1YrdzWYnF/A==", - "dev": true, - "requires": { - "JSONStream": "1.3.4", - "assert": "1.4.1", - "browser-pack": "6.1.0", - "browser-resolve": "1.11.3", - "browserify-zlib": "0.2.0", - "buffer": "5.2.1", - "cached-path-relative": "1.0.1", - "concat-stream": "1.6.2", - "console-browserify": "1.1.0", - "constants-browserify": "1.0.0", - "crypto-browserify": "3.12.0", - "defined": "1.0.0", - "deps-sort": "2.0.0", - "domain-browser": "1.2.0", - "duplexer2": "0.1.4", - "events": "2.1.0", - "glob": "7.1.3", - "has": "1.0.3", - "htmlescape": "1.1.1", - "https-browserify": "1.0.0", - "inherits": "2.0.1", - "insert-module-globals": "7.2.0", - "labeled-stream-splicer": "2.0.1", - "mkdirp": "0.5.1", - "module-deps": "6.1.0", - "os-browserify": "0.3.0", - "parents": "1.0.1", - "path-browserify": "0.0.1", - "process": "0.11.10", - "punycode": "1.4.1", - "querystring-es3": "0.2.1", - "read-only-stream": "2.0.0", - "readable-stream": "2.3.6", - "resolve": "1.8.1", - "shasum": "1.0.2", - "shell-quote": "1.6.1", - "stream-browserify": "2.0.1", - "stream-http": "2.8.3", - "string_decoder": "1.1.1", - "subarg": "1.0.0", - "syntax-error": "1.4.0", - "through2": "2.0.3", - "timers-browserify": "1.4.2", + "version": "16.2.3", + "resolved": "https://registry.npmjs.org/browserify/-/browserify-16.2.3.tgz", + "integrity": "sha512-zQt/Gd1+W+IY+h/xX2NYMW4orQWhqSwyV+xsblycTtpOuB27h1fZhhNQuipJ4t79ohw4P4mMem0jp/ZkISQtjQ==", + "dev": true, + "requires": { + "JSONStream": "^1.0.3", + "assert": "^1.4.0", + "browser-pack": "^6.0.1", + "browser-resolve": "^1.11.0", + "browserify-zlib": "~0.2.0", + "buffer": "^5.0.2", + "cached-path-relative": "^1.0.0", + "concat-stream": "^1.6.0", + "console-browserify": "^1.1.0", + "constants-browserify": "~1.0.0", + "crypto-browserify": "^3.0.0", + "defined": "^1.0.0", + "deps-sort": "^2.0.0", + "domain-browser": "^1.2.0", + "duplexer2": "~0.1.2", + "events": "^2.0.0", + "glob": "^7.1.0", + "has": "^1.0.0", + "htmlescape": "^1.1.0", + "https-browserify": "^1.0.0", + "inherits": "~2.0.1", + "insert-module-globals": "^7.0.0", + "labeled-stream-splicer": "^2.0.0", + "mkdirp": "^0.5.0", + "module-deps": "^6.0.0", + "os-browserify": "~0.3.0", + "parents": "^1.0.1", + "path-browserify": "~0.0.0", + "process": "~0.11.0", + "punycode": "^1.3.2", + "querystring-es3": "~0.2.0", + "read-only-stream": "^2.0.0", + "readable-stream": "^2.0.2", + "resolve": "^1.1.4", + "shasum": "^1.0.0", + "shell-quote": "^1.6.1", + "stream-browserify": "^2.0.0", + "stream-http": "^2.0.0", + "string_decoder": "^1.1.1", + "subarg": "^1.0.0", + "syntax-error": "^1.1.1", + "through2": "^2.0.0", + "timers-browserify": "^1.0.1", "tty-browserify": "0.0.1", - "url": "0.11.0", - "util": "0.10.3", - "vm-browserify": "1.1.0", - "xtend": "4.0.1" + "url": "~0.11.0", + "util": "~0.10.1", + "vm-browserify": "^1.0.0", + "xtend": "^4.0.0" } }, "browserify-aes": { @@ -211,12 +215,12 @@ "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "dev": true, "requires": { - "buffer-xor": "1.0.3", - "cipher-base": "1.0.4", - "create-hash": "1.2.0", - "evp_bytestokey": "1.0.3", - "inherits": "2.0.1", - "safe-buffer": "5.1.2" + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" } }, "browserify-cipher": { @@ -225,9 +229,9 @@ "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", "dev": true, "requires": { - "browserify-aes": "1.2.0", - "browserify-des": "1.0.2", - "evp_bytestokey": "1.0.3" + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" } }, "browserify-des": { @@ -236,10 +240,10 @@ "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", "dev": true, "requires": { - "cipher-base": "1.0.4", - "des.js": "1.0.0", - "inherits": "2.0.1", - "safe-buffer": "5.1.2" + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" } }, "browserify-rsa": { @@ -248,8 +252,8 @@ "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", "dev": true, "requires": { - "bn.js": "4.11.8", - "randombytes": "2.0.6" + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" } }, "browserify-sign": { @@ -258,13 +262,13 @@ "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", "dev": true, "requires": { - "bn.js": "4.11.8", - "browserify-rsa": "4.0.1", - "create-hash": "1.2.0", - "create-hmac": "1.1.7", - "elliptic": "6.4.1", - "inherits": "2.0.1", - "parse-asn1": "5.1.1" + "bn.js": "^4.1.1", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.2", + "elliptic": "^6.0.0", + "inherits": "^2.0.1", + "parse-asn1": "^5.0.0" } }, "browserify-zlib": { @@ -273,7 +277,7 @@ "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", "dev": true, "requires": { - "pako": "1.0.6" + "pako": "~1.0.5" } }, "buffer": { @@ -282,8 +286,8 @@ "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==", "dev": true, "requires": { - "base64-js": "1.3.0", - "ieee754": "1.1.12" + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" } }, "buffer-from": { @@ -305,9 +309,9 @@ "dev": true }, "cached-path-relative": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.1.tgz", - "integrity": "sha1-0JxLUoAKpMB44t2BqGmqyQ0uVOc=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.2.tgz", + "integrity": "sha512-5r2GqsoEb4qMTTN9J+WzXfjov+hjxT+j3u5K+kIVNIwAd99DLCJE9pBIMP1qVeybV6JiijL385Oz0DcYxfbOIg==", "dev": true }, "cipher-base": { @@ -316,8 +320,8 @@ "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", "dev": true, "requires": { - "inherits": "2.0.1", - "safe-buffer": "5.1.2" + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" } }, "combine-source-map": { @@ -326,10 +330,10 @@ "integrity": "sha1-pY0N8ELBhvz4IqjoAV9UUNLXmos=", "dev": true, "requires": { - "convert-source-map": "1.1.3", - "inline-source-map": "0.6.2", - "lodash.memoize": "3.0.4", - "source-map": "0.5.7" + "convert-source-map": "~1.1.0", + "inline-source-map": "~0.6.0", + "lodash.memoize": "~3.0.3", + "source-map": "~0.5.3" } }, "commander": { @@ -350,10 +354,10 @@ "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", "dev": true, "requires": { - "buffer-from": "1.1.1", - "inherits": "2.0.3", - "readable-stream": "2.3.6", - "typedarray": "0.0.6" + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" }, "dependencies": { "inherits": { @@ -370,7 +374,7 @@ "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", "dev": true, "requires": { - "date-now": "0.1.4" + "date-now": "^0.1.4" } }, "constants-browserify": { @@ -381,7 +385,7 @@ }, "convert-source-map": { "version": "1.1.3", - "resolved": "http://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", "integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA=", "dev": true }, @@ -397,8 +401,8 @@ "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", "dev": true, "requires": { - "bn.js": "4.11.8", - "elliptic": "6.4.1" + "bn.js": "^4.1.0", + "elliptic": "^6.0.0" } }, "create-hash": { @@ -407,11 +411,11 @@ "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "dev": true, "requires": { - "cipher-base": "1.0.4", - "inherits": "2.0.1", - "md5.js": "1.3.4", - "ripemd160": "2.0.2", - "sha.js": "2.4.11" + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" } }, "create-hmac": { @@ -420,12 +424,12 @@ "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "dev": true, "requires": { - "cipher-base": "1.0.4", - "create-hash": "1.2.0", - "inherits": "2.0.1", - "ripemd160": "2.0.2", - "safe-buffer": "5.1.2", - "sha.js": "2.4.11" + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" } }, "crypto-browserify": { @@ -434,19 +438,25 @@ "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", "dev": true, "requires": { - "browserify-cipher": "1.0.1", - "browserify-sign": "4.0.4", - "create-ecdh": "4.0.3", - "create-hash": "1.2.0", - "create-hmac": "1.1.7", - "diffie-hellman": "5.0.3", - "inherits": "2.0.1", - "pbkdf2": "3.0.16", - "public-encrypt": "4.0.2", - "randombytes": "2.0.6", - "randomfill": "1.0.4" + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" } }, + "dash-ast": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dash-ast/-/dash-ast-1.0.0.tgz", + "integrity": "sha512-Vy4dx7gquTeMcQR/hDkYLGUnwVil6vk4FOOct+djUnHOUWt+zJPJAaRIXaAFkPXtJjvlY7o3rfRu0/3hpnwoUA==", + "dev": true + }, "date-now": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", @@ -474,10 +484,10 @@ "integrity": "sha1-CRckkC6EZYJg65EHSMzNGvbiH7U=", "dev": true, "requires": { - "JSONStream": "1.3.4", - "shasum": "1.0.2", - "subarg": "1.0.0", - "through2": "2.0.3" + "JSONStream": "^1.0.3", + "shasum": "^1.0.0", + "subarg": "^1.0.0", + "through2": "^2.0.0" } }, "des.js": { @@ -486,19 +496,19 @@ "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", "dev": true, "requires": { - "inherits": "2.0.1", - "minimalistic-assert": "1.0.1" + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" } }, "detective": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/detective/-/detective-5.1.0.tgz", - "integrity": "sha512-TFHMqfOvxlgrfVzTEkNBSh9SvSNX/HfF4OFI2QFGCyPm02EsyILqnUeb5P6q7JZ3SFNTBL5t2sePRgrN4epUWQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz", + "integrity": "sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==", "dev": true, "requires": { - "acorn-node": "1.5.2", - "defined": "1.0.0", - "minimist": "1.2.0" + "acorn-node": "^1.6.1", + "defined": "^1.0.0", + "minimist": "^1.1.1" } }, "diff": { @@ -513,9 +523,9 @@ "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", "dev": true, "requires": { - "bn.js": "4.11.8", - "miller-rabin": "4.0.1", - "randombytes": "2.0.6" + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" } }, "domain-browser": { @@ -530,7 +540,7 @@ "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", "dev": true, "requires": { - "readable-stream": "2.3.6" + "readable-stream": "^2.0.2" } }, "elliptic": { @@ -539,13 +549,13 @@ "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==", "dev": true, "requires": { - "bn.js": "4.11.8", - "brorand": "1.1.0", - "hash.js": "1.1.5", - "hmac-drbg": "1.0.1", - "inherits": "2.0.1", - "minimalistic-assert": "1.0.1", - "minimalistic-crypto-utils": "1.0.1" + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" } }, "escape-string-regexp": { @@ -571,8 +581,8 @@ "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", "dev": true, "requires": { - "md5.js": "1.3.4", - "safe-buffer": "5.1.2" + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" } }, "fs.realpath": { @@ -599,12 +609,12 @@ "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", "dev": true, "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.1", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, "growl": { @@ -619,7 +629,7 @@ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, "requires": { - "function-bind": "1.1.1" + "function-bind": "^1.1.1" } }, "has-flag": { @@ -634,18 +644,18 @@ "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", "dev": true, "requires": { - "inherits": "2.0.1", - "safe-buffer": "5.1.2" + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" } }, "hash.js": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.5.tgz", - "integrity": "sha512-eWI5HG9Np+eHV1KQhisXWwM+4EPPYe5dFX1UZZH7k/E3JzDEazVH+VGlZi6R94ZqImq+A3D1mCEtrFIfg/E7sA==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", "dev": true, "requires": { - "inherits": "2.0.3", - "minimalistic-assert": "1.0.1" + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" }, "dependencies": { "inherits": { @@ -668,9 +678,9 @@ "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", "dev": true, "requires": { - "hash.js": "1.1.5", - "minimalistic-assert": "1.0.1", - "minimalistic-crypto-utils": "1.0.1" + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" } }, "htmlescape": { @@ -686,9 +696,9 @@ "dev": true }, "ieee754": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz", - "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", "dev": true }, "inflight": { @@ -697,8 +707,8 @@ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" + "once": "^1.3.0", + "wrappy": "1" } }, "inherits": { @@ -713,7 +723,7 @@ "integrity": "sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU=", "dev": true, "requires": { - "source-map": "0.5.7" + "source-map": "~0.5.3" } }, "insert-module-globals": { @@ -722,16 +732,16 @@ "integrity": "sha512-VE6NlW+WGn2/AeOMd496AHFYmE7eLKkUY6Ty31k4og5vmA3Fjuwe9v6ifH6Xx/Hz27QvdoMoviw1/pqWRB09Sw==", "dev": true, "requires": { - "JSONStream": "1.3.4", - "acorn-node": "1.5.2", - "combine-source-map": "0.8.0", - "concat-stream": "1.6.2", - "is-buffer": "1.1.6", - "path-is-absolute": "1.0.1", - "process": "0.11.10", - "through2": "2.0.3", - "undeclared-identifiers": "1.1.2", - "xtend": "4.0.1" + "JSONStream": "^1.0.3", + "acorn-node": "^1.5.2", + "combine-source-map": "^0.8.0", + "concat-stream": "^1.6.1", + "is-buffer": "^1.1.0", + "path-is-absolute": "^1.0.1", + "process": "~0.11.0", + "through2": "^2.0.0", + "undeclared-identifiers": "^1.1.2", + "xtend": "^4.0.0" } }, "is-buffer": { @@ -752,7 +762,7 @@ "integrity": "sha1-YRwj6BTbN1Un34URk9tZ3Sryf0U=", "dev": true, "requires": { - "jsonify": "0.0.0" + "jsonify": "~0.0.0" } }, "jsonify": { @@ -773,9 +783,9 @@ "integrity": "sha512-MC94mHZRvJ3LfykJlTUipBqenZz1pacOZEMhhQ8dMGcDHs0SBE5GbsavUXV7YtP3icBW17W0Zy1I0lfASmo9Pg==", "dev": true, "requires": { - "inherits": "2.0.1", - "isarray": "2.0.4", - "stream-splicer": "2.0.0" + "inherits": "^2.0.1", + "isarray": "^2.0.4", + "stream-splicer": "^2.0.0" }, "dependencies": { "isarray": { @@ -793,13 +803,14 @@ "dev": true }, "md5.js": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", - "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", "dev": true, "requires": { - "hash-base": "3.0.4", - "inherits": "2.0.1" + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" } }, "miller-rabin": { @@ -808,8 +819,8 @@ "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", "dev": true, "requires": { - "bn.js": "4.11.8", - "brorand": "1.1.0" + "bn.js": "^4.0.0", + "brorand": "^1.0.1" } }, "minimalistic-assert": { @@ -830,12 +841,12 @@ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { - "brace-expansion": "1.1.11" + "brace-expansion": "^1.1.7" } }, "minimist": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, @@ -881,37 +892,37 @@ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.1", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } } } }, "module-deps": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-6.1.0.tgz", - "integrity": "sha512-NPs5N511VD1rrVJihSso/LiBShRbJALYBKzDW91uZYy7BpjnO4bGnZL3HjZ9yKcFdZUWwaYjDz9zxbuP7vKMuQ==", - "dev": true, - "requires": { - "JSONStream": "1.3.4", - "browser-resolve": "1.11.3", - "cached-path-relative": "1.0.1", - "concat-stream": "1.6.2", - "defined": "1.0.0", - "detective": "5.1.0", - "duplexer2": "0.1.4", - "inherits": "2.0.1", - "parents": "1.0.1", - "readable-stream": "2.3.6", - "resolve": "1.8.1", - "stream-combiner2": "1.1.1", - "subarg": "1.0.0", - "through2": "2.0.3", - "xtend": "4.0.1" + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-6.2.0.tgz", + "integrity": "sha512-hKPmO06so6bL/ZvqVNVqdTVO8UAYsi3tQWlCa+z9KuWhoN4KDQtb5hcqQQv58qYiDE21wIvnttZEPiDgEbpwbA==", + "dev": true, + "requires": { + "JSONStream": "^1.0.3", + "browser-resolve": "^1.7.0", + "cached-path-relative": "^1.0.0", + "concat-stream": "~1.6.0", + "defined": "^1.0.0", + "detective": "^5.0.2", + "duplexer2": "^0.1.2", + "inherits": "^2.0.1", + "parents": "^1.0.0", + "readable-stream": "^2.0.2", + "resolve": "^1.4.0", + "stream-combiner2": "^1.1.1", + "subarg": "^1.0.0", + "through2": "^2.0.0", + "xtend": "^4.0.0" } }, "ms": { @@ -926,7 +937,7 @@ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { - "wrappy": "1.0.2" + "wrappy": "1" } }, "os-browserify": { @@ -936,9 +947,9 @@ "dev": true }, "pako": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", - "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz", + "integrity": "sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==", "dev": true }, "parents": { @@ -947,20 +958,21 @@ "integrity": "sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E=", "dev": true, "requires": { - "path-platform": "0.11.15" + "path-platform": "~0.11.15" } }, "parse-asn1": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", - "integrity": "sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.4.tgz", + "integrity": "sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw==", "dev": true, "requires": { - "asn1.js": "4.10.1", - "browserify-aes": "1.2.0", - "create-hash": "1.2.0", - "evp_bytestokey": "1.0.3", - "pbkdf2": "3.0.16" + "asn1.js": "^4.0.0", + "browserify-aes": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" } }, "path-browserify": { @@ -988,16 +1000,16 @@ "dev": true }, "pbkdf2": { - "version": "3.0.16", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.16.tgz", - "integrity": "sha512-y4CXP3thSxqf7c0qmOF+9UeOTrifiVTIM+u7NWlq+PRsHbr7r7dpCmvzrZxa96JJUNi0Y5w9VqG5ZNeCVMoDcA==", + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", + "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", "dev": true, "requires": { - "create-hash": "1.2.0", - "create-hmac": "1.1.7", - "ripemd160": "2.0.2", - "safe-buffer": "5.1.2", - "sha.js": "2.4.11" + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" } }, "process": { @@ -1013,16 +1025,17 @@ "dev": true }, "public-encrypt": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.2.tgz", - "integrity": "sha512-4kJ5Esocg8X3h8YgJsKAuoesBgB7mqH3eowiDzMUPKiRDDE7E/BqqZD1hnTByIaAFiwAw246YEltSq7tdrOH0Q==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", "dev": true, "requires": { - "bn.js": "4.11.8", - "browserify-rsa": "4.0.1", - "create-hash": "1.2.0", - "parse-asn1": "5.1.1", - "randombytes": "2.0.6" + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" } }, "punycode": { @@ -1044,12 +1057,12 @@ "dev": true }, "randombytes": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz", - "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, "requires": { - "safe-buffer": "5.1.2" + "safe-buffer": "^5.1.0" } }, "randomfill": { @@ -1058,8 +1071,8 @@ "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", "dev": true, "requires": { - "randombytes": "2.0.6", - "safe-buffer": "5.1.2" + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" } }, "read-only-stream": { @@ -1068,7 +1081,7 @@ "integrity": "sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A=", "dev": true, "requires": { - "readable-stream": "2.3.6" + "readable-stream": "^2.0.2" } }, "readable-stream": { @@ -1077,13 +1090,13 @@ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "2.0.0", - "safe-buffer": "5.1.2", - "string_decoder": "1.1.1", - "util-deprecate": "1.0.2" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" }, "dependencies": { "inherits": { @@ -1091,16 +1104,25 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } } } }, "resolve": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", - "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.1.tgz", + "integrity": "sha512-KuIe4mf++td/eFb6wkaPbMDnP6kObCaEtIDuHOUED6MNUo4K670KZUHuuvYPZDxNF0WVLw49n06M2m2dXphEzA==", "dev": true, "requires": { - "path-parse": "1.0.6" + "path-parse": "^1.0.6" } }, "ripemd160": { @@ -1109,8 +1131,8 @@ "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", "dev": true, "requires": { - "hash-base": "3.0.4", - "inherits": "2.0.1" + "hash-base": "^3.0.0", + "inherits": "^2.0.1" } }, "safe-buffer": { @@ -1125,8 +1147,8 @@ "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "dev": true, "requires": { - "inherits": "2.0.1", - "safe-buffer": "5.1.2" + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" } }, "shasum": { @@ -1135,8 +1157,8 @@ "integrity": "sha1-5wEjENj0F/TetXEhUOVni4euVl8=", "dev": true, "requires": { - "json-stable-stringify": "0.0.1", - "sha.js": "2.4.11" + "json-stable-stringify": "~0.0.0", + "sha.js": "~2.4.4" } }, "shell-quote": { @@ -1145,10 +1167,10 @@ "integrity": "sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c=", "dev": true, "requires": { - "array-filter": "0.0.1", - "array-map": "0.0.0", - "array-reduce": "0.0.0", - "jsonify": "0.0.0" + "array-filter": "~0.0.0", + "array-map": "~0.0.0", + "array-reduce": "~0.0.0", + "jsonify": "~0.0.0" } }, "simple-concat": { @@ -1164,13 +1186,13 @@ "dev": true }, "stream-browserify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", - "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", "dev": true, "requires": { - "inherits": "2.0.1", - "readable-stream": "2.3.6" + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" } }, "stream-combiner2": { @@ -1179,8 +1201,8 @@ "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=", "dev": true, "requires": { - "duplexer2": "0.1.4", - "readable-stream": "2.3.6" + "duplexer2": "~0.1.0", + "readable-stream": "^2.0.2" } }, "stream-http": { @@ -1189,11 +1211,11 @@ "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", "dev": true, "requires": { - "builtin-status-codes": "3.0.0", - "inherits": "2.0.1", - "readable-stream": "2.3.6", - "to-arraybuffer": "1.0.1", - "xtend": "4.0.1" + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" } }, "stream-splicer": { @@ -1202,17 +1224,17 @@ "integrity": "sha1-G2O+Q4oTPktnHMGTUZdgAXWRDYM=", "dev": true, "requires": { - "inherits": "2.0.1", - "readable-stream": "2.3.6" + "inherits": "^2.0.1", + "readable-stream": "^2.0.2" } }, "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz", + "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==", "dev": true, "requires": { - "safe-buffer": "5.1.2" + "safe-buffer": "~5.1.0" } }, "subarg": { @@ -1221,7 +1243,7 @@ "integrity": "sha1-9izxdYHplrSPyWVpn1TAauJouNI=", "dev": true, "requires": { - "minimist": "1.2.0" + "minimist": "^1.1.0" } }, "supports-color": { @@ -1230,7 +1252,7 @@ "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } }, "syntax-error": { @@ -1239,7 +1261,7 @@ "integrity": "sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w==", "dev": true, "requires": { - "acorn-node": "1.5.2" + "acorn-node": "^1.2.0" } }, "through": { @@ -1249,13 +1271,13 @@ "dev": true }, "through2": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", - "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", "dev": true, "requires": { - "readable-stream": "2.3.6", - "xtend": "4.0.1" + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" } }, "timers-browserify": { @@ -1264,7 +1286,7 @@ "integrity": "sha1-ycWLV1voQHN1y14kYtrO50NZ9B0=", "dev": true, "requires": { - "process": "0.11.10" + "process": "~0.11.0" } }, "to-arraybuffer": { @@ -1292,15 +1314,16 @@ "dev": true }, "undeclared-identifiers": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/undeclared-identifiers/-/undeclared-identifiers-1.1.2.tgz", - "integrity": "sha512-13EaeocO4edF/3JKime9rD7oB6QI8llAGhgn5fKOPyfkJbRb6NFv9pYV6dFEmpa4uRjKeBqLZP8GpuzqHlKDMQ==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/undeclared-identifiers/-/undeclared-identifiers-1.1.3.tgz", + "integrity": "sha512-pJOW4nxjlmfwKApE4zvxLScM/njmwj/DiUBv7EabwE4O8kRUy+HIwxQtZLBPll/jx1LJyBcqNfB3/cpv9EZwOw==", "dev": true, "requires": { - "acorn-node": "1.5.2", - "get-assigned-identifiers": "1.2.0", - "simple-concat": "1.0.0", - "xtend": "4.0.1" + "acorn-node": "^1.3.0", + "dash-ast": "^1.0.0", + "get-assigned-identifiers": "^1.2.0", + "simple-concat": "^1.0.0", + "xtend": "^4.0.1" } }, "url": { diff --git a/package.json b/package.json index cf11772..4ec340d 100644 --- a/package.json +++ b/package.json @@ -18,9 +18,9 @@ "transcompiler" ], "devDependencies": { - "browserify": "~16.2.2", - "mocha": "~5.2.0", - "assert": "~1.4.1" + "assert": "~1.4.1", + "browserify": "^16.2.3", + "mocha": "~5.2.0" }, "scripts": { "test": "mocha ./test/suite.js"