From 6a0170763a13f3d459e8cd150cf947365a1b1c49 Mon Sep 17 00:00:00 2001 From: Dan Phillimore Date: Sun, 25 Feb 2024 10:03:58 +0000 Subject: [PATCH] Introduce "snapshot" opcode replacing inefficient function chaining approach --- src/transpilerSpec.js | 544 ++++++++++++------ .../transpiler/constructs/emptyTest.js | 28 +- .../transpiler/constructs/evalTest.js | 6 +- .../transpiler/constructs/exitTest.js | 18 +- .../transpiler/constructs/heredocTest.js | 6 +- .../transpiler/constructs/issetTest.js | 56 +- .../transpiler/constructs/listTest.js | 16 +- .../transpiler/constructs/nowdocTest.js | 6 +- .../constructs/parentKeywordTest.js | 28 +- .../transpiler/constructs/selfKeywordTest.js | 20 +- .../constructs/staticKeywordTest.js | 20 +- .../constructs/stringInterpolationTest.js | 6 +- .../transpiler/constructs/unsetTest.js | 32 +- .../variable/variableVariableTest.js | 6 +- .../expressions/arrayLiteralTest.js | 113 +++- .../expressions/closure/closureTest.js | 4 +- .../expressions/closure/returnTypeTest.js | 4 +- .../expressions/functionCallTest.js | 53 +- .../transpiler/expressions/includeOnceTest.js | 4 +- .../transpiler/expressions/includeTest.js | 4 +- .../expressions/instanceMethodCallTest.js | 104 +++- .../transpiler/expressions/requireOnceTest.js | 4 +- .../transpiler/expressions/requireTest.js | 4 +- .../expressions/staticMethodCallTest.js | 128 ++++- .../transpiler/expressions/ternaryTest.js | 6 +- .../variable/variableFunctionCallTest.js | 45 +- .../variableInstanceMethodCallTest.js | 103 +++- .../variable/variableStaticMethodCallTest.js | 129 ++++- .../extensions/customStatementTest.js | 10 +- test/integration/transpiler/lineNumberTest.js | 22 +- test/integration/transpiler/modeOptionTest.js | 6 +- test/integration/transpiler/newTest.js | 80 --- .../arithmetic/additionOperatorTest.js | 55 +- .../arithmetic/divisionOperatorTest.js | 55 +- .../arithmetic/identityOperatorTest.js | 6 +- .../arithmetic/moduloOperatorTest.js | 55 +- .../arithmetic/multiplicationOperatorTest.js | 55 +- .../arithmetic/negationOperatorTest.js | 6 +- .../arithmetic/subtractionOperatorTest.js | 55 +- .../transpiler/operators/arrayAccessTest.js | 167 ++++-- .../assignment/additionAssignmentTest.js | 8 +- .../operators/assignment/assignmentTest.js | 8 +- .../assignment/bitwiseAndAssignmentTest.js | 8 +- .../bitwiseLeftShiftAssignmentTest.js | 8 +- .../assignment/bitwiseOrAssignmentTest.js | 8 +- .../bitwiseRightShiftAssignmentTest.js | 2 +- .../assignment/bitwiseXorAssignmentTest.js | 8 +- .../assignment/concatenationAssignmentTest.js | 8 +- .../assignment/divisionAssignmentTest.js | 8 +- .../assignment/moduloAssignmentTest.js | 8 +- .../multiplicationAssignmentTest.js | 8 +- .../assignment/referenceAssignmentTest.js | 16 +- .../assignment/subtractionAssignmentTest.js | 8 +- .../bitwise/bitwiseAndOperatorTest.js | 55 +- .../bitwise/bitwiseLeftShiftOperatorTest.js | 55 +- .../bitwise/bitwiseOrOperatorTest.js | 55 +- .../bitwise/bitwiseRightShiftOperatorTest.js | 55 +- .../bitwise/bitwiseXorOperatorTest.js | 55 +- .../transpiler/operators/cast/arrayTest.js | 2 +- .../transpiler/operators/cast/binaryTest.js | 2 +- .../transpiler/operators/cast/booleanTest.js | 2 +- .../transpiler/operators/cast/doubleTest.js | 2 +- .../transpiler/operators/cast/integerTest.js | 2 +- .../transpiler/operators/cast/objectTest.js | 2 +- .../transpiler/operators/cast/stringTest.js | 2 +- .../transpiler/operators/cast/unsetTest.js | 2 +- .../transpiler/operators/cloneTest.js | 6 +- .../comparison/greaterThanOrEqualTest.js | 49 +- .../operators/comparison/greaterThanTest.js | 49 +- .../comparison/lessThanOrEqualTest.js | 49 +- .../operators/comparison/lessThanTest.js | 49 +- .../operators/comparison/looseEqualityTest.js | 49 +- .../comparison/looseInequalityTest.js | 51 +- .../transpiler/operators/decrementTest.js | 12 +- .../transpiler/operators/errorControlTest.js | 6 +- .../transpiler/operators/incrementTest.js | 12 +- .../transpiler/operators/instanceOfTest.js | 24 +- .../transpiler/operators/logical/notTest.js | 54 +- .../transpiler/operators/logical/xorTest.js | 49 +- .../transpiler/operators/newTest.js | 173 ++++++ .../objectAccess/instancePropertyTest.js | 53 +- .../scopeResolution/staticPropertyTest.js | 6 +- test/integration/transpiler/printTest.js | 6 +- test/integration/transpiler/returnTest.js | 12 +- .../transpiler/scopeResolutionOperatorTest.js | 10 +- test/integration/transpiler/sourceMapTest.js | 9 +- .../transpiler/statements/breakTest.js | 34 +- .../statements/class/abstractTest.js | 6 +- .../statements/class/constantTest.js | 8 +- .../statements/class/extendsTest.js | 6 +- .../class/method/methodDefinitionTest.js | 6 +- .../statements/class/propertyTest.js | 28 +- .../transpiler/statements/constantTest.js | 6 +- .../transpiler/statements/continueTest.js | 36 +- .../transpiler/statements/doWhileTest.js | 14 +- .../transpiler/statements/echoTest.js | 6 +- .../transpiler/statements/forTest.js | 16 +- .../transpiler/statements/foreachTest.js | 18 +- .../statements/function/generatorTest.js | 59 +- .../transpiler/statements/functionTest.js | 24 +- .../transpiler/statements/globalTest.js | 8 +- .../transpiler/statements/gotoTest.js | 92 +-- .../transpiler/statements/hoistingTest.js | 56 +- .../transpiler/statements/ifStatementTest.js | 24 +- .../transpiler/statements/inlineHtmlTest.js | 8 +- .../transpiler/statements/interfaceTest.js | 9 +- .../transpiler/statements/labelTest.js | 37 ++ .../transpiler/statements/namespaceTest.js | 34 +- .../transpiler/statements/staticTest.js | 6 +- .../transpiler/statements/switchTest.js | 38 +- .../transpiler/statements/throwTest.js | 6 +- .../transpiler/statements/tryTest.js | 42 +- .../transpiler/statements/useStatementTest.js | 12 +- .../transpiler/statements/whileTest.js | 14 +- .../transpiler/statements/yieldTest.js | 110 ++++ .../transpiler/types/arrayTypeTest.js | 45 ++ .../transpiler/types/iterableTypeTest.js | 45 ++ 117 files changed, 3059 insertions(+), 988 deletions(-) delete mode 100644 test/integration/transpiler/newTest.js create mode 100644 test/integration/transpiler/operators/newTest.js create mode 100644 test/integration/transpiler/statements/labelTest.js create mode 100644 test/integration/transpiler/types/arrayTypeTest.js create mode 100644 test/integration/transpiler/types/iterableTypeTest.js diff --git a/src/transpilerSpec.js b/src/transpilerSpec.js index 7cdef14..640fe99 100644 --- a/src/transpilerSpec.js +++ b/src/transpilerSpec.js @@ -40,6 +40,16 @@ var _ = require('microdash'), VOID_FUNCTION_MUST_NOT_RETURN_VALUE = 'core.void_function_must_not_return_value', YIELD_OUTSIDE_FUNCTION = 'core.yield_outside_function', + LITERAL_NODE_TYPES = { + 'N_ARRAY_LITERAL': true, + 'N_BOOLEAN': true, + 'N_FLOAT': true, + 'N_INTEGER': true, + 'N_NULL': true, + 'N_STRING': true, + 'N_STRING_LITERAL': true + }, + binaryOperatorToOpcode = { '+': 'add', '-': 'subtract', @@ -104,6 +114,127 @@ var _ = require('microdash'), SourceNode = sourceMap.SourceNode, Translator = phpCommon.Translator; +/** + * Determines whether the given expression AST node is a literal. + * + * @param {name: string} node + * @return {boolean} + */ +function isLiteral(node) { + return hasOwn.call(LITERAL_NODE_TYPES, node.name); +} + +/** + * Determines whether the given expression AST node is benign. + * Benign nodes cannot cause writes, e.g. literals, identifiers or plain variables. + * + * @param {name: string} node + * @return {boolean} + */ +function isBenign(node) { + return node.name === 'N_VARIABLE' || isLiteral(node); +} + +/** + * Creates a snapshot opcode of the given expression chunks. + * + * @param {(*|string)[]} chunks + * @param {Object} context + * @returns {(*|string)[]} + */ +function snapshotExpression(chunks, context) { + return [context.useCoreSymbol('snapshot'), '(', chunks, ')']; +} + +/** + * Creates a snapshot opcode of the given left operand if needed, + * based on a simple heuristic of whether the right operand is simple enough not to warrant it. + * + * @param {Object} leftOperand + * @param {(*|string)[]} chunks + * @param {Object} rightOperand + * @param {Object} context + * @returns {(*|string)[]} + */ +function snapshotLeftOperand(leftOperand, chunks, rightOperand, context) { + var leftOperandIsLiteral = isLiteral(leftOperand), + rightOperandIsBenign = isBenign(rightOperand); + + /* + * When left operand is a literal, there is no need to snapshot it as we already have a value + * by definition. + * + * When left operand is not but right operand is benign, there is no need to snapshot + * the left operand because the right one cannot influence it. + */ + return leftOperandIsLiteral || rightOperandIsBenign ? + chunks : + snapshotExpression(chunks, context); +} + +function decideOperandListSnapshots(operandSpecs) { + var index, + operandCount = operandSpecs.length, + operandSpec, + snapshotMap = [], + snapshotting = false; + + for (index = operandCount - 1; index >= 0; index--) { + operandSpec = operandSpecs[index]; + + if (operandSpec.type === 'chunks') { + // Never any need to snapshot raw chunks. + snapshotMap[index] = false; + continue; + } + + if (isLiteral(operandSpec.node)) { + // Never any need to snapshot literal expressions. + snapshotMap[index] = false; + continue; + } + + if (snapshotting) { + snapshotMap[index] = true; + continue; + } + + /* + * Last non-benign expression turns on snapshotting for previous operands, + * but isn't itself snapshotted (as no subsequent operands can affect it). + * + * No need to snapshot the final operand, which will reach here, either, + * as there can by definition never be a subsequent operand to affect it. + */ + if (!isBenign(operandSpec.node)) { + snapshotting = true; + } + + snapshotMap[index] = false; + } + + return snapshotMap; +} + +function snapshotOperandList(operandSpecs, context) { + var resultChunks = [], + snapshotMap = decideOperandListSnapshots(operandSpecs); + + _.each(operandSpecs, function (operandSpec, index) { + if (index > 0) { + resultChunks.push(', '); + } + + resultChunks.push( + snapshotMap[index] ? + snapshotExpression(operandSpec.chunks, context) : + operandSpec.chunks + ); + }); + + return resultChunks; +} + /** * Builds chunks for the optional extra arguments that may need to be passed * when defining a function, method or closure. These include the parameter @@ -287,9 +418,7 @@ function interpretFunction(functionNode, nameNode, argNodes, bindingNodes, state return context.useCoreSymbol(name); }, subContext = { - // This sub-context will be merged with the parent one, - // so we need to override any value for the `assignment` option. - assignment: undefined, + // This sub-context will be merged with the parent one. blockContexts: [], hasVoidReturnType: functionNode.returnType && functionNode.returnType.name === 'N_VOID_TYPE', insideFunction: true, @@ -349,7 +478,7 @@ function interpretFunction(functionNode, nameNode, argNodes, bindingNodes, state useCoreSymbol('getVariable'), '(', JSON.stringify(variableName), - '))(', + '), ', useCoreSymbol(bindingOpcode), '(', JSON.stringify(variableName), @@ -457,7 +586,7 @@ function interpretClosureBindings(bindingNodes) { } /** - * Produces an array or object literal containing all the information about + * Produces an array of object literals containing all the information about * the parameters to a function, closure or static/instance method. * * @param {Object[]} argNodes @@ -701,8 +830,9 @@ module.exports = { [ context.useCoreSymbol('getVariableElement'), '(', - interpret(node.array), - ')(', + // Must be snapshotted because index expression may modify array variable/reference. + snapshotLeftOperand(node.array, interpret(node.array), node.index, context), + ', ', interpret(node.index), ')' ] : @@ -718,35 +848,46 @@ module.exports = { ); }, 'N_ARRAY_LITERAL': function (node, interpret, context) { - var allElementChunks = []; + var elementSpecs = []; - _.each(node.elements, function (element, index) { + _.each(node.elements, function (element) { var elementChunks; - if (index > 0) { - allElementChunks.push(')('); + if (element.name === 'N_REFERENCE') { + elementSpecs.push({ + type: 'chunks', + chunks: [ + context.useCoreSymbol('createReferenceElement'), + '(', + interpret(element.operand), + ')' + ] + }); + } else { + elementChunks = interpret(element); + + elementSpecs.push( + element.name === 'N_KEY_VALUE_PAIR' ? + // Never any need to snapshot a pair. + { + type: 'chunks', + chunks: elementChunks + } : + { + type: 'node', + chunks: elementChunks, + node: element + } + ); } - - elementChunks = (element.name === 'N_REFERENCE') ? - [ - context.useCoreSymbol('createReferenceElement'), - '(', - interpret(element.operand), - ')' - ] : - interpret(element); - - allElementChunks.push(elementChunks); }); return context.createExpressionSourceNode( [ context.useCoreSymbol('createArray'), - // Only append elements if non-empty. - allElementChunks.length > 0 ? - ['(', allElementChunks, ')'] : - [], - '()' + '(', + snapshotOperandList(elementSpecs, context), + ')' ], node ); @@ -863,7 +1004,7 @@ module.exports = { ), '(', classNameChunks, - ')(', + ', ', JSON.stringify(node.constant), ')' ]; @@ -1263,9 +1404,9 @@ module.exports = { }, 'N_EXPRESSION': function (node, interpret, context) { var chunks = [], - isAssignment = /^(?:[-+*/.%&|^]|<<|>>)?=$/.test(node.right[0].operator), + isAssignment = node.right[0].operator === '=', isReference, - leftChunks = interpret(node.left, {assignment: isAssignment}), + leftChunks = interpret(node.left), opcode, operation, rightOperand, @@ -1323,13 +1464,25 @@ module.exports = { opcode = opcode[isReference]; } - chunks.push(context.useCoreSymbol(opcode), '(', leftChunks, ')(', transpiledRightOperand, ')'); + chunks.push( + context.useCoreSymbol(opcode), + '(', + isAssignment ? + // Simple assignments do not need the original left operand value, + // so do not need to be snapshotted. + leftChunks : + // Must be snapshotted because right operand may modify left operand variable/reference. + snapshotLeftOperand(node.left, leftChunks, rightOperand, context), + ', ', + transpiledRightOperand, + ')' + ); } return context.createExpressionSourceNode(chunks, node); }, 'N_EXPRESSION_STATEMENT': function (node, interpret, context) { - return context.createStatementSourceNode(interpret(node.expression).concat(';'), node); + return context.createStatementSourceNode([interpret(node.expression), ';'], node); }, 'N_FLOAT': function (node, interpret, context) { return context.createExpressionSourceNode( @@ -1555,50 +1708,41 @@ module.exports = { ); }, 'N_FUNCTION_CALL': function (node, interpret, context) { - var argChunks = [], - callChunks; + var isStatic = node.func.name === 'N_STRING', + operandSpecs = []; - _.each(node.args, function (arg, index) { - if (index > 0) { - argChunks.push(')('); - } + operandSpecs.push( + isStatic ? + { + type: 'chunks', + chunks: [JSON.stringify(node.func.string)] + } : + { + type: 'node', + // Variable function name expression becomes one of the operands in this list of specs, + // so that it will be snapshotted if needed - no need to handle snapshotting here. + chunks: interpret(node.func, {allowBareword: true}), + node: node.func + } + ); - argChunks.push(interpret(arg)); + _.each(node.args, function (argNode) { + operandSpecs.push({ + type: 'node', + chunks: interpret(argNode), + node: argNode + }); }); - if (node.func.name === 'N_STRING') { - // Faster case: function call is to a statically-given function name - - callChunks = [ - context.useCoreSymbol('callFunction'), - '(', - JSON.stringify(node.func.string), - ')', - - // Only append arguments if non-empty. - argChunks.length > 0 ? - ['(', argChunks, ')'] : - [], - '()' - ]; - } else { - // Slower case: function call is to a variable function name - - callChunks = [ - context.useCoreSymbol('callVariableFunction'), + return context.createExpressionSourceNode( + [ + context.useCoreSymbol(isStatic ? 'callFunction' : 'callVariableFunction'), '(', - interpret(node.func, {allowBareword: true}), - ')', - - // Only append arguments if non-empty. - argChunks.length > 0 ? - ['(', argChunks, ')'] : - [], - '()' - ]; - } - - return context.createExpressionSourceNode(callChunks, node); + snapshotOperandList(operandSpecs, context), + ')' + ], + node + ); }, 'N_GLOBAL_STATEMENT': function (node, interpret, context) { var chunks = []; @@ -1730,8 +1874,8 @@ module.exports = { [ context.useCoreSymbol('instanceOf'), '(', - interpret(node.object), - ')(', + snapshotLeftOperand(node.object, interpret(node.object), node['class'], context), + ', ', interpret(node['class'], {allowBareword: true}), ')' ], @@ -1841,21 +1985,23 @@ module.exports = { }; }, 'N_ISSET': function (node, interpret, context) { - var issetChunks = []; - - _.each(node.variables, function (variable, index) { - if (index > 0) { - issetChunks.push(', '); - } + var operandSpecs = []; - issetChunks.push(interpret(variable)); + _.each(node.variables, function (variable) { + operandSpecs.push({ + type: 'node', + chunks: interpret(variable), + node: variable + }); }); return context.createExpressionSourceNode( [ context.useCoreSymbol('isSet'), + // TODO: Use variadic arg instead of native array literal, as we do for PHP array literals, + // to save on compiled bundle size? '()([', - issetChunks, + snapshotOperandList(operandSpecs, context), '])' ], node @@ -1865,16 +2011,18 @@ module.exports = { return '"type":"iterable"'; }, 'N_KEY_VALUE_PAIR': function (node, interpret, context) { - var isReference = node.value.name === 'N_REFERENCE'; + var isReference = node.value.name === 'N_REFERENCE', + valueOperand = isReference ? node.value.operand : node.value; return context.createExpressionSourceNode( [ context.useCoreSymbol('createKey' + (isReference ? 'Reference' : 'Value') + 'Pair'), '(', - interpret(node.key), - ')(', - // No need to wrap references in getReference(), createKeyReferencePair() will handle that - interpret(isReference ? node.value.operand : node.value), + // Must be snapshotted because value expression may modify key variable/reference. + snapshotLeftOperand(node.key, interpret(node.key), valueOperand, context), + ', ', + // No need to wrap references in getReference(), createKeyReferencePair() will handle that. + interpret(valueOperand), ')' ], node @@ -1905,7 +2053,7 @@ module.exports = { _.each(node.elements, function (element, index) { if (index > 0) { - elementsCodeChunks.push(')('); + elementsCodeChunks.push(', '); } elementsCodeChunks.push(interpret(element)); @@ -1914,11 +2062,9 @@ module.exports = { return context.createExpressionSourceNode( [ context.useCoreSymbol('createList'), - // Only append elements if non-empty. - elementsCodeChunks.length > 0 ? - ['(', elementsCodeChunks, ')'] : - [], - '()' + '(', + elementsCodeChunks, + ')' ], node ); @@ -1970,33 +2116,42 @@ module.exports = { ); }, 'N_METHOD_CALL': function (node, interpret, context) { - var argChunks = [], - isVariable = node.method.name !== 'N_STRING'; - - _.each(node.args, function (argNode, index) { - if (index > 0) { - argChunks.push(')('); - } + // Determine whether method name is a bareword. + var isStatic = node.method.name === 'N_STRING', + operandSpecs = [ + { + type: 'node', + chunks: interpret(node.object), + node: node.object + }, + isStatic ? + { + type: 'chunks', + chunks: [JSON.stringify(node.method.string)] + } : + { + type: 'node', + // Variable method name expression becomes one of the operands in this list of specs, + // so that it will be snapshotted if needed - no need to handle snapshotting here. + chunks: interpret(node.method, {allowBareword: true}), + node: node.method + } + ]; - argChunks.push(interpret(argNode)); + _.each(node.args, function (argNode) { + operandSpecs.push({ + type: 'node', + chunks: interpret(argNode), + node: argNode + }); }); return context.createExpressionSourceNode( [ - context.useCoreSymbol(isVariable ? 'callVariableInstanceMethod' : 'callInstanceMethod'), + context.useCoreSymbol(isStatic ? 'callInstanceMethod' : 'callVariableInstanceMethod'), '(', - interpret(node.object), - ')(', - - // Add the method name, which for a variable method call will be an expression. - isVariable ? interpret(node.method, {allowBareword: true}) : JSON.stringify(node.method.string), - ')', - - // Only append arguments if non-empty. - argChunks.length > 0 ? - ['(', argChunks, ')'] : - [], - '()' + snapshotOperandList(operandSpecs, context), + ')' ], node ); @@ -2067,27 +2222,28 @@ module.exports = { ); }, 'N_NEW_EXPRESSION': function (node, interpret, context) { - var argChunks = []; - - _.each(node.args, function (arg, index) { - if (index > 0) { - argChunks.push(')('); - } - - argChunks.push(interpret(arg)); + var operandSpecs = [{ + type: 'node', + // Class name expression becomes one of the operands in this list of specs, + // so that it will be snapshotted if needed - no need to handle snapshotting here. + chunks: interpret(node.className, {allowBareword: true}), + node: node.className + }]; + + _.each(node.args, function (argNode) { + operandSpecs.push({ + type: 'node', + chunks: interpret(argNode), + node: argNode + }); }); return context.createExpressionSourceNode( [ context.useCoreSymbol('createInstance'), '(', - interpret(node.className, {allowBareword: true}), - ')', - // Only append arguments if non-empty. - argChunks.length > 0 ? - ['(', argChunks, ')'] : - [], - '()' + snapshotOperandList(operandSpecs, context), + ')' ], node ); @@ -2124,26 +2280,16 @@ module.exports = { ); }, 'N_OBJECT_PROPERTY': function (node, interpret, context) { - var objectVariableCodeChunks, - property, + var objectVariableCodeChunks = interpret(node.object), + property = node.property, propertyCodeChunks = []; - if (context.assignment) { - objectVariableCodeChunks = [ - interpret(node.object) - ]; - } else { - objectVariableCodeChunks = interpret(node.object); - } - - property = node.property; - if (property.name === 'N_STRING') { propertyCodeChunks.push( context.useCoreSymbol('getInstanceProperty'), '(', objectVariableCodeChunks, - ')(', + ', ', JSON.stringify(property.string), ')' ); @@ -2151,9 +2297,10 @@ module.exports = { propertyCodeChunks.push( context.useCoreSymbol('getVariableInstanceProperty'), '(', - objectVariableCodeChunks, - ')(', - interpret(property, {assignment: false, allowBareword: true}), + // Must be snapshotted because property name expression may modify object variable/reference. + snapshotLeftOperand(node.object, objectVariableCodeChunks, property, context), + ', ', + interpret(property, {allowBareword: true}), ')' ); } @@ -2565,42 +2712,50 @@ module.exports = { ); }, 'N_STATIC_METHOD_CALL': function (node, interpret, context) { - var argChunks = [], - isForwarding = node.className.name === 'N_SELF' || + var isForwarding = node.className.name === 'N_SELF' || node.className.name === 'N_PARENT' || node.className.name === 'N_STATIC', - isVariable = node.method.name !== 'N_STRING'; - - _.each(node.args, function (arg, index) { - if (index > 0) { - argChunks.push(')('); - } + // Determine whether method name is a bareword. + isStatic = node.method.name === 'N_STRING', + operandSpecs = [ + { + type: 'node', + chunks: interpret(node.className, {allowBareword: true}), + node: node.className + }, + isStatic ? + { + type: 'chunks', + chunks: [JSON.stringify(node.method.string)] + } : + { + type: 'node', + // Variable method name expression becomes one of the operands in this list of specs, + // so that it will be snapshotted if needed - no need to handle snapshotting here. + chunks: interpret(node.method, {allowBareword: true}), + node: node.method + }, + // Add whether the call is forwarding or non-forwarding. + { + type: 'chunks', + chunks: [isForwarding ? 'true' : 'false'] + } + ]; - argChunks.push(interpret(arg)); + _.each(node.args, function (argNode) { + operandSpecs.push({ + type: 'node', + chunks: interpret(argNode), + node: argNode + }); }); return context.createExpressionSourceNode( [ - context.useCoreSymbol(isVariable ? 'callVariableStaticMethod' : 'callStaticMethod'), - '(', - interpret(node.className, {allowBareword: true}), - ')', - - // Add the method name, which for a variable method call will be an expression. - '(', - isVariable ? interpret(node.method, {allowBareword: true}) : JSON.stringify(node.method.string), - ')', - - // Add whether the call is forwarding or non-forwarding. + context.useCoreSymbol(isStatic ? 'callStaticMethod' : 'callVariableStaticMethod'), '(', - isForwarding ? 'true' : 'false', - ')', - - // Only append arguments if non-empty. - argChunks.length > 0 ? - ['(', argChunks, ')'] : - [], - '()' + snapshotOperandList(operandSpecs, context), + ')' ], node ); @@ -2650,7 +2805,7 @@ module.exports = { context.useCoreSymbol('getStaticProperty'), '(', classVariableCodeChunks, - ')(', + ', ', JSON.stringify(node.property.string), ')' ); @@ -2658,9 +2813,10 @@ module.exports = { propertyCodeChunks.push( context.useCoreSymbol('getVariableStaticProperty'), '(', - classVariableCodeChunks, - ')(', - interpret(node.property, {assignment: false, allowBareword: true}), + // Must be snapshotted because property name expression may modify object variable/reference. + snapshotLeftOperand(node.className, classVariableCodeChunks, node.property, context), + ', ', + interpret(node.property, {allowBareword: true}), ')' ); } @@ -2981,7 +3137,7 @@ module.exports = { _.each(node.variables, function (variableNode, index) { if (index > 0) { - statementChunks.push(')('); + statementChunks.push(', '); } statementChunks.push(interpret(variableNode)); @@ -2993,11 +3149,9 @@ module.exports = { [ context.useCoreSymbol('unset'), - // Only append references if non-empty. - statementChunks.length > 0 ? - ['(', statementChunks, ')'] : - [], - '();' + '(', + statementChunks, + ');' ], node ); @@ -3118,14 +3272,34 @@ module.exports = { ); }, 'N_YIELD_EXPRESSION': function (node, interpret, context) { + var operandSpecs; + if (!context.insideFunction) { context.raiseError(YIELD_OUTSIDE_FUNCTION, node); } - return context.createExpressionSourceNode( - node.key ? - [context.useCoreSymbol('yieldWithKey'), '(', interpret(node.key), ')(', interpret(node.value), ')'] : + if (!node.key) { + return context.createExpressionSourceNode( [context.useCoreSymbol('yield_'), '(', interpret(node.value), ')'] , + node + ); + } + + operandSpecs = [ + { + type: 'node', + chunks: interpret(node.key), + node: node.key + }, + { + type: 'node', + chunks: interpret(node.value), + node: node.value + } + ]; + + return context.createExpressionSourceNode( + [context.useCoreSymbol('yieldWithKey'), '(', snapshotOperandList(operandSpecs, context), ')'], node ); } diff --git a/test/integration/transpiler/constructs/emptyTest.js b/test/integration/transpiler/constructs/emptyTest.js index 5a0c903..b3cefb3 100644 --- a/test/integration/transpiler/constructs/emptyTest.js +++ b/test/integration/transpiler/constructs/emptyTest.js @@ -28,11 +28,11 @@ describe('Transpiler empty(...) construct expression test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var getVariable = core.getVariable, isEmpty = core.isEmpty;' + 'return isEmpty()(getVariable("a_var"));' + - '});' + '}' ); }); @@ -58,11 +58,11 @@ describe('Transpiler empty(...) construct expression test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var getElement = core.getElement, getVariable = core.getVariable, isEmpty = core.isEmpty;' + 'return isEmpty()(getElement(getVariable("myArray"), 21));' + - '});' + '}' ); }); @@ -88,11 +88,11 @@ describe('Transpiler empty(...) construct expression test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var getInstanceProperty = core.getInstanceProperty, getVariable = core.getVariable, isEmpty = core.isEmpty;' + - 'return isEmpty()(getInstanceProperty(getVariable("myObject"))("myProp"));' + - '});' + 'return isEmpty()(getInstanceProperty(getVariable("myObject"), "myProp"));' + + '}' ); }); @@ -117,11 +117,11 @@ describe('Transpiler empty(...) construct expression test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var getClassNameOrThrow = core.getClassNameOrThrow, getStaticProperty = core.getStaticProperty, isEmpty = core.isEmpty;' + - 'return isEmpty()(getStaticProperty(getClassNameOrThrow())("myProp"));' + - '});' + 'return isEmpty()(getStaticProperty(getClassNameOrThrow(), "myProp"));' + + '}' ); }); }); diff --git a/test/integration/transpiler/constructs/evalTest.js b/test/integration/transpiler/constructs/evalTest.js index 702ccb1..bb7f658 100644 --- a/test/integration/transpiler/constructs/evalTest.js +++ b/test/integration/transpiler/constructs/evalTest.js @@ -28,11 +28,11 @@ describe('Transpiler eval(...) construct expression test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var eval = core.eval, getVariable = core.getVariable;' + 'return eval(getVariable("myCode"));' + - '});' + '}' ); }); }); diff --git a/test/integration/transpiler/constructs/exitTest.js b/test/integration/transpiler/constructs/exitTest.js index fc68ef6..9cdc87d 100644 --- a/test/integration/transpiler/constructs/exitTest.js +++ b/test/integration/transpiler/constructs/exitTest.js @@ -24,11 +24,11 @@ describe('Transpiler exit(...) construct expression test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var exit = core.exit;' + 'exit();' + - '});' + '}' ); }); @@ -47,11 +47,11 @@ describe('Transpiler exit(...) construct expression test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var createInteger = core.createInteger, exit = core.exit;' + 'exit(createInteger(21));' + - '});' + '}' ); }); @@ -70,11 +70,11 @@ describe('Transpiler exit(...) construct expression test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var createString = core.createString, exit = core.exit, print = core.print;' + '(print(createString("My failure message")), exit());' + - '});' + '}' ); }); }); diff --git a/test/integration/transpiler/constructs/heredocTest.js b/test/integration/transpiler/constructs/heredocTest.js index c4c9581..b174e4f 100644 --- a/test/integration/transpiler/constructs/heredocTest.js +++ b/test/integration/transpiler/constructs/heredocTest.js @@ -37,8 +37,8 @@ describe('Transpiler "heredoc" statement test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var getVariable = core.getVariable, interpolate = core.interpolate;' + 'return interpolate([' + '"Increase ", ' + @@ -46,7 +46,7 @@ describe('Transpiler "heredoc" statement test', function () { '" with ", ' + 'getVariable("secondVar")' + ']);' + - '});' + '}' ); }); }); diff --git a/test/integration/transpiler/constructs/issetTest.js b/test/integration/transpiler/constructs/issetTest.js index 6f167f1..dbdecc0 100644 --- a/test/integration/transpiler/constructs/issetTest.js +++ b/test/integration/transpiler/constructs/issetTest.js @@ -28,15 +28,15 @@ describe('Transpiler isset(...) construct expression test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var getVariable = core.getVariable, isSet = core.isSet;' + 'return isSet()([getVariable("a_var")]);' + - '});' + '}' ); }); - it('should correctly transpile a return statement with multiple expressions', function () { + it('should correctly transpile a return statement with multiple benign expressions', function () { var ast = { name: 'N_PROGRAM', statements: [{ @@ -54,11 +54,47 @@ describe('Transpiler isset(...) construct expression test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var getVariable = core.getVariable, isSet = core.isSet;' + 'return isSet()([getVariable("a_var"), getVariable("another_var")]);' + - '});' + '}' + ); + }); + + it('should correctly transpile a return statement with multiple expressions, one complex', function () { + var ast = { + name: 'N_PROGRAM', + statements: [{ + name: 'N_RETURN_STATEMENT', + expression: { + name: 'N_ISSET', + variables: [{ + name: 'N_VARIABLE', + variable: 'a_var' + }, { + name: 'N_VARIABLE_EXPRESSION', + expression: { + name: 'N_VARIABLE', + variable: 'another_var' + } + }] + } + }] + }; + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + + 'var getVariable = core.getVariable, getVariableVariable = core.getVariableVariable, isSet = core.isSet, snapshot = core.snapshot;' + + 'return isSet()([' + + // This operand must be snapshotted as it is followed by an expression that may modify it + // (based on simple heuristics). + 'snapshot(getVariable("a_var")), ' + + // Final operand does not need to be snapshotted, as there are no subsequent complex operands + // that may affect its result prior to the actual call executing. + 'getVariableVariable(getVariable("another_var"))' + + ']);' + + '}' ); }); @@ -84,11 +120,11 @@ describe('Transpiler isset(...) construct expression test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var getElement = core.getElement, getVariable = core.getVariable, isSet = core.isSet;' + 'return isSet()([getElement(getVariable("myArray"), 21)]);' + - '});' + '}' ); }); }); diff --git a/test/integration/transpiler/constructs/listTest.js b/test/integration/transpiler/constructs/listTest.js index 7da7c28..3a4a4ff 100644 --- a/test/integration/transpiler/constructs/listTest.js +++ b/test/integration/transpiler/constructs/listTest.js @@ -44,7 +44,7 @@ describe('Transpiler list(...) construct test', function () { expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + 'var createArray = core.createArray, createInteger = core.createInteger, createList = core.createList, getVariable = core.getVariable, setValue = core.setValue;' + - 'setValue(createList(getVariable("myVar"))())(createArray(createInteger(21))());' + + 'setValue(createList(getVariable("myVar")), createArray(createInteger(21)));' + '}' ); }); @@ -82,11 +82,17 @@ describe('Transpiler list(...) construct test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var createArray = core.createArray, createInteger = core.createInteger, createList = core.createList, createVoid = core.createVoid, getVariable = core.getVariable, setValue = core.setValue;' + - 'setValue(createList(createVoid())(getVariable("myVar"))())(createArray(createInteger(4))(createInteger(21))());' + - '});' + 'setValue(' + + 'createList(' + + 'createVoid(), ' + + 'getVariable("myVar")' + + '), ' + + 'createArray(createInteger(4), createInteger(21))' + + ');' + + '}' ); }); }); diff --git a/test/integration/transpiler/constructs/nowdocTest.js b/test/integration/transpiler/constructs/nowdocTest.js index 91eadd7..d25887b 100644 --- a/test/integration/transpiler/constructs/nowdocTest.js +++ b/test/integration/transpiler/constructs/nowdocTest.js @@ -25,13 +25,13 @@ describe('Transpiler "nowdoc" statement test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var createString = core.createString;' + 'return createString(' + '"my nowdoc with strings that $look like $interpolated variables"' + ');' + - '});' + '}' ); }); }); diff --git a/test/integration/transpiler/constructs/parentKeywordTest.js b/test/integration/transpiler/constructs/parentKeywordTest.js index 3e23a1f..392b4b6 100644 --- a/test/integration/transpiler/constructs/parentKeywordTest.js +++ b/test/integration/transpiler/constructs/parentKeywordTest.js @@ -31,11 +31,11 @@ describe('Transpiler parent:: construct expression test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var getStaticProperty = core.getStaticProperty, getSuperClassNameOrThrow = core.getSuperClassNameOrThrow;' + - 'return getStaticProperty(getSuperClassNameOrThrow())("myProp");' + - '});' + 'return getStaticProperty(getSuperClassNameOrThrow(), "myProp");' + + '}' ); }); @@ -61,8 +61,8 @@ describe('Transpiler parent:: construct expression test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var defineClass = core.defineClass, getClassConstant = core.getClassConstant, getSuperClassName = core.getSuperClassName;' + 'defineClass("MyClass", {' + 'superClass: null, ' + @@ -72,11 +72,11 @@ describe('Transpiler parent:: construct expression test', function () { 'methods: {}, ' + 'constants: {' + '"MY_CONST": function (currentClass) { ' + - 'return getClassConstant(getSuperClassName(currentClass))("PARENT_CONST"); ' + + 'return getClassConstant(getSuperClassName(currentClass), "PARENT_CONST"); ' + '}' + '}' + '});' + - '});' + '}' ); }); @@ -101,13 +101,13 @@ describe('Transpiler parent:: construct expression test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var callStaticMethod = core.callStaticMethod, getSuperClassNameOrThrow = core.getSuperClassNameOrThrow;' + - 'callStaticMethod(getSuperClassNameOrThrow())("myMethod")' + - '(true)' + // Forwarding. - '();' + - '});' + 'callStaticMethod(getSuperClassNameOrThrow(), "myMethod", ' + + 'true' + // Forwarding. + ');' + + '}' ); }); }); diff --git a/test/integration/transpiler/constructs/selfKeywordTest.js b/test/integration/transpiler/constructs/selfKeywordTest.js index 4197ae7..42c806f 100644 --- a/test/integration/transpiler/constructs/selfKeywordTest.js +++ b/test/integration/transpiler/constructs/selfKeywordTest.js @@ -31,11 +31,11 @@ describe('Transpiler self:: construct expression test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var getClassNameOrThrow = core.getClassNameOrThrow, getStaticProperty = core.getStaticProperty;' + - 'return getStaticProperty(getClassNameOrThrow())("myProp");' + - '});' + 'return getStaticProperty(getClassNameOrThrow(), "myProp");' + + '}' ); }); @@ -60,13 +60,13 @@ describe('Transpiler self:: construct expression test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var callStaticMethod = core.callStaticMethod, getClassNameOrThrow = core.getClassNameOrThrow;' + - 'callStaticMethod(getClassNameOrThrow())("myMethod")' + - '(true)' + // Forwarding. - '();' + - '});' + 'callStaticMethod(getClassNameOrThrow(), "myMethod", ' + + 'true' + // Forwarding. + ');' + + '}' ); }); diff --git a/test/integration/transpiler/constructs/staticKeywordTest.js b/test/integration/transpiler/constructs/staticKeywordTest.js index 1f9aa78..2b444c2 100644 --- a/test/integration/transpiler/constructs/staticKeywordTest.js +++ b/test/integration/transpiler/constructs/staticKeywordTest.js @@ -31,11 +31,11 @@ describe('Transpiler static:: construct expression test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var getStaticClassName = core.getStaticClassName, getStaticProperty = core.getStaticProperty;' + - 'return getStaticProperty(getStaticClassName())("myProp");' + - '});' + 'return getStaticProperty(getStaticClassName(), "myProp");' + + '}' ); }); @@ -60,13 +60,13 @@ describe('Transpiler static:: construct expression test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var callStaticMethod = core.callStaticMethod, getStaticClassName = core.getStaticClassName;' + - 'callStaticMethod(getStaticClassName())("myMethod")' + - '(true)' + // Forwarding. - '();' + - '});' + 'callStaticMethod(getStaticClassName(), "myMethod", ' + + 'true' + // Forwarding. + ');' + + '}' ); }); }); diff --git a/test/integration/transpiler/constructs/stringInterpolationTest.js b/test/integration/transpiler/constructs/stringInterpolationTest.js index 14d4e5a..9335ac1 100644 --- a/test/integration/transpiler/constructs/stringInterpolationTest.js +++ b/test/integration/transpiler/constructs/stringInterpolationTest.js @@ -35,11 +35,11 @@ describe('Transpiler string interpolation construct test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var getVariable = core.getVariable, interpolate = core.interpolate;' + 'return interpolate(["The num\\"ber is $", getVariable("myVar"), "."]);' + - '});' + '}' ); }); }); diff --git a/test/integration/transpiler/constructs/unsetTest.js b/test/integration/transpiler/constructs/unsetTest.js index 2877b54..015a65d 100644 --- a/test/integration/transpiler/constructs/unsetTest.js +++ b/test/integration/transpiler/constructs/unsetTest.js @@ -25,11 +25,11 @@ describe('Transpiler unset(...) construct expression test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var getVariable = core.getVariable, unset = core.unset;' + - 'unset(getVariable("a_var"))();' + - '});' + 'unset(getVariable("a_var"));' + + '}' ); }); @@ -48,11 +48,11 @@ describe('Transpiler unset(...) construct expression test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var getVariable = core.getVariable, unset = core.unset;' + - 'unset(getVariable("first_var"))(getVariable("second_var"))();' + - '});' + 'unset(getVariable("first_var"), getVariable("second_var"));' + + '}' ); }); @@ -75,11 +75,11 @@ describe('Transpiler unset(...) construct expression test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var getElement = core.getElement, getVariable = core.getVariable, unset = core.unset;' + - 'unset(getElement(getVariable("myArray"), 21))();' + - '});' + 'unset(getElement(getVariable("myArray"), 21));' + + '}' ); }); @@ -102,11 +102,11 @@ describe('Transpiler unset(...) construct expression test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var getInstanceProperty = core.getInstanceProperty, getVariable = core.getVariable, unset = core.unset;' + - 'unset(getInstanceProperty(getVariable("an_object"))("prop"))();' + - '});' + 'unset(getInstanceProperty(getVariable("an_object"), "prop"));' + + '}' ); }); }); diff --git a/test/integration/transpiler/constructs/variable/variableVariableTest.js b/test/integration/transpiler/constructs/variable/variableVariableTest.js index d03a1de..2633a2f 100644 --- a/test/integration/transpiler/constructs/variable/variableVariableTest.js +++ b/test/integration/transpiler/constructs/variable/variableVariableTest.js @@ -28,11 +28,11 @@ describe('Transpiler variable-variable construct test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var getVariable = core.getVariable, getVariableVariable = core.getVariableVariable;' + 'return getVariableVariable(getVariable("myVariableNameVar"));' + - '});' + '}' ); }); }); diff --git a/test/integration/transpiler/expressions/arrayLiteralTest.js b/test/integration/transpiler/expressions/arrayLiteralTest.js index 814750e..a1d7175 100644 --- a/test/integration/transpiler/expressions/arrayLiteralTest.js +++ b/test/integration/transpiler/expressions/arrayLiteralTest.js @@ -60,11 +60,90 @@ describe('Transpiler array literal expression test', function () { expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + 'var createArray = core.createArray, createInteger = core.createInteger, createReferenceElement = core.createReferenceElement, getVariable = core.getVariable;' + - 'return createArray' + - '(createInteger(21))' + - '(getVariable("myVarByVal"))' + - '(createReferenceElement(getVariable("myVarByRef")))' + - '();' + + 'return createArray(' + + 'createInteger(21), ' + + 'getVariable("myVarByVal"), ' + + 'createReferenceElement(getVariable("myVarByRef"))' + + ');' + + '}' + ); + }); + + it('should correctly transpile a return of array with immediate integer between two variables', function () { + var ast = { + name: 'N_PROGRAM', + statements: [{ + name: 'N_RETURN_STATEMENT', + expression: { + name: 'N_ARRAY_LITERAL', + elements: [{ + name: 'N_VARIABLE', + variable: 'myFirstVar' + }, { + name: 'N_INTEGER', + number: 21 + }, { + name: 'N_VARIABLE', + variable: 'mySecondVar' + }] + } + }] + }; + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + + 'var createArray = core.createArray, createInteger = core.createInteger, getVariable = core.getVariable;' + + 'return createArray(' + + // All elements are benign despite containing variables, so no snapshotting is needed. + 'getVariable("myFirstVar"), ' + + 'createInteger(21), ' + + 'getVariable("mySecondVar")' + + ');' + + '}' + ); + }); + + it('should correctly transpile a return of array with immediate integer between a variable and an assignment', function () { + var ast = { + name: 'N_PROGRAM', + statements: [{ + name: 'N_RETURN_STATEMENT', + expression: { + name: 'N_ARRAY_LITERAL', + elements: [{ + name: 'N_VARIABLE', + variable: 'myFirstVar' + }, { + name: 'N_INTEGER', + number: 21 + }, { + name: 'N_EXPRESSION', + left: { + name: 'N_VARIABLE', + variable: 'mySecondVar' + }, + right: [{ + operator: '=', + operand: { + name: 'N_INTEGER', + number: 101 + } + }] + }] + } + }] + }; + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + + 'var createArray = core.createArray, createInteger = core.createInteger, getVariable = core.getVariable, setValue = core.setValue, snapshot = core.snapshot;' + + 'return createArray(' + + // This non-literal element could be affected by the assignment below, + // so it must be snapshotted. + 'snapshot(getVariable("myFirstVar")), ' + + 'createInteger(21), ' + + 'setValue(getVariable("mySecondVar"), createInteger(101))' + + ');' + '}' ); }); @@ -132,14 +211,14 @@ describe('Transpiler array literal expression test', function () { expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + 'var createArray = core.createArray, createInteger = core.createInteger, createReferenceElement = core.createReferenceElement, getElement = core.getElement, getInstanceProperty = core.getInstanceProperty, getVariable = core.getVariable, setValue = core.setValue;' + - 'setValue(getVariable("myTarget"))(' + - 'createArray' + - '(createInteger(21))' + - '(getVariable("myVarByVal"))' + - '(createReferenceElement(getVariable("myVarByRef")))' + - '(createReferenceElement(getElement(getVariable("myArray"), "myElementByRef")))' + - '(createReferenceElement(getInstanceProperty(getVariable("myObject"))("myPropertyByRef")))' + - '()' + + 'setValue(getVariable("myTarget"), ' + + 'createArray(' + + 'createInteger(21), ' + + 'getVariable("myVarByVal"), ' + + 'createReferenceElement(getVariable("myVarByRef")), ' + + 'createReferenceElement(getElement(getVariable("myArray"), "myElementByRef")), ' + + 'createReferenceElement(getInstanceProperty(getVariable("myObject"), "myPropertyByRef"))' + + ')' + ');' + '}' ); @@ -183,10 +262,10 @@ describe('Transpiler array literal expression test', function () { expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + 'var createArray = core.createArray, createKeyReferencePair = core.createKeyReferencePair, createKeyValuePair = core.createKeyValuePair, createString = core.createString, getVariable = core.getVariable;' + - 'return createArray' + - '(createKeyValuePair(createString("myKeyForVal"))(getVariable("myVarByVal")))' + - '(createKeyReferencePair(createString("myKeyForRef"))(getVariable("myVarByRef")))' + - '();' + + 'return createArray(' + + 'createKeyValuePair(createString("myKeyForVal"), getVariable("myVarByVal")), ' + + 'createKeyReferencePair(createString("myKeyForRef"), getVariable("myVarByRef"))' + + ');' + '}' ); }); diff --git a/test/integration/transpiler/expressions/closure/closureTest.js b/test/integration/transpiler/expressions/closure/closureTest.js index dbde177..ff0edff 100644 --- a/test/integration/transpiler/expressions/closure/closureTest.js +++ b/test/integration/transpiler/expressions/closure/closureTest.js @@ -78,8 +78,8 @@ describe('Transpiler closure expression test', function () { 'var createClosure = core.createClosure, createInteger = core.createInteger, echo = core.echo, getReferenceBinding = core.getReferenceBinding, getValueBinding = core.getValueBinding, getVariable = core.getVariable, setReference = core.setReference, setValue = core.setValue;' + 'return createClosure(' + 'function () {' + - 'setValue(getVariable("bound1"))(getValueBinding("bound1"));' + - 'setReference(getVariable("bound2"))(getReferenceBinding("bound2"));' + + 'setValue(getVariable("bound1"), getValueBinding("bound1"));' + + 'setReference(getVariable("bound2"), getReferenceBinding("bound2"));' + 'echo(createInteger(21));' + '}, [' + '{"name":"arg1"},' + diff --git a/test/integration/transpiler/expressions/closure/returnTypeTest.js b/test/integration/transpiler/expressions/closure/returnTypeTest.js index 588e2f4..bc86a3b 100644 --- a/test/integration/transpiler/expressions/closure/returnTypeTest.js +++ b/test/integration/transpiler/expressions/closure/returnTypeTest.js @@ -81,8 +81,8 @@ describe('Transpiler closure expression return type test', function () { 'var createClosure = core.createClosure, createInteger = core.createInteger, echo = core.echo, getReferenceBinding = core.getReferenceBinding, getValueBinding = core.getValueBinding, getVariable = core.getVariable, setReference = core.setReference, setValue = core.setValue;' + 'return createClosure(' + 'function () {' + - 'setValue(getVariable("bound1"))(getValueBinding("bound1"));' + - 'setReference(getVariable("bound2"))(getReferenceBinding("bound2"));' + + 'setValue(getVariable("bound1"), getValueBinding("bound1"));' + + 'setReference(getVariable("bound2"), getReferenceBinding("bound2"));' + 'echo(createInteger(21));' + '}, [' + '{"name":"arg1"},' + diff --git a/test/integration/transpiler/expressions/functionCallTest.js b/test/integration/transpiler/expressions/functionCallTest.js index b44f53b..0ef5270 100644 --- a/test/integration/transpiler/expressions/functionCallTest.js +++ b/test/integration/transpiler/expressions/functionCallTest.js @@ -32,7 +32,7 @@ describe('Transpiler function call expression test', function () { expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + 'var callFunction = core.callFunction;' + - 'callFunction("myFunc")();' + + 'callFunction("myFunc");' + '}' ); }); @@ -65,11 +65,11 @@ describe('Transpiler function call expression test', function () { expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + 'var callFunction = core.callFunction, createFloat = core.createFloat, createInteger = core.createInteger, createString = core.createString;' + - 'callFunction("myFunc")' + - '(createString("My string"))' + - '(createInteger(21))' + - '(createFloat(101.4))' + - '();' + + 'callFunction("myFunc", ' + + 'createString("My string"), ' + + 'createInteger(21), ' + + 'createFloat(101.4)' + + ');' + '}' ); }); @@ -120,7 +120,7 @@ describe('Transpiler function call expression test', function () { }] }, { name: 'N_VARIABLE', - variable: 'myVarAsArg' + variable: 'myVarAsArgBeforeComplex' }, { name: 'N_TERNARY', condition: { @@ -135,6 +135,9 @@ describe('Transpiler function call expression test', function () { name: 'N_STRING', string: 'show me if falsy' } + }, { + name: 'N_VARIABLE', + variable: 'myVarAsArgAfterComplex' }] } }] @@ -142,26 +145,28 @@ describe('Transpiler function call expression test', function () { expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + - 'var callFunction = core.callFunction, createArray = core.createArray, createKeyValuePair = core.createKeyValuePair, createString = core.createString, getConstant = core.getConstant, getInstanceProperty = core.getInstanceProperty, getVariable = core.getVariable, ternary = core.ternary;' + - 'callFunction("myFunc")' + - '(createArray' + - '(createKeyValuePair(' + - 'createString("myVarElement"))' + - '(getVariable("myVarInNamedElement")' + - '))' + - '(createKeyValuePair(' + - 'createString("myPropertyElement"))' + - '(getInstanceProperty(getVariable("myObject"))("myProp")' + - '))' + - '(getVariable("myVarInIndexedElement"))' + - '())(' + - 'getVariable("myVarAsArg")' + - ')(' + + 'var callFunction = core.callFunction, createArray = core.createArray, createKeyValuePair = core.createKeyValuePair, createString = core.createString, getConstant = core.getConstant, getInstanceProperty = core.getInstanceProperty, getVariable = core.getVariable, snapshot = core.snapshot, ternary = core.ternary;' + + 'callFunction("myFunc", ' + + 'createArray(' + + 'createKeyValuePair(' + + 'createString("myVarElement"), ' + + 'getVariable("myVarInNamedElement")' + + '), ' + + 'createKeyValuePair(' + + 'createString("myPropertyElement"), ' + + 'getInstanceProperty(getVariable("myObject"), "myProp")' + + '), ' + + 'getVariable("myVarInIndexedElement")' + + '), ' + + // Plain variable argument must be snapshotted due to complex subsequent argument (ternary). + 'snapshot(getVariable("myVarAsArgBeforeComplex")), ' + '(ternary(getVariable("myVarAsCondition")) ? ' + 'getConstant("show me if truthy") : ' + 'getConstant("show me if falsy")' + - '))' + - '();' + + '), ' + + // Plain variable argument must be snapshotted as its parameter could be by-reference. + 'getVariable("myVarAsArgAfterComplex")' + + ');' + '}' ); }); diff --git a/test/integration/transpiler/expressions/includeOnceTest.js b/test/integration/transpiler/expressions/includeOnceTest.js index 2f651b2..2efef5d 100644 --- a/test/integration/transpiler/expressions/includeOnceTest.js +++ b/test/integration/transpiler/expressions/includeOnceTest.js @@ -13,7 +13,7 @@ var expect = require('chai').expect, phpToJS = require('../../../..'); describe('Transpiler "include_once" expression test', function () { - it('should correctly transpile in default (async) mode', function () { + it('should correctly transpile', function () { var ast = { name: 'N_PROGRAM', statements: [{ @@ -41,7 +41,7 @@ describe('Transpiler "include_once" expression test', function () { expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + 'var createString = core.createString, getVariable = core.getVariable, includeOnce = core.includeOnce, setValue = core.setValue;' + - 'setValue(getVariable("map"))(includeOnce(createString("abc.php")));' + + 'setValue(getVariable("map"), includeOnce(createString("abc.php")));' + '}' ); }); diff --git a/test/integration/transpiler/expressions/includeTest.js b/test/integration/transpiler/expressions/includeTest.js index 56c4661..d092379 100644 --- a/test/integration/transpiler/expressions/includeTest.js +++ b/test/integration/transpiler/expressions/includeTest.js @@ -13,7 +13,7 @@ var expect = require('chai').expect, phpToJS = require('../../../..'); describe('Transpiler "include" expression test', function () { - it('should correctly transpile in default (async) mode', function () { + it('should correctly transpile', function () { var ast = { name: 'N_PROGRAM', statements: [{ @@ -41,7 +41,7 @@ describe('Transpiler "include" expression test', function () { expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + 'var createString = core.createString, getVariable = core.getVariable, include = core.include, setValue = core.setValue;' + - 'setValue(getVariable("map"))(include(createString("abc.php")));' + + 'setValue(getVariable("map"), include(createString("abc.php")));' + '}' ); }); diff --git a/test/integration/transpiler/expressions/instanceMethodCallTest.js b/test/integration/transpiler/expressions/instanceMethodCallTest.js index c6d8cec..bd832c5 100644 --- a/test/integration/transpiler/expressions/instanceMethodCallTest.js +++ b/test/integration/transpiler/expressions/instanceMethodCallTest.js @@ -42,10 +42,106 @@ describe('Transpiler instance method call expression test', function () { expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + 'var callInstanceMethod = core.callInstanceMethod, getVariable = core.getVariable;' + - 'callInstanceMethod(getVariable("myObject"))("myMethod")' + - '(getVariable("firstVar"))' + - '(getVariable("secondVar"))' + - '();' + + 'callInstanceMethod(getVariable("myObject"), "myMethod", ' + + 'getVariable("firstVar"), ' + + 'getVariable("secondVar")' + + ');' + + '}' + ); + }); + + it('should correctly transpile a call having arguments with complex values', function () { + var ast = { + name: 'N_PROGRAM', + statements: [{ + name: 'N_EXPRESSION_STATEMENT', + expression: { + name: 'N_METHOD_CALL', + object: { + name: 'N_VARIABLE', + variable: 'myObject' + }, + method: { + name: 'N_STRING', + string: 'myMethod' + }, + args: [{ + name: 'N_ARRAY_LITERAL', + elements: [{ + name: 'N_KEY_VALUE_PAIR', + key: { + name: 'N_STRING_LITERAL', + string: 'myVarElement' + }, + value: { + name: 'N_VARIABLE', + variable: 'myVarInNamedElement' + } + }, { + name: 'N_KEY_VALUE_PAIR', + key: { + name: 'N_STRING_LITERAL', + string: 'myPropertyElement' + }, + value: { + name: 'N_OBJECT_PROPERTY', + object: { + name: 'N_VARIABLE', + variable: 'myObject' + }, + property: { + name: 'N_STRING', + string: 'myProp' + } + } + }, { + name: 'N_VARIABLE', + variable: 'myVarInIndexedElement' + }] + }, { + name: 'N_VARIABLE', + variable: 'myVarAsArg' + }, { + name: 'N_TERNARY', + condition: { + name: 'N_VARIABLE', + variable: 'myVarAsCondition' + }, + consequent: { + name: 'N_STRING_LITERAL', + string: 'show me if truthy' + }, + alternate: { + name: 'N_STRING_LITERAL', + string: 'show me if falsy' + } + }] + } + }] + }; + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + + 'var callInstanceMethod = core.callInstanceMethod, createArray = core.createArray, createKeyValuePair = core.createKeyValuePair, createString = core.createString, getInstanceProperty = core.getInstanceProperty, getVariable = core.getVariable, snapshot = core.snapshot, ternary = core.ternary;' + + // Plain variable object operand must be snapshotted due to complex subsequent operand (ternary argument). + 'callInstanceMethod(snapshot(getVariable("myObject")), "myMethod", ' + + 'createArray(' + + 'createKeyValuePair(' + + 'createString("myVarElement"), ' + + 'getVariable("myVarInNamedElement")' + + '), ' + + 'createKeyValuePair(' + + 'createString("myPropertyElement"), ' + + 'getInstanceProperty(getVariable("myObject"), "myProp")' + + '), ' + + 'getVariable("myVarInIndexedElement")' + + '), ' + + // Plain variable argument must be snapshotted due to complex subsequent argument (ternary). + 'snapshot(getVariable("myVarAsArg")), ' + + '(ternary(getVariable("myVarAsCondition")) ? ' + + 'createString("show me if truthy") : ' + + 'createString("show me if falsy")' + + '));' + '}' ); }); diff --git a/test/integration/transpiler/expressions/requireOnceTest.js b/test/integration/transpiler/expressions/requireOnceTest.js index db13605..0e197a3 100644 --- a/test/integration/transpiler/expressions/requireOnceTest.js +++ b/test/integration/transpiler/expressions/requireOnceTest.js @@ -13,7 +13,7 @@ var expect = require('chai').expect, phpToJS = require('../../../..'); describe('Transpiler "require_once" expression test', function () { - it('should correctly transpile in default (async) mode', function () { + it('should correctly transpile', function () { var ast = { name: 'N_PROGRAM', statements: [{ @@ -41,7 +41,7 @@ describe('Transpiler "require_once" expression test', function () { expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + 'var createString = core.createString, getVariable = core.getVariable, requireOnce = core.requireOnce, setValue = core.setValue;' + - 'setValue(getVariable("map"))(requireOnce(createString("abc.php")));' + + 'setValue(getVariable("map"), requireOnce(createString("abc.php")));' + '}' ); }); diff --git a/test/integration/transpiler/expressions/requireTest.js b/test/integration/transpiler/expressions/requireTest.js index d7558a3..c10baee 100644 --- a/test/integration/transpiler/expressions/requireTest.js +++ b/test/integration/transpiler/expressions/requireTest.js @@ -13,7 +13,7 @@ var expect = require('chai').expect, phpToJS = require('../../../..'); describe('Transpiler "require" expression test', function () { - it('should correctly transpile in default (async) mode', function () { + it('should correctly transpile', function () { var ast = { name: 'N_PROGRAM', statements: [{ @@ -41,7 +41,7 @@ describe('Transpiler "require" expression test', function () { expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + 'var createString = core.createString, getVariable = core.getVariable, require = core.require, setValue = core.setValue;' + - 'setValue(getVariable("map"))(require(createString("abc.php")));' + + 'setValue(getVariable("map"), require(createString("abc.php")));' + '}' ); }); diff --git a/test/integration/transpiler/expressions/staticMethodCallTest.js b/test/integration/transpiler/expressions/staticMethodCallTest.js index ef5f9c4..ee7d568 100644 --- a/test/integration/transpiler/expressions/staticMethodCallTest.js +++ b/test/integration/transpiler/expressions/staticMethodCallTest.js @@ -38,15 +38,15 @@ describe('Transpiler static method call expression test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var callStaticMethod = core.callStaticMethod, getSuperClassNameOrThrow = core.getSuperClassNameOrThrow, getVariable = core.getVariable;' + - 'callStaticMethod(getSuperClassNameOrThrow())("myMethod")' + - '(true)' + // Forwarding. - '(getVariable("myVar"))' + - '(getVariable("yourVar"))' + - '();' + - '});' + 'callStaticMethod(getSuperClassNameOrThrow(), "myMethod", ' + + 'true, ' + // Forwarding. + 'getVariable("myVar"), ' + + 'getVariable("yourVar")' + + ');' + + '}' ); }); @@ -76,15 +76,111 @@ describe('Transpiler static method call expression test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var callStaticMethod = core.callStaticMethod, createBareword = core.createBareword, getVariable = core.getVariable;' + - 'callStaticMethod(createBareword("\\\\My\\\\Space\\\\MyClass"))("myMethod")' + - '(false)' + // Non-forwarding. - '(getVariable("myVar"))' + - '(getVariable("yourVar"))' + - '();' + - '});' + 'callStaticMethod(createBareword("\\\\My\\\\Space\\\\MyClass"), "myMethod", ' + + 'false, ' + // Non-forwarding. + 'getVariable("myVar"), ' + + 'getVariable("yourVar")' + + ');' + + '}' + ); + }); + + it('should correctly transpile a call having arguments with complex values', function () { + var ast = { + name: 'N_PROGRAM', + statements: [{ + name: 'N_EXPRESSION_STATEMENT', + expression: { + name: 'N_STATIC_METHOD_CALL', + className: { + name: 'N_VARIABLE', + variable: 'myClassName' + }, + method: { + name: 'N_STRING', + string: 'myMethod' + }, + args: [{ + name: 'N_ARRAY_LITERAL', + elements: [{ + name: 'N_KEY_VALUE_PAIR', + key: { + name: 'N_STRING_LITERAL', + string: 'myVarElement' + }, + value: { + name: 'N_VARIABLE', + variable: 'myVarInNamedElement' + } + }, { + name: 'N_KEY_VALUE_PAIR', + key: { + name: 'N_STRING_LITERAL', + string: 'myPropertyElement' + }, + value: { + name: 'N_OBJECT_PROPERTY', + object: { + name: 'N_VARIABLE', + variable: 'myObject' + }, + property: { + name: 'N_STRING', + string: 'myProp' + } + } + }, { + name: 'N_VARIABLE', + variable: 'myVarInIndexedElement' + }] + }, { + name: 'N_VARIABLE', + variable: 'myVarAsArg' + }, { + name: 'N_TERNARY', + condition: { + name: 'N_VARIABLE', + variable: 'myVarAsCondition' + }, + consequent: { + name: 'N_STRING', + string: 'show me if truthy' + }, + alternate: { + name: 'N_STRING', + string: 'show me if falsy' + } + }] + } + }] + }; + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + + 'var callStaticMethod = core.callStaticMethod, createArray = core.createArray, createKeyValuePair = core.createKeyValuePair, createString = core.createString, getConstant = core.getConstant, getInstanceProperty = core.getInstanceProperty, getVariable = core.getVariable, snapshot = core.snapshot, ternary = core.ternary;' + + // Plain variable object operand must be snapshotted due to complex subsequent operand (ternary argument). + 'callStaticMethod(snapshot(getVariable("myClassName")), "myMethod", false, ' + + 'createArray(' + + 'createKeyValuePair(' + + 'createString("myVarElement"), ' + + 'getVariable("myVarInNamedElement")' + + '), ' + + 'createKeyValuePair(' + + 'createString("myPropertyElement"), ' + + 'getInstanceProperty(getVariable("myObject"), "myProp")' + + '), ' + + 'getVariable("myVarInIndexedElement")' + + '), ' + + // Plain variable argument must be snapshotted due to complex subsequent argument (ternary). + 'snapshot(getVariable("myVarAsArg")), ' + + '(ternary(getVariable("myVarAsCondition")) ? ' + + 'getConstant("show me if truthy") : ' + + 'getConstant("show me if falsy")' + + '));' + + '}' ); }); }); diff --git a/test/integration/transpiler/expressions/ternaryTest.js b/test/integration/transpiler/expressions/ternaryTest.js index de98a5f..bf37ff1 100644 --- a/test/integration/transpiler/expressions/ternaryTest.js +++ b/test/integration/transpiler/expressions/ternaryTest.js @@ -49,7 +49,7 @@ describe('Transpiler ternary expression test', function () { expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + 'var createInteger = core.createInteger, getVariable = core.getVariable, isEqual = core.isEqual, ternary = core.ternary;' + - '(ternary(isEqual(getVariable("myVar"))(createInteger(21))) ? ' + + '(ternary(isEqual(getVariable("myVar"), createInteger(21))) ? ' + 'createInteger(22) : ' + 'createInteger(23));' + '}' @@ -89,7 +89,7 @@ describe('Transpiler ternary expression test', function () { expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + 'var createInteger = core.createInteger, getVariable = core.getVariable, isEqual = core.isEqual, ternary = core.ternary, ternaryCondition;' + - '(ternary(ternaryCondition = isEqual(getVariable("myVar"))(createInteger(21))) ? ' + + '(ternary(ternaryCondition = isEqual(getVariable("myVar"), createInteger(21))) ? ' + 'ternaryCondition : ' + 'createInteger(23));' + '}' @@ -142,7 +142,7 @@ describe('Transpiler ternary expression test', function () { 'var createInteger = core.createInteger, defineFunction = core.defineFunction, getVariable = core.getVariable, isEqual = core.isEqual, ternary = core.ternary;' + 'defineFunction("myFunc", function _myFunc() {' + 'var ternaryCondition;' + - '(ternary(ternaryCondition = isEqual(getVariable("myVar"))(createInteger(21))) ? ' + + '(ternary(ternaryCondition = isEqual(getVariable("myVar"), createInteger(21))) ? ' + 'ternaryCondition : ' + 'createInteger(23));' + '});' + diff --git a/test/integration/transpiler/expressions/variable/variableFunctionCallTest.js b/test/integration/transpiler/expressions/variable/variableFunctionCallTest.js index 961f807..cc04e64 100644 --- a/test/integration/transpiler/expressions/variable/variableFunctionCallTest.js +++ b/test/integration/transpiler/expressions/variable/variableFunctionCallTest.js @@ -32,7 +32,7 @@ describe('Transpiler variable function call expression test', function () { expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + 'var callVariableFunction = core.callVariableFunction, getVariable = core.getVariable;' + - 'callVariableFunction(getVariable("myFuncNameVar"))();' + + 'callVariableFunction(getVariable("myFuncNameVar"));' + '}' ); }); @@ -65,11 +65,11 @@ describe('Transpiler variable function call expression test', function () { expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + 'var callVariableFunction = core.callVariableFunction, createFloat = core.createFloat, createInteger = core.createInteger, createString = core.createString, getVariable = core.getVariable;' + - 'callVariableFunction(getVariable("myFuncNameVar"))' + - '(createString("My string"))' + - '(createInteger(21))' + - '(createFloat(101.4))' + - '();' + + 'callVariableFunction(getVariable("myFuncNameVar"), ' + + 'createString("My string"), ' + + 'createInteger(21), ' + + 'createFloat(101.4)' + + ');' + '}' ); }); @@ -142,26 +142,27 @@ describe('Transpiler variable function call expression test', function () { expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + - 'var callVariableFunction = core.callVariableFunction, createArray = core.createArray, createKeyValuePair = core.createKeyValuePair, createString = core.createString, getConstant = core.getConstant, getInstanceProperty = core.getInstanceProperty, getVariable = core.getVariable, ternary = core.ternary;' + - 'callVariableFunction(getVariable("myFuncNameVar"))(' + - 'createArray' + - '(createKeyValuePair(' + - 'createString("myVarElement"))' + - '(getVariable("myVarInNamedElement")' + - '))' + - '(createKeyValuePair(' + - 'createString("myPropertyElement"))' + - '(getInstanceProperty(getVariable("myObject"))("myProp")' + - '))' + - '(getVariable("myVarInIndexedElement"))' + - '())(' + - 'getVariable("myVarAsArg")' + - ')(' + + 'var callVariableFunction = core.callVariableFunction, createArray = core.createArray, createKeyValuePair = core.createKeyValuePair, createString = core.createString, getConstant = core.getConstant, getInstanceProperty = core.getInstanceProperty, getVariable = core.getVariable, snapshot = core.snapshot, ternary = core.ternary;' + + // Function name variable must be snapshotted due to complex subsequent operand. + 'callVariableFunction(snapshot(getVariable("myFuncNameVar")), ' + + 'createArray(' + + 'createKeyValuePair(' + + 'createString("myVarElement"), ' + + 'getVariable("myVarInNamedElement")' + + '), ' + + 'createKeyValuePair(' + + 'createString("myPropertyElement"), ' + + 'getInstanceProperty(getVariable("myObject"), "myProp")' + + '), ' + + 'getVariable("myVarInIndexedElement")' + + '), ' + + // Plain variable argument must be snapshotted due to complex subsequent argument (ternary). + 'snapshot(getVariable("myVarAsArg")), ' + '(ternary(getVariable("myVarAsCondition")) ? ' + 'getConstant("show me if truthy") : ' + 'getConstant("show me if falsy")' + ')' + - ')();' + + ');' + '}' ); }); diff --git a/test/integration/transpiler/expressions/variable/variableInstanceMethodCallTest.js b/test/integration/transpiler/expressions/variable/variableInstanceMethodCallTest.js index af6d514..a1988fe 100644 --- a/test/integration/transpiler/expressions/variable/variableInstanceMethodCallTest.js +++ b/test/integration/transpiler/expressions/variable/variableInstanceMethodCallTest.js @@ -39,9 +39,106 @@ describe('Transpiler variable instance method call expression test', function () expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + 'var callVariableInstanceMethod = core.callVariableInstanceMethod, getVariable = core.getVariable;' + - 'callVariableInstanceMethod(getVariable("myObject"))(getVariable("myMethodName"))' + - '(getVariable("myVar"))' + - '();' + + 'callVariableInstanceMethod(getVariable("myObject"), getVariable("myMethodName"), ' + + 'getVariable("myVar")' + + ');' + + '}' + ); + }); + + it('should correctly transpile a call having arguments with complex values', function () { + var ast = { + name: 'N_PROGRAM', + statements: [{ + name: 'N_EXPRESSION_STATEMENT', + expression: { + name: 'N_METHOD_CALL', + object: { + name: 'N_VARIABLE', + variable: 'myObject' + }, + method: { + name: 'N_VARIABLE', + variable: 'myMethodName' + }, + args: [{ + name: 'N_ARRAY_LITERAL', + elements: [{ + name: 'N_KEY_VALUE_PAIR', + key: { + name: 'N_STRING_LITERAL', + string: 'myVarElement' + }, + value: { + name: 'N_VARIABLE', + variable: 'myVarInNamedElement' + } + }, { + name: 'N_KEY_VALUE_PAIR', + key: { + name: 'N_STRING_LITERAL', + string: 'myPropertyElement' + }, + value: { + name: 'N_OBJECT_PROPERTY', + object: { + name: 'N_VARIABLE', + variable: 'myObject' + }, + property: { + name: 'N_STRING', + string: 'myProp' + } + } + }, { + name: 'N_VARIABLE', + variable: 'myVarInIndexedElement' + }] + }, { + name: 'N_VARIABLE', + variable: 'myVarAsArg' + }, { + name: 'N_TERNARY', + condition: { + name: 'N_VARIABLE', + variable: 'myVarAsCondition' + }, + consequent: { + name: 'N_STRING', + string: 'show me if truthy' + }, + alternate: { + name: 'N_STRING', + string: 'show me if falsy' + } + }] + } + }] + }; + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + + 'var callVariableInstanceMethod = core.callVariableInstanceMethod, createArray = core.createArray, createKeyValuePair = core.createKeyValuePair, createString = core.createString, getConstant = core.getConstant, getInstanceProperty = core.getInstanceProperty, getVariable = core.getVariable, snapshot = core.snapshot, ternary = core.ternary;' + + // Object and method name variables must be snapshotted due to complex subsequent operand. + 'callVariableInstanceMethod(snapshot(getVariable("myObject")), snapshot(getVariable("myMethodName")), ' + + 'createArray(' + + 'createKeyValuePair(' + + 'createString("myVarElement"), ' + + 'getVariable("myVarInNamedElement")' + + '), ' + + 'createKeyValuePair(' + + 'createString("myPropertyElement"), ' + + 'getInstanceProperty(getVariable("myObject"), "myProp")' + + '), ' + + 'getVariable("myVarInIndexedElement")' + + '), ' + + // Plain variable argument must be snapshotted due to complex subsequent argument (ternary). + 'snapshot(getVariable("myVarAsArg")), ' + + '(ternary(getVariable("myVarAsCondition")) ? ' + + 'getConstant("show me if truthy") : ' + + 'getConstant("show me if falsy")' + + ')' + + ');' + '}' ); }); diff --git a/test/integration/transpiler/expressions/variable/variableStaticMethodCallTest.js b/test/integration/transpiler/expressions/variable/variableStaticMethodCallTest.js index efc1a4b..c7dfb24 100644 --- a/test/integration/transpiler/expressions/variable/variableStaticMethodCallTest.js +++ b/test/integration/transpiler/expressions/variable/variableStaticMethodCallTest.js @@ -38,15 +38,15 @@ describe('Transpiler variable static method call expression test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var callVariableStaticMethod = core.callVariableStaticMethod, getSuperClassNameOrThrow = core.getSuperClassNameOrThrow, getVariable = core.getVariable;' + - 'callVariableStaticMethod(getSuperClassNameOrThrow())(getVariable("myMethodNameVar"))' + - '(true)' + // Forwarding. - '(getVariable("myVar"))' + - '(getVariable("yourVar"))' + - '();' + - '});' + 'callVariableStaticMethod(getSuperClassNameOrThrow(), getVariable("myMethodNameVar"), ' + + 'true, ' + // Forwarding. + 'getVariable("myVar"), ' + + 'getVariable("yourVar")' + + ');' + + '}' ); }); @@ -76,15 +76,112 @@ describe('Transpiler variable static method call expression test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var callVariableStaticMethod = core.callVariableStaticMethod, createBareword = core.createBareword, getVariable = core.getVariable;' + - 'callVariableStaticMethod(createBareword("\\\\My\\\\Space\\\\MyClass"))(getVariable("myMethodNameVar"))' + - '(false)' + // Non-forwarding. - '(getVariable("myVar"))' + - '(getVariable("yourVar"))' + - '();' + - '});' + 'callVariableStaticMethod(createBareword("\\\\My\\\\Space\\\\MyClass"), getVariable("myMethodNameVar"), ' + + 'false, ' + // Non-forwarding. + 'getVariable("myVar"), ' + + 'getVariable("yourVar")' + + ');' + + '}' + ); + }); + + it('should correctly transpile a call having arguments with complex values', function () { + var ast = { + name: 'N_PROGRAM', + statements: [{ + name: 'N_EXPRESSION_STATEMENT', + expression: { + name: 'N_STATIC_METHOD_CALL', + className: { + name: 'N_VARIABLE', + variable: 'myClassName' + }, + method: { + name: 'N_VARIABLE', + variable: 'myMethodNameVar' + }, + args: [{ + name: 'N_ARRAY_LITERAL', + elements: [{ + name: 'N_KEY_VALUE_PAIR', + key: { + name: 'N_STRING_LITERAL', + string: 'myVarElement' + }, + value: { + name: 'N_VARIABLE', + variable: 'myVarInNamedElement' + } + }, { + name: 'N_KEY_VALUE_PAIR', + key: { + name: 'N_STRING_LITERAL', + string: 'myPropertyElement' + }, + value: { + name: 'N_OBJECT_PROPERTY', + object: { + name: 'N_VARIABLE', + variable: 'myObject' + }, + property: { + name: 'N_STRING', + string: 'myProp' + } + } + }, { + name: 'N_VARIABLE', + variable: 'myVarInIndexedElement' + }] + }, { + name: 'N_VARIABLE', + variable: 'myVarAsArg' + }, { + name: 'N_TERNARY', + condition: { + name: 'N_VARIABLE', + variable: 'myVarAsCondition' + }, + consequent: { + name: 'N_STRING', + string: 'show me if truthy' + }, + alternate: { + name: 'N_STRING', + string: 'show me if falsy' + } + }] + } + }] + }; + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + + 'var callVariableStaticMethod = core.callVariableStaticMethod, createArray = core.createArray, createKeyValuePair = core.createKeyValuePair, createString = core.createString, getConstant = core.getConstant, getInstanceProperty = core.getInstanceProperty, getVariable = core.getVariable, snapshot = core.snapshot, ternary = core.ternary;' + + // Class name and method name variables must be snapshotted due to complex subsequent operand. + 'callVariableStaticMethod(snapshot(getVariable("myClassName")), snapshot(getVariable("myMethodNameVar")), ' + + 'false, ' + + 'createArray(' + + 'createKeyValuePair(' + + 'createString("myVarElement"), ' + + 'getVariable("myVarInNamedElement")' + + '), ' + + 'createKeyValuePair(' + + 'createString("myPropertyElement"), ' + + 'getInstanceProperty(getVariable("myObject"), "myProp")' + + '), ' + + 'getVariable("myVarInIndexedElement")' + + '), ' + + // Plain variable argument must be snapshotted due to complex subsequent argument (ternary). + 'snapshot(getVariable("myVarAsArg")), ' + + '(ternary(getVariable("myVarAsCondition")) ? ' + + 'getConstant("show me if truthy") : ' + + 'getConstant("show me if falsy")' + + '));' + + '}' ); }); }); diff --git a/test/integration/transpiler/extensions/customStatementTest.js b/test/integration/transpiler/extensions/customStatementTest.js index 8306880..01b49e0 100644 --- a/test/integration/transpiler/extensions/customStatementTest.js +++ b/test/integration/transpiler/extensions/customStatementTest.js @@ -56,13 +56,13 @@ describe('Transpiler custom statement test', function () { } }; - expect(phpToJS.transpile(ast, {}, options)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true}, options)).to.equal( + 'function (core) {' + 'var callFunction = core.callFunction, createInteger = core.createInteger, getNative = core.getNative, printRaw = core.printRaw;' + - 'callFunction("firstFunc")();' + + 'callFunction("firstFunc");' + 'printRaw("Trapped: " + getNative(createInteger(21)));' + - 'callFunction("secondFunc")();' + - '});' + 'callFunction("secondFunc");' + + '}' ); }); }); diff --git a/test/integration/transpiler/lineNumberTest.js b/test/integration/transpiler/lineNumberTest.js index 711094f..7d84ce6 100644 --- a/test/integration/transpiler/lineNumberTest.js +++ b/test/integration/transpiler/lineNumberTest.js @@ -43,16 +43,17 @@ describe('Transpiler line numbers test', function () { } }, options = { + bare: true, path: 'my_module.php', lineNumbers: true }; expect(phpToJS.transpile(ast, options)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + 'function (core) {' + 'var createInteger = core.createInteger, instrument = core.instrument, line;' + 'instrument(function () {return line;});' + 'line = 6;return (line = 8, createInteger(4));' + - '});' + '}' ); }); @@ -96,17 +97,18 @@ describe('Transpiler line numbers test', function () { } }, options = { + bare: true, path: 'my_module.php', lineNumbers: true }; expect(phpToJS.transpile(ast, options)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + 'function (core) {' + 'var createInteger = core.createInteger, instrument = core.instrument, line, useGlobalNamespaceScope = core.useGlobalNamespaceScope;' + 'instrument(function () {return line;});' + 'line = 4;useGlobalNamespaceScope();' + 'line = 6;return (line = 8, createInteger(101));' + - '});' + '}' ); }); @@ -168,18 +170,19 @@ describe('Transpiler line numbers test', function () { } }, options = { + bare: true, path: 'my_module.php', lineNumbers: true }; expect(phpToJS.transpile(ast, options)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + 'function (core) {' + 'var echo = core.echo, getVariable = core.getVariable, instrument = core.instrument, line, loop = core.loop;' + 'instrument(function () {return line;});' + 'line = 6;block_1: while (loop(0, (line = 8, getVariable("myCond")))) {' + 'line = 9;echo((line = 9, getVariable("myVar")));' + '}' + - '});' + '}' ); }); @@ -476,12 +479,13 @@ describe('Transpiler line numbers test', function () { } }, options = { + bare: true, path: 'my_module.php', lineNumbers: true }; expect(phpToJS.transpile(ast, options)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + 'function (core) {' + 'var createClosure = core.createClosure, defineClass = core.defineClass, defineFunction = core.defineFunction, defineInterface = core.defineInterface, getConstant = core.getConstant, getValueBinding = core.getValueBinding, getVariable = core.getVariable, instrument = core.instrument, line, setValue = core.setValue;' + 'instrument(function () {return line;});' + 'line = 3;defineFunction("myFunc", function _myFunc() {' + @@ -514,12 +518,12 @@ describe('Transpiler line numbers test', function () { 'line = 8;return (line = 12, createClosure(function () {' + 'var line;' + 'instrument(function () {return line;});' + - 'setValue(getVariable("myBoundVar"))(getValueBinding("myBoundVar"));' + + 'setValue(getVariable("myBoundVar"), getValueBinding("myBoundVar"));' + 'line = 8;return (line = 11, getVariable("myClosureVar"));' + '}, [' + '{"name":"myArgVar"}' + '], [{"name":"myBoundVar"}], false, null, 12));' + - '});' + '}' ); }); diff --git a/test/integration/transpiler/modeOptionTest.js b/test/integration/transpiler/modeOptionTest.js index 0e0a752..a81f0c4 100644 --- a/test/integration/transpiler/modeOptionTest.js +++ b/test/integration/transpiler/modeOptionTest.js @@ -65,11 +65,11 @@ describe('Transpiler "mode" option test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var createString = core.createString;' + 'return createString("my result");' + - '});' + '}' ); }); diff --git a/test/integration/transpiler/newTest.js b/test/integration/transpiler/newTest.js deleted file mode 100644 index c38b13e..0000000 --- a/test/integration/transpiler/newTest.js +++ /dev/null @@ -1,80 +0,0 @@ -/* - * PHPToJS - PHP-to-JavaScript transpiler - * Copyright (c) Dan Phillimore (asmblah) - * https://github.com/uniter/phptojs - * - * Released under the MIT license - * https://github.com/uniter/phptojs/raw/master/MIT-LICENSE.txt - */ - -'use strict'; - -var expect = require('chai').expect, - phpToJS = require('../../..'); - -describe('Transpiler new expression test', function () { - it('should correctly transpile an instantiation with no constructor arguments', function () { - var ast = { - name: 'N_PROGRAM', - statements: [{ - name: 'N_EXPRESSION_STATEMENT', - expression: { - name: 'N_EXPRESSION', - left: { - name: 'N_VARIABLE', - variable: 'object' - }, - right: [{ - operator: '=', - operand: { - name: 'N_NEW_EXPRESSION', - className: { - name: 'N_STRING', - string: 'Worker' - } - } - }] - } - }] - }; - - expect(phpToJS.transpile(ast, {bare: true})).to.equal( - 'function (core) {' + - 'var createBareword = core.createBareword, createInstance = core.createInstance, getVariable = core.getVariable, setValue = core.setValue;' + - 'setValue(getVariable("object"))(createInstance(createBareword("Worker"))());' + - '}' - ); - }); - - it('should correctly transpile a new expression in function call argument', function () { - var ast = { - name: 'N_PROGRAM', - statements: [{ - name: 'N_EXPRESSION_STATEMENT', - expression: { - name: 'N_FUNCTION_CALL', - func: { - name: 'N_STRING', - string: 'myFunc' - }, - args: [{ - name: 'N_NEW_EXPRESSION', - className: { - name: 'N_VARIABLE', - variable: 'myClassName' - } - }] - } - }] - }; - - expect(phpToJS.transpile(ast, {bare: true})).to.equal( - 'function (core) {' + - 'var callFunction = core.callFunction, createInstance = core.createInstance, getVariable = core.getVariable;' + - 'callFunction("myFunc")(' + - 'createInstance(getVariable("myClassName"))()' + - ')();' + - '}' - ); - }); -}); diff --git a/test/integration/transpiler/operators/arithmetic/additionOperatorTest.js b/test/integration/transpiler/operators/arithmetic/additionOperatorTest.js index cc7055f..3c5ba12 100644 --- a/test/integration/transpiler/operators/arithmetic/additionOperatorTest.js +++ b/test/integration/transpiler/operators/arithmetic/additionOperatorTest.js @@ -35,11 +35,58 @@ describe('Transpiler addition arithmetic operator "+" test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var add = core.add, createInteger = core.createInteger;' + - 'return add(createInteger(21))(createInteger(10));' + - '});' + 'return add(createInteger(21), createInteger(10));' + + '}' + ); + }); + + it('should correctly transpile a return with an operation between a variable and complex expression', function () { + var ast = { + name: 'N_PROGRAM', + statements: [{ + name: 'N_RETURN_STATEMENT', + expression: { + name: 'N_EXPRESSION', + left: { + name: 'N_VARIABLE', + variable: 'myLeftVar' + }, + right: [{ + operator: '+', + operand: { + name: 'N_TERNARY', + condition: { + name: 'N_VARIABLE', + variable: 'myCondition' + }, + consequent: { + name: 'N_STRING_LITERAL', + string: 'myRightStringIfTruthy' + }, + alternate: { + name: 'N_STRING_LITERAL', + string: 'myRightStringIfFalsy' + } + } + }] + } + }] + }; + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + + 'var add = core.add, createString = core.createString, getVariable = core.getVariable, snapshot = core.snapshot, ternary = core.ternary;' + + 'return add(' + + // Plain variable object operand must be snapshotted due to complex subsequent operand (ternary). + 'snapshot(getVariable("myLeftVar")), ' + + '(ternary(getVariable("myCondition")) ? ' + + 'createString("myRightStringIfTruthy") : ' + + 'createString("myRightStringIfFalsy")' + + '));' + + '}' ); }); }); diff --git a/test/integration/transpiler/operators/arithmetic/divisionOperatorTest.js b/test/integration/transpiler/operators/arithmetic/divisionOperatorTest.js index ff48c4c..e201288 100644 --- a/test/integration/transpiler/operators/arithmetic/divisionOperatorTest.js +++ b/test/integration/transpiler/operators/arithmetic/divisionOperatorTest.js @@ -35,11 +35,58 @@ describe('Transpiler division arithmetic operator "/" test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var createInteger = core.createInteger, divide = core.divide;' + - 'return divide(createInteger(21))(createInteger(10));' + - '});' + 'return divide(createInteger(21), createInteger(10));' + + '}' + ); + }); + + it('should correctly transpile a return with an operation between a variable and complex expression', function () { + var ast = { + name: 'N_PROGRAM', + statements: [{ + name: 'N_RETURN_STATEMENT', + expression: { + name: 'N_EXPRESSION', + left: { + name: 'N_VARIABLE', + variable: 'myLeftVar' + }, + right: [{ + operator: '/', + operand: { + name: 'N_TERNARY', + condition: { + name: 'N_VARIABLE', + variable: 'myCondition' + }, + consequent: { + name: 'N_STRING_LITERAL', + string: 'myRightStringIfTruthy' + }, + alternate: { + name: 'N_STRING_LITERAL', + string: 'myRightStringIfFalsy' + } + } + }] + } + }] + }; + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + + 'var createString = core.createString, divide = core.divide, getVariable = core.getVariable, snapshot = core.snapshot, ternary = core.ternary;' + + 'return divide(' + + // Plain variable object operand must be snapshotted due to complex subsequent operand (ternary). + 'snapshot(getVariable("myLeftVar")), ' + + '(ternary(getVariable("myCondition")) ? ' + + 'createString("myRightStringIfTruthy") : ' + + 'createString("myRightStringIfFalsy")' + + '));' + + '}' ); }); }); diff --git a/test/integration/transpiler/operators/arithmetic/identityOperatorTest.js b/test/integration/transpiler/operators/arithmetic/identityOperatorTest.js index f129c67..9533ac2 100644 --- a/test/integration/transpiler/operators/arithmetic/identityOperatorTest.js +++ b/test/integration/transpiler/operators/arithmetic/identityOperatorTest.js @@ -30,11 +30,11 @@ describe('Transpiler identity arithmetic operator "+" test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var getVariable = core.getVariable, identity = core.identity;' + 'return identity(getVariable("myVar"));' + - '});' + '}' ); }); }); diff --git a/test/integration/transpiler/operators/arithmetic/moduloOperatorTest.js b/test/integration/transpiler/operators/arithmetic/moduloOperatorTest.js index 3798bad..31d072b 100644 --- a/test/integration/transpiler/operators/arithmetic/moduloOperatorTest.js +++ b/test/integration/transpiler/operators/arithmetic/moduloOperatorTest.js @@ -35,11 +35,58 @@ describe('Transpiler modulo arithmetic operator "%" test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var createInteger = core.createInteger, modulo = core.modulo;' + - 'return modulo(createInteger(21))(createInteger(10));' + - '});' + 'return modulo(createInteger(21), createInteger(10));' + + '}' + ); + }); + + it('should correctly transpile a return with an operation between a variable and complex expression', function () { + var ast = { + name: 'N_PROGRAM', + statements: [{ + name: 'N_RETURN_STATEMENT', + expression: { + name: 'N_EXPRESSION', + left: { + name: 'N_VARIABLE', + variable: 'myLeftVar' + }, + right: [{ + operator: '%', + operand: { + name: 'N_TERNARY', + condition: { + name: 'N_VARIABLE', + variable: 'myCondition' + }, + consequent: { + name: 'N_STRING_LITERAL', + string: 'myRightStringIfTruthy' + }, + alternate: { + name: 'N_STRING_LITERAL', + string: 'myRightStringIfFalsy' + } + } + }] + } + }] + }; + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + + 'var createString = core.createString, getVariable = core.getVariable, modulo = core.modulo, snapshot = core.snapshot, ternary = core.ternary;' + + 'return modulo(' + + // Plain variable object operand must be snapshotted due to complex subsequent operand (ternary). + 'snapshot(getVariable("myLeftVar")), ' + + '(ternary(getVariable("myCondition")) ? ' + + 'createString("myRightStringIfTruthy") : ' + + 'createString("myRightStringIfFalsy")' + + '));' + + '}' ); }); }); diff --git a/test/integration/transpiler/operators/arithmetic/multiplicationOperatorTest.js b/test/integration/transpiler/operators/arithmetic/multiplicationOperatorTest.js index a3f76d2..6282ea8 100644 --- a/test/integration/transpiler/operators/arithmetic/multiplicationOperatorTest.js +++ b/test/integration/transpiler/operators/arithmetic/multiplicationOperatorTest.js @@ -35,11 +35,58 @@ describe('Transpiler multiplication arithmetic operator "*" test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var createInteger = core.createInteger, multiply = core.multiply;' + - 'return multiply(createInteger(21))(createInteger(10));' + - '});' + 'return multiply(createInteger(21), createInteger(10));' + + '}' + ); + }); + + it('should correctly transpile a return with an operation between a variable and complex expression', function () { + var ast = { + name: 'N_PROGRAM', + statements: [{ + name: 'N_RETURN_STATEMENT', + expression: { + name: 'N_EXPRESSION', + left: { + name: 'N_VARIABLE', + variable: 'myLeftVar' + }, + right: [{ + operator: '*', + operand: { + name: 'N_TERNARY', + condition: { + name: 'N_VARIABLE', + variable: 'myCondition' + }, + consequent: { + name: 'N_STRING_LITERAL', + string: 'myRightStringIfTruthy' + }, + alternate: { + name: 'N_STRING_LITERAL', + string: 'myRightStringIfFalsy' + } + } + }] + } + }] + }; + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + + 'var createString = core.createString, getVariable = core.getVariable, multiply = core.multiply, snapshot = core.snapshot, ternary = core.ternary;' + + 'return multiply(' + + // Plain variable object operand must be snapshotted due to complex subsequent operand (ternary). + 'snapshot(getVariable("myLeftVar")), ' + + '(ternary(getVariable("myCondition")) ? ' + + 'createString("myRightStringIfTruthy") : ' + + 'createString("myRightStringIfFalsy")' + + '));' + + '}' ); }); }); diff --git a/test/integration/transpiler/operators/arithmetic/negationOperatorTest.js b/test/integration/transpiler/operators/arithmetic/negationOperatorTest.js index 6bded7f..de27bbf 100644 --- a/test/integration/transpiler/operators/arithmetic/negationOperatorTest.js +++ b/test/integration/transpiler/operators/arithmetic/negationOperatorTest.js @@ -30,11 +30,11 @@ describe('Transpiler negation arithmetic operator "-" test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var getVariable = core.getVariable, negate = core.negate;' + 'return negate(getVariable("myVar"));' + - '});' + '}' ); }); }); diff --git a/test/integration/transpiler/operators/arithmetic/subtractionOperatorTest.js b/test/integration/transpiler/operators/arithmetic/subtractionOperatorTest.js index 57c41b7..147a368 100644 --- a/test/integration/transpiler/operators/arithmetic/subtractionOperatorTest.js +++ b/test/integration/transpiler/operators/arithmetic/subtractionOperatorTest.js @@ -35,11 +35,58 @@ describe('Transpiler subtraction arithmetic operator "-" test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var createInteger = core.createInteger, subtract = core.subtract;' + - 'return subtract(createInteger(21))(createInteger(10));' + - '});' + 'return subtract(createInteger(21), createInteger(10));' + + '}' + ); + }); + + it('should correctly transpile a return with an operation between a variable and complex expression', function () { + var ast = { + name: 'N_PROGRAM', + statements: [{ + name: 'N_RETURN_STATEMENT', + expression: { + name: 'N_EXPRESSION', + left: { + name: 'N_VARIABLE', + variable: 'myLeftVar' + }, + right: [{ + operator: '-', + operand: { + name: 'N_TERNARY', + condition: { + name: 'N_VARIABLE', + variable: 'myCondition' + }, + consequent: { + name: 'N_STRING_LITERAL', + string: 'myRightStringIfTruthy' + }, + alternate: { + name: 'N_STRING_LITERAL', + string: 'myRightStringIfFalsy' + } + } + }] + } + }] + }; + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + + 'var createString = core.createString, getVariable = core.getVariable, snapshot = core.snapshot, subtract = core.subtract, ternary = core.ternary;' + + 'return subtract(' + + // Plain variable object operand must be snapshotted due to complex subsequent operand (ternary). + 'snapshot(getVariable("myLeftVar")), ' + + '(ternary(getVariable("myCondition")) ? ' + + 'createString("myRightStringIfTruthy") : ' + + 'createString("myRightStringIfFalsy")' + + '));' + + '}' ); }); }); diff --git a/test/integration/transpiler/operators/arrayAccessTest.js b/test/integration/transpiler/operators/arrayAccessTest.js index a2eef6a..3b462e8 100644 --- a/test/integration/transpiler/operators/arrayAccessTest.js +++ b/test/integration/transpiler/operators/arrayAccessTest.js @@ -32,11 +32,11 @@ describe('Transpiler array access operator test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var getElement = core.getElement, getVariable = core.getVariable;' + 'return getElement(getVariable("myArray"), 21);' + - '});' + '}' ); }); @@ -59,11 +59,11 @@ describe('Transpiler array access operator test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var getElement = core.getElement, getVariable = core.getVariable;' + 'return getElement(getVariable("myArray"), "myKey");' + - '});' + '}' ); }); @@ -86,11 +86,55 @@ describe('Transpiler array access operator test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var getVariable = core.getVariable, getVariableElement = core.getVariableElement;' + - 'return getVariableElement(getVariable("myArray"))(getVariable("myKeyVar"));' + - '});' + 'return getVariableElement(getVariable("myArray"), getVariable("myKeyVar"));' + + '}' + ); + }); + + it('should correctly transpile a return statement with a complex expression as array index', function () { + var ast = { + name: 'N_PROGRAM', + statements: [{ + name: 'N_RETURN_STATEMENT', + expression: { + name: 'N_ARRAY_INDEX', + array: { + name: 'N_VARIABLE', + variable: 'myArray' + }, + index: { + name: 'N_TERNARY', + condition: { + name: 'N_VARIABLE', + variable: 'myCondition' + }, + consequent: { + name: 'N_STRING_LITERAL', + string: 'myKeyIfTruthy' + }, + alternate: { + name: 'N_STRING_LITERAL', + string: 'myKeyIfFalsy' + } + } + } + }] + }; + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + + 'var createString = core.createString, getVariable = core.getVariable, getVariableElement = core.getVariableElement, snapshot = core.snapshot, ternary = core.ternary;' + + 'return getVariableElement(' + + // Plain variable array/object operand must be snapshotted due to complex subsequent operand (ternary). + 'snapshot(getVariable("myArray")), ' + + '(ternary(getVariable("myCondition")) ? ' + + 'createString("myKeyIfTruthy") : ' + + 'createString("myKeyIfFalsy")' + + '));' + + '}' ); }); @@ -120,11 +164,11 @@ describe('Transpiler array access operator test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var getElement = core.getElement, getVariable = core.getVariable;' + 'return getElement(getElement(getVariable("myArray"), 21), 101);' + - '});' + '}' ); }); @@ -157,11 +201,11 @@ describe('Transpiler array access operator test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var createInteger = core.createInteger, getElement = core.getElement, getVariable = core.getVariable, setValue = core.setValue;' + - 'setValue(getElement(getVariable("myArray"), 21))(createInteger(5));' + - '});' + 'setValue(getElement(getVariable("myArray"), 21), createInteger(5));' + + '}' ); }); @@ -201,11 +245,11 @@ describe('Transpiler array access operator test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var createInteger = core.createInteger, getElement = core.getElement, getVariable = core.getVariable, setValue = core.setValue;' + - 'setValue(getElement(getElement(getVariable("myArray"), 21), 24))(createInteger(5));' + - '});' + 'setValue(getElement(getElement(getVariable("myArray"), 21), 24), createInteger(5));' + + '}' ); }); @@ -222,7 +266,7 @@ describe('Transpiler array access operator test', function () { name: 'N_VARIABLE', variable: 'myArray' }, - index: null // Indicates a push + index: null // Indicates a push. }, right: [{ operator: '=', @@ -235,11 +279,11 @@ describe('Transpiler array access operator test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var createInteger = core.createInteger, getVariable = core.getVariable, pushElement = core.pushElement, setValue = core.setValue;' + - 'setValue(pushElement(getVariable("myArray")))(createInteger(21));' + - '});' + 'setValue(pushElement(getVariable("myArray")), createInteger(21));' + + '}' ); }); @@ -256,7 +300,7 @@ describe('Transpiler array access operator test', function () { name: 'N_VARIABLE', variable: 'myArray' }, - index: null // Indicates a push + index: null // Indicates a push. }, right: [{ operator: '=', @@ -276,11 +320,11 @@ describe('Transpiler array access operator test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var getInstanceProperty = core.getInstanceProperty, getVariable = core.getVariable, pushElement = core.pushElement, setValue = core.setValue;' + - 'setValue(pushElement(getVariable("myArray")))(getInstanceProperty(getVariable("myTarget"))("myProp"));' + - '});' + 'setValue(pushElement(getVariable("myArray")), getInstanceProperty(getVariable("myTarget"), "myProp"));' + + '}' ); }); @@ -297,7 +341,7 @@ describe('Transpiler array access operator test', function () { name: 'N_VARIABLE', variable: 'myArray' }, - index: null // Indicates a push + index: null // Indicates a push. }, right: [{ operator: '=', @@ -313,11 +357,62 @@ describe('Transpiler array access operator test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var getVariable = core.getVariable, pushElement = core.pushElement, setReference = core.setReference;' + - 'setReference(pushElement(getVariable("myArray")))(getVariable("myTarget"));' + - '});' + 'setReference(pushElement(getVariable("myArray")), getVariable("myTarget"));' + + '}' + ); + }); + + it('should correctly transpile a push of complex expression', function () { + var ast = { + name: 'N_PROGRAM', + statements: [{ + name: 'N_EXPRESSION_STATEMENT', + expression: { + name: 'N_EXPRESSION', + left: { + name: 'N_ARRAY_INDEX', + array: { + name: 'N_VARIABLE', + variable: 'myArray' + }, + index: null // Indicates a push. + }, + right: [{ + operator: '=', + operand: { + name: 'N_TERNARY', + condition: { + name: 'N_VARIABLE', + variable: 'myCondition' + }, + consequent: { + name: 'N_STRING_LITERAL', + string: 'myRightStringIfTruthy' + }, + alternate: { + name: 'N_STRING_LITERAL', + string: 'myRightStringIfFalsy' + } + } + }] + } + }] + }; + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + + 'var createString = core.createString, getVariable = core.getVariable, pushElement = core.pushElement, setValue = core.setValue, ternary = core.ternary;' + + 'setValue(' + + // No snapshotting is required as push element is resolved first. + 'pushElement(getVariable("myArray")), ' + + '(ternary(getVariable("myCondition")) ? ' + + 'createString("myRightStringIfTruthy") : ' + + 'createString("myRightStringIfFalsy")' + + '));' + + '}' ); }); }); diff --git a/test/integration/transpiler/operators/assignment/additionAssignmentTest.js b/test/integration/transpiler/operators/assignment/additionAssignmentTest.js index 8f4430c..d989f5f 100644 --- a/test/integration/transpiler/operators/assignment/additionAssignmentTest.js +++ b/test/integration/transpiler/operators/assignment/additionAssignmentTest.js @@ -35,11 +35,11 @@ describe('Transpiler addition assignment operator "+=" test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var createInteger = core.createInteger, getVariable = core.getVariable, incrementBy = core.incrementBy;' + - 'return incrementBy(getVariable("myVar"))(createInteger(21));' + - '});' + 'return incrementBy(getVariable("myVar"), createInteger(21));' + + '}' ); }); }); diff --git a/test/integration/transpiler/operators/assignment/assignmentTest.js b/test/integration/transpiler/operators/assignment/assignmentTest.js index cb470de..32e7da7 100644 --- a/test/integration/transpiler/operators/assignment/assignmentTest.js +++ b/test/integration/transpiler/operators/assignment/assignmentTest.js @@ -35,11 +35,11 @@ describe('Transpiler reference assignment operator "=" test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var getVariable = core.getVariable, setValue = core.setValue;' + - 'return setValue(getVariable("myVar"))(getVariable("anotherVar"));' + - '});' + 'return setValue(getVariable("myVar"), getVariable("anotherVar"));' + + '}' ); }); }); diff --git a/test/integration/transpiler/operators/assignment/bitwiseAndAssignmentTest.js b/test/integration/transpiler/operators/assignment/bitwiseAndAssignmentTest.js index 66fcd3f..fd37b2c 100644 --- a/test/integration/transpiler/operators/assignment/bitwiseAndAssignmentTest.js +++ b/test/integration/transpiler/operators/assignment/bitwiseAndAssignmentTest.js @@ -35,11 +35,11 @@ describe('Transpiler bitwise AND assignment operator "&=" test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var bitwiseAndWith = core.bitwiseAndWith, createInteger = core.createInteger, getVariable = core.getVariable;' + - 'return bitwiseAndWith(getVariable("myVar"))(createInteger(27));' + - '});' + 'return bitwiseAndWith(getVariable("myVar"), createInteger(27));' + + '}' ); }); }); diff --git a/test/integration/transpiler/operators/assignment/bitwiseLeftShiftAssignmentTest.js b/test/integration/transpiler/operators/assignment/bitwiseLeftShiftAssignmentTest.js index 98039aa..b02c525 100644 --- a/test/integration/transpiler/operators/assignment/bitwiseLeftShiftAssignmentTest.js +++ b/test/integration/transpiler/operators/assignment/bitwiseLeftShiftAssignmentTest.js @@ -35,11 +35,11 @@ describe('Transpiler bitwise left-shift assignment operator "<<=" test', functio }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var createInteger = core.createInteger, getVariable = core.getVariable, shiftLeftBy = core.shiftLeftBy;' + - 'return shiftLeftBy(getVariable("myVar"))(createInteger(12));' + - '});' + 'return shiftLeftBy(getVariable("myVar"), createInteger(12));' + + '}' ); }); }); diff --git a/test/integration/transpiler/operators/assignment/bitwiseOrAssignmentTest.js b/test/integration/transpiler/operators/assignment/bitwiseOrAssignmentTest.js index 4e0e47e..6ad422a 100644 --- a/test/integration/transpiler/operators/assignment/bitwiseOrAssignmentTest.js +++ b/test/integration/transpiler/operators/assignment/bitwiseOrAssignmentTest.js @@ -35,11 +35,11 @@ describe('Transpiler bitwise OR assignment operator "|=" test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var bitwiseOrWith = core.bitwiseOrWith, createInteger = core.createInteger, getVariable = core.getVariable;' + - 'return bitwiseOrWith(getVariable("myVar"))(createInteger(27));' + - '});' + 'return bitwiseOrWith(getVariable("myVar"), createInteger(27));' + + '}' ); }); }); diff --git a/test/integration/transpiler/operators/assignment/bitwiseRightShiftAssignmentTest.js b/test/integration/transpiler/operators/assignment/bitwiseRightShiftAssignmentTest.js index 45a98a0..fd363f0 100644 --- a/test/integration/transpiler/operators/assignment/bitwiseRightShiftAssignmentTest.js +++ b/test/integration/transpiler/operators/assignment/bitwiseRightShiftAssignmentTest.js @@ -38,7 +38,7 @@ describe('Transpiler bitwise right-shift assignment operator ">>=" test', functi expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + 'var createInteger = core.createInteger, getVariable = core.getVariable, shiftRightBy = core.shiftRightBy;' + - 'return shiftRightBy(getVariable("myVar"))(createInteger(12));' + + 'return shiftRightBy(getVariable("myVar"), createInteger(12));' + '}' ); }); diff --git a/test/integration/transpiler/operators/assignment/bitwiseXorAssignmentTest.js b/test/integration/transpiler/operators/assignment/bitwiseXorAssignmentTest.js index 2452790..3fb44f4 100644 --- a/test/integration/transpiler/operators/assignment/bitwiseXorAssignmentTest.js +++ b/test/integration/transpiler/operators/assignment/bitwiseXorAssignmentTest.js @@ -35,11 +35,11 @@ describe('Transpiler bitwise XOR assignment operator "^=" test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var bitwiseXorWith = core.bitwiseXorWith, createInteger = core.createInteger, getVariable = core.getVariable;' + - 'return bitwiseXorWith(getVariable("myVar"))(createInteger(27));' + - '});' + 'return bitwiseXorWith(getVariable("myVar"), createInteger(27));' + + '}' ); }); }); diff --git a/test/integration/transpiler/operators/assignment/concatenationAssignmentTest.js b/test/integration/transpiler/operators/assignment/concatenationAssignmentTest.js index ca5d632..d186e5b 100644 --- a/test/integration/transpiler/operators/assignment/concatenationAssignmentTest.js +++ b/test/integration/transpiler/operators/assignment/concatenationAssignmentTest.js @@ -35,11 +35,11 @@ describe('Transpiler string concatenation assignment operator ".=" test', functi }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var concatWith = core.concatWith, createString = core.createString, getVariable = core.getVariable;' + - 'return concatWith(getVariable("myVar"))(createString("my string here"));' + - '});' + 'return concatWith(getVariable("myVar"), createString("my string here"));' + + '}' ); }); }); diff --git a/test/integration/transpiler/operators/assignment/divisionAssignmentTest.js b/test/integration/transpiler/operators/assignment/divisionAssignmentTest.js index d48427a..d078331 100644 --- a/test/integration/transpiler/operators/assignment/divisionAssignmentTest.js +++ b/test/integration/transpiler/operators/assignment/divisionAssignmentTest.js @@ -35,11 +35,11 @@ describe('Transpiler division assignment operator "/=" test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var createInteger = core.createInteger, divideBy = core.divideBy, getVariable = core.getVariable;' + - 'return divideBy(getVariable("myVar"))(createInteger(27));' + - '});' + 'return divideBy(getVariable("myVar"), createInteger(27));' + + '}' ); }); }); diff --git a/test/integration/transpiler/operators/assignment/moduloAssignmentTest.js b/test/integration/transpiler/operators/assignment/moduloAssignmentTest.js index 919f810..96c7c01 100644 --- a/test/integration/transpiler/operators/assignment/moduloAssignmentTest.js +++ b/test/integration/transpiler/operators/assignment/moduloAssignmentTest.js @@ -35,11 +35,11 @@ describe('Transpiler modulo assignment operator "%=" test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var createInteger = core.createInteger, getVariable = core.getVariable, moduloWith = core.moduloWith;' + - 'return moduloWith(getVariable("myVar"))(createInteger(21));' + - '});' + 'return moduloWith(getVariable("myVar"), createInteger(21));' + + '}' ); }); }); diff --git a/test/integration/transpiler/operators/assignment/multiplicationAssignmentTest.js b/test/integration/transpiler/operators/assignment/multiplicationAssignmentTest.js index dbb3e5d..c4d54c1 100644 --- a/test/integration/transpiler/operators/assignment/multiplicationAssignmentTest.js +++ b/test/integration/transpiler/operators/assignment/multiplicationAssignmentTest.js @@ -35,11 +35,11 @@ describe('Transpiler multiplication assignment operator "*=" test', function () }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var createInteger = core.createInteger, getVariable = core.getVariable, multiplyBy = core.multiplyBy;' + - 'return multiplyBy(getVariable("myVar"))(createInteger(21));' + - '});' + 'return multiplyBy(getVariable("myVar"), createInteger(21));' + + '}' ); }); }); diff --git a/test/integration/transpiler/operators/assignment/referenceAssignmentTest.js b/test/integration/transpiler/operators/assignment/referenceAssignmentTest.js index 910e8b8..ba07425 100644 --- a/test/integration/transpiler/operators/assignment/referenceAssignmentTest.js +++ b/test/integration/transpiler/operators/assignment/referenceAssignmentTest.js @@ -38,11 +38,11 @@ describe('Transpiler reference assignment pseudo-operator "=&" test', function ( }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var getVariable = core.getVariable, setReference = core.setReference;' + - 'return setReference(getVariable("myVar"))(getVariable("anotherVar"));' + - '});' + 'return setReference(getVariable("myVar"), getVariable("anotherVar"));' + + '}' ); }); @@ -81,11 +81,11 @@ describe('Transpiler reference assignment pseudo-operator "=&" test', function ( }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var getVariable = core.getVariable, setReference = core.setReference, setValue = core.setValue;' + - 'return setValue(getVariable("firstVar"))(setReference(getVariable("secondVar"))(getVariable("thirdVar")));' + - '});' + 'return setValue(getVariable("firstVar"), setReference(getVariable("secondVar"), getVariable("thirdVar")));' + + '}' ); }); }); diff --git a/test/integration/transpiler/operators/assignment/subtractionAssignmentTest.js b/test/integration/transpiler/operators/assignment/subtractionAssignmentTest.js index 7e95b25..21f9cd0 100644 --- a/test/integration/transpiler/operators/assignment/subtractionAssignmentTest.js +++ b/test/integration/transpiler/operators/assignment/subtractionAssignmentTest.js @@ -35,11 +35,11 @@ describe('Transpiler subtraction assignment operator "-=" test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var createInteger = core.createInteger, decrementBy = core.decrementBy, getVariable = core.getVariable;' + - 'return decrementBy(getVariable("myVar"))(createInteger(21));' + - '});' + 'return decrementBy(getVariable("myVar"), createInteger(21));' + + '}' ); }); }); diff --git a/test/integration/transpiler/operators/bitwise/bitwiseAndOperatorTest.js b/test/integration/transpiler/operators/bitwise/bitwiseAndOperatorTest.js index 754b1e8..b223dae 100644 --- a/test/integration/transpiler/operators/bitwise/bitwiseAndOperatorTest.js +++ b/test/integration/transpiler/operators/bitwise/bitwiseAndOperatorTest.js @@ -35,11 +35,58 @@ describe('Transpiler bitwise AND operator "&" test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var bitwiseAnd = core.bitwiseAnd, createInteger = core.createInteger;' + - 'return bitwiseAnd(createInteger(21))(createInteger(10));' + - '});' + 'return bitwiseAnd(createInteger(21), createInteger(10));' + + '}' + ); + }); + + it('should correctly transpile a return with an operation between a variable and complex expression', function () { + var ast = { + name: 'N_PROGRAM', + statements: [{ + name: 'N_RETURN_STATEMENT', + expression: { + name: 'N_EXPRESSION', + left: { + name: 'N_VARIABLE', + variable: 'myLeftVar' + }, + right: [{ + operator: '&', + operand: { + name: 'N_TERNARY', + condition: { + name: 'N_VARIABLE', + variable: 'myCondition' + }, + consequent: { + name: 'N_STRING_LITERAL', + string: 'myRightStringIfTruthy' + }, + alternate: { + name: 'N_STRING_LITERAL', + string: 'myRightStringIfFalsy' + } + } + }] + } + }] + }; + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + + 'var bitwiseAnd = core.bitwiseAnd, createString = core.createString, getVariable = core.getVariable, snapshot = core.snapshot, ternary = core.ternary;' + + 'return bitwiseAnd(' + + // Plain variable object operand must be snapshotted due to complex subsequent operand (ternary). + 'snapshot(getVariable("myLeftVar")), ' + + '(ternary(getVariable("myCondition")) ? ' + + 'createString("myRightStringIfTruthy") : ' + + 'createString("myRightStringIfFalsy")' + + '));' + + '}' ); }); }); diff --git a/test/integration/transpiler/operators/bitwise/bitwiseLeftShiftOperatorTest.js b/test/integration/transpiler/operators/bitwise/bitwiseLeftShiftOperatorTest.js index 6a9869b..0602fec 100644 --- a/test/integration/transpiler/operators/bitwise/bitwiseLeftShiftOperatorTest.js +++ b/test/integration/transpiler/operators/bitwise/bitwiseLeftShiftOperatorTest.js @@ -35,11 +35,58 @@ describe('Transpiler bitwise left-shift operator "<<" test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var createInteger = core.createInteger, shiftLeft = core.shiftLeft;' + - 'return shiftLeft(createInteger(21))(createInteger(10));' + - '});' + 'return shiftLeft(createInteger(21), createInteger(10));' + + '}' + ); + }); + + it('should correctly transpile a return with an operation between a variable and complex expression', function () { + var ast = { + name: 'N_PROGRAM', + statements: [{ + name: 'N_RETURN_STATEMENT', + expression: { + name: 'N_EXPRESSION', + left: { + name: 'N_VARIABLE', + variable: 'myLeftVar' + }, + right: [{ + operator: '<<', + operand: { + name: 'N_TERNARY', + condition: { + name: 'N_VARIABLE', + variable: 'myCondition' + }, + consequent: { + name: 'N_STRING_LITERAL', + string: 'myRightStringIfTruthy' + }, + alternate: { + name: 'N_STRING_LITERAL', + string: 'myRightStringIfFalsy' + } + } + }] + } + }] + }; + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + + 'var createString = core.createString, getVariable = core.getVariable, shiftLeft = core.shiftLeft, snapshot = core.snapshot, ternary = core.ternary;' + + 'return shiftLeft(' + + // Plain variable object operand must be snapshotted due to complex subsequent operand (ternary). + 'snapshot(getVariable("myLeftVar")), ' + + '(ternary(getVariable("myCondition")) ? ' + + 'createString("myRightStringIfTruthy") : ' + + 'createString("myRightStringIfFalsy")' + + '));' + + '}' ); }); }); diff --git a/test/integration/transpiler/operators/bitwise/bitwiseOrOperatorTest.js b/test/integration/transpiler/operators/bitwise/bitwiseOrOperatorTest.js index 7f02753..0709b99 100644 --- a/test/integration/transpiler/operators/bitwise/bitwiseOrOperatorTest.js +++ b/test/integration/transpiler/operators/bitwise/bitwiseOrOperatorTest.js @@ -35,11 +35,58 @@ describe('Transpiler bitwise OR operator "|" test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var bitwiseOr = core.bitwiseOr, createInteger = core.createInteger;' + - 'return bitwiseOr(createInteger(21))(createInteger(10));' + - '});' + 'return bitwiseOr(createInteger(21), createInteger(10));' + + '}' + ); + }); + + it('should correctly transpile a return with an operation between a variable and complex expression', function () { + var ast = { + name: 'N_PROGRAM', + statements: [{ + name: 'N_RETURN_STATEMENT', + expression: { + name: 'N_EXPRESSION', + left: { + name: 'N_VARIABLE', + variable: 'myLeftVar' + }, + right: [{ + operator: '|', + operand: { + name: 'N_TERNARY', + condition: { + name: 'N_VARIABLE', + variable: 'myCondition' + }, + consequent: { + name: 'N_STRING_LITERAL', + string: 'myRightStringIfTruthy' + }, + alternate: { + name: 'N_STRING_LITERAL', + string: 'myRightStringIfFalsy' + } + } + }] + } + }] + }; + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + + 'var bitwiseOr = core.bitwiseOr, createString = core.createString, getVariable = core.getVariable, snapshot = core.snapshot, ternary = core.ternary;' + + 'return bitwiseOr(' + + // Plain variable object operand must be snapshotted due to complex subsequent operand (ternary). + 'snapshot(getVariable("myLeftVar")), ' + + '(ternary(getVariable("myCondition")) ? ' + + 'createString("myRightStringIfTruthy") : ' + + 'createString("myRightStringIfFalsy")' + + '));' + + '}' ); }); }); diff --git a/test/integration/transpiler/operators/bitwise/bitwiseRightShiftOperatorTest.js b/test/integration/transpiler/operators/bitwise/bitwiseRightShiftOperatorTest.js index 1dff7f8..ea562ee 100644 --- a/test/integration/transpiler/operators/bitwise/bitwiseRightShiftOperatorTest.js +++ b/test/integration/transpiler/operators/bitwise/bitwiseRightShiftOperatorTest.js @@ -35,11 +35,58 @@ describe('Transpiler bitwise right-shift operator ">>" test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var createInteger = core.createInteger, shiftRight = core.shiftRight;' + - 'return shiftRight(createInteger(21))(createInteger(10));' + - '});' + 'return shiftRight(createInteger(21), createInteger(10));' + + '}' + ); + }); + + it('should correctly transpile a return with an operation between a variable and complex expression', function () { + var ast = { + name: 'N_PROGRAM', + statements: [{ + name: 'N_RETURN_STATEMENT', + expression: { + name: 'N_EXPRESSION', + left: { + name: 'N_VARIABLE', + variable: 'myLeftVar' + }, + right: [{ + operator: '>>', + operand: { + name: 'N_TERNARY', + condition: { + name: 'N_VARIABLE', + variable: 'myCondition' + }, + consequent: { + name: 'N_STRING_LITERAL', + string: 'myRightStringIfTruthy' + }, + alternate: { + name: 'N_STRING_LITERAL', + string: 'myRightStringIfFalsy' + } + } + }] + } + }] + }; + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + + 'var createString = core.createString, getVariable = core.getVariable, shiftRight = core.shiftRight, snapshot = core.snapshot, ternary = core.ternary;' + + 'return shiftRight(' + + // Plain variable object operand must be snapshotted due to complex subsequent operand (ternary). + 'snapshot(getVariable("myLeftVar")), ' + + '(ternary(getVariable("myCondition")) ? ' + + 'createString("myRightStringIfTruthy") : ' + + 'createString("myRightStringIfFalsy")' + + '));' + + '}' ); }); }); diff --git a/test/integration/transpiler/operators/bitwise/bitwiseXorOperatorTest.js b/test/integration/transpiler/operators/bitwise/bitwiseXorOperatorTest.js index 066acb5..f6c7344 100644 --- a/test/integration/transpiler/operators/bitwise/bitwiseXorOperatorTest.js +++ b/test/integration/transpiler/operators/bitwise/bitwiseXorOperatorTest.js @@ -35,11 +35,58 @@ describe('Transpiler bitwise XOR operator "^" test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var bitwiseXor = core.bitwiseXor, createInteger = core.createInteger;' + - 'return bitwiseXor(createInteger(21))(createInteger(10));' + - '});' + 'return bitwiseXor(createInteger(21), createInteger(10));' + + '}' + ); + }); + + it('should correctly transpile a return with an operation between a variable and complex expression', function () { + var ast = { + name: 'N_PROGRAM', + statements: [{ + name: 'N_RETURN_STATEMENT', + expression: { + name: 'N_EXPRESSION', + left: { + name: 'N_VARIABLE', + variable: 'myLeftVar' + }, + right: [{ + operator: '^', + operand: { + name: 'N_TERNARY', + condition: { + name: 'N_VARIABLE', + variable: 'myCondition' + }, + consequent: { + name: 'N_STRING_LITERAL', + string: 'myRightStringIfTruthy' + }, + alternate: { + name: 'N_STRING_LITERAL', + string: 'myRightStringIfFalsy' + } + } + }] + } + }] + }; + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + + 'var bitwiseXor = core.bitwiseXor, createString = core.createString, getVariable = core.getVariable, snapshot = core.snapshot, ternary = core.ternary;' + + 'return bitwiseXor(' + + // Plain variable object operand must be snapshotted due to complex subsequent operand (ternary). + 'snapshot(getVariable("myLeftVar")), ' + + '(ternary(getVariable("myCondition")) ? ' + + 'createString("myRightStringIfTruthy") : ' + + 'createString("myRightStringIfFalsy")' + + '));' + + '}' ); }); }); diff --git a/test/integration/transpiler/operators/cast/arrayTest.js b/test/integration/transpiler/operators/cast/arrayTest.js index d4e1cb6..1e08fa3 100644 --- a/test/integration/transpiler/operators/cast/arrayTest.js +++ b/test/integration/transpiler/operators/cast/arrayTest.js @@ -41,7 +41,7 @@ describe('Transpiler array cast operator test', function () { expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + 'var add = core.add, coerceToArray = core.coerceToArray, createInteger = core.createInteger, getVariable = core.getVariable;' + - 'coerceToArray(add(getVariable("myVar"))(createInteger(21)));' + + 'coerceToArray(add(getVariable("myVar"), createInteger(21)));' + '}' ); }); diff --git a/test/integration/transpiler/operators/cast/binaryTest.js b/test/integration/transpiler/operators/cast/binaryTest.js index f5c9b49..c716a48 100644 --- a/test/integration/transpiler/operators/cast/binaryTest.js +++ b/test/integration/transpiler/operators/cast/binaryTest.js @@ -44,7 +44,7 @@ describe('Transpiler binary cast operator test', function () { expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + 'var add = core.add, coerceToString = core.coerceToString, createInteger = core.createInteger, getVariable = core.getVariable;' + - 'coerceToString(add(getVariable("myVar"))(createInteger(21)));' + + 'coerceToString(add(getVariable("myVar"), createInteger(21)));' + '}' ); }); diff --git a/test/integration/transpiler/operators/cast/booleanTest.js b/test/integration/transpiler/operators/cast/booleanTest.js index 79d5f8c..1ddcd6b 100644 --- a/test/integration/transpiler/operators/cast/booleanTest.js +++ b/test/integration/transpiler/operators/cast/booleanTest.js @@ -41,7 +41,7 @@ describe('Transpiler boolean cast operator test', function () { expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + 'var add = core.add, coerceToBoolean = core.coerceToBoolean, createInteger = core.createInteger, getVariable = core.getVariable;' + - 'coerceToBoolean(add(getVariable("myVar"))(createInteger(21)));' + + 'coerceToBoolean(add(getVariable("myVar"), createInteger(21)));' + '}' ); }); diff --git a/test/integration/transpiler/operators/cast/doubleTest.js b/test/integration/transpiler/operators/cast/doubleTest.js index 321df58..a111fc9 100644 --- a/test/integration/transpiler/operators/cast/doubleTest.js +++ b/test/integration/transpiler/operators/cast/doubleTest.js @@ -41,7 +41,7 @@ describe('Transpiler double cast operator test', function () { expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + 'var add = core.add, coerceToFloat = core.coerceToFloat, createInteger = core.createInteger, getVariable = core.getVariable;' + - 'coerceToFloat(add(getVariable("myVar"))(createInteger(21)));' + + 'coerceToFloat(add(getVariable("myVar"), createInteger(21)));' + '}' ); }); diff --git a/test/integration/transpiler/operators/cast/integerTest.js b/test/integration/transpiler/operators/cast/integerTest.js index 68da522..beacfeb 100644 --- a/test/integration/transpiler/operators/cast/integerTest.js +++ b/test/integration/transpiler/operators/cast/integerTest.js @@ -41,7 +41,7 @@ describe('Transpiler integer cast operator test', function () { expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + 'var add = core.add, coerceToInteger = core.coerceToInteger, createInteger = core.createInteger, getVariable = core.getVariable;' + - 'coerceToInteger(add(getVariable("myVar"))(createInteger(21)));' + + 'coerceToInteger(add(getVariable("myVar"), createInteger(21)));' + '}' ); }); diff --git a/test/integration/transpiler/operators/cast/objectTest.js b/test/integration/transpiler/operators/cast/objectTest.js index e2c82c7..5dc2bc1 100644 --- a/test/integration/transpiler/operators/cast/objectTest.js +++ b/test/integration/transpiler/operators/cast/objectTest.js @@ -41,7 +41,7 @@ describe('Transpiler object cast operator test', function () { expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + 'var add = core.add, coerceToObject = core.coerceToObject, createInteger = core.createInteger, getVariable = core.getVariable;' + - 'coerceToObject(add(getVariable("myVar"))(createInteger(21)));' + + 'coerceToObject(add(getVariable("myVar"), createInteger(21)));' + '}' ); }); diff --git a/test/integration/transpiler/operators/cast/stringTest.js b/test/integration/transpiler/operators/cast/stringTest.js index 30458b7..d8388aa 100644 --- a/test/integration/transpiler/operators/cast/stringTest.js +++ b/test/integration/transpiler/operators/cast/stringTest.js @@ -41,7 +41,7 @@ describe('Transpiler string cast operator test', function () { expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + 'var add = core.add, coerceToString = core.coerceToString, createInteger = core.createInteger, getVariable = core.getVariable;' + - 'coerceToString(add(getVariable("myVar"))(createInteger(21)));' + + 'coerceToString(add(getVariable("myVar"), createInteger(21)));' + '}' ); }); diff --git a/test/integration/transpiler/operators/cast/unsetTest.js b/test/integration/transpiler/operators/cast/unsetTest.js index a56603d..8ad930d 100644 --- a/test/integration/transpiler/operators/cast/unsetTest.js +++ b/test/integration/transpiler/operators/cast/unsetTest.js @@ -41,7 +41,7 @@ describe('Transpiler unset cast operator test', function () { expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + 'var add = core.add, createInteger = core.createInteger, getVariable = core.getVariable, nullValue = core.nullValue;' + - '(add(getVariable("myVar"))(createInteger(21)), nullValue);' + + '(add(getVariable("myVar"), createInteger(21)), nullValue);' + '}' ); }); diff --git a/test/integration/transpiler/operators/cloneTest.js b/test/integration/transpiler/operators/cloneTest.js index 18ed3f8..837df23 100644 --- a/test/integration/transpiler/operators/cloneTest.js +++ b/test/integration/transpiler/operators/cloneTest.js @@ -28,11 +28,11 @@ describe('Transpiler clone operator test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var clone = core.clone, getVariable = core.getVariable;' + 'return clone(getVariable("myObject"));' + - '});' + '}' ); }); }); diff --git a/test/integration/transpiler/operators/comparison/greaterThanOrEqualTest.js b/test/integration/transpiler/operators/comparison/greaterThanOrEqualTest.js index 1707977..60f1e1d 100644 --- a/test/integration/transpiler/operators/comparison/greaterThanOrEqualTest.js +++ b/test/integration/transpiler/operators/comparison/greaterThanOrEqualTest.js @@ -38,7 +38,54 @@ describe('Transpiler greater-than-or-equal comparison operator test', function ( expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + 'var createInteger = core.createInteger, isGreaterThanOrEqual = core.isGreaterThanOrEqual;' + - 'return isGreaterThanOrEqual(createInteger(21))(createInteger(32));' + + 'return isGreaterThanOrEqual(createInteger(21), createInteger(32));' + + '}' + ); + }); + + it('should correctly transpile a return with a comparison between a variable and complex expression', function () { + var ast = { + name: 'N_PROGRAM', + statements: [{ + name: 'N_RETURN_STATEMENT', + expression: { + name: 'N_EXPRESSION', + left: { + name: 'N_VARIABLE', + variable: 'myLeftVar' + }, + right: [{ + operator: '>=', + operand: { + name: 'N_TERNARY', + condition: { + name: 'N_VARIABLE', + variable: 'myCondition' + }, + consequent: { + name: 'N_STRING_LITERAL', + string: 'myRightStringIfTruthy' + }, + alternate: { + name: 'N_STRING_LITERAL', + string: 'myRightStringIfFalsy' + } + } + }] + } + }] + }; + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + + 'var createString = core.createString, getVariable = core.getVariable, isGreaterThanOrEqual = core.isGreaterThanOrEqual, snapshot = core.snapshot, ternary = core.ternary;' + + 'return isGreaterThanOrEqual(' + + // Plain variable object operand must be snapshotted due to complex subsequent operand (ternary). + 'snapshot(getVariable("myLeftVar")), ' + + '(ternary(getVariable("myCondition")) ? ' + + 'createString("myRightStringIfTruthy") : ' + + 'createString("myRightStringIfFalsy")' + + '));' + '}' ); }); diff --git a/test/integration/transpiler/operators/comparison/greaterThanTest.js b/test/integration/transpiler/operators/comparison/greaterThanTest.js index b23eb8f..56f664e 100644 --- a/test/integration/transpiler/operators/comparison/greaterThanTest.js +++ b/test/integration/transpiler/operators/comparison/greaterThanTest.js @@ -38,7 +38,54 @@ describe('Transpiler greater-than comparison operator test', function () { expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + 'var createInteger = core.createInteger, isGreaterThan = core.isGreaterThan;' + - 'return isGreaterThan(createInteger(21))(createInteger(32));' + + 'return isGreaterThan(createInteger(21), createInteger(32));' + + '}' + ); + }); + + it('should correctly transpile a return with a comparison between a variable and complex expression', function () { + var ast = { + name: 'N_PROGRAM', + statements: [{ + name: 'N_RETURN_STATEMENT', + expression: { + name: 'N_EXPRESSION', + left: { + name: 'N_VARIABLE', + variable: 'myLeftVar' + }, + right: [{ + operator: '>', + operand: { + name: 'N_TERNARY', + condition: { + name: 'N_VARIABLE', + variable: 'myCondition' + }, + consequent: { + name: 'N_STRING_LITERAL', + string: 'myRightStringIfTruthy' + }, + alternate: { + name: 'N_STRING_LITERAL', + string: 'myRightStringIfFalsy' + } + } + }] + } + }] + }; + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + + 'var createString = core.createString, getVariable = core.getVariable, isGreaterThan = core.isGreaterThan, snapshot = core.snapshot, ternary = core.ternary;' + + 'return isGreaterThan(' + + // Plain variable object operand must be snapshotted due to complex subsequent operand (ternary). + 'snapshot(getVariable("myLeftVar")), ' + + '(ternary(getVariable("myCondition")) ? ' + + 'createString("myRightStringIfTruthy") : ' + + 'createString("myRightStringIfFalsy")' + + '));' + '}' ); }); diff --git a/test/integration/transpiler/operators/comparison/lessThanOrEqualTest.js b/test/integration/transpiler/operators/comparison/lessThanOrEqualTest.js index e117185..06fd0c4 100644 --- a/test/integration/transpiler/operators/comparison/lessThanOrEqualTest.js +++ b/test/integration/transpiler/operators/comparison/lessThanOrEqualTest.js @@ -38,7 +38,54 @@ describe('Transpiler less-than-or-equal comparison operator test', function () { expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + 'var createInteger = core.createInteger, isLessThanOrEqual = core.isLessThanOrEqual;' + - 'return isLessThanOrEqual(createInteger(21))(createInteger(32));' + + 'return isLessThanOrEqual(createInteger(21), createInteger(32));' + + '}' + ); + }); + + it('should correctly transpile a return with a comparison between a variable and complex expression', function () { + var ast = { + name: 'N_PROGRAM', + statements: [{ + name: 'N_RETURN_STATEMENT', + expression: { + name: 'N_EXPRESSION', + left: { + name: 'N_VARIABLE', + variable: 'myLeftVar' + }, + right: [{ + operator: '<=', + operand: { + name: 'N_TERNARY', + condition: { + name: 'N_VARIABLE', + variable: 'myCondition' + }, + consequent: { + name: 'N_STRING_LITERAL', + string: 'myRightStringIfTruthy' + }, + alternate: { + name: 'N_STRING_LITERAL', + string: 'myRightStringIfFalsy' + } + } + }] + } + }] + }; + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + + 'var createString = core.createString, getVariable = core.getVariable, isLessThanOrEqual = core.isLessThanOrEqual, snapshot = core.snapshot, ternary = core.ternary;' + + 'return isLessThanOrEqual(' + + // Plain variable object operand must be snapshotted due to complex subsequent operand (ternary). + 'snapshot(getVariable("myLeftVar")), ' + + '(ternary(getVariable("myCondition")) ? ' + + 'createString("myRightStringIfTruthy") : ' + + 'createString("myRightStringIfFalsy")' + + '));' + '}' ); }); diff --git a/test/integration/transpiler/operators/comparison/lessThanTest.js b/test/integration/transpiler/operators/comparison/lessThanTest.js index 8ae279d..a922b23 100644 --- a/test/integration/transpiler/operators/comparison/lessThanTest.js +++ b/test/integration/transpiler/operators/comparison/lessThanTest.js @@ -38,7 +38,54 @@ describe('Transpiler less-than comparison operator test', function () { expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + 'var createInteger = core.createInteger, isLessThan = core.isLessThan;' + - 'return isLessThan(createInteger(21))(createInteger(32));' + + 'return isLessThan(createInteger(21), createInteger(32));' + + '}' + ); + }); + + it('should correctly transpile a return with a comparison between a variable and complex expression', function () { + var ast = { + name: 'N_PROGRAM', + statements: [{ + name: 'N_RETURN_STATEMENT', + expression: { + name: 'N_EXPRESSION', + left: { + name: 'N_VARIABLE', + variable: 'myLeftVar' + }, + right: [{ + operator: '<', + operand: { + name: 'N_TERNARY', + condition: { + name: 'N_VARIABLE', + variable: 'myCondition' + }, + consequent: { + name: 'N_STRING_LITERAL', + string: 'myRightStringIfTruthy' + }, + alternate: { + name: 'N_STRING_LITERAL', + string: 'myRightStringIfFalsy' + } + } + }] + } + }] + }; + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + + 'var createString = core.createString, getVariable = core.getVariable, isLessThan = core.isLessThan, snapshot = core.snapshot, ternary = core.ternary;' + + 'return isLessThan(' + + // Plain variable object operand must be snapshotted due to complex subsequent operand (ternary). + 'snapshot(getVariable("myLeftVar")), ' + + '(ternary(getVariable("myCondition")) ? ' + + 'createString("myRightStringIfTruthy") : ' + + 'createString("myRightStringIfFalsy")' + + '));' + '}' ); }); diff --git a/test/integration/transpiler/operators/comparison/looseEqualityTest.js b/test/integration/transpiler/operators/comparison/looseEqualityTest.js index cfc4875..36078e9 100644 --- a/test/integration/transpiler/operators/comparison/looseEqualityTest.js +++ b/test/integration/transpiler/operators/comparison/looseEqualityTest.js @@ -38,7 +38,54 @@ describe('Transpiler loose equality comparison operator test', function () { expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + 'var createInteger = core.createInteger, isEqual = core.isEqual;' + - 'return isEqual(createInteger(21))(createInteger(32));' + + 'return isEqual(createInteger(21), createInteger(32));' + + '}' + ); + }); + + it('should correctly transpile a return with a comparison between a variable and complex expression', function () { + var ast = { + name: 'N_PROGRAM', + statements: [{ + name: 'N_RETURN_STATEMENT', + expression: { + name: 'N_EXPRESSION', + left: { + name: 'N_VARIABLE', + variable: 'myLeftVar' + }, + right: [{ + operator: '==', + operand: { + name: 'N_TERNARY', + condition: { + name: 'N_VARIABLE', + variable: 'myCondition' + }, + consequent: { + name: 'N_STRING_LITERAL', + string: 'myRightStringIfTruthy' + }, + alternate: { + name: 'N_STRING_LITERAL', + string: 'myRightStringIfFalsy' + } + } + }] + } + }] + }; + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + + 'var createString = core.createString, getVariable = core.getVariable, isEqual = core.isEqual, snapshot = core.snapshot, ternary = core.ternary;' + + 'return isEqual(' + + // Plain variable object operand must be snapshotted due to complex subsequent operand (ternary). + 'snapshot(getVariable("myLeftVar")), ' + + '(ternary(getVariable("myCondition")) ? ' + + 'createString("myRightStringIfTruthy") : ' + + 'createString("myRightStringIfFalsy")' + + '));' + '}' ); }); diff --git a/test/integration/transpiler/operators/comparison/looseInequalityTest.js b/test/integration/transpiler/operators/comparison/looseInequalityTest.js index 7fbe8ae..19a1773 100644 --- a/test/integration/transpiler/operators/comparison/looseInequalityTest.js +++ b/test/integration/transpiler/operators/comparison/looseInequalityTest.js @@ -38,7 +38,7 @@ describe('Transpiler loose inequality comparison operator test', function () { expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + 'var createInteger = core.createInteger, isNotEqual = core.isNotEqual;' + - 'return isNotEqual(createInteger(21))(createInteger(32));' + + 'return isNotEqual(createInteger(21), createInteger(32));' + '}' ); }); @@ -68,7 +68,54 @@ describe('Transpiler loose inequality comparison operator test', function () { expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + 'var createInteger = core.createInteger, isNotEqual = core.isNotEqual;' + - 'return isNotEqual(createInteger(21))(createInteger(32));' + + 'return isNotEqual(createInteger(21), createInteger(32));' + + '}' + ); + }); + + it('should correctly transpile a return with a comparison using "<>" between a variable and complex expression', function () { + var ast = { + name: 'N_PROGRAM', + statements: [{ + name: 'N_RETURN_STATEMENT', + expression: { + name: 'N_EXPRESSION', + left: { + name: 'N_VARIABLE', + variable: 'myLeftVar' + }, + right: [{ + operator: '<>', + operand: { + name: 'N_TERNARY', + condition: { + name: 'N_VARIABLE', + variable: 'myCondition' + }, + consequent: { + name: 'N_STRING_LITERAL', + string: 'myRightStringIfTruthy' + }, + alternate: { + name: 'N_STRING_LITERAL', + string: 'myRightStringIfFalsy' + } + } + }] + } + }] + }; + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + + 'var createString = core.createString, getVariable = core.getVariable, isNotEqual = core.isNotEqual, snapshot = core.snapshot, ternary = core.ternary;' + + 'return isNotEqual(' + + // Plain variable object operand must be snapshotted due to complex subsequent operand (ternary). + 'snapshot(getVariable("myLeftVar")), ' + + '(ternary(getVariable("myCondition")) ? ' + + 'createString("myRightStringIfTruthy") : ' + + 'createString("myRightStringIfFalsy")' + + '));' + '}' ); }); diff --git a/test/integration/transpiler/operators/decrementTest.js b/test/integration/transpiler/operators/decrementTest.js index d0e5099..af3a7f7 100644 --- a/test/integration/transpiler/operators/decrementTest.js +++ b/test/integration/transpiler/operators/decrementTest.js @@ -30,11 +30,11 @@ describe('Transpiler decrement "--" operator test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var getVariable = core.getVariable, preDecrement = core.preDecrement;' + 'preDecrement(getVariable("myVar"));' + - '});' + '}' ); }); @@ -55,11 +55,11 @@ describe('Transpiler decrement "--" operator test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var getVariable = core.getVariable, postDecrement = core.postDecrement;' + 'postDecrement(getVariable("myVar"));' + - '});' + '}' ); }); }); diff --git a/test/integration/transpiler/operators/errorControlTest.js b/test/integration/transpiler/operators/errorControlTest.js index 56c9eae..9c6153f 100644 --- a/test/integration/transpiler/operators/errorControlTest.js +++ b/test/integration/transpiler/operators/errorControlTest.js @@ -28,11 +28,11 @@ describe('Transpiler error control operator @(...) test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var getVariable = core.getVariable, suppressErrors = core.suppressErrors;' + 'return suppressErrors()(getVariable("myVar"));' + - '});' + '}' ); }); }); diff --git a/test/integration/transpiler/operators/incrementTest.js b/test/integration/transpiler/operators/incrementTest.js index 824bbe4..c3768d2 100644 --- a/test/integration/transpiler/operators/incrementTest.js +++ b/test/integration/transpiler/operators/incrementTest.js @@ -30,11 +30,11 @@ describe('Transpiler increment "++" operator test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var getVariable = core.getVariable, preIncrement = core.preIncrement;' + 'preIncrement(getVariable("myVar"));' + - '});' + '}' ); }); @@ -55,11 +55,11 @@ describe('Transpiler increment "++" operator test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var getVariable = core.getVariable, postIncrement = core.postIncrement;' + 'postIncrement(getVariable("myVar"));' + - '});' + '}' ); }); }); diff --git a/test/integration/transpiler/operators/instanceOfTest.js b/test/integration/transpiler/operators/instanceOfTest.js index 124f155..441ea22 100644 --- a/test/integration/transpiler/operators/instanceOfTest.js +++ b/test/integration/transpiler/operators/instanceOfTest.js @@ -32,11 +32,11 @@ describe('Transpiler instanceof binary operator test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var getVariable = core.getVariable, instanceOf = core.instanceOf;' + - 'return instanceOf(getVariable("myObject"))(getVariable("myClass"));' + - '});' + 'return instanceOf(getVariable("myObject"), getVariable("myClass"));' + + '}' ); }); @@ -59,11 +59,11 @@ describe('Transpiler instanceof binary operator test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var createBareword = core.createBareword, getVariable = core.getVariable, instanceOf = core.instanceOf;' + - 'return instanceOf(getVariable("myObject"))(createBareword("MyClass"));' + - '});' + 'return instanceOf(getVariable("myObject"), createBareword("MyClass"));' + + '}' ); }); @@ -93,11 +93,11 @@ describe('Transpiler instanceof binary operator test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var callFunction = core.callFunction, createBareword = core.createBareword, getVariable = core.getVariable, instanceOf = core.instanceOf;' + - 'return callFunction("myFunc")(instanceOf(getVariable("myObject"))(createBareword("MyClass")))();' + - '});' + 'return callFunction("myFunc", instanceOf(getVariable("myObject"), createBareword("MyClass")));' + + '}' ); }); }); diff --git a/test/integration/transpiler/operators/logical/notTest.js b/test/integration/transpiler/operators/logical/notTest.js index dea2530..83f551f 100644 --- a/test/integration/transpiler/operators/logical/notTest.js +++ b/test/integration/transpiler/operators/logical/notTest.js @@ -68,7 +68,59 @@ describe('Transpiler logical "not" operator test', function () { expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + 'var getVariable = core.getVariable, isNotIdentical = core.isNotIdentical, logicalNot = core.logicalNot;' + - 'return isNotIdentical(logicalNot(getVariable("leftVar")))(getVariable("rightVar"));' + + 'return isNotIdentical(logicalNot(getVariable("leftVar")), getVariable("rightVar"));' + + '}' + ); + }); + + it('should correctly transpile a logical OR with embedded NOT where right operand is complex', function () { + var ast = { + name: 'N_PROGRAM', + statements: [{ + name: 'N_RETURN_STATEMENT', + expression: { + name: 'N_EXPRESSION', + left: { + name: 'N_UNARY_EXPRESSION', + prefix: true, + operator: '!', + operand: { + name: 'N_VARIABLE', + variable: 'myLeftVar' + } + }, + right: [{ + operator: '!==', + operand: { + name: 'N_TERNARY', + condition: { + name: 'N_VARIABLE', + variable: 'myCondition' + }, + consequent: { + name: 'N_STRING_LITERAL', + string: 'myRightStringIfTruthy' + }, + alternate: { + name: 'N_STRING_LITERAL', + string: 'myRightStringIfFalsy' + } + } + }] + } + }] + }; + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + + 'var createString = core.createString, getVariable = core.getVariable, isNotIdentical = core.isNotIdentical, logicalNot = core.logicalNot, snapshot = core.snapshot, ternary = core.ternary;' + + 'return isNotIdentical(' + + // Plain variable operand must be snapshotted due to complex subsequent operand (ternary). + 'snapshot(logicalNot(getVariable("myLeftVar"))), ' + + '(ternary(getVariable("myCondition")) ? ' + + 'createString("myRightStringIfTruthy") : ' + + 'createString("myRightStringIfFalsy")' + + '));' + '}' ); }); diff --git a/test/integration/transpiler/operators/logical/xorTest.js b/test/integration/transpiler/operators/logical/xorTest.js index b14dd3c..9579c27 100644 --- a/test/integration/transpiler/operators/logical/xorTest.js +++ b/test/integration/transpiler/operators/logical/xorTest.js @@ -38,7 +38,54 @@ describe('Transpiler logical "xor" operator test', function () { expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + 'var createString = core.createString, logicalXor = core.logicalXor;' + - 'return logicalXor(createString("first"))(createString("second"));' + + 'return logicalXor(createString("first"), createString("second"));' + + '}' + ); + }); + + it('should correctly transpile a return with an operation on a variable and complex expression', function () { + var ast = { + name: 'N_PROGRAM', + statements: [{ + name: 'N_RETURN_STATEMENT', + expression: { + name: 'N_EXPRESSION', + left: { + name: 'N_VARIABLE', + variable: 'myLeftVar' + }, + right: [{ + operator: 'xor', + operand: { + name: 'N_TERNARY', + condition: { + name: 'N_VARIABLE', + variable: 'myCondition' + }, + consequent: { + name: 'N_STRING_LITERAL', + string: 'myRightStringIfTruthy' + }, + alternate: { + name: 'N_STRING_LITERAL', + string: 'myRightStringIfFalsy' + } + } + }] + } + }] + }; + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + + 'var createString = core.createString, getVariable = core.getVariable, logicalXor = core.logicalXor, snapshot = core.snapshot, ternary = core.ternary;' + + 'return logicalXor(' + + // Plain variable object operand must be snapshotted due to complex subsequent operand (ternary). + 'snapshot(getVariable("myLeftVar")), ' + + '(ternary(getVariable("myCondition")) ? ' + + 'createString("myRightStringIfTruthy") : ' + + 'createString("myRightStringIfFalsy")' + + '));' + '}' ); }); diff --git a/test/integration/transpiler/operators/newTest.js b/test/integration/transpiler/operators/newTest.js new file mode 100644 index 0000000..ff27da6 --- /dev/null +++ b/test/integration/transpiler/operators/newTest.js @@ -0,0 +1,173 @@ +/* + * PHPToJS - PHP-to-JavaScript transpiler + * Copyright (c) Dan Phillimore (asmblah) + * https://github.com/uniter/phptojs + * + * Released under the MIT license + * https://github.com/uniter/phptojs/raw/master/MIT-LICENSE.txt + */ + +'use strict'; + +var expect = require('chai').expect, + phpToJS = require('../../../..'); + +describe('Transpiler "new" operator test', function () { + it('should correctly transpile an instantiation with no constructor arguments', function () { + var ast = { + name: 'N_PROGRAM', + statements: [{ + name: 'N_EXPRESSION_STATEMENT', + expression: { + name: 'N_EXPRESSION', + left: { + name: 'N_VARIABLE', + variable: 'object' + }, + right: [{ + operator: '=', + operand: { + name: 'N_NEW_EXPRESSION', + className: { + name: 'N_STRING', + string: 'Worker' + } + } + }] + } + }] + }; + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + + 'var createBareword = core.createBareword, createInstance = core.createInstance, getVariable = core.getVariable, setValue = core.setValue;' + + 'setValue(getVariable("object"), createInstance(createBareword("Worker")));' + + '}' + ); + }); + + it('should correctly transpile a new expression in function call argument', function () { + var ast = { + name: 'N_PROGRAM', + statements: [{ + name: 'N_EXPRESSION_STATEMENT', + expression: { + name: 'N_FUNCTION_CALL', + func: { + name: 'N_STRING', + string: 'myFunc' + }, + args: [{ + name: 'N_NEW_EXPRESSION', + className: { + name: 'N_VARIABLE', + variable: 'myClassName' + } + }] + } + }] + }; + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + + 'var callFunction = core.callFunction, createInstance = core.createInstance, getVariable = core.getVariable;' + + 'callFunction("myFunc", ' + + 'createInstance(getVariable("myClassName"))' + + ');' + + '}' + ); + }); + + it('should correctly transpile an instantiation with a bareword class name with single complex constructor argument', function () { + var ast = { + name: 'N_PROGRAM', + statements: [{ + name: 'N_EXPRESSION_STATEMENT', + expression: { + name: 'N_EXPRESSION', + left: { + name: 'N_VARIABLE', + variable: 'object' + }, + right: [{ + operator: '=', + operand: { + name: 'N_NEW_EXPRESSION', + className: { + name: 'N_STRING', + string: 'MyClass' + }, + args: [{ + name: 'N_VARIABLE_EXPRESSION', + expression: { + name: 'N_VARIABLE', + variable: 'myVar' + } + }] + } + }] + } + }] + }; + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + + 'var createBareword = core.createBareword, createInstance = core.createInstance, getVariable = core.getVariable, getVariableVariable = core.getVariableVariable, setValue = core.setValue;' + + 'setValue(getVariable("object"), ' + + 'createInstance(' + + // No need to snapshot a bareword. + 'createBareword("MyClass"), ' + + 'getVariableVariable(getVariable("myVar"))' + + '));' + + '}' + ); + }); + + it('should correctly transpile an instantiation with a dynamic class name with single complex constructor argument', function () { + var ast = { + name: 'N_PROGRAM', + statements: [{ + name: 'N_EXPRESSION_STATEMENT', + expression: { + name: 'N_EXPRESSION', + left: { + name: 'N_VARIABLE', + variable: 'object' + }, + right: [{ + operator: '=', + operand: { + name: 'N_NEW_EXPRESSION', + className: { + name: 'N_VARIABLE', + variable: 'myClassName' + }, + args: [{ + name: 'N_VARIABLE_EXPRESSION', + expression: { + name: 'N_VARIABLE', + variable: 'myVar' + } + }] + } + }] + } + }] + }; + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + + 'var createInstance = core.createInstance, getVariable = core.getVariable, getVariableVariable = core.getVariableVariable, setValue = core.setValue, snapshot = core.snapshot;' + + 'setValue(getVariable("object"), ' + + 'createInstance(' + + // This operand must be snapshotted as it is followed by an expression that may modify it + // (based on simple heuristics). + 'snapshot(getVariable("myClassName")), ' + + // Final operand does not need to be snapshotted, as there are no subsequent complex operands + // that may affect its result prior to the actual call executing. + 'getVariableVariable(getVariable("myVar"))' + + '));' + + '}' + ); + }); +}); diff --git a/test/integration/transpiler/operators/objectAccess/instancePropertyTest.js b/test/integration/transpiler/operators/objectAccess/instancePropertyTest.js index 1f7301a..d1cd6ee 100644 --- a/test/integration/transpiler/operators/objectAccess/instancePropertyTest.js +++ b/test/integration/transpiler/operators/objectAccess/instancePropertyTest.js @@ -42,12 +42,12 @@ describe('Transpiler object instance property access test', function () { expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + 'var getInstanceProperty = core.getInstanceProperty, getVariable = core.getVariable;' + - 'return getInstanceProperty(getInstanceProperty(getVariable("myVar"))("firstProp"))("secondProp");' + + 'return getInstanceProperty(getInstanceProperty(getVariable("myVar"), "firstProp"), "secondProp");' + '}' ); }); - it('should correctly transpile a read of dynamically referenced to a property', function () { + it('should correctly transpile a read of dynamically referenced property', function () { var ast = { name: 'N_PROGRAM', statements: [{ @@ -69,7 +69,52 @@ describe('Transpiler object instance property access test', function () { expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + 'var getVariable = core.getVariable, getVariableInstanceProperty = core.getVariableInstanceProperty;' + - 'return getVariableInstanceProperty(getVariable("myObjectVar"))(getVariable("myVarHoldingPropName"));' + + 'return getVariableInstanceProperty(getVariable("myObjectVar"), getVariable("myVarHoldingPropName"));' + + '}' + ); + }); + + it('should correctly transpile a read of dynamically referenced property with complex expression', function () { + var ast = { + name: 'N_PROGRAM', + statements: [{ + name: 'N_RETURN_STATEMENT', + expression: { + name: 'N_OBJECT_PROPERTY', + object: { + name: 'N_VARIABLE', + variable: 'myObjectVar' + }, + property: { + name: 'N_TERNARY', + condition: { + name: 'N_VARIABLE', + variable: 'myVarAsCondition' + }, + consequent: { + name: 'N_STRING_LITERAL', + string: 'myVarHoldingPropNameIfTruthy' + }, + alternate: { + name: 'N_STRING_LITERAL', + string: 'myVarHoldingPropNameIfFalsy' + } + } + } + }] + }; + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + + 'var createString = core.createString, getVariable = core.getVariable, getVariableInstanceProperty = core.getVariableInstanceProperty, snapshot = core.snapshot, ternary = core.ternary;' + + 'return getVariableInstanceProperty(' + + // Plain variable object operand must be snapshotted due to complex subsequent operand + // (ternary property name operand). + 'snapshot(getVariable("myObjectVar")), ' + + '(ternary(getVariable("myVarAsCondition")) ? ' + + 'createString("myVarHoldingPropNameIfTruthy") : ' + + 'createString("myVarHoldingPropNameIfFalsy")' + + '));' + '}' ); }); @@ -106,7 +151,7 @@ describe('Transpiler object instance property access test', function () { expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + 'var createInteger = core.createInteger, getInstanceProperty = core.getInstanceProperty, getVariable = core.getVariable, setValue = core.setValue;' + - 'setValue(getInstanceProperty(getVariable("myVar"))("myProp"))(createInteger(1234));' + + 'setValue(getInstanceProperty(getVariable("myVar"), "myProp"), createInteger(1234));' + '}' ); }); diff --git a/test/integration/transpiler/operators/scopeResolution/staticPropertyTest.js b/test/integration/transpiler/operators/scopeResolution/staticPropertyTest.js index c095b7e..194ef1b 100644 --- a/test/integration/transpiler/operators/scopeResolution/staticPropertyTest.js +++ b/test/integration/transpiler/operators/scopeResolution/staticPropertyTest.js @@ -35,7 +35,7 @@ describe('Transpiler static property access test', function () { expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + 'var createBareword = core.createBareword, getStaticProperty = core.getStaticProperty;' + - 'return getStaticProperty(createBareword("MyImportedClass"))("myStaticProp");' + + 'return getStaticProperty(createBareword("MyImportedClass"), "myStaticProp");' + '}' ); }); @@ -69,7 +69,7 @@ describe('Transpiler static property access test', function () { expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + 'var getStaticProperty = core.getStaticProperty, getVariable = core.getVariable;' + - 'return getStaticProperty(getStaticProperty(getVariable("myVar"))("firstProp"))("secondProp");' + + 'return getStaticProperty(getStaticProperty(getVariable("myVar"), "firstProp"), "secondProp");' + '}' ); }); @@ -96,7 +96,7 @@ describe('Transpiler static property access test', function () { expect(phpToJS.transpile(ast, {bare: true})).to.equal( 'function (core) {' + 'var getVariable = core.getVariable, getVariableStaticProperty = core.getVariableStaticProperty;' + - 'return getVariableStaticProperty(getVariable("myClassNameVar"))(getVariable("myVarHoldingPropName"));' + + 'return getVariableStaticProperty(getVariable("myClassNameVar"), getVariable("myVarHoldingPropName"));' + '}' ); }); diff --git a/test/integration/transpiler/printTest.js b/test/integration/transpiler/printTest.js index f054215..ae404c6 100644 --- a/test/integration/transpiler/printTest.js +++ b/test/integration/transpiler/printTest.js @@ -28,11 +28,11 @@ describe('Transpiler "print" expression test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var createString = core.createString, print = core.print;' + 'print(createString("hello world"));' + - '});' + '}' ); }); }); diff --git a/test/integration/transpiler/returnTest.js b/test/integration/transpiler/returnTest.js index 43e8c3b..b2d4ee2 100644 --- a/test/integration/transpiler/returnTest.js +++ b/test/integration/transpiler/returnTest.js @@ -21,10 +21,10 @@ describe('Transpiler "return" statement test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'return;' + - '});' + '}' ); }); @@ -40,11 +40,11 @@ describe('Transpiler "return" statement test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var createInteger = core.createInteger;' + 'return createInteger(4);' + - '});' + '}' ); }); diff --git a/test/integration/transpiler/scopeResolutionOperatorTest.js b/test/integration/transpiler/scopeResolutionOperatorTest.js index 01fc9c7..35987b2 100644 --- a/test/integration/transpiler/scopeResolutionOperatorTest.js +++ b/test/integration/transpiler/scopeResolutionOperatorTest.js @@ -13,7 +13,7 @@ var expect = require('chai').expect, phpToJS = require('../../..'); describe('Transpiler scope resolution operator test', function () { - it('should correctly transpile a return statement with static class constant read in default (async) mode', function () { + it('should correctly transpile a return statement with static class constant read', function () { var ast = { name: 'N_PROGRAM', statements: [{ @@ -29,11 +29,11 @@ describe('Transpiler scope resolution operator test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var createBareword = core.createBareword, getClassConstant = core.getClassConstant;' + - 'return getClassConstant(createBareword("MyClass"))("MY_CONST");' + - '});' + 'return getClassConstant(createBareword("MyClass"), "MY_CONST");' + + '}' ); }); }); diff --git a/test/integration/transpiler/sourceMapTest.js b/test/integration/transpiler/sourceMapTest.js index fd3c326..f377b16 100644 --- a/test/integration/transpiler/sourceMapTest.js +++ b/test/integration/transpiler/sourceMapTest.js @@ -326,6 +326,7 @@ describe('Transpiler source map test', function () { } }, options = { + bare: true, path: 'my_module.php', sourceMap: { sourceContent: 'My inline HTML

");' + - '});' + '}' ); }); }); diff --git a/test/integration/transpiler/statements/interfaceTest.js b/test/integration/transpiler/statements/interfaceTest.js index 133ff4c..0256d37 100644 --- a/test/integration/transpiler/statements/interfaceTest.js +++ b/test/integration/transpiler/statements/interfaceTest.js @@ -15,7 +15,7 @@ var expect = require('chai').expect, PHPFatalError = phpCommon.PHPFatalError; describe('Transpiler interface statement test', function () { - it('should correctly transpile an interface in default (async) mode', function () { + it('should correctly transpile an interface', function () { var ast = { name: 'N_PROGRAM', statements: [{ @@ -79,8 +79,8 @@ describe('Transpiler interface statement test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var createString = core.createString, defineInterface = core.defineInterface;' + 'defineInterface("Thing", {' + 'superClass: null, ' + @@ -93,11 +93,12 @@ describe('Transpiler interface statement test', function () { '}, ' + 'constants: {' + // TODO: Prevent unnecessary currentClass parameter from being output when not referenced + // (not urgent as minifiers/optimisers should remove). '"SHAPE_ONE": function (currentClass) { return createString("sphere"); }, ' + '"SHAPE_TWO": function (currentClass) { return createString("circle"); }' + '}' + '});' + - '});' + '}' ); }); diff --git a/test/integration/transpiler/statements/labelTest.js b/test/integration/transpiler/statements/labelTest.js new file mode 100644 index 0000000..2bb6a86 --- /dev/null +++ b/test/integration/transpiler/statements/labelTest.js @@ -0,0 +1,37 @@ +/* + * PHPToJS - PHP-to-JavaScript transpiler + * Copyright (c) Dan Phillimore (asmblah) + * https://github.com/uniter/phptojs + * + * Released under the MIT license + * https://github.com/uniter/phptojs/raw/master/MIT-LICENSE.txt + */ + +'use strict'; + +var expect = require('chai').expect, + phpToJS = require('../../../..'); + +describe('Transpiler label statement test', function () { + it('should correctly transpile a lone label', function () { + var ast = { + name: 'N_PROGRAM', + statements: [{ + name: 'N_LABEL_STATEMENT', + label: { + name: 'N_STRING', + string: 'my_goto_label' + } + }] + }; + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + + // TODO: No need to output anything at all in this scenario, + // as there is no goto referencing the label. + 'var goingToLabel_my_goto_label = false;' + + 'goingToLabel_my_goto_label = false;' + + '}' + ); + }); +}); diff --git a/test/integration/transpiler/statements/namespaceTest.js b/test/integration/transpiler/statements/namespaceTest.js index 6299fdd..eeeca86 100644 --- a/test/integration/transpiler/statements/namespaceTest.js +++ b/test/integration/transpiler/statements/namespaceTest.js @@ -42,18 +42,18 @@ describe('Transpiler namespace statement test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var createInteger = core.createInteger, print = core.print, useDescendantNamespaceScope = core.useDescendantNamespaceScope;' + - // First namespace scope + // First namespace scope. 'useDescendantNamespaceScope("This\\\\Is\\\\My\\\\FirstSpace");' + 'print(createInteger(1234));' + - // Second namespace scope + // Second namespace scope. 'useDescendantNamespaceScope("This\\\\Is\\\\My\\\\SecondSpace");' + 'return createInteger(9876);' + - '});' + '}' ); }); @@ -89,18 +89,18 @@ describe('Transpiler namespace statement test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var createInteger = core.createInteger, print = core.print, useDescendantNamespaceScope = core.useDescendantNamespaceScope, useGlobalNamespaceScope = core.useGlobalNamespaceScope;' + - // First namespace scope + // First namespace scope. 'useGlobalNamespaceScope();' + 'print(createInteger(1234));' + - // Second namespace scope + // Second namespace scope. 'useDescendantNamespaceScope("This\\\\Is\\\\My\\\\SubSpace");' + 'print(createInteger(6543));' + - '});' + '}' ); }); @@ -136,18 +136,18 @@ describe('Transpiler namespace statement test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var createInteger = core.createInteger, print = core.print, useDescendantNamespaceScope = core.useDescendantNamespaceScope, useGlobalNamespaceScope = core.useGlobalNamespaceScope;' + - // First namespace scope + // First namespace scope. 'useDescendantNamespaceScope("This\\\\Is\\\\My\\\\SubSpace");' + 'print(createInteger(6543));' + - // Second namespace scope + // Second namespace scope. 'useGlobalNamespaceScope();' + 'print(createInteger(1234));' + - '});' + '}' ); }); @@ -208,11 +208,11 @@ describe('Transpiler namespace statement test', function () { 'function (core) {' + 'var createInteger = core.createInteger, defineClass = core.defineClass, getNamespaceName = core.getNamespaceName, print = core.print, useDescendantNamespaceScope = core.useDescendantNamespaceScope;' + - // First namespace scope + // First namespace scope. 'useDescendantNamespaceScope("Your\\\\Space");' + 'print(createInteger(1234));' + - // Second namespace scope + // Second namespace scope. 'useDescendantNamespaceScope("This\\\\Is\\\\My\\\\Space");' + 'defineClass("MyClass", {superClass: null, interfaces: [], staticProperties: {}, properties: {}, methods: {' + '"getClass": {' + diff --git a/test/integration/transpiler/statements/staticTest.js b/test/integration/transpiler/statements/staticTest.js index 0007e08..af54795 100644 --- a/test/integration/transpiler/statements/staticTest.js +++ b/test/integration/transpiler/statements/staticTest.js @@ -56,8 +56,8 @@ describe('Transpiler static variable scope statement test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var createInteger = core.createInteger, defineFunction = core.defineFunction, importStatic = core.importStatic;' + 'defineFunction("myFunc", function _myFunc() {' + 'importStatic("firstVar");' + @@ -65,7 +65,7 @@ describe('Transpiler static variable scope statement test', function () { '}, [' + '{"type":"callable","name":"myArg"}' + ']);' + - '});' + '}' ); }); }); diff --git a/test/integration/transpiler/statements/switchTest.js b/test/integration/transpiler/statements/switchTest.js index 051f277..59cde68 100644 --- a/test/integration/transpiler/statements/switchTest.js +++ b/test/integration/transpiler/statements/switchTest.js @@ -94,24 +94,24 @@ describe('Transpiler "switch" statement test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var add = core.add, createInteger = core.createInteger, getVariable = core.getVariable, setValue = core.setValue, switchCase = core.switchCase, switchOn = core.switchOn;' + - 'var switchExpression_1 = switchOn(add(createInteger(21))(createInteger(6))), ' + + 'var switchExpression_1 = switchOn(add(createInteger(21), createInteger(6))), ' + 'switchMatched_1 = false;' + 'block_1: {' + 'if (switchMatched_1 || switchCase(switchExpression_1, createInteger(27))) {' + 'switchMatched_1 = true;' + - 'setValue(getVariable("a"))(createInteger(7));' + + 'setValue(getVariable("a"), createInteger(7));' + 'break block_1;' + '}' + 'if (switchMatched_1 || switchCase(switchExpression_1, createInteger(101))) {' + 'switchMatched_1 = true;' + - 'setValue(getVariable("a"))(createInteger(10));' + + 'setValue(getVariable("a"), createInteger(10));' + 'break block_1;' + '}' + '}' + - '});' + '}' ); }); @@ -186,21 +186,21 @@ describe('Transpiler "switch" statement test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var add = core.add, createInteger = core.createInteger, getVariable = core.getVariable, setValue = core.setValue, switchCase = core.switchCase, switchOn = core.switchOn;' + - 'var switchExpression_1 = switchOn(add(createInteger(21))(createInteger(6))), ' + + 'var switchExpression_1 = switchOn(add(createInteger(21), createInteger(6))), ' + 'switchMatched_1 = false;' + 'block_1: {' + 'if (switchMatched_1 || switchCase(switchExpression_1, createInteger(27))) {' + 'switchMatched_1 = true;' + - 'setValue(getVariable("a"))(createInteger(7));' + + 'setValue(getVariable("a"), createInteger(7));' + 'break block_1;' + '}' + 'switchMatched_1 = true;' + - 'setValue(getVariable("a"))(createInteger(8));' + + 'setValue(getVariable("a"), createInteger(8));' + '}' + - '});' + '}' ); }); @@ -304,29 +304,29 @@ describe('Transpiler "switch" statement test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var add = core.add, createInteger = core.createInteger, getVariable = core.getVariable, setValue = core.setValue, switchCase = core.switchCase, switchDefault = core.switchDefault, switchOn = core.switchOn;' + - 'var switchExpression_1 = switchOn(add(createInteger(21))(createInteger(6))), ' + + 'var switchExpression_1 = switchOn(add(createInteger(21), createInteger(6))), ' + 'switchMatched_1 = false;' + 'block_1: while (true) {' + 'if (switchMatched_1 || switchCase(switchExpression_1, createInteger(101))) {' + 'switchMatched_1 = true;' + - 'setValue(getVariable("a"))(createInteger(7));' + + 'setValue(getVariable("a"), createInteger(7));' + 'break block_1;' + '}' + 'if (switchMatched_1 || switchDefault(switchExpression_1)) {' + 'switchMatched_1 = true;' + - 'setValue(getVariable("a"))(createInteger(8));' + + 'setValue(getVariable("a"), createInteger(8));' + '}' + 'if (switchMatched_1 || switchCase(switchExpression_1, createInteger(27))) {' + 'switchMatched_1 = true;' + - 'setValue(getVariable("a"))(createInteger(1001));' + + 'setValue(getVariable("a"), createInteger(1001));' + 'break block_1;' + '}' + 'if (switchMatched_1) {break;} else {switchExpression_1 = null;}' + '}' + - '});' + '}' ); }); }); diff --git a/test/integration/transpiler/statements/throwTest.js b/test/integration/transpiler/statements/throwTest.js index b0d9524..6275b8c 100644 --- a/test/integration/transpiler/statements/throwTest.js +++ b/test/integration/transpiler/statements/throwTest.js @@ -25,11 +25,11 @@ describe('Transpiler throw statement test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var getVariable = core.getVariable, throw_ = core.throw_;' + 'throw_(getVariable("myError"));' + - '});' + '}' ); }); }); diff --git a/test/integration/transpiler/statements/tryTest.js b/test/integration/transpiler/statements/tryTest.js index 7533840..834a2f6 100644 --- a/test/integration/transpiler/statements/tryTest.js +++ b/test/integration/transpiler/statements/tryTest.js @@ -50,18 +50,18 @@ describe('Transpiler try statement test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var callFunction = core.callFunction, pausing = core.pausing;' + 'try {' + - 'callFunction("myFunc")();' + + 'callFunction("myFunc");' + '} finally {' + // Skip finally clause if we're pausing. 'if (!pausing()) {' + - 'callFunction("yourFunc")();' + + 'callFunction("yourFunc");' + '}' + '}' + - '});' + '}' ); }); @@ -135,24 +135,24 @@ describe('Transpiler try statement test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var callFunction = core.callFunction, caught = core.caught, getVariable = core.getVariable, pausing = core.pausing, setValue = core.setValue;' + 'try {' + - 'callFunction("myFunc")();' + + 'callFunction("myFunc");' + '} catch (e) {' + // Re-throw the error if we're pausing. 'if (pausing()) {throw e;} ' + 'if (caught("My\\\\Exception\\\\Type", e)) {' + 'setValue(getVariable("ex1"), e);' + - 'callFunction("catchFunc1")();' + + 'callFunction("catchFunc1");' + '} else if (caught("Another\\\\Exception\\\\Type", e)) {' + 'setValue(getVariable("ex2"), e);' + - 'callFunction("catchFunc1")();' + + 'callFunction("catchFunc1");' + // Rethrow if none of the catch guards matched, as the throwable was not caught. '} else { throw e; }' + '}' + - '});' + '}' ); }); @@ -239,29 +239,29 @@ describe('Transpiler try statement test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var callFunction = core.callFunction, caught = core.caught, getVariable = core.getVariable, pausing = core.pausing, setValue = core.setValue;' + 'try {' + - 'callFunction("myFunc")();' + + 'callFunction("myFunc");' + '} catch (e) {' + // Re-throw the error if we're pausing. 'if (pausing()) {throw e;} ' + 'if (caught("My\\\\Exception\\\\Type", e)) {' + 'setValue(getVariable("ex1"), e);' + - 'callFunction("catchFunc1")();' + + 'callFunction("catchFunc1");' + '} else if (caught("Another\\\\Exception\\\\Type", e)) {' + 'setValue(getVariable("ex2"), e);' + - 'callFunction("catchFunc1")();' + + 'callFunction("catchFunc1");' + // Rethrow if none of the catch guards matched, as the throwable was not caught. '} else { throw e; }' + '} finally {' + // Skip finally clause if we're pausing. 'if (!pausing()) {' + - 'callFunction("yourFunc")();' + + 'callFunction("yourFunc");' + '}' + '}' + - '});' + '}' ); }); @@ -313,8 +313,8 @@ describe('Transpiler try statement test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var caught = core.caught, createString = core.createString, getVariable = core.getVariable, pausing = core.pausing, setValue = core.setValue, tryReturn = core.tryReturn;' + 'try {' + 'return tryReturn(createString("from try"));' + @@ -332,7 +332,7 @@ describe('Transpiler try statement test', function () { 'return createString("from finally");' + '}' + '}' + - '});' + '}' ); }); }); diff --git a/test/integration/transpiler/statements/useStatementTest.js b/test/integration/transpiler/statements/useStatementTest.js index 8531bb6..3db2471 100644 --- a/test/integration/transpiler/statements/useStatementTest.js +++ b/test/integration/transpiler/statements/useStatementTest.js @@ -28,13 +28,13 @@ describe('Transpiler use statement test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var useClass = core.useClass, useDescendantNamespaceScope = core.useDescendantNamespaceScope;' + 'useDescendantNamespaceScope("This\\\\Is\\\\My\\\\Space");' + 'useClass("My\\\\Imported\\\\MyClass");' + - '});' + '}' ); }); @@ -54,13 +54,13 @@ describe('Transpiler use statement test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var useClass = core.useClass, useDescendantNamespaceScope = core.useDescendantNamespaceScope;' + 'useDescendantNamespaceScope("This\\\\Is\\\\My\\\\Space");' + 'useClass("My\\\\Imported\\\\MyClass", "MyAlias");' + - '});' + '}' ); }); }); diff --git a/test/integration/transpiler/statements/whileTest.js b/test/integration/transpiler/statements/whileTest.js index e7e6e93..877042e 100644 --- a/test/integration/transpiler/statements/whileTest.js +++ b/test/integration/transpiler/statements/whileTest.js @@ -45,13 +45,13 @@ describe('Transpiler "while" statement test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var createInteger = core.createInteger, echo = core.echo, isGreaterThan = core.isGreaterThan, loop = core.loop;' + - 'block_1: while (loop(0, isGreaterThan(createInteger(27))(createInteger(21)))) {' + + 'block_1: while (loop(0, isGreaterThan(createInteger(27), createInteger(21)))) {' + 'echo(createInteger(4));' + '}' + - '});' + '}' ); }); @@ -109,8 +109,8 @@ describe('Transpiler "while" statement test', function () { }] }; - expect(phpToJS.transpile(ast)).to.equal( - 'require(\'phpruntime\').compile(function (core) {' + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + 'var createString = core.createString, echo = core.echo, getVariable = core.getVariable, loop = core.loop;' + 'block_1: while (loop(0, getVariable("firstVar"))) {' + 'echo(createString("first"));' + @@ -121,7 +121,7 @@ describe('Transpiler "while" statement test', function () { 'block_1: while (loop(2, getVariable("thirdVar"))) {' + 'echo(createString("third"));' + '}' + - '});' + '}' ); }); }); diff --git a/test/integration/transpiler/statements/yieldTest.js b/test/integration/transpiler/statements/yieldTest.js index 83d20c9..2a5574f 100644 --- a/test/integration/transpiler/statements/yieldTest.js +++ b/test/integration/transpiler/statements/yieldTest.js @@ -17,6 +17,116 @@ var expect = require('chai').expect, describe('Transpiler "yield" statement test', function () { // See also test/integration/transpiler/statements/function/generatorTest.js. + it('should snapshot the key when given as a non-literal and value is complex', function () { + var ast = { + name: 'N_PROGRAM', + statements: [{ + name: 'N_FUNCTION_STATEMENT', + func: { + name: 'N_STRING', + string: 'myGenerator' + }, + generator: true, + args: [], + body: { + name: 'N_COMPOUND_STATEMENT', + statements: [{ + name: 'N_EXPRESSION_STATEMENT', + expression: { + name: 'N_YIELD_EXPRESSION', + key: { + name: 'N_VARIABLE', + variable: 'myKey' + }, + value: { + name: 'N_TERNARY', + condition: { + name: 'N_VARIABLE', + variable: 'myCondition' + }, + consequent: { + name: 'N_STRING_LITERAL', + string: 'my value if truthy' + }, + alternate: { + name: 'N_STRING_LITERAL', + string: 'my value if falsy' + } + } + } + }] + } + }] + }; + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + + 'var createString = core.createString, defineFunction = core.defineFunction, getVariable = core.getVariable, snapshot = core.snapshot, ternary = core.ternary, wrapGenerator = core.wrapGenerator, yieldWithKey = core.yieldWithKey;' + + 'defineFunction("myGenerator", wrapGenerator(function _myGenerator() {' + + 'yieldWithKey(' + + 'snapshot(getVariable("myKey")), ' + + '(ternary(getVariable("myCondition")) ? createString("my value if truthy") : createString("my value if falsy"))' + + ');' + + '}));' + + '}' + ); + }); + + it('should not snapshot the key when given and value is complex but key is a literal', function () { + var ast = { + name: 'N_PROGRAM', + statements: [{ + name: 'N_FUNCTION_STATEMENT', + func: { + name: 'N_STRING', + string: 'myGenerator' + }, + generator: true, + args: [], + body: { + name: 'N_COMPOUND_STATEMENT', + statements: [{ + name: 'N_EXPRESSION_STATEMENT', + expression: { + name: 'N_YIELD_EXPRESSION', + key: { + name: 'N_STRING_LITERAL', + string: 'my key' + }, + value: { + name: 'N_TERNARY', + condition: { + name: 'N_VARIABLE', + variable: 'myCondition' + }, + consequent: { + name: 'N_STRING_LITERAL', + string: 'my value if truthy' + }, + alternate: { + name: 'N_STRING_LITERAL', + string: 'my value if falsy' + } + } + } + }] + } + }] + }; + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + + 'var createString = core.createString, defineFunction = core.defineFunction, getVariable = core.getVariable, ternary = core.ternary, wrapGenerator = core.wrapGenerator, yieldWithKey = core.yieldWithKey;' + + 'defineFunction("myGenerator", wrapGenerator(function _myGenerator() {' + + 'yieldWithKey(' + + 'createString("my key"), ' + + '(ternary(getVariable("myCondition")) ? createString("my value if truthy") : createString("my value if falsy"))' + + ');' + + '}));' + + '}' + ); + }); + it('should throw when a yield statement is used at the top level of a module', function () { var ast = { name: 'N_PROGRAM', diff --git a/test/integration/transpiler/types/arrayTypeTest.js b/test/integration/transpiler/types/arrayTypeTest.js new file mode 100644 index 0000000..66fa8db --- /dev/null +++ b/test/integration/transpiler/types/arrayTypeTest.js @@ -0,0 +1,45 @@ +/* + * PHPToJS - PHP-to-JavaScript transpiler + * Copyright (c) Dan Phillimore (asmblah) + * https://github.com/uniter/phptojs + * + * Released under the MIT license + * https://github.com/uniter/phptojs/raw/master/MIT-LICENSE.txt + */ + +'use strict'; + +var expect = require('chai').expect, + phpToJS = require('../../../..'); + +describe('Transpiler array type test', function () { + it('should correctly transpile an empty function with array return type', function () { + var ast = { + name: 'N_PROGRAM', + statements: [{ + name: 'N_FUNCTION_STATEMENT', + func: { + name: 'N_STRING', + string: 'gogo' + }, + args: [], + body: { + name: 'N_COMPOUND_STATEMENT', + statements: [] + }, + returnType: { + name: 'N_ARRAY_TYPE' + } + }] + }; + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + + 'var defineFunction = core.defineFunction;' + + 'defineFunction("gogo", function _gogo() {}, [], ' + + '{"type":"array"}' + + ');' + + '}' + ); + }); +}); diff --git a/test/integration/transpiler/types/iterableTypeTest.js b/test/integration/transpiler/types/iterableTypeTest.js new file mode 100644 index 0000000..644e3ad --- /dev/null +++ b/test/integration/transpiler/types/iterableTypeTest.js @@ -0,0 +1,45 @@ +/* + * PHPToJS - PHP-to-JavaScript transpiler + * Copyright (c) Dan Phillimore (asmblah) + * https://github.com/uniter/phptojs + * + * Released under the MIT license + * https://github.com/uniter/phptojs/raw/master/MIT-LICENSE.txt + */ + +'use strict'; + +var expect = require('chai').expect, + phpToJS = require('../../../..'); + +describe('Transpiler iterable type test', function () { + it('should correctly transpile an empty function with iterable return type', function () { + var ast = { + name: 'N_PROGRAM', + statements: [{ + name: 'N_FUNCTION_STATEMENT', + func: { + name: 'N_STRING', + string: 'gogo' + }, + args: [], + body: { + name: 'N_COMPOUND_STATEMENT', + statements: [] + }, + returnType: { + name: 'N_ITERABLE_TYPE' + } + }] + }; + + expect(phpToJS.transpile(ast, {bare: true})).to.equal( + 'function (core) {' + + 'var defineFunction = core.defineFunction;' + + 'defineFunction("gogo", function _gogo() {}, [], ' + + '{"type":"iterable"}' + + ');' + + '}' + ); + }); +});