aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/jsifier.js23
-rw-r--r--src/preamble.js6
-rw-r--r--tools/eliminator/eliminator.coffee82
-rw-r--r--tools/shared.py2
4 files changed, 62 insertions, 51 deletions
diff --git a/src/jsifier.js b/src/jsifier.js
index 3da296f1..1e02555f 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -277,19 +277,18 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) {
snippet = '_' + snippet;
}
} else if (typeof snippet === 'object') {
- // JSON.stringify removes functions, so we need to make sure they are added
- var funcs = [];
- var hasNonFunc = false;
- for (var x in snippet) {
- if (typeof snippet[x] === 'function') {
- funcs.push(x + ': ' + snippet[x].toString());
- } else {
- hasNonFunc = true;
+ if (snippet === null) {
+ snippet = 'null';
+ } else {
+ var members = [];
+ for (var property in snippet) {
+ if (typeof snippet[property] === 'function') {
+ members.push(property + ': ' + snippet[property].toString());
+ } else {
+ members.push(property + ': ' + JSON.stringify(snippet[property]));
+ }
}
- }
- snippet = JSON.stringify(snippet);
- if (funcs.length > 0) {
- snippet = snippet.replace(/}$/, (hasNonFunc ? ', ' : '') + funcs.join(', ') + ' }');
+ snippet = '{' + members.join(', ') + ' }';
}
} else if (typeof snippet === 'function') {
snippet = snippet.toString();
diff --git a/src/preamble.js b/src/preamble.js
index 2cbd9cf0..64b3e222 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -398,6 +398,7 @@ function Pointer_stringify(ptr) {
}
return ret;
}
+Module['Pointer_stringify'] = Pointer_stringify;
function Array_stringify(array) {
var ret = "";
@@ -406,6 +407,7 @@ function Array_stringify(array) {
}
return ret;
}
+Module['Array_stringify'] = Array_stringify;
// Memory management
@@ -537,12 +539,14 @@ function Array_copy(ptr, num) {
#endif
return HEAP.slice(ptr, ptr+num);
}
+Module['Array_copy'] = Array_copy;
function String_len(ptr) {
var i = 0;
while ({{{ makeGetValue('ptr', 'i', 'i8') }}}) i++; // Note: should be |!= 0|, technically. But this helps catch bugs with undefineds
return i;
}
+Module['String_len'] = String_len;
// Copies a C-style string, terminated by a zero, from the HEAP into
// a normal JavaScript array of numbers
@@ -553,6 +557,7 @@ function String_copy(ptr, addZero) {
if (addZero) ret[len-1] = 0;
return ret;
}
+Module['String_copy'] = String_copy;
// Tools
@@ -598,6 +603,7 @@ function intArrayToString(array) {
}
return ret.join('');
}
+Module['intArrayToString'] = intArrayToString;
{{{ unSign }}}
{{{ reSign }}}
diff --git a/tools/eliminator/eliminator.coffee b/tools/eliminator/eliminator.coffee
index 260ed289..242394b7 100644
--- a/tools/eliminator/eliminator.coffee
+++ b/tools/eliminator/eliminator.coffee
@@ -4,17 +4,17 @@
A variable is eliminateable if it matches a leaf of this condition tree:
Single-def
- Single-use
- Uses only simple nodes
+ Uses only side-effect-free nodes
+ Single-use
Uses only local, single-def names
*
Uses non-local or non-single-def names
No flow-controlling statements between def and use
No references to any deps between def and use
- *
- Multi-use
- Uses only simple nodes
- Uses only single-def names
+ No indirect accesses (subscript, dot notation) between def and use
+ *
+ Multi-use
+ Uses only local, single-def names
*
TODO(max99x): Eliminate single-def undefined-initialized vars with no uses
@@ -25,15 +25,6 @@
uglify = require 'uglify-js'
fs = require 'fs'
-# Node types which can be evaluated without side effects.
-SIMPLE_NODES =
- name: true
- num: true
- string: true
- binary: true
- sub: true
- string: true
-
# Maximum number of uses to consider a variable not worth eliminating.
MAX_USES = 3
@@ -43,15 +34,32 @@ GEN_OPTIONS =
beautify: true
indent_level: 2
+# Node types which can be evaluated without side effects.
+NODES_WITHOUT_SIDE_EFFECTS =
+ name: true
+ num: true
+ string: true
+ binary: true
+ sub: true
+
+# Nodes which may alter control flow.
+CONTROL_FLOW_NODES =
+ return: true
+ break: true
+ continue: true
+ new: true
+ call: true
+ label: true
+
# Traverses a JavaScript syntax tree rooted at the given node calling the given
# callback for each node.
# @arg node: The root of the AST.
# @arg callback: The callback to call for each node. This will be called with
-# the node as the first argument and its type as the second. If a
-# non-undefined value is returned, it replaces the passed node in the tree.
-# If false is returned, the traversal is stopped.
-# @returns: If the root node was replaced, the new root node. Otherwise
-# undefined.
+# the node as the first argument and its type as the second. If false is
+# returned, the traversal is stopped. If a non-undefined value is returned,
+# it replaces the passed node in the tree.
+# @returns: If the root node was replaced, the new root node. If the traversal
+# was stopped, false. Otherwise undefined.
traverse = (node, callback) ->
type = node[0]
if type
@@ -60,7 +68,7 @@ traverse = (node, callback) ->
for subnode, index in node
if typeof subnode is 'object' and subnode?.length
- # NOTE: For-in nodes have unspecified var mutations. Leave them alone.
+ # NOTE: For-in nodes have unspecified var mutations. Skip them.
if type == 'for-in' and subnode?[0] == 'var' then continue
subresult = traverse subnode, callback
if subresult is false
@@ -75,6 +83,8 @@ class Eliminator
constructor: (func) ->
# The statements of the function to analyze.
@body = func[3]
+
+ # Identifier stats. Each of these objects is indexed by the identifier name.
# Whether the identifier is never modified after initialization.
@isSingleDef = {}
# How many times the identifier is used.
@@ -147,8 +157,7 @@ class Eliminator
if varName of @useCount then @useCount[varName]++
else if type in ['assign', 'unary-prefix', 'unary-postfix']
varName = node[2][1]
- if @isSingleDef.hasOwnProperty varName
- @isSingleDef[varName] = false
+ if @isSingleDef[varName] then @isSingleDef[varName] = false
return undefined
return undefined
@@ -163,7 +172,7 @@ class Eliminator
@usesOnlySimpleNodes[varName] = true
@usesOnlySingleDefs[varName] = true
traverse @initialValue[varName], (node, type) =>
- if type not of SIMPLE_NODES
+ if type not of NODES_WITHOUT_SIDE_EFFECTS
@usesOnlySimpleNodes[varName] = false
else if type is 'name'
reference = node[1]
@@ -193,12 +202,11 @@ class Eliminator
# Analyzes the live ranges of single-def single-use variables. Requires
# dependencies to have been calculated. Fills the following member variables:
# depsMutatedInLiveRange
- # TODO: Refactor.
analyzeLiveRanges: ->
isLive = {}
checkForMutations = (node, type) =>
- if type in ['label', 'return', 'break', 'continue', 'call', 'new']
+ if type of CONTROL_FLOW_NODES
for varName of isLive
@depsMutatedInLiveRange[varName] = true
isLive = {}
@@ -227,7 +235,7 @@ class Eliminator
usedInThisStatement[node[1]] = true
else if type in ['sub', 'dot']
hasIndirectAccess = true
- undefined
+ return undefined
if hasIndirectAccess
for varName of isLive
if not usedInThisStatement[varName]
@@ -242,15 +250,14 @@ class Eliminator
# Determines whether a given variable can be safely eliminated. Requires all
# analysis passes to have been run.
isEliminateable: (varName) ->
- if @isSingleDef[varName]
+ if @isSingleDef[varName] and @usesOnlySimpleNodes[varName]
if @useCount[varName] == 0
- return @usesOnlySimpleNodes[varName]
+ return true
else if @useCount[varName] == 1
- if @usesOnlySimpleNodes[varName]
- return (@usesOnlySingleDefs[varName] or
- not @depsMutatedInLiveRange[varName])
+ return (@usesOnlySingleDefs[varName] or
+ not @depsMutatedInLiveRange[varName])
else if @useCount[varName] <= MAX_USES
- return @usesOnlySimpleNodes[varName] and @usesOnlySingleDefs[varName]
+ return @usesOnlySingleDefs[varName]
return false
# Removes all var declarations for the specified variables.
@@ -276,10 +283,9 @@ class Eliminator
incomplete = false
for varName, varValue of values
result = traverse varValue, (node, type) ->
- if type == 'name' and node[1] of values
- if node[1] != varName
- incomplete = true
- return values[node[1]]
+ if type == 'name' and node[1] of values and node[1] != varName
+ incomplete = true
+ return values[node[1]]
return undefined
if result? then values[varName] = result
return undefined
@@ -291,7 +297,7 @@ class Eliminator
traverse @body, (node, type) ->
if type is 'name' and node[1] of replacements
return replacements[node[1]]
- undefined
+ return undefined
return undefined
diff --git a/tools/shared.py b/tools/shared.py
index 3c9eeb60..ce42d794 100644
--- a/tools/shared.py
+++ b/tools/shared.py
@@ -44,7 +44,7 @@ def timeout_run(proc, timeout, note):
return proc.communicate()[0]
def run_js(engine, filename, args, check_timeout=False, stdout=PIPE, stderr=STDOUT, cwd=None):
- return timeout_run(Popen(engine + [filename] + (['--'] if 'v8' in engine[0] else []) + args,
+ return timeout_run(Popen(engine + [filename] + (['--'] if 'd8' in engine[0] else []) + args,
stdout=stdout, stderr=stderr, cwd=cwd), 15*60 if check_timeout else None, 'Execution')
def to_cc(cxx):