~evarlast/ubuntu/utopic/mongodb/upstart-workaround-debian-bug-718702

« back to all changes in this revision

Viewing changes to src/third_party/v8/tools/jsmin.py

  • Committer: Package Import Robot
  • Author(s): James Page, James Page, Robie Basak
  • Date: 2013-05-29 17:44:42 UTC
  • mfrom: (44.1.7 sid)
  • Revision ID: package-import@ubuntu.com-20130529174442-z0a4qmoww4y0t458
Tags: 1:2.4.3-1ubuntu1
[ James Page ]
* Merge from Debian unstable, remaining changes:
  - Enable SSL support:
    + d/control: Add libssl-dev to BD's.
    + d/rules: Enabled --ssl option.
    + d/mongodb.conf: Add example SSL configuration options.
  - d/mongodb-server.mongodb.upstart: Add upstart configuration.
  - d/rules: Don't strip binaries during scons build for Ubuntu.
  - d/control: Add armhf to target archs.
  - d/p/SConscript.client.patch: fixup install of client libraries.
  - d/p/0010-install-libs-to-usr-lib-not-usr-lib64-Closes-588557.patch:
    Install libraries to lib not lib64.
* Dropped changes:
  - d/p/arm-support.patch: Included in Debian.
  - d/p/double-alignment.patch: Included in Debian.
  - d/rules,control: Debian also builds with avaliable system libraries
    now.
* Fix FTBFS due to gcc and boost upgrades in saucy:
  - d/p/0008-ignore-unused-local-typedefs.patch: Add -Wno-unused-typedefs
    to unbreak building with g++-4.8.
  - d/p/0009-boost-1.53.patch: Fixup signed/unsigned casting issue.

[ Robie Basak ]
* d/p/0011-Use-a-signed-char-to-store-BSONType-enumerations.patch: Fixup
  build failure on ARM due to missing signed'ness of char cast.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/python2.4
 
2
 
 
3
# Copyright 2012 the V8 project authors. All rights reserved.
 
4
# Redistribution and use in source and binary forms, with or without
 
5
# modification, are permitted provided that the following conditions are
 
6
# met:
 
7
#
 
8
#     * Redistributions of source code must retain the above copyright
 
9
#       notice, this list of conditions and the following disclaimer.
 
10
#     * Redistributions in binary form must reproduce the above
 
11
#       copyright notice, this list of conditions and the following
 
12
#       disclaimer in the documentation and/or other materials provided
 
13
#       with the distribution.
 
14
#     * Neither the name of Google Inc. nor the names of its
 
15
#       contributors may be used to endorse or promote products derived
 
16
#       from this software without specific prior written permission.
 
17
#
 
18
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 
19
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 
20
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 
21
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 
22
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 
23
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 
24
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 
25
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 
26
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
27
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 
28
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
29
 
 
30
"""A JavaScript minifier.
 
31
 
 
32
It is far from being a complete JS parser, so there are many valid
 
33
JavaScript programs that will be ruined by it.  Another strangeness is that
 
34
it accepts $ and % as parts of identifiers.  It doesn't merge lines or strip
 
35
out blank lines in order to ease debugging.  Variables at the top scope are
 
36
properties of the global object so we can't rename them.  It is assumed that
 
37
you introduce variables with var as if JavaScript followed C++ scope rules
 
38
around curly braces, so the declaration must be above the first use.
 
39
 
 
40
Use as:
 
41
import jsmin
 
42
minifier = JavaScriptMinifier()
 
43
program1 = minifier.JSMinify(program1)
 
44
program2 = minifier.JSMinify(program2)
 
45
"""
 
46
 
 
47
import re
 
48
 
 
49
 
 
50
class JavaScriptMinifier(object):
 
51
  """An object that you can feed code snippets to to get them minified."""
 
52
 
 
53
  def __init__(self):
 
54
    # We prepopulate the list of identifiers that shouldn't be used.  These
 
55
    # short language keywords could otherwise be used by the script as variable
 
56
    # names.
 
57
    self.seen_identifiers = {"do": True, "in": True}
 
58
    self.identifier_counter = 0
 
59
    self.in_comment = False
 
60
    self.map = {}
 
61
    self.nesting = 0
 
62
 
 
63
  def LookAtIdentifier(self, m):
 
64
    """Records identifiers or keywords that we see in use.
 
65
 
 
66
    (So we can avoid renaming variables to these strings.)
 
67
    Args:
 
68
      m: The match object returned by re.search.
 
69
 
 
70
    Returns:
 
71
      Nothing.
 
72
    """
 
73
    identifier = m.group(1)
 
74
    self.seen_identifiers[identifier] = True
 
