Bug 462300 - Embed self-hosted scripts and compile into special compartment. r=bhackett, r=gerv, r=ted

This commit is contained in:
Till Schneidereit 2012-07-23 23:01:54 +02:00
Родитель ad0c7a9dde
Коммит d953aff856
30 изменённых файлов: 1260 добавлений и 80 удалений

Просмотреть файл

@ -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
#

1
js/src/builtin/array.js Normal file
Просмотреть файл

@ -0,0 +1 @@
//this space intentionally left blank

49
js/src/builtin/embedjs.py Normal file
Просмотреть файл

@ -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()

348
js/src/builtin/js2c.py Normal file
Просмотреть файл

@ -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()

282
js/src/builtin/jsmin.py Normal file
Просмотреть файл

@ -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"

239
js/src/builtin/macros.py Normal file
Просмотреть файл

@ -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