Skip to content
Open

... #42

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
ccabd98
Infrastructure to pass options to js2php
cscott Feb 4, 2019
c1d6bed
Handle command-line arguments to js2php better
cscott Jan 31, 2019
5ab3159
Add option to use concise array syntax.
cscott Feb 4, 2019
0ed2bbd
Refactor to match line breaks in original JS.
cscott Jan 29, 2019
410d96a
Improvements to RegExp/String method translation.
cscott Jan 29, 2019
aa6402d
More improvements to mapping of standard library functions.
cscott Jan 30, 2019
930b01e
Fix translation of array functions with expression values like `()=>3`
cscott Jan 30, 2019
cfc8754
Support rest and spread arguments
cscott Jan 31, 2019
120cf03
Add namespace option; nominally support import statements using require.
cscott Jan 29, 2019
49c811e
Parsoid-specific: Skip `require('core-update.js')`
cscott Jan 29, 2019
0bfeeea
Parsoid-specific: handle Promise.async(... yield ...)
cscott Jan 31, 2019
a45dbd4
Parsoid-specific: Add support for a watermark at the top of the file.
cscott Feb 4, 2019
2a3a31e
Bug-fix to for loops with multiple initializers
cscott Feb 5, 2019
ed5b647
Fix regular expression matching in preg_replace()
cscott Feb 5, 2019
ea8ac24
Improve support for imports using `require`
cscott Feb 5, 2019
7e37a83
Fix Function#apply with non-literal argument
cscott Feb 5, 2019
761bcdc
Stub support for RegExp#exec
cscott Feb 5, 2019
02a15c1
Avoid unwanted aliases when looking up core library functions
cscott Feb 5, 2019
d48896e
Bug fix to binary operators when variable wasn't initialized
cscott Feb 5, 2019
22d0067
Support SequenceExpression and ObjectPattern assignments
cscott Feb 5, 2019
97b1cdd
Improvements to regular expression pattern for core library functions
cscott Feb 5, 2019
afc9702
Bug fix to for loops when initializer, test, or update expression is …
cscott Feb 5, 2019
b4bd92a
Tweak to match the Wikimedia PHP code style better (in particular, wh…
cscott Feb 11, 2019
4085195
Factor out utils.isType() and utils.isId() to simplify core.js
cscott Feb 12, 2019
59646b7
Use new utils.isType() and utils.isId() where appropriate
cscott Feb 12, 2019
68835b1
Disambiguate property access from method access in core library support
cscott Feb 12, 2019
74730de
JSON.stringify / JSON.parse are static methods
cscott Feb 12, 2019
42c4aef
Date.now() is a static method
cscott Feb 12, 2019
9ef8f03
Handle <class>.prototype.<method>.call(...)
cscott Feb 12, 2019
859c78b
Improve support for regexp literals
cscott Feb 12, 2019
5a55da1
Emit `elseif` where appropriate
cscott Feb 12, 2019
79e81e7
Ensure callee is marked as 'used' in scope when rearranging parameters
cscott Feb 12, 2019
2f58fde
Use PHP `implode` rather than `join`
cscott Feb 12, 2019
8c5b523
Catch some obvious cases of string concatenation and use the dot oper…
cscott Feb 12, 2019
6834267
Handle EmptyStatement
cscott Feb 12, 2019
9fb16d5
Don't return a value when evaluating a object pattern assignment
cscott Feb 12, 2019
94b4a16
Fix function name translation
cscott Feb 12, 2019
9e03fa1
Emit efficient single-quoted PHP strings where possible
cscott Feb 13, 2019
d081a1f
Emit NULL for JS undefined
cscott Feb 13, 2019
87d8818
Fixes to regular expression literals
cscott Feb 13, 2019
08abce1
String#match can be called with a plain string as argument
cscott Feb 13, 2019
0956fa0
More tweaks to string and regular expression literals
cscott Feb 13, 2019
485566a
Fix to parenthesized `new` expressions
cscott Feb 13, 2019
4235ef1
Fixes to string template expressions containing funny characters
cscott Feb 13, 2019
a09497e
Fix class reference in `new` expressions
cscott Feb 13, 2019
74295f9
Fix to backslash corner case in single-quoted string literals
cscott Feb 13, 2019
ec00f21
Suppress unnecessary `use` clauses in method definitions
cscott Feb 13, 2019
8e32151
Handle calls to Array#slice with no arguments
cscott Feb 13, 2019
0c26ebe
Ensure token-boundary code works at the beginning/start of the string
cscott Feb 13, 2019
da730e8
Parsoid-specific: add special case code for parsing pegjs files
cscott Feb 13, 2019
34e5e0a
Improve handling of String#substring and String#substr
cscott Mar 19, 2019
4ca1b38
$ npm audit fix
zaoqi May 4, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 62 additions & 4 deletions core.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@ 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'),
_promise = require('./core/promise'),
_regexp = require('./core/regexp'),
_string = require('./core/string'),
_math = require('./core/math'),
_number = require('./core/number');
Expand All @@ -13,13 +17,67 @@ module.exports = {

evaluate: function(node) {
var handler = undefined;
var get = function(obj, name) {
if (obj.hasOwnProperty(name)) { return obj[name]; }
return undefined;
};

if (node.property) {
var method = node.property.name;
handler = _array[method] || _date[method] || _function[method] || _json[method] || _string[method] || _math[method] || _number[method];
if (utils.isType(node.object, 'MemberExpression')) {
var newNode = module.exports.evaluate(node.object);
if (newNode !== node.object) {
node.object = newNode;
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)) {
method = '.' + method;
}
handler = node.object.regex ? get(_regexp, method) : get(_string, method);
} 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(_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)) {
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(_regexp, 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) {
Expand Down
108 changes: 101 additions & 7 deletions core/array.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,39 @@ 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);
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',
callee: {
type: 'Identifier',
name: 'array_unshift',
},
arguments: [node.parent.callee.object, args[0]]
arguments: args,
};
},

shift: function(node) {
node.parent.arguments = false;
scope.get(node).getDefinition(node.parent.callee.object);
return {
type: 'CallExpression',
callee: {
Expand All @@ -31,6 +47,8 @@ module.exports = {
},

reverse: function(node) {
node.parent.arguments = false;
scope.get(node).getDefinition(node.parent.callee.object);
return {
type: 'CallExpression',
callee: {
Expand All @@ -44,18 +62,23 @@ 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);

return {
type: 'CallExpression',
callee: {
type: 'Identifier',
name: 'array_push',
},
arguments: [ node.parent.callee.object, args[0] ]
arguments: args,
};
},

pop: function(node) {
node.parent.arguments = false;
scope.get(node).getDefinition(node.parent.callee.object);
return {
type: 'CallExpression',
callee: {
Expand All @@ -69,22 +92,89 @@ 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 {
type: 'CallExpression',
callee: {
type: 'Identifier',
name: 'join',
name: 'implode',
},
arguments: [ args[0], node.parent.callee.object ]
};
},

splice: function(node) {
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 {
type: 'CallExpression',
callee: {
type: 'Identifier',
name: 'array_map',
},
arguments: [ node.parent.callee.object, args[0] ]
};
},

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;
}
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) {
// 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 if (node.parent.arguments.length === 0) {
args.unshift({ type: 'Literal', value: 0, raw: '0' });
} 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: 'array_slice',
},
arguments: args
};
},

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',
Expand All @@ -101,6 +191,8 @@ 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);
if (utils.isString(node.parent.callee.object) || (targetDefinition && targetDefinition.dataType == "String")) {
Expand All @@ -122,15 +214,15 @@ module.exports = {

},

length: function(node) {
'.length': function(node) {
var method,
object = (node.parent.callee && node.parent.callee.object) || node.object,
isString = utils.isString(object);

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;
Expand All @@ -155,4 +247,6 @@ module.exports = {
};
},

}
};

utils.coreAddHash(module.exports, 'Array');
19 changes: 19 additions & 0 deletions core/console.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
var utils = require('../utils');

module.exports = {
'console.assert': function(node) {
return {
type: 'MemberExpression',
object: {
type: 'Identifier',
name: 'Assert',
},
property: {
type: 'Identifier',
name: 'invariant',
}
};
}
};

utils.coreAddHash(module.exports, 'console');
34 changes: 14 additions & 20 deletions core/date.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,21 @@
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: []
};
}

}
};

utils.coreAddHash(module.exports, 'Date');
18 changes: 13 additions & 5 deletions core/function.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
var utils = require('../utils');

function apply(node, isCall) {
var method, arguments = [];

Expand Down Expand Up @@ -25,10 +27,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;
Expand All @@ -53,4 +59,6 @@ module.exports = {
return apply(node, false)
},

}
};

utils.coreAddHash(module.exports, 'Function');
2 changes: 1 addition & 1 deletion core/global.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ module.exports = {
return newNode;
}

}
};
Loading