75
 
 
76
  def Push(self):
 
77
    """Called when we encounter a '{'."""
 
78
    self.nesting += 1
 
79
 
 
80
  def Pop(self):
 
81
    """Called when we encounter a '}'."""
 
82
    self.nesting -= 1
 
83
    # We treat each top-level opening brace as a single scope that can span
 
84
    # several sets of nested braces.
 
85
    if self.nesting == 0:
 
86
      self.map = {}
 
87
      self.identifier_counter = 0
 
88
 
 
89
  def Declaration(self, m):
 
90
    """Rewrites bits of the program selected by a regexp.
 
91
 
 
92
    These can be curly braces, literal strings, function declarations and var
 
93
    declarations.  (These last two must be on one line including the opening
 
94
    curly brace of the function for their variables to be renamed).
 
95
 
 
96
    Args:
 
97
      m: The match object returned by re.search.
 
98
 
 
99
    Returns:
 
100
      The string that should replace the match in the rewritten program.
 
101
    """
 
102
    matched_text = m.group(0)
 
103
    if matched_text == "{":
 
104
      self.Push()
 
105
      return matched_text
 
106
    if matched_text == "}":
 
107
      self.Pop()
 
108
      return matched_text
 
109
    if re.match("[\"'/]", matched_text):
 
110
      return matched_text
 
111
    m = re.match(r"var ", matched_text)
 
112
    if m:
 
113
      var_names = matched_text[m.end():]
 
114
      var_names = re.split(r",", var_names)
 
115
      return "var " + ",".join(map(self.FindNewName, var_names))
 
116
    m = re.match(r"(function\b[^(]*)\((.*)\)\{$", matched_text)
 
117
    if m:
 
118
      up_to_args = m.group(1)
 
119
      args = m.group(2)
 
120
      args = re.split(r",", args)
 
121
      self.Push()
 
122
      return up_to_args + "(" + ",".join(map(self.FindNewName, args)) + "){"
 
123
 
 
124
    if matched_text in self.map:
 
125
      return self.map[matched_text]
 
126
 
 
127
    return matched_text
 
128
 
 
129
  def CharFromNumber(self, number):
 
130
    """A single-digit base-52 encoding using a-zA-Z."""
 
131
    if number < 26:
 
132
      return chr(number + 97)
 
133
    number -= 26
 
134
    return chr(number + 65)
 
135
 
 
136
  def FindNewName(self, var_name):
 
137
    """Finds a new 1-character or 2-character name for a variable.
 
138
 
 
139
    Enters it into the mapping table for this scope.
 
140
 
 
141
    Args:
 
142
      var_name: The name of the variable before renaming.
 
143
 
 
144
    Returns:
 
145
      The new name of the variable.
 
146
    """
 
147
    new_identifier = ""
 
148
    # Variable names that end in _ are member variables of the global object,
 
149
    # so they can be visible from code in a different scope.  We leave them
 
150
    # alone.
 
151
    if var_name in self.map:
 
152
      return self.map[var_name]
 
153
    if self.nesting == 0:
 
154
      return var_name
 
155
    while True:
 
156
      identifier_first_char = self.identifier_counter % 52
 
157
      identifier_second_char = self.identifier_counter // 52
 
158
      new_identifier = self.CharFromNumber(identifier_first_char)
 
159
      if identifier_second_char != 0:
 
160
        new_identifier = (
 
161
            self.CharFromNumber(identifier_second_char - 1) + new_identifier)
 
162
      self.identifier_counter += 1
 
163
      if not new_identifier in self.seen_identifiers:
 
164
        break
 
165
 
 
166
    self.map[var_name] = new_identifier
 
167
    return new_identifier
 
168
 
 
169
  def RemoveSpaces(self, m):
 
170
    """Returns literal strings unchanged, replaces other inputs with group 2.
 
171
 
 
172
    Other inputs are replaced with the contents of capture 1.  This is either
 
173
    a single space or an empty string.
 
174
 
 
175
    Args:
 
176
      m: The match object returned by re.search.
 
177
 
 
178
    Returns:
 
179
      The string that should be inserted instead of the matched text.
 
180
    """
 
181
    entire_match = m.group(0)
 
182
    replacement = m.group(1)
 
183
    if re.match(r"'.*'$", entire_match):
 
184
      return entire_match
 
185
    if re.match(r'".*"$', entire_match):
 
186
      return entire_match
 
187
    if re.match(r"/.+/$", entire_match):
 
188
      return entire_match
 
189
    return replacement
 
190
 
 
191
  def JSMinify(self, text):
 
