forked from facebook/hermes
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add REPL features to Hermes CLI tool
Reviewed By: avp Differential Revision: D20996738 fbshipit-source-id: 5e7652737daf4db2681c23752e08748f8e9d75e6
- Loading branch information
1 parent
f13566d
commit db7344d
Showing
6 changed files
with
689 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
/* | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
|
||
// This file is processed by CMake. | ||
// See https://cmake.org/cmake/help/v3.0/command/configure_file.html. | ||
|
||
#ifndef REPLCONFIG_H | ||
#define REPLCONFIG_H | ||
|
||
/* | ||
* These values are automatically set according to their cmake variables. | ||
*/ | ||
#cmakedefine HAVE_LIBREADLINE 1 | ||
|
||
#endif // REPLCONFIG_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,242 @@ | ||
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
|
||
/// Line evaluator for the REPL. | ||
/// Runs a line of JS input and pretty-prints the output. | ||
/// This is included into C++ file as a string literal at compilation time. | ||
C_STRING((function() { | ||
var colors = {}; | ||
function populateColors() { | ||
colors.red = '\033[31m'; | ||
colors.green = '\033[32m'; | ||
colors.yellow = '\033[33m'; | ||
colors.blue = '\033[34m'; | ||
colors.magenta = '\033[35m'; | ||
colors.cyan = '\033[36m'; | ||
colors.white = '\033[37m'; | ||
|
||
colors.reset = '\033[0m'; | ||
} | ||
|
||
function clearColors() { | ||
colors.red = ""; | ||
colors.green = ""; | ||
colors.yellow = ""; | ||
colors.blue = ""; | ||
colors.magenta = ""; | ||
colors.cyan = ""; | ||
colors.white = ""; | ||
|
||
colors.reset = ""; | ||
} | ||
|
||
function prettyPrintProp(value, prop, visited) { | ||
var desc = Object.getOwnPropertyDescriptor(value, prop); | ||
var result = ""; | ||
if (desc.enumerable) { | ||
result += String(prop) + ': '; | ||
} else { | ||
result += '[' + String(prop) + ']: '; | ||
} | ||
if (desc.get || desc.set) { | ||
result += colors.cyan + '[accessor]' + colors.reset; | ||
} else { | ||
result += prettyPrintRec(desc.value, visited); | ||
} | ||
return result; | ||
} | ||
|
||
function prettyPrintRec(value, visited) { | ||
// First, check for cycles. | ||
if (visited.has(value)) { | ||
return colors.magenta + '[cyclic]' + colors.reset; | ||
} | ||
|
||
switch (typeof value) { | ||
case "undefined": | ||
return colors.white + "undefined" + colors.reset; | ||
case "number": | ||
return colors.yellow + String(value) + colors.reset; | ||
case "string": | ||
// Wrap strings in quotes so we their type. | ||
return colors.green + '"' + value + '"' + colors.reset; | ||
case "symbol": | ||
return colors.green + String(value) + colors.reset; | ||
case "boolean": | ||
return colors.yellow + String(value) + colors.reset; | ||
case "function": | ||
// Default Function.prototype.toString doesn't look very good nested. | ||
var functionColor = colors.cyan; | ||
if (visited.size === 0) { | ||
return functionColor + String(value) + colors.reset; | ||
} | ||
if (!value.name) { | ||
return functionColor + '[Function]' + colors.reset; | ||
} | ||
return functionColor + '[Function ' + value.name + ']' + colors.reset; | ||
} | ||
|
||
if (value === null) { | ||
return colors.white + 'null' + colors.reset; | ||
} | ||
|
||
// We know this is an object, so add it to the visited set. | ||
visited.add(value); | ||
|
||
if (Array.isArray(value)) { | ||
// Print array using square brackets. | ||
var length = value.length; | ||
var elements = []; | ||
var numEmpty = 0; | ||
for (var i = 0; i < length; ++i) { | ||
// First, handle the indexed properties of at most length. | ||
if (!value.hasOwnProperty(i)) { | ||
// No property here, just an empty slot. | ||
++numEmpty; | ||
} else { | ||
if (numEmpty > 0) { | ||
elements.push( | ||
colors.white + numEmpty + ' x <empty>' + colors.reset); | ||
} | ||
numEmpty = 0; | ||
if (value.propertyIsEnumerable(i)) { | ||
elements.push(prettyPrintRec(value[i], visited)); | ||
} else { | ||
elements.push( | ||
'[' + String(i) + ']: ' + prettyPrintRec(value[i], visited)); | ||
} | ||
} | ||
} | ||
if (numEmpty > 0) { | ||
elements.push( | ||
colors.white + numEmpty + ' x <empty>' + colors.reset); | ||
} | ||
var propNames = Object.getOwnPropertyNames(value); | ||
for (var i = 0; i < propNames.length; ++i) { | ||
// Handle other stored properties, and show their names. | ||
var prop = propNames[i]; | ||
if (isNaN(Number(prop))) { | ||
elements.push(prettyPrintProp(value, prop, visited)); | ||
} | ||
} | ||
return '[ ' + elements.join(', ') + ' ]'; | ||
} | ||
|
||
if (value instanceof RegExp) { | ||
return colors.green + value.toString() + colors.reset; | ||
} | ||
|
||
if (value instanceof Set) { | ||
var elementStrings = []; | ||
value.forEach(function(element) { | ||
elementStrings.push(prettyPrintRec(element, visited)); | ||
}); | ||
return "Set { " + elementStrings.join(", ") + " }"; | ||
} | ||
|
||
if (value instanceof Map) { | ||
var elementStrings = []; | ||
value.forEach(function(v, k) { | ||
elementStrings.push( | ||
prettyPrintRec(k, visited) + " => " + prettyPrintRec(v, visited) | ||
); | ||
}); | ||
return "Map { " + elementStrings.join(", ") + " }"; | ||
} | ||
|
||
if (value instanceof Date) { | ||
var isValid = !isNaN(value.getTime()); | ||
return colors.cyan + "[Date " + | ||
(isValid ? value.toISOString() : "Invalid") + | ||
"]" + colors.reset; | ||
} | ||
|
||
function isTypedArray(val) { | ||
return val instanceof Int8Array || | ||
val instanceof Int16Array || | ||
val instanceof Int32Array || | ||
val instanceof Uint8Array || | ||
val instanceof Uint16Array || | ||
val instanceof Uint32Array || | ||
val instanceof Float32Array || | ||
val instanceof Float64Array; | ||
} | ||
|
||
if (isTypedArray(value)) { | ||
var elementStrings = []; | ||
value.forEach(function(i) { | ||
elementStrings.push(prettyPrintRec(i, visited)); | ||
}); | ||
return value.constructor.name + " [ " + elementStrings.join(", ") + " ]"; | ||
} | ||
|
||
// Regular object. Print out its properties directly as a literal. | ||
var elements = []; | ||
var propNames = Object.getOwnPropertyNames(value); | ||
for (var i = 0; i < propNames.length; ++i) { | ||
var prop = propNames[i]; | ||
elements.push(prettyPrintProp(value, prop, visited)); | ||
} | ||
if (Object.getOwnPropertySymbols) { | ||
var propSymbols = Object.getOwnPropertySymbols(value); | ||
for (var i = 0; i < propSymbols.length; ++i) { | ||
var prop = propSymbols[i]; | ||
elements.push(prettyPrintProp(value, prop, visited)); | ||
} | ||
} | ||
if (value.constructor && value.constructor.name && value.constructor.name !== "Object") { | ||
return value.constructor.name + ' { ' + elements.join(', ') + ' }'; | ||
} else { | ||
return '{ ' + elements.join(', ') + ' }'; | ||
} | ||
|
||
} | ||
|
||
function prettyPrint(value, isColored) { | ||
isColored ? populateColors() : clearColors(); | ||
return prettyPrintRec(value, new Set()); | ||
} | ||
|
||
var singleCommentPattern = new RegExp("^//"); | ||
var multiCommentPattern = new RegExp("^/\\*.*\\*/$"); | ||
|
||
/// Evaluates the specified line for the REPL. | ||
/// Returns a pretty-printed string of the result, | ||
/// and undefined if the input is empty or just a comment. | ||
function evaluateLine(input, isColored) { | ||
var output; | ||
var trimmed = input.trim(); | ||
if (trimmed.length === 0) { | ||
// Input is empty, return early. | ||
return undefined; | ||
} | ||
if (singleCommentPattern.test(trimmed) || | ||
multiCommentPattern.test(trimmed)) { | ||
// Input consists only of a comment, return early. | ||
return undefined; | ||
} | ||
// Use (1, eval) to run indirect eval (global eval) and allow | ||
// var declaration. | ||
if (trimmed[0] === '{' && trimmed[trimmed.length - 1] === '}') { | ||
try { | ||
// The input starts with { and ends with }, so try wrap with ( and ). | ||
output = (1, eval)('(' + input + ')'); | ||
} catch (e) { | ||
// Wrapping the input failed, so just fall back to regular eval. | ||
output = (1, eval)(input); | ||
} | ||
} else { | ||
// Can't be mistaken for a block, so just use regular eval. | ||
output = (1, eval)(input); | ||
} | ||
|
||
// Otherwise, just run eval directly. | ||
return prettyPrint(output, isColored); | ||
} | ||
|
||
return evaluateLine; | ||
})()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.