зеркало из https://github.com/mozilla/gecko-dev.git
Bug 462300 - Embed self-hosted scripts and compile into special compartment. r=bhackett, r=gerv, r=ted
This commit is contained in:
Родитель
ad0c7a9dde
Коммит
d953aff856
|
@ -452,6 +452,8 @@ HOST_CPPSRCS += jsoplengen.cpp
|
|||
HOST_SIMPLE_PROGRAMS += host_jsoplengen$(HOST_BIN_SUFFIX)
|
||||
GARBAGE += jsautooplen.h host_jsoplengen$(HOST_BIN_SUFFIX)
|
||||
|
||||
GARBAGE += selfhosted.out.h
|
||||
|
||||
USE_HOST_CXX = 1
|
||||
|
||||
ifdef HAVE_DTRACE
|
||||
|
@ -822,6 +824,24 @@ ifdef HAVE_LINUX_PERF_EVENT_H
|
|||
pm_linux.$(OBJ_SUFFIX): CXXFLAGS += $(LINUX_HEADERS_INCLUDES)
|
||||
endif
|
||||
|
||||
# Prepare self-hosted JS code for embedding
|
||||
export:: selfhosted.out.h
|
||||
|
||||
selfhosting_srcs := \
|
||||
$(srcdir)/builtin/array.js \
|
||||
$(NULL)
|
||||
|
||||
selfhosted_out_h_deps := \
|
||||
$(selfhosting_srcs) \
|
||||
$(srcdir)/js.msg \
|
||||
$(srcdir)/builtin/macros.py \
|
||||
$(srcdir)/builtin/js2c.py \
|
||||
$(srcdir)/builtin/embedjs.py
|
||||
|
||||
selfhosted.out.h: $(selfhosted_out_h_deps)
|
||||
$(PYTHON) $(srcdir)/builtin/embedjs.py $@ $(srcdir)/js.msg \
|
||||
$(srcdir)/builtin/macros.py $(selfhosting_srcs)
|
||||
|
||||
###############################################
|
||||
# BEGIN kludges for the Nitro assembler
|
||||
#
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
//this space intentionally left blank
|
|
@ -0,0 +1,49 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
# This utility converts JS files containing self-hosted builtins into a C
|
||||
# header file that can be embedded into SpiderMonkey.
|
||||
#
|
||||
# It expects error messages in the JS code to be referenced by their C enum
|
||||
# keys as literals.
|
||||
|
||||
from __future__ import with_statement
|
||||
import re, sys, os, js2c, fileinput
|
||||
|
||||
def replaceErrorMsgs(source_files, messages_file, output_file):
|
||||
messages = buildMessagesTable(messages_file)
|
||||
with open(output_file, 'w') as output:
|
||||
if len(source_files) == 0:
|
||||
return
|
||||
for line in fileinput.input(source_files):
|
||||
output.write(replaceMessages(line, messages))
|
||||
|
||||
def buildMessagesTable(messages_file):
|
||||
table = {}
|
||||
pattern = re.compile(r"MSG_DEF\(([\w_]+),\s*(\d+)")
|
||||
for line in fileinput.input(messages_file):
|
||||
match = pattern.match(line)
|
||||
if match:
|
||||
table[match.group(1)] = match.group(2)
|
||||
return table
|
||||
|
||||
def replaceMessages(line, messages):
|
||||
if not 'JSMSG_' in line:
|
||||
return line
|
||||
for message_str, message_num in messages.iteritems():
|
||||
line = line.replace(message_str, message_num)
|
||||
return line
|
||||
|
||||
def main():
|
||||
output_file = sys.argv[1]
|
||||
messages_file = sys.argv[2]
|
||||
macros_file = sys.argv[3]
|
||||
source_files = sys.argv[4:]
|
||||
combined_file = 'combined.js'
|
||||
replaceErrorMsgs(source_files, messages_file, combined_file)
|
||||
js2c.JS2C([combined_file, macros_file], [output_file], { 'TYPE': 'CORE', 'COMPRESSION': 'off' })
|
||||
os.remove(combined_file)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -0,0 +1,348 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2012 the V8 project authors. All rights reserved.
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following
|
||||
# disclaimer in the documentation and/or other materials provided
|
||||
# with the distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived
|
||||
# from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# This is a utility for converting JavaScript source code into C-style
|
||||
# char arrays. It is used for embedded JavaScript code in the V8
|
||||
# library.
|
||||
|
||||
import os, re, sys, string
|
||||
import jsmin
|
||||
import bz2
|
||||
|
||||
|
||||
def ToCAsciiArray(lines):
|
||||
result = []
|
||||
for chr in lines:
|
||||
value = ord(chr)
|
||||
assert value < 128
|
||||
result.append(str(value))
|
||||
return ", ".join(result)
|
||||
|
||||
|
||||
def ToCArray(lines):
|
||||
result = []
|
||||
for chr in lines:
|
||||
result.append(str(ord(chr)))
|
||||
return ", ".join(result)
|
||||
|
||||
|
||||
def RemoveCommentsAndTrailingWhitespace(lines):
|
||||
lines = re.sub(r'//.*\n', '\n', lines) # end-of-line comments
|
||||
lines = re.sub(re.compile(r'/\*.*?\*/', re.DOTALL), '', lines) # comments.
|
||||
lines = re.sub(r'\s+\n+', '\n', lines) # trailing whitespace
|
||||
return lines
|
||||
|
||||
|
||||
def ReadFile(filename):
|
||||
file = open(filename, "rt")
|
||||
try:
|
||||
lines = file.read()
|
||||
finally:
|
||||
file.close()
|
||||
return lines
|
||||
|
||||
|
||||
def ReadLines(filename):
|
||||
result = []
|
||||
for line in open(filename, "rt"):
|
||||
if '#' in line:
|
||||
line = line[:line.index('#')]
|
||||
line = line.strip()
|
||||
if len(line) > 0:
|
||||
result.append(line)
|
||||
return result
|
||||
|
||||
|
||||
def LoadConfigFrom(name):
|
||||
import ConfigParser
|
||||
config = ConfigParser.ConfigParser()
|
||||
config.read(name)
|
||||
return config
|
||||
|
||||
|
||||
def ParseValue(string):
|
||||
string = string.strip()
|
||||
if string.startswith('[') and string.endswith(']'):
|
||||
return string.lstrip('[').rstrip(']').split()
|
||||
else:
|
||||
return string
|
||||
|
||||
|
||||
EVAL_PATTERN = re.compile(r'\beval\s*\(')
|
||||
WITH_PATTERN = re.compile(r'\bwith\s*\(')
|
||||
|
||||
|
||||
def Validate(lines, file):
|
||||
lines = RemoveCommentsAndTrailingWhitespace(lines)
|
||||
# Because of simplified context setup, eval and with is not
|
||||
# allowed in the natives files.
|
||||
eval_match = EVAL_PATTERN.search(lines)
|
||||
if eval_match:
|
||||
raise ("Eval disallowed in natives: %s" % file)
|
||||
with_match = WITH_PATTERN.search(lines)
|
||||
if with_match:
|
||||
raise ("With statements disallowed in natives: %s" % file)
|
||||
|
||||
|
||||
def ExpandConstants(lines, constants):
|
||||
for key, value in constants:
|
||||
lines = key.sub(str(value), lines)
|
||||
return lines
|
||||
|
||||
|
||||
def ExpandMacros(lines, macros):
|
||||
# We allow macros to depend on the previously declared macros, but
|
||||
# we don't allow self-dependecies or recursion.
|
||||
for name_pattern, macro in reversed(macros):
|
||||
pattern_match = name_pattern.search(lines, 0)
|
||||
while pattern_match is not None:
|
||||
# Scan over the arguments
|
||||
height = 1
|
||||
start = pattern_match.start()
|
||||
end = pattern_match.end()
|
||||
assert lines[end - 1] == '('
|
||||
last_match = end
|
||||
arg_index = [0] # Wrap state into array, to work around Python "scoping"
|
||||
mapping = { }
|
||||
def add_arg(str):
|
||||
# Remember to expand recursively in the arguments
|
||||
replacement = ExpandMacros(str.strip(), macros)
|
||||
mapping[macro.args[arg_index[0]]] = replacement
|
||||
arg_index[0] += 1
|
||||
while end < len(lines) and height > 0:
|
||||
# We don't count commas at higher nesting levels.
|
||||
if lines[end] == ',' and height == 1:
|
||||
add_arg(lines[last_match:end])
|
||||
last_match = end + 1
|
||||
elif lines[end] in ['(', '{', '[']:
|
||||
height = height + 1
|
||||
elif lines[end] in [')', '}', ']']:
|
||||
height = height - 1
|
||||
end = end + 1
|
||||
# Remember to add the last match.
|
||||
add_arg(lines[last_match:end-1])
|
||||
result = macro.expand(mapping)
|
||||
# Replace the occurrence of the macro with the expansion
|
||||
lines = lines[:start] + result + lines[end:]
|
||||
pattern_match = name_pattern.search(lines, start + len(result))
|
||||
return lines
|
||||
|
||||
class TextMacro:
|
||||
def __init__(self, args, body):
|
||||
self.args = args
|
||||
self.body = body
|
||||
def expand(self, mapping):
|
||||
result = self.body
|
||||
for key, value in mapping.items():
|
||||
result = result.replace(key, value)
|
||||
return result
|
||||
|
||||
class PythonMacro:
|
||||
def __init__(self, args, fun):
|
||||
self.args = args
|
||||
self.fun = fun
|
||||
def expand(self, mapping):
|
||||
args = []
|
||||
for arg in self.args:
|
||||
args.append(mapping[arg])
|
||||
return str(self.fun(*args))
|
||||
|
||||
CONST_PATTERN = re.compile(r'^const\s+([a-zA-Z0-9_]+)\s*=\s*([^;]*);$')
|
||||
MACRO_PATTERN = re.compile(r'^macro\s+([a-zA-Z0-9_]+)\s*\(([^)]*)\)\s*=\s*([^;]*);$')
|
||||
PYTHON_MACRO_PATTERN = re.compile(r'^python\s+macro\s+([a-zA-Z0-9_]+)\s*\(([^)]*)\)\s*=\s*([^;]*);$')
|
||||
|
||||
|
||||
def ReadMacros(lines):
|
||||
constants = []
|
||||
macros = []
|
||||
for line in lines:
|
||||
hash = line.find('#')
|
||||
if hash != -1: line = line[:hash]
|
||||
line = line.strip()
|
||||
if len(line) is 0: continue
|
||||
const_match = CONST_PATTERN.match(line)
|
||||
if const_match:
|
||||
name = const_match.group(1)
|
||||
value = const_match.group(2).strip()
|
||||
constants.append((re.compile("\\b%s\\b" % name), value))
|
||||
else:
|
||||
macro_match = MACRO_PATTERN.match(line)
|
||||
if macro_match:
|
||||
name = macro_match.group(1)
|
||||
args = [match.strip() for match in macro_match.group(2).split(',')]
|
||||
body = macro_match.group(3).strip()
|
||||
macros.append((re.compile("\\b%s\\(" % name), TextMacro(args, body)))
|
||||
else:
|
||||
python_match = PYTHON_MACRO_PATTERN.match(line)
|
||||
if python_match:
|
||||
name = python_match.group(1)
|
||||
args = [match.strip() for match in python_match.group(2).split(',')]
|
||||
body = python_match.group(3).strip()
|
||||
fun = eval("lambda " + ",".join(args) + ': ' + body)
|
||||
macros.append((re.compile("\\b%s\\(" % name), PythonMacro(args, fun)))
|
||||
else:
|
||||
raise ("Illegal line: " + line)
|
||||
return (constants, macros)
|
||||
|
||||
|
||||
HEADER_TEMPLATE = """\
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
namespace js {
|
||||
namespace selfhosted {
|
||||
static const char sources[] = { %(sources_data)s };
|
||||
|
||||
%(raw_sources_declaration)s\
|
||||
|
||||
uint32_t GetRawScriptsSize() {
|
||||
return %(raw_total_length)i;
|
||||
}
|
||||
} // selfhosted
|
||||
} // js
|
||||
"""
|
||||
|
||||
|
||||
RAW_SOURCES_COMPRESSION_DECLARATION = """\
|
||||
static const char* raw_sources = NULL;
|
||||
"""
|
||||
|
||||
|
||||
RAW_SOURCES_DECLARATION = """\
|
||||
static const char* raw_sources = reinterpret_cast<const char*>(sources);
|
||||
"""
|
||||
|
||||
|
||||
GET_INDEX_CASE = """\
|
||||
if (strcmp(name, "%(id)s") == 0) return %(i)i;
|
||||
"""
|
||||
|
||||
|
||||
GET_RAW_SCRIPT_SOURCE_CASE = """\
|
||||
if (index == %(i)i) return Vector<const char>(raw_sources + %(offset)i, %(raw_length)i);
|
||||
"""
|
||||
|
||||
|
||||
GET_SCRIPT_NAME_CASE = """\
|
||||
if (index == %(i)i) return Vector<const char>("%(name)s", %(length)i);
|
||||
"""
|
||||
|
||||
def JS2C(source, target, env):
|
||||
ids = []
|
||||
debugger_ids = []
|
||||
modules = []
|
||||
# Locate the macros file name.
|
||||
consts = []
|
||||
macros = []
|
||||
for s in source:
|
||||
if 'macros.py' == (os.path.split(str(s))[1]):
|
||||
(consts, macros) = ReadMacros(ReadLines(str(s)))
|
||||
else:
|
||||
modules.append(s)
|
||||
|
||||
minifier = jsmin.JavaScriptMinifier()
|
||||
|
||||
module_offset = 0
|
||||
all_sources = []
|
||||
for module in modules:
|
||||
filename = str(module)
|
||||
debugger = filename.endswith('-debugger.js')
|
||||
lines = ReadFile(filename)
|
||||
lines = ExpandConstants(lines, consts)
|
||||
lines = ExpandMacros(lines, macros)
|
||||
Validate(lines, filename)
|
||||
lines = minifier.JSMinify(lines)
|
||||
id = (os.path.split(filename)[1])[:-3]
|
||||
if debugger: id = id[:-9]
|
||||
raw_length = len(lines)
|
||||
if debugger:
|
||||
debugger_ids.append((id, raw_length, module_offset))
|
||||
else:
|
||||
ids.append((id, raw_length, module_offset))
|
||||
all_sources.append(lines)
|
||||
module_offset += raw_length
|
||||
total_length = raw_total_length = module_offset
|
||||
|
||||
if env['COMPRESSION'] == 'off':
|
||||
raw_sources_declaration = RAW_SOURCES_DECLARATION
|
||||
sources_data = ToCAsciiArray("".join(all_sources))
|
||||
else:
|
||||
raw_sources_declaration = RAW_SOURCES_COMPRESSION_DECLARATION
|
||||
if env['COMPRESSION'] == 'bz2':
|
||||
all_sources = bz2.compress("".join(all_sources))
|
||||
total_length = len(all_sources)
|
||||
sources_data = ToCArray(all_sources)
|
||||
|
||||
# Build debugger support functions
|
||||
get_index_cases = [ ]
|
||||
get_raw_script_source_cases = [ ]
|
||||
get_script_name_cases = [ ]
|
||||
|
||||
i = 0
|
||||
for (id, raw_length, module_offset) in debugger_ids + ids:
|
||||
native_name = "native %s.js" % id
|
||||
get_index_cases.append(GET_INDEX_CASE % { 'id': id, 'i': i })
|
||||
get_raw_script_source_cases.append(GET_RAW_SCRIPT_SOURCE_CASE % {
|
||||
'offset': module_offset,
|
||||
'raw_length': raw_length,
|
||||
'i': i
|
||||
})
|
||||
get_script_name_cases.append(GET_SCRIPT_NAME_CASE % {
|
||||
'name': native_name,
|
||||
'length': len(native_name),
|
||||
'i': i
|
||||
})
|
||||
i = i + 1
|
||||
|
||||
# Emit result
|
||||
output = open(str(target[0]), "w")
|
||||
output.write(HEADER_TEMPLATE % {
|
||||
'builtin_count': len(ids) + len(debugger_ids),
|
||||
'debugger_count': len(debugger_ids),
|
||||
'sources_data': sources_data,
|
||||
'raw_sources_declaration': raw_sources_declaration,
|
||||
'raw_total_length': raw_total_length,
|
||||
'total_length': total_length,
|
||||
'get_index_cases': "".join(get_index_cases),
|
||||
'get_raw_script_source_cases': "".join(get_raw_script_source_cases),
|
||||
'get_script_name_cases': "".join(get_script_name_cases),
|
||||
'type': env['TYPE']
|
||||
})
|
||||
output.close()
|
||||
|
||||
def main():
|
||||
natives = sys.argv[1]
|
||||
type = sys.argv[2]
|
||||
compression = sys.argv[3]
|
||||
source_files = sys.argv[4:]
|
||||
JS2C(source_files, [natives], { 'TYPE': type, 'COMPRESSION': compression })
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -0,0 +1,282 @@
|
|||
#!/usr/bin/python2.4
|
||||
|
||||
# Copyright 2012 the V8 project authors. All rights reserved.
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following
|
||||
# disclaimer in the documentation and/or other materials provided
|
||||
# with the distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived
|
||||
# from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
"""A JavaScript minifier.
|
||||
|
||||
It is far from being a complete JS parser, so there are many valid
|
||||
JavaScript programs that will be ruined by it. Another strangeness is that
|
||||
it accepts $ and % as parts of identifiers. It doesn't merge lines or strip
|
||||
out blank lines in order to ease debugging. Variables at the top scope are
|
||||
properties of the global object so we can't rename them. It is assumed that
|
||||
you introduce variables with var as if JavaScript followed C++ scope rules
|
||||
around curly braces, so the declaration must be above the first use.
|
||||
|
||||
Use as:
|
||||
import jsmin
|
||||
minifier = JavaScriptMinifier()
|
||||
program1 = minifier.JSMinify(program1)
|
||||
program2 = minifier.JSMinify(program2)
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
|
||||
class JavaScriptMinifier(object):
|
||||
"""An object that you can feed code snippets to to get them minified."""
|
||||
|
||||
def __init__(self):
|
||||
# We prepopulate the list of identifiers that shouldn't be used. These
|
||||
# short language keywords could otherwise be used by the script as variable
|
||||
# names.
|
||||
self.seen_identifiers = {"do": True, "in": True}
|
||||
self.identifier_counter = 0
|
||||
self.in_comment = False
|
||||
self.map = {}
|
||||
self.nesting = 0
|
||||
|
||||
def LookAtIdentifier(self, m):
|
||||
"""Records identifiers or keywords that we see in use.
|
||||
|
||||
(So we can avoid renaming variables to these strings.)
|
||||
Args:
|
||||
m: The match object returned by re.search.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
"""
|
||||
identifier = m.group(1)
|
||||
self.seen_identifiers[identifier] = True
|
||||
|
||||
def Push(self):
|
||||
"""Called when we encounter a '{'."""
|
||||
self.nesting += 1
|
||||
|
||||
def Pop(self):
|
||||
"""Called when we encounter a '}'."""
|
||||
self.nesting -= 1
|
||||
# We treat each top-level opening brace as a single scope that can span
|
||||
# several sets of nested braces.
|
||||
if self.nesting == 0:
|
||||
self.map = {}
|
||||
self.identifier_counter = 0
|
||||
|
||||
def Declaration(self, m):
|
||||
"""Rewrites bits of the program selected by a regexp.
|
||||
|
||||
These can be curly braces, literal strings, function declarations and var
|
||||
declarations. (These last two must be on one line including the opening
|
||||
curly brace of the function for their variables to be renamed).
|
||||
|
||||
Args:
|
||||
m: The match object returned by re.search.
|
||||
|
||||
Returns:
|
||||
The string that should replace the match in the rewritten program.
|
||||
"""
|
||||
matched_text = m.group(0)
|
||||
if matched_text == "{":
|
||||
self.Push()
|
||||
return matched_text
|
||||
if matched_text == "}":
|
||||
self.Pop()
|
||||
return matched_text
|
||||
if re.match("[\"'/]", matched_text):
|
||||
return matched_text
|
||||
m = re.match(r"var ", matched_text)
|
||||
if m:
|
||||
var_names = matched_text[m.end():]
|
||||
var_names = re.split(r",", var_names)
|
||||
return "var " + ",".join(map(self.FindNewName, var_names))
|
||||
m = re.match(r"(function\b[^(]*)\((.*)\)\{$", matched_text)
|
||||
if m:
|
||||
up_to_args = m.group(1)
|
||||
args = m.group(2)
|
||||
args = re.split(r",", args)
|
||||
self.Push()
|
||||
return up_to_args + "(" + ",".join(map(self.FindNewName, args)) + "){"
|
||||
|
||||
if matched_text in self.map:
|
||||
return self.map[matched_text]
|
||||
|
||||
return matched_text
|
||||
|
||||
def CharFromNumber(self, number):
|
||||
"""A single-digit base-52 encoding using a-zA-Z."""
|
||||
if number < 26:
|
||||
return chr(number + 97)
|
||||
number -= 26
|
||||
return chr(number + 65)
|
||||
|
||||
def FindNewName(self, var_name):
|
||||
"""Finds a new 1-character or 2-character name for a variable.
|
||||
|
||||
Enters it into the mapping table for this scope.
|
||||
|
||||
Args:
|
||||
var_name: The name of the variable before renaming.
|
||||
|
||||
Returns:
|
||||
The new name of the variable.
|
||||
"""
|
||||
new_identifier = ""
|
||||
# Variable names that end in _ are member variables of the global object,
|
||||
# so they can be visible from code in a different scope. We leave them
|
||||
# alone.
|
||||
if var_name in self.map:
|
||||
return self.map[var_name]
|
||||
if self.nesting == 0:
|
||||
return var_name
|
||||
while True:
|
||||
identifier_first_char = self.identifier_counter % 52
|
||||
identifier_second_char = self.identifier_counter // 52
|
||||
new_identifier = self.CharFromNumber(identifier_first_char)
|
||||
if identifier_second_char != 0:
|
||||
new_identifier = (
|
||||
self.CharFromNumber(identifier_second_char - 1) + new_identifier)
|
||||
self.identifier_counter += 1
|
||||
if not new_identifier in self.seen_identifiers:
|
||||
break
|
||||
|
||||
self.map[var_name] = new_identifier
|
||||
return new_identifier
|
||||
|
||||
def RemoveSpaces(self, m):
|
||||
"""Returns literal strings unchanged, replaces other inputs with group 2.
|
||||
|
||||
Other inputs are replaced with the contents of capture 1. This is either
|
||||
a single space or an empty string.
|
||||
|
||||
Args:
|
||||
m: The match object returned by re.search.
|
||||
|
||||
Returns:
|
||||
The string that should be inserted instead of the matched text.
|
||||
"""
|
||||
entire_match = m.group(0)
|
||||
replacement = m.group(1)
|
||||
if re.match(r"'.*'$", entire_match):
|
||||
return entire_match
|
||||
if re.match(r'".*"$', entire_match):
|
||||
return entire_match
|
||||
if re.match(r"/.+/$", entire_match):
|
||||
return entire_match
|
||||
return replacement
|
||||
|
||||
def JSMinify(self, text):
|
||||
"""The main entry point. Takes a text and returns a compressed version.
|
||||
|
||||
The compressed version hopefully does the same thing. Line breaks are
|
||||
preserved.
|
||||
|
||||
Args:
|
||||
text: The text of the code snippet as a multiline string.
|
||||
|
||||
Returns:
|
||||
The compressed text of the code snippet as a multiline string.
|
||||
"""
|
||||
new_lines = []
|
||||
for line in re.split(r"\n", text):
|
||||
line = line.replace("\t", " ")
|
||||
if self.in_comment:
|
||||
m = re.search(r"\*/", line)
|
||||
if m:
|
||||
line = line[m.end():]
|
||||
self.in_comment = False
|
||||
else:
|
||||
new_lines.append("")
|
||||
continue
|
||||
|
||||
if not self.in_comment:
|
||||
line = re.sub(r"/\*.*?\*/", " ", line)
|
||||
line = re.sub(r"//.*", "", line)
|
||||
m = re.search(r"/\*", line)
|
||||
if m:
|
||||
line = line[:m.start()]
|
||||
self.in_comment = True
|
||||
|
||||
# Strip leading and trailing spaces.
|
||||
line = re.sub(r"^ +", "", line)
|
||||
line = re.sub(r" +$", "", line)
|
||||
# A regexp that matches a literal string surrounded by "double quotes".
|
||||
# This regexp can handle embedded backslash-escaped characters including
|
||||
# embedded backslash-escaped double quotes.
|
||||
double_quoted_string = r'"(?:[^"\\]|\\.)*"'
|
||||
# A regexp that matches a literal string surrounded by 'double quotes'.
|
||||
single_quoted_string = r"'(?:[^'\\]|\\.)*'"
|
||||
# A regexp that matches a regexp literal surrounded by /slashes/.
|
||||
# Don't allow a regexp to have a ) before the first ( since that's a
|
||||
# syntax error and it's probably just two unrelated slashes.
|
||||
# Also don't allow it to come after anything that can only be the
|
||||
# end of a primary expression.
|
||||
slash_quoted_regexp = r"(?<![\w$'\")\]])/(?:(?=\()|(?:[^()/\\]|\\.)+)(?:\([^/\\]|\\.)*/"
|
||||
# Replace multiple spaces with a single space.
|
||||
line = re.sub("|".join([double_quoted_string,
|
||||
single_quoted_string,
|
||||
slash_quoted_regexp,
|
||||
"( )+"]),
|
||||
self.RemoveSpaces,
|
||||
line)
|
||||
# Strip single spaces unless they have an identifier character both before
|
||||
# and after the space. % and $ are counted as identifier characters.
|
||||
line = re.sub("|".join([double_quoted_string,
|
||||
single_quoted_string,
|
||||
slash_quoted_regexp,
|
||||
r"(?<![a-zA-Z_0-9$%]) | (?![a-zA-Z_0-9$%])()"]),
|
||||
self.RemoveSpaces,
|
||||
line)
|
||||
# Collect keywords and identifiers that are already in use.
|
||||
if self.nesting == 0:
|
||||
re.sub(r"([a-zA-Z0-9_$%]+)", self.LookAtIdentifier, line)
|
||||
function_declaration_regexp = (
|
||||
r"\bfunction" # Function definition keyword...
|
||||
r"( [\w$%]+)?" # ...optional function name...
|
||||
r"\([\w$%,]+\)\{") # ...argument declarations.
|
||||
# Unfortunately the keyword-value syntax { key:value } makes the key look
|
||||
# like a variable where in fact it is a literal string. We use the
|
||||
# presence or absence of a question mark to try to distinguish between
|
||||
# this case and the ternary operator: "condition ? iftrue : iffalse".
|
||||
if re.search(r"\?", line):
|
||||
block_trailing_colon = r""
|
||||
else:
|
||||
block_trailing_colon = r"(?![:\w$%])"
|
||||
# Variable use. Cannot follow a period precede a colon.
|
||||
variable_use_regexp = r"(?<![.\w$%])[\w$%]+" + block_trailing_colon
|
||||
line = re.sub("|".join([double_quoted_string,
|
||||
single_quoted_string,
|
||||
slash_quoted_regexp,
|
||||
r"\{", # Curly braces.
|
||||
r"\}",
|
||||
r"\bvar [\w$%,]+", # var declarations.
|
||||
function_declaration_regexp,
|
||||
variable_use_regexp]),
|
||||
self.Declaration,
|
||||
line)
|
||||
new_lines.append(line)
|
||||
|
||||
return "\n".join(new_lines) + "\n"
|
|
@ -0,0 +1,239 @@
|
|||
# Copyright 2006-2009 the V8 project authors. All rights reserved.
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following
|
||||
# disclaimer in the documentation and/or other materials provided
|
||||
# with the distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived
|
||||
# from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# Dictionary that is passed as defines for js2c.py.
|
||||
# Used for defines that must be defined for all native JS files.
|
||||
|
||||
const NONE = 0;
|
||||
const READ_ONLY = 1;
|
||||
const DONT_ENUM = 2;
|
||||
const DONT_DELETE = 4;
|
||||
|
||||
# Constants used for getter and setter operations.
|
||||
const GETTER = 0;
|
||||
const SETTER = 1;
|
||||
|
||||
# These definitions must match the index of the properties in objects.h.
|
||||
const kApiTagOffset = 0;
|
||||
const kApiPropertyListOffset = 1;
|
||||
const kApiSerialNumberOffset = 2;
|
||||
const kApiConstructorOffset = 2;
|
||||
const kApiPrototypeTemplateOffset = 5;
|
||||
const kApiParentTemplateOffset = 6;
|
||||
const kApiFlagOffset = 14;
|
||||
|
||||
const NO_HINT = 0;
|
||||
const NUMBER_HINT = 1;
|
||||
const STRING_HINT = 2;
|
||||
|
||||
const kFunctionTag = 0;
|
||||
const kNewObjectTag = 1;
|
||||
|
||||
# For date.js.
|
||||
const HoursPerDay = 24;
|
||||
const MinutesPerHour = 60;
|
||||
const SecondsPerMinute = 60;
|
||||
const msPerSecond = 1000;
|
||||
const msPerMinute = 60000;
|
||||
const msPerHour = 3600000;
|
||||
const msPerDay = 86400000;
|
||||
const msPerMonth = 2592000000;
|
||||
|
||||
# For apinatives.js
|
||||
const kUninitialized = -1;
|
||||
const kReadOnlyPrototypeBit = 3; # For FunctionTemplateInfo, matches objects.h
|
||||
|
||||
# Note: kDayZeroInJulianDay = ToJulianDay(1970, 0, 1).
|
||||
const kInvalidDate = 'Invalid Date';
|
||||
const kDayZeroInJulianDay = 2440588;
|
||||
const kMonthMask = 0x1e0;
|
||||
const kDayMask = 0x01f;
|
||||
const kYearShift = 9;
|
||||
const kMonthShift = 5;
|
||||
|
||||
# Limits for parts of the date, so that we support all the dates that
|
||||
# ECMA 262 - 15.9.1.1 requires us to, but at the same time be sure that
|
||||
# the date (days since 1970) is in SMI range.
|
||||
const kMinYear = -1000000;
|
||||
const kMaxYear = 1000000;
|
||||
const kMinMonth = -10000000;
|
||||
const kMaxMonth = 10000000;
|
||||
|
||||
# Native cache ids.
|
||||
const STRING_TO_REGEXP_CACHE_ID = 0;
|
||||
|
||||
# Type query macros.
|
||||
#
|
||||
# Note: We have special support for typeof(foo) === 'bar' in the compiler.
|
||||
# It will *not* generate a runtime typeof call for the most important
|
||||
# values of 'bar'.
|
||||
macro IS_NULL(arg) = (arg === null);
|
||||
macro IS_NULL_OR_UNDEFINED(arg) = (arg == null);
|
||||
macro IS_UNDEFINED(arg) = (typeof(arg) === 'undefined');
|
||||
macro IS_NUMBER(arg) = (typeof(arg) === 'number');
|
||||
macro IS_STRING(arg) = (typeof(arg) === 'string');
|
||||
macro IS_BOOLEAN(arg) = (typeof(arg) === 'boolean');
|
||||
macro IS_OBJECT(arg) = (%_IsObject(arg));
|
||||
macro IS_ARRAY(arg) = (%_IsArray(arg));
|
||||
macro IS_FUNCTION(arg) = (%_IsFunction(arg));
|
||||
macro IS_REGEXP(arg) = (%_IsRegExp(arg));
|
||||
macro IS_SET(arg) = (%_ClassOf(arg) === 'Set');
|
||||
macro IS_MAP(arg) = (%_ClassOf(arg) === 'Map');
|
||||
macro IS_WEAKMAP(arg) = (%_ClassOf(arg) === 'WeakMap');
|
||||
macro IS_DATE(arg) = (%_ClassOf(arg) === 'Date');
|
||||
macro IS_NUMBER_WRAPPER(arg) = (%_ClassOf(arg) === 'Number');
|
||||
macro IS_STRING_WRAPPER(arg) = (%_ClassOf(arg) === 'String');
|
||||
macro IS_BOOLEAN_WRAPPER(arg) = (%_ClassOf(arg) === 'Boolean');
|
||||
macro IS_ERROR(arg) = (%_ClassOf(arg) === 'Error');
|
||||
macro IS_SCRIPT(arg) = (%_ClassOf(arg) === 'Script');
|
||||
macro IS_ARGUMENTS(arg) = (%_ClassOf(arg) === 'Arguments');
|
||||
macro IS_GLOBAL(arg) = (%_ClassOf(arg) === 'global');
|
||||
macro IS_UNDETECTABLE(arg) = (%_IsUndetectableObject(arg));
|
||||
macro FLOOR(arg) = $floor(arg);
|
||||
|
||||
# Macro for ECMAScript 5 queries of the type:
|
||||
# "Type(O) is object."
|
||||
# This is the same as being either a function or an object in V8 terminology
|
||||
# (including proxies).
|
||||
# In addition, an undetectable object is also included by this.
|
||||
macro IS_SPEC_OBJECT(arg) = (%_IsSpecObject(arg));
|
||||
|
||||
# Macro for ECMAScript 5 queries of the type:
|
||||
# "IsCallable(O)"
|
||||
# We assume here that this is the same as being either a function or a function
|
||||
# proxy. That ignores host objects with [[Call]] methods, but in most situations
|
||||
# we cannot handle those anyway.
|
||||
macro IS_SPEC_FUNCTION(arg) = (%_ClassOf(arg) === 'Function');
|
||||
|
||||
# Indices in bound function info retrieved by %BoundFunctionGetBindings(...).
|
||||
const kBoundFunctionIndex = 0;
|
||||
const kBoundThisIndex = 1;
|
||||
const kBoundArgumentsStartIndex = 2;
|
||||
|
||||
# Inline macros. Use %IS_VAR to make sure arg is evaluated only once.
|
||||
macro NUMBER_IS_NAN(arg) = (!%_IsSmi(%IS_VAR(arg)) && !(arg == arg));
|
||||
macro NUMBER_IS_FINITE(arg) = (%_IsSmi(%IS_VAR(arg)) || ((arg == arg) && (arg != 1/0) && (arg != -1/0)));
|
||||
macro TO_INTEGER(arg) = (%_IsSmi(%IS_VAR(arg)) ? arg : %NumberToInteger(ToNumber(arg)));
|
||||
macro TO_INTEGER_MAP_MINUS_ZERO(arg) = (%_IsSmi(%IS_VAR(arg)) ? arg : %NumberToIntegerMapMinusZero(ToNumber(arg)));
|
||||
macro TO_INT32(arg) = (%_IsSmi(%IS_VAR(arg)) ? arg : (arg >> 0));
|
||||
macro TO_UINT32(arg) = (arg >>> 0);
|
||||
macro TO_STRING_INLINE(arg) = (IS_STRING(%IS_VAR(arg)) ? arg : NonStringToString(arg));
|
||||
macro TO_NUMBER_INLINE(arg) = (IS_NUMBER(%IS_VAR(arg)) ? arg : NonNumberToNumber(arg));
|
||||
macro TO_OBJECT_INLINE(arg) = (IS_SPEC_OBJECT(%IS_VAR(arg)) ? arg : ToObject(arg));
|
||||
macro JSON_NUMBER_TO_STRING(arg) = ((%_IsSmi(%IS_VAR(arg)) || arg - arg == 0) ? %_NumberToString(arg) : "null");
|
||||
|
||||
# Macros implemented in Python.
|
||||
python macro CHAR_CODE(str) = ord(str[1]);
|
||||
|
||||
# Constants used on an array to implement the properties of the RegExp object.
|
||||
const REGEXP_NUMBER_OF_CAPTURES = 0;
|
||||
const REGEXP_FIRST_CAPTURE = 3;
|
||||
|
||||
# We can't put macros in macros so we use constants here.
|
||||
# REGEXP_NUMBER_OF_CAPTURES
|
||||
macro NUMBER_OF_CAPTURES(array) = ((array)[0]);
|
||||
|
||||
# Limit according to ECMA 262 15.9.1.1
|
||||
const MAX_TIME_MS = 8640000000000000;
|
||||
# Limit which is MAX_TIME_MS + msPerMonth.
|
||||
const MAX_TIME_BEFORE_UTC = 8640002592000000;
|
||||
|
||||
# Gets the value of a Date object. If arg is not a Date object
|
||||
# a type error is thrown.
|
||||
macro CHECK_DATE(arg) = if (%_ClassOf(arg) !== 'Date') ThrowDateTypeError();
|
||||
macro LOCAL_DATE_VALUE(arg) = (%_DateField(arg, 0) + %_DateField(arg, 21));
|
||||
macro UTC_DATE_VALUE(arg) = (%_DateField(arg, 0));
|
||||
|
||||
macro LOCAL_YEAR(arg) = (%_DateField(arg, 1));
|
||||
macro LOCAL_MONTH(arg) = (%_DateField(arg, 2));
|
||||
macro LOCAL_DAY(arg) = (%_DateField(arg, 3));
|
||||
macro LOCAL_WEEKDAY(arg) = (%_DateField(arg, 4));
|
||||
macro LOCAL_HOUR(arg) = (%_DateField(arg, 5));
|
||||
macro LOCAL_MIN(arg) = (%_DateField(arg, 6));
|
||||
macro LOCAL_SEC(arg) = (%_DateField(arg, 7));
|
||||
macro LOCAL_MS(arg) = (%_DateField(arg, 8));
|
||||
macro LOCAL_DAYS(arg) = (%_DateField(arg, 9));
|
||||
macro LOCAL_TIME_IN_DAY(arg) = (%_DateField(arg, 10));
|
||||
|
||||
macro UTC_YEAR(arg) = (%_DateField(arg, 11));
|
||||
macro UTC_MONTH(arg) = (%_DateField(arg, 12));
|
||||
macro UTC_DAY(arg) = (%_DateField(arg, 13));
|
||||
macro UTC_WEEKDAY(arg) = (%_DateField(arg, 14));
|
||||
macro UTC_HOUR(arg) = (%_DateField(arg, 15));
|
||||
macro UTC_MIN(arg) = (%_DateField(arg, 16));
|
||||
macro UTC_SEC(arg) = (%_DateField(arg, 17));
|
||||
macro UTC_MS(arg) = (%_DateField(arg, 18));
|
||||
macro UTC_DAYS(arg) = (%_DateField(arg, 19));
|
||||
macro UTC_TIME_IN_DAY(arg) = (%_DateField(arg, 20));
|
||||
|
||||
macro TIMEZONE_OFFSET(arg) = (%_DateField(arg, 21));
|
||||
|
||||
macro SET_UTC_DATE_VALUE(arg, value) = (%DateSetValue(arg, value, 1));
|
||||
macro SET_LOCAL_DATE_VALUE(arg, value) = (%DateSetValue(arg, value, 0));
|
||||
|
||||
# Last input and last subject of regexp matches.
|
||||
const LAST_SUBJECT_INDEX = 1;
|
||||
macro LAST_SUBJECT(array) = ((array)[1]);
|
||||
macro LAST_INPUT(array) = ((array)[2]);
|
||||
|
||||
# REGEXP_FIRST_CAPTURE
|
||||
macro CAPTURE(index) = (3 + (index));
|
||||
const CAPTURE0 = 3;
|
||||
const CAPTURE1 = 4;
|
||||
|
||||
# For the regexp capture override array. This has the same
|
||||
# format as the arguments to a function called from
|
||||
# String.prototype.replace.
|
||||
macro OVERRIDE_MATCH(override) = ((override)[0]);
|
||||
macro OVERRIDE_POS(override) = ((override)[(override).length - 2]);
|
||||
macro OVERRIDE_SUBJECT(override) = ((override)[(override).length - 1]);
|
||||
# 1-based so index of 1 returns the first capture
|
||||
macro OVERRIDE_CAPTURE(override, index) = ((override)[(index)]);
|
||||
|
||||
# PropertyDescriptor return value indices - must match
|
||||
# PropertyDescriptorIndices in runtime.cc.
|
||||
const IS_ACCESSOR_INDEX = 0;
|
||||
const VALUE_INDEX = 1;
|
||||
const GETTER_INDEX = 2;
|
||||
const SETTER_INDEX = 3;
|
||||
const WRITABLE_INDEX = 4;
|
||||
const ENUMERABLE_INDEX = 5;
|
||||
const CONFIGURABLE_INDEX = 6;
|
||||
|
||||
# For messages.js
|
||||
# Matches Script::Type from objects.h
|
||||
const TYPE_NATIVE = 0;
|
||||
const TYPE_EXTENSION = 1;
|
||||
const TYPE_NORMAL = 2;
|
||||
|
||||
# Matches Script::CompilationType from objects.h
|
||||
const COMPILATION_TYPE_HOST = 0;
|
||||
const COMPILATION_TYPE_EVAL = 1;
|
||||
const COMPILATION_TYPE_JSON = 2;
|
||||
|
||||
# Matches Messages::kNoLineNumberInfo from v8.h
|
||||
const kNoLineNumberInfo = 0;
|
|
@ -111,7 +111,7 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain, StackFrame *call
|
|||
JS_ASSERT_IF(globalScope, JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(globalScope->getClass()));
|
||||
|
||||
BytecodeEmitter bce(/* parent = */ NULL, &parser, &sc, script, callerFrame, !!globalScope,
|
||||
options.lineno);
|
||||
options.lineno, options.selfHostingMode);
|
||||
if (!bce.init())
|
||||
return NULL;
|
||||
|
||||
|
|
|
@ -103,7 +103,7 @@ struct frontend::StmtInfoBCE : public StmtInfoBase
|
|||
|
||||
BytecodeEmitter::BytecodeEmitter(BytecodeEmitter *parent, Parser *parser, SharedContext *sc,
|
||||
HandleScript script, StackFrame *callerFrame, bool hasGlobalScope,
|
||||
unsigned lineno)
|
||||
unsigned lineno, bool selfHostingMode)
|
||||
: sc(sc),
|
||||
parent(parent),
|
||||
script(sc->context, script),
|
||||
|
@ -122,7 +122,8 @@ BytecodeEmitter::BytecodeEmitter(BytecodeEmitter *parent, Parser *parser, Shared
|
|||
typesetCount(0),
|
||||
hasSingletons(false),
|
||||
inForInit(false),
|
||||
hasGlobalScope(hasGlobalScope)
|
||||
hasGlobalScope(hasGlobalScope),
|
||||
selfHostingMode(selfHostingMode)
|
||||
{
|
||||
JS_ASSERT_IF(callerFrame, callerFrame->isScriptFrame());
|
||||
memset(&prolog, 0, sizeof prolog);
|
||||
|
@ -1150,7 +1151,7 @@ EmitEnterBlock(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSOp op)
|
|||
* Try to convert a *NAME op to a *GNAME op, which optimizes access to
|
||||
* undeclared globals. Return true if a conversion was made.
|
||||
*
|
||||
* This conversion is not made if we are in strict mode. In eval code nested
|
||||
* This conversion is not made if we are in strict mode. In eval code nested
|
||||
* within (strict mode) eval code, access to an undeclared "global" might
|
||||
* merely be to a binding local to that outer eval:
|
||||
*
|
||||
|
@ -1166,15 +1167,26 @@ EmitEnterBlock(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSOp op)
|
|||
* undeclared = 17; // throws ReferenceError
|
||||
* }
|
||||
* foo();
|
||||
*
|
||||
* In self-hosting mode, JSOP_NAME is unconditionally converted to
|
||||
* JSOP_INTRINSICNAME. This causes the lookup to be redirected to the special
|
||||
* intrinsics holder in the global object, into which any missing objects are
|
||||
* cloned lazily upon first access.
|
||||
*/
|
||||
static bool
|
||||
TryConvertToGname(BytecodeEmitter *bce, ParseNode *pn, JSOp *op)
|
||||
{
|
||||
if (bce->selfHostingMode) {
|
||||
JS_ASSERT(*op == JSOP_NAME);
|
||||
*op = JSOP_INTRINSICNAME;
|
||||
return true;
|
||||
}
|
||||
if (bce->script->compileAndGo &&
|
||||
bce->hasGlobalScope &&
|
||||
!bce->sc->funMightAliasLocals() &&
|
||||
!pn->isDeoptimized() &&
|
||||
!bce->sc->inStrictMode()) {
|
||||
!bce->sc->inStrictMode())
|
||||
{
|
||||
switch (*op) {
|
||||
case JSOP_NAME: *op = JSOP_GETGNAME; break;
|
||||
case JSOP_SETNAME: *op = JSOP_SETGNAME; break;
|
||||
|
@ -4865,7 +4877,7 @@ EmitFunc(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
script->bindings = funbox->bindings;
|
||||
|
||||
BytecodeEmitter bce2(bce, bce->parser, &sc, script, bce->callerFrame, bce->hasGlobalScope,
|
||||
pn->pn_pos.begin.lineno);
|
||||
pn->pn_pos.begin.lineno, bce->selfHostingMode);
|
||||
if (!bce2.init())
|
||||
return false;
|
||||
|
||||
|
|
|
@ -115,9 +115,14 @@ struct BytecodeEmitter
|
|||
const bool hasGlobalScope:1; /* frontend::CompileScript's scope chain is the
|
||||
global object */
|
||||
|
||||
const bool selfHostingMode:1; /* Emit JSOP_CALLINTRINSIC instead of JSOP_NAME
|
||||
and assert that JSOP_NAME and JSOP_*GNAME
|
||||
don't ever get emitted. See the comment for
|
||||
the field |selfHostingMode| in Parser.h for details. */
|
||||
|
||||
BytecodeEmitter(BytecodeEmitter *parent, Parser *parser, SharedContext *sc,
|
||||
HandleScript script, StackFrame *callerFrame, bool hasGlobalScope,
|
||||
unsigned lineno);
|
||||
unsigned lineno, bool selfHostingMode = false);
|
||||
bool init();
|
||||
|
||||
/*
|
||||
|
|
|
@ -118,7 +118,7 @@ Parser::Parser(JSContext *cx, const CompileOptions &options,
|
|||
keepAtoms(cx->runtime),
|
||||
foldConstants(foldConstants),
|
||||
compileAndGo(options.compileAndGo),
|
||||
allowIntrinsicsCalls(options.allowIntrinsicsCalls)
|
||||
selfHostingMode(options.selfHostingMode)
|
||||
{
|
||||
cx->activeCompilations++;
|
||||
}
|
||||
|
@ -837,9 +837,10 @@ Parser::newFunction(TreeContext *tc, JSAtom *atom, FunctionSyntaxKind kind)
|
|||
parent = tc->sc->inFunction() ? NULL : tc->sc->scopeChain();
|
||||
|
||||
RootedFunction fun(context);
|
||||
fun = js_NewFunction(context, NULL, NULL, 0,
|
||||
JSFUN_INTERPRETED | (kind == Expression ? JSFUN_LAMBDA : 0),
|
||||
parent, atom);
|
||||
uint32_t flags = JSFUN_INTERPRETED | (kind == Expression ? JSFUN_LAMBDA : 0);
|
||||
if (selfHostingMode)
|
||||
flags |= JSFUN_SELF_HOSTED;
|
||||
fun = js_NewFunction(context, NULL, NULL, 0, flags, parent, atom);
|
||||
if (fun && !compileAndGo) {
|
||||
if (!JSObject::clearParent(context, fun))
|
||||
return NULL;
|
||||
|
@ -6773,7 +6774,7 @@ Parser::primaryExpr(TokenKind tt, bool afterDoubleDot)
|
|||
return new_<NullLiteral>(tokenStream.currentToken().pos);
|
||||
|
||||
case TOK_MOD:
|
||||
if (allowIntrinsicsCalls)
|
||||
if (selfHostingMode)
|
||||
return intrinsicName();
|
||||
else
|
||||
goto syntaxerror;
|
||||
|
|
|
@ -55,10 +55,20 @@ struct Parser : private AutoGCRooter
|
|||
const bool compileAndGo:1;
|
||||
|
||||
/*
|
||||
* Self-hosted scripts can use the special syntax %funName(..args) to call
|
||||
* internal functions.
|
||||
* In self-hosting mode, scripts emit JSOP_CALLINTRINSIC instead of
|
||||
* JSOP_NAME or JSOP_GNAME to access unbound variables. JSOP_CALLINTRINSIC
|
||||
* does a name lookup in a special object that contains properties
|
||||
* installed during global initialization and that properties from
|
||||
* self-hosted scripts get copied into lazily upon first access in a
|
||||
* global.
|
||||
* As that object is inaccessible to client code, the lookups are
|
||||
* guaranteed to return the original objects, ensuring safe implementation
|
||||
* of self-hosted builtins.
|
||||
* Additionally, the special syntax %_CallName(receiver, ...args, fun) is
|
||||
* supported, for which bytecode is emitted that invokes |fun| with
|
||||
* |receiver| as the this-object and ...args as the arguments..
|
||||
*/
|
||||
const bool allowIntrinsicsCalls:1;
|
||||
const bool selfHostingMode:1;
|
||||
|
||||
public:
|
||||
Parser(JSContext *cx, const CompileOptions &options,
|
||||
|
|
|
@ -733,6 +733,7 @@ JSRuntime::JSRuntime()
|
|||
#ifdef JS_METHODJIT
|
||||
jaegerRuntime_(NULL),
|
||||
#endif
|
||||
selfHostedGlobal_(NULL),
|
||||
nativeStackBase(0),
|
||||
nativeStackQuota(0),
|
||||
interpreterFrames(NULL),
|
||||
|
@ -890,7 +891,8 @@ JSRuntime::init(uint32_t maxbytes)
|
|||
|
||||
if (!(atomsCompartment = this->new_<JSCompartment>(this)) ||
|
||||
!atomsCompartment->init(NULL) ||
|
||||
!compartments.append(atomsCompartment)) {
|
||||
!compartments.append(atomsCompartment))
|
||||
{
|
||||
Foreground::delete_(atomsCompartment);
|
||||
return false;
|
||||
}
|
||||
|
@ -4882,22 +4884,19 @@ JS_CloneFunctionObject(JSContext *cx, JSObject *funobjArg, JSRawObject parentArg
|
|||
}
|
||||
|
||||
/*
|
||||
* If a function was compiled as compile-and-go or was compiled to be
|
||||
* lexically nested inside some other script, we cannot clone it without
|
||||
* breaking the compiler's assumptions.
|
||||
* If a function was compiled to be lexically nested inside some other
|
||||
* script, we cannot clone it without breaking the compiler's assumptions.
|
||||
*/
|
||||
RootedFunction fun(cx, funobj->toFunction());
|
||||
if (fun->isInterpreted() &&
|
||||
(fun->script()->compileAndGo || fun->script()->enclosingStaticScope()))
|
||||
if (fun->isInterpreted() && (fun->script()->enclosingStaticScope() ||
|
||||
(fun->script()->compileAndGo && !parent->isGlobal())))
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_BAD_CLONE_FUNOBJ_SCOPE);
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_CLONE_FUNOBJ_SCOPE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (fun->isBoundFunction()) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_CANT_CLONE_OBJECT);
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_CLONE_OBJECT);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -5019,7 +5018,7 @@ JS_DefineFunctions(JSContext *cx, JSObject *objArg, JSFunctionSpec *fs)
|
|||
|
||||
flags &= ~JSFUN_GENERIC_NATIVE;
|
||||
fun = js_DefineFunction(cx, ctor, id, js_generic_native_method_dispatcher,
|
||||
fs->nargs + 1, flags, JSFunction::ExtendedFinalizeKind);
|
||||
fs->nargs + 1, flags, NULL, JSFunction::ExtendedFinalizeKind);
|
||||
if (!fun)
|
||||
return JS_FALSE;
|
||||
|
||||
|
@ -5030,7 +5029,7 @@ JS_DefineFunctions(JSContext *cx, JSObject *objArg, JSFunctionSpec *fs)
|
|||
fun->setExtendedSlot(0, PrivateValue(fs));
|
||||
}
|
||||
|
||||
fun = js_DefineFunction(cx, obj, id, fs->call.op, fs->nargs, flags);
|
||||
fun = js_DefineFunction(cx, obj, id, fs->call.op, fs->nargs, flags, fs->selfHostedName);
|
||||
if (!fun)
|
||||
return JS_FALSE;
|
||||
if (fs->call.info)
|
||||
|
@ -5196,7 +5195,7 @@ JS::CompileOptions::CompileOptions(JSContext *cx)
|
|||
lineno(1),
|
||||
compileAndGo(cx->hasRunOption(JSOPTION_COMPILE_N_GO)),
|
||||
noScriptRval(cx->hasRunOption(JSOPTION_NO_SCRIPT_RVAL)),
|
||||
allowIntrinsicsCalls(false),
|
||||
selfHostingMode(false),
|
||||
sourcePolicy(SAVE_SOURCE)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -2540,6 +2540,11 @@ class AutoIdRooter : private AutoGCRooter
|
|||
|
||||
/* Function flags, internal use only, returned by JS_GetFunctionFlags. */
|
||||
#define JSFUN_LAMBDA 0x08 /* expressed, not declared, function */
|
||||
|
||||
#define JSFUN_SELF_HOSTED 0x40 /* function is self-hosted native and
|
||||
must not be decompilable nor
|
||||
constructible. */
|
||||
|
||||
#define JSFUN_HEAVYWEIGHT 0x80 /* activation requires a Call object */
|
||||
|
||||
#define JSFUN_HEAVYWEIGHT_TEST(f) ((f) & JSFUN_HEAVYWEIGHT)
|
||||
|
@ -4414,11 +4419,17 @@ struct JSPropertySpec {
|
|||
JSStrictPropertyOpWrapper setter;
|
||||
};
|
||||
|
||||
/*
|
||||
* To define a native function, set call to a JSNativeWrapper. To define a
|
||||
* self-hosted function, set selfHostedName to the name of a function
|
||||
* compiled during JSRuntime::initSelfHosting.
|
||||
*/
|
||||
struct JSFunctionSpec {
|
||||
const char *name;
|
||||
JSNativeWrapper call;
|
||||
uint16_t nargs;
|
||||
uint16_t flags;
|
||||
const char *selfHostedName;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -5147,7 +5158,7 @@ struct JS_PUBLIC_API(CompileOptions) {
|
|||
unsigned lineno;
|
||||
bool compileAndGo;
|
||||
bool noScriptRval;
|
||||
bool allowIntrinsicsCalls;
|
||||
bool selfHostingMode;
|
||||
enum SourcePolicy {
|
||||
NO_SOURCE,
|
||||
LAZY_SOURCE,
|
||||
|
@ -5164,7 +5175,7 @@ struct JS_PUBLIC_API(CompileOptions) {
|
|||
}
|
||||
CompileOptions &setCompileAndGo(bool cng) { compileAndGo = cng; return *this; }
|
||||
CompileOptions &setNoScriptRval(bool nsr) { noScriptRval = nsr; return *this; }
|
||||
CompileOptions &setAllowIntrinsicsCalls(bool aic) { allowIntrinsicsCalls = aic; return *this; }
|
||||
CompileOptions &setSelfHostingMode(bool shm) { selfHostingMode = shm; return *this; }
|
||||
CompileOptions &setSourcePolicy(SourcePolicy sp) { sourcePolicy = sp; return *this; }
|
||||
};
|
||||
|
||||
|
|
|
@ -58,6 +58,8 @@
|
|||
#include "jscompartment.h"
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
#include "selfhosted.out.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::gc;
|
||||
|
||||
|
@ -201,6 +203,84 @@ JSRuntime::createJaegerRuntime(JSContext *cx)
|
|||
}
|
||||
#endif
|
||||
|
||||
|
||||
static JSClass self_hosting_global_class = {
|
||||
"self-hosting-global", JSCLASS_GLOBAL_FLAGS,
|
||||
JS_PropertyStub, JS_PropertyStub,
|
||||
JS_PropertyStub, JS_StrictPropertyStub,
|
||||
JS_EnumerateStub, JS_ResolveStub,
|
||||
JS_ConvertStub, NULL
|
||||
};
|
||||
bool
|
||||
JSRuntime::initSelfHosting(JSContext *cx)
|
||||
{
|
||||
JS_ASSERT(!selfHostedGlobal_);
|
||||
RootedObject savedGlobal(cx, JS_GetGlobalObject(cx));
|
||||
if (!(selfHostedGlobal_ = JS_NewGlobalObject(cx, &self_hosting_global_class, NULL)))
|
||||
return false;
|
||||
JS_SetGlobalObject(cx, selfHostedGlobal_);
|
||||
|
||||
JSAutoEnterCompartment ac;
|
||||
if (!ac.enter(cx, cx->global()))
|
||||
return false;
|
||||
|
||||
const char *src = selfhosted::raw_sources;
|
||||
uint32_t srcLen = selfhosted::GetRawScriptsSize();
|
||||
|
||||
CompileOptions options(cx);
|
||||
options.setFileAndLine("self-hosted", 1);
|
||||
options.setSelfHostingMode(true);
|
||||
|
||||
RootedObject shg(cx, selfHostedGlobal_);
|
||||
Value rv;
|
||||
if (!Evaluate(cx, shg, options, src, srcLen, &rv))
|
||||
return false;
|
||||
|
||||
JS_SetGlobalObject(cx, savedGlobal);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
JSRuntime::markSelfHostedGlobal(JSTracer *trc)
|
||||
{
|
||||
MarkObjectRoot(trc, &selfHostedGlobal_, "self-hosting global");
|
||||
}
|
||||
|
||||
JSFunction *
|
||||
JSRuntime::getSelfHostedFunction(JSContext *cx, const char *name)
|
||||
{
|
||||
RootedObject holder(cx, cx->global()->getIntrinsicsHolder());
|
||||
JSAtom *atom = Atomize(cx, name, strlen(name));
|
||||
if (!atom)
|
||||
return NULL;
|
||||
Value funVal = NullValue();
|
||||
JSAutoByteString bytes;
|
||||
if (!cloneSelfHostedValueById(cx, AtomToId(atom), holder, &funVal))
|
||||
return NULL;
|
||||
return funVal.toObject().toFunction();
|
||||
}
|
||||
|
||||
bool
|
||||
JSRuntime::cloneSelfHostedValueById(JSContext *cx, jsid id, HandleObject holder, Value *vp)
|
||||
{
|
||||
Value funVal;
|
||||
{
|
||||
RootedObject shg(cx, selfHostedGlobal_);
|
||||
JSAutoEnterCompartment ac;
|
||||
if (!ac.enter(cx, shg) || !JS_GetPropertyById(cx, shg, id, &funVal) || !funVal.isObject())
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedObject clone(cx, JS_CloneFunctionObject(cx, &funVal.toObject(), cx->global()));
|
||||
if (!clone)
|
||||
return false;
|
||||
|
||||
vp->setObjectOrNull(clone);
|
||||
DebugOnly<bool> ok = JS_DefinePropertyById(cx, holder, id, *vp, NULL, NULL, 0);
|
||||
JS_ASSERT(ok);
|
||||
return true;
|
||||
}
|
||||
|
||||
JSScript *
|
||||
js_GetCurrentScript(JSContext *cx)
|
||||
{
|
||||
|
@ -234,11 +314,11 @@ js::NewContext(JSRuntime *rt, size_t stackChunkSize)
|
|||
|
||||
/*
|
||||
* If cx is the first context on this runtime, initialize well-known atoms,
|
||||
* keywords, numbers, and strings. If one of these steps should fail, the
|
||||
* runtime will be left in a partially initialized state, with zeroes and
|
||||
* nulls stored in the default-initialized remainder of the struct. We'll
|
||||
* clean the runtime up under DestroyContext, because cx will be "last"
|
||||
* as well as "first".
|
||||
* keywords, numbers, strings and self-hosted scripts. If one of these
|
||||
* steps should fail, the runtime will be left in a partially initialized
|
||||
* state, with zeroes and nulls stored in the default-initialized remainder
|
||||
* of the struct. We'll clean the runtime up under DestroyContext, because
|
||||
* cx will be "last" as well as "first".
|
||||
*/
|
||||
if (first) {
|
||||
#ifdef JS_THREADSAFE
|
||||
|
@ -247,6 +327,8 @@ js::NewContext(JSRuntime *rt, size_t stackChunkSize)
|
|||
bool ok = rt->staticStrings.init(cx);
|
||||
if (ok)
|
||||
ok = InitCommonAtoms(cx);
|
||||
if (ok)
|
||||
ok = rt->initSelfHosting(cx);
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
JS_EndRequest(cx);
|
||||
|
@ -384,10 +466,10 @@ static void
|
|||
PopulateReportBlame(JSContext *cx, JSErrorReport *report)
|
||||
{
|
||||
/*
|
||||
* Walk stack until we find a frame that is associated with some script
|
||||
* rather than a native frame.
|
||||
* Walk stack until we find a frame that is associated with a non-builtin
|
||||
* rather than a builtin frame.
|
||||
*/
|
||||
ScriptFrameIter iter(cx);
|
||||
NonBuiltinScriptFrameIter iter(cx);
|
||||
if (iter.done())
|
||||
return;
|
||||
|
||||
|
|
|
@ -407,6 +407,8 @@ struct JSRuntime : js::RuntimeFriendFields
|
|||
js::mjit::JaegerRuntime *jaegerRuntime_;
|
||||
#endif
|
||||
|
||||
JSObject *selfHostedGlobal_;
|
||||
|
||||
JSC::ExecutableAllocator *createExecutableAllocator(JSContext *cx);
|
||||
WTF::BumpPointerAllocator *createBumpPointerAllocator(JSContext *cx);
|
||||
js::mjit::JaegerRuntime *createJaegerRuntime(JSContext *cx);
|
||||
|
@ -435,6 +437,11 @@ struct JSRuntime : js::RuntimeFriendFields
|
|||
}
|
||||
#endif
|
||||
|
||||
bool initSelfHosting(JSContext *cx);
|
||||
void markSelfHostedGlobal(JSTracer *trc);
|
||||
JSFunction *getSelfHostedFunction(JSContext *cx, const char *name);
|
||||
bool cloneSelfHostedValueById(JSContext *cx, jsid id, js::HandleObject holder, js::Value *vp);
|
||||
|
||||
/* Base address of the native stack for the current thread. */
|
||||
uintptr_t nativeStackBase;
|
||||
|
||||
|
|
|
@ -260,7 +260,7 @@ InitExnPrivate(JSContext *cx, HandleObject exnObject, HandleString message,
|
|||
Vector<JSStackTraceStackElem> frames(cx);
|
||||
{
|
||||
SuppressErrorsGuard seg(cx);
|
||||
for (ScriptFrameIter i(cx); !i.done(); ++i) {
|
||||
for (NonBuiltinScriptFrameIter i(cx); !i.done(); ++i) {
|
||||
StackFrame *fp = i.fp();
|
||||
|
||||
/*
|
||||
|
@ -560,7 +560,7 @@ Exception(JSContext *cx, unsigned argc, Value *vp)
|
|||
}
|
||||
|
||||
/* Find the scripted caller. */
|
||||
ScriptFrameIter iter(cx);
|
||||
NonBuiltinScriptFrameIter iter(cx);
|
||||
|
||||
/* XXX StackIter should not point directly to scripts. */
|
||||
SkipRoot skip(cx, &iter);
|
||||
|
|
|
@ -364,7 +364,7 @@ js::DefineFunctionWithReserved(JSContext *cx, JSObject *objArg, const char *name
|
|||
if (!atom)
|
||||
return NULL;
|
||||
Rooted<jsid> id(cx, AtomToId(atom));
|
||||
return js_DefineFunction(cx, obj, id, call, nargs, attrs, JSFunction::ExtendedFinalizeKind);
|
||||
return js_DefineFunction(cx, obj, id, call, nargs, attrs, NULL, JSFunction::ExtendedFinalizeKind);
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSFunction *)
|
||||
|
|
|
@ -606,7 +606,7 @@ js::FunctionToString(JSContext *cx, HandleFunction fun, bool bodyOnly, bool lamb
|
|||
return NULL;
|
||||
}
|
||||
}
|
||||
bool haveSource = fun->isInterpreted();
|
||||
bool haveSource = fun->isInterpreted() && !fun->isSelfHostedBuiltin();
|
||||
if (haveSource && !fun->script()->scriptSource()->hasSourceData() &&
|
||||
!fun->script()->loadSource(cx, &haveSource))
|
||||
{
|
||||
|
@ -709,7 +709,7 @@ js::FunctionToString(JSContext *cx, HandleFunction fun, bool bodyOnly, bool lamb
|
|||
if (!out.append(")"))
|
||||
return NULL;
|
||||
}
|
||||
} else if (fun->isInterpreted()) {
|
||||
} else if (fun->isInterpreted() && !fun->isSelfHostedBuiltin()) {
|
||||
if ((!bodyOnly && !out.append("() {\n ")) ||
|
||||
!out.append("[sourceless code]") ||
|
||||
(!bodyOnly && !out.append("\n}")))
|
||||
|
@ -1451,7 +1451,7 @@ js_CloneFunctionObject(JSContext *cx, HandleFunction fun, HandleObject parent,
|
|||
JS_ASSERT(script);
|
||||
JS_ASSERT(script->compartment() == fun->compartment());
|
||||
JS_ASSERT_IF(script->compartment() != cx->compartment,
|
||||
!script->enclosingStaticScope() && !script->compileAndGo);
|
||||
!script->enclosingStaticScope());
|
||||
|
||||
RootedObject scope(cx, script->enclosingStaticScope());
|
||||
|
||||
|
@ -1475,7 +1475,7 @@ js_CloneFunctionObject(JSContext *cx, HandleFunction fun, HandleObject parent,
|
|||
|
||||
JSFunction *
|
||||
js_DefineFunction(JSContext *cx, HandleObject obj, HandleId id, Native native,
|
||||
unsigned nargs, unsigned attrs, AllocKind kind)
|
||||
unsigned nargs, unsigned attrs, const char *selfHostedName, AllocKind kind)
|
||||
{
|
||||
PropertyOp gop;
|
||||
StrictPropertyOp sop;
|
||||
|
@ -1497,11 +1497,24 @@ js_DefineFunction(JSContext *cx, HandleObject obj, HandleId id, Native native,
|
|||
sop = NULL;
|
||||
}
|
||||
|
||||
fun = js_NewFunction(cx, NULL, native, nargs,
|
||||
attrs & (JSFUN_FLAGS_MASK),
|
||||
obj,
|
||||
JSID_IS_ATOM(id) ? JSID_TO_ATOM(id) : NULL,
|
||||
kind);
|
||||
/*
|
||||
* To support specifying both native and self-hosted functions using
|
||||
* JSFunctionSpec, js_DefineFunction can be invoked with either native
|
||||
* or selfHostedName set. It is assumed that selfHostedName is set if
|
||||
* native isn't.
|
||||
*/
|
||||
if (native) {
|
||||
JS_ASSERT(!selfHostedName);
|
||||
fun = js_NewFunction(cx, NULL, native, nargs,
|
||||
attrs & (JSFUN_FLAGS_MASK),
|
||||
obj,
|
||||
JSID_IS_ATOM(id) ? JSID_TO_ATOM(id) : NULL,
|
||||
kind);
|
||||
} else {
|
||||
JS_ASSERT(attrs & JSFUN_INTERPRETED);
|
||||
fun = cx->runtime->getSelfHostedFunction(cx, selfHostedName);
|
||||
fun->atom = JSID_TO_ATOM(id);
|
||||
}
|
||||
if (!fun)
|
||||
return NULL;
|
||||
|
||||
|
|
|
@ -74,10 +74,13 @@ struct JSFunction : public JSObject
|
|||
bool hasRest() const { return flags & JSFUN_HAS_REST; }
|
||||
bool isInterpreted() const { return flags & JSFUN_INTERPRETED; }
|
||||
bool isNative() const { return !isInterpreted(); }
|
||||
bool isSelfHostedBuiltin() const { return flags & JSFUN_SELF_HOSTED; }
|
||||
bool isNativeConstructor() const { return flags & JSFUN_CONSTRUCTOR; }
|
||||
bool isHeavyweight() const { return JSFUN_HEAVYWEIGHT_TEST(flags); }
|
||||
bool isFunctionPrototype() const { return flags & JSFUN_PROTOTYPE; }
|
||||
bool isInterpretedConstructor() const { return isInterpreted() && !isFunctionPrototype(); }
|
||||
bool isInterpretedConstructor() const {
|
||||
return isInterpreted() && !isFunctionPrototype() && !isSelfHostedBuiltin();
|
||||
}
|
||||
bool isNamedLambda() const { return (flags & JSFUN_LAMBDA) && atom; }
|
||||
|
||||
/* Returns the strictness of this function, which must be interpreted. */
|
||||
|
@ -228,7 +231,7 @@ js_CloneFunctionObject(JSContext *cx, js::HandleFunction fun,
|
|||
|
||||
extern JSFunction *
|
||||
js_DefineFunction(JSContext *cx, js::HandleObject obj, js::HandleId id, JSNative native,
|
||||
unsigned nargs, unsigned flags,
|
||||
unsigned nargs, unsigned flags, const char *selfHostedName = NULL,
|
||||
js::gc::AllocKind kind = JSFunction::FinalizeKind);
|
||||
|
||||
namespace js {
|
||||
|
|
|
@ -2496,6 +2496,7 @@ MarkRuntime(JSTracer *trc, bool useSavedRoots = false)
|
|||
#else
|
||||
MarkConservativeStackRoots(trc, useSavedRoots);
|
||||
#endif
|
||||
rt->markSelfHostedGlobal(trc);
|
||||
}
|
||||
|
||||
for (RootRange r = rt->gcRootsHash.all(); !r.empty(); r.popFront())
|
||||
|
|
|
@ -80,7 +80,7 @@ ComputeThis(JSContext *cx, StackFrame *fp)
|
|||
if (thisv.isObject())
|
||||
return true;
|
||||
if (fp->isFunctionFrame()) {
|
||||
if (fp->fun()->inStrictMode())
|
||||
if (fp->fun()->inStrictMode() || fp->fun()->isSelfHostedBuiltin())
|
||||
return true;
|
||||
/*
|
||||
* Eval function frames have their own |this| slot, which is a copy of the function's
|
||||
|
@ -364,8 +364,7 @@ IntrinsicNameOperation(JSContext *cx, JSScript *script, jsbytecode *pc, Value *v
|
|||
JSOp op = JSOp(*pc);
|
||||
RootedPropertyName name(cx);
|
||||
name = GetNameFromBytecode(cx, script, pc, op);
|
||||
JSFunction *fun = cx->global()->getIntrinsicFunction(cx, name);
|
||||
vp->setObject(*fun);
|
||||
cx->global()->getIntrinsicValue(cx, name, vp);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -5569,7 +5569,7 @@ js_DecompileFunctionBody(JSPrinter *jp)
|
|||
|
||||
JS_ASSERT(jp->fun);
|
||||
JS_ASSERT(!jp->script);
|
||||
if (!jp->fun->isInterpreted()) {
|
||||
if (jp->fun->isNative() || jp->fun->isSelfHostedBuiltin()) {
|
||||
js_printf(jp, native_code_str);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
@ -5604,7 +5604,7 @@ js_DecompileFunction(JSPrinter *jp)
|
|||
return JS_FALSE;
|
||||
js_puts(jp, "(");
|
||||
|
||||
if (!fun->isInterpreted()) {
|
||||
if (fun->isNative() || fun->isSelfHostedBuiltin()) {
|
||||
js_printf(jp, ") {\n");
|
||||
jp->indent += 4;
|
||||
js_printf(jp, native_code_str);
|
||||
|
@ -5738,7 +5738,7 @@ static JSObject *
|
|||
GetBlockChainAtPC(JSContext *cx, JSScript *script, jsbytecode *pc)
|
||||
{
|
||||
jsbytecode *start = script->main();
|
||||
|
||||
|
||||
JS_ASSERT(pc >= start && pc < script->code + script->length);
|
||||
|
||||
JSObject *blockChain = NULL;
|
||||
|
@ -5773,7 +5773,7 @@ GetBlockChainAtPC(JSContext *cx, JSScript *script, jsbytecode *pc)
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return blockChain;
|
||||
}
|
||||
|
||||
|
@ -6131,7 +6131,8 @@ ExpressionDecompiler::getOutput(char **res)
|
|||
}
|
||||
|
||||
static bool
|
||||
FindStartPC(JSContext *cx, JSScript *script, int spindex, Value v, jsbytecode **valuepc)
|
||||
FindStartPC(JSContext *cx, JSScript *script, int spindex, int skipStackHits,
|
||||
Value v, jsbytecode **valuepc)
|
||||
{
|
||||
jsbytecode *current = *valuepc;
|
||||
|
||||
|
@ -6150,10 +6151,11 @@ FindStartPC(JSContext *cx, JSScript *script, int spindex, Value v, jsbytecode **
|
|||
// exception.
|
||||
Value *stackBase = cx->regs().spForStackDepth(0);
|
||||
Value *sp = cx->regs().sp;
|
||||
int stackHits = 0;
|
||||
do {
|
||||
if (sp == stackBase)
|
||||
return true;
|
||||
} while (*--sp != v);
|
||||
} while (*--sp != v || stackHits++ != skipStackHits);
|
||||
if (sp < stackBase + pcstack.depth())
|
||||
*valuepc = pcstack[sp - stackBase];
|
||||
} else {
|
||||
|
@ -6163,7 +6165,7 @@ FindStartPC(JSContext *cx, JSScript *script, int spindex, Value v, jsbytecode **
|
|||
}
|
||||
|
||||
static bool
|
||||
DecompileExpressionFromStack(JSContext *cx, int spindex, Value v, char **res)
|
||||
DecompileExpressionFromStack(JSContext *cx, int spindex, int skipStackHits, Value v, char **res)
|
||||
{
|
||||
JS_ASSERT(spindex < 0 ||
|
||||
spindex == JSDVG_IGNORE_STACK ||
|
||||
|
@ -6183,7 +6185,7 @@ DecompileExpressionFromStack(JSContext *cx, int spindex, Value v, char **res)
|
|||
if (valuepc < script->main())
|
||||
return true;
|
||||
|
||||
if (!FindStartPC(cx, script, spindex, v, &valuepc))
|
||||
if (!FindStartPC(cx, script, spindex, skipStackHits, v, &valuepc))
|
||||
return false;
|
||||
if (!valuepc)
|
||||
return true;
|
||||
|
@ -6199,12 +6201,12 @@ DecompileExpressionFromStack(JSContext *cx, int spindex, Value v, char **res)
|
|||
|
||||
char *
|
||||
js::DecompileValueGenerator(JSContext *cx, int spindex, HandleValue v,
|
||||
HandleString fallbackArg)
|
||||
HandleString fallbackArg, int skipStackHits)
|
||||
{
|
||||
RootedString fallback(cx, fallbackArg);
|
||||
{
|
||||
char *result;
|
||||
if (!DecompileExpressionFromStack(cx, spindex, v, &result))
|
||||
if (!DecompileExpressionFromStack(cx, spindex, skipStackHits, v, &result))
|
||||
return NULL;
|
||||
if (result) {
|
||||
if (strcmp(result, "(intermediate value)"))
|
||||
|
|
|
@ -355,10 +355,16 @@ namespace js {
|
|||
* spindex is the negative index of v, measured from cx->fp->sp, or from a
|
||||
* lower frame's sp if cx->fp is native.
|
||||
*
|
||||
* The optional argument skipStackHits can be used to skip a hit in the stack
|
||||
* frame. This can be useful in self-hosted code that wants to report value
|
||||
* errors containing decompiled values that are useful for the user, instead of
|
||||
* values used internally by the self-hosted code.
|
||||
*
|
||||
* The caller must call JS_free on the result after a succsesful call.
|
||||
*/
|
||||
char *
|
||||
DecompileValueGenerator(JSContext *cx, int spindex, HandleValue v, HandleString fallback);
|
||||
DecompileValueGenerator(JSContext *cx, int spindex, HandleValue v,
|
||||
HandleString fallback, int skipStackHits = 0);
|
||||
|
||||
/*
|
||||
* Sprintf, but with unlimited and automatically allocated buffering.
|
||||
|
|
|
@ -353,7 +353,7 @@ OPDEF(JSOP_ALIASEDVARDEC, 142,"aliasedvardec",NULL, 10, 0, 1, 15, JOF_SCOPEC
|
|||
|
||||
/*
|
||||
* Intrinsic names have the syntax %name and can only be used when the
|
||||
* CompileOptions flag "allowIntrinsicsCalls" is set.
|
||||
* CompileOptions flag "selfHostingMode" is set.
|
||||
*
|
||||
* They are used to access intrinsic functions the runtime doesn't give
|
||||
* client JS code access to from self-hosted code.
|
||||
|
|
|
@ -1999,7 +1999,7 @@ void
|
|||
CurrentScriptFileLineOriginSlow(JSContext *cx, const char **file, unsigned *linenop,
|
||||
JSPrincipals **origin)
|
||||
{
|
||||
ScriptFrameIter iter(cx);
|
||||
NonBuiltinScriptFrameIter iter(cx);
|
||||
|
||||
if (iter.done()) {
|
||||
*file = NULL;
|
||||
|
|
|
@ -5602,8 +5602,9 @@ mjit::Compiler::jsop_setprop(PropertyName *name, bool popGuaranteed)
|
|||
void
|
||||
mjit::Compiler::jsop_intrinsicname(PropertyName *name, JSValueType type)
|
||||
{
|
||||
JSFunction *fun = cx->global().get()->getIntrinsicFunction(cx, name);
|
||||
frame.push(ObjectValue(*fun));
|
||||
Value vp = NullValue();
|
||||
cx->global().get()->getIntrinsicValue(cx, name, &vp);
|
||||
frame.push(vp);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -5908,11 +5909,13 @@ mjit::Compiler::jsop_this()
|
|||
frame.pushThis();
|
||||
|
||||
/*
|
||||
* In strict mode code, we don't wrap 'this'.
|
||||
* In strict mode and self-hosted code, we don't wrap 'this'.
|
||||
* In direct-call eval code, we wrapped 'this' before entering the eval.
|
||||
* In global code, 'this' is always an object.
|
||||
*/
|
||||
if (script->function() && !script->strictModeCode) {
|
||||
if (script->function() && !script->strictModeCode &&
|
||||
!script->function()->isSelfHostedBuiltin())
|
||||
{
|
||||
FrameEntry *thisFe = frame.peek(-1);
|
||||
|
||||
if (!thisFe->isType(JSVAL_TYPE_OBJECT)) {
|
||||
|
|
|
@ -173,8 +173,69 @@ ProtoSetter(JSContext *cx, unsigned argc, Value *vp)
|
|||
return CallNonGenericMethod(cx, TestProtoSetterThis, ProtoSetterImpl, args);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
intrinsic_ToObject(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
RootedValue val(cx, args[0]);
|
||||
RootedObject obj(cx, ToObject(cx, val));
|
||||
if (!obj)
|
||||
return false;
|
||||
args.rval().set(OBJECT_TO_JSVAL(obj));
|
||||
return true;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
intrinsic_ToInteger(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
double result;
|
||||
if (!ToInteger(cx, args[0], &result))
|
||||
return false;
|
||||
args.rval().set(DOUBLE_TO_JSVAL(result));
|
||||
return true;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
intrinsic_IsCallable(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
Value val = args[0];
|
||||
bool isCallable = val.isObject() && val.toObject().isCallable();
|
||||
args.rval().set(BOOLEAN_TO_JSVAL(isCallable));
|
||||
return true;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
intrinsic_ThrowError(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
JS_ASSERT(args.length() >= 1);
|
||||
uint32_t errorNumber = args[0].toInt32();
|
||||
|
||||
char *errorArgs[3] = {NULL, NULL, NULL};
|
||||
for (int i = 1; i < 3 && i < args.length(); i++) {
|
||||
RootedValue val(cx, args[i]);
|
||||
if (val.isInt32() || val.isString()) {
|
||||
errorArgs[i - 1] = JS_EncodeString(cx, ToString(cx, val));
|
||||
} else {
|
||||
ptrdiff_t spIndex = cx->stack.spIndexOf(val.address());
|
||||
errorArgs[i - 1] = DecompileValueGenerator(cx, spIndex, val, NullPtr(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, errorNumber,
|
||||
errorArgs[0], errorArgs[1], errorArgs[2]);
|
||||
for (uint32_t i = 0; i < 3; i++)
|
||||
cx->free_(errorArgs[i]);
|
||||
return false;
|
||||
}
|
||||
|
||||
JSFunctionSpec intrinsic_functions[] = {
|
||||
JS_FN("ThrowTypeError", ThrowTypeError, 0,0),
|
||||
JS_FN("ToObject", intrinsic_ToObject, 1,0),
|
||||
JS_FN("ToInteger", intrinsic_ToInteger, 1,0),
|
||||
JS_FN("IsCallable", intrinsic_IsCallable, 1,0),
|
||||
JS_FN("ThrowError", intrinsic_ThrowError, 4,0),
|
||||
JS_FS_END
|
||||
};
|
||||
JSObject *
|
||||
|
|
|
@ -320,6 +320,11 @@ class GlobalObject : public JSObject
|
|||
return &getPrototype(JSProto_Iterator).toObject();
|
||||
}
|
||||
|
||||
JSObject *getIntrinsicsHolder() {
|
||||
JS_ASSERT(!getSlotRef(INTRINSICS).isUndefined());
|
||||
return &getSlotRef(INTRINSICS).toObject();
|
||||
}
|
||||
|
||||
private:
|
||||
typedef bool (*ObjectInitOp)(JSContext *cx, Handle<GlobalObject*> global);
|
||||
|
||||
|
@ -369,12 +374,18 @@ class GlobalObject : public JSObject
|
|||
return HasDataProperty(cx, holder, NameToId(name), &fun);
|
||||
}
|
||||
|
||||
JSFunction *getIntrinsicFunction(JSContext *cx, PropertyName *name) {
|
||||
bool getIntrinsicValue(JSContext *cx, PropertyName *name, Value *vp) {
|
||||
RootedObject holder(cx, &getSlotRef(INTRINSICS).toObject());
|
||||
Value fun = NullValue();
|
||||
DebugOnly<bool> ok = HasDataProperty(cx, holder, NameToId(name), &fun);
|
||||
jsid id = NameToId(name);
|
||||
if (HasDataProperty(cx, holder, id, vp))
|
||||
return true;
|
||||
bool ok = cx->runtime->cloneSelfHostedValueById(cx, id, holder, vp);
|
||||
if (!ok)
|
||||
return false;
|
||||
|
||||
ok = JS_DefinePropertyById(cx, holder, id, *vp, NULL, NULL, 0);
|
||||
JS_ASSERT(ok);
|
||||
return fun.toObject().toFunction();
|
||||
return true;
|
||||
}
|
||||
|
||||
inline RegExpStatics *getRegExpStatics() const;
|
||||
|
|
|
@ -1743,6 +1743,21 @@ class ScriptFrameIter : public StackIter
|
|||
ScriptFrameIter &operator++() { StackIter::operator++(); settle(); return *this; }
|
||||
};
|
||||
|
||||
/* A filtering of the StackIter to only stop at non-self-hosted scripts. */
|
||||
class NonBuiltinScriptFrameIter : public StackIter
|
||||
{
|
||||
void settle() {
|
||||
while (!done() && (!isScript() || (isFunctionFrame() && fp()->fun()->isSelfHostedBuiltin())))
|
||||
StackIter::operator++();
|
||||
}
|
||||
|
||||
public:
|
||||
NonBuiltinScriptFrameIter(JSContext *cx, StackIter::SavedOption opt = StackIter::STOP_AT_SAVED)
|
||||
: StackIter(cx, opt) { settle(); }
|
||||
|
||||
NonBuiltinScriptFrameIter &operator++() { StackIter::operator++(); settle(); return *this; }
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/*
|
||||
|
|
|
@ -2839,10 +2839,10 @@ Additional Contributors:
|
|||
|
||||
<h1><a id="v8"></a>V8 License</h1>
|
||||
|
||||
<p>This license applies to certain files in the directory
|
||||
<span class="path">js/src/v8-dtoa</span>.</p>
|
||||
<p>This license applies to certain files in the directories
|
||||
<span class="path">js/src/v8-dtoa</span> and <span class="path">js/src/builtin</span>.</p>
|
||||
<pre>
|
||||
Copyright 2006-2008 the V8 project authors. All rights reserved.
|
||||
Copyright 2006-2012 the V8 project authors. All rights reserved.
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met
|
||||
|
|
Загрузка…
Ссылка в новой задаче