192
    """The main entry point.  Takes a text and returns a compressed version.
 
193
 
 
194
    The compressed version hopefully does the same thing.  Line breaks are
 
195
    preserved.
 
196
 
 
197
    Args:
 
198
      text: The text of the code snippet as a multiline string.
 
199
 
 
200
    Returns:
 
201
      The compressed text of the code snippet as a multiline string.
 
202
    """
 
203
    new_lines = []
 
204
    for line in re.split(r"\n", text):
 
205
      line = line.replace("\t", " ")
 
206
      if self.in_comment:
 
207
        m = re.search(r"\*/", line)
 
208
        if m:
 
209
          line = line[m.end():]
 
210
          self.in_comment = False
 
211
        else:
 
212
          new_lines.append("")
 
213
          continue
 
214
 
 
215
      if not self.in_comment:
 
216
        line = re.sub(r"/\*.*?\*/", " ", line)
 
217
        line = re.sub(r"//.*", "", line)
 
218
        m = re.search(r"/\*", line)
 
219
        if m:
 
220
          line = line[:m.start()]
 
221
          self.in_comment = True
 
222
 
 
223
      # Strip leading and trailing spaces.
 
224
      line = re.sub(r"^ +", "", line)
 
225
      line = re.sub(r" +$", "", line)
 
226
      # A regexp that matches a literal string surrounded by "double quotes".
 
227
      # This regexp can handle embedded backslash-escaped characters including
 
228
      # embedded backslash-escaped double quotes.
 
229
      double_quoted_string = r'"(?:[^"\\]|\\.)*"'
 
230
      # A regexp that matches a literal string surrounded by 'double quotes'.
 
231
      single_quoted_string = r"'(?:[^'\\]|\\.)*'"
 
232
      # A regexp that matches a regexp literal surrounded by /slashes/.
 
233
      # Don't allow a regexp to have a ) before the first ( since that's a
 
234
      # syntax error and it's probably just two unrelated slashes.
 
235
      # Also don't allow it to come after anything that can only be the
 
236
      # end of a primary expression.
 
237
      slash_quoted_regexp = r"(?<![\w$'\")\]])/(?:(?=\()|(?:[^()/\\]|\\.)+)(?:\([^/\\]|\\.)*/"
 
238
      # Replace multiple spaces with a single space.
 
239
      line = re.sub("|".join([double_quoted_string,
 
240
                              single_quoted_string,
 
241
                              slash_quoted_regexp,
 
242
                              "( )+"]),
 
243
                    self.RemoveSpaces,
 
244
                    line)
 
245
      # Strip single spaces unless they have an identifier character both before
 
246
      # and after the space.  % and $ are counted as identifier characters.
 
247
      line = re.sub("|".join([double_quoted_string,
 
248
                              single_quoted_string,
 
249
                              slash_quoted_regexp,
 
250
                              r"(?<![a-zA-Z_0-9$%]) | (?![a-zA-Z_0-9$%])()"]),
 
251
                    self.RemoveSpaces,
 
252
                    line)
 
253
      # Collect keywords and identifiers that are already in use.
 
254
      if self.nesting == 0:
 
255
        re.sub(r"([a-zA-Z0-9_$%]+)", self.LookAtIdentifier, line)
 
256
      function_declaration_regexp = (
 
257
          r"\bfunction"              # Function definition keyword...
 
258
          r"( [\w$%]+)?"             # ...optional function name...
 
259
          r"\([\w$%,]+\)\{")         # ...argument declarations.
 
260
      # Unfortunately the keyword-value syntax { key:value } makes the key look
 
261
      # like a variable where in fact it is a literal string.  We use the
 
262
      # presence or absence of a question mark to try to distinguish between
 
263
      # this case and the ternary operator: "condition ? iftrue : iffalse".
 
264
      if re.search(r"\?", line):
 
265
        block_trailing_colon = r""
 
266
      else:
 
267
        block_trailing_colon = r"(?![:\w$%])"
 
268
      # Variable use.  Cannot follow a period precede a colon.
 
269
      variable_use_regexp = r"(?<![.\w$%])[\w$%]+" + block_trailing_colon
 
270
      line = re.sub("|".join([double_quoted_string,
 
271
                              single_quoted_string,
 
272
                              slash_quoted_regexp,
 
273
                              r"\{",                  # Curly braces.
 
274
                              r"\}",
 
275
                              r"\bvar [\w$%,]+",      # var declarations.
 
276
                              function_declaration_regexp,
 
277
                              variable_use_regexp]),
 
278
                    self.Declaration,
 
279
                    line)
 
280
      new_lines.append(line)
 
281
 
 
282
    return "\n".join(new_lines) + "\n"