Split the global object into two parts: The state holding global object and the global object proxy.
Fixed bug that affected the value of an assignment to an element in certain cases (issue 116).
Added GetPropertyNames functionality (issue 33) and extra Date functions (issue 77) to the API.
Changed WeakReferenceCallback to take a Persistent<Value> instead of a Persistent<Object> (issue 101).
Fixed issues with message reporting for exceptions in try-finally blocks (issues 73 and 75).
Optimized flattening of strings and string equality checking.
Improved Boyer-Moore implementation for faster indexOf operations.
Added development shell (d8) which includes counters and completion support.
Fixed problem with the receiver passed to functions called from eval (issue 124).
git-svn-id: http://v8.googlecode.com/svn/trunk@572 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/AUTHORS b/AUTHORS
index cc96cd1..596cc58 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -10,3 +10,4 @@
Jay Freeman <saurik@saurik.com>
Daniel James <dnljms@gmail.com>
Paolo Giarrusso <p.giarrusso@gmail.com>
+Daniel Andersson <kodandersson@gmail.com>
diff --git a/ChangeLog b/ChangeLog
index 943b657..95d3202 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,31 @@
+2008-10-23: Version 0.4.0
+
+ Split the global object into two parts: The state holding global
+ object and the global object proxy.
+
+ Fixed bug that affected the value of an assignment to an element
+ in certain cases (issue 116).
+
+ Added GetPropertyNames functionality (issue 33) and extra Date
+ functions (issue 77) to the API.
+
+ Changed WeakReferenceCallback to take a Persistent<Value> instead
+ of a Persistent<Object> (issue 101).
+
+ Fixed issues with message reporting for exceptions in try-finally
+ blocks (issues 73 and 75).
+
+ Optimized flattening of strings and string equality checking.
+
+ Improved Boyer-Moore implementation for faster indexOf operations.
+
+ Added development shell (d8) which includes counters and
+ completion support.
+
+ Fixed problem with the receiver passed to functions called from
+ eval (issue 124).
+
+
2008-10-16: Version 0.3.5
Improved string hash-code distribution by excluding bit-field bits
diff --git a/SConstruct b/SConstruct
index 7789518..6e5bc7a 100644
--- a/SConstruct
+++ b/SConstruct
@@ -197,6 +197,7 @@
'msvc': {
'all': {
'CCFLAGS': ['/nologo'],
+ 'LINKFLAGS': ['/nologo'],
},
'library:shared': {
'CPPDEFINES': ['USING_V8_SHARED']
@@ -216,6 +217,16 @@
}
+D8_FLAGS = {
+ 'gcc': {
+ 'console:readline': {
+ 'LIBS': ['readline']
+ }
+ },
+ 'msvc': { }
+}
+
+
SUFFIXES = {
'release': '',
'debug': '_g'
@@ -312,6 +323,11 @@
'values': ['MD5', 'timestamp'],
'default': 'MD5',
'help': 'set how the build system detects file changes'
+ },
+ 'console': {
+ 'values': ['dumb', 'readline'],
+ 'default': 'dumb',
+ 'help': 'the console to use for the d8 shell'
}
}
@@ -364,6 +380,7 @@
self.library_targets = []
self.cctest_targets = []
self.sample_targets = []
+ self.d8_targets = []
self.options = options
self.env_overrides = env_overrides
self.samples = samples
@@ -447,13 +464,15 @@
dtoa_flags = context.AddRelevantFlags(library_flags, DTOA_EXTRA_FLAGS)
cctest_flags = context.AddRelevantFlags(v8_flags, CCTEST_EXTRA_FLAGS)
sample_flags = context.AddRelevantFlags(os.environ, SAMPLE_FLAGS)
+ d8_flags = context.AddRelevantFlags(library_flags, D8_FLAGS)
context.flags = {
'v8': v8_flags,
'jscre': jscre_flags,
'dtoa': dtoa_flags,
'cctest': cctest_flags,
- 'sample': sample_flags
+ 'sample': sample_flags,
+ 'd8': d8_flags
}
target_id = mode
@@ -462,13 +481,13 @@
env['LIBRARY'] = library_name
# Build the object files by invoking SCons recursively.
- object_files = env.SConscript(
+ (object_files, shell_files) = env.SConscript(
join('src', 'SConscript'),
build_dir=join('obj', target_id),
exports='context',
duplicate=False
)
-
+
# Link the object files into a library.
env.Replace(**context.flags['v8'])
context.ApplyEnvOverrides(env)
@@ -482,6 +501,11 @@
library = env.SharedLibrary(library_name, object_files, PDB=pdb_name)
context.library_targets.append(library)
+ d8_env = Environment()
+ d8_env.Replace(**context.flags['d8'])
+ shell = d8_env.Program('d8' + suffix, object_files + shell_files)
+ context.d8_targets.append(shell)
+
for sample in context.samples:
sample_env = Environment(LIBRARY=library_name)
sample_env.Replace(**context.flags['sample'])
@@ -514,22 +538,25 @@
Help(opts.GenerateHelpText(env))
VerifyOptions(env)
env_overrides = ParseEnvOverrides(env['env'])
-
+
SourceSignatures(env['sourcesignatures'])
-
+
libraries = []
cctests = []
samples = []
+ d8s = []
modes = SplitList(env['mode'])
for mode in modes:
context = BuildSpecific(env.Copy(), mode, env_overrides)
libraries += context.library_targets
cctests += context.cctest_targets
samples += context.sample_targets
+ d8s += context.d8_targets
env.Alias('library', libraries)
env.Alias('cctests', cctests)
env.Alias('sample', samples)
+ env.Alias('d8', d8s)
if env['sample']:
env.Default('sample')
diff --git a/benchmarks/base.js b/benchmarks/base.js
index f02398d..2c26132 100644
--- a/benchmarks/base.js
+++ b/benchmarks/base.js
@@ -169,6 +169,17 @@
}
+// Notifies the runner that running a benchmark resulted in an error.
+BenchmarkSuite.prototype.NotifyError = function(error) {
+ if (this.runner.NotifyError) {
+ this.runner.NotifyError(this.name, error);
+ }
+ if (this.runner.NotifyStep) {
+ this.runner.NotifyStep(this.name);
+ }
+}
+
+
// Runs a single benchmark for at least a second and computes the
// average time it takes to run a single iteration.
BenchmarkSuite.prototype.RunSingle = function(benchmark) {
@@ -195,7 +206,12 @@
var suite = this;
function RunNext() {
if (index < length) {
- suite.RunSingle(suite.benchmarks[index++]);
+ try {
+ suite.RunSingle(suite.benchmarks[index++]);
+ } catch (e) {
+ suite.NotifyError(e);
+ return null;
+ }
return RunNext;
}
suite.NotifyResult();
diff --git a/benchmarks/run.html b/benchmarks/run.html
index 2d49dbc..41e4f78 100644
--- a/benchmarks/run.html
+++ b/benchmarks/run.html
@@ -63,6 +63,8 @@
<script type="text/javascript">
var completed = 0;
var benchmarks = BenchmarkSuite.CountBenchmarks();
+var success = true;
+
function ShowProgress(name) {
var status = document.getElementById("status");
var percentage = ((++completed) / benchmarks) * 100;
@@ -77,14 +79,23 @@
}
+function AddError(name, error) {
+ AddResult(name, '<b>error</b>');
+ success = false;
+}
+
+
function AddScore(score) {
var status = document.getElementById("status");
- status.innerHTML = "Score: " + score;
+ if (success) {
+ status.innerHTML = "Score: " + score;
+ }
}
function Run() {
BenchmarkSuite.RunSuites({ NotifyStep: ShowProgress,
+ NotifyError: AddError,
NotifyResult: AddResult,
NotifyScore: AddScore });
}
diff --git a/benchmarks/run.js b/benchmarks/run.js
index f73f1af..da2373b 100644
--- a/benchmarks/run.js
+++ b/benchmarks/run.js
@@ -33,17 +33,27 @@
load('raytrace.js');
load('earley-boyer.js');
+var success = true;
function PrintResult(name, result) {
print(name + ': ' + result);
}
+function PrintError(name, error) {
+ PrintResult(name, error);
+ success = false;
+}
+
+
function PrintScore(score) {
- print('----');
- print('Score (version ' + BenchmarkSuite.version + '): ' + score);
+ if (success) {
+ print('----');
+ print('Score (version ' + BenchmarkSuite.version + '): ' + score);
+ }
}
BenchmarkSuite.RunSuites({ NotifyResult: PrintResult,
+ NotifyError: PrintError,
NotifyScore: PrintScore });
diff --git a/include/v8.h b/include/v8.h
index eba21f5..70b1f52 100644
--- a/include/v8.h
+++ b/include/v8.h
@@ -128,7 +128,7 @@
* \param object the weak global object to be reclaimed by the garbage collector
* \param parameter the value passed in when making the weak global object
*/
-typedef void (*WeakReferenceCallback)(Persistent<Object> object,
+typedef void (*WeakReferenceCallback)(Persistent<Value> object,
void* parameter);
@@ -327,6 +327,10 @@
template <class S> inline Persistent(S* that) : Handle<T>(that) { }
+ /**
+ * "Casts" a plain handle which is known to be a persistent handle
+ * to a persistent handle.
+ */
template <class S> explicit inline Persistent(Handle<S> that)
: Handle<T>(*that) { }
@@ -508,9 +512,9 @@
: resource_name_(resource_name),
resource_line_offset_(resource_line_offset),
resource_column_offset_(resource_column_offset) { }
- inline Handle<Value> ResourceName();
- inline Handle<Integer> ResourceLineOffset();
- inline Handle<Integer> ResourceColumnOffset();
+ inline Handle<Value> ResourceName() const;
+ inline Handle<Integer> ResourceLineOffset() const;
+ inline Handle<Integer> ResourceColumnOffset() const;
private:
Handle<Value> resource_name_;
Handle<Integer> resource_line_offset_;
@@ -555,16 +559,7 @@
Local<String> Get();
Local<String> GetSourceLine();
- // TODO(1241256): Rewrite (or remove) this method. We don't want to
- // deal with ownership of the returned string and we want to use
- // JavaScript data structures exclusively.
- char* GetUnderline(char* source_line, char underline_char);
-
- Handle<String> GetScriptResourceName();
-
- // TODO(1240903): Remove this when no longer used in WebKit V8
- // bindings.
- Handle<Value> GetSourceData();
+ Handle<Value> GetScriptResourceName();
/**
* Returns the number, 1-based, of the line where the error occurred.
@@ -672,6 +667,11 @@
*/
bool IsInt32();
+ /**
+ * Returns true if this value is a Date.
+ */
+ bool IsDate();
+
Local<Boolean> ToBoolean();
Local<Number> ToNumber();
Local<String> ToString();
@@ -987,6 +987,14 @@
class EXPORT Date : public Value {
public:
static Local<Value> New(double time);
+
+ /**
+ * A specialization of Value::NumberValue that is more efficient
+ * because we know the structure of this object.
+ */
+ double NumberValue();
+
+ static Date* Cast(v8::Value* obj);
};
@@ -1015,6 +1023,14 @@
bool Delete(uint32_t index);
/**
+ * Returns an array containing the names of the enumerable properties
+ * of this object, including properties from prototype objects. The
+ * array returned by this method contains the same values as would
+ * be enumerated by a for-in statement over this object.
+ */
+ Local<Array> GetPropertyNames();
+
+ /**
* Get the prototype object. This does not skip objects marked to
* be skipped by __proto__ and it does not consult the security
* handler.
@@ -1052,6 +1068,12 @@
/** Tests for an index lookup interceptor.*/
bool HasIndexedLookupInterceptor();
+ /**
+ * Turns on access check on the object if the object is an instance of
+ * a template that has access check callbacks. If an object has no
+ * access check info, the object cannot be accessed by anyone.
+ */
+ void TurnOnAccessCheck();
static Local<Object> New();
static Object* Cast(Value* obj);
@@ -1609,10 +1631,15 @@
* When accessing properties on instances of this object template,
* the access check callback will be called to determine whether or
* not to allow cross-context access to the properties.
+ * The last parameter specifies whether access checks are turned
+ * on by default on instances. If access checks are off by default,
+ * they can be turned on on individual instances by calling
+ * Object::TurnOnAccessCheck().
*/
void SetAccessCheckCallbacks(NamedSecurityCallback named_handler,
IndexedSecurityCallback indexed_handler,
- Handle<Value> data = Handle<Value>());
+ Handle<Value> data = Handle<Value>(),
+ bool turned_on_by_default = true);
/**
* Gets the number of internal fields for objects generated from
@@ -1730,11 +1757,11 @@
class EXPORT ResourceConstraints {
public:
ResourceConstraints();
- int max_young_space_size() { return max_young_space_size_; }
+ int max_young_space_size() const { return max_young_space_size_; }
void set_max_young_space_size(int value) { max_young_space_size_ = value; }
- int max_old_space_size() { return max_old_space_size_; }
+ int max_old_space_size() const { return max_old_space_size_; }
void set_max_old_space_size(int value) { max_old_space_size_ = value; }
- uint32_t* stack_limit() { return stack_limit_; }
+ uint32_t* stack_limit() const { return stack_limit_; }
void set_stack_limit(uint32_t* value) { stack_limit_ = value; }
private:
int max_young_space_size_;
@@ -1965,7 +1992,7 @@
/**
* Returns true if an exception has been caught by this try/catch block.
*/
- bool HasCaught();
+ bool HasCaught() const;
/**
* Returns the exception caught by this try/catch block. If no exception has
@@ -1973,7 +2000,7 @@
*
* The returned handle is valid until this TryCatch block has been destroyed.
*/
- Local<Value> Exception();
+ Local<Value> Exception() const;
/**
* Returns the message associated with this exception. If there is
@@ -1982,7 +2009,7 @@
* The returned handle is valid until this TryCatch block has been
* destroyed.
*/
- Local<v8::Message> Message();
+ Local<v8::Message> Message() const;
/**
* Clears any exceptions that may have been caught by this try/catch block.
@@ -2044,8 +2071,15 @@
*/
class EXPORT Context {
public:
+ /** Returns the global object of the context. */
Local<Object> Global();
+ /**
+ * Detaches the global object from its context before
+ * the global object can be reused to create a new context.
+ */
+ void DetachGlobal();
+
/** Creates a new context. */
static Persistent<Context> New(
ExtensionConfiguration* extensions = 0,
@@ -2058,15 +2092,15 @@
/** Returns the context that is on the top of the stack. */
static Local<Context> GetCurrent();
- /** Returns the security context that is currently used. */
- static Local<Context> GetCurrentSecurityContext();
-
/**
* Sets the security token for the context. To access an object in
* another context, the security tokens must match.
*/
void SetSecurityToken(Handle<Value> token);
+ /** Restores the security token to the default value. */
+ void UseDefaultSecurityToken();
+
/** Returns the security token of this context.*/
Handle<Value> GetSecurityToken();
@@ -2090,9 +2124,6 @@
/** Returns true if V8 has a current context. */
static bool InContext();
- /** Returns true if V8 has a current security context. */
- static bool InSecurityContext();
-
/**
* Stack-allocated class which sets the execution context for all
* operations executed within a local scope.
@@ -2354,17 +2385,17 @@
return Local<T>(reinterpret_cast<T*>(after));
}
-Handle<Value> ScriptOrigin::ResourceName() {
+Handle<Value> ScriptOrigin::ResourceName() const {
return resource_name_;
}
-Handle<Integer> ScriptOrigin::ResourceLineOffset() {
+Handle<Integer> ScriptOrigin::ResourceLineOffset() const {
return resource_line_offset_;
}
-Handle<Integer> ScriptOrigin::ResourceColumnOffset() {
+Handle<Integer> ScriptOrigin::ResourceColumnOffset() const {
return resource_column_offset_;
}
diff --git a/src/SConscript b/src/SConscript
index 725f935..c1a6433 100644
--- a/src/SConscript
+++ b/src/SConscript
@@ -64,6 +64,16 @@
}
+D8_FILES = {
+ 'all': [
+ 'd8.cc'
+ ],
+ 'console:readline': [
+ 'd8-readline.cc'
+ ]
+}
+
+
LIBRARY_FILES = '''
runtime.js
v8natives.js
@@ -99,16 +109,21 @@
env.Replace(**context.flags['v8'])
context.ApplyEnvOverrides(env)
env['BUILDERS']['JS2C'] = Builder(action=js2c.JS2C)
- env['BUILDERS']['Snapshot'] = Builder(action='$SOURCE $TARGET --logfile $LOGFILE')
+ env['BUILDERS']['Snapshot'] = Builder(action='$SOURCE $TARGET --logfile "$LOGFILE"')
# Build the standard platform-independent source files.
source_files = context.GetRelevantSources(SOURCES)
+ d8_files = context.GetRelevantSources(D8_FILES)
+ d8_js = env.JS2C('d8-js.cc', 'd8.js', TYPE='D8')
+ d8_js_obj = context.ConfigureObject(env, d8_js, CPPPATH=['.'])
+ d8_objs = [context.ConfigureObject(env, [d8_files]), d8_js_obj]
+
# Combine the JavaScript library files into a single C++ file and
# compile it.
library_files = [s for s in LIBRARY_FILES]
library_files.append('macros.py')
- libraries_src, libraries_empty_src = env.JS2C(['libraries.cc', 'libraries-empty.cc'], library_files)
+ libraries_src, libraries_empty_src = env.JS2C(['libraries.cc', 'libraries-empty.cc'], library_files, TYPE='CORE')
libraries_obj = context.ConfigureObject(env, libraries_src, CPPPATH=['.'])
# Build JSCRE.
@@ -137,8 +152,9 @@
else:
snapshot_obj = empty_snapshot_obj
- return [non_snapshot_files, libraries_obj, snapshot_obj]
+ library_objs = [non_snapshot_files, libraries_obj, snapshot_obj]
+ return (library_objs, d8_objs)
-library_objects = ConfigureObjectFiles()
-Return('library_objects')
+(library_objs, d8_objs) = ConfigureObjectFiles()
+Return('library_objs d8_objs')
diff --git a/src/api.cc b/src/api.cc
index b1258fd..494a9d2 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -412,10 +412,6 @@
thread_local.SaveContext(i::GlobalHandles::Create(i::Top::context()));
i::Top::set_context(*env);
-
- thread_local.SaveSecurityContext(
- i::GlobalHandles::Create(i::Top::security_context()));
- i::Top::set_security_context(*env);
}
@@ -427,16 +423,10 @@
return;
}
- // Content of 'last_context' and 'last_security_context' could be NULL.
+ // Content of 'last_context' could be NULL.
i::Handle<i::Object> last_context = thread_local.RestoreContext();
i::Top::set_context(static_cast<i::Context*>(*last_context));
i::GlobalHandles::Destroy(last_context.location());
-
- i::Handle<i::Object> last_security_context =
- thread_local.RestoreSecurityContext();
- i::Top::set_security_context(
- static_cast<i::Context*>(*last_security_context));
- i::GlobalHandles::Destroy(last_security_context.location());
}
@@ -908,7 +898,8 @@
void ObjectTemplate::SetAccessCheckCallbacks(
NamedSecurityCallback named_callback,
IndexedSecurityCallback indexed_callback,
- Handle<Value> data) {
+ Handle<Value> data,
+ bool turned_on_by_default) {
if (IsDeadCheck("v8::ObjectTemplate::SetAccessCheckCallbacks()")) return;
HandleScope scope;
EnsureConstructor(this);
@@ -925,8 +916,8 @@
i::FunctionTemplateInfo* constructor =
i::FunctionTemplateInfo::cast(Utils::OpenHandle(this)->constructor());
i::Handle<i::FunctionTemplateInfo> cons(constructor);
- cons->set_needs_access_check(true);
cons->set_access_check_info(*info);
+ cons->set_needs_access_check(turned_on_by_default);
}
@@ -1064,9 +1055,9 @@
HandleScope scope;
i::Handle<i::JSFunction> fun = Utils::OpenHandle(this);
EXCEPTION_PREAMBLE();
- i::Handle<i::Object> global(i::Top::context()->global());
+ i::Handle<i::Object> receiver(i::Top::context()->global_proxy());
i::Handle<i::Object> result =
- i::Execution::Call(fun, global, 0, NULL, &has_pending_exception);
+ i::Execution::Call(fun, receiver, 0, NULL, &has_pending_exception);
EXCEPTION_BAILOUT_CHECK(Local<Value>());
raw_result = *result;
}
@@ -1093,12 +1084,12 @@
}
-bool v8::TryCatch::HasCaught() {
+bool v8::TryCatch::HasCaught() const {
return !reinterpret_cast<i::Object*>(exception_)->IsTheHole();
}
-v8::Local<Value> v8::TryCatch::Exception() {
+v8::Local<Value> v8::TryCatch::Exception() const {
if (HasCaught()) {
// Check for out of memory exception.
i::Object* exception = reinterpret_cast<i::Object*>(exception_);
@@ -1109,7 +1100,7 @@
}
-v8::Local<v8::Message> v8::TryCatch::Message() {
+v8::Local<v8::Message> v8::TryCatch::Message() const {
if (HasCaught() && message_ != i::Smi::FromInt(0)) {
i::Object* message = reinterpret_cast<i::Object*>(message_);
return v8::Utils::MessageToLocal(i::Handle<i::Object>(message));
@@ -1148,7 +1139,7 @@
}
-v8::Handle<String> Message::GetScriptResourceName() {
+v8::Handle<Value> Message::GetScriptResourceName() {
if (IsDeadCheck("v8::Message::GetScriptResourceName()")) {
return Local<String>();
}
@@ -1159,22 +1150,10 @@
i::Handle<i::JSValue> script =
i::Handle<i::JSValue>::cast(GetProperty(obj, "script"));
i::Handle<i::Object> resource_name(i::Script::cast(script->value())->name());
- if (!resource_name->IsString()) {
- return Local<String>();
- }
- Local<String> result =
- Utils::ToLocal(i::Handle<i::String>::cast(resource_name));
- return scope.Close(result);
+ return scope.Close(Utils::ToLocal(resource_name));
}
-// TODO(1240903): Remove this when no longer used in WebKit V8 bindings.
-Handle<Value> Message::GetSourceData() {
- Handle<String> data = GetScriptResourceName();
- if (data.IsEmpty()) return v8::Undefined();
- return data;
-}
-
static i::Handle<i::Object> CallV8HeapFunction(const char* name,
i::Handle<i::Object> recv,
int argc,
@@ -1277,53 +1256,6 @@
}
-char* Message::GetUnderline(char* source_line, char underline_char) {
- if (IsDeadCheck("v8::Message::GetUnderline()")) return 0;
- HandleScope scope;
-
- i::Handle<i::JSObject> data_obj = Utils::OpenHandle(this);
- int start_pos = static_cast<int>(GetProperty(data_obj, "startPos")->Number());
- int end_pos = static_cast<int>(GetProperty(data_obj, "endPos")->Number());
- EXCEPTION_PREAMBLE();
- i::Handle<i::Object> start_col_obj = CallV8HeapFunction(
- "GetPositionInLine",
- data_obj,
- &has_pending_exception);
- EXCEPTION_BAILOUT_CHECK(0);
- int start_col = static_cast<int>(start_col_obj->Number());
- int end_col = start_col + (end_pos - start_pos);
-
- // Any tabs before or between the selected columns have to be
- // expanded into spaces. We assume that a tab character advances
- // the cursor up until the next 8-character boundary and at least
- // one character.
- int real_start_col = 0;
- for (int i = 0; i < start_col; i++) {
- real_start_col++;
- if (source_line[i] == '\t') {
- real_start_col++;
- while (real_start_col % 8 != 0)
- real_start_col++;
- }
- }
- int real_end_col = real_start_col;
- for (int i = start_col; i < end_col; i++) {
- real_end_col++;
- if (source_line[i] == '\t') {
- while (real_end_col % 8 != 0)
- real_end_col++;
- }
- }
- char* result = i::NewArray<char>(real_end_col + 1);
- for (int i = 0; i < real_start_col; i++)
- result[i] = ' ';
- for (int i = real_start_col; i < real_end_col; i++)
- result[i] = underline_char;
- result[real_end_col] = '\0';
- return result;
-}
-
-
void Message::PrintCurrentStackTrace(FILE* out) {
if (IsDeadCheck("v8::Message::PrintCurrentStackTrace()")) return;
i::Top::PrintCurrentStackTrace(out);
@@ -1410,6 +1342,13 @@
}
+bool Value::IsDate() {
+ if (IsDeadCheck("v8::Value::IsDate()")) return false;
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ return obj->HasSpecificClassOf(i::Heap::Date_symbol());
+}
+
+
Local<String> Value::ToString() {
if (IsDeadCheck("v8::Value::ToString()")) return Local<String>();
LOG_API("ToString");
@@ -1570,6 +1509,16 @@
}
+v8::Date* v8::Date::Cast(v8::Value* that) {
+ if (IsDeadCheck("v8::Date::Cast()")) return 0;
+ i::Handle<i::Object> obj = Utils::OpenHandle(that);
+ ApiCheck(obj->HasSpecificClassOf(i::Heap::Date_symbol()),
+ "v8::Date::Cast()",
+ "Could not convert to date");
+ return static_cast<v8::Date*>(that);
+}
+
+
bool Value::BooleanValue() {
if (IsDeadCheck("v8::Value::BooleanValue()")) return false;
LOG_API("BooleanValue");
@@ -1764,7 +1713,7 @@
bool v8::Object::Set(v8::Handle<Value> key, v8::Handle<Value> value,
- v8::PropertyAttribute attribs) {
+ v8::PropertyAttribute attribs) {
ON_BAILOUT("v8::Object::Set()", return false);
i::Handle<i::Object> self = Utils::OpenHandle(this);
i::Handle<i::Object> key_obj = Utils::OpenHandle(*key);
@@ -1801,6 +1750,20 @@
}
+Local<Array> v8::Object::GetPropertyNames() {
+ ON_BAILOUT("v8::Object::GetPropertyNames()", return Local<v8::Array>());
+ v8::HandleScope scope;
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ i::Handle<i::FixedArray> value = i::GetKeysInFixedArrayFor(self);
+ // Because we use caching to speed up enumeration it is important
+ // to never change the result of the basic enumeration function so
+ // we clone the result.
+ i::Handle<i::FixedArray> elms = i::Factory::CopyFixedArray(value);
+ i::Handle<i::JSArray> result = i::Factory::NewJSArrayWithElements(elms);
+ return scope.Close(Utils::ToLocal(result));
+}
+
+
Local<String> v8::Object::ObjectProtoToString() {
ON_BAILOUT("v8::Object::ObjectProtoToString()", return Local<v8::String>());
i::Handle<i::JSObject> self = Utils::OpenHandle(this);
@@ -1937,6 +1900,20 @@
}
+// Turns on access checks by copying the map and setting the check flag.
+// Because the object gets a new map, existing inline cache caching
+// the old map of this object will fail.
+void v8::Object::TurnOnAccessCheck() {
+ ON_BAILOUT("v8::Object::TurnOnAccessCheck()", return);
+ i::Handle<i::JSObject> obj = Utils::OpenHandle(this);
+
+ i::Handle<i::Map> new_map =
+ i::Factory::CopyMapDropTransitions(i::Handle<i::Map>(obj->map()));
+ new_map->set_is_access_check_needed();
+ obj->set_map(*new_map);
+}
+
+
Local<v8::Object> Function::NewInstance() {
return NewInstance(0, NULL);
}
@@ -2216,24 +2193,58 @@
const char* v8::V8::GetVersion() {
- return "0.3.5.2";
+ return "0.4.0";
}
-Persistent<Context> v8::Context::New(v8::ExtensionConfiguration* extensions,
- v8::Handle<ObjectTemplate> global_template,
- v8::Handle<Value> global_object) {
+static i::Handle<i::FunctionTemplateInfo>
+ EnsureConstructor(i::Handle<i::ObjectTemplateInfo> templ) {
+ if (templ->constructor()->IsUndefined()) {
+ Local<FunctionTemplate> constructor = FunctionTemplate::New();
+ Utils::OpenHandle(*constructor)->set_instance_template(*templ);
+ templ->set_constructor(*Utils::OpenHandle(*constructor));
+ }
+ return i::Handle<i::FunctionTemplateInfo>(
+ i::FunctionTemplateInfo::cast(templ->constructor()));
+}
+
+
+Persistent<Context> v8::Context::New(
+ v8::ExtensionConfiguration* extensions,
+ v8::Handle<ObjectTemplate> global_template,
+ v8::Handle<Value> global_object) {
EnsureInitialized("v8::Context::New()");
LOG_API("Context::New");
ON_BAILOUT("v8::Context::New()", return Persistent<Context>());
+
// Make sure that the global_template has a constructor.
- if (!global_template.IsEmpty() &&
- Utils::OpenHandle(*global_template)->constructor()->IsUndefined()) {
- Local<FunctionTemplate> templ = FunctionTemplate::New();
- Utils::OpenHandle(*templ)->set_instance_template(
+ if (!global_template.IsEmpty()) {
+ i::Handle<i::FunctionTemplateInfo> constructor =
+ EnsureConstructor(Utils::OpenHandle(*global_template));
+
+ // Create a fresh template for global proxy object.
+ Local<ObjectTemplate> proxy_template = ObjectTemplate::New();
+
+ i::Handle<i::FunctionTemplateInfo> proxy_constructor =
+ EnsureConstructor(Utils::OpenHandle(*proxy_template));
+
+ // Set the global template to be the prototype template
+ // of global proxy template.
+ proxy_constructor->set_prototype_template(
*Utils::OpenHandle(*global_template));
- i::Handle<i::FunctionTemplateInfo> constructor = Utils::OpenHandle(*templ);
- Utils::OpenHandle(*global_template)->set_constructor(*constructor);
+
+ // Migrate security handlers from global_template to proxy_template.
+ if (!constructor->access_check_info()->IsUndefined()) {
+ proxy_constructor->set_access_check_info(
+ constructor->access_check_info());
+ proxy_constructor->set_needs_access_check(true);
+
+ // Remove access check info from global_template.
+ constructor->set_needs_access_check(false);
+ constructor->set_access_check_info(i::Heap::undefined_value());
+ }
+
+ global_template = proxy_template;
}
i::Handle<i::Context> env = i::Bootstrapper::CreateEnvironment(
@@ -2248,18 +2259,24 @@
void v8::Context::SetSecurityToken(Handle<Value> token) {
+ if (IsDeadCheck("v8::Context::SetSecurityToken()")) return;
i::Handle<i::Context> env = Utils::OpenHandle(this);
i::Handle<i::Object> token_handle = Utils::OpenHandle(*token);
- // The global object of an environment is always a real global
- // object with security token and reference to the builtins object.
- i::JSGlobalObject::cast(env->global())->set_security_token(*token_handle);
+ env->set_security_token(*token_handle);
+}
+
+
+void v8::Context::UseDefaultSecurityToken() {
+ if (IsDeadCheck("v8::Context::UseDefaultSecurityToken()")) return;
+ i::Handle<i::Context> env = Utils::OpenHandle(this);
+ env->set_security_token(env->global());
}
Handle<Value> v8::Context::GetSecurityToken() {
+ if (IsDeadCheck("v8::Context::GetSecurityToken()")) return Handle<Value>();
i::Handle<i::Context> env = Utils::OpenHandle(this);
- i::Object* security_token =
- i::JSGlobalObject::cast(env->global())->security_token();
+ i::Object* security_token = env->security_token();
i::Handle<i::Object> token_handle(security_token);
return Utils::ToLocal(token_handle);
}
@@ -2276,11 +2293,6 @@
}
-bool Context::InSecurityContext() {
- return i::Top::security_context() != NULL;
-}
-
-
v8::Local<v8::Context> Context::GetEntered() {
if (IsDeadCheck("v8::Context::GetEntered()")) return Local<Context>();
i::Handle<i::Object> last = thread_local.LastEnteredContext();
@@ -2297,22 +2309,22 @@
}
-v8::Local<v8::Context> Context::GetCurrentSecurityContext() {
- if (IsDeadCheck("v8::Context::GetCurrentSecurityContext()")) {
- return Local<Context>();
- }
- i::Handle<i::Context> context(i::Top::security_context());
- return Utils::ToLocal(context);
-}
-
-
v8::Local<v8::Object> Context::Global() {
if (IsDeadCheck("v8::Context::Global()")) return Local<v8::Object>();
i::Object** ctx = reinterpret_cast<i::Object**>(this);
i::Handle<i::Context> context =
i::Handle<i::Context>::cast(i::Handle<i::Object>(ctx));
- i::Handle<i::JSObject> global(context->global());
- return Utils::ToLocal(global);
+ i::Handle<i::Object> global(context->global_proxy());
+ return Utils::ToLocal(i::Handle<i::JSObject>::cast(global));
+}
+
+
+void Context::DetachGlobal() {
+ if (IsDeadCheck("v8::Context::DetachGlobal()")) return;
+ i::Object** ctx = reinterpret_cast<i::Object**>(this);
+ i::Handle<i::Context> context =
+ i::Handle<i::Context>::cast(i::Handle<i::Object>(ctx));
+ i::Bootstrapper::DetachGlobal(context);
}
@@ -2422,7 +2434,7 @@
}
-static void DisposeExternalString(v8::Persistent<v8::Object> obj,
+static void DisposeExternalString(v8::Persistent<v8::Value> obj,
void* parameter) {
v8::String::ExternalStringResource* resource =
reinterpret_cast<v8::String::ExternalStringResource*>(parameter);
@@ -2433,7 +2445,7 @@
}
-static void DisposeExternalAsciiString(v8::Persistent<v8::Object> obj,
+static void DisposeExternalAsciiString(v8::Persistent<v8::Value> obj,
void* parameter) {
v8::String::ExternalAsciiStringResource* resource =
reinterpret_cast<v8::String::ExternalAsciiStringResource*>(parameter);
@@ -2494,6 +2506,15 @@
}
+double v8::Date::NumberValue() {
+ if (IsDeadCheck("v8::Date::NumberValue()")) return 0;
+ LOG_API("Date::NumberValue");
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ i::Handle<i::JSValue> jsvalue = i::Handle<i::JSValue>::cast(obj);
+ return jsvalue->value()->Number();
+}
+
+
Local<v8::Array> v8::Array::New(int length) {
EnsureInitialized("v8::Array::New()");
LOG_API("Array::New");
@@ -2623,6 +2644,11 @@
String::Utf8Value::Utf8Value(v8::Handle<v8::Value> obj) {
EnsureInitialized("v8::String::Utf8Value::Utf8Value()");
+ if (obj.IsEmpty()) {
+ str_ = NULL;
+ length_ = 0;
+ return;
+ }
HandleScope scope;
TryCatch try_catch;
Handle<String> str = obj->ToString();
@@ -2644,6 +2670,11 @@
String::AsciiValue::AsciiValue(v8::Handle<v8::Value> obj) {
EnsureInitialized("v8::String::AsciiValue::AsciiValue()");
+ if (obj.IsEmpty()) {
+ str_ = NULL;
+ length_ = 0;
+ return;
+ }
HandleScope scope;
TryCatch try_catch;
Handle<String> str = obj->ToString();
@@ -2665,6 +2696,11 @@
String::Value::Value(v8::Handle<v8::Value> obj) {
EnsureInitialized("v8::String::Value::Value()");
+ if (obj.IsEmpty()) {
+ str_ = NULL;
+ length_ = 0;
+ return;
+ }
HandleScope scope;
TryCatch try_catch;
Handle<String> str = obj->ToString();
diff --git a/src/api.h b/src/api.h
index 99cda18..4843926 100644
--- a/src/api.h
+++ b/src/api.h
@@ -262,6 +262,8 @@
OpenHandle(v8::Signature* sig);
static inline v8::internal::Handle<v8::internal::TypeSwitchInfo>
OpenHandle(v8::TypeSwitch* that);
+ static inline v8::internal::Handle<v8::internal::Proxy>
+ OpenHandle(v8::External* that);
};
@@ -325,6 +327,7 @@
MAKE_OPEN_HANDLE(Function, JSFunction)
MAKE_OPEN_HANDLE(Message, JSObject)
MAKE_OPEN_HANDLE(Context, Context)
+MAKE_OPEN_HANDLE(External, Proxy)
#undef MAKE_OPEN_HANDLE
@@ -346,8 +349,7 @@
HandleScopeImplementer()
: blocks(0),
entered_contexts_(0),
- saved_contexts_(0),
- saved_security_contexts_(0) {
+ saved_contexts_(0) {
Initialize();
}
@@ -355,7 +357,6 @@
blocks.Initialize(0);
entered_contexts_.Initialize(0);
saved_contexts_.Initialize(0);
- saved_security_contexts_.Initialize(0);
spare = NULL;
ignore_out_of_memory = false;
call_depth = 0;
@@ -391,10 +392,6 @@
inline Handle<Object> RestoreContext();
inline bool HasSavedContexts();
- inline void SaveSecurityContext(Handle<Object> context);
- inline Handle<Object> RestoreSecurityContext();
- inline bool HasSavedSecurityContexts();
-
inline List<void**>* Blocks() { return &blocks; }
inline bool IgnoreOutOfMemory() { return ignore_out_of_memory; }
@@ -408,8 +405,6 @@
List<Handle<Object> > entered_contexts_;
// Used as a stack to keep track of saved contexts.
List<Handle<Object> > saved_contexts_;
- // Used as a stack to keep track of saved security contexts.
- List<Handle<Object> > saved_security_contexts_;
bool ignore_out_of_memory;
// This is only used for threading support.
ImplementationUtilities::HandleScopeData handle_scope_data_;
@@ -442,21 +437,6 @@
}
-void HandleScopeImplementer::SaveSecurityContext(Handle<Object> context) {
- saved_security_contexts_.Add(context);
-}
-
-
-Handle<Object> HandleScopeImplementer::RestoreSecurityContext() {
- return saved_security_contexts_.RemoveLast();
-}
-
-
-bool HandleScopeImplementer::HasSavedSecurityContexts() {
- return !saved_security_contexts_.is_empty();
-}
-
-
void HandleScopeImplementer::EnterContext(Handle<Object> context) {
entered_contexts_.Add(context);
}
diff --git a/src/assembler-ia32.cc b/src/assembler-ia32.cc
index bacee5a..fbe63de 100644
--- a/src/assembler-ia32.cc
+++ b/src/assembler-ia32.cc
@@ -428,12 +428,24 @@
// relocation information generated between the last instruction and this
// pop instruction.
byte instr = last_pc_[0];
- if (instr == (0x50 | dst.code())) {
- pc_ = last_pc_;
- last_pc_ = NULL;
- if (FLAG_print_push_pop_elimination) {
- PrintF("%d push/pop (same reg) eliminated\n", pc_offset());
+ if ((instr & ~0x7) == 0x50) {
+ int push_reg_code = instr & 0x7;
+ if (push_reg_code == dst.code()) {
+ pc_ = last_pc_;
+ if (FLAG_print_push_pop_elimination) {
+ PrintF("%d push/pop (same reg) eliminated\n", pc_offset());
+ }
+ } else {
+ // Convert 'push src; pop dst' to 'mov dst, src'.
+ last_pc_[0] = 0x8b;
+ Register src = { push_reg_code };
+ EnsureSpace ensure_space(this);
+ emit_operand(dst, Operand(src));
+ if (FLAG_print_push_pop_elimination) {
+ PrintF("%d push/pop (reg->reg) eliminated\n", pc_offset());
+ }
}
+ last_pc_ = NULL;
return;
} else if (instr == 0xff) { // push of an operand, convert to a move
byte op1 = last_pc_[1];
@@ -2043,6 +2055,7 @@
reloc_info_writer.Write(&rinfo);
}
+
void Assembler::WriteInternalReference(int position, const Label& bound_label) {
ASSERT(bound_label.is_bound());
ASSERT(0 <= position);
diff --git a/src/assembler.h b/src/assembler.h
index e5b9f04..137c37c 100644
--- a/src/assembler.h
+++ b/src/assembler.h
@@ -48,7 +48,7 @@
// unknown pc location. Assembler::bind() is used to bind a label to the
// current pc. A label can be bound only once.
-class Label : public ZoneObject { // ShadowLables are dynamically allocated.
+class Label : public ZoneObject { // LabelShadows are dynamically allocated.
public:
INLINE(Label()) { Unuse(); }
INLINE(~Label()) { ASSERT(!is_linked()); }
@@ -87,17 +87,19 @@
};
-// A LabelShadow is a label that temporarily shadows another label. It
-// is used to catch linking and binding of labels in certain scopes,
-// e.g. try blocks. LabelShadows are themselves labels which can be
-// used (only) after they are not shadowing anymore.
-class LabelShadow: public Label {
+// A LabelShadow represents a label that is temporarily shadowed by another
+// label (represented by the original label during shadowing). They are used
+// to catch jumps to labels in certain contexts, e.g. try blocks. After
+// shadowing ends, the formerly shadowed label is again represented by the
+// original label and the LabelShadow can be used as a label in its own
+// right, representing the formerly shadowing label.
+class LabelShadow : public Label {
public:
- explicit LabelShadow(Label* shadowed) {
- ASSERT(shadowed != NULL);
- shadowed_ = shadowed;
- shadowed_pos_ = shadowed->pos_;
- shadowed->Unuse();
+ explicit LabelShadow(Label* original) {
+ ASSERT(original != NULL);
+ original_label_ = original;
+ original_pos_ = original->pos_;
+ original->Unuse();
#ifdef DEBUG
is_shadowing_ = true;
#endif
@@ -109,18 +111,23 @@
void StopShadowing() {
ASSERT(is_shadowing_ && is_unused());
- pos_ = shadowed_->pos_;
- shadowed_->pos_ = shadowed_pos_;
+ pos_ = original_label_->pos_;
+ original_label_->pos_ = original_pos_;
#ifdef DEBUG
is_shadowing_ = false;
#endif
}
- Label* shadowed() const { return shadowed_; }
+ Label* original_label() const { return original_label_; }
private:
- Label* shadowed_;
- int shadowed_pos_;
+ // During shadowing, the currently shadowing label. After shadowing, the
+ // label that was shadowed.
+ Label* original_label_;
+
+ // During shadowing, the saved state of the original label.
+ int original_pos_;
+
#ifdef DEBUG
bool is_shadowing_;
#endif
diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc
index 0c5650a..0562c23 100644
--- a/src/bootstrapper.cc
+++ b/src/bootstrapper.cc
@@ -275,7 +275,9 @@
bool InstallExtension(const char* name);
bool InstallExtension(v8::RegisteredExtension* current);
bool InstallSpecialObjects();
- bool ConfigureGlobalObject(v8::Handle<v8::ObjectTemplate> global_template);
+ bool ConfigureApiObject(Handle<JSObject> object,
+ Handle<ObjectTemplateInfo> object_template);
+ bool ConfigureGlobalObjects(v8::Handle<v8::ObjectTemplate> global_template);
// Migrates all properties from the 'from' object to the 'to'
// object and overrides the prototype in 'to' with the one from
@@ -337,11 +339,30 @@
}
+static void SetObjectPrototype(Handle<JSObject> object, Handle<Object> proto) {
+ // object.__proto__ = proto;
+ Handle<Map> old_to_map = Handle<Map>(object->map());
+ Handle<Map> new_to_map = Factory::CopyMapDropTransitions(old_to_map);
+ new_to_map->set_prototype(*proto);
+ object->set_map(*new_to_map);
+}
+
+
+void Bootstrapper::DetachGlobal(Handle<Context> env) {
+ JSGlobalProxy::cast(env->global_proxy())->set_context(*Factory::null_value());
+ SetObjectPrototype(Handle<JSObject>(env->global_proxy()),
+ Factory::null_value());
+ env->set_global_proxy(env->global());
+ env->global()->set_global_receiver(env->global());
+}
+
+
Genesis::~Genesis() {
ASSERT(current_ == this);
current_ = previous_;
}
+
static Handle<JSFunction> InstallFunction(Handle<JSObject> target,
const char* name,
InstanceType type,
@@ -431,7 +452,6 @@
global_context_ =
Handle<Context>::cast(
GlobalHandles::Create(*Factory::NewGlobalContext()));
- Top::set_security_context(*global_context());
Top::set_context(*global_context());
// Allocate the message listeners object.
@@ -506,54 +526,99 @@
}
{ // --- G l o b a l ---
- Handle<String> global_name = Factory::LookupAsciiSymbol("global");
- Handle<JSFunction> global_function;
-
- if (global_template.IsEmpty()) {
- Handle<String> name = Handle<String>(Heap::empty_symbol());
- Handle<Code> code = Handle<Code>(Builtins::builtin(Builtins::Illegal));
- global_function = Factory::NewFunction(name, JS_GLOBAL_OBJECT_TYPE,
- JSGlobalObject::kSize, code, true);
- // Change the constructor property of the prototype of the
- // hidden global function to refer to the Object function.
- Handle<JSObject> prototype =
- Handle<JSObject>(
- JSObject::cast(global_function->instance_prototype()));
- SetProperty(prototype, Factory::constructor_symbol(),
- Top::object_function(), NONE);
- } else {
- Handle<ObjectTemplateInfo> data = v8::Utils::OpenHandle(*global_template);
- Handle<FunctionTemplateInfo> global_constructor =
- Handle<FunctionTemplateInfo>(
- FunctionTemplateInfo::cast(data->constructor()));
- global_function = Factory::CreateApiFunction(global_constructor, true);
- }
-
- SetExpectedNofProperties(global_function, 100);
- global_function->shared()->set_instance_class_name(*global_name);
- global_function->initial_map()->set_needs_access_check();
-
+ // Step 1: create a fresh inner JSGlobalObject
Handle<JSGlobalObject> object;
- if (global_object.location() != NULL) {
- ASSERT(global_object->IsJSGlobalObject());
- object =
- ReinitializeJSGlobalObject(
- global_function,
- Handle<JSGlobalObject>::cast(global_object));
- } else {
- object =
- Handle<JSGlobalObject>::cast(Factory::NewJSObject(global_function,
- TENURED));
+ {
+ Handle<JSFunction> js_global_function;
+ Handle<ObjectTemplateInfo> js_global_template;
+ if (!global_template.IsEmpty()) {
+ // Get prototype template of the global_template
+ Handle<ObjectTemplateInfo> data =
+ v8::Utils::OpenHandle(*global_template);
+ Handle<FunctionTemplateInfo> global_constructor =
+ Handle<FunctionTemplateInfo>(
+ FunctionTemplateInfo::cast(data->constructor()));
+ Handle<Object> proto_template(global_constructor->prototype_template());
+ if (!proto_template->IsUndefined()) {
+ js_global_template =
+ Handle<ObjectTemplateInfo>::cast(proto_template);
+ }
+ }
+
+ if (js_global_template.is_null()) {
+ Handle<String> name = Handle<String>(Heap::empty_symbol());
+ Handle<Code> code = Handle<Code>(Builtins::builtin(Builtins::Illegal));
+ js_global_function =
+ Factory::NewFunction(name, JS_GLOBAL_OBJECT_TYPE,
+ JSGlobalObject::kSize, code, true);
+ // Change the constructor property of the prototype of the
+ // hidden global function to refer to the Object function.
+ Handle<JSObject> prototype =
+ Handle<JSObject>(
+ JSObject::cast(js_global_function->instance_prototype()));
+ SetProperty(prototype, Factory::constructor_symbol(),
+ Top::object_function(), NONE);
+ } else {
+ Handle<FunctionTemplateInfo> js_global_constructor(
+ FunctionTemplateInfo::cast(js_global_template->constructor()));
+ js_global_function =
+ Factory::CreateApiFunction(js_global_constructor,
+ Factory::InnerGlobalObject);
+ }
+
+ js_global_function->initial_map()->set_is_hidden_prototype();
+ SetExpectedNofProperties(js_global_function, 100);
+ object = Handle<JSGlobalObject>::cast(
+ Factory::NewJSObject(js_global_function, TENURED));
}
// Set the global context for the global object.
object->set_global_context(*global_context());
- // Security setup: Set the security token of the global object to
- // its global context. This makes the security check between two
- // different contexts fail by default even in case of global
- // object reinitialization.
- object->set_security_token(*global_context());
+ // Step 2: create or re-initialize the global proxy object.
+ Handle<JSGlobalProxy> global_proxy;
+ {
+ Handle<JSFunction> global_proxy_function;
+ if (global_template.IsEmpty()) {
+ Handle<String> name = Handle<String>(Heap::empty_symbol());
+ Handle<Code> code = Handle<Code>(Builtins::builtin(Builtins::Illegal));
+ global_proxy_function =
+ Factory::NewFunction(name, JS_GLOBAL_PROXY_TYPE,
+ JSGlobalProxy::kSize, code, true);
+ } else {
+ Handle<ObjectTemplateInfo> data =
+ v8::Utils::OpenHandle(*global_template);
+ Handle<FunctionTemplateInfo> global_constructor(
+ FunctionTemplateInfo::cast(data->constructor()));
+ global_proxy_function =
+ Factory::CreateApiFunction(global_constructor,
+ Factory::OuterGlobalObject);
+ }
+
+ Handle<String> global_name = Factory::LookupAsciiSymbol("global");
+ global_proxy_function->shared()->set_instance_class_name(*global_name);
+ global_proxy_function->initial_map()->set_is_access_check_needed();
+
+ // Set global_proxy.__proto__ to js_global after ConfigureGlobalObjects
+
+ if (global_object.location() != NULL) {
+ ASSERT(global_object->IsJSGlobalProxy());
+ global_proxy =
+ ReinitializeJSGlobalProxy(
+ global_proxy_function,
+ Handle<JSGlobalProxy>::cast(global_object));
+ } else {
+ global_proxy = Handle<JSGlobalProxy>::cast(
+ Factory::NewJSObject(global_proxy_function, TENURED));
+ }
+
+ // Security setup: Set the security token of the global object to
+ // its the inner global. This makes the security check between two
+ // different contexts fail by default even in case of global
+ // object reinitialization.
+ object->set_global_receiver(*global_proxy);
+ global_proxy->set_context(*global_context());
+ }
{ // --- G l o b a l C o n t e x t ---
// use the empty function as closure (no scope info)
@@ -564,6 +629,9 @@
// set extension and global object
global_context()->set_extension(*object);
global_context()->set_global(*object);
+ global_context()->set_global_proxy(*global_proxy);
+ // use inner global object as security token by default
+ global_context()->set_security_token(*object);
}
Handle<JSObject> global = Handle<JSObject>(global_context()->global());
@@ -671,25 +739,46 @@
Handle<JSObject> prototype =
Handle<JSObject>(
JSObject::cast(global_context()->object_function()->prototype()));
- Handle<JSFunction> function =
- Factory::NewFunctionWithPrototype(symbol, JS_OBJECT_TYPE,
- JSObject::kHeaderSize, prototype,
- code, true);
- function->shared()->set_instance_class_name(*symbol);
+ Handle<JSFunction> function =
+ Factory::NewFunctionWithPrototype(symbol,
+ JS_OBJECT_TYPE,
+ JSObject::kHeaderSize,
+ prototype,
+ code,
+ false);
+ ASSERT(!function->has_initial_map());
+ function->shared()->set_instance_class_name(*symbol);
+ function->shared()->set_expected_nof_properties(2);
Handle<JSObject> result = Factory::NewJSObject(function);
global_context()->set_arguments_boilerplate(*result);
// Note: callee must be added as the first property and
// length must be added as the second property.
- SetProperty(result, Factory::callee_symbol(), Factory::undefined_value(),
+ SetProperty(result, Factory::callee_symbol(),
+ Factory::undefined_value(),
DONT_ENUM);
- SetProperty(result, Factory::length_symbol(), Factory::undefined_value(),
+ SetProperty(result, Factory::length_symbol(),
+ Factory::undefined_value(),
DONT_ENUM);
+#ifdef DEBUG
+ LookupResult lookup;
+ result->LocalLookup(Heap::callee_symbol(), &lookup);
+ ASSERT(lookup.IsValid() && (lookup.type() == FIELD));
+ ASSERT(lookup.GetFieldIndex() == Heap::arguments_callee_index);
+
+ result->LocalLookup(Heap::length_symbol(), &lookup);
+ ASSERT(lookup.IsValid() && (lookup.type() == FIELD));
+ ASSERT(lookup.GetFieldIndex() == Heap::arguments_length_index);
+
+ ASSERT(result->map()->inobject_properties() > Heap::arguments_callee_index);
+ ASSERT(result->map()->inobject_properties() > Heap::arguments_length_index);
+
// Check the state of the object.
ASSERT(result->HasFastProperties());
ASSERT(result->HasFastElements());
+#endif
}
{ // --- context extension
@@ -835,6 +924,7 @@
TENURED));
builtins->set_builtins(*builtins);
builtins->set_global_context(*global_context());
+ builtins->set_global_receiver(*builtins);
// Setup the 'global' properties of the builtins object. The
// 'global' property that refers to the global object is the only
@@ -932,13 +1022,11 @@
SetupLazy(Handle<JSFunction>(global_context()->date_function()),
Natives::GetIndex("date"),
Top::global_context(),
- Handle<Context>(Top::context()->runtime_context()),
- Handle<Context>(Top::security_context()));
+ Handle<Context>(Top::context()->runtime_context()));
SetupLazy(Handle<JSFunction>(global_context()->regexp_function()),
Natives::GetIndex("regexp"),
Top::global_context(),
- Handle<Context>(Top::context()->runtime_context()),
- Handle<Context>(Top::security_context()));
+ Handle<Context>(Top::context()->runtime_context()));
} else if (strlen(FLAG_natives_file) != 0) {
// Otherwise install natives from natives file if file exists and
@@ -1004,14 +1092,14 @@
bool Genesis::InstallSpecialObjects() {
HandleScope scope;
- Handle<JSGlobalObject> global(
+ Handle<JSGlobalObject> js_global(
JSGlobalObject::cast(global_context()->global()));
// Expose the natives in global if a name for it is specified.
if (FLAG_expose_natives_as != NULL && strlen(FLAG_expose_natives_as) != 0) {
Handle<String> natives_string =
Factory::LookupAsciiSymbol(FLAG_expose_natives_as);
- SetProperty(global, natives_string,
- Handle<JSObject>(global->builtins()), DONT_ENUM);
+ SetProperty(js_global, natives_string,
+ Handle<JSObject>(js_global->builtins()), DONT_ENUM);
}
// Expose the debug global object in global if a name for it is specified.
@@ -1020,18 +1108,16 @@
// debugger but without tanking the whole context.
if (!Debug::Load())
return true;
- Handle<JSGlobalObject> debug_global =
- Handle<JSGlobalObject>(
- JSGlobalObject::cast(Debug::debug_context()->global()));
+ // Set the security token for the debugger context to the same as
+ // the shell global context to allow calling between these (otherwise
+ // exposing debug global object doesn't make much sense).
+ Debug::debug_context()->set_security_token(
+ global_context()->security_token());
+
Handle<String> debug_string =
Factory::LookupAsciiSymbol(FLAG_expose_debug_as);
- SetProperty(global, debug_string,
- Handle<JSObject>(debug_global), DONT_ENUM);
-
- // Set the security token for the debugger global object to the same as
- // the shell global object to allow calling between these (otherwise
- // exposing debug global object doesn't make much sense).
- debug_global->set_security_token(global->security_token());
+ SetProperty(js_global, debug_string,
+ Handle<Object>(Debug::debug_context()->global_proxy()), DONT_ENUM);
}
return true;
@@ -1122,21 +1208,48 @@
}
-bool Genesis::ConfigureGlobalObject(
- v8::Handle<v8::ObjectTemplate> global_template) {
- Handle<JSObject> global = Handle<JSObject>(global_context()->global());
- if (!global_template.IsEmpty()) {
- Handle<ObjectTemplateInfo> data = v8::Utils::OpenHandle(*global_template);
- bool pending_exception = false;
- Handle<JSObject> obj =
- Execution::InstantiateObject(data, &pending_exception);
- if (pending_exception) {
- ASSERT(Top::has_pending_exception());
- Top::clear_pending_exception();
- return false;
+bool Genesis::ConfigureGlobalObjects(
+ v8::Handle<v8::ObjectTemplate> global_proxy_template) {
+ Handle<JSObject> global_proxy(
+ JSObject::cast(global_context()->global_proxy()));
+ Handle<JSObject> js_global(JSObject::cast(global_context()->global()));
+
+ if (!global_proxy_template.IsEmpty()) {
+ // Configure the global proxy object.
+ Handle<ObjectTemplateInfo> proxy_data =
+ v8::Utils::OpenHandle(*global_proxy_template);
+ if (!ConfigureApiObject(global_proxy, proxy_data)) return false;
+
+ // Configure the inner global object.
+ Handle<FunctionTemplateInfo> proxy_constructor(
+ FunctionTemplateInfo::cast(proxy_data->constructor()));
+ if (!proxy_constructor->prototype_template()->IsUndefined()) {
+ Handle<ObjectTemplateInfo> inner_data(
+ ObjectTemplateInfo::cast(proxy_constructor->prototype_template()));
+ if (!ConfigureApiObject(js_global, inner_data)) return false;
}
- TransferObject(obj, global);
}
+
+ SetObjectPrototype(global_proxy, js_global);
+ return true;
+}
+
+
+bool Genesis::ConfigureApiObject(Handle<JSObject> object,
+ Handle<ObjectTemplateInfo> object_template) {
+ ASSERT(!object_template.is_null());
+ ASSERT(object->IsInstanceOf(
+ FunctionTemplateInfo::cast(object_template->constructor())));
+
+ bool pending_exception = false;
+ Handle<JSObject> obj =
+ Execution::InstantiateObject(object_template, &pending_exception);
+ if (pending_exception) {
+ ASSERT(Top::has_pending_exception());
+ Top::clear_pending_exception();
+ return false;
+ }
+ TransferObject(obj, object);
return true;
}
@@ -1331,7 +1444,8 @@
MakeFunctionInstancePrototypeWritable();
BuildSpecialFunctionTable();
- if (!ConfigureGlobalObject(global_template)) return;
+
+ if (!ConfigureGlobalObjects(global_template)) return;
if (!InstallExtensions(extensions)) return;
diff --git a/src/bootstrapper.h b/src/bootstrapper.h
index 6b5f987..0b0784e 100644
--- a/src/bootstrapper.h
+++ b/src/bootstrapper.h
@@ -46,6 +46,9 @@
v8::Handle<v8::ObjectTemplate> global_template,
v8::ExtensionConfiguration* extensions);
+ // Detach the environment from its outer global object.
+ static void DetachGlobal(Handle<Context> env);
+
// Traverses the pointers for memory manangment.
static void Iterate(ObjectVisitor* v);
@@ -58,7 +61,7 @@
// Append code that needs fixup at the end of boot strapping.
static void AddFixup(Code* code, MacroAssembler* masm);
- // Tells wheter boostrapping is active.
+ // Tells whether boostrapping is active.
static bool IsActive();
// Encoding/decoding support for fixup flags.
diff --git a/src/builtins-arm.cc b/src/builtins-arm.cc
index 68b5f1d..c1b654f 100644
--- a/src/builtins-arm.cc
+++ b/src/builtins-arm.cc
@@ -333,11 +333,12 @@
__ LeaveInternalFrame();
__ b(&patch_receiver);
- // Use the global object from the called function as the receiver.
+ // Use the global receiver object from the called function as the receiver.
__ bind(&use_global_receiver);
const int kGlobalIndex =
Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
__ ldr(r2, FieldMemOperand(cp, kGlobalIndex));
+ __ ldr(r2, FieldMemOperand(r2, GlobalObject::kGlobalReceiverOffset));
__ bind(&patch_receiver);
__ add(r3, sp, Operand(r0, LSL, kPointerSizeLog2));
@@ -472,10 +473,12 @@
__ InvokeBuiltin(Builtins::TO_OBJECT, CALL_JS);
__ b(&push_receiver);
- // Use the current global object as the receiver.
+ // Use the current global receiver object as the receiver.
__ bind(&use_global_receiver);
- __ ldr(r0, FieldMemOperand(cp, Context::kHeaderSize +
- Context::GLOBAL_INDEX * kPointerSize));
+ const int kGlobalOffset =
+ Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
+ __ ldr(r0, FieldMemOperand(cp, kGlobalOffset));
+ __ ldr(r0, FieldMemOperand(r0, GlobalObject::kGlobalReceiverOffset));
// Push the receiver.
// r0: receiver
diff --git a/src/builtins-ia32.cc b/src/builtins-ia32.cc
index 28122cf..0de381c 100644
--- a/src/builtins-ia32.cc
+++ b/src/builtins-ia32.cc
@@ -450,11 +450,12 @@
__ LeaveInternalFrame();
__ jmp(&patch_receiver);
- // Use the global object from the called function as the receiver.
+ // Use the global receiver object from the called function as the receiver.
__ bind(&use_global_receiver);
const int kGlobalIndex =
Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
__ mov(ebx, FieldOperand(esi, kGlobalIndex));
+ __ mov(ebx, FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset));
__ bind(&patch_receiver);
__ mov(Operand(esp, eax, times_4, 0), ebx);
@@ -593,11 +594,12 @@
__ mov(ebx, Operand(eax));
__ jmp(&push_receiver);
- // Use the current global object as the receiver.
+ // Use the current global receiver object as the receiver.
__ bind(&use_global_receiver);
const int kGlobalOffset =
Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
__ mov(ebx, FieldOperand(esi, kGlobalOffset));
+ __ mov(ebx, FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset));
// Push the receiver.
__ bind(&push_receiver);
diff --git a/src/builtins.cc b/src/builtins.cc
index c8c428b..03bce65 100644
--- a/src/builtins.cc
+++ b/src/builtins.cc
@@ -105,8 +105,8 @@
Code* code = Builtins::builtin(Builtins::Illegal);
*resolved = false;
- if (Top::security_context() != NULL) {
- Object* object = Top::security_context_builtins()->javascript_builtin(id);
+ if (Top::context() != NULL) {
+ Object* object = Top::builtins()->javascript_builtin(id);
if (object->IsJSFunction()) {
Handle<JSFunction> function(JSFunction::cast(object));
// Make sure the number of parameters match the formal parameter count.
@@ -177,7 +177,7 @@
Object* obj = Heap::AllocateFixedArrayWithHoles(len->value());
if (obj->IsFailure()) return obj;
FixedArray* elms = FixedArray::cast(obj);
- FixedArray::WriteBarrierMode mode = elms->GetWriteBarrierMode();
+ WriteBarrierMode mode = elms->GetWriteBarrierMode();
// Fill in the content
for (int index = 0; index < number_of_elements; index++) {
elms->set(index, BUILTIN_ARG(index+1), mode);
@@ -185,7 +185,7 @@
// Set length and elements on the array.
array->set_elements(FixedArray::cast(obj));
- array->set_length(len);
+ array->set_length(len, SKIP_WRITE_BARRIER);
return array;
}
@@ -214,7 +214,7 @@
Object* obj = Heap::AllocateFixedArrayWithHoles(capacity);
if (obj->IsFailure()) return obj;
FixedArray* new_elms = FixedArray::cast(obj);
- FixedArray::WriteBarrierMode mode = new_elms->GetWriteBarrierMode();
+ WriteBarrierMode mode = new_elms->GetWriteBarrierMode();
// Fill out the new array with old elements.
for (int i = 0; i < len; i++) new_elms->set(i, elms->get(i), mode);
// Add the provided values.
@@ -225,7 +225,7 @@
array->set_elements(new_elms);
}
// Set the length.
- array->set_length(Smi::FromInt(new_length));
+ array->set_length(Smi::FromInt(new_length), SKIP_WRITE_BARRIER);
return array->length();
}
BUILTIN_END
@@ -244,12 +244,11 @@
Object* top = elms->get(len - 1);
// Set the length.
- array->set_length(Smi::FromInt(len - 1));
+ array->set_length(Smi::FromInt(len - 1), SKIP_WRITE_BARRIER);
if (!top->IsTheHole()) {
// Delete the top element.
elms->set_the_hole(len - 1);
-
return top;
}
diff --git a/src/codegen-arm.cc b/src/codegen-arm.cc
index 92206dd..74f1ea7 100644
--- a/src/codegen-arm.cc
+++ b/src/codegen-arm.cc
@@ -415,6 +415,13 @@
}
+void CodeGenerator::LoadGlobalReceiver(Register s) {
+ __ ldr(s, ContextOperand(cp, Context::GLOBAL_INDEX));
+ __ ldr(s, FieldMemOperand(s, GlobalObject::kGlobalReceiverOffset));
+ __ push(s);
+}
+
+
// TODO(1241834): Get rid of this function in favor of just using Load, now
// that we have the INSIDE_TYPEOF typeof state. => Need to handle global
// variables w/o reference errors elsewhere.
@@ -1236,8 +1243,12 @@
void CodeGenerator::GenerateFastCaseSwitchJumpTable(
- SwitchStatement* node, int min_index, int range, Label *fail_label,
- SmartPointer<Label*> &case_targets, SmartPointer<Label> &case_labels) {
+ SwitchStatement* node,
+ int min_index,
+ int range,
+ Label* fail_label,
+ Vector<Label*> case_targets,
+ Vector<Label> case_labels) {
ASSERT(kSmiTag == 0 && kSmiTagSize <= 2);
@@ -1627,9 +1638,13 @@
__ PushTryHandler(IN_JAVASCRIPT, TRY_CATCH_HANDLER);
- // Introduce shadow labels for all escapes from the try block,
- // including returns. We should probably try to unify the escaping
- // labels and the return label.
+ // Shadow the labels for all escapes from the try block, including
+ // returns. During shadowing, the original label is hidden as the
+ // LabelShadow and operations on the original actually affect the
+ // shadowing label.
+ //
+ // We should probably try to unify the escaping labels and the return
+ // label.
int nof_escapes = node->escaping_labels()->length();
List<LabelShadow*> shadows(1 + nof_escapes);
shadows.Add(new LabelShadow(&function_return_));
@@ -1642,6 +1657,8 @@
__ pop(r0); // Discard the result.
// Stop the introduced shadowing and count the number of required unlinks.
+ // After shadowing stops, the original labels are unshadowed and the
+ // LabelShadows represent the formerly shadowing labels.
int nof_unlinks = 0;
for (int i = 0; i <= nof_escapes; i++) {
shadows[i]->StopShadowing();
@@ -1660,7 +1677,8 @@
// Code slot popped.
if (nof_unlinks > 0) __ b(&exit);
- // Generate unlink code for all used shadow labels.
+ // Generate unlink code for the (formerly) shadowing labels that have been
+ // jumped to.
for (int i = 0; i <= nof_escapes; i++) {
if (shadows[i]->is_linked()) {
// Unlink from try chain;
@@ -1677,7 +1695,7 @@
__ add(sp, sp, Operand(StackHandlerConstants::kSize - kPointerSize));
// Code slot popped.
- __ b(shadows[i]->shadowed());
+ __ b(shadows[i]->original_label());
}
}
@@ -1708,9 +1726,12 @@
__ PushTryHandler(IN_JAVASCRIPT, TRY_FINALLY_HANDLER);
- // Introduce shadow labels for all escapes from the try block,
- // including returns. We should probably try to unify the escaping
- // labels and the return label.
+ // Shadow the labels for all escapes from the try block, including
+ // returns. Shadowing hides the original label as the LabelShadow and
+ // operations on the original actually affect the shadowing label.
+ //
+ // We should probably try to unify the escaping labels and the return
+ // label.
int nof_escapes = node->escaping_labels()->length();
List<LabelShadow*> shadows(1 + nof_escapes);
shadows.Add(new LabelShadow(&function_return_));
@@ -1721,8 +1742,9 @@
// Generate code for the statements in the try block.
VisitStatements(node->try_block()->statements());
- // Stop the introduced shadowing and count the number of required
- // unlinks.
+ // Stop the introduced shadowing and count the number of required unlinks.
+ // After shadowing stops, the original labels are unshadowed and the
+ // LabelShadows represent the formerly shadowing labels.
int nof_unlinks = 0;
for (int i = 0; i <= nof_escapes; i++) {
shadows[i]->StopShadowing();
@@ -1735,14 +1757,17 @@
__ mov(r2, Operand(Smi::FromInt(FALLING)));
if (nof_unlinks > 0) __ b(&unlink);
- // Generate code that sets the state for all used shadow labels.
+ // Generate code to set the state for the (formerly) shadowing labels that
+ // have been jumped to.
for (int i = 0; i <= nof_escapes; i++) {
if (shadows[i]->is_linked()) {
__ bind(shadows[i]);
- if (shadows[i]->shadowed() == &function_return_) {
- __ push(r0); // Materialize the return value on the stack
+ if (shadows[i]->original_label() == &function_return_) {
+ // If this label shadowed the function return, materialize the
+ // return value on the stack.
+ __ push(r0);
} else {
- // Fake TOS for break and continue (not return).
+ // Fake TOS for labels that shadowed breaks and continues.
__ mov(r0, Operand(Factory::undefined_value()));
__ push(r0);
}
@@ -1789,18 +1814,18 @@
__ pop(r0);
break_stack_height_ -= kFinallyStackSize;
- // Generate code that jumps to the right destination for all used
- // shadow labels.
+ // Generate code to jump to the right destination for all used (formerly)
+ // shadowing labels.
for (int i = 0; i <= nof_escapes; i++) {
if (shadows[i]->is_bound()) {
__ cmp(r2, Operand(Smi::FromInt(JUMPING + i)));
- if (shadows[i]->shadowed() != &function_return_) {
+ if (shadows[i]->original_label() != &function_return_) {
Label next;
__ b(ne, &next);
- __ b(shadows[i]->shadowed());
+ __ b(shadows[i]->original_label());
__ bind(&next);
} else {
- __ b(eq, shadows[i]->shadowed());
+ __ b(eq, shadows[i]->original_label());
}
}
}
@@ -2220,7 +2245,10 @@
// Push the name of the function and the receiver onto the stack.
__ mov(r0, Operand(var->name()));
__ push(r0);
- LoadGlobal();
+
+ // TODO(120): use JSGlobalObject for function lookup and inline cache,
+ // and use global proxy as 'this' for invocation.
+ LoadGlobalReceiver(r0);
// Load the arguments.
for (int i = 0; i < args->length(); i++) Load(args->at(i));
@@ -2308,7 +2336,10 @@
// Load the function.
Load(function);
// Pass the global object as the receiver.
- LoadGlobal();
+
+ // TODO(120): use JSGlobalObject for function lookup and inline cache,
+ // and use global proxy as 'this' for invocation.
+ LoadGlobalReceiver(r0);
// Call the function.
CallWithArguments(args, node->position());
__ push(r0);
@@ -2328,7 +2359,7 @@
// Compute function to call and use the global object as the
// receiver.
Load(node->expression());
- LoadGlobal();
+ LoadGlobalReceiver(r0);
// Push the arguments ("left-to-right") on the stack.
ZoneList<Expression*>* args = node->arguments();
@@ -2873,12 +2904,11 @@
// inlining a null check instead of calling the (very) general
// runtime routine for checking equality.
- bool left_is_null =
- left->AsLiteral() != NULL && left->AsLiteral()->IsNull();
- bool right_is_null =
- right->AsLiteral() != NULL && right->AsLiteral()->IsNull();
-
if (op == Token::EQ || op == Token::EQ_STRICT) {
+ bool left_is_null =
+ left->AsLiteral() != NULL && left->AsLiteral()->IsNull();
+ bool right_is_null =
+ right->AsLiteral() != NULL && right->AsLiteral()->IsNull();
// The 'null' value is only equal to 'null' or 'undefined'.
if (left_is_null || right_is_null) {
Load(left_is_null ? right : left);
@@ -3897,7 +3927,7 @@
#ifdef DEBUG
if (FLAG_gc_greedy) {
- Failure* failure = Failure::RetryAfterGC(0, NEW_SPACE);
+ Failure* failure = Failure::RetryAfterGC(0);
__ mov(r0, Operand(reinterpret_cast<intptr_t>(failure)));
}
GenerateCore(masm,
diff --git a/src/codegen-arm.h b/src/codegen-arm.h
index 5342b5a..8058138 100644
--- a/src/codegen-arm.h
+++ b/src/codegen-arm.h
@@ -225,6 +225,7 @@
bool force_cc);
void Load(Expression* x, TypeofState typeof_state = NOT_INSIDE_TYPEOF);
void LoadGlobal();
+ void LoadGlobalReceiver(Register scratch);
// Read a value from a slot and leave it on top of the expression stack.
void LoadFromSlot(Slot* slot, TypeofState typeof_state);
@@ -311,24 +312,28 @@
// Allocate a jump table and create code to jump through it.
// Should call GenerateFastCaseSwitchCases to generate the code for
// all the cases at the appropriate point.
- void GenerateFastCaseSwitchJumpTable(SwitchStatement* node, int min_index,
- int range, Label *fail_label,
- SmartPointer<Label*> &case_targets,
- SmartPointer<Label>& case_labels);
+ void GenerateFastCaseSwitchJumpTable(SwitchStatement* node,
+ int min_index,
+ int range,
+ Label* fail_label,
+ Vector<Label*> case_targets,
+ Vector<Label> case_labels);
// Generate the code for cases for the fast case switch.
// Called by GenerateFastCaseSwitchJumpTable.
void GenerateFastCaseSwitchCases(SwitchStatement* node,
- SmartPointer<Label> &case_labels);
+ Vector<Label> case_labels);
// Fast support for constant-Smi switches.
- void GenerateFastCaseSwitchStatement(SwitchStatement *node, int min_index,
- int range, int default_index);
+ void GenerateFastCaseSwitchStatement(SwitchStatement* node,
+ int min_index,
+ int range,
+ int default_index);
// Fast support for constant-Smi switches. Tests whether switch statement
// permits optimization and calls GenerateFastCaseSwitch if it does.
// Returns true if the fast-case switch was generated, and false if not.
- bool TryGenerateFastCaseSwitchStatement(SwitchStatement *node);
+ bool TryGenerateFastCaseSwitchStatement(SwitchStatement* node);
// Bottle-neck interface to call the Assembler to generate the statement
diff --git a/src/codegen-ia32.cc b/src/codegen-ia32.cc
index 54ff293..a339a96 100644
--- a/src/codegen-ia32.cc
+++ b/src/codegen-ia32.cc
@@ -519,6 +519,12 @@
}
+void CodeGenerator::LoadGlobalReceiver(Register scratch) {
+ __ mov(scratch, GlobalObject());
+ frame_->Push(FieldOperand(scratch, GlobalObject::kGlobalReceiverOffset));
+}
+
+
// TODO(1241834): Get rid of this function in favor of just using Load, now
// that we have the INSIDE_TYPEOF typeof state. => Need to handle global
// variables w/o reference errors elsewhere.
@@ -686,34 +692,50 @@
};
+// Flag that indicates whether or not the code for dealing with smis
+// is inlined or should be dealt with in the stub.
+enum GenericBinaryFlags {
+ SMI_CODE_IN_STUB,
+ SMI_CODE_INLINED
+};
+
+
class GenericBinaryOpStub: public CodeStub {
public:
- GenericBinaryOpStub(Token::Value op, OverwriteMode mode)
- : op_(op), mode_(mode) { }
+ GenericBinaryOpStub(Token::Value op,
+ OverwriteMode mode,
+ GenericBinaryFlags flags)
+ : op_(op), mode_(mode), flags_(flags) { }
+
+ void GenerateSmiCode(MacroAssembler* masm, Label* slow);
private:
Token::Value op_;
OverwriteMode mode_;
+ GenericBinaryFlags flags_;
const char* GetName();
#ifdef DEBUG
void Print() {
- PrintF("GenericBinaryOpStub (op %s), (mode %d)\n",
+ PrintF("GenericBinaryOpStub (op %s), (mode %d, flags %d)\n",
Token::String(op_),
- static_cast<int>(mode_));
+ static_cast<int>(mode_),
+ static_cast<int>(flags_));
}
#endif
- // Minor key encoding in 16 bits OOOOOOOOOOOOOOMM.
+ // Minor key encoding in 16 bits FOOOOOOOOOOOOOMM.
class ModeBits: public BitField<OverwriteMode, 0, 2> {};
- class OpBits: public BitField<Token::Value, 2, 14> {};
+ class OpBits: public BitField<Token::Value, 2, 13> {};
+ class FlagBits: public BitField<GenericBinaryFlags, 15, 1> {};
Major MajorKey() { return GenericBinaryOp; }
int MinorKey() {
// Encode the parameters in a unique 16 bit value.
return OpBits::encode(op_) |
- ModeBits::encode(mode_);
+ ModeBits::encode(mode_) |
+ FlagBits::encode(flags_);
}
void Generate(MacroAssembler* masm);
};
@@ -736,115 +758,84 @@
}
+class DeferredInlineBinaryOperation: public DeferredCode {
+ public:
+ DeferredInlineBinaryOperation(CodeGenerator* generator,
+ Token::Value op,
+ OverwriteMode mode,
+ GenericBinaryFlags flags)
+ : DeferredCode(generator), stub_(op, mode, flags) { }
+
+ void GenerateInlineCode() {
+ stub_.GenerateSmiCode(masm(), enter());
+ }
+
+ virtual void Generate() {
+ __ push(ebx);
+ __ CallStub(&stub_);
+ // We must preserve the eax value here, because it will be written
+ // to the top-of-stack element when getting back to the fast case
+ // code. See comment in GenericBinaryOperation where
+ // deferred->exit() is bound.
+ __ push(eax);
+ }
+
+ private:
+ GenericBinaryOpStub stub_;
+};
+
+
void CodeGenerator::GenericBinaryOperation(Token::Value op,
- OverwriteMode overwrite_mode) {
+ OverwriteMode overwrite_mode) {
Comment cmnt(masm_, "[ BinaryOperation");
Comment cmnt_token(masm_, Token::String(op));
+
+ if (op == Token::COMMA) {
+ // Simply discard left value.
+ frame_->Pop(eax);
+ frame_->Pop();
+ frame_->Push(eax);
+ return;
+ }
+
+ // For now, we keep the old behavior and only inline the smi code
+ // for the bitwise operations.
+ GenericBinaryFlags flags;
switch (op) {
- case Token::ADD:
- case Token::SUB:
- case Token::MUL:
- case Token::DIV:
- case Token::MOD: {
- GenericBinaryOpStub stub(op, overwrite_mode);
- __ CallStub(&stub);
- frame_->Push(eax);
- break;
- }
case Token::BIT_OR:
case Token::BIT_AND:
- case Token::BIT_XOR: {
- Label slow, exit;
- frame_->Pop(eax); // get y
- frame_->Pop(edx); // get x
- __ mov(ecx, Operand(edx)); // Prepare smi check.
- // tag check
- __ or_(ecx, Operand(eax)); // ecx = x | y;
- ASSERT(kSmiTag == 0); // adjust code below
- __ test(ecx, Immediate(kSmiTagMask));
- __ j(not_zero, &slow, taken);
- switch (op) {
- case Token::BIT_OR: __ or_(eax, Operand(edx)); break;
- case Token::BIT_AND: __ and_(eax, Operand(edx)); break;
- case Token::BIT_XOR: __ xor_(eax, Operand(edx)); break;
- default: UNREACHABLE();
- }
- __ jmp(&exit);
- __ bind(&slow);
- frame_->Push(edx); // restore stack slots
- frame_->Push(eax);
- GenericBinaryOpStub stub(op, overwrite_mode);
- __ CallStub(&stub);
- __ bind(&exit);
- frame_->Push(eax); // push the result to the stack
- break;
- }
+ case Token::BIT_XOR:
case Token::SHL:
case Token::SHR:
- case Token::SAR: {
- Label slow, exit;
- frame_->Pop(edx); // get y
- frame_->Pop(eax); // get x
- // tag check
- __ mov(ecx, Operand(edx));
- __ or_(ecx, Operand(eax)); // ecx = x | y;
- ASSERT(kSmiTag == 0); // adjust code below
- __ test(ecx, Immediate(kSmiTagMask));
- __ j(not_zero, &slow, not_taken);
- // get copies of operands
- __ mov(ebx, Operand(eax));
- __ mov(ecx, Operand(edx));
- // remove tags from operands (but keep sign)
- __ sar(ebx, kSmiTagSize);
- __ sar(ecx, kSmiTagSize);
- // perform operation
- switch (op) {
- case Token::SAR:
- __ sar(ebx);
- // no checks of result necessary
- break;
- case Token::SHR:
- __ shr(ebx);
- // Check that the *unsigned* result fits in a smi.
- // neither of the two high-order bits can be set:
- // - 0x80000000: high bit would be lost when smi tagging.
- // - 0x40000000: this number would convert to negative when
- // smi tagging these two cases can only happen with shifts
- // by 0 or 1 when handed a valid smi.
- __ test(ebx, Immediate(0xc0000000));
- __ j(not_zero, &slow, not_taken);
- break;
- case Token::SHL:
- __ shl(ebx);
- // Check that the *signed* result fits in a smi.
- __ lea(ecx, Operand(ebx, 0x40000000));
- __ test(ecx, Immediate(0x80000000));
- __ j(not_zero, &slow, not_taken);
- break;
- default: UNREACHABLE();
- }
- // tag result and store it in TOS (eax)
- ASSERT(kSmiTagSize == times_2); // adjust code if not the case
- __ lea(eax, Operand(ebx, times_2, kSmiTag));
- __ jmp(&exit);
- // slow case
- __ bind(&slow);
- frame_->Push(eax); // restore stack
- frame_->Push(edx);
- GenericBinaryOpStub stub(op, overwrite_mode);
- __ CallStub(&stub);
- __ bind(&exit);
- frame_->Push(eax);
+ case Token::SAR:
+ flags = SMI_CODE_INLINED;
break;
- }
- case Token::COMMA: {
- // simply discard left value
- frame_->Pop(eax);
- frame_->Pop();
- frame_->Push(eax);
+
+ default:
+ flags = SMI_CODE_IN_STUB;
break;
- }
- default: UNREACHABLE();
+ }
+
+ if (flags == SMI_CODE_INLINED) {
+ // Create a new deferred code for the slow-case part.
+ DeferredInlineBinaryOperation* deferred =
+ new DeferredInlineBinaryOperation(this, op, overwrite_mode, flags);
+ // Fetch the operands from the stack.
+ frame_->Pop(ebx); // get y
+ __ mov(eax, frame_->Top()); // get x
+ // Generate the inline part of the code.
+ deferred->GenerateInlineCode();
+ // Put result back on the stack. It seems somewhat weird to let
+ // the deferred code jump back before the assignment to the frame
+ // top, but this is just to let the peephole optimizer get rid of
+ // more code.
+ __ bind(deferred->exit());
+ __ mov(frame_->Top(), eax);
+ } else {
+ // Call the stub and push the result to the stack.
+ GenericBinaryOpStub stub(op, overwrite_mode, flags);
+ __ CallStub(&stub);
+ frame_->Push(eax);
}
}
@@ -861,7 +852,7 @@
virtual void Generate() {
__ push(eax);
__ push(Immediate(Smi::FromInt(value_)));
- GenericBinaryOpStub igostub(op_, overwrite_mode_);
+ GenericBinaryOpStub igostub(op_, overwrite_mode_, SMI_CODE_INLINED);
__ CallStub(&igostub);
}
@@ -884,7 +875,7 @@
virtual void Generate() {
__ push(Immediate(Smi::FromInt(value_)));
__ push(eax);
- GenericBinaryOpStub igostub(op_, overwrite_mode_);
+ GenericBinaryOpStub igostub(op_, overwrite_mode_, SMI_CODE_INLINED);
__ CallStub(&igostub);
}
@@ -909,7 +900,7 @@
__ sub(Operand(eax), immediate);
__ push(eax);
__ push(immediate);
- GenericBinaryOpStub igostub(Token::ADD, overwrite_mode_);
+ GenericBinaryOpStub igostub(Token::ADD, overwrite_mode_, SMI_CODE_INLINED);
__ CallStub(&igostub);
}
@@ -933,7 +924,7 @@
__ sub(Operand(eax), immediate);
__ push(immediate);
__ push(eax);
- GenericBinaryOpStub igostub(Token::ADD, overwrite_mode_);
+ GenericBinaryOpStub igostub(Token::ADD, overwrite_mode_, SMI_CODE_INLINED);
__ CallStub(&igostub);
}
@@ -957,7 +948,7 @@
__ add(Operand(eax), immediate);
__ push(eax);
__ push(immediate);
- GenericBinaryOpStub igostub(Token::SUB, overwrite_mode_);
+ GenericBinaryOpStub igostub(Token::SUB, overwrite_mode_, SMI_CODE_INLINED);
__ CallStub(&igostub);
}
@@ -983,7 +974,7 @@
__ add(eax, Operand(tos_reg_));
__ push(eax);
__ push(tos_reg_);
- GenericBinaryOpStub igostub(Token::SUB, overwrite_mode_);
+ GenericBinaryOpStub igostub(Token::SUB, overwrite_mode_, SMI_CODE_INLINED);
__ CallStub(&igostub);
}
@@ -1608,8 +1599,12 @@
// Generate a computed jump to a switch case.
void CodeGenerator::GenerateFastCaseSwitchJumpTable(
- SwitchStatement* node, int min_index, int range, Label *fail_label,
- SmartPointer<Label*> &case_targets, SmartPointer<Label> &case_labels) {
+ SwitchStatement* node,
+ int min_index,
+ int range,
+ Label* fail_label,
+ Vector<Label*> case_targets,
+ Vector<Label> case_labels) {
// Notice: Internal references, used by both the jmp instruction and
// the table entries, need to be relocated if the buffer grows. This
// prevents the forward use of Labels, since a displacement cannot
@@ -2009,9 +2004,13 @@
// TODO(1222589): remove the reliance of PushTryHandler on a cached TOS
frame_->Push(eax); //
- // Introduce shadow labels for all escapes from the try block,
- // including returns. We should probably try to unify the escaping
- // labels and the return label.
+ // Shadow the labels for all escapes from the try block, including
+ // returns. During shadowing, the original label is hidden as the
+ // LabelShadow and operations on the original actually affect the
+ // shadowing label.
+ //
+ // We should probably try to unify the escaping labels and the return
+ // label.
int nof_escapes = node->escaping_labels()->length();
List<LabelShadow*> shadows(1 + nof_escapes);
shadows.Add(new LabelShadow(&function_return_));
@@ -2026,6 +2025,8 @@
is_inside_try_ = was_inside_try;
// Stop the introduced shadowing and count the number of required unlinks.
+ // After shadowing stops, the original labels are unshadowed and the
+ // LabelShadows represent the formerly shadowing labels.
int nof_unlinks = 0;
for (int i = 0; i <= nof_escapes; i++) {
shadows[i]->StopShadowing();
@@ -2051,7 +2052,8 @@
// next_sp popped.
if (nof_unlinks > 0) __ jmp(&exit);
- // Generate unlink code for all used shadow labels.
+ // Generate unlink code for the (formerly) shadowing labels that have been
+ // jumped to.
for (int i = 0; i <= nof_escapes; i++) {
if (shadows[i]->is_linked()) {
// Unlink from try chain; be careful not to destroy the TOS.
@@ -2067,7 +2069,7 @@
frame_->Pop(Operand::StaticVariable(handler_address));
frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
// next_sp popped.
- __ jmp(shadows[i]->shadowed());
+ __ jmp(shadows[i]->original_label());
}
}
@@ -2100,9 +2102,13 @@
// TODO(1222589): remove the reliance of PushTryHandler on a cached TOS
frame_->Push(eax);
- // Introduce shadow labels for all escapes from the try block,
- // including returns. We should probably try to unify the escaping
- // labels and the return label.
+ // Shadow the labels for all escapes from the try block, including
+ // returns. During shadowing, the original label is hidden as the
+ // LabelShadow and operations on the original actually affect the
+ // shadowing label.
+ //
+ // We should probably try to unify the escaping labels and the return
+ // label.
int nof_escapes = node->escaping_labels()->length();
List<LabelShadow*> shadows(1 + nof_escapes);
shadows.Add(new LabelShadow(&function_return_));
@@ -2116,8 +2122,9 @@
VisitStatements(node->try_block()->statements());
is_inside_try_ = was_inside_try;
- // Stop the introduced shadowing and count the number of required
- // unlinks.
+ // Stop the introduced shadowing and count the number of required unlinks.
+ // After shadowing stops, the original labels are unshadowed and the
+ // LabelShadows represent the formerly shadowing labels.
int nof_unlinks = 0;
for (int i = 0; i <= nof_escapes; i++) {
shadows[i]->StopShadowing();
@@ -2129,15 +2136,17 @@
__ Set(ecx, Immediate(Smi::FromInt(FALLING)));
if (nof_unlinks > 0) __ jmp(&unlink);
- // Generate code that sets the state for all used shadow labels.
+ // Generate code to set the state for the (formerly) shadowing labels that
+ // have been jumped to.
for (int i = 0; i <= nof_escapes; i++) {
if (shadows[i]->is_linked()) {
__ bind(shadows[i]);
- if (shadows[i]->shadowed() == &function_return_) {
- // Materialize the return value on the stack.
+ if (shadows[i]->original_label() == &function_return_) {
+ // If this label shadowed the function return, materialize the
+ // return value on the stack.
frame_->Push(eax);
} else {
- // Fake TOS for break and continue.
+ // Fake TOS for labels that shadowed breaks and continues.
frame_->Push(Immediate(Factory::undefined_value()));
}
__ Set(ecx, Immediate(Smi::FromInt(JUMPING + i)));
@@ -2184,12 +2193,12 @@
frame_->Pop(eax);
break_stack_height_ -= kFinallyStackSize;
- // Generate code that jumps to the right destination for all used
- // shadow labels.
+ // Generate code to jump to the right destination for all used (formerly)
+ // shadowing labels.
for (int i = 0; i <= nof_escapes; i++) {
if (shadows[i]->is_bound()) {
__ cmp(Operand(ecx), Immediate(Smi::FromInt(JUMPING + i)));
- __ j(equal, shadows[i]->shadowed());
+ __ j(equal, shadows[i]->original_label());
}
}
@@ -2650,7 +2659,10 @@
// Push the name of the function and the receiver onto the stack.
frame_->Push(Immediate(var->name()));
- LoadGlobal();
+
+ // TODO(120): use JSGlobalObject for function lookup and inline cache,
+ // and use global proxy as 'this' for invocation.
+ LoadGlobalReceiver(eax);
// Load the arguments.
for (int i = 0; i < args->length(); i++) {
@@ -2736,7 +2748,10 @@
Load(function);
// Pass the global object as the receiver.
- LoadGlobal();
+
+ // TODO(120): use JSGlobalObject for function lookup and inline cache,
+ // and use global proxy as 'this' for invocation.
+ LoadGlobalReceiver(eax);
// Call the function.
CallWithArguments(args, node->position());
@@ -2756,7 +2771,7 @@
// Compute function to call and use the global object as the
// receiver.
Load(node->expression());
- LoadGlobal();
+ LoadGlobalReceiver(eax);
// Push the arguments ("left-to-right") on the stack.
ZoneList<Expression*>* args = node->arguments();
@@ -3496,12 +3511,11 @@
// inlining a null check instead of calling the (very) general
// runtime routine for checking equality.
- bool left_is_null =
- left->AsLiteral() != NULL && left->AsLiteral()->IsNull();
- bool right_is_null =
- right->AsLiteral() != NULL && right->AsLiteral()->IsNull();
-
if (op == Token::EQ || op == Token::EQ_STRICT) {
+ bool left_is_null =
+ left->AsLiteral() != NULL && left->AsLiteral()->IsNull();
+ bool right_is_null =
+ right->AsLiteral() != NULL && right->AsLiteral()->IsNull();
// The 'null' value is only equal to 'null' or 'undefined'.
if (left_is_null || right_is_null) {
Load(left_is_null ? right : left);
@@ -3536,7 +3550,6 @@
}
}
-
// NOTE: To make typeof testing for natives implemented in
// JavaScript really efficient, we generate special code for
// expressions of the form: 'typeof <expression> == <string>'.
@@ -3959,182 +3972,172 @@
}
-void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
- Label call_runtime;
- __ mov(eax, Operand(esp, 1 * kPointerSize)); // Get y.
- __ mov(edx, Operand(esp, 2 * kPointerSize)); // Get x.
+void GenericBinaryOpStub::GenerateSmiCode(MacroAssembler* masm, Label* slow) {
+ // Perform fast-case smi code for the operation (eax <op> ebx) and
+ // leave result in register eax.
- // 1. Smi case.
+ // Prepare the smi check of both operands by or'ing them together
+ // before checking against the smi mask.
+ __ mov(ecx, Operand(ebx));
+ __ or_(ecx, Operand(eax));
+
switch (op_) {
- case Token::ADD: {
- // eax: y.
- // edx: x.
- Label revert;
- __ mov(ecx, Operand(eax));
- __ or_(ecx, Operand(edx)); // ecx = x | y.
- __ add(eax, Operand(edx)); // Add y optimistically.
- // Go slow-path in case of overflow.
- __ j(overflow, &revert, not_taken);
- // Go slow-path in case of non-smi operands.
- ASSERT(kSmiTag == 0); // adjust code below
- __ test(ecx, Immediate(kSmiTagMask));
- __ j(not_zero, &revert, not_taken);
- __ ret(2 * kPointerSize); // Remove all operands.
-
- // Revert optimistic add.
- __ bind(&revert);
- __ sub(eax, Operand(edx));
+ case Token::ADD:
+ __ add(eax, Operand(ebx)); // add optimistically
+ __ j(overflow, slow, not_taken);
break;
- }
- case Token::SUB: {
- // eax: y.
- // edx: x.
- Label revert;
- __ mov(ecx, Operand(edx));
- __ or_(ecx, Operand(eax)); // ecx = x | y.
- __ sub(edx, Operand(eax)); // Subtract y optimistically.
- // Go slow-path in case of overflow.
- __ j(overflow, &revert, not_taken);
- // Go slow-path in case of non-smi operands.
- ASSERT(kSmiTag == 0); // adjust code below
- __ test(ecx, Immediate(kSmiTagMask));
- __ j(not_zero, &revert, not_taken);
- __ mov(eax, Operand(edx));
- __ ret(2 * kPointerSize); // Remove all operands.
- // Revert optimistic sub.
- __ bind(&revert);
- __ add(edx, Operand(eax));
+ case Token::SUB:
+ __ sub(eax, Operand(ebx)); // subtract optimistically
+ __ j(overflow, slow, not_taken);
break;
- }
- case Token::MUL: {
- // eax: y
- // edx: x
- // a) both operands smi and result fits into a smi -> return.
- // b) at least one of operands non-smi -> non_smi_operands.
- // c) result does not fit in a smi -> non_smi_result.
- Label non_smi_operands, non_smi_result;
- // Tag check.
- __ mov(ecx, Operand(edx));
- __ or_(ecx, Operand(eax)); // ecx = x | y.
- ASSERT(kSmiTag == 0); // Adjust code below.
- __ test(ecx, Immediate(kSmiTagMask));
- // Jump if not both smi; check if float numbers.
- __ j(not_zero, &non_smi_operands, not_taken);
- // Get copies of operands.
- __ mov(ebx, Operand(eax));
- __ mov(ecx, Operand(edx));
- // If the smi tag is 0 we can just leave the tag on one operand.
- ASSERT(kSmiTag == 0); // adjust code below
- // Remove tag from one of the operands (but keep sign).
- __ sar(ecx, kSmiTagSize);
- // Do multiplication.
- __ imul(eax, Operand(ecx)); // Multiplication of Smis; result in eax.
- // Go slow on overflows.
- __ j(overflow, &non_smi_result, not_taken);
- // ...but operands OK for float arithmetic.
-
- // If the result is +0 we may need to check if the result should
- // really be -0. Welcome to the -0 fan club.
- __ NegativeZeroTest(eax, ebx, edx, ecx, &non_smi_result);
-
- __ ret(2 * kPointerSize);
-
- __ bind(&non_smi_result);
- // TODO(1243132): Do not check float operands here.
- __ bind(&non_smi_operands);
- __ mov(eax, Operand(esp, 1 * kPointerSize));
- __ mov(edx, Operand(esp, 2 * kPointerSize));
+ case Token::DIV:
+ case Token::MOD:
+ // Sign extend eax into edx:eax.
+ __ cdq();
+ // Check for 0 divisor.
+ __ test(ebx, Operand(ebx));
+ __ j(zero, slow, not_taken);
break;
- }
- case Token::DIV: {
- // eax: y
- // edx: x
- Label non_smi_operands, non_smi_result, division_by_zero;
- __ mov(ebx, Operand(eax)); // Get y
- __ mov(eax, Operand(edx)); // Get x
- __ cdq(); // Sign extend eax into edx:eax.
- // Tag check.
- __ mov(ecx, Operand(ebx));
- __ or_(ecx, Operand(eax)); // ecx = x | y.
- ASSERT(kSmiTag == 0); // Adjust code below.
- __ test(ecx, Immediate(kSmiTagMask));
- // Jump if not both smi; check if float numbers.
- __ j(not_zero, &non_smi_operands, not_taken);
- __ test(ebx, Operand(ebx)); // Check for 0 divisor.
- __ j(zero, &division_by_zero, not_taken);
-
- __ idiv(ebx);
- // Check for the corner case of dividing the most negative smi by -1.
- // (We cannot use the overflow flag, since it is not set by idiv.)
- ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
- __ cmp(eax, 0x40000000);
- __ j(equal, &non_smi_result);
- // If the result is +0 we may need to check if the result should
- // really be -0. Welcome to the -0 fan club.
- __ NegativeZeroTest(eax, ecx, &non_smi_result); // Use ecx = x | y.
- __ test(edx, Operand(edx));
- // Use floats if there's a remainder.
- __ j(not_zero, &non_smi_result, not_taken);
- __ shl(eax, kSmiTagSize);
- __ ret(2 * kPointerSize); // Remove all operands.
-
- __ bind(&division_by_zero);
- __ mov(eax, Operand(esp, 1 * kPointerSize));
- __ mov(edx, Operand(esp, 2 * kPointerSize));
- __ jmp(&call_runtime); // Division by zero must go through runtime.
-
- __ bind(&non_smi_result);
- // TODO(1243132): Do not check float operands here.
- __ bind(&non_smi_operands);
- __ mov(eax, Operand(esp, 1 * kPointerSize));
- __ mov(edx, Operand(esp, 2 * kPointerSize));
+ default:
+ // Fall-through to smi check.
break;
- }
- case Token::MOD: {
- Label slow;
- __ mov(ebx, Operand(eax)); // get y
- __ mov(eax, Operand(edx)); // get x
- __ cdq(); // sign extend eax into edx:eax
- // tag check
- __ mov(ecx, Operand(ebx));
- __ or_(ecx, Operand(eax)); // ecx = x | y;
- ASSERT(kSmiTag == 0); // adjust code below
- __ test(ecx, Immediate(kSmiTagMask));
- __ j(not_zero, &slow, not_taken);
- __ test(ebx, Operand(ebx)); // test for y == 0
- __ j(zero, &slow);
-
- // Fast case: Do integer division and use remainder.
- __ idiv(ebx);
- __ NegativeZeroTest(edx, ecx, &slow); // use ecx = x | y
- __ mov(eax, Operand(edx));
- __ ret(2 * kPointerSize);
-
- // Slow case: Call runtime operator implementation.
- __ bind(&slow);
- __ mov(eax, Operand(esp, 1 * kPointerSize));
- __ mov(edx, Operand(esp, 2 * kPointerSize));
- // Fall through to |call_runtime|.
- break;
- }
- case Token::BIT_OR:
- case Token::BIT_AND:
- case Token::BIT_XOR:
- case Token::SAR:
- case Token::SHL:
- case Token::SHR: {
- // Smi-case for bitops should already have been inlined.
- break;
- }
- default: {
- UNREACHABLE();
- }
}
- // 2. Floating point case.
+ // Perform the actual smi check.
+ ASSERT(kSmiTag == 0); // adjust zero check if not the case
+ __ test(ecx, Immediate(kSmiTagMask));
+ __ j(not_zero, slow, not_taken);
+
+ switch (op_) {
+ case Token::ADD:
+ case Token::SUB:
+ // Do nothing here.
+ break;
+
+ case Token::MUL:
+ // If the smi tag is 0 we can just leave the tag on one operand.
+ ASSERT(kSmiTag == 0); // adjust code below if not the case
+ // Remove tag from one of the operands (but keep sign).
+ __ sar(eax, kSmiTagSize);
+ // Do multiplication.
+ __ imul(eax, Operand(ebx)); // multiplication of smis; result in eax
+ // Go slow on overflows.
+ __ j(overflow, slow, not_taken);
+ // Check for negative zero result.
+ __ NegativeZeroTest(eax, ecx, slow); // use ecx = x | y
+ break;
+
+ case Token::DIV:
+ // Divide edx:eax by ebx.
+ __ idiv(ebx);
+ // Check for the corner case of dividing the most negative smi
+ // by -1. We cannot use the overflow flag, since it is not set
+ // by idiv instruction.
+ ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
+ __ cmp(eax, 0x40000000);
+ __ j(equal, slow);
+ // Check for negative zero result.
+ __ NegativeZeroTest(eax, ecx, slow); // use ecx = x | y
+ // Check that the remainder is zero.
+ __ test(edx, Operand(edx));
+ __ j(not_zero, slow);
+ // Tag the result and store it in register eax.
+ ASSERT(kSmiTagSize == times_2); // adjust code if not the case
+ __ lea(eax, Operand(eax, times_2, kSmiTag));
+ break;
+
+ case Token::MOD:
+ // Divide edx:eax by ebx.
+ __ idiv(ebx);
+ // Check for negative zero result.
+ __ NegativeZeroTest(edx, ecx, slow); // use ecx = x | y
+ // Move remainder to register eax.
+ __ mov(eax, Operand(edx));
+ break;
+
+ case Token::BIT_OR:
+ __ or_(eax, Operand(ebx));
+ break;
+
+ case Token::BIT_AND:
+ __ and_(eax, Operand(ebx));
+ break;
+
+ case Token::BIT_XOR:
+ __ xor_(eax, Operand(ebx));
+ break;
+
+ case Token::SHL:
+ case Token::SHR:
+ case Token::SAR:
+ // Move the second operand into register ecx.
+ __ mov(ecx, Operand(ebx));
+ // Remove tags from operands (but keep sign).
+ __ sar(eax, kSmiTagSize);
+ __ sar(ecx, kSmiTagSize);
+ // Perform the operation.
+ switch (op_) {
+ case Token::SAR:
+ __ sar(eax);
+ // No checks of result necessary
+ break;
+ case Token::SHR:
+ __ shr(eax);
+ // Check that the *unsigned* result fits in a smi.
+ // Neither of the two high-order bits can be set:
+ // - 0x80000000: high bit would be lost when smi tagging.
+ // - 0x40000000: this number would convert to negative when
+ // Smi tagging these two cases can only happen with shifts
+ // by 0 or 1 when handed a valid smi.
+ __ test(eax, Immediate(0xc0000000));
+ __ j(not_zero, slow, not_taken);
+ break;
+ case Token::SHL:
+ __ shl(eax);
+ // Check that the *signed* result fits in a smi.
+ __ lea(ecx, Operand(eax, 0x40000000));
+ __ test(ecx, Immediate(0x80000000));
+ __ j(not_zero, slow, not_taken);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ // Tag the result and store it in register eax.
+ ASSERT(kSmiTagSize == times_2); // adjust code if not the case
+ __ lea(eax, Operand(eax, times_2, kSmiTag));
+ break;
+
+ default:
+ UNREACHABLE();
+ break;
+ }
+}
+
+
+void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
+ Label call_runtime;
+
+ if (flags_ == SMI_CODE_IN_STUB) {
+ // The fast case smi code wasn't inlined in the stub caller
+ // code. Generate it here to speed up common operations.
+ Label slow;
+ __ mov(ebx, Operand(esp, 1 * kPointerSize)); // get y
+ __ mov(eax, Operand(esp, 2 * kPointerSize)); // get x
+ GenerateSmiCode(masm, &slow);
+ __ ret(2 * kPointerSize); // remove both operands
+
+ // Too bad. The fast case smi code didn't succeed.
+ __ bind(&slow);
+ }
+
+ // Setup registers.
+ __ mov(eax, Operand(esp, 1 * kPointerSize)); // get y
+ __ mov(edx, Operand(esp, 2 * kPointerSize)); // get x
+
+ // Floating point case.
switch (op_) {
case Token::ADD:
case Token::SUB:
@@ -4276,7 +4279,8 @@
default: UNREACHABLE(); break;
}
- // 3. If all else fails, use the runtime system to get the correct result.
+ // If all else fails, use the runtime system to get the correct
+ // result.
__ bind(&call_runtime);
switch (op_) {
case Token::ADD:
@@ -4951,7 +4955,7 @@
#ifdef DEBUG
if (FLAG_gc_greedy) {
- Failure* failure = Failure::RetryAfterGC(0, NEW_SPACE);
+ Failure* failure = Failure::RetryAfterGC(0);
__ mov(Operand(eax), Immediate(reinterpret_cast<int32_t>(failure)));
}
GenerateCore(masm, &throw_normal_exception,
diff --git a/src/codegen-ia32.h b/src/codegen-ia32.h
index 21d5167..3f1bf22 100644
--- a/src/codegen-ia32.h
+++ b/src/codegen-ia32.h
@@ -271,6 +271,7 @@
bool force_cc);
void Load(Expression* x, TypeofState typeof_state = NOT_INSIDE_TYPEOF);
void LoadGlobal();
+ void LoadGlobalReceiver(Register scratch);
// Read a value from a slot and leave it on top of the expression stack.
void LoadFromSlot(Slot* slot, TypeofState typeof_state);
@@ -287,10 +288,11 @@
void GenericBinaryOperation(Token::Value op,
const OverwriteMode overwrite_mode = NO_OVERWRITE);
+
void Comparison(Condition cc, bool strict = false);
// Inline small integer literals. To prevent long attacker-controlled byte
- // sequences, we only inline small Smi:s.
+ // sequences, we only inline small Smis.
static const int kMaxSmiInlinedBits = 16;
bool IsInlineSmi(Literal* literal);
void SmiComparison(Condition cc, Handle<Object> value, bool strict = false);
@@ -367,24 +369,28 @@
// Allocate a jump table and create code to jump through it.
// Should call GenerateFastCaseSwitchCases to generate the code for
// all the cases at the appropriate point.
- void GenerateFastCaseSwitchJumpTable(SwitchStatement* node, int min_index,
- int range, Label *fail_label,
- SmartPointer<Label*> &case_targets,
- SmartPointer<Label>& case_labels);
+ void GenerateFastCaseSwitchJumpTable(SwitchStatement* node,
+ int min_index,
+ int range,
+ Label* fail_label,
+ Vector<Label*> case_targets,
+ Vector<Label> case_labels);
// Generate the code for cases for the fast case switch.
// Called by GenerateFastCaseSwitchJumpTable.
void GenerateFastCaseSwitchCases(SwitchStatement* node,
- SmartPointer<Label> &case_labels);
+ Vector<Label> case_labels);
// Fast support for constant-Smi switches.
- void GenerateFastCaseSwitchStatement(SwitchStatement *node, int min_index,
- int range, int default_index);
+ void GenerateFastCaseSwitchStatement(SwitchStatement* node,
+ int min_index,
+ int range,
+ int default_index);
// Fast support for constant-Smi switches. Tests whether switch statement
// permits optimization and calls GenerateFastCaseSwitch if it does.
// Returns true if the fast-case switch was generated, and false if not.
- bool TryGenerateFastCaseSwitchStatement(SwitchStatement *node);
+ bool TryGenerateFastCaseSwitchStatement(SwitchStatement* node);
// Bottle-neck interface to call the Assembler to generate the statement
diff --git a/src/codegen.cc b/src/codegen.cc
index 6c3a113..47f7842 100644
--- a/src/codegen.cc
+++ b/src/codegen.cc
@@ -345,9 +345,10 @@
}
-void CodeGenerator::GenerateFastCaseSwitchStatement(
- SwitchStatement *node, int min_index, int range, int default_index) {
-
+void CodeGenerator::GenerateFastCaseSwitchStatement(SwitchStatement* node,
+ int min_index,
+ int range,
+ int default_index) {
ZoneList<CaseClause*>* cases = node->cases();
int length = cases->length();
@@ -357,8 +358,8 @@
// Label per switch case
SmartPointer<Label> case_labels(NewArray<Label>(length));
- Label* fail_label = (default_index >= 0 ? &(case_labels[default_index])
- : node->break_target());
+ Label* fail_label = default_index >= 0 ? &(case_labels[default_index])
+ : node->break_target();
// Populate array of label pointers for each number in the range.
// Initally put the failure label everywhere.
@@ -378,18 +379,23 @@
}
}
- GenerateFastCaseSwitchJumpTable(node, min_index, range, fail_label,
- case_targets, case_labels);
+ GenerateFastCaseSwitchJumpTable(node,
+ min_index,
+ range,
+ fail_label,
+ Vector<Label*>(*case_targets, range),
+ Vector<Label>(*case_labels, length));
}
-void CodeGenerator::GenerateFastCaseSwitchCases(
- SwitchStatement* node, SmartPointer<Label> &case_labels) {
+void CodeGenerator::GenerateFastCaseSwitchCases(
+ SwitchStatement* node,
+ Vector<Label> case_labels) {
ZoneList<CaseClause*>* cases = node->cases();
int length = cases->length();
for (int i = 0; i < length; i++) {
- Comment cmnt(masm(), "[ case clause");
+ Comment cmnt(masm(), "[ Case clause");
masm()->bind(&(case_labels[i]));
VisitStatements(cases->at(i)->statements());
}
diff --git a/src/contexts.cc b/src/contexts.cc
index e987b45..81ad8d2 100644
--- a/src/contexts.cc
+++ b/src/contexts.cc
@@ -60,6 +60,15 @@
}
+JSObject* Context::global_proxy() {
+ return global_context()->global_proxy_object();
+}
+
+void Context::set_global_proxy(JSObject* object) {
+ global_context()->set_global_proxy_object(object);
+}
+
+
Handle<Object> Context::Lookup(Handle<String> name, ContextLookupFlags flags,
int* index_, PropertyAttributes* attributes) {
Handle<Context> context(this);
diff --git a/src/contexts.h b/src/contexts.h
index 00ad7b4..2e6c39a 100644
--- a/src/contexts.h
+++ b/src/contexts.h
@@ -56,6 +56,8 @@
// Array.prototype.{push,pop}.
#define GLOBAL_CONTEXT_FIELDS(V) \
+ V(GLOBAL_PROXY_INDEX, JSObject, global_proxy_object) \
+ V(SECURITY_TOKEN_INDEX, Object, security_token) \
V(BOOLEAN_FUNCTION_INDEX, JSFunction, boolean_function) \
V(NUMBER_FUNCTION_INDEX, JSFunction, number_function) \
V(STRING_FUNCTION_INDEX, JSFunction, string_function) \
@@ -172,7 +174,9 @@
MIN_CONTEXT_SLOTS,
// These slots are only in global contexts.
- ARGUMENTS_BOILERPLATE_INDEX = MIN_CONTEXT_SLOTS,
+ GLOBAL_PROXY_INDEX = MIN_CONTEXT_SLOTS,
+ SECURITY_TOKEN_INDEX,
+ ARGUMENTS_BOILERPLATE_INDEX,
JS_ARRAY_MAP_INDEX,
FUNCTION_MAP_INDEX,
FUNCTION_INSTANCE_MAP_INDEX,
@@ -236,6 +240,10 @@
}
void set_global(GlobalObject* global) { set(GLOBAL_INDEX, global); }
+ // Returns a JSGlobalProxy object or null.
+ JSObject* global_proxy();
+ void set_global_proxy(JSObject* global);
+
// The builtins object.
JSBuiltinsObject* builtins();
diff --git a/src/d8-readline.cc b/src/d8-readline.cc
new file mode 100644
index 0000000..4cfee8b
--- /dev/null
+++ b/src/d8-readline.cc
@@ -0,0 +1,120 @@
+// Copyright 2008 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.
+
+
+#include <readline/readline.h>
+#include <readline/history.h>
+
+
+#include "d8.h"
+
+
+namespace v8 {
+
+
+class ReadLineEditor: public LineEditor {
+ public:
+ ReadLineEditor() : LineEditor(LineEditor::READLINE, "readline") { }
+ virtual i::SmartPointer<char> Prompt(const char* prompt);
+ virtual bool Open();
+ virtual bool Close();
+ virtual void AddHistory(const char* str);
+ private:
+ static char** AttemptedCompletion(const char* text, int start, int end);
+ static char* CompletionGenerator(const char* text, int state);
+ static char kWordBreakCharacters[];
+};
+
+
+static ReadLineEditor read_line_editor;
+char ReadLineEditor::kWordBreakCharacters[] = {' ', '\t', '\n', '"',
+ '\\', '\'', '`', '@', '.', '>', '<', '=', ';', '|', '&', '{', '(',
+ '\0'};
+
+
+bool ReadLineEditor::Open() {
+ rl_initialize();
+ rl_attempted_completion_function = AttemptedCompletion;
+ rl_completer_word_break_characters = kWordBreakCharacters;
+ rl_bind_key('\t', rl_complete);
+ using_history();
+ return read_history(Shell::kHistoryFileName) == 0;
+}
+
+
+bool ReadLineEditor::Close() {
+ return write_history(Shell::kHistoryFileName) == 0;
+}
+
+
+i::SmartPointer<char> ReadLineEditor::Prompt(const char* prompt) {
+ char* result = readline(prompt);
+ return i::SmartPointer<char>(result);
+}
+
+
+void ReadLineEditor::AddHistory(const char* str) {
+ add_history(str);
+}
+
+
+char** ReadLineEditor::AttemptedCompletion(const char* text,
+ int start,
+ int end) {
+ char** result = rl_completion_matches(text, CompletionGenerator);
+ rl_attempted_completion_over = true;
+ return result;
+}
+
+
+char* ReadLineEditor::CompletionGenerator(const char* text, int state) {
+ static unsigned current_index;
+ static Persistent<Array> current_completions;
+ if (state == 0) {
+ i::SmartPointer<char> full_text(strndup(rl_line_buffer, rl_point));
+ HandleScope scope;
+ Handle<Array> completions =
+ Shell::GetCompletions(String::New(text), String::New(*full_text));
+ current_completions = Persistent<Array>::New(completions);
+ current_index = 0;
+ }
+ if (current_index < current_completions->Length()) {
+ HandleScope scope;
+ Handle<Integer> index = Integer::New(current_index);
+ Handle<Value> str_obj = current_completions->Get(index);
+ current_index++;
+ String::Utf8Value str(str_obj);
+ return strdup(*str);
+ } else {
+ current_completions.Dispose();
+ current_completions.Clear();
+ return NULL;
+ }
+}
+
+
+} // namespace v8
diff --git a/src/d8.cc b/src/d8.cc
new file mode 100644
index 0000000..af6eb4c
--- /dev/null
+++ b/src/d8.cc
@@ -0,0 +1,348 @@
+// Copyright 2008 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.
+
+
+#include "d8.h"
+#include "debug.h"
+#include "api.h"
+#include "natives.h"
+
+
+namespace v8 {
+
+
+const char* Shell::kHistoryFileName = ".d8_history";
+const char* Shell::kPrompt = "d8> ";
+
+
+LineEditor *LineEditor::first_ = NULL;
+
+
+LineEditor::LineEditor(Type type, const char* name)
+ : type_(type),
+ name_(name),
+ next_(first_) {
+ first_ = this;
+}
+
+
+LineEditor* LineEditor::Get() {
+ LineEditor* current = first_;
+ LineEditor* best = current;
+ while (current != NULL) {
+ if (current->type_ > best->type_)
+ best = current;
+ current = current->next_;
+ }
+ return best;
+}
+
+
+class DumbLineEditor: public LineEditor {
+ public:
+ DumbLineEditor() : LineEditor(LineEditor::DUMB, "dumb") { }
+ virtual i::SmartPointer<char> Prompt(const char* prompt);
+};
+
+
+static DumbLineEditor dumb_line_editor;
+
+
+i::SmartPointer<char> DumbLineEditor::Prompt(const char* prompt) {
+ static const int kBufferSize = 256;
+ char buffer[kBufferSize];
+ printf("%s", prompt);
+ char* str = fgets(buffer, kBufferSize, stdin);
+ return i::SmartPointer<char>(str ? i::OS::StrDup(str) : str);
+}
+
+
+Shell::CounterMap Shell::counter_map_;
+Persistent<Context> Shell::utility_context_;
+Persistent<Context> Shell::evaluation_context_;
+
+
+// Executes a string within the current v8 context.
+bool Shell::ExecuteString(Handle<String> source,
+ Handle<Value> name,
+ bool print_result,
+ bool report_exceptions) {
+ HandleScope handle_scope;
+ TryCatch try_catch;
+ Handle<Script> script = Script::Compile(source, name);
+ if (script.IsEmpty()) {
+ // Print errors that happened during compilation.
+ if (report_exceptions)
+ ReportException(&try_catch);
+ return false;
+ } else {
+ Handle<Value> result = script->Run();
+ if (result.IsEmpty()) {
+ // Print errors that happened during execution.
+ if (report_exceptions)
+ ReportException(&try_catch);
+ return false;
+ } else {
+ if (print_result && !result->IsUndefined()) {
+ // If all went well and the result wasn't undefined then print
+ // the returned value.
+ String::Utf8Value str(result);
+ printf("%s\n", *str);
+ }
+ return true;
+ }
+ }
+}
+
+
+Handle<Value> Shell::Print(const Arguments& args) {
+ bool first = true;
+ for (int i = 0; i < args.Length(); i++) {
+ HandleScope handle_scope;
+ if (first) {
+ first = false;
+ } else {
+ printf(" ");
+ }
+ String::Utf8Value str(args[i]);
+ printf("%s", *str);
+ }
+ printf("\n");
+ return Undefined();
+}
+
+
+Handle<Value> Shell::Load(const Arguments& args) {
+ for (int i = 0; i < args.Length(); i++) {
+ HandleScope handle_scope;
+ String::Utf8Value file(args[i]);
+ Handle<String> source = ReadFile(*file);
+ if (source.IsEmpty()) {
+ return ThrowException(String::New("Error loading file"));
+ }
+ if (!ExecuteString(source, String::New(*file), false, false)) {
+ return ThrowException(String::New("Error executing file"));
+ }
+ }
+ return Undefined();
+}
+
+
+Handle<Value> Shell::Quit(const Arguments& args) {
+ int exit_code = args[0]->Int32Value();
+ OnExit();
+ exit(exit_code);
+ return Undefined();
+}
+
+
+Handle<Value> Shell::Version(const Arguments& args) {
+ return String::New(V8::GetVersion());
+}
+
+
+void Shell::ReportException(v8::TryCatch* try_catch) {
+ HandleScope handle_scope;
+ String::Utf8Value exception(try_catch->Exception());
+ Handle<Message> message = try_catch->Message();
+ if (message.IsEmpty()) {
+ // V8 didn't provide any extra information about this error; just
+ // print the exception.
+ printf("%s\n", *exception);
+ } else {
+ // Print (filename):(line number): (message).
+ String::Utf8Value filename(message->GetScriptResourceName());
+ int linenum = message->GetLineNumber();
+ printf("%s:%i: %s\n", *filename, linenum, *exception);
+ // Print line of source code.
+ String::Utf8Value sourceline(message->GetSourceLine());
+ printf("%s\n", *sourceline);
+ // Print wavy underline (GetUnderline is deprecated).
+ int start = message->GetStartColumn();
+ for (int i = 0; i < start; i++) {
+ printf(" ");
+ }
+ int end = message->GetEndColumn();
+ for (int i = start; i < end; i++) {
+ printf("^");
+ }
+ printf("\n");
+ }
+}
+
+
+Handle<Array> Shell::GetCompletions(Handle<String> text, Handle<String> full) {
+ HandleScope handle_scope;
+ Context::Scope context_scope(utility_context_);
+ Handle<Object> global = utility_context_->Global();
+ Handle<Value> fun = global->Get(String::New("GetCompletions"));
+ static const int kArgc = 3;
+ Handle<Value> argv[kArgc] = { evaluation_context_->Global(), text, full };
+ Handle<Value> val = Handle<Function>::Cast(fun)->Call(global, kArgc, argv);
+ return handle_scope.Close(Handle<Array>::Cast(val));
+}
+
+
+int* Shell::LookupCounter(const wchar_t* name) {
+ CounterMap::iterator item = counter_map_.find(name);
+ if (item != counter_map_.end()) {
+ Counter* result = (*item).second;
+ return result->GetValuePtr();
+ }
+ Counter* result = new Counter(name);
+ counter_map_[name] = result;
+ return result->GetValuePtr();
+}
+
+
+void Shell::Initialize() {
+ // Set up counters
+ if (i::FLAG_dump_counters)
+ V8::SetCounterFunction(LookupCounter);
+ // Initialize the global objects
+ HandleScope scope;
+ Handle<ObjectTemplate> global_template = ObjectTemplate::New();
+ global_template->Set(String::New("print"), FunctionTemplate::New(Print));
+ global_template->Set(String::New("load"), FunctionTemplate::New(Load));
+ global_template->Set(String::New("quit"), FunctionTemplate::New(Quit));
+ global_template->Set(String::New("version"), FunctionTemplate::New(Version));
+
+ utility_context_ = Context::New(NULL, global_template);
+ utility_context_->SetSecurityToken(Undefined());
+ Context::Scope utility_scope(utility_context_);
+
+ // Install the debugger object in the utility scope
+ i::Debug::Load();
+ i::Debug::debug_context()->set_security_token(i::Heap::undefined_value());
+ i::JSObject* debug = i::Debug::debug_context()->global();
+ utility_context_->Global()->Set(String::New("$debug"),
+ Utils::ToLocal(&debug));
+
+ // Run the d8 shell utility script in the utility context
+ int source_index = i::NativesCollection<i::D8>::GetIndex("d8");
+ i::Vector<const char> shell_source
+ = i::NativesCollection<i::D8>::GetScriptSource(source_index);
+ i::Vector<const char> shell_source_name
+ = i::NativesCollection<i::D8>::GetScriptName(source_index);
+ Handle<String> source = String::New(shell_source.start(),
+ shell_source.length());
+ Handle<String> name = String::New(shell_source_name.start(),
+ shell_source_name.length());
+ Script::Compile(source, name)->Run();
+
+ // Create the evaluation context
+ evaluation_context_ = Context::New(NULL, global_template);
+ evaluation_context_->SetSecurityToken(Undefined());
+}
+
+
+void Shell::OnExit() {
+ if (i::FLAG_dump_counters) {
+ ::printf("+----------------------------------------+----------+\n");
+ ::printf("| Name | Value |\n");
+ ::printf("+----------------------------------------+----------+\n");
+ for (CounterMap::iterator i = counter_map_.begin();
+ i != counter_map_.end();
+ i++) {
+ Counter* counter = (*i).second;
+ ::printf("| %-38ls | %8i |\n", counter->name(), counter->value());
+ }
+ ::printf("+----------------------------------------+----------+\n");
+ }
+}
+
+
+// Reads a file into a v8 string.
+Handle<String> Shell::ReadFile(const char* name) {
+ FILE* file = i::OS::FOpen(name, "rb");
+ if (file == NULL) return Handle<String>();
+
+ fseek(file, 0, SEEK_END);
+ int size = ftell(file);
+ rewind(file);
+
+ char* chars = new char[size + 1];
+ chars[size] = '\0';
+ for (int i = 0; i < size;) {
+ int read = fread(&chars[i], 1, size - i, file);
+ i += read;
+ }
+ fclose(file);
+ Handle<String> result = String::New(chars, size);
+ delete[] chars;
+ return result;
+}
+
+
+void Shell::RunShell() {
+ LineEditor* editor = LineEditor::Get();
+ printf("V8 version %s [console: %s]\n", V8::GetVersion(), editor->name());
+ editor->Open();
+ while (true) {
+ HandleScope handle_scope;
+ i::SmartPointer<char> input = editor->Prompt(Shell::kPrompt);
+ if (input.is_empty())
+ break;
+ editor->AddHistory(*input);
+ Handle<String> name = String::New("(d8)");
+ ExecuteString(String::New(*input), name, true, true);
+ }
+ editor->Close();
+ printf("\n");
+}
+
+
+int Shell::Main(int argc, char* argv[]) {
+ i::FlagList::SetFlagsFromCommandLine(&argc, argv, true);
+ Initialize();
+ bool run_shell = (argc == 1);
+ Context::Scope context_scope(evaluation_context_);
+ for (int i = 1; i < argc; i++) {
+ char* str = argv[i];
+ HandleScope handle_scope;
+ Handle<String> file_name = v8::String::New(str);
+ Handle<String> source = ReadFile(str);
+ if (source.IsEmpty()) {
+ printf("Error reading '%s'\n", str);
+ return 1;
+ }
+ if (!ExecuteString(source, file_name, false, true))
+ return 1;
+ }
+ if (run_shell)
+ RunShell();
+ OnExit();
+ return 0;
+}
+
+
+} // namespace v8
+
+
+int main(int argc, char* argv[]) {
+ return v8::Shell::Main(argc, argv);
+}
diff --git a/src/d8.h b/src/d8.h
new file mode 100644
index 0000000..7e988c4
--- /dev/null
+++ b/src/d8.h
@@ -0,0 +1,113 @@
+// Copyright 2008 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.
+
+#ifndef V8_D8_H_
+#define V8_D8_H_
+
+
+// Disable exceptions on windows to not generate warnings from <map>.
+#define _HAS_EXCEPTIONS 0
+#include <map>
+
+#include "v8.h"
+
+
+namespace v8 {
+
+
+namespace i = v8::internal;
+
+
+class Counter {
+ public:
+ explicit Counter(const wchar_t* name)
+ : name_(name), value_(0) { }
+ int* GetValuePtr() { return &value_; }
+ const wchar_t* name() { return name_; }
+ int value() { return value_; }
+ private:
+ const wchar_t* name_;
+ int value_;
+};
+
+
+class Shell: public i::AllStatic {
+ public:
+ static bool ExecuteString(Handle<String> source,
+ Handle<Value> name,
+ bool print_result,
+ bool report_exceptions);
+ static void ReportException(TryCatch* try_catch);
+ static void Initialize();
+ static void OnExit();
+ static int* LookupCounter(const wchar_t* name);
+ static Handle<String> ReadFile(const char* name);
+ static void RunShell();
+ static int Main(int argc, char* argv[]);
+ static Handle<Array> GetCompletions(Handle<String> text,
+ Handle<String> full);
+
+ static Handle<Value> Print(const Arguments& args);
+ static Handle<Value> Quit(const Arguments& args);
+ static Handle<Value> Version(const Arguments& args);
+ static Handle<Value> Load(const Arguments& args);
+
+ static const char* kHistoryFileName;
+ static const char* kPrompt;
+ private:
+ static Persistent<Context> utility_context_;
+ static Persistent<Context> evaluation_context_;
+ typedef std::map<const wchar_t*, Counter*> CounterMap;
+ static CounterMap counter_map_;
+};
+
+
+class LineEditor {
+ public:
+ enum Type { DUMB = 0, READLINE = 1 };
+ LineEditor(Type type, const char* name);
+ virtual ~LineEditor() { }
+
+ virtual i::SmartPointer<char> Prompt(const char* prompt) = 0;
+ virtual bool Open() { return true; }
+ virtual bool Close() { return true; }
+ virtual void AddHistory(const char* str) { }
+
+ const char* name() { return name_; }
+ static LineEditor* Get();
+ private:
+ Type type_;
+ const char* name_;
+ LineEditor* next_;
+ static LineEditor* first_;
+};
+
+
+} // namespace v8
+
+
+#endif // V8_D8_H_
diff --git a/src/d8.js b/src/d8.js
new file mode 100644
index 0000000..cf8b60c
--- /dev/null
+++ b/src/d8.js
@@ -0,0 +1,70 @@
+// Copyright 2008 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.
+
+// How crappy is it that I have to implement completely basic stuff
+// like this myself? Answer: very.
+String.prototype.startsWith = function (str) {
+ if (str.length > this.length)
+ return false;
+ return this.substr(0, str.length) == str;
+};
+
+function ToInspectableObject(obj) {
+ if (!obj && typeof obj === 'object') {
+ return void 0;
+ } else {
+ return Object(obj);
+ }
+}
+
+function GetCompletions(global, last, full) {
+ var full_tokens = full.split();
+ full = full_tokens.pop();
+ var parts = full.split('.');
+ parts.pop();
+ var current = global;
+ for (var i = 0; i < parts.length; i++) {
+ var part = parts[i];
+ var next = current[part];
+ if (!next)
+ return [];
+ current = next;
+ }
+ var result = [];
+ current = ToInspectableObject(current);
+ while (typeof current !== 'undefined') {
+ var mirror = new $debug.ObjectMirror(current);
+ var properties = mirror.properties();
+ for (var i = 0; i < properties.length; i++) {
+ var name = properties[i].name();
+ if (typeof name === 'string' && name.startsWith(last))
+ result.push(name);
+ }
+ current = ToInspectableObject(current.__proto__);
+ }
+ return result;
+}
diff --git a/src/dateparser.cc b/src/dateparser.cc
index 69cf748..4b64785 100644
--- a/src/dateparser.cc
+++ b/src/dateparser.cc
@@ -149,9 +149,15 @@
if (!Smi::IsValid(year) || !IsMonth(month) || !IsDay(day)) return false;
- output->set(YEAR, Smi::FromInt(year));
- output->set(MONTH, Smi::FromInt(month - 1)); // 0-based
- output->set(DAY, Smi::FromInt(day));
+ output->set(YEAR,
+ Smi::FromInt(year),
+ SKIP_WRITE_BARRIER);
+ output->set(MONTH,
+ Smi::FromInt(month - 1),
+ SKIP_WRITE_BARRIER); // 0-based
+ output->set(DAY,
+ Smi::FromInt(day),
+ SKIP_WRITE_BARRIER);
return true;
}
@@ -174,9 +180,15 @@
if (!IsHour(hour) || !IsMinute(minute) || !IsSecond(second)) return false;
- output->set(HOUR, Smi::FromInt(hour));
- output->set(MINUTE, Smi::FromInt(minute));
- output->set(SECOND, Smi::FromInt(second));
+ output->set(HOUR,
+ Smi::FromInt(hour),
+ SKIP_WRITE_BARRIER);
+ output->set(MINUTE,
+ Smi::FromInt(minute),
+ SKIP_WRITE_BARRIER);
+ output->set(SECOND,
+ Smi::FromInt(second),
+ SKIP_WRITE_BARRIER);
return true;
}
@@ -187,9 +199,13 @@
if (minute_ == kNone) minute_ = 0;
int total_seconds = sign_ * (hour_ * 3600 + minute_ * 60);
if (!Smi::IsValid(total_seconds)) return false;
- output->set(UTC_OFFSET, Smi::FromInt(total_seconds));
+ output->set(UTC_OFFSET,
+ Smi::FromInt(total_seconds),
+ SKIP_WRITE_BARRIER);
} else {
- output->set(UTC_OFFSET, Heap::null_value());
+ output->set(UTC_OFFSET,
+ Heap::null_value(),
+ SKIP_WRITE_BARRIER);
}
return true;
}
diff --git a/src/debug.cc b/src/debug.cc
index acdf11d..33fafe7 100644
--- a/src/debug.cc
+++ b/src/debug.cc
@@ -451,7 +451,7 @@
Code* Debug::debug_break_return_ = NULL;
-void Debug::HandleWeakDebugInfo(v8::Persistent<v8::Object> obj, void* data) {
+void Debug::HandleWeakDebugInfo(v8::Persistent<v8::Value> obj, void* data) {
DebugInfoListNode* node = reinterpret_cast<DebugInfoListNode*>(data);
RemoveDebugInfo(node->debug_info());
#ifdef DEBUG
@@ -570,7 +570,6 @@
// Use the debugger context.
SaveContext save;
Top::set_context(*context);
- Top::set_security_context(*context);
// Expose the builtins object in the debugger context.
Handle<String> key = Factory::LookupAsciiSymbol("builtins");
diff --git a/src/debug.h b/src/debug.h
index 715ce1c..69e2aaa 100644
--- a/src/debug.h
+++ b/src/debug.h
@@ -248,7 +248,7 @@
static const int kEstimatedNofDebugInfoEntries = 16;
static const int kEstimatedNofBreakPointsInFunction = 16;
- static void HandleWeakDebugInfo(v8::Persistent<v8::Object> obj, void* data);
+ static void HandleWeakDebugInfo(v8::Persistent<v8::Value> obj, void* data);
friend class Debugger;
friend Handle<FixedArray> GetDebuggedFunctions(); // Found in test-debug.cc
@@ -494,7 +494,6 @@
// NOTE the member variable save which saves the previous context before
// this change.
Top::set_context(*Debug::debug_context());
- Top::set_security_context(*Debug::debug_context());
}
}
diff --git a/src/execution.cc b/src/execution.cc
index d10a3ab..0598ad7 100644
--- a/src/execution.cc
+++ b/src/execution.cc
@@ -94,6 +94,9 @@
// Update the pending exception flag and return the value.
*has_pending_exception = value->IsException();
ASSERT(*has_pending_exception == Top::has_pending_exception());
+ if (*has_pending_exception) {
+ Top::setup_external_caught();
+ }
// If the pending exception is OutOfMemoryException set out_of_memory in
// the global context. Note: We have to mark the global context here
diff --git a/src/factory.cc b/src/factory.cc
index 2eeb3b9..4e5f5da 100644
--- a/src/factory.cc
+++ b/src/factory.cc
@@ -79,6 +79,8 @@
Handle<String> Factory::NewConsString(Handle<String> first,
Handle<String> second) {
+ if (first->length() == 0) return second;
+ if (second->length() == 0) return first;
CALL_HEAP_FUNCTION(Heap::AllocateConsString(*first, *second), String);
}
@@ -312,7 +314,7 @@
Handle<JSFunction> fun =
Handle<JSFunction>(
JSFunction::cast(
- Top::security_context_builtins()->GetProperty(*make_str)));
+ Top::builtins()->GetProperty(*make_str)));
Handle<Object> type_obj = Factory::LookupAsciiSymbol(type);
Object** argv[2] = { type_obj.location(),
Handle<Object>::cast(args).location() };
@@ -321,7 +323,7 @@
// running the factory method, use the exception as the result.
bool caught_exception;
Handle<Object> result = Execution::TryCall(fun,
- Top::security_context_builtins(),
+ Top::builtins(),
2,
argv,
&caught_exception);
@@ -340,14 +342,14 @@
Handle<JSFunction> fun =
Handle<JSFunction>(
JSFunction::cast(
- Top::security_context_builtins()->GetProperty(*constr)));
+ Top::builtins()->GetProperty(*constr)));
Object** argv[1] = { Handle<Object>::cast(message).location() };
// Invoke the JavaScript factory method. If an exception is thrown while
// running the factory method, use the exception as the result.
bool caught_exception;
Handle<Object> result = Execution::TryCall(fun,
- Top::security_context_builtins(),
+ Top::builtins(),
1,
argv,
&caught_exception);
@@ -670,8 +672,7 @@
Handle<JSFunction> Factory::CreateApiFunction(
- Handle<FunctionTemplateInfo> obj,
- bool is_global) {
+ Handle<FunctionTemplateInfo> obj, ApiInstanceType instance_type) {
Handle<Code> code = Handle<Code>(Builtins::builtin(Builtins::HandleApiCall));
int internal_field_count = 0;
@@ -684,14 +685,25 @@
}
int instance_size = kPointerSize * internal_field_count;
- if (is_global) {
- instance_size += JSGlobalObject::kSize;
- } else {
- instance_size += JSObject::kHeaderSize;
+ InstanceType type = JS_OBJECT_TYPE; // initialize to a valid value
+ switch (instance_type) {
+ case JavaScriptObject:
+ type = JS_OBJECT_TYPE;
+ instance_size += JSObject::kHeaderSize;
+ break;
+ case InnerGlobalObject:
+ type = JS_GLOBAL_OBJECT_TYPE;
+ instance_size += JSGlobalObject::kSize;
+ break;
+ case OuterGlobalObject:
+ type = JS_GLOBAL_PROXY_TYPE;
+ instance_size += JSGlobalProxy::kSize;
+ break;
+ default:
+ ASSERT(false);
+ break;
}
- InstanceType type = is_global ? JS_GLOBAL_OBJECT_TYPE : JS_OBJECT_TYPE;
-
Handle<JSFunction> result =
Factory::NewFunction(Factory::empty_symbol(), type, instance_size,
code, true);
@@ -716,7 +728,7 @@
// Mark as needs_access_check if needed.
if (obj->needs_access_check()) {
- map->set_needs_access_check();
+ map->set_is_access_check_needed();
}
// Set interceptor information in the map.
diff --git a/src/factory.h b/src/factory.h
index 0d854c0..42de71c 100644
--- a/src/factory.h
+++ b/src/factory.h
@@ -265,8 +265,15 @@
Handle<Object> value,
PropertyAttributes attributes);
- static Handle<JSFunction> CreateApiFunction(Handle<FunctionTemplateInfo> data,
- bool is_global = false);
+ enum ApiInstanceType {
+ JavaScriptObject,
+ InnerGlobalObject,
+ OuterGlobalObject
+ };
+
+ static Handle<JSFunction> CreateApiFunction(
+ Handle<FunctionTemplateInfo> data,
+ ApiInstanceType type = JavaScriptObject);
static Handle<JSFunction> InstallMembers(Handle<JSFunction> function);
diff --git a/src/flag-definitions.h b/src/flag-definitions.h
index 6508c77..c4de404 100644
--- a/src/flag-definitions.h
+++ b/src/flag-definitions.h
@@ -191,7 +191,11 @@
"file in which to serialize heap")
#endif
+//
+// Dev shell flags
+//
+DEFINE_bool(dump_counters, false, "Dump counters on exit")
//
// Debug only flags
@@ -302,7 +306,6 @@
// codegen-ia32.cc / codegen-arm.cc
DEFINE_bool(print_code, false, "print generated code")
-
// Cleanup...
#undef FLAG_FULL
#undef FLAG_READONLY
diff --git a/src/globals.h b/src/globals.h
index 34baa0d..cc0dbde 100644
--- a/src/globals.h
+++ b/src/globals.h
@@ -73,6 +73,9 @@
typedef uint16_t uc16;
typedef signed int uc32;
+#ifndef ARM
+#define CAN_READ_UNALIGNED 1
+#endif
// -----------------------------------------------------------------------------
// Constants
@@ -375,7 +378,6 @@
#define OBJECT_SIZE_ALIGN(value) \
((value + kObjectAlignmentMask) & ~kObjectAlignmentMask)
-
// The expression OFFSET_OF(type, field) computes the byte-offset
// of the specified field relative to the containing type. This
// corresponds to 'offsetof' (in stddef.h), except that it doesn't
diff --git a/src/handles.cc b/src/handles.cc
index 5f86792..1a600db 100644
--- a/src/handles.cc
+++ b/src/handles.cc
@@ -62,11 +62,11 @@
}
-Handle<JSGlobalObject> ReinitializeJSGlobalObject(
+Handle<JSGlobalProxy> ReinitializeJSGlobalProxy(
Handle<JSFunction> constructor,
- Handle<JSGlobalObject> global) {
- CALL_HEAP_FUNCTION(Heap::ReinitializeJSGlobalObject(*constructor, *global),
- JSGlobalObject);
+ Handle<JSGlobalProxy> global) {
+ CALL_HEAP_FUNCTION(Heap::ReinitializeJSGlobalProxy(*constructor, *global),
+ JSGlobalProxy);
}
@@ -237,8 +237,8 @@
}
-Handle<JSObject> Copy(Handle<JSObject> obj, PretenureFlag pretenure) {
- CALL_HEAP_FUNCTION(obj->Copy(pretenure), JSObject);
+Handle<JSObject> Copy(Handle<JSObject> obj) {
+ CALL_HEAP_FUNCTION(Heap::CopyJSObject(*obj), JSObject);
}
@@ -248,7 +248,7 @@
// collector will call the weak callback on the global handle
// associated with the wrapper and get rid of both the wrapper and the
// handle.
-static void ClearWrapperCache(Persistent<v8::Object> handle, void*) {
+static void ClearWrapperCache(Persistent<v8::Value> handle, void*) {
Handle<Object> cache = Utils::OpenHandle(*handle);
JSValue* wrapper = JSValue::cast(*cache);
Proxy* proxy = Script::cast(wrapper->value())->wrapper();
@@ -485,7 +485,6 @@
ASSERT(index >= 0);
Handle<Context> compile_context(Context::cast(info->get(1)));
Handle<Context> function_context(Context::cast(info->get(2)));
- Handle<Context> security_context(Context::cast(info->get(3)));
Handle<Object> receiver(compile_context->global()->builtins());
Vector<const char> name = Natives::GetScriptName(index);
@@ -520,7 +519,6 @@
if (!Debug::debug_context().is_null() &&
Top::context() == *Debug::debug_context()) {
Top::set_context(*compile_context);
- Top::set_security_context(*security_context);
}
// Reset the lazy load data before running the script to make sure
@@ -540,13 +538,11 @@
void SetupLazy(Handle<JSFunction> fun,
int index,
Handle<Context> compile_context,
- Handle<Context> function_context,
- Handle<Context> security_context) {
- Handle<FixedArray> arr = Factory::NewFixedArray(4);
+ Handle<Context> function_context) {
+ Handle<FixedArray> arr = Factory::NewFixedArray(3);
arr->set(0, Smi::FromInt(index));
arr->set(1, *compile_context); // Compile in this context
arr->set(2, *function_context); // Set function context to this
- arr->set(3, *security_context); // Receiver for call
fun->shared()->set_lazy_load_data(*arr);
}
diff --git a/src/handles.h b/src/handles.h
index 75f219d..a9adf3d 100644
--- a/src/handles.h
+++ b/src/handles.h
@@ -144,7 +144,7 @@
Handle<Object> LookupSingleCharacterStringFromCode(uint32_t index);
-Handle<JSObject> Copy(Handle<JSObject> obj, PretenureFlag = NOT_TENURED);
+Handle<JSObject> Copy(Handle<JSObject> obj);
// Get the JS object corresponding to the given script; create it
// if none exists.
@@ -183,9 +183,9 @@
int estimate);
-Handle<JSGlobalObject> ReinitializeJSGlobalObject(
+Handle<JSGlobalProxy> ReinitializeJSGlobalProxy(
Handle<JSFunction> constructor,
- Handle<JSGlobalObject> global);
+ Handle<JSGlobalProxy> global);
Handle<Object> SetPrototype(Handle<JSFunction> function,
Handle<Object> prototype);
@@ -202,8 +202,7 @@
void SetupLazy(Handle<JSFunction> fun,
int index,
Handle<Context> compile_context,
- Handle<Context> function_context,
- Handle<Context> security_context);
+ Handle<Context> function_context);
void LoadLazy(Handle<JSFunction> fun, bool* pending_exception);
class NoHandleAllocation BASE_EMBEDDED {
diff --git a/src/heap-inl.h b/src/heap-inl.h
index c841464..bfa2b65 100644
--- a/src/heap-inl.h
+++ b/src/heap-inl.h
@@ -51,7 +51,7 @@
Counters::objs_since_last_young.Increment();
#endif
if (NEW_SPACE == space) {
- return new_space_->AllocateRaw(size_in_bytes);
+ return new_space_.AllocateRaw(size_in_bytes);
}
Object* result;
@@ -100,17 +100,17 @@
bool Heap::InNewSpace(Object* object) {
- return new_space_->Contains(object);
+ return new_space_.Contains(object);
}
bool Heap::InFromSpace(Object* object) {
- return new_space_->FromSpaceContains(object);
+ return new_space_.FromSpaceContains(object);
}
bool Heap::InToSpace(Object* object) {
- return new_space_->ToSpaceContains(object);
+ return new_space_.ToSpaceContains(object);
}
@@ -118,14 +118,14 @@
// An object should be promoted if:
// - the object has survived a scavenge operation or
// - to space is already 25% full.
- return old_address < new_space_->age_mark()
- || (new_space_->Size() + object_size) >= (new_space_->Capacity() >> 2);
+ return old_address < new_space_.age_mark()
+ || (new_space_.Size() + object_size) >= (new_space_.Capacity() >> 2);
}
void Heap::RecordWrite(Address address, int offset) {
- if (new_space_->Contains(address)) return;
- ASSERT(!new_space_->FromSpaceContains(address));
+ if (new_space_.Contains(address)) return;
+ ASSERT(!new_space_.FromSpaceContains(address));
SLOW_ASSERT(Contains(address + offset));
Page::SetRSet(address, offset);
}
@@ -146,11 +146,47 @@
}
-#define GC_GREEDY_CHECK() \
- ASSERT(!FLAG_gc_greedy \
- || v8::internal::Heap::disallow_allocation_failure() \
- || v8::internal::Heap::CollectGarbage(0, NEW_SPACE))
+void Heap::CopyBlock(Object** dst, Object** src, int byte_size) {
+ ASSERT(IsAligned(byte_size, kPointerSize));
+ // Use block copying memcpy if the segment we're copying is
+ // enough to justify the extra call/setup overhead.
+ static const int kBlockCopyLimit = 16 * kPointerSize;
+
+ if (byte_size >= kBlockCopyLimit) {
+ memcpy(dst, src, byte_size);
+ } else {
+ int remaining = byte_size / kPointerSize;
+ do {
+ remaining--;
+ *dst++ = *src++;
+ } while (remaining > 0);
+ }
+}
+
+
+Object* Heap::GetKeyedLookupCache() {
+ if (keyed_lookup_cache()->IsUndefined()) {
+ Object* obj = LookupCache::Allocate(4);
+ if (obj->IsFailure()) return obj;
+ keyed_lookup_cache_ = obj;
+ }
+ return keyed_lookup_cache();
+}
+
+
+void Heap::SetKeyedLookupCache(LookupCache* cache) {
+ keyed_lookup_cache_ = cache;
+}
+
+
+void Heap::ClearKeyedLookupCache() {
+ keyed_lookup_cache_ = undefined_value();
+}
+
+
+#define GC_GREEDY_CHECK() \
+ ASSERT(!FLAG_gc_greedy || v8::internal::Heap::GarbageCollectionGreedyCheck())
// Do not use the identifier __object__ in a call to this macro.
//
diff --git a/src/heap.cc b/src/heap.cc
index 7ebc96c..00cf78b 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -57,8 +57,7 @@
SYMBOL_LIST(SYMBOL_ALLOCATION)
#undef SYMBOL_ALLOCATION
-
-NewSpace* Heap::new_space_ = NULL;
+NewSpace Heap::new_space_;
OldSpace* Heap::old_pointer_space_ = NULL;
OldSpace* Heap::old_data_space_ = NULL;
OldSpace* Heap::code_space_ = NULL;
@@ -73,7 +72,7 @@
// semispace_size_ should be a power of 2 and old_generation_size_ should be
// a multiple of Page::kPageSize.
-int Heap::semispace_size_ = 1*MB;
+int Heap::semispace_size_ = 2*MB;
int Heap::old_generation_size_ = 512*MB;
int Heap::initial_semispace_size_ = 256*KB;
@@ -103,7 +102,7 @@
int Heap::Capacity() {
if (!HasBeenSetup()) return 0;
- return new_space_->Capacity() +
+ return new_space_.Capacity() +
old_pointer_space_->Capacity() +
old_data_space_->Capacity() +
code_space_->Capacity() +
@@ -114,7 +113,7 @@
int Heap::Available() {
if (!HasBeenSetup()) return 0;
- return new_space_->Available() +
+ return new_space_.Available() +
old_pointer_space_->Available() +
old_data_space_->Available() +
code_space_->Available() +
@@ -123,8 +122,7 @@
bool Heap::HasBeenSetup() {
- return new_space_ != NULL &&
- old_pointer_space_ != NULL &&
+ return old_pointer_space_ != NULL &&
old_data_space_ != NULL &&
code_space_ != NULL &&
map_space_ != NULL &&
@@ -161,7 +159,7 @@
// and does not count available bytes already in the old space or code
// space. Undercounting is safe---we may get an unrequested full GC when
// a scavenge would have succeeded.
- if (MemoryAllocator::MaxAvailable() <= new_space_->Size()) {
+ if (MemoryAllocator::MaxAvailable() <= new_space_.Size()) {
Counters::gc_compactor_caused_by_oldspace_exhaustion.Increment();
return MARK_COMPACTOR;
}
@@ -179,24 +177,24 @@
// compiled with ENABLE_LOGGING_AND_PROFILING and --log-gc is set. The
// following logic is used to avoid double logging.
#if defined(DEBUG) && defined(ENABLE_LOGGING_AND_PROFILING)
- if (FLAG_heap_stats || FLAG_log_gc) new_space_->CollectStatistics();
+ if (FLAG_heap_stats || FLAG_log_gc) new_space_.CollectStatistics();
if (FLAG_heap_stats) {
ReportHeapStatistics("Before GC");
} else if (FLAG_log_gc) {
- new_space_->ReportStatistics();
+ new_space_.ReportStatistics();
}
- if (FLAG_heap_stats || FLAG_log_gc) new_space_->ClearHistograms();
+ if (FLAG_heap_stats || FLAG_log_gc) new_space_.ClearHistograms();
#elif defined(DEBUG)
if (FLAG_heap_stats) {
- new_space_->CollectStatistics();
+ new_space_.CollectStatistics();
ReportHeapStatistics("Before GC");
- new_space_->ClearHistograms();
+ new_space_.ClearHistograms();
}
#elif defined(ENABLE_LOGGING_AND_PROFILING)
if (FLAG_log_gc) {
- new_space_->CollectStatistics();
- new_space_->ReportStatistics();
- new_space_->ClearHistograms();
+ new_space_.CollectStatistics();
+ new_space_.ReportStatistics();
+ new_space_.ClearHistograms();
}
#endif
}
@@ -211,12 +209,12 @@
if (FLAG_heap_stats) {
ReportHeapStatistics("After GC");
} else if (FLAG_log_gc) {
- new_space_->ReportStatistics();
+ new_space_.ReportStatistics();
}
#elif defined(DEBUG)
if (FLAG_heap_stats) ReportHeapStatistics("After GC");
#elif defined(ENABLE_LOGGING_AND_PROFILING)
- if (FLAG_log_gc) new_space_->ReportStatistics();
+ if (FLAG_log_gc) new_space_.ReportStatistics();
#endif
}
#endif // defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING)
@@ -329,7 +327,7 @@
switch (space) {
case NEW_SPACE:
- return new_space_->Available() >= requested_size;
+ return new_space_.Available() >= requested_size;
case OLD_POINTER_SPACE:
return old_pointer_space_->Available() >= requested_size;
case OLD_DATA_SPACE:
@@ -423,6 +421,7 @@
void Heap::MarkCompactPrologue() {
+ ClearKeyedLookupCache();
CompilationCache::MarkCompactPrologue();
RegExpImpl::OldSpaceCollectionPrologue();
Top::MarkCompactPrologue();
@@ -447,27 +446,27 @@
// Helper class for copying HeapObjects
-class CopyVisitor: public ObjectVisitor {
+class ScavengeVisitor: public ObjectVisitor {
public:
- void VisitPointer(Object** p) {
- CopyObject(p);
- }
+ void VisitPointer(Object** p) { ScavengePointer(p); }
void VisitPointers(Object** start, Object** end) {
// Copy all HeapObject pointers in [start, end)
- for (Object** p = start; p < end; p++) CopyObject(p);
+ for (Object** p = start; p < end; p++) ScavengePointer(p);
}
private:
- void CopyObject(Object** p) {
- if (!Heap::InFromSpace(*p)) return;
- Heap::CopyObject(reinterpret_cast<HeapObject**>(p));
+ void ScavengePointer(Object** p) {
+ Object* object = *p;
+ if (!Heap::InNewSpace(object)) return;
+ Heap::ScavengeObject(reinterpret_cast<HeapObject**>(p),
+ reinterpret_cast<HeapObject*>(object));
}
};
-// Shared state read by the scavenge collector and set by CopyObject.
+// Shared state read by the scavenge collector and set by ScavengeObject.
static Address promoted_top = NULL;
@@ -510,21 +509,21 @@
LOG(ResourceEvent("scavenge", "begin"));
scavenge_count_++;
- if (new_space_->Capacity() < new_space_->MaximumCapacity() &&
+ if (new_space_.Capacity() < new_space_.MaximumCapacity() &&
scavenge_count_ > new_space_growth_limit_) {
// Double the size of the new space, and double the limit. The next
// doubling attempt will occur after the current new_space_growth_limit_
// more collections.
// TODO(1240712): NewSpace::Double has a return value which is
// ignored here.
- new_space_->Double();
+ new_space_.Double();
new_space_growth_limit_ *= 2;
}
// Flip the semispaces. After flipping, to space is empty, from space has
// live objects.
- new_space_->Flip();
- new_space_->ResetAllocationInfo();
+ new_space_.Flip();
+ new_space_.ResetAllocationInfo();
// We need to sweep newly copied objects which can be in either the to space
// or the old space. For to space objects, we use a mark. Newly copied
@@ -540,34 +539,34 @@
// in size. Using the new space to record promoted addresses makes the
// scavenge collector agnostic to the allocation strategy (eg, linear or
// free-list) used in old space.
- Address new_mark = new_space_->ToSpaceLow();
- Address promoted_mark = new_space_->ToSpaceHigh();
- promoted_top = new_space_->ToSpaceHigh();
+ Address new_mark = new_space_.ToSpaceLow();
+ Address promoted_mark = new_space_.ToSpaceHigh();
+ promoted_top = new_space_.ToSpaceHigh();
- CopyVisitor copy_visitor;
+ ScavengeVisitor scavenge_visitor;
// Copy roots.
- IterateRoots(©_visitor);
+ IterateRoots(&scavenge_visitor);
// Copy objects reachable from the old generation. By definition, there
// are no intergenerational pointers in code or data spaces.
- IterateRSet(old_pointer_space_, &CopyObject);
- IterateRSet(map_space_, &CopyObject);
- lo_space_->IterateRSet(&CopyObject);
+ IterateRSet(old_pointer_space_, &ScavengePointer);
+ IterateRSet(map_space_, &ScavengePointer);
+ lo_space_->IterateRSet(&ScavengePointer);
bool has_processed_weak_pointers = false;
while (true) {
- ASSERT(new_mark <= new_space_->top());
+ ASSERT(new_mark <= new_space_.top());
ASSERT(promoted_mark >= promoted_top);
// Copy objects reachable from newly copied objects.
- while (new_mark < new_space_->top() || promoted_mark > promoted_top) {
+ while (new_mark < new_space_.top() || promoted_mark > promoted_top) {
// Sweep newly copied objects in the to space. The allocation pointer
// can change during sweeping.
- Address previous_top = new_space_->top();
- SemiSpaceIterator new_it(new_space_, new_mark);
+ Address previous_top = new_space_.top();
+ SemiSpaceIterator new_it(new_space(), new_mark);
while (new_it.has_next()) {
- new_it.next()->Iterate(©_visitor);
+ new_it.next()->Iterate(&scavenge_visitor);
}
new_mark = previous_top;
@@ -578,7 +577,7 @@
current >= previous_top;
current -= kPointerSize) {
HeapObject* object = HeapObject::cast(Memory::Object_at(current));
- object->Iterate(©_visitor);
+ object->Iterate(&scavenge_visitor);
UpdateRSet(object);
}
promoted_mark = previous_top;
@@ -586,12 +585,12 @@
if (has_processed_weak_pointers) break; // We are done.
// Copy objects reachable from weak pointers.
- GlobalHandles::IterateWeakRoots(©_visitor);
+ GlobalHandles::IterateWeakRoots(&scavenge_visitor);
has_processed_weak_pointers = true;
}
// Set age mark.
- new_space_->set_age_mark(new_mark);
+ new_space_.set_age_mark(new_mark);
LOG(ResourceEvent("scavenge", "end"));
@@ -718,38 +717,27 @@
should_record = should_record || FLAG_log_gc;
#endif
if (should_record) {
- if (new_space_->Contains(obj)) {
- new_space_->RecordAllocation(obj);
+ if (new_space_.Contains(obj)) {
+ new_space_.RecordAllocation(obj);
} else {
- new_space_->RecordPromotion(obj);
+ new_space_.RecordPromotion(obj);
}
}
}
#endif // defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING)
-HeapObject* Heap::MigrateObject(HeapObject** source_p,
+
+HeapObject* Heap::MigrateObject(HeapObject* source,
HeapObject* target,
int size) {
- void** src = reinterpret_cast<void**>((*source_p)->address());
- void** dst = reinterpret_cast<void**>(target->address());
-
- // Use block copying memcpy if the object we're migrating is big
- // enough to justify the extra call/setup overhead.
- static const int kBlockCopyLimit = 16 * kPointerSize;
-
- if (size >= kBlockCopyLimit) {
- memcpy(dst, src, size);
- } else {
- int remaining = size / kPointerSize;
- do {
- remaining--;
- *dst++ = *src++;
- } while (remaining > 0);
- }
+ // Copy the content of source to target.
+ CopyBlock(reinterpret_cast<Object**>(target->address()),
+ reinterpret_cast<Object**>(source->address()),
+ size);
// Set the forwarding address.
- (*source_p)->set_map_word(MapWord::FromForwardingAddress(target));
+ source->set_map_word(MapWord::FromForwardingAddress(target));
// Update NewSpace stats if necessary.
#if defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING)
@@ -760,10 +748,9 @@
}
-void Heap::CopyObject(HeapObject** p) {
- ASSERT(InFromSpace(*p));
-
- HeapObject* object = *p;
+// Inlined function.
+void Heap::ScavengeObject(HeapObject** p, HeapObject* object) {
+ ASSERT(InFromSpace(object));
// We use the first word (where the map pointer usually is) of a heap
// object to record the forwarding pointer. A forwarding pointer can
@@ -778,37 +765,48 @@
return;
}
- // Optimization: Bypass ConsString objects where the right-hand side is
- // Heap::empty_string(). We do not use object->IsConsString because we
- // already know that object has the heap object tag.
- InstanceType type = first_word.ToMap()->instance_type();
- if (type < FIRST_NONSTRING_TYPE &&
- String::cast(object)->representation_tag() == kConsStringTag &&
- ConsString::cast(object)->second() == Heap::empty_string()) {
+ // Call the slow part of scavenge object.
+ return ScavengeObjectSlow(p, object);
+}
+
+static inline bool IsShortcutCandidate(HeapObject* object, Map* map) {
+ // A ConString object with Heap::empty_string() as the right side
+ // is a candidate for being shortcut by the scavenger.
+ ASSERT(object->map() == map);
+ return (map->instance_type() < FIRST_NONSTRING_TYPE) &&
+ (String::cast(object)->map_representation_tag(map) == kConsStringTag) &&
+ (ConsString::cast(object)->second() == Heap::empty_string());
+}
+
+
+void Heap::ScavengeObjectSlow(HeapObject** p, HeapObject* object) {
+ ASSERT(InFromSpace(object));
+ MapWord first_word = object->map_word();
+ ASSERT(!first_word.IsForwardingAddress());
+
+ // Optimization: Bypass flattened ConsString objects.
+ if (IsShortcutCandidate(object, first_word.ToMap())) {
object = HeapObject::cast(ConsString::cast(object)->first());
*p = object;
// After patching *p we have to repeat the checks that object is in the
// active semispace of the young generation and not already copied.
- if (!InFromSpace(object)) return;
+ if (!InNewSpace(object)) return;
first_word = object->map_word();
if (first_word.IsForwardingAddress()) {
*p = first_word.ToForwardingAddress();
return;
}
- type = first_word.ToMap()->instance_type();
}
int object_size = object->SizeFromMap(first_word.ToMap());
- Object* result;
// If the object should be promoted, we try to copy it to old space.
if (ShouldBePromoted(object->address(), object_size)) {
OldSpace* target_space = Heap::TargetSpace(object);
ASSERT(target_space == Heap::old_pointer_space_ ||
target_space == Heap::old_data_space_);
- result = target_space->AllocateRaw(object_size);
-
+ Object* result = target_space->AllocateRaw(object_size);
if (!result->IsFailure()) {
- *p = MigrateObject(p, HeapObject::cast(result), object_size);
+ *p = MigrateObject(object, HeapObject::cast(result), object_size);
if (target_space == Heap::old_pointer_space_) {
// Record the object's address at the top of the to space, to allow
// it to be swept by the scavenger.
@@ -827,10 +825,15 @@
}
// The object should remain in new space or the old space allocation failed.
- result = new_space_->AllocateRaw(object_size);
+ Object* result = new_space_.AllocateRaw(object_size);
// Failed allocation at this point is utterly unexpected.
ASSERT(!result->IsFailure());
- *p = MigrateObject(p, HeapObject::cast(result), object_size);
+ *p = MigrateObject(object, HeapObject::cast(result), object_size);
+}
+
+
+void Heap::ScavengePointer(HeapObject** p) {
+ ScavengeObject(p, *p);
}
@@ -1030,7 +1033,7 @@
// allocation in new space.
STATIC_ASSERT(HeapNumber::kSize <= Page::kMaxHeapObjectSize);
ASSERT(allocation_allowed_ && gc_state_ == NOT_IN_GC);
- Object* result = new_space_->AllocateRaw(HeapNumber::kSize);
+ Object* result = new_space_.AllocateRaw(HeapNumber::kSize);
if (result->IsFailure()) return result;
HeapObject::cast(result)->set_map(heap_number_map());
HeapNumber::cast(result)->set_value(value);
@@ -1193,6 +1196,9 @@
if (obj->IsFailure()) return false;
natives_source_cache_ = FixedArray::cast(obj);
+ // Initialize keyed lookup cache.
+ ClearKeyedLookupCache();
+
// Initialize compilation cache.
CompilationCache::Clear();
@@ -1236,7 +1242,7 @@
int hash;
if (number->IsSmi()) {
hash = smi_get_hash(Smi::cast(number));
- number_string_cache_->set(hash * 2, number, FixedArray::SKIP_WRITE_BARRIER);
+ number_string_cache_->set(hash * 2, number, SKIP_WRITE_BARRIER);
} else {
hash = double_get_hash(number->Number());
number_string_cache_->set(hash * 2, number);
@@ -1329,29 +1335,33 @@
Object* Heap::AllocateConsString(String* first, String* second) {
- int length = first->length() + second->length();
+ int first_length = first->length();
+ int second_length = second->length();
+ int length = first_length + second_length;
bool is_ascii = first->is_ascii_representation()
&& second->is_ascii_representation();
// If the resulting string is small make a flat string.
- if (length < ConsString::kMinLength) {
- Object* result = is_ascii
- ? AllocateRawAsciiString(length)
- : AllocateRawTwoByteString(length);
- if (result->IsFailure()) return result;
- // Copy the characters into the new object.
- String* string_result = String::cast(result);
- int first_length = first->length();
- // Copy the content of the first string.
- for (int i = 0; i < first_length; i++) {
- string_result->Set(i, first->Get(i));
+ if (length < String::kMinNonFlatLength) {
+ ASSERT(first->IsFlat());
+ ASSERT(second->IsFlat());
+ if (is_ascii) {
+ Object* result = AllocateRawAsciiString(length);
+ if (result->IsFailure()) return result;
+ // Copy the characters into the new object.
+ char* dest = SeqAsciiString::cast(result)->GetChars();
+ String::WriteToFlat(first, dest, 0, first_length);
+ String::WriteToFlat(second, dest + first_length, 0, second_length);
+ return result;
+ } else {
+ Object* result = AllocateRawTwoByteString(length);
+ if (result->IsFailure()) return result;
+ // Copy the characters into the new object.
+ uc16* dest = SeqTwoByteString::cast(result)->GetChars();
+ String::WriteToFlat(first, dest, 0, first_length);
+ String::WriteToFlat(second, dest + first_length, 0, second_length);
+ return result;
}
- int second_length = second->length();
- // Copy the content of the first string.
- for (int i = 0; i < second_length; i++) {
- string_result->Set(first_length + i, second->Get(i));
- }
- return result;
}
Map* map;
@@ -1382,7 +1392,7 @@
int length = end - start;
// If the resulting string is small make a sub string.
- if (end - start <= SlicedString::kMinLength) {
+ if (end - start <= String::kMinNonFlatLength) {
return Heap::AllocateSubString(buffer, start, end);
}
@@ -1490,16 +1500,20 @@
}
-Object* Heap:: LookupSingleCharacterStringFromCode(uint16_t code) {
+Object* Heap::LookupSingleCharacterStringFromCode(uint16_t code) {
if (code <= String::kMaxAsciiCharCode) {
Object* value = Heap::single_character_string_cache()->get(code);
if (value != Heap::undefined_value()) return value;
- Object* result = Heap::AllocateRawAsciiString(1);
+
+ char buffer[1];
+ buffer[0] = static_cast<char>(code);
+ Object* result = LookupSymbol(Vector<const char>(buffer, 1));
+
if (result->IsFailure()) return result;
- String::cast(result)->Set(0, code);
Heap::single_character_string_cache()->set(code, result);
return result;
}
+
Object* result = Heap::AllocateRawTwoByteString(1);
if (result->IsFailure()) return result;
String::cast(result)->Set(0, code);
@@ -1572,8 +1586,9 @@
// Copy code object.
Address old_addr = code->address();
Address new_addr = reinterpret_cast<HeapObject*>(result)->address();
- memcpy(new_addr, old_addr, obj_size);
-
+ CopyBlock(reinterpret_cast<Object**>(new_addr),
+ reinterpret_cast<Object**>(old_addr),
+ obj_size);
// Relocate the copy.
Code* new_code = Code::cast(result);
new_code->Relocate(new_addr - old_addr);
@@ -1640,17 +1655,34 @@
JSObject* boilerplate =
Top::context()->global_context()->arguments_boilerplate();
- Object* result = boilerplate->Copy();
+
+ // Make the clone.
+ Map* map = boilerplate->map();
+ int object_size = map->instance_size();
+ Object* result = new_space_.AllocateRaw(object_size);
if (result->IsFailure()) return result;
+ ASSERT(Heap::InNewSpace(result));
- Object* obj = JSObject::cast(result)->properties();
- FixedArray::cast(obj)->set(arguments_callee_index, callee);
- FixedArray::cast(obj)->set(arguments_length_index, Smi::FromInt(length));
+ // Copy the content.
+ CopyBlock(reinterpret_cast<Object**>(HeapObject::cast(result)->address()),
+ reinterpret_cast<Object**>(boilerplate->address()),
+ object_size);
- // Allocate the fixed array.
- obj = Heap::AllocateFixedArray(length);
- if (obj->IsFailure()) return obj;
- JSObject::cast(result)->set_elements(FixedArray::cast(obj));
+ // Set the two properties.
+ JSObject::cast(result)->InObjectPropertyAtPut(arguments_callee_index,
+ callee,
+ SKIP_WRITE_BARRIER);
+ JSObject::cast(result)->InObjectPropertyAtPut(arguments_length_index,
+ Smi::FromInt(length),
+ SKIP_WRITE_BARRIER);
+
+ // Allocate the elements if needed.
+ if (length > 0) {
+ // Allocate the fixed array.
+ Object* obj = Heap::AllocateFixedArray(length);
+ if (obj->IsFailure()) return obj;
+ JSObject::cast(result)->set_elements(FixedArray::cast(obj));
+ }
// Check the state of the object
ASSERT(JSObject::cast(result)->HasFastProperties());
@@ -1747,8 +1779,44 @@
}
-Object* Heap::ReinitializeJSGlobalObject(JSFunction* constructor,
- JSGlobalObject* object) {
+Object* Heap::CopyJSObject(JSObject* source) {
+ // Never used to copy functions. If functions need to be copied we
+ // have to be careful to clear the literals array.
+ ASSERT(!source->IsJSFunction());
+
+ // Make the clone.
+ Map* map = source->map();
+ int object_size = map->instance_size();
+ Object* clone = new_space_.AllocateRaw(object_size);
+ if (clone->IsFailure()) return clone;
+ ASSERT(Heap::InNewSpace(clone));
+
+ // Copy the content.
+ CopyBlock(reinterpret_cast<Object**>(HeapObject::cast(clone)->address()),
+ reinterpret_cast<Object**>(source->address()),
+ object_size);
+
+ FixedArray* elements = FixedArray::cast(source->elements());
+ FixedArray* properties = FixedArray::cast(source->properties());
+ // Update elements if necessary.
+ if (elements->length()> 0) {
+ Object* elem = CopyFixedArray(elements);
+ if (elem->IsFailure()) return elem;
+ JSObject::cast(clone)->set_elements(FixedArray::cast(elem));
+ }
+ // Update properties if necessary.
+ if (properties->length() > 0) {
+ Object* prop = CopyFixedArray(properties);
+ if (prop->IsFailure()) return prop;
+ JSObject::cast(clone)->set_properties(FixedArray::cast(prop));
+ }
+ // Return the new clone.
+ return clone;
+}
+
+
+Object* Heap::ReinitializeJSGlobalProxy(JSFunction* constructor,
+ JSGlobalProxy* object) {
// Allocate initial map if absent.
if (!constructor->has_initial_map()) {
Object* initial_map = AllocateInitialMap(constructor);
@@ -2036,6 +2104,53 @@
}
+Object* Heap::AllocateRawFixedArray(int length) {
+ // Allocate the raw data for a fixed array.
+ int size = FixedArray::SizeFor(length);
+ return (size > MaxHeapObjectSize())
+ ? lo_space_->AllocateRawFixedArray(size)
+ : new_space_.AllocateRaw(size);
+}
+
+
+Object* Heap::CopyFixedArray(FixedArray* src) {
+ int len = src->length();
+ Object* obj = AllocateRawFixedArray(len);
+ if (obj->IsFailure()) return obj;
+ if (Heap::InNewSpace(obj)) {
+ HeapObject* dst = HeapObject::cast(obj);
+ CopyBlock(reinterpret_cast<Object**>(dst->address()),
+ reinterpret_cast<Object**>(src->address()),
+ FixedArray::SizeFor(len));
+ return obj;
+ }
+ HeapObject::cast(obj)->set_map(src->map());
+ FixedArray* result = FixedArray::cast(obj);
+ result->set_length(len);
+ // Copy the content
+ WriteBarrierMode mode = result->GetWriteBarrierMode();
+ for (int i = 0; i < len; i++) result->set(i, src->get(i), mode);
+ return result;
+}
+
+
+Object* Heap::AllocateFixedArray(int length) {
+ Object* result = AllocateRawFixedArray(length);
+ if (!result->IsFailure()) {
+ // Initialize header.
+ reinterpret_cast<Array*>(result)->set_map(fixed_array_map());
+ FixedArray* array = FixedArray::cast(result);
+ array->set_length(length);
+ Object* value = undefined_value();
+ // Initialize body.
+ for (int index = 0; index < length; index++) {
+ array->set(index, value, SKIP_WRITE_BARRIER);
+ }
+ }
+ return result;
+}
+
+
Object* Heap::AllocateFixedArray(int length, PretenureFlag pretenure) {
ASSERT(empty_fixed_array()->IsFixedArray());
if (length == 0) return empty_fixed_array();
@@ -2055,25 +2170,29 @@
reinterpret_cast<Array*>(result)->set_map(fixed_array_map());
FixedArray* array = FixedArray::cast(result);
array->set_length(length);
- for (int index = 0; index < length; index++) array->set_undefined(index);
+ Object* value = undefined_value();
+ for (int index = 0; index < length; index++) {
+ array->set(index, value, SKIP_WRITE_BARRIER);
+ }
return array;
}
Object* Heap::AllocateFixedArrayWithHoles(int length) {
if (length == 0) return empty_fixed_array();
- int size = FixedArray::SizeFor(length);
- Object* result = size > MaxHeapObjectSize()
- ? lo_space_->AllocateRawFixedArray(size)
- : AllocateRaw(size, NEW_SPACE);
- if (result->IsFailure()) return result;
-
- // Initialize the object.
- reinterpret_cast<Array*>(result)->set_map(fixed_array_map());
- FixedArray* array = FixedArray::cast(result);
- array->set_length(length);
- for (int index = 0; index < length; index++) array->set_the_hole(index);
- return array;
+ Object* result = AllocateRawFixedArray(length);
+ if (!result->IsFailure()) {
+ // Initialize header.
+ reinterpret_cast<Array*>(result)->set_map(fixed_array_map());
+ FixedArray* array = FixedArray::cast(result);
+ array->set_length(length);
+ // Initialize body.
+ Object* value = the_hole_value();
+ for (int index = 0; index < length; index++) {
+ array->set(index, value, SKIP_WRITE_BARRIER);
+ }
+ }
+ return result;
}
@@ -2191,7 +2310,7 @@
PrintF("Heap statistics : ");
MemoryAllocator::ReportStatistics();
PrintF("To space : ");
- new_space_->ReportStatistics();
+ new_space_.ReportStatistics();
PrintF("Old pointer space : ");
old_pointer_space_->ReportStatistics();
PrintF("Old data space : ");
@@ -2215,7 +2334,7 @@
bool Heap::Contains(Address addr) {
if (OS::IsOutsideAllocatedSpace(addr)) return false;
return HasBeenSetup() &&
- (new_space_->ToSpaceContains(addr) ||
+ (new_space_.ToSpaceContains(addr) ||
old_pointer_space_->Contains(addr) ||
old_data_space_->Contains(addr) ||
code_space_->Contains(addr) ||
@@ -2235,7 +2354,7 @@
switch (space) {
case NEW_SPACE:
- return new_space_->ToSpaceContains(addr);
+ return new_space_.ToSpaceContains(addr);
case OLD_POINTER_SPACE:
return old_pointer_space_->Contains(addr);
case OLD_DATA_SPACE:
@@ -2303,8 +2422,8 @@
#ifdef DEBUG
void Heap::ZapFromSpace() {
ASSERT(HAS_HEAP_OBJECT_TAG(kFromSpaceZapValue));
- for (Address a = new_space_->FromSpaceLow();
- a < new_space_->FromSpaceHigh();
+ for (Address a = new_space_.FromSpaceLow();
+ a < new_space_.FromSpaceHigh();
a += kPointerSize) {
Memory::Address_at(a) = kFromSpaceZapValue;
}
@@ -2322,29 +2441,21 @@
// Loop over all the pointers in [object_start, object_end).
while (object_address < object_end) {
uint32_t rset_word = Memory::uint32_at(rset_address);
-
if (rset_word != 0) {
- // Bits were set.
uint32_t result_rset = rset_word;
-
- // Loop over all the bits in the remembered set word. Though
- // remembered sets are sparse, faster (eg, binary) search for
- // set bits does not seem to help much here.
- for (int bit_offset = 0; bit_offset < kBitsPerInt; bit_offset++) {
- uint32_t bitmask = 1 << bit_offset;
+ for (uint32_t bitmask = 1; bitmask != 0; bitmask = bitmask << 1) {
// Do not dereference pointers at or past object_end.
if ((rset_word & bitmask) != 0 && object_address < object_end) {
Object** object_p = reinterpret_cast<Object**>(object_address);
- if (Heap::InFromSpace(*object_p)) {
+ if (Heap::InNewSpace(*object_p)) {
copy_object_func(reinterpret_cast<HeapObject**>(object_p));
}
// If this pointer does not need to be remembered anymore, clear
// the remembered set bit.
- if (!Heap::InToSpace(*object_p)) result_rset &= ~bitmask;
+ if (!Heap::InNewSpace(*object_p)) result_rset &= ~bitmask;
}
object_address += kPointerSize;
}
-
// Update the remembered set if it has changed.
if (result_rset != rset_word) {
Memory::uint32_at(rset_address) = result_rset;
@@ -2353,7 +2464,6 @@
// No bits in the word were set. This is the common case.
object_address += kPointerSize * kBitsPerInt;
}
-
rset_address += kIntSize;
}
}
@@ -2517,11 +2627,7 @@
int old_space_size = young_generation_size_ - code_space_size;
// Initialize new space.
- new_space_ = new NewSpace(initial_semispace_size_,
- semispace_size_,
- NEW_SPACE);
- if (new_space_ == NULL) return false;
- if (!new_space_->Setup(new_space_start, young_generation_size_)) return false;
+ if (!new_space_.Setup(new_space_start, young_generation_size_)) return false;
// Initialize old space, set the maximum capacity to the old generation
// size. It will not contain code.
@@ -2579,11 +2685,7 @@
void Heap::TearDown() {
GlobalHandles::TearDown();
- if (new_space_ != NULL) {
- new_space_->TearDown();
- delete new_space_;
- new_space_ = NULL;
- }
+ new_space_.TearDown();
if (old_pointer_space_ != NULL) {
old_pointer_space_->TearDown();
@@ -3090,4 +3192,13 @@
}
+#ifdef DEBUG
+bool Heap::GarbageCollectionGreedyCheck() {
+ ASSERT(FLAG_gc_greedy);
+ if (Bootstrapper::IsActive()) return true;
+ if (disallow_allocation_failure()) return true;
+ return CollectGarbage(0, NEW_SPACE);
+}
+#endif
+
} } // namespace v8::internal
diff --git a/src/heap.h b/src/heap.h
index 4da9a98..c619e9f 100644
--- a/src/heap.h
+++ b/src/heap.h
@@ -122,7 +122,8 @@
V(Code, c_entry_debug_break_code) \
V(FixedArray, number_string_cache) \
V(FixedArray, single_character_string_cache) \
- V(FixedArray, natives_source_cache)
+ V(FixedArray, natives_source_cache) \
+ V(Object, keyed_lookup_cache)
#define ROOT_LIST(V) \
@@ -135,11 +136,13 @@
V(Proto_symbol, "__proto__") \
V(StringImpl_symbol, "StringImpl") \
V(arguments_symbol, "arguments") \
+ V(Arguments_symbol, "Arguments") \
V(arguments_shadow_symbol, ".arguments") \
V(call_symbol, "call") \
V(apply_symbol, "apply") \
V(caller_symbol, "caller") \
V(boolean_symbol, "boolean") \
+ V(Boolean_symbol, "Boolean") \
V(callee_symbol, "callee") \
V(constructor_symbol, "constructor") \
V(code_symbol, ".code") \
@@ -151,6 +154,8 @@
V(length_symbol, "length") \
V(name_symbol, "name") \
V(number_symbol, "number") \
+ V(Number_symbol, "Number") \
+ V(RegExp_symbol, "RegExp") \
V(object_symbol, "object") \
V(prototype_symbol, "prototype") \
V(string_symbol, "string") \
@@ -244,11 +249,11 @@
// Return the starting address and a mask for the new space. And-masking an
// address with the mask will result in the start address of the new space
// for all addresses in either semispace.
- static Address NewSpaceStart() { return new_space_->start(); }
- static uint32_t NewSpaceMask() { return new_space_->mask(); }
- static Address NewSpaceTop() { return new_space_->top(); }
+ static Address NewSpaceStart() { return new_space_.start(); }
+ static uint32_t NewSpaceMask() { return new_space_.mask(); }
+ static Address NewSpaceTop() { return new_space_.top(); }
- static NewSpace* new_space() { return new_space_; }
+ static NewSpace* new_space() { return &new_space_; }
static OldSpace* old_pointer_space() { return old_pointer_space_; }
static OldSpace* old_data_space() { return old_data_space_; }
static OldSpace* code_space() { return code_space_; }
@@ -256,10 +261,10 @@
static LargeObjectSpace* lo_space() { return lo_space_; }
static Address* NewSpaceAllocationTopAddress() {
- return new_space_->allocation_top_address();
+ return new_space_.allocation_top_address();
}
static Address* NewSpaceAllocationLimitAddress() {
- return new_space_->allocation_limit_address();
+ return new_space_.allocation_limit_address();
}
// Allocates and initializes a new JavaScript object based on a
@@ -270,18 +275,23 @@
static Object* AllocateJSObject(JSFunction* constructor,
PretenureFlag pretenure = NOT_TENURED);
+ // Returns a deep copy of the JavaScript object.
+ // Properties and elements are copied too.
+ // Returns failure if allocation failed.
+ static Object* CopyJSObject(JSObject* source);
+
// Allocates the function prototype.
// Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
// failed.
// Please note this does not perform a garbage collection.
static Object* AllocateFunctionPrototype(JSFunction* function);
- // Reinitialize a JSGlobalObject based on a constructor. The JSObject
+ // Reinitialize an JSGlobalProxy based on a constructor. The object
// must have the same size as objects allocated using the
- // constructor. The JSObject is reinitialized and behaves as an
+ // constructor. The object is reinitialized and behaves as an
// object that has been freshly allocated using the constructor.
- static Object* ReinitializeJSGlobalObject(JSFunction* constructor,
- JSGlobalObject* global);
+ static Object* ReinitializeJSGlobalProxy(JSFunction* constructor,
+ JSGlobalProxy* global);
// Allocates and initializes a new JavaScript object based on a map.
// Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
@@ -375,9 +385,13 @@
// Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
// failed.
// Please note this does not perform a garbage collection.
- static Object* AllocateFixedArray(int length,
- PretenureFlag pretenure = NOT_TENURED);
+ static Object* AllocateFixedArray(int length, PretenureFlag pretenure);
+ // Allocate uninitialized, non-tenured fixed array with length elements.
+ static Object* AllocateFixedArray(int length);
+ // Make a copy of src and return it. Returns
+ // Failure::RetryAfterGC(requested_bytes, space) if the allocation failed.
+ static Object* CopyFixedArray(FixedArray* src);
// Allocates a fixed array initialized with the hole values.
// Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
@@ -557,6 +571,11 @@
// ensure correct callback for weak global handles.
static void PerformScavenge();
+#ifdef DEBUG
+ // Utility used with flag gc-greedy.
+ static bool GarbageCollectionGreedyCheck();
+#endif
+
static void SetGlobalGCPrologueCallback(GCCallback callback) {
global_gc_prologue_callback_ = callback;
}
@@ -621,6 +640,11 @@
non_monomorphic_cache_ = value;
}
+ // Gets, sets and clears the lookup cache used for keyed access.
+ static inline Object* GetKeyedLookupCache();
+ static inline void SetKeyedLookupCache(LookupCache* cache);
+ static inline void ClearKeyedLookupCache();
+
#ifdef DEBUG
static void Print();
static void PrintHandles();
@@ -671,7 +695,8 @@
// necessary, the object might be promoted to an old space. The caller must
// ensure the precondition that the object is (a) a heap object and (b) in
// the heap's from space.
- static void CopyObject(HeapObject** p);
+ static void ScavengePointer(HeapObject** p);
+ static inline void ScavengeObject(HeapObject** p, HeapObject* object);
// Clear a range of remembered set addresses corresponding to the object
// area address 'start' with size 'size_in_bytes', eg, when adding blocks
@@ -727,7 +752,7 @@
static const int kMaxMapSpaceSize = 8*MB;
- static NewSpace* new_space_;
+ static NewSpace new_space_;
static OldSpace* old_pointer_space_;
static OldSpace* old_data_space_;
static OldSpace* code_space_;
@@ -810,6 +835,8 @@
// (since both AllocateRaw and AllocateRawMap are inlined).
static inline Object* AllocateRawMap(int size_in_bytes);
+ // Allocate unitialized fixed array (pretenure == NON_TENURE).
+ static Object* AllocateRawFixedArray(int length);
// Initializes a JSObject based on its map.
static void InitializeJSObjectFromMap(JSObject* obj,
@@ -839,7 +866,7 @@
// Helper function used by CopyObject to copy a source object to an
// allocated target object and update the forwarding pointer in the source
// object. Returns the target object.
- static HeapObject* MigrateObject(HeapObject** source_p,
+ static HeapObject* MigrateObject(HeapObject* source,
HeapObject* target,
int size);
@@ -866,6 +893,12 @@
// Rebuild remembered set in the large object space.
static void RebuildRSets(LargeObjectSpace* space);
+ // Slow part of scavenge object.
+ static void ScavengeObjectSlow(HeapObject** p, HeapObject* object);
+
+ // Copy memory from src to dst.
+ inline static void CopyBlock(Object** dst, Object** src, int byte_size);
+
static const int kInitialSymbolTableSize = 2048;
static const int kInitialEvalCacheSize = 64;
diff --git a/src/ic-arm.cc b/src/ic-arm.cc
index 5a60322..d08e7b7 100644
--- a/src/ic-arm.cc
+++ b/src/ic-arm.cc
@@ -366,7 +366,7 @@
ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
// Check for access to global object (unlikely).
- __ cmp(r0, Operand(JS_GLOBAL_OBJECT_TYPE));
+ __ cmp(r0, Operand(JS_GLOBAL_PROXY_TYPE));
__ b(eq, &global);
// Search the dictionary placing the result in r1.
@@ -392,7 +392,7 @@
// Global object access: Check access rights.
__ bind(&global);
- __ CheckAccessGlobal(r1, r0, &miss);
+ __ CheckAccessGlobalProxy(r1, r0, &miss);
__ b(&probe);
// Cache miss: Jump to runtime.
@@ -482,7 +482,7 @@
ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
// Check for access to global object (unlikely).
- __ cmp(r1, Operand(JS_GLOBAL_OBJECT_TYPE));
+ __ cmp(r1, Operand(JS_GLOBAL_PROXY_TYPE));
__ b(eq, &global);
@@ -492,7 +492,7 @@
// Global object access: Check access rights.
__ bind(&global);
- __ CheckAccessGlobal(r0, r1, &miss);
+ __ CheckAccessGlobalProxy(r0, r1, &miss);
__ b(&probe);
// Cache miss: Restore receiver from stack and jump to runtime.
diff --git a/src/ic-ia32.cc b/src/ic-ia32.cc
index 3b12f29..d5fe47d 100644
--- a/src/ic-ia32.cc
+++ b/src/ic-ia32.cc
@@ -212,7 +212,7 @@
// -- esp[4] : name
// -- esp[8] : receiver
// -----------------------------------
- Label slow, fast, check_string;
+ Label slow, fast, check_string, index_int, index_string;
__ mov(eax, (Operand(esp, kPointerSize)));
__ mov(ecx, (Operand(esp, 2 * kPointerSize)));
@@ -234,6 +234,7 @@
__ j(not_zero, &check_string, not_taken);
__ sar(eax, kSmiTagSize);
// Get the elements array of the object.
+ __ bind(&index_int);
__ mov(ecx, FieldOperand(ecx, JSObject::kElementsOffset));
// Check that the object is in fast mode (not dictionary).
__ cmp(FieldOperand(ecx, HeapObject::kMapOffset),
@@ -248,18 +249,28 @@
KeyedLoadIC::Generate(masm, ExternalReference(Runtime::kKeyedGetProperty));
// Check if the key is a symbol that is not an array index.
__ bind(&check_string);
+ __ mov(ebx, FieldOperand(eax, String::kLengthOffset));
+ __ test(ebx, Immediate(String::kIsArrayIndexMask));
+ __ j(not_zero, &index_string, not_taken);
__ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
__ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
__ test(ebx, Immediate(kIsSymbolMask));
- __ j(zero, &slow, not_taken);
- __ mov(ebx, FieldOperand(eax, String::kLengthOffset));
- __ test(ebx, Immediate(String::kIsArrayIndexMask));
__ j(not_zero, &slow, not_taken);
// Probe the dictionary leaving result in ecx.
GenerateDictionaryLoad(masm, &slow, ebx, ecx, edx, eax);
__ mov(eax, Operand(ecx));
__ IncrementCounter(&Counters::keyed_load_generic_symbol, 1);
__ ret(0);
+ // Array index string: If short enough use cache in length/hash field (ebx).
+ __ bind(&index_string);
+ const int kLengthFieldLimit =
+ (String::kMaxCachedArrayIndexLength + 1) << String::kShortLengthShift;
+ __ cmp(ebx, kLengthFieldLimit);
+ __ j(above_equal, &slow);
+ __ mov(eax, Operand(ebx));
+ __ and_(eax, (1 << String::kShortLengthShift) - 1);
+ __ shr(eax, String::kLongLengthShift);
+ __ jmp(&index_int);
// Fast case: Do the load.
__ bind(&fast);
__ mov(eax, Operand(ecx, eax, times_4, Array::kHeaderSize - kHeapObjectTag));
@@ -469,7 +480,7 @@
ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
// Check for access to global object.
- __ cmp(eax, JS_GLOBAL_OBJECT_TYPE);
+ __ cmp(eax, JS_GLOBAL_PROXY_TYPE);
__ j(equal, &global, not_taken);
// Search the dictionary placing the result in edx.
@@ -493,7 +504,7 @@
// Global object access: Check access rights.
__ bind(&global);
- __ CheckAccessGlobal(edx, eax, &miss);
+ __ CheckAccessGlobalProxy(edx, eax, &miss);
__ jmp(&probe);
// Cache miss: Jump to runtime.
@@ -583,7 +594,7 @@
ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
// Check for access to global object (unlikely).
- __ cmp(edx, JS_GLOBAL_OBJECT_TYPE);
+ __ cmp(edx, JS_GLOBAL_PROXY_TYPE);
__ j(equal, &global, not_taken);
// Search the dictionary placing the result in eax.
@@ -593,7 +604,7 @@
// Global object access: Check access rights.
__ bind(&global);
- __ CheckAccessGlobal(eax, edx, &miss);
+ __ CheckAccessGlobalProxy(eax, edx, &miss);
__ jmp(&probe);
// Cache miss: Restore receiver from stack and jump to runtime.
diff --git a/src/ic.cc b/src/ic.cc
index 61dc3d0..9d954bb 100644
--- a/src/ic.cc
+++ b/src/ic.cc
@@ -850,6 +850,9 @@
Handle<String> name,
Handle<Object> value) {
ASSERT(lookup->IsLoaded());
+ // Skip JSGlobalProxy.
+ if (receiver->IsJSGlobalProxy()) return;
+
// Bail out if we didn't find a result.
if (!lookup->IsValid() || !lookup->IsCacheable()) return;
@@ -956,6 +959,7 @@
// Do not use ICs for objects that require access checks (including
// the global object).
bool use_ic = FLAG_use_ic && !object->IsAccessCheckNeeded();
+ ASSERT(!(use_ic && object->IsJSGlobalProxy()));
if (use_ic) set_target(generic_stub());
@@ -970,6 +974,10 @@
Handle<String> name,
Handle<Object> value) {
ASSERT(lookup->IsLoaded());
+
+ // Skip JSGlobalProxy.
+ if (receiver->IsJSGlobalProxy()) return;
+
// Bail out if we didn't find a result.
if (!lookup->IsValid() || !lookup->IsCacheable()) return;
diff --git a/src/jsregexp.cc b/src/jsregexp.cc
index 768c719..675f82a 100644
--- a/src/jsregexp.cc
+++ b/src/jsregexp.cc
@@ -222,8 +222,12 @@
if (value == -1) return Factory::null_value();
Handle<FixedArray> array = Factory::NewFixedArray(2);
- array->set(0, Smi::FromInt(value));
- array->set(1, Smi::FromInt(value + needle->length()));
+ array->set(0,
+ Smi::FromInt(value),
+ SKIP_WRITE_BARRIER);
+ array->set(1,
+ Smi::FromInt(value + needle->length()),
+ SKIP_WRITE_BARRIER);
return Factory::NewJSArrayWithElements(array);
}
@@ -247,8 +251,12 @@
int end = value + needle_length;
Handle<FixedArray> array = Factory::NewFixedArray(2);
- array->set(0, Smi::FromInt(value));
- array->set(1, Smi::FromInt(end));
+ array->set(0,
+ Smi::FromInt(value),
+ SKIP_WRITE_BARRIER);
+ array->set(1,
+ Smi::FromInt(end),
+ SKIP_WRITE_BARRIER);
Handle<JSArray> pair = Factory::NewJSArrayWithElements(array);
SetElement(result, match_count, pair);
match_count++;
@@ -372,8 +380,12 @@
Handle<FixedArray> array = Factory::NewFixedArray(2 * (num_captures+1));
// The captures come in (start, end+1) pairs.
for (int i = 0; i < 2 * (num_captures+1); i += 2) {
- array->set(i, Smi::FromInt(offsets_vector[i]));
- array->set(i+1, Smi::FromInt(offsets_vector[i+1]));
+ array->set(i,
+ Smi::FromInt(offsets_vector[i]),
+ SKIP_WRITE_BARRIER);
+ array->set(i+1,
+ Smi::FromInt(offsets_vector[i+1]),
+ SKIP_WRITE_BARRIER);
}
return Factory::NewJSArrayWithElements(array);
}
diff --git a/src/log.cc b/src/log.cc
index 01ca6dd..be76b96 100644
--- a/src/log.cc
+++ b/src/log.cc
@@ -349,6 +349,24 @@
#ifdef ENABLE_LOGGING_AND_PROFILING
+void Logger::LogString(Handle<String> str) {
+ int len = str->length();
+ if (len > 256)
+ len = 256;
+ for (int i = 0; i < len; i++) {
+ uc32 c = str->Get(i);
+ if (c < 32 || (c > 126 && c <= 255)) {
+ fprintf(logfile_, "\\x%02x", c);
+ } else if (c > 255) {
+ fprintf(logfile_, "\\u%04x", c);
+ } else if (c == ',') {
+ fprintf(logfile_, "\\,");
+ } else {
+ fprintf(logfile_, "%lc", c);
+ }
+ }
+}
+
void Logger::LogRegExpSource(Handle<JSRegExp> regexp) {
// Prints "/" + re.source + "/" +
// (re.global?"g":"") + (re.ignorecase?"i":"") + (re.multiline?"m":"")
@@ -358,9 +376,7 @@
fprintf(logfile_, "no source");
return;
}
- Handle<String> source_string = Handle<String>::cast(source);
- SmartPointer<uc16> cstring = source_string->ToWideCString();
if (regexp->type()->IsSmi()) {
switch (regexp->type_tag()) {
case JSRegExp::ATOM:
@@ -371,16 +387,7 @@
}
}
fprintf(logfile_, "/");
- for (int i = 0, n = source_string->length(); i < n; i++) {
- uc16 c = cstring[i];
- if (c < 32 || (c > 126 && c <= 255)) {
- fprintf(logfile_, "\\x%02x", c);
- } else if (c > 255) {
- fprintf(logfile_, "\\u%04x", c);
- } else {
- fprintf(logfile_, "%lc", c);
- }
- }
+ LogString(Handle<String>::cast(source));
fprintf(logfile_, "/");
// global flag
@@ -423,8 +430,9 @@
fprintf(logfile_, "regexp-run,");
LogRegExpSource(regexp);
- fprintf(logfile_, ",0x%08x,%d..%d\n",
- input_string->Hash(), start_index, input_string->length());
+ fprintf(logfile_, ",");
+ LogString(input_string);
+ fprintf(logfile_, ",%d..%d\n", start_index, input_string->length());
#endif
}
diff --git a/src/log.h b/src/log.h
index ef77117..69be103 100644
--- a/src/log.h
+++ b/src/log.h
@@ -199,6 +199,8 @@
// Emits the source code of a regexp. Used by regexp events.
static void LogRegExpSource(Handle<JSRegExp> regexp);
+ static void LogString(Handle<String> str);
+
// Emits a profiler tick event. Used by the profiler thread.
static void TickEvent(TickSample* sample, bool overflow);
diff --git a/src/macro-assembler-arm.cc b/src/macro-assembler-arm.cc
index c7139f4..2e957e4 100644
--- a/src/macro-assembler-arm.cc
+++ b/src/macro-assembler-arm.cc
@@ -593,7 +593,7 @@
// Only global objects and objects that do not require access
// checks are allowed in stubs.
- ASSERT(object->IsJSGlobalObject() || !object->IsAccessCheckNeeded());
+ ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded());
// Get the map of the current object.
ldr(scratch, FieldMemOperand(reg, HeapObject::kMapOffset));
@@ -605,8 +605,8 @@
// Check access rights to the global object. This has to happen
// after the map check so that we know that the object is
// actually a global object.
- if (object->IsJSGlobalObject()) {
- CheckAccessGlobal(reg, scratch, miss);
+ if (object->IsJSGlobalProxy()) {
+ CheckAccessGlobalProxy(reg, scratch, miss);
// Restore scratch register to be the map of the object. In the
// new space case below, we load the prototype from the map in
// the scratch register.
@@ -639,38 +639,85 @@
// Perform security check for access to the global object and return
// the holder register.
ASSERT(object == holder);
- ASSERT(object->IsJSGlobalObject() || !object->IsAccessCheckNeeded());
- if (object->IsJSGlobalObject()) {
- CheckAccessGlobal(reg, scratch, miss);
+ ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded());
+ if (object->IsJSGlobalProxy()) {
+ CheckAccessGlobalProxy(reg, scratch, miss);
}
return reg;
}
-void MacroAssembler::CheckAccessGlobal(Register holder_reg,
- Register scratch,
- Label* miss) {
- ASSERT(!holder_reg.is(scratch));
+void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg,
+ Register scratch,
+ Label* miss) {
+ Label same_contexts;
- // Load the security context.
- mov(scratch, Operand(Top::security_context_address()));
- ldr(scratch, MemOperand(scratch));
- // In debug mode, make sure the security context is set.
+ ASSERT(!holder_reg.is(scratch));
+ ASSERT(!holder_reg.is(ip));
+ ASSERT(!scratch.is(ip));
+
+ // Load current lexical context from the stack frame.
+ ldr(scratch, MemOperand(fp, StandardFrameConstants::kContextOffset));
+ // In debug mode, make sure the lexical context is set.
if (kDebug) {
cmp(scratch, Operand(0));
- Check(ne, "we should not have an empty security context");
+ Check(ne, "we should not have an empty lexical context");
}
- // Load the global object of the security context.
+ // Load the global context of the current context.
int offset = Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
ldr(scratch, FieldMemOperand(scratch, offset));
+ ldr(scratch, FieldMemOperand(scratch, GlobalObject::kGlobalContextOffset));
+
+ // Check the context is a global context.
+ if (FLAG_debug_code) {
+ // TODO(119): avoid push(holder_reg)/pop(holder_reg)
+ // Cannot use ip as a temporary in this verification code. Due to the fact
+ // that ip is clobbered as part of cmp with an object Operand.
+ push(holder_reg); // Temporarily save holder on the stack.
+ // Read the first word and compare to the global_context_map.
+ ldr(holder_reg, FieldMemOperand(scratch, HeapObject::kMapOffset));
+ cmp(holder_reg, Operand(Factory::global_context_map()));
+ Check(eq, "JSGlobalObject::global_context should be a global context.");
+ pop(holder_reg); // Restore holder.
+ }
+
+ // Check if both contexts are the same.
+ ldr(ip, FieldMemOperand(holder_reg, JSGlobalProxy::kContextOffset));
+ cmp(scratch, Operand(ip));
+ b(eq, &same_contexts);
+
+ // Check the context is a global context.
+ if (FLAG_debug_code) {
+ // TODO(119): avoid push(holder_reg)/pop(holder_reg)
+ // Cannot use ip as a temporary in this verification code. Due to the fact
+ // that ip is clobbered as part of cmp with an object Operand.
+ push(holder_reg); // Temporarily save holder on the stack.
+ mov(holder_reg, ip); // Move ip to its holding place.
+ cmp(holder_reg, Operand(Factory::null_value()));
+ Check(ne, "JSGlobalProxy::context() should not be null.");
+
+ ldr(holder_reg, FieldMemOperand(holder_reg, HeapObject::kMapOffset));
+ cmp(holder_reg, Operand(Factory::global_context_map()));
+ Check(eq, "JSGlobalObject::global_context should be a global context.");
+ // Restore ip is not needed. ip is reloaded below.
+ pop(holder_reg); // Restore holder.
+ // Restore ip to holder's context.
+ ldr(ip, FieldMemOperand(holder_reg, JSGlobalProxy::kContextOffset));
+ }
+
// Check that the security token in the calling global object is
// compatible with the security token in the receiving global
// object.
- ldr(scratch, FieldMemOperand(scratch, JSGlobalObject::kSecurityTokenOffset));
- ldr(ip, FieldMemOperand(holder_reg, JSGlobalObject::kSecurityTokenOffset));
+ int token_offset = Context::kHeaderSize +
+ Context::SECURITY_TOKEN_INDEX * kPointerSize;
+
+ ldr(scratch, FieldMemOperand(scratch, token_offset));
+ ldr(ip, FieldMemOperand(ip, token_offset));
cmp(scratch, Operand(ip));
b(ne, miss);
+
+ bind(&same_contexts);
}
diff --git a/src/macro-assembler-arm.h b/src/macro-assembler-arm.h
index d1a4a44..956cd71 100644
--- a/src/macro-assembler-arm.h
+++ b/src/macro-assembler-arm.h
@@ -176,7 +176,9 @@
// Generate code for checking access rights - used for security checks
// on access to global objects across environments. The holder register
// is left untouched, whereas both scratch registers are clobbered.
- void CheckAccessGlobal(Register holder_reg, Register scratch, Label* miss);
+ void CheckAccessGlobalProxy(Register holder_reg,
+ Register scratch,
+ Label* miss);
// ---------------------------------------------------------------------------
diff --git a/src/macro-assembler-ia32.cc b/src/macro-assembler-ia32.cc
index e72eeb2..72808de 100644
--- a/src/macro-assembler-ia32.cc
+++ b/src/macro-assembler-ia32.cc
@@ -469,7 +469,7 @@
// Only global objects and objects that do not require access
// checks are allowed in stubs.
- ASSERT(object->IsJSGlobalObject() || !object->IsAccessCheckNeeded());
+ ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded());
JSObject* prototype = JSObject::cast(object->GetPrototype());
if (Heap::InNewSpace(prototype)) {
@@ -481,16 +481,18 @@
// Check access rights to the global object. This has to happen
// after the map check so that we know that the object is
// actually a global object.
- if (object->IsJSGlobalObject()) {
- CheckAccessGlobal(reg, scratch, miss);
- // Restore scratch register to be the map of the object. We
- // load the prototype from the map in the scratch register.
+ if (object->IsJSGlobalProxy()) {
+ CheckAccessGlobalProxy(reg, scratch, miss);
+
+ // Restore scratch register to be the map of the object.
+ // We load the prototype from the map in the scratch register.
mov(scratch, FieldOperand(reg, HeapObject::kMapOffset));
}
// The prototype is in new space; we cannot store a reference
// to it in the code. Load it from the map.
reg = holder_reg; // from now the object is in holder_reg
mov(reg, FieldOperand(scratch, Map::kPrototypeOffset));
+
} else {
// Check the map of the current object.
cmp(FieldOperand(reg, HeapObject::kMapOffset),
@@ -500,8 +502,8 @@
// Check access rights to the global object. This has to happen
// after the map check so that we know that the object is
// actually a global object.
- if (object->IsJSGlobalObject()) {
- CheckAccessGlobal(reg, scratch, miss);
+ if (object->IsJSGlobalProxy()) {
+ CheckAccessGlobalProxy(reg, scratch, miss);
}
// The prototype is in old space; load it directly.
reg = holder_reg; // from now the object is in holder_reg
@@ -523,37 +525,79 @@
// Perform security check for access to the global object and return
// the holder register.
ASSERT(object == holder);
- ASSERT(object->IsJSGlobalObject() || !object->IsAccessCheckNeeded());
- if (object->IsJSGlobalObject()) {
- CheckAccessGlobal(reg, scratch, miss);
+ ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded());
+ if (object->IsJSGlobalProxy()) {
+ CheckAccessGlobalProxy(reg, scratch, miss);
}
return reg;
}
-void MacroAssembler::CheckAccessGlobal(Register holder_reg,
- Register scratch,
- Label* miss) {
+void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg,
+ Register scratch,
+ Label* miss) {
+ Label same_contexts;
+
ASSERT(!holder_reg.is(scratch));
- // Load the security context.
- ExternalReference security_context =
- ExternalReference(Top::k_security_context_address);
- mov(scratch, Operand::StaticVariable(security_context));
- // When generating debug code, make sure the security context is set.
+ // Load current lexical context from the stack frame.
+ mov(scratch, Operand(ebp, StandardFrameConstants::kContextOffset));
+
+ // When generating debug code, make sure the lexical context is set.
if (FLAG_debug_code) {
cmp(Operand(scratch), Immediate(0));
- Check(not_equal, "we should not have an empty security context");
+ Check(not_equal, "we should not have an empty lexical context");
}
- // Load the global object of the security context.
+ // Load the global context of the current context.
int offset = Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
mov(scratch, FieldOperand(scratch, offset));
+ mov(scratch, FieldOperand(scratch, GlobalObject::kGlobalContextOffset));
+
+ // Check the context is a global context.
+ if (FLAG_debug_code) {
+ push(scratch);
+ // Read the first word and compare to global_context_map.
+ mov(scratch, FieldOperand(scratch, HeapObject::kMapOffset));
+ cmp(scratch, Factory::global_context_map());
+ Check(equal, "JSGlobalObject::global_context should be a global context.");
+ pop(scratch);
+ }
+
+ // Check if both contexts are the same.
+ cmp(scratch, FieldOperand(holder_reg, JSGlobalProxy::kContextOffset));
+ j(equal, &same_contexts, taken);
+
+ // Compare security tokens, save holder_reg on the stack so we can use it
+ // as a temporary register.
+ //
+ // TODO(119): avoid push(holder_reg)/pop(holder_reg)
+ push(holder_reg);
// Check that the security token in the calling global object is
// compatible with the security token in the receiving global
// object.
- mov(scratch, FieldOperand(scratch, JSGlobalObject::kSecurityTokenOffset));
- cmp(scratch, FieldOperand(holder_reg, JSGlobalObject::kSecurityTokenOffset));
+ mov(holder_reg, FieldOperand(holder_reg, JSGlobalProxy::kContextOffset));
+
+ // Check the context is a global context.
+ if (FLAG_debug_code) {
+ cmp(holder_reg, Factory::null_value());
+ Check(not_equal, "JSGlobalProxy::context() should not be null.");
+
+ push(holder_reg);
+ // Read the first word and compare to global_context_map(),
+ mov(holder_reg, FieldOperand(holder_reg, HeapObject::kMapOffset));
+ cmp(holder_reg, Factory::global_context_map());
+ Check(equal, "JSGlobalObject::global_context should be a global context.");
+ pop(holder_reg);
+ }
+
+ int token_offset = Context::kHeaderSize +
+ Context::SECURITY_TOKEN_INDEX * kPointerSize;
+ mov(scratch, FieldOperand(scratch, token_offset));
+ cmp(scratch, FieldOperand(holder_reg, token_offset));
+ pop(holder_reg);
j(not_equal, miss, not_taken);
+
+ bind(&same_contexts);
}
diff --git a/src/macro-assembler-ia32.h b/src/macro-assembler-ia32.h
index e126c3c..8bcc651 100644
--- a/src/macro-assembler-ia32.h
+++ b/src/macro-assembler-ia32.h
@@ -168,7 +168,9 @@
// Generate code for checking access rights - used for security checks
// on access to global objects across environments. The holder register
// is left untouched, but the scratch register is clobbered.
- void CheckAccessGlobal(Register holder_reg, Register scratch, Label* miss);
+ void CheckAccessGlobalProxy(Register holder_reg,
+ Register scratch,
+ Label* miss);
// ---------------------------------------------------------------------------
diff --git a/src/macros.py b/src/macros.py
index 48ca367..5a7606a 100644
--- a/src/macros.py
+++ b/src/macros.py
@@ -78,9 +78,9 @@
macro IS_STRING(arg) = (typeof(arg) === 'string');
macro IS_OBJECT(arg) = (typeof(arg) === 'object');
macro IS_BOOLEAN(arg) = (typeof(arg) === 'boolean');
-macro IS_REGEXP(arg) = (%ClassOf(arg) === 'RegExp');
-macro IS_ARRAY(arg) = %IsArrayClass(arg);
-macro IS_DATE(arg) = %IsDateClass(arg);
+macro IS_REGEXP(arg) = %HasRegExpClass(arg);
+macro IS_ARRAY(arg) = %HasArrayClass(arg);
+macro IS_DATE(arg) = %HasDateClass(arg);
macro IS_ERROR(arg) = (%ClassOf(arg) === 'Error');
macro IS_SCRIPT(arg) = (%ClassOf(arg) === 'Script');
macro FLOOR(arg) = %Math_floor(arg);
diff --git a/src/mark-compact.cc b/src/mark-compact.cc
index e1937b8..8daf28d 100644
--- a/src/mark-compact.cc
+++ b/src/mark-compact.cc
@@ -207,7 +207,7 @@
static MarkingStack marking_stack;
-inline HeapObject* ShortCircuitConsString(Object** p) {
+static inline HeapObject* ShortCircuitConsString(Object** p) {
// Optimization: If the heap object pointed to by p is a non-symbol
// cons string whose right substring is Heap::empty_string, update
// it in place to its left substring. Return the updated value.
diff --git a/src/messages.cc b/src/messages.cc
index 7e3c0d6..7edb07f 100644
--- a/src/messages.cc
+++ b/src/messages.cc
@@ -96,15 +96,26 @@
script.location(),
stack_trace_val.location() };
+ // Setup a catch handler to catch exceptions in creating the message. This
+ // handler is non-verbose to avoid calling MakeMessage recursively in case of
+ // an exception.
+ v8::TryCatch catcher;
+ catcher.SetVerbose(false);
+ catcher.SetCaptureMessage(false);
+
+ // Format the message.
bool caught_exception = false;
Handle<Object> message =
- Execution::TryCall(fun, Factory::undefined_value(), argc, argv,
- &caught_exception);
- // If creating the message (in JS code) resulted in an exception, we
- // skip doing the callback. This usually only happens in case of
- // stack overflow exceptions being thrown by the parser when the
- // stack is almost full.
- if (caught_exception) return Handle<Object>();
+ Execution::Call(fun, Factory::undefined_value(), argc, argv,
+ &caught_exception);
+ if (caught_exception) {
+ // If creating the message (in JS code) resulted in an exception, we
+ // skip doing the callback. This usually only happens in case of
+ // stack overflow exceptions being thrown by the parser when the
+ // stack is almost full.
+ if (caught_exception) return Handle<Object>();
+ }
+
return message.EscapeFrom(&scope);
}
@@ -137,12 +148,12 @@
Handle<JSFunction> fun =
Handle<JSFunction>(
JSFunction::cast(
- Top::security_context_builtins()->GetProperty(*fmt_str)));
+ Top::builtins()->GetProperty(*fmt_str)));
Object** argv[1] = { data.location() };
bool caught_exception;
Handle<Object> result =
- Execution::TryCall(fun, Top::security_context_builtins(), 1, argv,
+ Execution::TryCall(fun, Top::builtins(), 1, argv,
&caught_exception);
if (caught_exception || !result->IsString()) {
diff --git a/src/mirror-delay.js b/src/mirror-delay.js
index 2a297b5..caf963e 100644
--- a/src/mirror-delay.js
+++ b/src/mirror-delay.js
@@ -682,7 +682,7 @@
ObjectMirror.prototype.property = function(name) {
- var details = %DebugGetLocalPropertyDetails(this.value_, %ToString(name));
+ var details = %DebugGetPropertyDetails(this.value_, %ToString(name));
if (details) {
return new PropertyMirror(this, name, details[0], details[1]);
}
@@ -975,7 +975,7 @@
if (from_index > to_index) return new Array();
var values = new Array(to_index - from_index + 1);
for (var i = from_index; i <= to_index; i++) {
- var details = %DebugGetLocalPropertyDetails(this.value_, %ToString(i));
+ var details = %DebugGetPropertyDetails(this.value_, %ToString(i));
var value;
if (details) {
value = new PropertyMirror(this, i, details[0], details[1]);
diff --git a/src/natives.h b/src/natives.h
index 0bb879f..3eb8090 100644
--- a/src/natives.h
+++ b/src/natives.h
@@ -34,7 +34,12 @@
Vector<const char> source,
int index);
-class Natives {
+enum NativeType {
+ CORE, D8
+};
+
+template <NativeType type>
+class NativesCollection {
public:
// Number of built-in scripts.
static int GetBuiltinsCount();
@@ -50,6 +55,8 @@
static Vector<const char> GetScriptName(int index);
};
+typedef NativesCollection<CORE> Natives;
+
} } // namespace v8::internal
#endif // V8_NATIVES_H_
diff --git a/src/objects-debug.cc b/src/objects-debug.cc
index 3c56590..dc5cb60 100644
--- a/src/objects-debug.cc
+++ b/src/objects-debug.cc
@@ -127,6 +127,9 @@
case JS_FUNCTION_TYPE:
JSFunction::cast(this)->JSFunctionPrint();
break;
+ case JS_GLOBAL_PROXY_TYPE:
+ JSGlobalProxy::cast(this)->JSGlobalProxyPrint();
+ break;
case JS_GLOBAL_OBJECT_TYPE:
JSGlobalObject::cast(this)->JSGlobalObjectPrint();
break;
@@ -198,6 +201,9 @@
case JS_FUNCTION_TYPE:
JSFunction::cast(this)->JSFunctionVerify();
break;
+ case JS_GLOBAL_PROXY_TYPE:
+ JSGlobalProxy::cast(this)->JSGlobalProxyVerify();
+ break;
case JS_GLOBAL_OBJECT_TYPE:
JSGlobalObject::cast(this)->JSGlobalObjectVerify();
break;
@@ -384,6 +390,7 @@
case JS_VALUE_TYPE: return "JS_VALUE";
case JS_GLOBAL_OBJECT_TYPE: return "JS_GLOBAL_OBJECT";
case JS_BUILTINS_OBJECT_TYPE: return "JS_BUILTINS_OBJECT";
+ case JS_GLOBAL_PROXY_TYPE: return "JS_GLOBAL_PROXY";
case PROXY_TYPE: return "PROXY";
case SMI_TYPE: return "SMI";
#define MAKE_STRUCT_CASE(NAME, Name, name) case NAME##_TYPE: return #NAME;
@@ -551,9 +558,31 @@
}
+void JSGlobalProxy::JSGlobalProxyPrint() {
+ PrintF("global_proxy");
+ JSObjectPrint();
+ PrintF("context : ");
+ context()->ShortPrint();
+ PrintF("\n");
+}
+
+
+void JSGlobalProxy::JSGlobalProxyVerify() {
+ CHECK(IsJSGlobalProxy());
+ JSObjectVerify();
+ VerifyObjectField(JSGlobalProxy::kContextOffset);
+ // Make sure that this object has no properties, elements.
+ CHECK_EQ(0, properties()->length());
+ CHECK_EQ(0, elements()->length());
+}
+
+
void JSGlobalObject::JSGlobalObjectPrint() {
PrintF("global ");
JSObjectPrint();
+ PrintF("global context : ");
+ global_context()->ShortPrint();
+ PrintF("\n");
}
diff --git a/src/objects-inl.h b/src/objects-inl.h
index ce2da12..0cfa12b 100644
--- a/src/objects-inl.h
+++ b/src/objects-inl.h
@@ -66,12 +66,13 @@
#define ACCESSORS(holder, name, type, offset) \
type* holder::name() { return type::cast(READ_FIELD(this, offset)); } \
- void holder::set_##name(type* value) { \
+ void holder::set_##name(type* value, WriteBarrierMode mode) { \
WRITE_FIELD(this, offset, value); \
- WRITE_BARRIER(this, offset); \
+ CONDITIONAL_WRITE_BARRIER(this, offset, mode); \
}
+
#define SMI_ACCESSORS(holder, name, offset) \
int holder::name() { \
Object* value = READ_FIELD(this, offset); \
@@ -125,6 +126,12 @@
}
+bool String::IsSeqAsciiString() {
+ return (this->representation_tag() == kSeqStringTag)
+ && is_ascii_representation();
+}
+
+
bool Object::IsSeqTwoByteString() {
return IsSeqString()
&& !String::cast(this)->IsAsciiRepresentation();
@@ -348,28 +355,35 @@
}
+bool Object::IsLookupCache() {
+ return IsHashTable();
+}
+
+
bool Object::IsPrimitive() {
return IsOddball() || IsNumber() || IsString();
}
+bool Object::IsJSGlobalProxy() {
+ bool result = IsHeapObject() &&
+ (HeapObject::cast(this)->map()->instance_type() ==
+ JS_GLOBAL_PROXY_TYPE);
+ ASSERT(!result || IsAccessCheckNeeded());
+ return result;
+}
+
+
bool Object::IsGlobalObject() {
- return IsHeapObject() &&
- ((HeapObject::cast(this)->map()->instance_type() ==
- JS_GLOBAL_OBJECT_TYPE) ||
- (HeapObject::cast(this)->map()->instance_type() ==
- JS_BUILTINS_OBJECT_TYPE));
+ if (!IsHeapObject()) return false;
+
+ InstanceType type = HeapObject::cast(this)->map()->instance_type();
+ return type == JS_GLOBAL_OBJECT_TYPE ||
+ type == JS_BUILTINS_OBJECT_TYPE;
}
bool Object::IsJSGlobalObject() {
-#ifdef DEBUG
- if (IsHeapObject() &&
- (HeapObject::cast(this)->map()->instance_type() ==
- JS_GLOBAL_OBJECT_TYPE)) {
- ASSERT(IsAccessCheckNeeded());
- }
-#endif
return IsHeapObject() &&
(HeapObject::cast(this)->map()->instance_type() ==
JS_GLOBAL_OBJECT_TYPE);
@@ -391,7 +405,7 @@
bool Object::IsAccessCheckNeeded() {
return IsHeapObject()
- && HeapObject::cast(this)->map()->needs_access_check();
+ && HeapObject::cast(this)->map()->is_access_check_needed();
}
@@ -462,6 +476,11 @@
}
+bool Object::HasSpecificClassOf(String* name) {
+ return this->IsJSObject() && (JSObject::cast(this)->class_name() == name);
+}
+
+
Object* Object::GetElement(uint32_t index) {
return GetElementWithReceiver(this, index);
}
@@ -487,9 +506,21 @@
#define WRITE_FIELD(p, offset, value) \
(*reinterpret_cast<Object**>(FIELD_ADDR(p, offset)) = value)
+
#define WRITE_BARRIER(object, offset) \
Heap::RecordWrite(object->address(), offset);
+// CONITIONAL_WRITE_BARRIER must be issued after the actual
+// write due to the assert validating the written value.
+#define CONDITIONAL_WRITE_BARRIER(object, offset, mode) \
+ if (mode == UPDATE_WRITE_BARRIER) { \
+ Heap::RecordWrite(object->address(), offset); \
+ } else { \
+ ASSERT(mode == SKIP_WRITE_BARRIER); \
+ ASSERT(Heap::InNewSpace(object) || \
+ !Heap::InNewSpace(READ_FIELD(object, offset))); \
+ }
+
#define READ_DOUBLE_FIELD(p, offset) \
(*reinterpret_cast<double*>(FIELD_ADDR(p, offset)))
@@ -587,6 +618,17 @@
}
+Failure* Failure::RetryAfterGC(int requested_bytes) {
+ int requested = requested_bytes >> kObjectAlignmentBits;
+ int value = (requested << kSpaceTagSize) | NEW_SPACE;
+ ASSERT(value >> kSpaceTagSize == requested);
+ ASSERT(Smi::IsValid(value));
+ ASSERT(value == ((value << kFailureTypeTagSize) >> kFailureTypeTagSize));
+ ASSERT(Smi::IsValid(value << kFailureTypeTagSize));
+ return Construct(RETRY_AFTER_GC, value);
+}
+
+
Failure* Failure::Construct(Type type, int value) {
int info = (value << kFailureTypeTagSize) | type;
ASSERT(Smi::IsValid(info)); // Same validation check as in Smi
@@ -783,21 +825,6 @@
}
-void HeapObject::CopyBody(JSObject* from) {
- ASSERT(map() == from->map());
- ASSERT(Size() == from->Size());
- int object_size = Size();
- for (int offset = kHeaderSize;
- offset < object_size;
- offset += kPointerSize) {
- Object* value = READ_FIELD(from, offset);
- // Note: WRITE_FIELD does not update the write barrier.
- WRITE_FIELD(this, offset, value);
- WRITE_BARRIER(this, offset);
- }
-}
-
-
bool HeapObject::IsMarked() {
return map_word().IsMarked();
}
@@ -850,7 +877,7 @@
ACCESSORS(JSObject, properties, FixedArray, kPropertiesOffset)
-ACCESSORS(JSObject, elements, HeapObject, kElementsOffset)
+ACCESSORS(JSObject, elements, FixedArray, kElementsOffset)
void JSObject::initialize_properties() {
@@ -871,6 +898,8 @@
int JSObject::GetHeaderSize() {
switch (map()->instance_type()) {
+ case JS_GLOBAL_PROXY_TYPE:
+ return JSGlobalProxy::kSize;
case JS_GLOBAL_OBJECT_TYPE:
return JSGlobalObject::kSize;
case JS_BUILTINS_OBJECT_TYPE:
@@ -924,7 +953,7 @@
// Access fast-case object properties at index. The use of these routines
// is needed to correctly distinguish between properties stored in-object and
// properties stored in the properties array.
-inline Object* JSObject::FastPropertyAt(int index) {
+Object* JSObject::FastPropertyAt(int index) {
// Adjust for the number of properties stored in the object.
index -= map()->inobject_properties();
if (index < 0) {
@@ -937,7 +966,7 @@
}
-inline Object* JSObject::FastPropertyAtPut(int index, Object* value) {
+Object* JSObject::FastPropertyAtPut(int index, Object* value) {
// Adjust for the number of properties stored in the object.
index -= map()->inobject_properties();
if (index < 0) {
@@ -952,16 +981,32 @@
}
+Object* JSObject::InObjectPropertyAtPut(int index,
+ Object* value,
+ WriteBarrierMode mode) {
+ // Adjust for the number of properties stored in the object.
+ index -= map()->inobject_properties();
+ ASSERT(index < 0);
+ int offset = map()->instance_size() + (index * kPointerSize);
+ WRITE_FIELD(this, offset, value);
+ CONDITIONAL_WRITE_BARRIER(this, offset, mode);
+ return value;
+}
+
+
+
void JSObject::InitializeBody(int object_size) {
+ Object* value = Heap::undefined_value();
for (int offset = kHeaderSize; offset < object_size; offset += kPointerSize) {
- WRITE_FIELD(this, offset, Heap::undefined_value());
+ WRITE_FIELD(this, offset, value);
}
}
void Struct::InitializeBody(int object_size) {
+ Object* value = Heap::undefined_value();
for (int offset = kHeaderSize; offset < object_size; offset += kPointerSize) {
- WRITE_FIELD(this, offset, Heap::undefined_value());
+ WRITE_FIELD(this, offset, value);
}
}
@@ -1017,7 +1062,7 @@
}
-FixedArray::WriteBarrierMode FixedArray::GetWriteBarrierMode() {
+WriteBarrierMode HeapObject::GetWriteBarrierMode() {
if (Heap::InNewSpace(this)) return SKIP_WRITE_BARRIER;
return UPDATE_WRITE_BARRIER;
}
@@ -1025,16 +1070,11 @@
void FixedArray::set(int index,
Object* value,
- FixedArray::WriteBarrierMode mode) {
+ WriteBarrierMode mode) {
ASSERT(index >= 0 && index < this->length());
int offset = kHeaderSize + index * kPointerSize;
WRITE_FIELD(this, offset, value);
- if (mode == UPDATE_WRITE_BARRIER) {
- WRITE_BARRIER(this, offset);
- } else {
- ASSERT(mode == SKIP_WRITE_BARRIER);
- ASSERT(Heap::InNewSpace(this) || !Heap::InNewSpace(value));
- }
+ CONDITIONAL_WRITE_BARRIER(this, offset, mode);
}
@@ -1152,7 +1192,7 @@
bool Dictionary::requires_slow_elements() {
- Object* max_index_object = get(kPrefixStartIndex);
+ Object* max_index_object = get(kMaxNumberKeyIndex);
if (!max_index_object->IsSmi()) return false;
return 0 !=
(Smi::cast(max_index_object)->value() & kRequiresSlowElementsMask);
@@ -1161,7 +1201,7 @@
uint32_t Dictionary::max_number_key() {
ASSERT(!requires_slow_elements());
- Object* max_index_object = get(kPrefixStartIndex);
+ Object* max_index_object = get(kMaxNumberKeyIndex);
if (!max_index_object->IsSmi()) return 0;
uint32_t value = static_cast<uint32_t>(Smi::cast(max_index_object)->value());
return value >> kRequiresSlowElementsTagSize;
@@ -1178,6 +1218,7 @@
CAST_ACCESSOR(SymbolTable)
CAST_ACCESSOR(CompilationCacheTable)
CAST_ACCESSOR(MapCache)
+CAST_ACCESSOR(LookupCache)
CAST_ACCESSOR(String)
CAST_ACCESSOR(SeqString)
CAST_ACCESSOR(SeqAsciiString)
@@ -1196,6 +1237,8 @@
CAST_ACCESSOR(SharedFunctionInfo)
CAST_ACCESSOR(Map)
CAST_ACCESSOR(JSFunction)
+CAST_ACCESSOR(GlobalObject)
+CAST_ACCESSOR(JSGlobalProxy)
CAST_ACCESSOR(JSGlobalObject)
CAST_ACCESSOR(JSBuiltinsObject)
CAST_ACCESSOR(Code)
@@ -1363,6 +1406,12 @@
}
+int String::full_representation_tag() {
+ return map()->instance_type() &
+ (kStringRepresentationMask | kStringEncodingMask);
+}
+
+
StringRepresentationTag String::representation_tag() {
return map_representation_tag(map());
}
@@ -1408,11 +1457,21 @@
}
+char* SeqAsciiString::GetChars() {
+ return reinterpret_cast<char*>(GetCharsAddress());
+}
+
+
Address SeqTwoByteString::GetCharsAddress() {
return FIELD_ADDR(this, kHeaderSize);
}
+uc16* SeqTwoByteString::GetChars() {
+ return reinterpret_cast<uc16*>(FIELD_ADDR(this, kHeaderSize));
+}
+
+
uint16_t SeqTwoByteString::SeqTwoByteStringGet(int index) {
ASSERT(index >= 0 && index < length());
return READ_SHORT_FIELD(this, kHeaderSize + index * kShortSize);
@@ -1768,10 +1827,10 @@
}
-void Map::set_prototype(Object* value) {
+void Map::set_prototype(Object* value, WriteBarrierMode mode) {
ASSERT(value->IsNull() || value->IsJSObject());
WRITE_FIELD(this, kPrototypeOffset, value);
- WRITE_BARRIER(this, kPrototypeOffset);
+ CONDITIONAL_WRITE_BARRIER(this, kPrototypeOffset, mode);
}
@@ -1785,8 +1844,9 @@
ACCESSORS(GlobalObject, builtins, JSBuiltinsObject, kBuiltinsOffset)
ACCESSORS(GlobalObject, global_context, Context, kGlobalContextOffset)
+ACCESSORS(GlobalObject, global_receiver, JSObject, kGlobalReceiverOffset)
-ACCESSORS(JSGlobalObject, security_token, Object, kSecurityTokenOffset)
+ACCESSORS(JSGlobalProxy, context, Object, kContextOffset)
ACCESSORS(AccessorInfo, getter, Object, kGetterOffset)
ACCESSORS(AccessorInfo, setter, Object, kSetterOffset)
@@ -1911,9 +1971,9 @@
}
-void SharedFunctionInfo::set_code(Code* value) {
+void SharedFunctionInfo::set_code(Code* value, WriteBarrierMode mode) {
WRITE_FIELD(this, kCodeOffset, value);
- WRITE_BARRIER(this, kCodeOffset);
+ CONDITIONAL_WRITE_BARRIER(this, kCodeOffset, mode);
}
@@ -2150,7 +2210,7 @@
// Fast case: has hash code already been computed?
uint32_t field = length_field();
if (field & kHashComputedMask) return field >> kHashShift;
- // Slow case: compute hash code and set it..
+ // Slow case: compute hash code and set it.
return ComputeAndSetHash();
}
@@ -2170,11 +2230,12 @@
void StringHasher::AddCharacter(uc32 c) {
- // Note: the Jenkins one-at-a-time hash function
+ // Use the Jenkins one-at-a-time hash function to update the hash
+ // for the given character.
raw_running_hash_ += c;
raw_running_hash_ += (raw_running_hash_ << 10);
raw_running_hash_ ^= (raw_running_hash_ >> 6);
- // Incremental array index computation
+ // Incremental array index computation.
if (is_array_index_) {
if (c < '0' || c > '9') {
is_array_index_ = false;
@@ -2295,6 +2356,12 @@
}
+Object* FixedArray::Copy() {
+ if (length() == 0) return this;
+ return Heap::CopyFixedArray(this);
+}
+
+
#undef CAST_ACCESSOR
#undef INT_ACCESSORS
#undef SMI_ACCESSORS
@@ -2303,6 +2370,7 @@
#undef READ_FIELD
#undef WRITE_FIELD
#undef WRITE_BARRIER
+#undef CONDITIONAL_WRITE_BARRIER
#undef READ_MEMADDR_FIELD
#undef WRITE_MEMADDR_FIELD
#undef READ_DOUBLE_FIELD
diff --git a/src/objects.cc b/src/objects.cc
index 2805c3d..d12c3eb 100644
--- a/src/objects.cc
+++ b/src/objects.cc
@@ -288,6 +288,7 @@
String* name,
Object* value,
PropertyAttributes attributes) {
+ ASSERT(!IsJSGlobalProxy());
HandleScope scope;
Handle<JSObject> this_handle(this);
Handle<String> name_handle(name);
@@ -528,12 +529,33 @@
// an old space GC.
PretenureFlag tenure = Heap::InNewSpace(this) ? NOT_TENURED : TENURED;
int len = length();
- Object* object = IsAsciiRepresentation() ?
- Heap::AllocateRawAsciiString(len, tenure) :
- Heap::AllocateRawTwoByteString(len, tenure);
- if (object->IsFailure()) return object;
- String* result = String::cast(object);
- Flatten(this, result, 0, len, 0);
+ Object* object;
+ String* result;
+ if (IsAsciiRepresentation()) {
+ object = Heap::AllocateRawAsciiString(len, tenure);
+ if (object->IsFailure()) return object;
+ result = String::cast(object);
+ String* first = String::cast(cs->first());
+ int first_length = first->length();
+ char* dest = SeqAsciiString::cast(result)->GetChars();
+ WriteToFlat(first, dest, 0, first_length);
+ WriteToFlat(String::cast(cs->second()),
+ dest + first_length,
+ 0,
+ len - first_length);
+ } else {
+ object = Heap::AllocateRawTwoByteString(len, tenure);
+ if (object->IsFailure()) return object;
+ result = String::cast(object);
+ uc16* dest = SeqTwoByteString::cast(result)->GetChars();
+ String* first = String::cast(cs->first());
+ int first_length = first->length();
+ WriteToFlat(first, dest, 0, first_length);
+ WriteToFlat(String::cast(cs->second()),
+ dest + first_length,
+ 0,
+ len - first_length);
+ }
cs->set_first(result);
cs->set_second(Heap::empty_string());
return this;
@@ -636,7 +658,7 @@
break;
}
// All other JSObjects are rather similar to each other (JSObject,
- // JSGlobalObject, JSUndetectableObject, JSValue).
+ // JSGlobalProxy, JSGlobalObject, JSUndetectableObject, JSValue).
default: {
Object* constructor = map()->constructor();
bool printed = false;
@@ -644,7 +666,7 @@
!Heap::Contains(HeapObject::cast(constructor))) {
accumulator->Add("!!!INVALID CONSTRUCTOR!!!");
} else {
- bool global_object = IsJSGlobalObject();
+ bool global_object = IsJSGlobalProxy();
if (constructor->IsJSFunction()) {
if (!Heap::Contains(JSFunction::cast(constructor)->shared())) {
accumulator->Add("!!!INVALID SHARED ON CONSTRUCTOR!!!");
@@ -656,7 +678,7 @@
if (str->length() > 0) {
bool vowel = AnWord(str);
accumulator->Add("<%sa%s ",
- global_object ? "JS Global Object: " : "",
+ global_object ? "Global Object: " : "",
vowel ? "n" : "");
accumulator->Put(str);
accumulator->Put('>');
@@ -820,9 +842,8 @@
case JS_ARRAY_TYPE:
case JS_REGEXP_TYPE:
case JS_FUNCTION_TYPE:
+ case JS_GLOBAL_PROXY_TYPE:
case JS_GLOBAL_OBJECT_TYPE:
- reinterpret_cast<JSObject*>(this)->JSObjectIterateBody(object_size, v);
- break;
case JS_BUILTINS_OBJECT_TYPE:
reinterpret_cast<JSObject*>(this)->JSObjectIterateBody(object_size, v);
break;
@@ -895,13 +916,12 @@
String* JSObject::class_name() {
if (IsJSFunction()) return Heap::function_class_symbol();
- // If the constructor is not present "Object" is returned.
- String* result = Heap::Object_symbol();
if (map()->constructor()->IsJSFunction()) {
JSFunction* constructor = JSFunction::cast(map()->constructor());
return String::cast(constructor->shared()->instance_class_name());
}
- return result;
+ // If the constructor is not present, return "Object".
+ return Heap::Object_symbol();
}
@@ -911,33 +931,6 @@
}
-Object* JSObject::Copy(PretenureFlag pretenure) {
- // Never used to copy functions. If functions need to be copied we
- // have to be careful to clear the literals array.
- ASSERT(!IsJSFunction());
-
- // Copy the elements and properties.
- Object* elem = FixedArray::cast(elements())->Copy();
- if (elem->IsFailure()) return elem;
- Object* prop = properties()->Copy();
- if (prop->IsFailure()) return prop;
-
- // Make the clone.
- Object* clone = (pretenure == NOT_TENURED) ?
- Heap::Allocate(map(), NEW_SPACE) :
- Heap::Allocate(map(), OLD_POINTER_SPACE);
- if (clone->IsFailure()) return clone;
- JSObject::cast(clone)->CopyBody(this);
-
- // Set the new elements and properties.
- JSObject::cast(clone)->set_elements(FixedArray::cast(elem));
- JSObject::cast(clone)->set_properties(FixedArray::cast(prop));
-
- // Return the new clone.
- return clone;
-}
-
-
Object* JSObject::AddFastPropertyUsingMap(Map* new_map,
String* name,
Object* value) {
@@ -1086,6 +1079,7 @@
Object* JSObject::AddProperty(String* name,
Object* value,
PropertyAttributes attributes) {
+ ASSERT(!IsJSGlobalProxy());
if (HasFastProperties()) {
// Ensure the descriptor array does not get too big.
if (map()->instance_descriptors()->number_of_descriptors() <
@@ -1369,6 +1363,13 @@
void JSObject::LocalLookupRealNamedProperty(String* name,
LookupResult* result) {
+ if (IsJSGlobalProxy()) {
+ Object* proto = GetPrototype();
+ if (proto->IsNull()) return result->NotFound();
+ ASSERT(proto->IsJSGlobalObject());
+ return JSObject::cast(proto)->LocalLookupRealNamedProperty(name, result);
+ }
+
if (HasFastProperties()) {
LookupInDescriptor(name, result);
if (result->IsValid()) {
@@ -1487,6 +1488,14 @@
&& !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
return SetPropertyWithFailedAccessCheck(result, name, value);
}
+
+ if (IsJSGlobalProxy()) {
+ Object* proto = GetPrototype();
+ if (proto->IsNull()) return value;
+ ASSERT(proto->IsJSGlobalObject());
+ return JSObject::cast(proto)->SetProperty(result, name, value, attributes);
+ }
+
if (result->IsNotFound() || !result->IsProperty()) {
// We could not find a local property so let's check whether there is an
// accessor that wants to handle the property.
@@ -1979,6 +1988,20 @@
Object* JSObject::DeleteElement(uint32_t index) {
+ // Check access rights if needed.
+ if (IsAccessCheckNeeded() &&
+ !Top::MayIndexedAccess(this, index, v8::ACCESS_DELETE)) {
+ Top::ReportFailedAccessCheck(this, v8::ACCESS_DELETE);
+ return Heap::false_value();
+ }
+
+ if (IsJSGlobalProxy()) {
+ Object* proto = GetPrototype();
+ if (proto->IsNull()) return Heap::false_value();
+ ASSERT(proto->IsJSGlobalObject());
+ return JSGlobalObject::cast(proto)->DeleteElement(index);
+ }
+
if (HasIndexedInterceptor()) {
return DeleteElementWithInterceptor(index);
}
@@ -2001,6 +2024,9 @@
Object* JSObject::DeleteProperty(String* name) {
+ // ECMA-262, 3rd, 8.6.2.5
+ ASSERT(name->IsString());
+
// Check access rights if needed.
if (IsAccessCheckNeeded() &&
!Top::MayNamedAccess(this, name, v8::ACCESS_DELETE)) {
@@ -2008,8 +2034,12 @@
return Heap::false_value();
}
- // ECMA-262, 3rd, 8.6.2.5
- ASSERT(name->IsString());
+ if (IsJSGlobalProxy()) {
+ Object* proto = GetPrototype();
+ if (proto->IsNull()) return Heap::false_value();
+ ASSERT(proto->IsJSGlobalObject());
+ return JSGlobalObject::cast(proto)->DeleteProperty(name);
+ }
uint32_t index = 0;
if (name->AsArrayIndex(&index)) {
@@ -2192,9 +2222,16 @@
void JSObject::LocalLookup(String* name, LookupResult* result) {
ASSERT(name->IsString());
+ if (IsJSGlobalProxy()) {
+ Object* proto = GetPrototype();
+ if (proto->IsNull()) return result->NotFound();
+ ASSERT(proto->IsJSGlobalObject());
+ return JSObject::cast(proto)->LocalLookup(name, result);
+ }
+
// Do not use inline caching if the object is a non-global object
// that requires access checks.
- if (!IsJSGlobalObject() && IsAccessCheckNeeded()) {
+ if (!IsJSGlobalProxy() && IsAccessCheckNeeded()) {
result->DisallowCaching();
}
@@ -2281,6 +2318,21 @@
Object* JSObject::DefineAccessor(String* name, bool is_getter, JSFunction* fun,
PropertyAttributes attributes) {
+ // Check access rights if needed.
+ if (IsAccessCheckNeeded() &&
+ !Top::MayNamedAccess(this, name, v8::ACCESS_HAS)) {
+ Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
+ return Heap::undefined_value();
+ }
+
+ if (IsJSGlobalProxy()) {
+ Object* proto = GetPrototype();
+ if (proto->IsNull()) return this;
+ ASSERT(proto->IsJSGlobalObject());
+ return JSObject::cast(proto)->DefineAccessor(name, is_getter,
+ fun, attributes);
+ }
+
Object* array = DefineGetterSetter(name, attributes);
if (array->IsFailure() || array->IsUndefined()) return array;
FixedArray::cast(array)->set(is_getter ? 0 : 1, fun);
@@ -2365,7 +2417,7 @@
Object* Map::CopyDropTransitions() {
- Object *new_map = Copy();
+ Object* new_map = Copy();
if (new_map->IsFailure()) return new_map;
Object* descriptors = instance_descriptors()->RemoveTransitions();
if (descriptors->IsFailure()) return descriptors;
@@ -2544,21 +2596,6 @@
}
-Object* FixedArray::Copy() {
- int len = length();
- if (len == 0) return this;
- Object* obj = Heap::AllocateFixedArray(len);
- if (obj->IsFailure()) return obj;
- FixedArray* result = FixedArray::cast(obj);
- WriteBarrierMode mode = result->GetWriteBarrierMode();
- // Copy the content
- for (int i = 0; i < len; i++) {
- result->set(i, get(i), mode);
- }
- result->set_map(map());
- return result;
-}
-
Object* FixedArray::CopySize(int new_length) {
if (new_length == 0) return Heap::empty_fixed_array();
Object* obj = Heap::AllocateFixedArray(new_length);
@@ -2610,7 +2647,8 @@
if (array->IsFailure()) return array;
result->set(kContentArrayIndex, array);
result->set(kEnumerationIndexIndex,
- Smi::FromInt(PropertyDetails::kInitialIndex));
+ Smi::FromInt(PropertyDetails::kInitialIndex),
+ SKIP_WRITE_BARRIER);
return result;
}
@@ -2894,19 +2932,19 @@
StringRepresentationTag string_tag = representation_tag();
String* string = this;
if (string_tag == kSlicedStringTag) {
- SlicedString* sliced = SlicedString::cast(string);
- offset += sliced->start();
- string = String::cast(sliced->buffer());
- string_tag = string->representation_tag();
+ SlicedString* sliced = SlicedString::cast(string);
+ offset += sliced->start();
+ string = String::cast(sliced->buffer());
+ string_tag = string->representation_tag();
} else if (string_tag == kConsStringTag) {
- ConsString* cons = ConsString::cast(string);
- ASSERT(String::cast(cons->second())->length() == 0);
- string = String::cast(cons->first());
- string_tag = string->representation_tag();
+ ConsString* cons = ConsString::cast(string);
+ ASSERT(String::cast(cons->second())->length() == 0);
+ string = String::cast(cons->first());
+ string_tag = string->representation_tag();
}
if (string_tag == kSeqStringTag) {
SeqAsciiString* seq = SeqAsciiString::cast(string);
- char* start = reinterpret_cast<char*>(seq->GetCharsAddress());
+ char* start = seq->GetChars();
return Vector<const char>(start + offset, length);
}
ASSERT(string_tag == kExternalStringTag);
@@ -2925,20 +2963,19 @@
StringRepresentationTag string_tag = representation_tag();
String* string = this;
if (string_tag == kSlicedStringTag) {
- SlicedString* sliced = SlicedString::cast(string);
- offset += sliced->start();
- string = String::cast(sliced->buffer());
- string_tag = string->representation_tag();
+ SlicedString* sliced = SlicedString::cast(string);
+ offset += sliced->start();
+ string = String::cast(sliced->buffer());
+ string_tag = string->representation_tag();
} else if (string_tag == kConsStringTag) {
- ConsString* cons = ConsString::cast(string);
- ASSERT(String::cast(cons->second())->length() == 0);
- string = String::cast(cons->first());
- string_tag = string->representation_tag();
+ ConsString* cons = ConsString::cast(string);
+ ASSERT(String::cast(cons->second())->length() == 0);
+ string = String::cast(cons->first());
+ string_tag = string->representation_tag();
}
if (string_tag == kSeqStringTag) {
SeqTwoByteString* seq = SeqTwoByteString::cast(string);
- uc16* start = reinterpret_cast<uc16*>(seq->GetCharsAddress());
- return Vector<const uc16>(start + offset, length);
+ return Vector<const uc16>(seq->GetChars() + offset, length);
}
ASSERT(string_tag == kExternalStringTag);
ExternalTwoByteString* ext = ExternalTwoByteString::cast(string);
@@ -3106,7 +3143,6 @@
unsigned* remaining,
unsigned* offset_ptr,
unsigned max_chars) {
- // Cast const char* to unibrow::byte* (signedness difference).
const unibrow::byte* b = reinterpret_cast<unibrow::byte*>(this) -
kHeapObjectTag + kHeaderSize + *offset_ptr * kCharSize;
*remaining = max_chars;
@@ -3573,47 +3609,62 @@
}
-void String::Flatten(String* src,
- String* sink,
- int f,
- int t,
- int so) {
+template <typename sinkchar>
+void String::WriteToFlat(String* src,
+ sinkchar* sink,
+ int f,
+ int t) {
String* source = src;
int from = f;
int to = t;
- int sink_offset = so;
while (true) {
ASSERT(0 <= from && from <= to && to <= source->length());
- ASSERT(0 <= sink_offset && sink_offset < sink->length());
- switch (source->representation_tag()) {
- case kSeqStringTag:
- case kExternalStringTag: {
- Access<StringInputBuffer> buffer(&string_input_buffer);
- buffer->Reset(from, source);
- int j = sink_offset;
- for (int i = from; i < to; i++) {
- uc32 c = buffer->GetNext();
- sink->Set(j++, c);
- }
+ switch (source->full_representation_tag()) {
+ case kAsciiStringTag | kExternalStringTag: {
+ CopyChars(sink,
+ ExternalAsciiString::cast(source)->resource()->data() + from,
+ to - from);
return;
}
- case kSlicedStringTag: {
+ case kTwoByteStringTag | kExternalStringTag: {
+ const uc16* data =
+ ExternalTwoByteString::cast(source)->resource()->data();
+ CopyChars(sink,
+ data + from,
+ to - from);
+ return;
+ }
+ case kAsciiStringTag | kSeqStringTag: {
+ CopyChars(sink,
+ SeqAsciiString::cast(source)->GetChars() + from,
+ to - from);
+ return;
+ }
+ case kTwoByteStringTag | kSeqStringTag: {
+ CopyChars(sink,
+ SeqTwoByteString::cast(source)->GetChars() + from,
+ to - from);
+ return;
+ }
+ case kAsciiStringTag | kSlicedStringTag:
+ case kTwoByteStringTag | kSlicedStringTag: {
SlicedString* sliced_string = SlicedString::cast(source);
int start = sliced_string->start();
from += start;
to += start;
source = String::cast(sliced_string->buffer());
+ break;
}
- break;
- case kConsStringTag: {
+ case kAsciiStringTag | kConsStringTag:
+ case kTwoByteStringTag | kConsStringTag: {
ConsString* cons_string = ConsString::cast(source);
String* first = String::cast(cons_string->first());
int boundary = first->length();
if (to - boundary >= boundary - from) {
// Right hand side is longer. Recurse over left.
if (from < boundary) {
- Flatten(first, sink, from, boundary, sink_offset);
- sink_offset += boundary - from;
+ WriteToFlat(first, sink, from, boundary);
+ sink += boundary - from;
from = 0;
} else {
from -= boundary;
@@ -3621,22 +3672,19 @@
to -= boundary;
source = String::cast(cons_string->second());
} else {
- // Left hand side is longer. Recurse over right. The hasher
- // needs us to visit the string from left to right so doing
- // this invalidates that hash.
+ // Left hand side is longer. Recurse over right.
if (to > boundary) {
String* second = String::cast(cons_string->second());
- Flatten(second,
- sink,
- 0,
- to - boundary,
- sink_offset + boundary - from);
+ WriteToFlat(second,
+ sink + boundary - from,
+ 0,
+ to - boundary);
to = boundary;
}
source = first;
}
+ break;
}
- break;
}
}
}
@@ -3668,6 +3716,47 @@
}
+// Compares the contents of two strings by reading and comparing
+// int-sized blocks of characters.
+template <typename Char>
+static inline bool CompareRawStringContents(Vector<Char> a, Vector<Char> b) {
+ int length = a.length();
+ ASSERT_EQ(length, b.length());
+ const Char* pa = a.start();
+ const Char* pb = b.start();
+ int i = 0;
+#ifndef CAN_READ_UNALIGNED
+ // If this architecture isn't comfortable reading unaligned ints
+ // then we have to check that the strings are aligned before
+ // comparing them blockwise.
+ const int kAlignmentMask = sizeof(uint32_t) - 1; // NOLINT
+ uint32_t pa_addr = reinterpret_cast<uint32_t>(pa);
+ uint32_t pb_addr = reinterpret_cast<uint32_t>(pb);
+ if ((pa_addr & kAlignmentMask) | (pb_addr & kAlignmentMask) == 0) {
+#endif
+ const int kStepSize = sizeof(int) / sizeof(Char); // NOLINT
+ int endpoint = length - kStepSize;
+ // Compare blocks until we reach near the end of the string.
+ for (; i <= endpoint; i += kStepSize) {
+ uint32_t wa = *reinterpret_cast<const uint32_t*>(pa + i);
+ uint32_t wb = *reinterpret_cast<const uint32_t*>(pb + i);
+ if (wa != wb) {
+ return false;
+ }
+ }
+#ifndef CAN_READ_UNALIGNED
+ }
+#endif
+ // Compare the remaining characters that didn't fit into a block.
+ for (; i < length; i++) {
+ if (a[i] != b[i]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+
static StringInputBuffer string_compare_buffer_b;
@@ -3703,13 +3792,46 @@
if (Hash() != other->Hash()) return false;
}
+ if (this->IsSeqAsciiString() && other->IsSeqAsciiString()) {
+ const char* str1 = SeqAsciiString::cast(this)->GetChars();
+ const char* str2 = SeqAsciiString::cast(other)->GetChars();
+ return CompareRawStringContents(Vector<const char>(str1, len),
+ Vector<const char>(str2, len));
+ }
+
if (this->IsFlat()) {
if (this->IsAsciiRepresentation()) {
- VectorIterator<char> buf1(this->ToAsciiVector());
- return CompareStringContentsPartial(&buf1, other);
+ Vector<const char> vec1 = this->ToAsciiVector();
+ if (other->IsFlat()) {
+ if (other->IsAsciiRepresentation()) {
+ Vector<const char> vec2 = other->ToAsciiVector();
+ return CompareRawStringContents(vec1, vec2);
+ } else {
+ VectorIterator<char> buf1(vec1);
+ VectorIterator<uc16> ib(other->ToUC16Vector());
+ return CompareStringContents(&buf1, &ib);
+ }
+ } else {
+ VectorIterator<char> buf1(vec1);
+ string_compare_buffer_b.Reset(0, other);
+ return CompareStringContents(&buf1, &string_compare_buffer_b);
+ }
} else {
- VectorIterator<uc16> buf1(this->ToUC16Vector());
- return CompareStringContentsPartial(&buf1, other);
+ Vector<const uc16> vec1 = this->ToUC16Vector();
+ if (other->IsFlat()) {
+ if (other->IsAsciiRepresentation()) {
+ VectorIterator<uc16> buf1(vec1);
+ VectorIterator<char> ib(other->ToAsciiVector());
+ return CompareStringContents(&buf1, &ib);
+ } else {
+ Vector<const uc16> vec2(other->ToUC16Vector());
+ return CompareRawStringContents(vec1, vec2);
+ }
+ } else {
+ VectorIterator<uc16> buf1(vec1);
+ string_compare_buffer_b.Reset(0, other);
+ return CompareStringContents(&buf1, &string_compare_buffer_b);
+ }
}
} else {
string_compare_buffer_a.Reset(0, this);
@@ -3779,7 +3901,7 @@
bool String::ComputeArrayIndex(unibrow::CharacterStream* buffer,
uint32_t* index,
int length) {
- if (length == 0) return false;
+ if (length == 0 || length > kMaxArrayIndexSize) return false;
uc32 ch = buffer->GetNext();
// If the string begins with a '0' character, it must only consist
@@ -3807,13 +3929,24 @@
bool String::SlowAsArrayIndex(uint32_t* index) {
- StringInputBuffer buffer(this);
- return ComputeArrayIndex(&buffer, index, length());
+ if (length() <= kMaxCachedArrayIndexLength) {
+ Hash(); // force computation of hash code
+ uint32_t field = length_field();
+ if ((field & kIsArrayIndexMask) == 0) return false;
+ *index = (field & ((1 << kShortLengthShift) - 1)) >> kLongLengthShift;
+ return true;
+ } else {
+ StringInputBuffer buffer(this);
+ return ComputeArrayIndex(&buffer, index, length());
+ }
}
static inline uint32_t HashField(uint32_t hash, bool is_array_index) {
- return (hash << String::kLongLengthShift) | (is_array_index ? 3 : 1);
+ uint32_t result =
+ (hash << String::kLongLengthShift) | String::kHashComputedMask;
+ if (is_array_index) result |= String::kIsArrayIndexMask;
+ return result;
}
@@ -3842,18 +3975,21 @@
// Very long strings have a trivial hash that doesn't inspect the
// string contents.
- if (hasher.has_trivial_hash())
+ if (hasher.has_trivial_hash()) {
return hasher.GetHashField();
+ }
// Do the iterative array index computation as long as there is a
// chance this is an array index.
- while (buffer->has_more() && hasher.is_array_index())
+ while (buffer->has_more() && hasher.is_array_index()) {
hasher.AddCharacter(buffer->GetNext());
+ }
// Process the remaining characters without updating the array
// index.
- while (buffer->has_more())
+ while (buffer->has_more()) {
hasher.AddCharacterNoIndex(buffer->GetNext());
+ }
return hasher.GetHashField();
}
@@ -4316,7 +4452,7 @@
uint32_t len = static_cast<uint32_t>(elems->length());
for (uint32_t i = 0; i < len; i++) ASSERT(elems->get(i)->IsTheHole());
#endif
- FixedArray::WriteBarrierMode mode = elems->GetWriteBarrierMode();
+ WriteBarrierMode mode = elems->GetWriteBarrierMode();
if (HasFastElements()) {
FixedArray* old_elements = FixedArray::cast(elements());
uint32_t old_length = static_cast<uint32_t>(old_elements->length());
@@ -4365,7 +4501,7 @@
Object* JSArray::Initialize(int capacity) {
ASSERT(capacity >= 0);
- set_length(Smi::FromInt(0));
+ set_length(Smi::FromInt(0), SKIP_WRITE_BARRIER);
FixedArray* new_elements;
if (capacity == 0) {
new_elements = Heap::empty_fixed_array();
@@ -4409,7 +4545,7 @@
for (int i = value; i < old_length; i++) {
FixedArray::cast(elements())->set_the_hole(i);
}
- JSArray::cast(this)->set_length(smi_length);
+ JSArray::cast(this)->set_length(smi_length, SKIP_WRITE_BARRIER);
}
return this;
}
@@ -4419,7 +4555,8 @@
!ShouldConvertToSlowElements(new_capacity)) {
Object* obj = Heap::AllocateFixedArrayWithHoles(new_capacity);
if (obj->IsFailure()) return obj;
- if (IsJSArray()) JSArray::cast(this)->set_length(smi_length);
+ if (IsJSArray()) JSArray::cast(this)->set_length(smi_length,
+ SKIP_WRITE_BARRIER);
SetFastElements(FixedArray::cast(obj));
return this;
}
@@ -4436,7 +4573,7 @@
static_cast<uint32_t>(JSArray::cast(this)->length()->Number());
element_dictionary()->RemoveNumberEntries(value, old_length);
}
- JSArray::cast(this)->set_length(smi_length);
+ JSArray::cast(this)->set_length(smi_length, SKIP_WRITE_BARRIER);
}
return this;
}
@@ -4457,7 +4594,8 @@
Object* obj = Heap::AllocateFixedArray(1);
if (obj->IsFailure()) return obj;
FixedArray::cast(obj)->set(0, len);
- if (IsJSArray()) JSArray::cast(this)->set_length(Smi::FromInt(1));
+ if (IsJSArray()) JSArray::cast(this)->set_length(Smi::FromInt(1),
+ SKIP_WRITE_BARRIER);
set_elements(FixedArray::cast(obj));
return this;
}
@@ -4658,7 +4796,8 @@
CHECK(Array::IndexFromObject(JSArray::cast(this)->length(),
&array_length));
if (index >= array_length) {
- JSArray::cast(this)->set_length(Smi::FromInt(index + 1));
+ JSArray::cast(this)->set_length(Smi::FromInt(index + 1),
+ SKIP_WRITE_BARRIER);
}
}
return value;
@@ -4674,7 +4813,8 @@
Object* obj = Heap::AllocateFixedArrayWithHoles(new_capacity);
if (obj->IsFailure()) return obj;
SetFastElements(FixedArray::cast(obj));
- if (IsJSArray()) JSArray::cast(this)->set_length(Smi::FromInt(index + 1));
+ if (IsJSArray()) JSArray::cast(this)->set_length(Smi::FromInt(index + 1),
+ SKIP_WRITE_BARRIER);
FixedArray::cast(elements())->set(index, value);
return value;
}
@@ -4695,6 +4835,13 @@
return value;
}
+ if (IsJSGlobalProxy()) {
+ Object* proto = GetPrototype();
+ if (proto->IsNull()) return value;
+ ASSERT(proto->IsJSGlobalObject());
+ return JSObject::cast(proto)->SetElement(index, value);
+ }
+
// Check for lookup interceptor
if (HasIndexedInterceptor()) {
return SetElementWithInterceptor(index, value);
@@ -4949,7 +5096,7 @@
pos++;
}
}
- set_length(Smi::FromInt(pos));
+ set_length(Smi::FromInt(pos), SKIP_WRITE_BARRIER);
for (int index = pos; index < len; index++) {
elms->set_the_hole(index);
}
@@ -4965,7 +5112,7 @@
Object* obj = Heap::AllocateFixedArray(length);
if (obj->IsFailure()) return obj;
dict->CopyValuesTo(FixedArray::cast(obj));
- set_length(Smi::FromInt(length));
+ set_length(Smi::FromInt(length), SKIP_WRITE_BARRIER);
set_elements(FixedArray::cast(obj));
return this;
}
@@ -4973,7 +5120,7 @@
// Make another dictionary with smaller indices.
Object* obj = dict->RemoveHoles();
if (obj->IsFailure()) return obj;
- set_length(Smi::FromInt(length));
+ set_length(Smi::FromInt(length), SKIP_WRITE_BARRIER);
set_elements(Dictionary::cast(obj));
return this;
}
@@ -5301,9 +5448,7 @@
for (int i = 0; i < length; i++) {
if (!FixedArray::cast(elements())->get(i)->IsTheHole()) {
if (storage) {
- storage->set(counter,
- Smi::FromInt(i),
- FixedArray::SKIP_WRITE_BARRIER);
+ storage->set(counter, Smi::FromInt(i), SKIP_WRITE_BARRIER);
}
counter++;
}
@@ -5322,9 +5467,7 @@
String* str = String::cast(val);
if (storage) {
for (int i = 0; i < str->length(); i++) {
- storage->set(counter + i,
- Smi::FromInt(i),
- FixedArray::SKIP_WRITE_BARRIER);
+ storage->set(counter + i, Smi::FromInt(i), SKIP_WRITE_BARRIER);
}
}
counter += str->length();
@@ -5345,13 +5488,11 @@
// This avoids allocation in HasProperty.
class NumberKey : public HashTableKey {
public:
- explicit NumberKey(uint32_t number) {
- number_ = number;
- }
+ explicit NumberKey(uint32_t number) : number_(number) { }
private:
- bool IsMatch(Object* other) {
- return number_ == ToUint32(other);
+ bool IsMatch(Object* number) {
+ return number_ == ToUint32(number);
}
// Thomas Wang, Integer Hash Functions.
@@ -5393,13 +5534,10 @@
// StringKey simply carries a string object as key.
class StringKey : public HashTableKey {
public:
- explicit StringKey(String* string) {
- string_ = string;
- }
+ explicit StringKey(String* string) : string_(string) { }
- bool IsMatch(Object* other) {
- if (!other->IsString()) return false;
- return string_->Equals(String::cast(other));
+ bool IsMatch(Object* string) {
+ return string_->Equals(String::cast(string));
}
uint32_t Hash() { return StringHash(string_); }
@@ -5423,9 +5561,8 @@
explicit Utf8SymbolKey(Vector<const char> string)
: string_(string), length_field_(0) { }
- bool IsMatch(Object* other) {
- if (!other->IsString()) return false;
- return String::cast(other)->IsEqualTo(string_);
+ bool IsMatch(Object* string) {
+ return String::cast(string)->IsEqualTo(string_);
}
HashFunction GetHashFunction() {
@@ -5469,9 +5606,8 @@
return StringHash;
}
- bool IsMatch(Object* other) {
- if (!other->IsString()) return false;
- return String::cast(other)->Equals(string_);
+ bool IsMatch(Object* string) {
+ return String::cast(string)->Equals(string_);
}
uint32_t Hash() { return string_->Hash(); }
@@ -5688,11 +5824,8 @@
Object* CompilationCacheTable::Lookup(String* src) {
StringKey key(src);
int entry = FindEntry(&key);
- if (entry != -1) {
- return get(EntryToIndex(entry) + 1);
- } else {
- return Heap::undefined_value();
- }
+ if (entry == -1) return Heap::undefined_value();
+ return get(EntryToIndex(entry) + 1);
}
@@ -5714,13 +5847,10 @@
// SymbolsKey used for HashTable where key is array of symbols.
class SymbolsKey : public HashTableKey {
public:
- explicit SymbolsKey(FixedArray* symbols) {
- symbols_ = symbols;
- }
+ explicit SymbolsKey(FixedArray* symbols) : symbols_(symbols) { }
- bool IsMatch(Object* other) {
- if (!other->IsFixedArray()) return false;
- FixedArray* o = FixedArray::cast(other);
+ bool IsMatch(Object* symbols) {
+ FixedArray* o = FixedArray::cast(symbols);
int len = symbols_->length();
if (o->length() != len) return false;
for (int i = 0; i < len; i++) {
@@ -5736,28 +5866,78 @@
Object* GetObject() { return symbols_; }
static uint32_t SymbolsHash(Object* obj) {
- FixedArray* symbols_ = FixedArray::cast(obj);
- int len = symbols_->length();
- uint32_t hash = 0;
+ FixedArray* symbols = FixedArray::cast(obj);
+ int len = symbols->length();
+ uint32_t hash = 0;
for (int i = 0; i < len; i++) {
- hash ^= String::cast(symbols_->get(i))->Hash();
+ hash ^= String::cast(symbols->get(i))->Hash();
}
return hash;
}
bool IsStringKey() { return false; }
+ private:
FixedArray* symbols_;
};
+
+// MapNameKeys are used as keys in lookup caches.
+class MapNameKey : public HashTableKey {
+ public:
+ MapNameKey(Map* map, String* name)
+ : map_(map), name_(name) { }
+
+ bool IsMatch(Object* other) {
+ if (!other->IsFixedArray()) return false;
+ FixedArray* pair = FixedArray::cast(other);
+ Map* map = Map::cast(pair->get(0));
+ if (map != map_) return false;
+ String* name = String::cast(pair->get(1));
+ return name->Equals(name_);
+ }
+
+ typedef uint32_t (*HashFunction)(Object* obj);
+
+ virtual HashFunction GetHashFunction() { return MapNameHash; }
+
+ static uint32_t MapNameHashHelper(Map* map, String* name) {
+ return reinterpret_cast<uint32_t>(map) ^ name->Hash();
+ }
+
+ static uint32_t MapNameHash(Object* obj) {
+ FixedArray* pair = FixedArray::cast(obj);
+ Map* map = Map::cast(pair->get(0));
+ String* name = String::cast(pair->get(1));
+ return MapNameHashHelper(map, name);
+ }
+
+ virtual uint32_t Hash() {
+ return MapNameHashHelper(map_, name_);
+ }
+
+ virtual Object* GetObject() {
+ Object* obj = Heap::AllocateFixedArray(2);
+ if (obj->IsFailure()) return obj;
+ FixedArray* pair = FixedArray::cast(obj);
+ pair->set(0, map_);
+ pair->set(1, name_);
+ return pair;
+ }
+
+ virtual bool IsStringKey() { return false; }
+
+ private:
+ Map* map_;
+ String* name_;
+};
+
+
Object* MapCache::Lookup(FixedArray* array) {
SymbolsKey key(array);
int entry = FindEntry(&key);
- if (entry != -1) {
- return get(EntryToIndex(entry) + 1);
- } else {
- return Heap::undefined_value();
- }
+ if (entry == -1) return Heap::undefined_value();
+ return get(EntryToIndex(entry) + 1);
}
@@ -5775,6 +5955,31 @@
}
+int LookupCache::Lookup(Map* map, String* name) {
+ MapNameKey key(map, name);
+ int entry = FindEntry(&key);
+ if (entry == -1) return kNotFound;
+ return Smi::cast(get(EntryToIndex(entry) + 1))->value();
+}
+
+
+Object* LookupCache::Put(Map* map, String* name, int value) {
+ MapNameKey key(map, name);
+ Object* obj = EnsureCapacity(1, &key);
+ if (obj->IsFailure()) return obj;
+ Object* k = key.GetObject();
+ if (k->IsFailure()) return k;
+
+ LookupCache* cache = reinterpret_cast<LookupCache*>(obj);
+ int entry = cache->FindInsertionEntry(k, key.Hash());
+ int index = EntryToIndex(entry);
+ cache->set(index, k);
+ cache->set(index + 1, Smi::FromInt(value));
+ cache->ElementAdded();
+ return cache;
+}
+
+
Object* Dictionary::Allocate(int at_least_space_for) {
Object* obj = DictionaryBase::Allocate(at_least_space_for);
// Initialize the next enumeration index.
@@ -5785,6 +5990,7 @@
return obj;
}
+
Object* Dictionary::GenerateNewEnumerationIndices() {
int length = NumberOfElements();
@@ -5792,7 +5998,9 @@
Object* obj = Heap::AllocateFixedArray(length);
if (obj->IsFailure()) return obj;
FixedArray* iteration_order = FixedArray::cast(obj);
- for (int i = 0; i < length; i++) iteration_order->set(i, Smi::FromInt(i));
+ for (int i = 0; i < length; i++) {
+ iteration_order->set(i, Smi::FromInt(i), SKIP_WRITE_BARRIER);
+ }
// Allocate array with enumeration order.
obj = Heap::AllocateFixedArray(length);
@@ -5804,7 +6012,9 @@
int pos = 0;
for (int i = 0; i < capacity; i++) {
if (IsKey(KeyAt(i))) {
- enumeration_order->set(pos++, Smi::FromInt(DetailsAt(i).index()));
+ enumeration_order->set(pos++,
+ Smi::FromInt(DetailsAt(i).index()),
+ SKIP_WRITE_BARRIER);
}
}
@@ -5815,7 +6025,9 @@
for (int i = 0; i < length; i++) {
int index = Smi::cast(iteration_order->get(i))->value();
int enum_index = PropertyDetails::kInitialIndex + i;
- enumeration_order->set(index, Smi::FromInt(enum_index));
+ enumeration_order->set(index,
+ Smi::FromInt(enum_index),
+ SKIP_WRITE_BARRIER);
}
// Update the dictionary with new indices.
@@ -5953,13 +6165,17 @@
// Check if this index is high enough that we should require slow
// elements.
if (key > kRequiresSlowElementsLimit) {
- set(kPrefixStartIndex, Smi::FromInt(kRequiresSlowElementsMask));
+ set(kMaxNumberKeyIndex,
+ Smi::FromInt(kRequiresSlowElementsMask),
+ SKIP_WRITE_BARRIER);
return;
}
// Update max key value.
- Object* max_index_object = get(kPrefixStartIndex);
+ Object* max_index_object = get(kMaxNumberKeyIndex);
if (!max_index_object->IsSmi() || max_number_key() < key) {
- set(kPrefixStartIndex, Smi::FromInt(key << kRequiresSlowElementsTagSize));
+ set(kMaxNumberKeyIndex,
+ Smi::FromInt(key << kRequiresSlowElementsTagSize),
+ SKIP_WRITE_BARRIER);
}
}
@@ -6056,7 +6272,9 @@
PropertyDetails details = DetailsAt(i);
if (!details.IsDontEnum()) {
storage->set(index, k);
- sort_array->set(index, Smi::FromInt(details.index()));
+ sort_array->set(index,
+ Smi::FromInt(details.index()),
+ SKIP_WRITE_BARRIER);
index++;
}
}
diff --git a/src/objects.h b/src/objects.h
index 3f0150e..e3bd522 100644
--- a/src/objects.h
+++ b/src/objects.h
@@ -48,6 +48,7 @@
// - GlobalObject
// - JSGlobalObject
// - JSBuiltinsObject
+// _ JSGlobalProxy
// - JSValue
// - Script
// - Array
@@ -57,6 +58,9 @@
// - HashTable
// - Dictionary
// - SymbolTable
+// - CompilationCacheTable
+// - MapCache
+// - LookupCache
// - Context
// - GlobalContext
// - String
@@ -162,6 +166,9 @@
uint32_t value_;
};
+// Setter that skips the write barrier if mode is SKIP_WRITE_BARRIER.
+enum WriteBarrierMode { SKIP_WRITE_BARRIER, UPDATE_WRITE_BARRIER };
+
// All Maps have a field instance_type containing a InstanceType.
// It describes the type of the instances.
//
@@ -263,6 +270,7 @@
V(JS_OBJECT_TYPE) \
V(JS_GLOBAL_OBJECT_TYPE) \
V(JS_BUILTINS_OBJECT_TYPE) \
+ V(JS_GLOBAL_PROXY_TYPE) \
V(JS_ARRAY_TYPE) \
V(JS_REGEXP_TYPE) \
\
@@ -518,6 +526,7 @@
JS_OBJECT_TYPE,
JS_GLOBAL_OBJECT_TYPE,
JS_BUILTINS_OBJECT_TYPE,
+ JS_GLOBAL_PROXY_TYPE,
JS_ARRAY_TYPE,
JS_REGEXP_TYPE,
@@ -549,9 +558,10 @@
inline void set_##name(bool value); \
-#define DECL_ACCESSORS(name, type) \
- inline type* name(); \
- inline void set_##name(type* value);
+#define DECL_ACCESSORS(name, type) \
+ inline type* name(); \
+ inline void set_##name(type* value, \
+ WriteBarrierMode mode = UPDATE_WRITE_BARRIER); \
class StringStream;
@@ -623,10 +633,12 @@
inline bool IsSymbolTable();
inline bool IsCompilationCacheTable();
inline bool IsMapCache();
+ inline bool IsLookupCache();
inline bool IsPrimitive();
inline bool IsGlobalObject();
inline bool IsJSGlobalObject();
inline bool IsJSBuiltinsObject();
+ inline bool IsJSGlobalProxy();
inline bool IsUndetectableObject();
inline bool IsAccessCheckNeeded();
@@ -649,6 +661,8 @@
// Extract the number.
inline double Number();
+ inline bool HasSpecificClassOf(String* name);
+
Object* ToObject(); // ECMA-262 9.9.
Object* ToBoolean(); // ECMA-262 9.2.
@@ -808,6 +822,7 @@
inline bool IsOutOfMemoryException() const;
static Failure* RetryAfterGC(int requested_bytes, AllocationSpace space);
+ static inline Failure* RetryAfterGC(int requested_bytes); // NEW_SPACE
static inline Failure* Exception();
static inline Failure* InternalError();
static inline Failure* OutOfMemoryException();
@@ -992,10 +1007,6 @@
// of this struct.
void IterateStructBody(int object_size, ObjectVisitor* v);
- // Copy the body from the 'from' object to this.
- // Please note the two object must have the same map prior to the call.
- inline void CopyBody(JSObject* from);
-
// Returns the heap object's size in bytes
inline int Size();
@@ -1034,6 +1045,9 @@
// Casting.
static inline HeapObject* cast(Object* obj);
+ // Return the write barrier mode for this.
+ inline WriteBarrierMode GetWriteBarrierMode();
+
// Dispatched behavior.
void HeapObjectShortPrint(StringStream* accumulator);
#ifdef DEBUG
@@ -1114,7 +1128,7 @@
// [elements]: The elements (properties with names that are integers).
// elements is a FixedArray in the fast case, and a Dictionary in the slow
// case.
- DECL_ACCESSORS(elements, HeapObject) // Get and set fast elements.
+ DECL_ACCESSORS(elements, FixedArray) // Get and set fast elements.
inline void initialize_elements();
inline bool HasFastElements();
inline Dictionary* element_dictionary(); // Gets slow elements.
@@ -1249,11 +1263,6 @@
inline Object* GetInternalField(int index);
inline void SetInternalField(int index, Object* value);
- // Returns a deep copy of the JavaScript object.
- // Properties and elements are copied too.
- // Returns failure if allocation failed.
- Object* Copy(PretenureFlag pretenure = NOT_TENURED);
-
// Lookup a property. If found, the result is valid and has
// detailed information.
void LocalLookup(String* name, LookupResult* result);
@@ -1353,6 +1362,11 @@
inline Object* FastPropertyAt(int index);
inline Object* FastPropertyAtPut(int index, Object* value);
+ // Access to set in object properties.
+ inline Object* InObjectPropertyAtPut(int index,
+ Object* value,
+ WriteBarrierMode mode
+ = UPDATE_WRITE_BARRIER);
// initializes the body after properties slot, properties slot is
// initialized by set_properties
@@ -1477,19 +1491,16 @@
inline Object* get(int index);
inline void set(int index, Object* value);
+ // Setter with barrier mode.
+ inline void set(int index, Object* value, WriteBarrierMode mode);
+
// Setters for frequently used oddballs located in old space.
inline void set_undefined(int index);
inline void set_null(int index);
inline void set_the_hole(int index);
- // Setter that skips the write barrier if mode is SKIP_WRITE_BARRIER.
- enum WriteBarrierMode { SKIP_WRITE_BARRIER, UPDATE_WRITE_BARRIER };
- inline void set(int index, Object* value, WriteBarrierMode mode);
- // Return the write barrier mode for this.
- inline WriteBarrierMode GetWriteBarrierMode();
-
// Copy operations.
- Object* Copy();
+ inline Object* Copy();
Object* CopySize(int new_length);
// Add the elements of a JSArray to this FixedArray.
@@ -1874,6 +1885,27 @@
};
+// LookupCache.
+//
+// Maps a key consisting of a map and a name to an index within a
+// fast-case properties array.
+//
+// LookupCaches are used to avoid repeatedly searching instance
+// descriptors.
+class LookupCache: public HashTable<0, 2> {
+ public:
+ int Lookup(Map* map, String* name);
+ Object* Put(Map* map, String* name, int offset);
+ static inline LookupCache* cast(Object* obj);
+
+ // Constant returned by Lookup when the key was not found.
+ static const int kNotFound = -1;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(LookupCache);
+};
+
+
// Dictionary for keeping properties and elements in slow case.
//
// One element in the prefix is used for storing non-element
@@ -1970,11 +2002,11 @@
// Accessors for next enumeration index.
void SetNextEnumerationIndex(int index) {
- fast_set(this, kNextEnumnerationIndexIndex, Smi::FromInt(index));
+ fast_set(this, kNextEnumerationIndexIndex, Smi::FromInt(index));
}
int NextEnumerationIndex() {
- return Smi::cast(get(kNextEnumnerationIndexIndex))->value();
+ return Smi::cast(get(kNextEnumerationIndexIndex))->value();
}
// Returns a new array for dictionary usage. Might return Failure.
@@ -2018,7 +2050,7 @@
Object* GenerateNewEnumerationIndices();
static const int kMaxNumberKeyIndex = kPrefixStartIndex;
- static const int kNextEnumnerationIndexIndex = kMaxNumberKeyIndex + 1;
+ static const int kNextEnumerationIndexIndex = kMaxNumberKeyIndex + 1;
DISALLOW_IMPLICIT_CONSTRUCTORS(Dictionary);
};
@@ -2338,12 +2370,12 @@
// Tells whether the instance needs security checks when accessing its
// properties.
- inline void set_needs_access_check() {
- set_bit_field(bit_field() | (1 << kNeedsAccessCheck));
+ inline void set_is_access_check_needed() {
+ set_bit_field(bit_field() | (1 << kIsAccessCheckNeeded));
}
- inline bool needs_access_check() {
- return ((1 << kNeedsAccessCheck) & bit_field()) != 0;
+ inline bool is_access_check_needed() {
+ return ((1 << kIsAccessCheckNeeded) & bit_field()) != 0;
}
// [prototype]: implicit prototype object.
@@ -2435,7 +2467,7 @@
static const int kHasIndexedInterceptor = 4;
static const int kIsUndetectable = 5;
static const int kHasInstanceCallHandler = 6;
- static const int kNeedsAccessCheck = 7;
+ static const int kIsAccessCheckNeeded = 7;
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(Map);
};
@@ -2739,6 +2771,39 @@
};
+// JSGlobalProxy's prototype must be a JSGlobalObject or null,
+// and the prototype is hidden. JSGlobalProxy always delegates
+// property accesses to its prototype if the prototype is not null.
+//
+// A JSGlobalProxy can be reinitialized which will preserve its identity.
+//
+// Accessing a JSGlobalProxy requires security check.
+
+class JSGlobalProxy : public JSObject {
+ public:
+ // [context]: the owner global context of this proxy object.
+ // It is null value if this object is not used by any context.
+ DECL_ACCESSORS(context, Object)
+
+ // Casting.
+ static inline JSGlobalProxy* cast(Object* obj);
+
+ // Dispatched behavior.
+#ifdef DEBUG
+ void JSGlobalProxyPrint();
+ void JSGlobalProxyVerify();
+#endif
+
+ // Layout description.
+ static const int kContextOffset = JSObject::kHeaderSize;
+ static const int kSize = kContextOffset + kPointerSize;
+
+ private:
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(JSGlobalProxy);
+};
+
+
// Forward declaration.
class JSBuiltinsObject;
@@ -2752,10 +2817,17 @@
// [global context]: the global context corresponding to this global objet.
DECL_ACCESSORS(global_context, Context)
+ // [global receiver]: the global receiver object of the context
+ DECL_ACCESSORS(global_receiver, JSObject)
+
+ // Casting.
+ static inline GlobalObject* cast(Object* obj);
+
// Layout description.
static const int kBuiltinsOffset = JSObject::kHeaderSize;
static const int kGlobalContextOffset = kBuiltinsOffset + kPointerSize;
- static const int kHeaderSize = kGlobalContextOffset + kPointerSize;
+ static const int kGlobalReceiverOffset = kGlobalContextOffset + kPointerSize;
+ static const int kHeaderSize = kGlobalReceiverOffset + kPointerSize;
private:
friend class AGCCVersionRequiresThisClassToHaveAFriendSoHereItIs;
@@ -2767,10 +2839,6 @@
// JavaScript global object.
class JSGlobalObject: public GlobalObject {
public:
- // [security token]: the object being used for security check when accessing
- // global properties.
- DECL_ACCESSORS(security_token, Object)
-
// Casting.
static inline JSGlobalObject* cast(Object* obj);
@@ -2781,8 +2849,7 @@
#endif
// Layout description.
- static const int kSecurityTokenOffset = GlobalObject::kHeaderSize;
- static const int kSize = kSecurityTokenOffset + kPointerSize;
+ static const int kSize = GlobalObject::kHeaderSize;
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(JSGlobalObject);
@@ -2956,6 +3023,10 @@
// Is this string an ascii string.
inline bool IsAsciiRepresentation();
+ // Specialization of this function from Object that skips the
+ // string check.
+ inline bool IsSeqAsciiString();
+
// Fast testing routines that assume the receiver is a string and
// just check whether it is a certain kind of string.
inline bool StringIsSlicedString();
@@ -3039,6 +3110,8 @@
// Get the representation tag.
inline StringRepresentationTag representation_tag();
+ // Get the representation and ASCII tag.
+ inline int full_representation_tag();
static inline StringRepresentationTag map_representation_tag(Map* map);
// For use during stack traces. Performs rudimentary sanity check.
@@ -3063,7 +3136,10 @@
static const int kMaxArrayIndexSize = 10;
// Max ascii char code.
- static const int kMaxAsciiCharCode = 127;
+ static const int kMaxAsciiCharCode = unibrow::Utf8::kMaxOneByteChar;
+
+ // Minimum length for a cons or sliced string.
+ static const int kMinNonFlatLength = 13;
// Mask constant for checking if a string has a computed hash code
// and if it is an array index. The least significant bit indicates
@@ -3074,6 +3150,10 @@
static const int kIsArrayIndexMask = 1 << 1;
static const int kNofLengthBitFields = 2;
+ // Array index strings this short can keep their index in the hash
+ // field.
+ static const int kMaxCachedArrayIndexLength = 6;
+
// Shift constants for retriving length and hash code from
// length/hash field.
static const int kHashShift = kNofLengthBitFields;
@@ -3081,7 +3161,6 @@
static const int kMediumLengthShift = 2 * kBitsPerByte;
static const int kLongLengthShift = kHashShift;
-
// Limit for truncation in short printing.
static const int kMaxShortPrintLength = 1024;
@@ -3102,11 +3181,11 @@
unsigned* offset);
// Helper function for flattening strings.
- static void Flatten(String* source,
- String* sink,
- int from,
- int to,
- int sink_offset);
+ template <typename sinkchar>
+ static void WriteToFlat(String* source,
+ sinkchar* sink,
+ int from,
+ int to);
protected:
class ReadBlockBuffer {
@@ -3183,6 +3262,8 @@
// Get the address of the characters in this string.
inline Address GetCharsAddress();
+ inline char* GetChars();
+
// Casting
static inline SeqAsciiString* cast(Object* obj);
@@ -3223,6 +3304,8 @@
// Get the address of the characters in this string.
inline Address GetCharsAddress();
+ inline uc16* GetChars();
+
// For regexp code.
const uint16_t* SeqTwoByteStringGetData(unsigned start);
@@ -3294,8 +3377,7 @@
unsigned* offset_ptr,
unsigned chars);
-
- // Minimum lenth for a cons string.
+ // Minimum length for a cons string.
static const int kMinLength = 13;
private:
@@ -3342,9 +3424,6 @@
unsigned* offset_ptr,
unsigned chars);
- // Minimum lenth for a sliced string.
- static const int kMinLength = 13;
-
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(SlicedString);
};
diff --git a/src/parser.cc b/src/parser.cc
index d310979..9090dbb 100644
--- a/src/parser.cc
+++ b/src/parser.cc
@@ -2921,7 +2921,7 @@
entry.set_contains_array_literal(contains_array_literal);
}
- FunctionLiteral *function_literal =
+ FunctionLiteral* function_literal =
NEW(FunctionLiteral(name, top_scope_,
body.elements(), materialized_literal_count,
contains_array_literal, expected_property_count,
diff --git a/src/runtime.cc b/src/runtime.cc
index 8e329b0..32246f3 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -94,14 +94,14 @@
static Object* Runtime_CloneObjectLiteralBoilerplate(Arguments args) {
CONVERT_CHECKED(JSObject, boilerplate, args[0]);
- return boilerplate->Copy();
+ return Heap::CopyJSObject(boilerplate);
}
static Handle<Map> ComputeObjectLiteralMap(
Handle<Context> context,
Handle<FixedArray> constant_properties,
- bool &is_result_from_cache) {
+ bool* is_result_from_cache) {
if (FLAG_canonicalize_object_literal_maps) {
// First find prefix of consecutive symbol keys.
int number_of_properties = constant_properties->length()/2;
@@ -113,18 +113,18 @@
// Based on the number of prefix symbols key we decide whether
// to use the map cache in the global context.
const int kMaxKeys = 10;
- if ((number_of_symbol_keys == number_of_properties)
- && (number_of_symbol_keys < kMaxKeys)) {
+ if ((number_of_symbol_keys == number_of_properties) &&
+ (number_of_symbol_keys < kMaxKeys)) {
// Create the fixed array with the key.
Handle<FixedArray> keys = Factory::NewFixedArray(number_of_symbol_keys);
for (int i = 0; i < number_of_symbol_keys; i++) {
keys->set(i, constant_properties->get(i*2));
}
- is_result_from_cache = true;
+ *is_result_from_cache = true;
return Factory::ObjectLiteralMapFromCache(context, keys);
}
}
- is_result_from_cache = false;
+ *is_result_from_cache = false;
return Handle<Map>(context->object_function()->initial_map());
}
@@ -149,7 +149,7 @@
bool is_result_from_cache;
Handle<Map> map = ComputeObjectLiteralMap(context,
constant_properties,
- is_result_from_cache);
+ &is_result_from_cache);
Handle<JSObject> boilerplate = Factory::NewJSObjectFromMap(map);
{ // Add the constant propeties to the boilerplate.
@@ -221,28 +221,46 @@
return JSObject::cast(obj)->class_name();
}
-inline static Object* IsSpecificClassOf(Arguments args, String* name) {
- NoHandleAllocation ha;
- ASSERT(args.length() == 1);
- Object* obj = args[0];
- if (obj->IsJSObject() && (JSObject::cast(obj)->class_name() == name)) {
- return Heap::true_value();
- }
- return Heap::false_value();
-}
-static Object* Runtime_IsStringClass(Arguments args) {
- return IsSpecificClassOf(args, Heap::String_symbol());
+static Object* Runtime_HasStringClass(Arguments args) {
+ return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::String_symbol()));
}
-static Object* Runtime_IsDateClass(Arguments args) {
- return IsSpecificClassOf(args, Heap::Date_symbol());
+static Object* Runtime_HasDateClass(Arguments args) {
+ return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::Date_symbol()));
}
-static Object* Runtime_IsArrayClass(Arguments args) {
- return IsSpecificClassOf(args, Heap::Array_symbol());
+static Object* Runtime_HasArrayClass(Arguments args) {
+ return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::Array_symbol()));
+}
+
+
+static Object* Runtime_HasFunctionClass(Arguments args) {
+ return Heap::ToBoolean(
+ args[0]->HasSpecificClassOf(Heap::function_class_symbol()));
+}
+
+
+static Object* Runtime_HasNumberClass(Arguments args) {
+ return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::Number_symbol()));
+}
+
+
+static Object* Runtime_HasBooleanClass(Arguments args) {
+ return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::Boolean_symbol()));
+}
+
+
+static Object* Runtime_HasArgumentsClass(Arguments args) {
+ return Heap::ToBoolean(
+ args[0]->HasSpecificClassOf(Heap::Arguments_symbol()));
+}
+
+
+static Object* Runtime_HasRegExpClass(Arguments args) {
+ return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::RegExp_symbol()));
}
@@ -449,7 +467,7 @@
int index;
PropertyAttributes attributes;
ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
- Handle<Object> context_obj =
+ Handle<Object> holder =
context->Lookup(name, flags, &index, &attributes);
if (attributes != ABSENT) {
@@ -469,14 +487,14 @@
// The variable or constant context slot should always be in
// the function context; not in any outer context nor in the
// arguments object.
- ASSERT(context_obj.is_identical_to(context));
+ ASSERT(holder.is_identical_to(context));
if (((attributes & READ_ONLY) == 0) ||
context->get(index)->IsTheHole()) {
context->set(index, *initial_value);
}
} else {
// Slow case: The property is not in the FixedArray part of the context.
- Handle<JSObject> context_ext = Handle<JSObject>::cast(context_obj);
+ Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
SetProperty(context_ext, name, initial_value, mode);
}
}
@@ -677,7 +695,7 @@
int index;
PropertyAttributes attributes;
ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
- Handle<Object> context_obj =
+ Handle<Object> holder =
context->Lookup(name, flags, &index, &attributes);
// The property should always be present. It is always declared
@@ -689,7 +707,7 @@
if (index >= 0) {
// The constant context slot should always be in the function
// context; not in any outer context nor in the arguments object.
- ASSERT(context_obj.is_identical_to(context));
+ ASSERT(holder.is_identical_to(context));
if (context->get(index)->IsTheHole()) {
context->set(index, *value);
}
@@ -697,7 +715,7 @@
}
// Otherwise, the slot must be in a JS object extension.
- Handle<JSObject> context_ext(JSObject::cast(*context_obj));
+ Handle<JSObject> context_ext(JSObject::cast(*holder));
// We must initialize the value only if it wasn't initialized
// before, e.g. for const declarations in a loop. The property has
@@ -956,12 +974,16 @@
// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
// limit, we can fix the size of tables.
static const int kBMMaxShift = 0xff;
-static const int kBMAlphabetSize = 0x100; // Reduce alphabet to this size.
+// Reduce alphabet to this size.
+static const int kBMAlphabetSize = 0x100;
+// For patterns below this length, the skip length of Boyer-Moore is too short
+// to compensate for the algorithmic overhead compared to simple brute force.
+static const int kBMMinPatternLength = 5;
// Holds the two buffers used by Boyer-Moore string search's Good Suffix
// shift. Only allows the last kBMMaxShift characters of the needle
// to be indexed.
-class BMGoodSuffixBuffers: public AllStatic {
+class BMGoodSuffixBuffers {
public:
BMGoodSuffixBuffers() {}
inline void init(int needle_length) {
@@ -985,8 +1007,8 @@
private:
int suffixes_[kBMMaxShift + 1];
int good_suffix_shift_[kBMMaxShift + 1];
- int *biased_suffixes_;
- int *biased_good_suffix_shift_;
+ int* biased_suffixes_;
+ int* biased_good_suffix_shift_;
DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
};
@@ -995,27 +1017,33 @@
static BMGoodSuffixBuffers bmgs_buffers;
// Compute the bad-char table for Boyer-Moore in the static buffer.
-// Return false if the pattern contains non-ASCII characters that cannot be
-// in the searched string.
template <typename pchar>
static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern,
int start) {
// Run forwards to populate bad_char_table, so that *last* instance
// of character equivalence class is the one registered.
// Notice: Doesn't include the last character.
- for (int i = 0; i < kBMAlphabetSize; i++) {
- bad_char_occurence[i] = start - 1;
+ int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
+ : kBMAlphabetSize;
+ if (start == 0) { // All patterns less than kBMMaxShift in length.
+ memset(bad_char_occurence, -1, table_size * sizeof(*bad_char_occurence));
+ } else {
+ for (int i = 0; i < table_size; i++) {
+ bad_char_occurence[i] = start - 1;
+ }
}
- for (int i = start; i < pattern.length(); i++) {
- bad_char_occurence[pattern[i] % kBMAlphabetSize] = i;
+ for (int i = start; i < pattern.length() - 1; i++) {
+ pchar c = pattern[i];
+ int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
+ bad_char_occurence[bucket] = i;
}
}
template <typename pchar>
static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern,
- int start,
- int len) {
+ int start) {
int m = pattern.length();
+ int len = m - start;
// Compute Good Suffix tables.
bmgs_buffers.init(m);
@@ -1061,34 +1089,60 @@
}
}
-// Restricted Boyer-Moore string matching. Restricts tables to a
+template <typename schar, typename pchar>
+static inline int CharOccurence(int char_code) {
+ if (sizeof(schar) == 1) {
+ return bad_char_occurence[char_code];
+ }
+ if (sizeof(pchar) == 1) {
+ if (char_code > String::kMaxAsciiCharCode) {
+ return -1;
+ }
+ return bad_char_occurence[char_code];
+ }
+ return bad_char_occurence[char_code % kBMAlphabetSize];
+}
+
+// Restricted simplified Boyer-Moore string matching. Restricts tables to a
// suffix of long pattern strings and handles only equivalence classes
// of the full alphabet. This allows us to ensure that tables take only
// a fixed amount of space.
template <typename schar, typename pchar>
-static int BoyerMooreIndexOf(Vector<const schar> subject,
- Vector<const pchar> pattern,
- int start_index) {
- int m = pattern.length();
+static int BoyerMooreSimplified(Vector<const schar> subject,
+ Vector<const pchar> pattern,
+ int start_index,
+ bool* complete) {
int n = subject.length();
-
+ int m = pattern.length();
// Only preprocess at most kBMMaxShift last characters of pattern.
int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
- int len = m - start;
BoyerMoorePopulateBadCharTable(pattern, start);
- int badness = 0; // How bad we are doing without a good-suffix table.
+ int badness = -m; // How bad we are doing without a good-suffix table.
int idx; // No matches found prior to this index.
+ pchar last_char = pattern[m - 1];
// Perform search
for (idx = start_index; idx <= n - m;) {
int j = m - 1;
- schar c;
+ int c;
+ while (last_char != (c = subject[idx + j])) {
+ int bc_occ = CharOccurence<schar, pchar>(c);
+ int shift = j - bc_occ;
+ idx += shift;
+ badness += 1 - shift; // at most zero, so badness cannot increase.
+ if (idx > n - m) {
+ *complete = true;
+ return -1;
+ }
+ }
+ j--;
while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
if (j < 0) {
+ *complete = true;
return idx;
} else {
- int bc_occ = bad_char_occurence[c % kBMAlphabetSize];
+ int bc_occ = CharOccurence<schar, pchar>(c);
int shift = bc_occ < j ? j - bc_occ : 1;
idx += shift;
// Badness increases by the number of characters we have
@@ -1096,40 +1150,66 @@
// can skip by shifting. It's a measure of how we are doing
// compared to reading each character exactly once.
badness += (m - j) - shift;
- if (badness > m) break;
+ if (badness > 0) {
+ *complete = false;
+ return idx;
+ }
}
}
+ *complete = true;
+ return -1;
+}
- // If we are not done, we got here because we should build the Good Suffix
- // table and continue searching.
- if (idx <= n - m) {
- BoyerMoorePopulateGoodSuffixTable(pattern, start, len);
- // Continue search from i.
- do {
- int j = m - 1;
- schar c;
- while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
- if (j < 0) {
- return idx;
- } else if (j < start) {
- // we have matched more than our tables allow us to be smart about.
- idx += 1;
- } else {
- int gs_shift = bmgs_buffers.shift(j + 1);
- int bc_occ = bad_char_occurence[c % kBMAlphabetSize];
- int bc_shift = j - bc_occ;
- idx += (gs_shift > bc_shift) ? gs_shift : bc_shift;
+
+template <typename schar, typename pchar>
+static int BoyerMooreIndexOf(Vector<const schar> subject,
+ Vector<const pchar> pattern,
+ int idx) {
+ int n = subject.length();
+ int m = pattern.length();
+ // Only preprocess at most kBMMaxShift last characters of pattern.
+ int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
+
+ // Build the Good Suffix table and continue searching.
+ BoyerMoorePopulateGoodSuffixTable(pattern, start);
+ pchar last_char = pattern[m - 1];
+ // Continue search from i.
+ do {
+ int j = m - 1;
+ schar c;
+ while (last_char != (c = subject[idx + j])) {
+ int shift = j - CharOccurence<schar, pchar>(c);
+ idx += shift;
+ if (idx > n - m) {
+ return -1;
}
- } while (idx <= n - m);
- }
+ }
+ while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
+ if (j < 0) {
+ return idx;
+ } else if (j < start) {
+ // we have matched more than our tables allow us to be smart about.
+ idx += 1;
+ } else {
+ int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
+ int bc_occ = CharOccurence<schar, pchar>(c);
+ int shift = j - bc_occ; // Bad-char shift.
+ shift = (gs_shift > shift) ? gs_shift : shift;
+ idx += shift;
+ }
+ } while (idx <= n - m);
return -1;
}
-template <typename schar, typename pchar>
+
+template <typename schar>
static int SingleCharIndexOf(Vector<const schar> string,
- pchar pattern_char,
+ uc16 pattern_char,
int start_index) {
+ if (sizeof(schar) == 1 && pattern_char > String::kMaxAsciiCharCode) {
+ return -1;
+ }
for (int i = start_index, n = string.length(); i < n; i++) {
if (pattern_char == string[i]) {
return i;
@@ -1147,20 +1227,22 @@
template <typename pchar, typename schar>
static int SimpleIndexOf(Vector<const schar> subject,
Vector<const pchar> pattern,
- int start_index,
- bool &complete) {
- int pattern_length = pattern.length();
- int subject_length = subject.length();
- // Badness is a count of how many extra times the same character
- // is checked. We compare it to the index counter, so we start
- // it at the start_index, and give it a little discount to avoid
- // very early bail-outs.
- int badness = start_index - pattern_length;
+ int idx,
+ bool* complete) {
+ // Badness is a count of how much work we have done. When we have
+ // done enough work we decide it's probably worth switching to a better
+ // algorithm.
+ int badness = -10 - (pattern.length() << 2);
// We know our pattern is at least 2 characters, we cache the first so
// the common case of the first character not matching is faster.
pchar pattern_first_char = pattern[0];
- for (int i = start_index, n = subject_length - pattern_length; i <= n; i++) {
+ for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
+ badness++;
+ if (badness > 0) {
+ *complete = false;
+ return (i);
+ }
if (subject[i] != pattern_first_char) continue;
int j = 1;
do {
@@ -1168,22 +1250,41 @@
break;
}
j++;
- } while (j < pattern_length);
- if (j == pattern_length) {
- complete = true;
+ } while (j < pattern.length());
+ if (j == pattern.length()) {
+ *complete = true;
return i;
}
badness += j;
- if (badness > i) { // More than one extra character on average.
- complete = false;
- return (i + 1); // No matches up to index i+1.
- }
}
- complete = true;
+ *complete = true;
return -1;
}
-// Dispatch to different algorithms for different length of pattern/subject
+// Simple indexOf that never bails out. For short patterns only.
+template <typename pchar, typename schar>
+static int SimpleIndexOf(Vector<const schar> subject,
+ Vector<const pchar> pattern,
+ int idx) {
+ pchar pattern_first_char = pattern[0];
+ for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
+ if (subject[i] != pattern_first_char) continue;
+ int j = 1;
+ do {
+ if (pattern[j] != subject[i+j]) {
+ break;
+ }
+ j++;
+ } while (j < pattern.length());
+ if (j == pattern.length()) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+
+// Dispatch to different algorithms.
template <typename schar, typename pchar>
static int StringMatchStrategy(Vector<const schar> sub,
Vector<const pchar> pat,
@@ -1201,9 +1302,17 @@
}
}
}
- // For small searches, a complex sort is not worth the setup overhead.
+ if (pat.length() < kBMMinPatternLength) {
+ // We don't believe fancy searching can ever be more efficient.
+ // The max shift of Boyer-Moore on a pattern of this length does
+ // not compensate for the overhead.
+ return SimpleIndexOf(sub, pat, start_index);
+ }
+ // Try algorithms in order of increasing setup cost and expected performance.
bool complete;
- int idx = SimpleIndexOf(sub, pat, start_index, complete);
+ int idx = SimpleIndexOf(sub, pat, start_index, &complete);
+ if (complete) return idx;
+ idx = BoyerMooreSimplified(sub, pat, idx, &complete);
if (complete) return idx;
return BoyerMooreIndexOf(sub, pat, idx);
}
@@ -1553,23 +1662,57 @@
-// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric
+// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
static Object* Runtime_KeyedGetProperty(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
- Object* receiver = args[0];
- Object* key = args[1];
- if (receiver->IsJSObject() &&
- key->IsString() &&
- !JSObject::cast(receiver)->HasFastProperties()) {
- Dictionary* dictionary = JSObject::cast(receiver)->property_dictionary();
- int entry = dictionary->FindStringEntry(String::cast(key));
- if ((entry != DescriptorArray::kNotFound)
- && (dictionary->DetailsAt(entry).type() == NORMAL)) {
- return dictionary->ValueAt(entry);
+ // Fast cases for getting named properties of the receiver JSObject
+ // itself. The global proxy objects has to be excluded since
+ // LocalLookup on the global proxy object can return a valid result
+ // eventhough the global proxy object never has properties. This is
+ // the case because the global proxy object forwards everything to
+ // its hidden prototype including local lookups.
+ if (args[0]->IsJSObject() &&
+ !args[0]->IsJSGlobalProxy() &&
+ args[1]->IsString()) {
+ JSObject* receiver = JSObject::cast(args[0]);
+ String* key = String::cast(args[1]);
+ if (receiver->HasFastProperties()) {
+ // Attempt to use lookup cache.
+ Object* obj = Heap::GetKeyedLookupCache();
+ if (obj->IsFailure()) return obj;
+ LookupCache* cache = LookupCache::cast(obj);
+ Map* receiver_map = receiver->map();
+ int offset = cache->Lookup(receiver_map, key);
+ if (offset != LookupCache::kNotFound) {
+ Object* value = receiver->FastPropertyAt(offset);
+ return value->IsTheHole() ? Heap::undefined_value() : value;
+ }
+ // Lookup cache miss. Perform lookup and update the cache if
+ // appropriate.
+ LookupResult result;
+ receiver->LocalLookup(key, &result);
+ if (result.IsProperty() && result.IsLoaded() && result.type() == FIELD) {
+ int offset = result.GetFieldIndex();
+ Object* obj = cache->Put(receiver_map, key, offset);
+ if (obj->IsFailure()) return obj;
+ Heap::SetKeyedLookupCache(LookupCache::cast(obj));
+ Object* value = receiver->FastPropertyAt(offset);
+ return value->IsTheHole() ? Heap::undefined_value() : value;
+ }
+ } else {
+ // Attempt dictionary lookup.
+ Dictionary* dictionary = receiver->property_dictionary();
+ int entry = dictionary->FindStringEntry(key);
+ if ((entry != DescriptorArray::kNotFound) &&
+ (dictionary->DetailsAt(entry).type() == NORMAL)) {
+ return dictionary->ValueAt(entry);
+ }
}
}
+
+ // Fall back to GetObjectProperty.
return Runtime::GetObjectProperty(args.at<Object>(0),
args.at<Object>(1));
}
@@ -2475,6 +2618,30 @@
}
+template<typename sinkchar>
+static inline void StringBuilderConcatHelper(String* special,
+ sinkchar* sink,
+ FixedArray* fixed_array,
+ int array_length) {
+ int position = 0;
+ for (int i = 0; i < array_length; i++) {
+ Object* element = fixed_array->get(i);
+ if (element->IsSmi()) {
+ int len = Smi::cast(element)->value();
+ int pos = len >> 11;
+ len &= 0x7ff;
+ String::WriteToFlat(special, sink + position, pos, pos + len);
+ position += len;
+ } else {
+ String* string = String::cast(element);
+ int element_length = string->length();
+ String::WriteToFlat(string, sink + position, 0, element_length);
+ position += element_length;
+ }
+ }
+}
+
+
static Object* Runtime_StringBuilderConcat(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
@@ -2531,32 +2698,27 @@
}
int length = position;
- position = 0;
Object* object;
+
if (ascii) {
object = Heap::AllocateRawAsciiString(length);
+ if (object->IsFailure()) return object;
+ SeqAsciiString* answer = SeqAsciiString::cast(object);
+ StringBuilderConcatHelper(special,
+ answer->GetChars(),
+ fixed_array,
+ array_length);
+ return answer;
} else {
object = Heap::AllocateRawTwoByteString(length);
+ if (object->IsFailure()) return object;
+ SeqTwoByteString* answer = SeqTwoByteString::cast(object);
+ StringBuilderConcatHelper(special,
+ answer->GetChars(),
+ fixed_array,
+ array_length);
+ return answer;
}
- if (object->IsFailure()) return object;
-
- String* answer = String::cast(object);
- for (int i = 0; i < array_length; i++) {
- Object* element = fixed_array->get(i);
- if (element->IsSmi()) {
- int len = Smi::cast(element)->value();
- int pos = len >> 11;
- len &= 0x7ff;
- String::Flatten(special, answer, pos, pos + len, position);
- position += len;
- } else {
- String* string = String::cast(element);
- int element_length = string->length();
- String::Flatten(string, answer, 0, element_length, position);
- position += element_length;
- }
- }
- return answer;
}
@@ -2655,38 +2817,14 @@
CONVERT_CHECKED(String, x, args[0]);
CONVERT_CHECKED(String, y, args[1]);
- // This is very similar to String::Equals(String*) but that version
- // requires flattened strings as input, whereas we flatten the
- // strings only if the fast cases fail. Note that this may fail,
- // requiring a GC. String::Equals(String*) returns a bool and has
- // no way to signal a failure.
- if (y == x) return Smi::FromInt(EQUAL);
- if (x->IsSymbol() && y->IsSymbol()) return Smi::FromInt(NOT_EQUAL);
- // Compare contents
- int len = x->length();
- if (len != y->length()) return Smi::FromInt(NOT_EQUAL);
- if (len == 0) return Smi::FromInt(EQUAL);
-
- // Handle one elment strings.
- if (x->Get(0) != y->Get(0)) return Smi::FromInt(NOT_EQUAL);
- if (len == 1) return Smi::FromInt(EQUAL);
-
- // Fast case: First, middle and last characters.
- if (x->Get(len>>1) != y->Get(len>>1)) return Smi::FromInt(NOT_EQUAL);
- if (x->Get(len - 1) != y->Get(len - 1)) return Smi::FromInt(NOT_EQUAL);
-
- x->TryFlatten();
- y->TryFlatten();
-
- static StringInputBuffer buf1;
- static StringInputBuffer buf2;
- buf1.Reset(x);
- buf2.Reset(y);
- while (buf1.has_more()) {
- if (buf1.GetNext() != buf2.GetNext())
- return Smi::FromInt(NOT_EQUAL);
- }
- return Smi::FromInt(EQUAL);
+ bool not_equal = !x->Equals(y);
+ // This is slightly convoluted because the value that signifies
+ // equality is 0 and inequality is 1 so we have to negate the result
+ // from String::Equals.
+ ASSERT(not_equal == 0 || not_equal == 1);
+ STATIC_CHECK(EQUAL == 0);
+ STATIC_CHECK(NOT_EQUAL == 1);
+ return Smi::FromInt(not_equal);
}
@@ -2998,8 +3136,9 @@
if (result->IsFailure()) return result;
FixedArray* array = FixedArray::cast(JSObject::cast(result)->elements());
ASSERT(array->length() == length);
+ WriteBarrierMode mode = array->GetWriteBarrierMode();
for (int i = 0; i < length; i++) {
- array->set(i, frame->GetParameter(i));
+ array->set(i, frame->GetParameter(i), mode);
}
return result;
}
@@ -3017,7 +3156,7 @@
if (result->IsFailure()) return result;
FixedArray* array = FixedArray::cast(JSObject::cast(result)->elements());
ASSERT(array->length() == length);
- FixedArray::WriteBarrierMode mode = array->GetWriteBarrierMode();
+ WriteBarrierMode mode = array->GetWriteBarrierMode();
for (int i = 0; i < length; i++) {
array->set(i, *--parameters, mode);
}
@@ -3189,12 +3328,12 @@
int index;
PropertyAttributes attributes;
ContextLookupFlags flags = FOLLOW_CHAINS;
- Handle<Object> context_obj =
+ Handle<Object> holder =
context->Lookup(name, flags, &index, &attributes);
- if (index < 0 && *context_obj != NULL) {
- ASSERT(context_obj->IsJSObject());
- return *context_obj;
+ if (index < 0 && *holder != NULL) {
+ ASSERT(holder->IsJSObject());
+ return *holder;
}
// No intermediate context found. Use global object by default.
@@ -3223,6 +3362,40 @@
}
+static Object* ComputeContextSlotReceiver(Object* holder) {
+ // If the "property" we were looking for is a local variable or an
+ // argument in a context, the receiver is the global object; see
+ // ECMA-262, 3rd., 10.1.6 and 10.2.3.
+ HeapObject* object = HeapObject::cast(holder);
+ Context* top = Top::context();
+ if (holder->IsContext()) return top->global()->global_receiver();
+
+ // TODO(125): Find a better - and faster way - of checking for
+ // arguments and context extension objects. This kinda sucks.
+ JSFunction* context_extension_function =
+ top->global_context()->context_extension_function();
+ JSObject* arguments_boilerplate =
+ top->global_context()->arguments_boilerplate();
+ JSFunction* arguments_function =
+ JSFunction::cast(arguments_boilerplate->map()->constructor());
+ // If the holder is an arguments object or a context extension then the
+ // receiver is also the global object;
+ Object* constructor = HeapObject::cast(holder)->map()->constructor();
+ if (constructor == context_extension_function ||
+ constructor == arguments_function) {
+ return Top::context()->global()->global_receiver();
+ }
+
+ // If the holder is a global object, we have to be careful to wrap
+ // it in its proxy if necessary.
+ if (object->IsGlobalObject()) {
+ return GlobalObject::cast(object)->global_receiver();
+ } else {
+ return object;
+ }
+}
+
+
static ObjPair LoadContextSlotHelper(Arguments args, bool throw_error) {
HandleScope scope;
ASSERT(args.length() == 2);
@@ -3234,34 +3407,32 @@
int index;
PropertyAttributes attributes;
ContextLookupFlags flags = FOLLOW_CHAINS;
- Handle<Object> context_obj =
+ Handle<Object> holder =
context->Lookup(name, flags, &index, &attributes);
if (index >= 0) {
- if (context_obj->IsContext()) {
- // The context is an Execution context, and the "property" we were looking
- // for is a local variable in that context. According to ECMA-262, 3rd.,
- // 10.1.6 and 10.2.3, the receiver is the global object.
- return MakePair(
- Unhole(Handle<Context>::cast(context_obj)->get(index), attributes),
- Top::context()->global());
+ Handle<Object> receiver =
+ Handle<Object>(ComputeContextSlotReceiver(*holder));
+ Handle<Object> value;
+ if (holder->IsContext()) {
+ value = Handle<Object>(Context::cast(*holder)->get(index));
} else {
- return MakePair(
- Unhole(Handle<JSObject>::cast(context_obj)->GetElement(index),
- attributes),
- *context_obj);
+ // Arguments object.
+ value = Handle<Object>(JSObject::cast(*holder)->GetElement(index));
}
+ return MakePair(Unhole(*value, attributes), *receiver);
}
- if (*context_obj != NULL) {
- ASSERT(Handle<JSObject>::cast(context_obj)->HasProperty(*name));
- // Note: As of 5/29/2008, GetProperty does the "unholing" and so this call
- // here is redundant. We left it anyway, to be explicit; also it's not clear
- // why GetProperty should do the unholing in the first place.
+ if (*holder != NULL) {
+ ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
+ // Note: As of 5/29/2008, GetProperty does the "unholing" and so
+ // this call here is redundant. We left it anyway, to be explicit;
+ // also it's not clear why GetProperty should do the unholing in
+ // the first place.
return MakePair(
- Unhole(Handle<JSObject>::cast(context_obj)->GetProperty(*name),
+ Unhole(Handle<JSObject>::cast(holder)->GetProperty(*name),
attributes),
- *context_obj);
+ ComputeContextSlotReceiver(*holder));
}
if (throw_error) {
@@ -3297,19 +3468,19 @@
int index;
PropertyAttributes attributes;
ContextLookupFlags flags = FOLLOW_CHAINS;
- Handle<Object> context_obj =
+ Handle<Object> holder =
context->Lookup(name, flags, &index, &attributes);
if (index >= 0) {
- if (context_obj->IsContext()) {
+ if (holder->IsContext()) {
// Ignore if read_only variable.
if ((attributes & READ_ONLY) == 0) {
- Handle<Context>::cast(context_obj)->set(index, *value);
+ Handle<Context>::cast(holder)->set(index, *value);
}
} else {
ASSERT((attributes & READ_ONLY) == 0);
Object* result =
- Handle<JSObject>::cast(context_obj)->SetElement(index, *value);
+ Handle<JSObject>::cast(holder)->SetElement(index, *value);
USE(result);
ASSERT(!result->IsFailure());
}
@@ -3320,9 +3491,9 @@
// It is either in an JSObject extension context or it was not found.
Handle<JSObject> context_ext;
- if (*context_obj != NULL) {
+ if (*holder != NULL) {
// The property exists in the extension context.
- context_ext = Handle<JSObject>::cast(context_obj);
+ context_ext = Handle<JSObject>::cast(holder);
} else {
// The property was not found. It needs to be stored in the global context.
ASSERT(attributes == ABSENT);
@@ -3707,6 +3878,14 @@
}
+static Object* Runtime_GlobalReceiver(Arguments args) {
+ ASSERT(args.length() == 1);
+ Object* global = args[0];
+ if (!global->IsJSGlobalObject()) return Heap::null_value();
+ return JSGlobalObject::cast(global)->global_receiver();
+}
+
+
static Object* Runtime_CompileString(Arguments args) {
HandleScope scope;
ASSERT(args.length() == 3);
@@ -3872,7 +4051,9 @@
} else {
Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
// -1 means start of array.
- single_interval->set(0, Smi::FromInt(-1));
+ single_interval->set(0,
+ Smi::FromInt(-1),
+ SKIP_WRITE_BARRIER);
Handle<Object> length_object =
Factory::NewNumber(static_cast<double>(length));
single_interval->set(1, *length_object);
@@ -4000,7 +4181,7 @@
}
-static Object* Runtime_DebugGetLocalPropertyDetails(Arguments args) {
+static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
HandleScope scope;
ASSERT(args.length() == 2);
@@ -4020,7 +4201,7 @@
// Perform standard local lookup on the object.
LookupResult result;
- obj->LocalLookup(*name, &result);
+ obj->Lookup(*name, &result);
if (result.IsProperty()) {
Handle<Object> value(DebugLookupResultValue(&result));
Handle<FixedArray> details = Factory::NewFixedArray(2);
@@ -4722,8 +4903,9 @@
Handle<Object> arguments = Factory::NewArgumentsObject(function, length);
FixedArray* array = FixedArray::cast(JSObject::cast(*arguments)->elements());
ASSERT(array->length() == length);
+ WriteBarrierMode mode = array->GetWriteBarrierMode();
for (int i = 0; i < length; i++) {
- array->set(i, frame->GetParameter(i));
+ array->set(i, frame->GetParameter(i), mode);
}
return arguments;
}
@@ -4772,7 +4954,6 @@
ASSERT(save != NULL);
SaveContext savex;
Top::set_context(*(save->context()));
- Top::set_security_context(*(save->security_context()));
// Create the (empty) function replacing the function on the stack frame for
// the purpose of evaluating in the context created below. It is important
@@ -4899,7 +5080,6 @@
}
if (top != NULL) {
Top::set_context(*top->context());
- Top::set_security_context(*top->security_context());
}
// Get the global context now set to the top context from before the
diff --git a/src/runtime.h b/src/runtime.h
index 100077b..63bdc87 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -165,9 +165,14 @@
F(GetScript, 1) \
\
F(ClassOf, 1) \
- F(IsDateClass, 1) \
- F(IsStringClass, 1) \
- F(IsArrayClass, 1) \
+ F(HasDateClass, 1) \
+ F(HasStringClass, 1) \
+ F(HasArrayClass, 1) \
+ F(HasFunctionClass, 1) \
+ F(HasNumberClass, 1) \
+ F(HasBooleanClass, 1) \
+ F(HasArgumentsClass, 1) \
+ F(HasRegExpClass, 1) \
F(SetCode, 2) \
\
F(CreateApiFunction, 1) \
@@ -191,6 +196,7 @@
\
/* Eval */ \
F(EvalReceiver, 1) \
+ F(GlobalReceiver, 1) \
\
F(SetProperty, -1 /* 3 or 4 */) \
F(IgnoreAttributesAndSetProperty, -1 /* 3 or 4 */) \
@@ -209,7 +215,7 @@
F(AddDebugEventListener, 2) \
F(RemoveDebugEventListener, 1) \
F(Break, 0) \
- F(DebugGetLocalPropertyDetails, 2) \
+ F(DebugGetPropertyDetails, 2) \
F(DebugGetProperty, 2) \
F(DebugLocalPropertyNames, 1) \
F(DebugLocalElementNames, 1) \
diff --git a/src/runtime.js b/src/runtime.js
index e44a103..1355356 100644
--- a/src/runtime.js
+++ b/src/runtime.js
@@ -352,8 +352,8 @@
// Make sure the arguments list has the right type.
if (args != null &&
- %ClassOf(args) != 'Array' &&
- %ClassOf(args) != 'Arguments') {
+ !%HasArrayClass(args) &&
+ !%HasArgumentsClass(args)) {
throw %MakeTypeError('apply_wrong_args', []);
}
diff --git a/src/serialize.cc b/src/serialize.cc
index df3c7af..1fd94ab 100644
--- a/src/serialize.cc
+++ b/src/serialize.cc
@@ -878,14 +878,14 @@
}
-bool Serializer::IsVisited(HeapObject *obj) {
+bool Serializer::IsVisited(HeapObject* obj) {
HashMap::Entry* entry =
saved_addresses_.Lookup(obj, HeapObjectHash(obj), false);
return entry != NULL;
}
-Address Serializer::GetSavedAddress(HeapObject *obj) {
+Address Serializer::GetSavedAddress(HeapObject* obj) {
HashMap::Entry* entry
= saved_addresses_.Lookup(obj, HeapObjectHash(obj), false);
ASSERT(entry != NULL);
@@ -1055,18 +1055,6 @@
HandleScopeImplementer::instance()->SaveContext(contexts[i]);
}
PutGlobalHandleStack(contexts);
-
- List<Handle<Object> > security_contexts(2);
- while (HandleScopeImplementer::instance()->HasSavedSecurityContexts()) {
- Handle<Object> context =
- HandleScopeImplementer::instance()->RestoreSecurityContext();
- security_contexts.Add(context);
- }
- for (int i = security_contexts.length() - 1; i >= 0; i--) {
- Handle<Object> context = security_contexts[i];
- HandleScopeImplementer::instance()->SaveSecurityContext(context);
- }
- PutGlobalHandleStack(security_contexts);
}
@@ -1390,12 +1378,6 @@
for (int i = 0; i < entered_contexts.length(); i++) {
HandleScopeImplementer::instance()->SaveContext(entered_contexts[i]);
}
- List<Handle<Object> > security_contexts(2);
- GetGlobalHandleStack(&security_contexts);
- for (int i = 0; i < security_contexts.length(); i++) {
- HandleScopeImplementer::instance()->
- SaveSecurityContext(security_contexts[i]);
- }
}
@@ -1413,7 +1395,7 @@
// Get a raw object of the right size in the right space.
AllocationSpace space = GetSpace(a);
- Object *o;
+ Object* o;
if (IsLargeExecutableObject(a)) {
o = Heap::lo_space()->AllocateRawCode(size);
} else if (IsLargeFixedArray(a)) {
@@ -1471,7 +1453,7 @@
template<typename T>
-void ConcatReversed(List<T> * target, const List<T> & source) {
+void ConcatReversed(List<T>* target, const List<T>& source) {
for (int i = source.length() - 1; i >= 0; i--) {
target->Add(source[i]);
}
diff --git a/src/serialize.h b/src/serialize.h
index 5c65daf..e17ee15 100644
--- a/src/serialize.h
+++ b/src/serialize.h
@@ -154,9 +154,9 @@
virtual void VisitPointers(Object** start, Object** end);
- bool IsVisited(HeapObject *obj);
+ bool IsVisited(HeapObject* obj);
- Address GetSavedAddress(HeapObject *obj);
+ Address GetSavedAddress(HeapObject* obj);
void SaveAddress(HeapObject* obj, Address addr);
diff --git a/src/smart-pointer.h b/src/smart-pointer.h
index 03fd229..c39df16 100644
--- a/src/smart-pointer.h
+++ b/src/smart-pointer.h
@@ -86,7 +86,7 @@
// the copy constructor it removes the pointer in the original to avoid
// double freeing.
inline SmartPointer& operator=(const SmartPointer<T>& rhs) {
- ASSERT(p == NULL);
+ ASSERT(is_empty());
T* tmp = rhs.p; // swap to handle self-assignment
const_cast<SmartPointer<T>&>(rhs).p = NULL;
p = tmp;
@@ -94,6 +94,11 @@
}
+ inline bool is_empty() {
+ return p == NULL;
+ }
+
+
private:
T* p;
};
diff --git a/src/spaces-inl.h b/src/spaces-inl.h
index 86369b9..7a81db3 100644
--- a/src/spaces-inl.h
+++ b/src/spaces-inl.h
@@ -300,15 +300,13 @@
Object* NewSpace::AllocateRawInternal(int size_in_bytes,
AllocationInfo* alloc_info) {
Address new_top = alloc_info->top + size_in_bytes;
- if (new_top > alloc_info->limit) {
- return Failure::RetryAfterGC(size_in_bytes, identity());
- }
+ if (new_top > alloc_info->limit) return Failure::RetryAfterGC(size_in_bytes);
Object* obj = HeapObject::FromAddress(alloc_info->top);
alloc_info->top = new_top;
#ifdef DEBUG
SemiSpace* space =
- (alloc_info == &allocation_info_) ? to_space_ : from_space_;
+ (alloc_info == &allocation_info_) ? &to_space_ : &from_space_;
ASSERT(space->low() <= alloc_info->top
&& alloc_info->top <= space->high()
&& alloc_info->limit == space->high());
diff --git a/src/spaces.cc b/src/spaces.cc
index d1aaceb..7894f24 100644
--- a/src/spaces.cc
+++ b/src/spaces.cc
@@ -36,9 +36,9 @@
// For contiguous spaces, top should be in the space (or at the end) and limit
// should be the end of the space.
#define ASSERT_SEMISPACE_ALLOCATION_INFO(info, space) \
- ASSERT((space)->low() <= (info).top \
- && (info).top <= (space)->high() \
- && (info).limit == (space)->high())
+ ASSERT((space).low() <= (info).top \
+ && (info).top <= (space).high() \
+ && (info).limit == (space).high())
// ----------------------------------------------------------------------------
@@ -760,16 +760,19 @@
// -----------------------------------------------------------------------------
// NewSpace implementation
-NewSpace::NewSpace(int initial_semispace_capacity,
- int maximum_semispace_capacity,
- AllocationSpace id)
- : Space(id, NOT_EXECUTABLE) {
+
+bool NewSpace::Setup(Address start, int size) {
+ // Setup new space based on the preallocated memory block defined by
+ // start and size. The provided space is divided into two semi-spaces.
+ // To support fast containment testing in the new space, the size of
+ // this chunk must be a power of two and it must be aligned to its size.
+ int initial_semispace_capacity = Heap::InitialSemiSpaceSize();
+ int maximum_semispace_capacity = Heap::SemiSpaceSize();
+
ASSERT(initial_semispace_capacity <= maximum_semispace_capacity);
ASSERT(IsPowerOf2(maximum_semispace_capacity));
maximum_capacity_ = maximum_semispace_capacity;
capacity_ = initial_semispace_capacity;
- to_space_ = new SemiSpace(capacity_, maximum_capacity_, id);
- from_space_ = new SemiSpace(capacity_, maximum_capacity_, id);
// Allocate and setup the histogram arrays if necessary.
#if defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING)
@@ -781,19 +784,16 @@
INSTANCE_TYPE_LIST(SET_NAME)
#undef SET_NAME
#endif
-}
-
-bool NewSpace::Setup(Address start, int size) {
ASSERT(size == 2 * maximum_capacity_);
ASSERT(IsAddressAligned(start, size, 0));
- if (to_space_ == NULL
- || !to_space_->Setup(start, maximum_capacity_)) {
+ if (!to_space_.Setup(start, capacity_, maximum_capacity_)) {
return false;
}
- if (from_space_ == NULL
- || !from_space_->Setup(start + maximum_capacity_, maximum_capacity_)) {
+ if (!from_space_.Setup(start + maximum_capacity_,
+ capacity_,
+ maximum_capacity_)) {
return false;
}
@@ -802,8 +802,8 @@
object_mask_ = address_mask_ | kHeapObjectTag;
object_expected_ = reinterpret_cast<uint32_t>(start) | kHeapObjectTag;
- allocation_info_.top = to_space_->low();
- allocation_info_.limit = to_space_->high();
+ allocation_info_.top = to_space_.low();
+ allocation_info_.limit = to_space_.high();
mc_forwarding_info_.top = NULL;
mc_forwarding_info_.limit = NULL;
@@ -831,22 +831,13 @@
mc_forwarding_info_.top = NULL;
mc_forwarding_info_.limit = NULL;
- if (to_space_ != NULL) {
- to_space_->TearDown();
- delete to_space_;
- to_space_ = NULL;
- }
-
- if (from_space_ != NULL) {
- from_space_->TearDown();
- delete from_space_;
- from_space_ = NULL;
- }
+ to_space_.TearDown();
+ from_space_.TearDown();
}
void NewSpace::Flip() {
- SemiSpace* tmp = from_space_;
+ SemiSpace tmp = from_space_;
from_space_ = to_space_;
to_space_ = tmp;
}
@@ -857,24 +848,24 @@
// TODO(1240712): Failure to double the from space can result in
// semispaces of different sizes. In the event of that failure, the
// to space doubling should be rolled back before returning false.
- if (!to_space_->Double() || !from_space_->Double()) return false;
+ if (!to_space_.Double() || !from_space_.Double()) return false;
capacity_ *= 2;
- allocation_info_.limit = to_space_->high();
+ allocation_info_.limit = to_space_.high();
ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_);
return true;
}
void NewSpace::ResetAllocationInfo() {
- allocation_info_.top = to_space_->low();
- allocation_info_.limit = to_space_->high();
+ allocation_info_.top = to_space_.low();
+ allocation_info_.limit = to_space_.high();
ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_);
}
void NewSpace::MCResetRelocationInfo() {
- mc_forwarding_info_.top = from_space_->low();
- mc_forwarding_info_.limit = from_space_->high();
+ mc_forwarding_info_.top = from_space_.low();
+ mc_forwarding_info_.limit = from_space_.high();
ASSERT_SEMISPACE_ALLOCATION_INFO(mc_forwarding_info_, from_space_);
}
@@ -883,7 +874,7 @@
// Assumes that the spaces have been flipped so that mc_forwarding_info_ is
// valid allocation info for the to space.
allocation_info_.top = mc_forwarding_info_.top;
- allocation_info_.limit = to_space_->high();
+ allocation_info_.limit = to_space_.high();
ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_);
}
@@ -897,7 +888,7 @@
// There should be objects packed in from the low address up to the
// allocation pointer.
- Address current = to_space_->low();
+ Address current = to_space_.low();
while (current < top()) {
HeapObject* object = HeapObject::FromAddress(current);
@@ -931,22 +922,24 @@
// -----------------------------------------------------------------------------
// SemiSpace implementation
-SemiSpace::SemiSpace(int initial_capacity,
- int maximum_capacity,
- AllocationSpace id)
- : Space(id, NOT_EXECUTABLE), capacity_(initial_capacity),
- maximum_capacity_(maximum_capacity), start_(NULL), age_mark_(NULL) {
-}
+bool SemiSpace::Setup(Address start,
+ int initial_capacity,
+ int maximum_capacity) {
+ // Creates a space in the young generation. The constructor does not
+ // allocate memory from the OS. A SemiSpace is given a contiguous chunk of
+ // memory of size 'capacity' when set up, and does not grow or shrink
+ // otherwise. In the mark-compact collector, the memory region of the from
+ // space is used as the marking stack. It requires contiguous memory
+ // addresses.
+ capacity_ = initial_capacity;
+ maximum_capacity_ = maximum_capacity;
-
-bool SemiSpace::Setup(Address start, int size) {
- ASSERT(size == maximum_capacity_);
if (!MemoryAllocator::CommitBlock(start, capacity_, executable())) {
return false;
}
start_ = start;
- address_mask_ = ~(size - 1);
+ address_mask_ = ~(maximum_capacity - 1);
object_mask_ = address_mask_ | kHeapObjectTag;
object_expected_ = reinterpret_cast<uint32_t>(start) | kHeapObjectTag;
@@ -1002,7 +995,7 @@
ASSERT(space->ToSpaceContains(start));
ASSERT(space->ToSpaceLow() <= end
&& end <= space->ToSpaceHigh());
- space_ = space->to_space_;
+ space_ = &space->to_space_;
current_ = start;
limit_ = end;
size_func_ = size_func;
diff --git a/src/spaces.h b/src/spaces.h
index 04bf01e..718f888 100644
--- a/src/spaces.h
+++ b/src/spaces.h
@@ -878,19 +878,14 @@
class SemiSpace : public Space {
public:
- // Creates a space in the young generation. The constructor does not
- // allocate memory from the OS. A SemiSpace is given a contiguous chunk of
- // memory of size 'capacity' when set up, and does not grow or shrink
- // otherwise. In the mark-compact collector, the memory region of the from
- // space is used as the marking stack. It requires contiguous memory
- // addresses.
- SemiSpace(int initial_capacity,
- int maximum_capacity,
- AllocationSpace id);
- virtual ~SemiSpace() {}
+ // Constructor.
+ SemiSpace() :Space(NEW_SPACE, NOT_EXECUTABLE) {
+ start_ = NULL;
+ age_mark_ = NULL;
+ }
// Sets up the semispace using the given chunk.
- bool Setup(Address start, int size);
+ bool Setup(Address start, int initial_capacity, int maximum_capacity);
// Tear down the space. Heap memory was not allocated by the space, so it
// is not deallocated here.
@@ -1016,16 +1011,8 @@
class NewSpace : public Space {
public:
- // Create a new space with a given allocation capacity (ie, the capacity of
- // *one* of the semispaces). The constructor does not allocate heap memory
- // from the OS. When the space is set up, it is given a contiguous chunk of
- // memory of size 2 * semispace_capacity. To support fast containment
- // testing in the new space, the size of this chunk must be a power of two
- // and it must be aligned to its size.
- NewSpace(int initial_semispace_capacity,
- int maximum_semispace_capacity,
- AllocationSpace id);
- virtual ~NewSpace() {}
+ // Constructor.
+ NewSpace() : Space(NEW_SPACE, NOT_EXECUTABLE) {}
// Sets up the new space using the given chunk.
bool Setup(Address start, int size);
@@ -1036,7 +1023,7 @@
// True if the space has been set up but not torn down.
bool HasBeenSetup() {
- return to_space_->HasBeenSetup() && from_space_->HasBeenSetup();
+ return to_space_.HasBeenSetup() && from_space_.HasBeenSetup();
}
// Flip the pair of spaces.
@@ -1069,12 +1056,12 @@
// Return the address of the allocation pointer in the active semispace.
Address top() { return allocation_info_.top; }
// Return the address of the first object in the active semispace.
- Address bottom() { return to_space_->low(); }
+ Address bottom() { return to_space_.low(); }
// Get the age mark of the inactive semispace.
- Address age_mark() { return from_space_->age_mark(); }
+ Address age_mark() { return from_space_.age_mark(); }
// Set the age mark in the active semispace.
- void set_age_mark(Address mark) { to_space_->set_age_mark(mark); }
+ void set_age_mark(Address mark) { to_space_.set_age_mark(mark); }
// The start address of the space and a bit mask. Anding an address in the
// new space with the mask will result in the start address.
@@ -1105,36 +1092,36 @@
void MCCommitRelocationInfo();
// Get the extent of the inactive semispace (for use as a marking stack).
- Address FromSpaceLow() { return from_space_->low(); }
- Address FromSpaceHigh() { return from_space_->high(); }
+ Address FromSpaceLow() { return from_space_.low(); }
+ Address FromSpaceHigh() { return from_space_.high(); }
// Get the extent of the active semispace (to sweep newly copied objects
// during a scavenge collection).
- Address ToSpaceLow() { return to_space_->low(); }
- Address ToSpaceHigh() { return to_space_->high(); }
+ Address ToSpaceLow() { return to_space_.low(); }
+ Address ToSpaceHigh() { return to_space_.high(); }
// Offsets from the beginning of the semispaces.
int ToSpaceOffsetForAddress(Address a) {
- return to_space_->SpaceOffsetForAddress(a);
+ return to_space_.SpaceOffsetForAddress(a);
}
int FromSpaceOffsetForAddress(Address a) {
- return from_space_->SpaceOffsetForAddress(a);
+ return from_space_.SpaceOffsetForAddress(a);
}
// True if the object is a heap object in the address range of the
// respective semispace (not necessarily below the allocation pointer of the
// semispace).
- bool ToSpaceContains(Object* o) { return to_space_->Contains(o); }
- bool FromSpaceContains(Object* o) { return from_space_->Contains(o); }
+ bool ToSpaceContains(Object* o) { return to_space_.Contains(o); }
+ bool FromSpaceContains(Object* o) { return from_space_.Contains(o); }
- bool ToSpaceContains(Address a) { return to_space_->Contains(a); }
- bool FromSpaceContains(Address a) { return from_space_->Contains(a); }
+ bool ToSpaceContains(Address a) { return to_space_.Contains(a); }
+ bool FromSpaceContains(Address a) { return from_space_.Contains(a); }
#ifdef DEBUG
// Verify the active semispace.
virtual void Verify();
// Print the active semispace.
- virtual void Print() { to_space_->Print(); }
+ virtual void Print() { to_space_.Print(); }
#endif
#if defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING)
@@ -1158,8 +1145,8 @@
int maximum_capacity_;
// The semispaces.
- SemiSpace* to_space_;
- SemiSpace* from_space_;
+ SemiSpace to_space_;
+ SemiSpace from_space_;
// Start address and bit mask for containment testing.
Address start_;
diff --git a/src/string-stream.cc b/src/string-stream.cc
index 055ec95..81cbea5 100644
--- a/src/string-stream.cc
+++ b/src/string-stream.cc
@@ -420,19 +420,10 @@
Add("(Function context is outside heap)\n");
return;
}
- GlobalObject* global = context->global();
- if (!Heap::Contains(global)) {
- Add("(Function context global is outside heap)\n");
- return;
- }
- if (global->IsJSGlobalObject()) {
- Object* token = JSGlobalObject::cast(global)->security_token();
- if (token != current_security_token) {
- Add("Security context: %o\n", token);
- current_security_token = token;
- }
- } else {
- Add("(No security context)\n");
+ Object* token = context->global_context()->security_token();
+ if (token != current_security_token) {
+ Add("Security context: %o\n", token);
+ current_security_token = token;
}
} else {
Add("(Function context is corrupt)\n");
diff --git a/src/string.js b/src/string.js
index 26318a3..984d969 100644
--- a/src/string.js
+++ b/src/string.js
@@ -46,7 +46,7 @@
// ECMA-262 section 15.5.4.2
function StringToString() {
- if (!IS_STRING(this) && !%IsStringClass(this))
+ if (!IS_STRING(this) && !%HasStringClass(this))
throw new $TypeError('String.prototype.toString is not generic');
return %_ValueOf(this);
}
@@ -54,7 +54,7 @@
// ECMA-262 section 15.5.4.3
function StringValueOf() {
- if (!IS_STRING(this) && !%IsStringClass(this))
+ if (!IS_STRING(this) && !%HasStringClass(this))
throw new $TypeError('String.prototype.valueOf is not generic');
return %_ValueOf(this);
}
diff --git a/src/stub-cache-arm.cc b/src/stub-cache-arm.cc
index 5bcc6b0..085c04a 100644
--- a/src/stub-cache-arm.cc
+++ b/src/stub-cache-arm.cc
@@ -420,13 +420,13 @@
__ b(ne, &miss);
// Perform global security token check if needed.
- if (object->IsJSGlobalObject()) {
- __ CheckAccessGlobal(r3, r1, &miss);
+ if (object->IsJSGlobalProxy()) {
+ __ CheckAccessGlobalProxy(r3, r1, &miss);
}
// Stub never generated for non-global objects that require access
// checks.
- ASSERT(object->IsJSGlobalObject() || !object->IsAccessCheckNeeded());
+ ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded());
// Perform map transition for the receiver if necessary.
if ((transition != NULL) && (object->map()->unused_property_fields() == 0)) {
@@ -522,13 +522,13 @@
__ b(ne, &miss);
// Perform global security token check if needed.
- if (object->IsJSGlobalObject()) {
- __ CheckAccessGlobal(r3, r1, &miss);
+ if (object->IsJSGlobalProxy()) {
+ __ CheckAccessGlobalProxy(r3, r1, &miss);
}
// Stub never generated for non-global objects that require access
// checks.
- ASSERT(object->IsJSGlobalObject() || !object->IsAccessCheckNeeded());
+ ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded());
__ ldr(ip, MemOperand(sp)); // receiver
__ push(ip);
@@ -578,13 +578,13 @@
__ b(ne, &miss);
// Perform global security token check if needed.
- if (receiver->IsJSGlobalObject()) {
- __ CheckAccessGlobal(r3, r1, &miss);
+ if (receiver->IsJSGlobalProxy()) {
+ __ CheckAccessGlobalProxy(r3, r1, &miss);
}
// Stub never generated for non-global objects that require access
// checks.
- ASSERT(receiver->IsJSGlobalObject() || !receiver->IsAccessCheckNeeded());
+ ASSERT(receiver->IsJSGlobalProxy() || !receiver->IsAccessCheckNeeded());
__ ldr(ip, MemOperand(sp)); // receiver
__ push(ip);
diff --git a/src/stub-cache-ia32.cc b/src/stub-cache-ia32.cc
index 01526da..37f855a 100644
--- a/src/stub-cache-ia32.cc
+++ b/src/stub-cache-ia32.cc
@@ -398,13 +398,13 @@
__ j(not_equal, miss_label, not_taken);
// Perform global security token check if needed.
- if (object->IsJSGlobalObject()) {
- __ CheckAccessGlobal(receiver_reg, scratch, miss_label);
+ if (object->IsJSGlobalProxy()) {
+ __ CheckAccessGlobalProxy(receiver_reg, scratch, miss_label);
}
// Stub never generated for non-global objects that require access
// checks.
- ASSERT(object->IsJSGlobalObject() || !object->IsAccessCheckNeeded());
+ ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded());
// Perform map transition for the receiver if necessary.
if ((transition != NULL) && (object->map()->unused_property_fields() == 0)) {
@@ -773,13 +773,13 @@
__ j(not_equal, &miss, not_taken);
// Perform global security token check if needed.
- if (object->IsJSGlobalObject()) {
- __ CheckAccessGlobal(ebx, edx, &miss);
+ if (object->IsJSGlobalProxy()) {
+ __ CheckAccessGlobalProxy(ebx, edx, &miss);
}
// Stub never generated for non-global objects that require access
// checks.
- ASSERT(object->IsJSGlobalObject() || !object->IsAccessCheckNeeded());
+ ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded());
__ pop(ebx); // remove the return address
__ push(Operand(esp, 0)); // receiver
@@ -829,13 +829,13 @@
__ j(not_equal, &miss, not_taken);
// Perform global security token check if needed.
- if (receiver->IsJSGlobalObject()) {
- __ CheckAccessGlobal(ebx, edx, &miss);
+ if (receiver->IsJSGlobalProxy()) {
+ __ CheckAccessGlobalProxy(ebx, edx, &miss);
}
// Stub never generated for non-global objects that require access
// checks.
- ASSERT(receiver->IsJSGlobalObject() || !receiver->IsAccessCheckNeeded());
+ ASSERT(receiver->IsJSGlobalProxy() || !receiver->IsAccessCheckNeeded());
__ pop(ebx); // remove the return address
__ push(Operand(esp, 0)); // receiver
diff --git a/src/top.cc b/src/top.cc
index 8ad4c23..a78ba32 100644
--- a/src/top.cc
+++ b/src/top.cc
@@ -66,7 +66,6 @@
void Top::Iterate(ObjectVisitor* v, ThreadLocalTop* thread) {
v->VisitPointer(&(thread->pending_exception_));
- v->VisitPointer(bit_cast<Object**, Context**>(&(thread->security_context_)));
v->VisitPointer(bit_cast<Object**, Context**>(&(thread->context_)));
v->VisitPointer(&(thread->scheduled_exception_));
@@ -96,13 +95,13 @@
thread_local_.handler_ = 0;
thread_local_.stack_is_cooked_ = false;
thread_local_.try_catch_handler_ = NULL;
- thread_local_.security_context_ = NULL;
thread_local_.context_ = NULL;
thread_local_.external_caught_exception_ = false;
thread_local_.failed_access_check_callback_ = NULL;
clear_pending_exception();
clear_scheduled_exception();
thread_local_.save_context_ = NULL;
+ thread_local_.catcher_ = NULL;
}
@@ -444,7 +443,7 @@
if (!thread_local_.failed_access_check_callback_) return;
ASSERT(receiver->IsAccessCheckNeeded());
- ASSERT(Top::security_context());
+ ASSERT(Top::context());
// The callers of this method are not expecting a GC.
AssertNoAllocation no_gc;
@@ -465,23 +464,45 @@
v8::Utils::ToLocal(data));
}
+
+enum MayAccessDecision {
+ YES, NO, UNKNOWN
+};
+
+
+static MayAccessDecision MayAccessPreCheck(JSObject* receiver,
+ v8::AccessType type) {
+ // During bootstrapping, callback functions are not enabled yet.
+ if (Bootstrapper::IsActive()) return YES;
+
+ if (receiver->IsJSGlobalProxy()) {
+ Object* receiver_context = JSGlobalProxy::cast(receiver)->context();
+ if (!receiver_context->IsContext()) return NO;
+
+ // Get the global context of current top context.
+ // avoid using Top::global_context() because it uses Handle.
+ Context* global_context = Top::context()->global()->global_context();
+ if (receiver_context == global_context) return YES;
+
+ if (Context::cast(receiver_context)->security_token() ==
+ global_context->security_token())
+ return YES;
+ }
+
+ return UNKNOWN;
+}
+
+
bool Top::MayNamedAccess(JSObject* receiver, Object* key, v8::AccessType type) {
ASSERT(receiver->IsAccessCheckNeeded());
// Check for compatibility between the security tokens in the
- // current security context and the accessed object.
- ASSERT(Top::security_context());
+ // current lexical context and the accessed object.
+ ASSERT(Top::context());
// The callers of this method are not expecting a GC.
AssertNoAllocation no_gc;
- // During bootstrapping, callback functions are not enabled yet.
- if (Bootstrapper::IsActive()) return true;
-
- if (receiver->IsJSGlobalObject()) {
- JSGlobalObject* global = JSGlobalObject::cast(receiver);
- JSGlobalObject* current =
- JSGlobalObject::cast(Top::security_context()->global());
- if (current->security_token() == global->security_token()) return true;
- }
+ MayAccessDecision decision = MayAccessPreCheck(receiver, type);
+ if (decision != UNKNOWN) return decision == YES;
// Get named access check callback
JSFunction* constructor = JSFunction::cast(receiver->map()->constructor());
@@ -520,20 +541,13 @@
v8::AccessType type) {
ASSERT(receiver->IsAccessCheckNeeded());
// Check for compatibility between the security tokens in the
- // current security context and the accessed object.
- ASSERT(Top::security_context());
+ // current lexical context and the accessed object.
+ ASSERT(Top::context());
// The callers of this method are not expecting a GC.
AssertNoAllocation no_gc;
- // During bootstrapping, callback functions are not enabled yet.
- if (Bootstrapper::IsActive()) return true;
-
- if (receiver->IsJSGlobalObject()) {
- JSGlobalObject* global = JSGlobalObject::cast(receiver);
- JSGlobalObject* current =
- JSGlobalObject::cast(Top::security_context()->global());
- if (current->security_token() == global->security_token()) return true;
- }
+ MayAccessDecision decision = MayAccessPreCheck(receiver, type);
+ if (decision != UNKNOWN) return decision == YES;
// Get indexed access check callback
JSFunction* constructor = JSFunction::cast(receiver->map()->constructor());
@@ -570,8 +584,7 @@
HandleScope scope;
Handle<String> key = Factory::stack_overflow_symbol();
Handle<JSObject> boilerplate =
- Handle<JSObject>::cast(
- GetProperty(Top::security_context_builtins(), key));
+ Handle<JSObject>::cast(GetProperty(Top::builtins(), key));
Handle<Object> exception = Copy(boilerplate);
// TODO(1240995): To avoid having to call JavaScript code to compute
// the message for stack overflow exceptions which is very likely to
@@ -582,19 +595,20 @@
// reworked.
static const char* kMessage =
"Uncaught RangeError: Maximum call stack size exceeded";
- DoThrow(*exception, NULL, kMessage, false);
+ DoThrow(*exception, NULL, kMessage);
return Failure::Exception();
}
Failure* Top::Throw(Object* exception, MessageLocation* location) {
- DoThrow(exception, location, NULL, false);
+ DoThrow(exception, location, NULL);
return Failure::Exception();
}
Failure* Top::ReThrow(Object* exception, MessageLocation* location) {
- DoThrow(exception, location, NULL, true);
+ // Set the exception beeing re-thrown.
+ set_pending_exception(exception);
return Failure::Exception();
}
@@ -712,22 +726,17 @@
// the handler is at a higher address than the external address it
// means that it is below it on the stack.
- // Find the top-most try-catch or try-finally handler.
- while (handler != NULL && handler->is_entry()) {
- handler = handler->next();
- }
-
- // The exception has been externally caught if and only if there is
- // an external handler which is above any JavaScript try-catch or
- // try-finally handlers.
- *is_caught_externally = has_external_handler &&
- (handler == NULL || handler->address() > external_handler_address);
-
// Find the top-most try-catch handler.
while (handler != NULL && !handler->is_try_catch()) {
handler = handler->next();
}
+ // The exception has been externally caught if and only if there is
+ // an external handler which is above any JavaScript try-catch but NOT
+ // try-finally handlers.
+ *is_caught_externally = has_external_handler &&
+ (handler == NULL || handler->address() > external_handler_address);
+
// If we have a try-catch handler then the exception is caught in
// JavaScript code.
bool is_uncaught_by_js = (handler == NULL);
@@ -748,23 +757,23 @@
void Top::DoThrow(Object* exception,
MessageLocation* location,
- const char* message,
- bool is_rethrow) {
+ const char* message) {
ASSERT(!has_pending_exception());
- ASSERT(!external_caught_exception());
HandleScope scope;
Handle<Object> exception_handle(exception);
+ // Determine reporting and whether the exception is caught externally.
bool is_caught_externally = false;
bool report_exception = (exception != Failure::OutOfMemoryException()) &&
- ShouldReportException(&is_caught_externally);
- if (is_rethrow) report_exception = false;
+ ShouldReportException(&is_caught_externally);
+ // Generate the message.
Handle<Object> message_obj;
MessageLocation potential_computed_location;
bool try_catch_needs_message =
- is_caught_externally && thread_local_.try_catch_handler_->capture_message_;
+ is_caught_externally &&
+ thread_local_.try_catch_handler_->capture_message_;
if (report_exception || try_catch_needs_message) {
if (location == NULL) {
// If no location was specified we use a computed one instead
@@ -780,7 +789,9 @@
// If the exception is caught externally, we store it in the
// try/catch handler. The C code can find it later and process it if
// necessary.
+ thread_local_.catcher_ = NULL;
if (is_caught_externally) {
+ thread_local_.catcher_ = thread_local_.try_catch_handler_;
thread_local_.try_catch_handler_->exception_ =
reinterpret_cast<void*>(*exception_handle);
if (!message_obj.is_null()) {
@@ -799,7 +810,7 @@
MessageHandler::ReportMessage(location, message_obj);
}
}
- thread_local_.external_caught_exception_ = is_caught_externally;
+
// NOTE: Notifying the debugger or reporting the exception may have caused
// new exceptions. For now, we just ignore that and set the pending exception
// to the original one.
diff --git a/src/top.h b/src/top.h
index 1937493..f7595b1 100644
--- a/src/top.h
+++ b/src/top.h
@@ -42,7 +42,6 @@
class ThreadLocalTop BASE_EMBEDDED {
public:
- Context* security_context_;
// The context where the current execution method is created and for variable
// lookups.
Context* context_;
@@ -54,6 +53,7 @@
bool external_caught_exception_;
v8::TryCatch* try_catch_handler_;
SaveContext* save_context_;
+ v8::TryCatch* catcher_;
// Stack.
Address c_entry_fp_; // the frame pointer of the top c entry frame
@@ -70,12 +70,11 @@
};
#define TOP_ADDRESS_LIST(C) \
- C(handler_address) \
+ C(handler_address) \
C(c_entry_fp_address) \
C(context_address) \
C(pending_exception_address) \
- C(external_caught_exception_address) \
- C(security_context_address)
+ C(external_caught_exception_address)
class Top {
public:
@@ -88,18 +87,6 @@
static Address get_address_from_id(AddressId id);
- // Access to the security context from which JS execution started.
- // In a browser world, it is the JS context of the frame which initiated
- // JavaScript execution.
- static Context* security_context() { return thread_local_.security_context_; }
- static void set_security_context(Context* context) {
- ASSERT(context == NULL || context->IsGlobalContext());
- thread_local_.security_context_ = context;
- }
- static Context** security_context_address() {
- return &thread_local_.security_context_;
- }
-
// Access to top context (where the current function object was created).
static Context* context() { return thread_local_.context_; }
static void set_context(Context* context) {
@@ -157,6 +144,12 @@
thread_local_.scheduled_exception_ = Heap::the_hole_value();
}
+ static void setup_external_caught() {
+ thread_local_.external_caught_exception_ =
+ (thread_local_.catcher_ != NULL) &&
+ (Top::thread_local_.try_catch_handler_ == Top::thread_local_.catcher_);
+ }
+
// Tells whether the current context has experienced an out of memory
// exception.
static bool is_out_of_memory();
@@ -219,8 +212,7 @@
static Object* PromoteScheduledException();
static void DoThrow(Object* exception,
MessageLocation* location,
- const char* message,
- bool is_rethrow);
+ const char* message);
static bool ShouldReportException(bool* is_caught_externally);
static void ReportUncaughtException(Handle<Object> exception,
MessageLocation* location,
@@ -243,18 +235,22 @@
static void Iterate(ObjectVisitor* v, ThreadLocalTop* t);
static char* Iterate(ObjectVisitor* v, char* t);
- static Handle<JSObject> global() {
- return Handle<JSObject>(context()->global());
+ // Returns the global object of the current context. It could be
+ // a builtin object, or a js global object.
+ static Handle<GlobalObject> global() {
+ return Handle<GlobalObject>(context()->global());
}
+
+ // Returns the global proxy object of the current context.
+ static Object* global_proxy() {
+ return context()->global_proxy();
+ }
+
static Handle<Context> global_context();
static Handle<JSBuiltinsObject> builtins() {
return Handle<JSBuiltinsObject>(thread_local_.context_->builtins());
}
- static Handle<JSBuiltinsObject> security_context_builtins() {
- return Handle<JSBuiltinsObject>(
- thread_local_.security_context_->builtins());
- }
static Object* LookupSpecialFunction(JSObject* receiver,
JSObject* prototype,
@@ -304,28 +300,33 @@
};
+// TODO(122): If the GCC version is 4.2.0 or higher an additional field is added
+// to this class as a workarround for a bug in the generated code found with
+// GCC 4.2.3.
class SaveContext BASE_EMBEDDED {
public:
SaveContext() :
context_(Top::context()),
- security_context_(Top::security_context()),
+#if __GNUC_VERSION__ >= 40200
+ dummy_(Top::context()),
+#endif
prev_(Top::save_context()) {
Top::set_save_context(this);
}
~SaveContext() {
Top::set_context(*context_);
- Top::set_security_context(*security_context_);
Top::set_save_context(prev_);
}
Handle<Context> context() { return context_; }
- Handle<Context> security_context() { return security_context_; }
SaveContext* prev() { return prev_; }
private:
Handle<Context> context_;
- Handle<Context> security_context_;
+#if __GNUC_VERSION__ >= 40200
+ Handle<Context> dummy_;
+#endif
SaveContext* prev_;
};
@@ -334,19 +335,16 @@
#ifdef DEBUG
public:
AssertNoContextChange() :
- context_(Top::context()),
- security_context_(Top::security_context()) {
+ context_(Top::context()) {
}
~AssertNoContextChange() {
ASSERT(Top::context() == *context_);
- ASSERT(Top::security_context() == *security_context_);
}
private:
HandleScope scope_;
Handle<Context> context_;
- Handle<Context> security_context_;
#else
public:
AssertNoContextChange() { }
diff --git a/src/utils.h b/src/utils.h
index 1f38525..9f24079 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -443,6 +443,28 @@
DISALLOW_IMPLICIT_CONSTRUCTORS(StringBuilder);
};
+
+// Copy from ASCII/16bit chars to ASCII/16bit chars.
+template <typename sourcechar, typename sinkchar>
+static inline void CopyChars(sinkchar* dest, const sourcechar* src, int chars) {
+ sinkchar* limit = dest + chars;
+#ifdef CAN_READ_UNALIGNED
+ if (sizeof(*dest) == sizeof(*src)) {
+ // Number of characters in a uint32_t.
+ static const int kStepSize = sizeof(uint32_t) / sizeof(*dest); // NOLINT
+ while (dest <= limit - kStepSize) {
+ *reinterpret_cast<uint32_t*>(dest) =
+ *reinterpret_cast<const uint32_t*>(src);
+ dest += kStepSize;
+ src += kStepSize;
+ }
+ }
+#endif
+ while (dest < limit) {
+ *dest++ = static_cast<sinkchar>(*src++);
+ }
+}
+
} } // namespace v8::internal
#endif // V8_UTILS_H_
diff --git a/src/v8natives.js b/src/v8natives.js
index 64b2610..094d5ea 100644
--- a/src/v8natives.js
+++ b/src/v8natives.js
@@ -117,7 +117,7 @@
// NOTE: We don't care about the character casing.
if (!lang || /javascript/i.test(lang)) {
var f = %CompileString(ToString(expr), 0, false);
- f.call(global);
+ f.call(%GlobalReceiver(global));
}
return null;
}
@@ -292,7 +292,7 @@
function BooleanToString() {
// NOTE: Both Boolean objects and values can enter here as
// 'this'. This is not as dictated by ECMA-262.
- if (!IS_BOOLEAN(this) && %ClassOf(this) !== 'Boolean')
+ if (!IS_BOOLEAN(this) && !%HasBooleanClass(this))
throw new $TypeError('Boolean.prototype.toString is not generic');
return ToString(%_ValueOf(this));
}
@@ -301,7 +301,7 @@
function BooleanValueOf() {
// NOTE: Both Boolean objects and values can enter here as
// 'this'. This is not as dictated by ECMA-262.
- if (!IS_BOOLEAN(this) && %ClassOf(this) !== 'Boolean')
+ if (!IS_BOOLEAN(this) && !%HasBooleanClass(this))
throw new $TypeError('Boolean.prototype.valueOf is not generic');
return %_ValueOf(this);
}
@@ -340,7 +340,7 @@
// 'this'. This is not as dictated by ECMA-262.
var number = this;
if (!IS_NUMBER(this)) {
- if (%ClassOf(this) !== 'Number')
+ if (!%HasNumberClass(this))
throw new $TypeError('Number.prototype.toString is not generic');
// Get the value of this number in case it's an object.
number = %_ValueOf(this);
@@ -370,7 +370,7 @@
function NumberValueOf() {
// NOTE: Both Number objects and values can enter here as
// 'this'. This is not as dictated by ECMA-262.
- if (!IS_NUMBER(this) && %ClassOf(this) !== 'Number')
+ if (!IS_NUMBER(this) && !%HasNumberClass(this))
throw new $TypeError('Number.prototype.valueOf is not generic');
return %_ValueOf(this);
}
@@ -466,7 +466,7 @@
function FunctionSourceString(func) {
// NOTE: Both Function objects and values can enter here as
// 'func'. This is not as dictated by ECMA-262.
- if (!IS_FUNCTION(func) && %ClassOf(func) != 'Function')
+ if (!IS_FUNCTION(func) && !%HasFunctionClass(func))
throw new $TypeError('Function.prototype.toString is not generic');
var source = %FunctionGetSourceCode(func);
diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc
index 7bc60bd..6364d76 100644
--- a/test/cctest/test-api.cc
+++ b/test/cctest/test-api.cc
@@ -987,13 +987,9 @@
}
-v8::Handle<Value> pre_post_global;
-
static v8::Handle<Value> PrePropertyHandlerGet(Local<String> key,
const AccessorInfo& info) {
ApiTestFuzzer::Fuzz();
- CHECK(info.This()->Equals(pre_post_global));
- CHECK(info.Holder()->Equals(pre_post_global));
if (v8_str("pre")->Equals(key)) {
return v8_str("PrePropertyHandler: pre");
}
@@ -1018,7 +1014,6 @@
0,
PrePropertyHandlerHas);
LocalContext env(NULL, desc->InstanceTemplate());
- pre_post_global = env->Global();
Script::Compile(v8_str(
"var pre = 'Object: pre'; var on = 'Object: on';"))->Run();
v8::Handle<Value> result_pre = Script::Compile(v8_str("pre"))->Run();
@@ -1165,12 +1160,11 @@
THREADED_TEST(InternalFields) {
v8::HandleScope scope;
+ LocalContext env;
+
Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
instance_templ->SetInternalFieldCount(1);
- LocalContext env(0, instance_templ);
- CHECK_EQ(1, env->Global()->InternalFieldCount());
-
Local<v8::Object> obj = templ->GetFunction()->NewInstance();
CHECK_EQ(1, obj->InternalFieldCount());
CHECK(obj->GetInternalField(0)->IsUndefined());
@@ -1225,7 +1219,7 @@
static void check_message(v8::Handle<v8::Message> message,
v8::Handle<Value> data) {
CHECK_EQ(5.76, data->NumberValue());
- CHECK_EQ(6.75, message->GetSourceData()->NumberValue());
+ CHECK_EQ(6.75, message->GetScriptResourceName()->NumberValue());
message_received = true;
}
@@ -2371,8 +2365,8 @@
static void MissingScriptInfoMessageListener(v8::Handle<v8::Message> message,
v8::Handle<Value> data) {
CHECK_EQ(v8::Undefined(), data);
- CHECK(message->GetScriptResourceName().IsEmpty());
- CHECK_EQ(v8::Undefined(), message->GetSourceData());
+ CHECK(message->GetScriptResourceName()->IsUndefined());
+ CHECK_EQ(v8::Undefined(), message->GetScriptResourceName());
message->GetLineNumber();
message->GetSourceLine();
}
@@ -2416,7 +2410,7 @@
v8::Persistent<Script> script_;
};
-static void HandleWeakReference(v8::Persistent<v8::Object> obj, void* data) {
+static void HandleWeakReference(v8::Persistent<v8::Value> obj, void* data) {
Snorkel* snorkel = reinterpret_cast<Snorkel*>(data);
delete snorkel;
obj.ClearWeak();
@@ -3061,7 +3055,7 @@
CHECK(spy->IsFunction());
// Create another function accessing global objects.
- Script::Compile(v8_str("spy2=function(){return new Array();}"))->Run();
+ Script::Compile(v8_str("spy2=function(){return new this.Array();}"))->Run();
Local<Value> spy2 = env1->Global()->Get(v8_str("spy2"));
CHECK(spy2->IsFunction());
@@ -3074,13 +3068,9 @@
CHECK(result->IsFunction());
}
- // Change env2 to a new domain and invoke spy on env2. It should be blocked
- // by security check.
{
env2->SetSecurityToken(bar);
Context::Scope scope_env2(env2);
- Local<Value> result = Function::Cast(*spy)->Call(env2->Global(), 0, NULL);
- CHECK(result->IsUndefined());
// Call cross_domain_call, it should throw an exception
v8::TryCatch try_catch;
@@ -3227,11 +3217,75 @@
}
+TEST(ContextDetachGlobal) {
+ v8::HandleScope handle_scope;
+ LocalContext env1;
+ v8::Persistent<Context> env2 = Context::New();
+
+ Local<v8::Object> global1 = env1->Global();
+
+ Local<Value> foo = v8_str("foo");
+
+ // Set to the same domain.
+ env1->SetSecurityToken(foo);
+ env2->SetSecurityToken(foo);
+
+ // Enter env2
+ env2->Enter();
+
+ // Create a function in env1
+ Local<v8::Object> global2 = env2->Global();
+ global2->Set(v8_str("prop"), v8::Integer::New(1));
+ CompileRun("function getProp() {return prop;}");
+
+ env1->Global()->Set(v8_str("getProp"),
+ global2->Get(v8_str("getProp")));
+
+ // Detach env1's global, and reuse the global object of env1
+ env2->Exit();
+ env2->DetachGlobal();
+ // env2 has a new global object.
+ CHECK(!env2->Global()->Equals(global2));
+
+ v8::Persistent<Context> env3 =
+ Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2);
+ env3->SetSecurityToken(v8_str("bar"));
+ env3->Enter();
+
+ Local<v8::Object> global3 = env3->Global();
+ CHECK_EQ(global2, global3);
+ CHECK(global3->Get(v8_str("prop"))->IsUndefined());
+ CHECK(global3->Get(v8_str("getProp"))->IsUndefined());
+ global3->Set(v8_str("prop"), v8::Integer::New(-1));
+ global3->Set(v8_str("prop2"), v8::Integer::New(2));
+ env3->Exit();
+
+ // Call getProp in env1, and it should return the value 1
+ {
+ Local<Value> get_prop = global1->Get(v8_str("getProp"));
+ CHECK(get_prop->IsFunction());
+ v8::TryCatch try_catch;
+ Local<Value> r = Function::Cast(*get_prop)->Call(global1, 0, NULL);
+ CHECK(!try_catch.HasCaught());
+ CHECK_EQ(1, r->Int32Value());
+ }
+
+ // Check that env3 is not accessible from env1
+ {
+ Local<Value> r = global3->Get(v8_str("prop2"));
+ CHECK(r->IsUndefined());
+ }
+
+ env2.Dispose();
+ env3.Dispose();
+}
+
+
static bool NamedAccessBlocker(Local<v8::Object> global,
Local<Value> name,
v8::AccessType type,
Local<Value> data) {
- return Context::GetCurrentSecurityContext()->Global()->Equals(global);
+ return Context::GetCurrent()->Global()->Equals(global);
}
@@ -3239,7 +3293,7 @@
uint32_t key,
v8::AccessType type,
Local<Value> data) {
- return Context::GetCurrentSecurityContext()->Global()->Equals(global);
+ return Context::GetCurrent()->Global()->Equals(global);
}
@@ -3252,7 +3306,7 @@
static void EchoSetter(Local<String> name,
Local<Value> value,
- const AccessorInfo& info) {
+ const AccessorInfo&) {
if (value->IsNumber())
g_echo_value = value->Int32Value();
}
@@ -3265,9 +3319,8 @@
}
-static void UnreachableSetter(Local<String> name,
- Local<Value> value,
- const AccessorInfo& info) {
+static void UnreachableSetter(Local<String>, Local<Value>,
+ const AccessorInfo&) {
CHECK(false); // This function should nto be called.
}
@@ -3546,15 +3599,14 @@
}
-static v8::Handle<Value> AccessControlNamedGetter(Local<String> name,
- const AccessorInfo& info) {
+static v8::Handle<Value> AccessControlNamedGetter(
+ Local<String>, const AccessorInfo&) {
return v8::Integer::New(42);
}
-static v8::Handle<Value> AccessControlNamedSetter(Local<String> key,
- Local<Value> value,
- const AccessorInfo&) {
+static v8::Handle<Value> AccessControlNamedSetter(
+ Local<String>, Local<Value> value, const AccessorInfo&) {
return value;
}
@@ -3566,9 +3618,8 @@
}
-static v8::Handle<Value> AccessControlIndexedSetter(uint32_t index,
- Local<Value> value,
- const AccessorInfo&) {
+static v8::Handle<Value> AccessControlIndexedSetter(
+ uint32_t, Local<Value> value, const AccessorInfo&) {
return value;
}
@@ -3727,9 +3778,7 @@
static int shadow_y_getter_call_count;
-static void ShadowYSetter(Local<String> name,
- Local<Value> value,
- const AccessorInfo& info) {
+static void ShadowYSetter(Local<String>, Local<Value>, const AccessorInfo&) {
shadow_y_setter_call_count++;
shadow_y = 42;
}
@@ -4145,9 +4194,7 @@
static v8::Handle<Value> InterceptorStoreICSetter(
- Local<String> key,
- Local<Value> value,
- const AccessorInfo&) {
+ Local<String> key, Local<Value> value, const AccessorInfo&) {
CHECK(v8_str("x")->Equals(key));
CHECK_EQ(42, value->Int32Value());
return value;
@@ -4292,9 +4339,7 @@
static int interceptor_ic_exception_set_count = 0;
static v8::Handle<Value> InterceptorICExceptionSetter(
- Local<String> key,
- Local<Value> value,
- const AccessorInfo&) {
+ Local<String> key, Local<Value> value, const AccessorInfo&) {
ApiTestFuzzer::Fuzz();
if (++interceptor_ic_exception_set_count > 20) {
return v8::ThrowException(v8_num(42));
@@ -4774,6 +4819,10 @@
v8::V8::Initialize();
+ // TODO(121): when running "cctest test-api", the initial count is 2,
+ // after second GC, the counter drops to 1. Needs to figure out why
+ // one GC is not enough to collect all garbage.
+ GetSurvivingGlobalObjectsCount();
int count = GetSurvivingGlobalObjectsCount();
for (int i = 0; i < 5; i++) {
@@ -4975,3 +5024,52 @@
v8::String::AsciiValue name(value);
CHECK_EQ("asdf", *name);
}
+
+
+THREADED_TEST(DateAccess) {
+ v8::HandleScope scope;
+ LocalContext context;
+ v8::Handle<v8::Value> date = v8::Date::New(1224744689038.0);
+ CHECK(date->IsDate());
+ CHECK_EQ(1224744689038.0, v8::Handle<v8::Date>::Cast(date)->NumberValue());
+}
+
+
+void CheckProperties(v8::Handle<v8::Value> val, int elmc, const char* elmv[]) {
+ v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(val);
+ v8::Handle<v8::Array> props = obj->GetPropertyNames();
+ CHECK_EQ(elmc, props->Length());
+ for (int i = 0; i < elmc; i++) {
+ v8::String::Utf8Value elm(props->Get(v8::Integer::New(i)));
+ CHECK_EQ(elmv[i], *elm);
+ }
+}
+
+
+THREADED_TEST(PropertyEnumeration) {
+ v8::HandleScope scope;
+ LocalContext context;
+ v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New(
+ "var result = [];"
+ "result[0] = {};"
+ "result[1] = {a: 1, b: 2};"
+ "result[2] = [1, 2, 3];"
+ "var proto = {x: 1, y: 2, z: 3};"
+ "var x = { __proto__: proto, w: 0, z: 1 };"
+ "result[3] = x;"
+ "result;"))->Run();
+ v8::Handle<v8::Array> elms = v8::Handle<v8::Array>::Cast(obj);
+ CHECK_EQ(4, elms->Length());
+ int elmc0 = 0;
+ const char** elmv0 = NULL;
+ CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0);
+ int elmc1 = 2;
+ const char* elmv1[] = {"a", "b"};
+ CheckProperties(elms->Get(v8::Integer::New(1)), elmc1, elmv1);
+ int elmc2 = 3;
+ const char* elmv2[] = {"0", "1", "2"};
+ CheckProperties(elms->Get(v8::Integer::New(2)), elmc2, elmv2);
+ int elmc3 = 4;
+ const char* elmv3[] = {"w", "z", "x", "y"};
+ CheckProperties(elms->Get(v8::Integer::New(3)), elmc3, elmv3);
+}
diff --git a/test/cctest/test-debug.cc b/test/cctest/test-debug.cc
index 3b217db..2ad3347 100644
--- a/test/cctest/test-debug.cc
+++ b/test/cctest/test-debug.cc
@@ -41,7 +41,7 @@
using ::v8::internal::OS;
using ::v8::internal::Handle;
using ::v8::internal::Heap;
-using ::v8::internal::JSGlobalObject;
+using ::v8::internal::JSGlobalProxy;
using ::v8::internal::Code;
using ::v8::internal::Debug;
using ::v8::internal::Debugger;
@@ -137,14 +137,15 @@
void ExposeDebug() {
// Expose the debug context global object in the global object for testing.
Debug::Load();
- Handle<JSGlobalObject> global(Handle<JSGlobalObject>::cast(
+ Debug::debug_context()->set_security_token(
+ v8::Utils::OpenHandle(*context_)->security_token());
+
+ Handle<JSGlobalProxy> global(Handle<JSGlobalProxy>::cast(
v8::Utils::OpenHandle(*context_->Global())));
- Handle<JSGlobalObject> debug_global(JSGlobalObject::cast(
- Debug::debug_context()->global()));
- debug_global->set_security_token(global->security_token());
Handle<v8::internal::String> debug_string =
v8::internal::Factory::LookupAsciiSymbol("debug");
- SetProperty(global, debug_string, debug_global, DONT_ENUM);
+ SetProperty(global, debug_string,
+ Handle<Object>(Debug::debug_context()->global_proxy()), DONT_ENUM);
}
private:
v8::Persistent<v8::Context> context_;
@@ -225,8 +226,13 @@
script_data, line);
}
buffer[SMALL_STRING_BUFFER_SIZE - 1] = '\0';
- v8::Handle<v8::String> str = v8::String::New(buffer.start());
- return v8::Script::Compile(str)->Run()->Int32Value();
+ {
+ v8::TryCatch try_catch;
+ v8::Handle<v8::String> str = v8::String::New(buffer.start());
+ v8::Handle<v8::Value> value = v8::Script::Compile(str)->Run();
+ ASSERT(!try_catch.HasCaught());
+ return value->Int32Value();
+ }
}
diff --git a/test/cctest/test-heap.cc b/test/cctest/test-heap.cc
index 9bb9f98..0a4f90f 100644
--- a/test/cctest/test-heap.cc
+++ b/test/cctest/test-heap.cc
@@ -306,7 +306,7 @@
static bool WeakPointerCleared = false;
-static void TestWeakGlobalHandleCallback(v8::Persistent<v8::Object> handle,
+static void TestWeakGlobalHandleCallback(v8::Persistent<v8::Value> handle,
void* id) {
USE(handle);
if (1234 == reinterpret_cast<int>(id)) WeakPointerCleared = true;
@@ -377,7 +377,7 @@
}
static void TestDeleteWeakGlobalHandleCallback(
- v8::Persistent<v8::Object> handle,
+ v8::Persistent<v8::Value> handle,
void* id) {
if (1234 == reinterpret_cast<int>(id)) WeakPointerCleared = true;
handle.Dispose();
@@ -664,7 +664,7 @@
obj->SetElement(1, second);
// Make the clone.
- JSObject* clone = JSObject::cast(obj->Copy());
+ JSObject* clone = JSObject::cast(Heap::CopyJSObject(obj));
CHECK(clone != obj);
CHECK_EQ(obj->GetElement(0), clone->GetElement(0));
diff --git a/test/cctest/test-mark-compact.cc b/test/cctest/test-mark-compact.cc
index 36b286a..d468c24 100644
--- a/test/cctest/test-mark-compact.cc
+++ b/test/cctest/test-mark-compact.cc
@@ -244,7 +244,7 @@
static int NumberOfWeakCalls = 0;
-static void WeakPointerCallback(v8::Persistent<v8::Object> handle, void* id) {
+static void WeakPointerCallback(v8::Persistent<v8::Value> handle, void* id) {
NumberOfWeakCalls++;
}
diff --git a/test/cctest/test-spaces.cc b/test/cctest/test-spaces.cc
index 98bae69..86aa5f8 100644
--- a/test/cctest/test-spaces.cc
+++ b/test/cctest/test-spaces.cc
@@ -157,27 +157,23 @@
CHECK(Heap::ConfigureHeapDefault());
CHECK(MemoryAllocator::Setup(Heap::MaxCapacity()));
- NewSpace* s = new NewSpace(Heap::InitialSemiSpaceSize(),
- Heap::SemiSpaceSize(),
- NEW_SPACE);
- CHECK(s != NULL);
+ NewSpace new_space;
void* chunk =
MemoryAllocator::ReserveInitialChunk(2 * Heap::YoungGenerationSize());
CHECK(chunk != NULL);
Address start = RoundUp(static_cast<Address>(chunk),
Heap::YoungGenerationSize());
- CHECK(s->Setup(start, Heap::YoungGenerationSize()));
- CHECK(s->HasBeenSetup());
+ CHECK(new_space.Setup(start, Heap::YoungGenerationSize()));
+ CHECK(new_space.HasBeenSetup());
- while (s->Available() >= Page::kMaxHeapObjectSize) {
- Object* obj = s->AllocateRaw(Page::kMaxHeapObjectSize);
+ while (new_space.Available() >= Page::kMaxHeapObjectSize) {
+ Object* obj = new_space.AllocateRaw(Page::kMaxHeapObjectSize);
CHECK(!obj->IsFailure());
- CHECK(s->Contains(HeapObject::cast(obj)));
+ CHECK(new_space.Contains(HeapObject::cast(obj)));
}
- s->TearDown();
- delete s;
+ new_space.TearDown();
MemoryAllocator::TearDown();
}
diff --git a/test/message/regress/regress-73.js b/test/message/regress/regress-73.js
new file mode 100644
index 0000000..72652d7
--- /dev/null
+++ b/test/message/regress/regress-73.js
@@ -0,0 +1,33 @@
+// Copyright 2008 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.
+
+try {
+ throw 'a';
+} catch (e) {
+ throw 'b';
+ print('c');
+}
diff --git a/test/message/bugs/try-finally-linenum.out b/test/message/regress/regress-73.out
similarity index 96%
copy from test/message/bugs/try-finally-linenum.out
copy to test/message/regress/regress-73.out
index 001d447..28606dd 100644
--- a/test/message/bugs/try-finally-linenum.out
+++ b/test/message/regress/regress-73.out
@@ -25,6 +25,6 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-.*%(basename)s:29: foo
- throw "foo";
+*%(basename)s:31: b
+ throw 'b';
^
diff --git a/test/message/bugs/try-finally-linenum.js b/test/message/regress/regress-75.js
similarity index 100%
rename from test/message/bugs/try-finally-linenum.js
rename to test/message/regress/regress-75.js
diff --git a/test/message/bugs/try-finally-linenum.out b/test/message/regress/regress-75.out
similarity index 97%
rename from test/message/bugs/try-finally-linenum.out
rename to test/message/regress/regress-75.out
index 001d447..336d4ce 100644
--- a/test/message/bugs/try-finally-linenum.out
+++ b/test/message/regress/regress-75.out
@@ -25,6 +25,6 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-.*%(basename)s:29: foo
+*%(basename)s:29: foo
throw "foo";
^
diff --git a/test/message/testcfg.py b/test/message/testcfg.py
index 0de4bf1..381ad5c 100644
--- a/test/message/testcfg.py
+++ b/test/message/testcfg.py
@@ -97,8 +97,9 @@
def ListTests(self, current_path, path, mode):
mjsunit = [current_path + [t] for t in self.Ls(self.root)]
+ regress = [current_path + ['regress', t] for t in self.Ls(join(self.root, 'regress'))]
bugs = [current_path + ['bugs', t] for t in self.Ls(join(self.root, 'bugs'))]
- all_tests = mjsunit + bugs
+ all_tests = mjsunit + regress + bugs
result = []
for test in all_tests:
if self.Contains(path, test):
diff --git a/test/message/try-catch-finally-no-message.js b/test/message/try-catch-finally-no-message.js
new file mode 100644
index 0000000..6459ed8
--- /dev/null
+++ b/test/message/try-catch-finally-no-message.js
@@ -0,0 +1,51 @@
+// Copyright 2008 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.
+
+function f() {
+ try {
+ throw "foo";
+ } catch (e) {
+ throw "bar";
+ } finally {
+ return 4;
+ }
+}
+
+function g() {
+ while (true) {
+ try {
+ throw "foo";
+ } catch (e) {
+ throw "bar";
+ } finally {
+ break;
+ }
+ }
+}
+
+f();
+g();
diff --git a/test/message/try-catch-finally-no-message.out b/test/message/try-catch-finally-no-message.out
new file mode 100644
index 0000000..d85fc7d
--- /dev/null
+++ b/test/message/try-catch-finally-no-message.out
@@ -0,0 +1,26 @@
+// Copyright 2008 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.
diff --git a/test/message/try-catch-finally-throw-in-catch-and-finally.js b/test/message/try-catch-finally-throw-in-catch-and-finally.js
new file mode 100644
index 0000000..164ab69
--- /dev/null
+++ b/test/message/try-catch-finally-throw-in-catch-and-finally.js
@@ -0,0 +1,34 @@
+// Copyright 2008 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.
+
+try {
+ throw "foo";
+} catch (e) {
+ throw "bar";
+} finally {
+ throw "baz";
+}
diff --git a/test/message/bugs/try-finally-linenum.out b/test/message/try-catch-finally-throw-in-catch-and-finally.out
similarity index 96%
copy from test/message/bugs/try-finally-linenum.out
copy to test/message/try-catch-finally-throw-in-catch-and-finally.out
index 001d447..e3e2348 100644
--- a/test/message/bugs/try-finally-linenum.out
+++ b/test/message/try-catch-finally-throw-in-catch-and-finally.out
@@ -25,6 +25,6 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-.*%(basename)s:29: foo
- throw "foo";
+*%(basename)s:33: baz
+ throw "baz";
^
diff --git a/test/message/try-catch-finally-throw-in-catch.js b/test/message/try-catch-finally-throw-in-catch.js
new file mode 100644
index 0000000..afba12c
--- /dev/null
+++ b/test/message/try-catch-finally-throw-in-catch.js
@@ -0,0 +1,34 @@
+// Copyright 2008 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.
+
+try {
+ throw "foo";
+} catch (e) {
+ throw "bar";
+} finally {
+ "baz";
+}
diff --git a/test/message/bugs/try-finally-linenum.out b/test/message/try-catch-finally-throw-in-catch.out
similarity index 96%
copy from test/message/bugs/try-finally-linenum.out
copy to test/message/try-catch-finally-throw-in-catch.out
index 001d447..618c13c 100644
--- a/test/message/bugs/try-finally-linenum.out
+++ b/test/message/try-catch-finally-throw-in-catch.out
@@ -25,6 +25,6 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-.*%(basename)s:29: foo
- throw "foo";
+*%(basename)s:31: bar
+ throw "bar";
^
diff --git a/test/message/try-catch-finally-throw-in-finally.js b/test/message/try-catch-finally-throw-in-finally.js
new file mode 100644
index 0000000..387df7a
--- /dev/null
+++ b/test/message/try-catch-finally-throw-in-finally.js
@@ -0,0 +1,34 @@
+// Copyright 2008 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.
+
+try {
+ throw "foo";
+} catch (e) {
+ "bar"
+} finally {
+ throw "baz";
+}
diff --git a/test/message/bugs/try-finally-linenum.out b/test/message/try-catch-finally-throw-in-finally.out
similarity index 96%
copy from test/message/bugs/try-finally-linenum.out
copy to test/message/try-catch-finally-throw-in-finally.out
index 001d447..e3e2348 100644
--- a/test/message/bugs/try-finally-linenum.out
+++ b/test/message/try-catch-finally-throw-in-finally.out
@@ -25,6 +25,6 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-.*%(basename)s:29: foo
- throw "foo";
+*%(basename)s:33: baz
+ throw "baz";
^
diff --git a/test/message/try-finally-throw-in-finally.js b/test/message/try-finally-throw-in-finally.js
new file mode 100644
index 0000000..fbd5764
--- /dev/null
+++ b/test/message/try-finally-throw-in-finally.js
@@ -0,0 +1,32 @@
+// Copyright 2008 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.
+
+try {
+ "foo";
+} finally {
+ throw "bar";
+}
diff --git a/test/message/bugs/try-finally-linenum.out b/test/message/try-finally-throw-in-finally.out
similarity index 96%
copy from test/message/bugs/try-finally-linenum.out
copy to test/message/try-finally-throw-in-finally.out
index 001d447..618c13c 100644
--- a/test/message/bugs/try-finally-linenum.out
+++ b/test/message/try-finally-throw-in-finally.out
@@ -25,6 +25,6 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-.*%(basename)s:29: foo
- throw "foo";
+*%(basename)s:31: bar
+ throw "bar";
^
diff --git a/test/message/try-finally-throw-in-try-and-finally.js b/test/message/try-finally-throw-in-try-and-finally.js
new file mode 100644
index 0000000..1a9660c
--- /dev/null
+++ b/test/message/try-finally-throw-in-try-and-finally.js
@@ -0,0 +1,32 @@
+// Copyright 2008 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.
+
+try {
+ throw "foo";
+} finally {
+ throw "bar";
+}
diff --git a/test/message/bugs/try-finally-linenum.out b/test/message/try-finally-throw-in-try-and-finally.out
similarity index 96%
copy from test/message/bugs/try-finally-linenum.out
copy to test/message/try-finally-throw-in-try-and-finally.out
index 001d447..618c13c 100644
--- a/test/message/bugs/try-finally-linenum.out
+++ b/test/message/try-finally-throw-in-try-and-finally.out
@@ -25,6 +25,6 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-.*%(basename)s:29: foo
- throw "foo";
+*%(basename)s:31: bar
+ throw "bar";
^
diff --git a/test/message/bugs/try-finally-linenum.js b/test/message/try-finally-throw-in-try.js
similarity index 100%
copy from test/message/bugs/try-finally-linenum.js
copy to test/message/try-finally-throw-in-try.js
diff --git a/test/message/bugs/try-finally-linenum.out b/test/message/try-finally-throw-in-try.out
similarity index 97%
copy from test/message/bugs/try-finally-linenum.out
copy to test/message/try-finally-throw-in-try.out
index 001d447..336d4ce 100644
--- a/test/message/bugs/try-finally-linenum.out
+++ b/test/message/try-finally-throw-in-try.out
@@ -25,6 +25,6 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-.*%(basename)s:29: foo
+*%(basename)s:29: foo
throw "foo";
^
diff --git a/test/mjsunit/api-call-after-overridden-exception.js b/test/mjsunit/api-call-after-overridden-exception.js
new file mode 100644
index 0000000..78a2aed
--- /dev/null
+++ b/test/mjsunit/api-call-after-overridden-exception.js
@@ -0,0 +1,41 @@
+// Copyright 2008 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 test of making an API call after an exception thrown in JavaScript
+// has been swallowed by a return in the finally block.
+function foo()
+{
+ try {
+ throw "bar";
+ }
+ finally {
+ return "baz";
+ }
+}
+
+foo();
+print({});
diff --git a/test/mjsunit/regress/regress-124.js b/test/mjsunit/regress/regress-124.js
new file mode 100644
index 0000000..0c7e300
--- /dev/null
+++ b/test/mjsunit/regress/regress-124.js
@@ -0,0 +1,54 @@
+// Copyright 2008 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.
+
+assertEquals("[object global]", this.toString());
+assertEquals("[object global]", toString());
+
+assertEquals("[object global]", eval("this.toString()"));
+assertEquals("[object global]", eval("toString()"));
+
+assertEquals("[object global]", eval("var f; this.toString()"));
+assertEquals("[object global]", eval("var f; toString()"));
+
+
+function F(f) {
+ assertEquals("[object global]", this.toString());
+ assertEquals("[object global]", toString());
+
+ assertEquals("[object global]", eval("this.toString()"));
+ assertEquals("[object global]", eval("toString()"));
+
+ assertEquals("[object global]", eval("var f; this.toString()"));
+ assertEquals("[object global]", eval("var f; toString()"));
+
+ assertEquals("[object global]", eval("f()"));
+
+ // Receiver should be the arguments object here.
+ assertEquals("[object Object]", eval("arguments[0]()"));
+}
+
+F(Object.prototype.toString);
diff --git a/test/mozilla/mozilla.status b/test/mozilla/mozilla.status
index b43ea17..46ecc48 100644
--- a/test/mozilla/mozilla.status
+++ b/test/mozilla/mozilla.status
@@ -170,6 +170,12 @@
js1_5/Regress/regress-404755: PASS || FAIL
+# Test that depends on timer resolution. Fails every now and then
+# if we're unlucky enough to get a context switch at a bad time.
+js1_5/extensions/regress-363258: PASS || FAIL
+
+
+
##################### INCOMPATIBLE TESTS #####################
# This section is for tests that fail in both V8 and KJS. Thus they
@@ -749,11 +755,6 @@
js1_5/decompilation/regress-383721: PASS || FAIL
js1_5/decompilation/regress-406555: PASS || FAIL
-[ $system == windows ]
-
-# This test is flaky because of the default timer resolution on Windows.
-js1_5/extensions/regress-363258: PASS || FAIL
-mozilla/js1_5/GC/regress-383269-02: PASS, FLAKY IF $mode == debug
[ $FAST == yes ]
@@ -763,6 +764,7 @@
js1_5/Regress/regress-312588: SKIP
js1_5/Regress/regress-271716-n: SKIP
+
[ $FAST == yes && $ARCH == arm ]
# In fast mode on arm we try to skip all tests that would time out,
diff --git a/tools/js2c.py b/tools/js2c.py
index 181edb6..fb6d6c5 100755
--- a/tools/js2c.py
+++ b/tools/js2c.py
@@ -200,25 +200,30 @@
%(source_lines)s\
- int Natives::GetBuiltinsCount() {
+ template <>
+ int NativesCollection<%(type)s>::GetBuiltinsCount() {
return %(builtin_count)i;
}
- int Natives::GetDelayCount() {
+ template <>
+ int NativesCollection<%(type)s>::GetDelayCount() {
return %(delay_count)i;
}
- int Natives::GetIndex(const char* name) {
+ template <>
+ int NativesCollection<%(type)s>::GetIndex(const char* name) {
%(get_index_cases)s\
return -1;
}
- Vector<const char> Natives::GetScriptSource(int index) {
+ template <>
+ Vector<const char> NativesCollection<%(type)s>::GetScriptSource(int index) {
%(get_script_source_cases)s\
return Vector<const char>("", 0);
}
- Vector<const char> Natives::GetScriptName(int index) {
+ template <>
+ Vector<const char> NativesCollection<%(type)s>::GetScriptName(int index) {
%(get_script_name_cases)s\
return Vector<const char>("", 0);
}
@@ -323,25 +328,30 @@
'source_lines': "\n".join(source_lines),
'get_index_cases': "".join(get_index_cases),
'get_script_source_cases': "".join(get_script_source_cases),
- 'get_script_name_cases': "".join(get_script_name_cases)
+ 'get_script_name_cases': "".join(get_script_name_cases),
+ 'type': env['TYPE']
})
output.close()
- output = open(str(target[1]), "w")
- output.write(HEADER_TEMPLATE % {
- 'builtin_count': len(ids) + len(delay_ids),
- 'delay_count': len(delay_ids),
- 'source_lines': "\n".join(source_lines_empty),
- 'get_index_cases': "".join(get_index_cases),
- 'get_script_source_cases': "".join(get_script_source_cases),
- 'get_script_name_cases': "".join(get_script_name_cases)
- })
- output.close()
+
+ if len(target) > 1:
+ output = open(str(target[1]), "w")
+ output.write(HEADER_TEMPLATE % {
+ 'builtin_count': len(ids) + len(delay_ids),
+ 'delay_count': len(delay_ids),
+ 'source_lines': "\n".join(source_lines_empty),
+ 'get_index_cases': "".join(get_index_cases),
+ 'get_script_source_cases': "".join(get_script_source_cases),
+ 'get_script_name_cases': "".join(get_script_name_cases),
+ 'type': env['TYPE']
+ })
+ output.close()
def main():
natives = sys.argv[1]
natives_empty = sys.argv[2]
- source_files = sys.argv[3:]
- JS2C(source_files, [natives, natives_empty], None)
+ type = sys.argv[3]
+ source_files = sys.argv[4:]
+ JS2C(source_files, [natives, natives_empty], { 'TYPE': type })
if __name__ == "__main__":
main()
diff --git a/tools/linux-tick-processor.py b/tools/linux-tick-processor.py
index 794dcea..d2cabac 100755
--- a/tools/linux-tick-processor.py
+++ b/tools/linux-tick-processor.py
@@ -30,13 +30,18 @@
# Usage: process-ticks.py <logfile>
# Where <logfile> is the log file name (eg, v8.log).
-import os, re, sys, tickprocessor, getopt;
+import subprocess, re, sys, tickprocessor, getopt
class LinuxTickProcessor(tickprocessor.TickProcessor):
def ParseVMSymbols(self, filename, start, end):
"""Extract symbols and add them to the cpp entries."""
- pipe = os.popen('nm -n %s | c++filt' % filename, 'r')
+ # Extra both dynamic and non-dynamic symbols.
+ command = 'nm -C -n "%s"; nm -C -n -D "%s"' % (filename, filename)
+ process = subprocess.Popen(command, shell=True,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+ pipe = process.stdout
try:
for line in pipe:
row = re.match('^([0-9a-fA-F]{8}) . (.*)$', line)
diff --git a/tools/v8.xcodeproj/project.pbxproj b/tools/v8.xcodeproj/project.pbxproj
index 205fd95..6a04153 100644
--- a/tools/v8.xcodeproj/project.pbxproj
+++ b/tools/v8.xcodeproj/project.pbxproj
@@ -952,7 +952,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "set -ex\nJS_FILES=\"runtime.js\"\\\n\" v8natives.js\"\\\n\" array.js\"\\\n\" string.js\"\\\n\" uri.js\"\\\n\" math.js\"\\\n\" messages.js\"\\\n\" apinatives.js\"\\\n\" debug-delay.js\"\\\n\" mirror-delay.js\"\\\n\" date-delay.js\"\\\n\" regexp-delay.js\"\\\n\" macros.py\"\n\nV8ROOT=\"${SRCROOT}/..\"\n\nSRC_DIR=\"${V8ROOT}/src\"\n\nNATIVE_JS_FILES=\"\"\n\nfor i in ${JS_FILES} ; do\n NATIVE_JS_FILES+=\"${SRC_DIR}/${i} \"\ndone\n\nV8_GENERATED_SOURCES_DIR=\"${CONFIGURATION_TEMP_DIR}/generated\"\nmkdir -p \"${V8_GENERATED_SOURCES_DIR}\"\n\nLIBRARIES_CC=\"${V8_GENERATED_SOURCES_DIR}/libraries.cc\"\nLIBRARIES_EMPTY_CC=\"${V8_GENERATED_SOURCES_DIR}/libraries-empty.cc\"\n\npython \"${V8ROOT}/tools/js2c.py\" \\\n \"${LIBRARIES_CC}.new\" \\\n \"${LIBRARIES_EMPTY_CC}.new\" \\\n ${NATIVE_JS_FILES}\n\n# Only use the new files if they're different from the existing files (if any),\n# preserving the existing files' timestamps when there are no changes. This\n# minimizes unnecessary build activity for a no-change build.\n\nif ! diff -q \"${LIBRARIES_CC}.new\" \"${LIBRARIES_CC}\" >& /dev/null ; then\n mv \"${LIBRARIES_CC}.new\" \"${LIBRARIES_CC}\"\nelse\n rm \"${LIBRARIES_CC}.new\"\nfi\n\nif ! diff -q \"${LIBRARIES_EMPTY_CC}.new\" \"${LIBRARIES_EMPTY_CC}\" >& /dev/null ; then\n mv \"${LIBRARIES_EMPTY_CC}.new\" \"${LIBRARIES_EMPTY_CC}\"\nelse\n rm \"${LIBRARIES_EMPTY_CC}.new\"\nfi\n";
+ shellScript = "set -ex\nJS_FILES=\"runtime.js\"\\\n\" v8natives.js\"\\\n\" array.js\"\\\n\" string.js\"\\\n\" uri.js\"\\\n\" math.js\"\\\n\" messages.js\"\\\n\" apinatives.js\"\\\n\" debug-delay.js\"\\\n\" mirror-delay.js\"\\\n\" date-delay.js\"\\\n\" regexp-delay.js\"\\\n\" macros.py\"\n\nV8ROOT=\"${SRCROOT}/..\"\n\nSRC_DIR=\"${V8ROOT}/src\"\n\nNATIVE_JS_FILES=\"\"\n\nfor i in ${JS_FILES} ; do\n NATIVE_JS_FILES+=\"${SRC_DIR}/${i} \"\ndone\n\nV8_GENERATED_SOURCES_DIR=\"${CONFIGURATION_TEMP_DIR}/generated\"\nmkdir -p \"${V8_GENERATED_SOURCES_DIR}\"\n\nLIBRARIES_CC=\"${V8_GENERATED_SOURCES_DIR}/libraries.cc\"\nLIBRARIES_EMPTY_CC=\"${V8_GENERATED_SOURCES_DIR}/libraries-empty.cc\"\n\npython \"${V8ROOT}/tools/js2c.py\" \\\n \"${LIBRARIES_CC}.new\" \\\n \"${LIBRARIES_EMPTY_CC}.new\" \\\n \"CORE\" \\\n ${NATIVE_JS_FILES}\n\n# Only use the new files if they're different from the existing files (if any),\n# preserving the existing files' timestamps when there are no changes. This\n# minimizes unnecessary build activity for a no-change build.\n\nif ! diff -q \"${LIBRARIES_CC}.new\" \"${LIBRARIES_CC}\" >& /dev/null ; then\n mv \"${LIBRARIES_CC}.new\" \"${LIBRARIES_CC}\"\nelse\n rm \"${LIBRARIES_CC}.new\"\nfi\n\nif ! diff -q \"${LIBRARIES_EMPTY_CC}.new\" \"${LIBRARIES_EMPTY_CC}\" >& /dev/null ; then\n mv \"${LIBRARIES_EMPTY_CC}.new\" \"${LIBRARIES_EMPTY_CC}\"\nelse\n rm \"${LIBRARIES_EMPTY_CC}.new\"\nfi\n";
};
89F23C3D0E78D5B2006B2466 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
@@ -965,7 +965,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "set -ex\nJS_FILES=\"runtime.js\"\\\n\" v8natives.js\"\\\n\" array.js\"\\\n\" string.js\"\\\n\" uri.js\"\\\n\" math.js\"\\\n\" messages.js\"\\\n\" apinatives.js\"\\\n\" debug-delay.js\"\\\n\" mirror-delay.js\"\\\n\" date-delay.js\"\\\n\" regexp-delay.js\"\\\n\" macros.py\"\n\nV8ROOT=\"${SRCROOT}/..\"\n\nSRC_DIR=\"${V8ROOT}/src\"\n\nNATIVE_JS_FILES=\"\"\n\nfor i in ${JS_FILES} ; do\n NATIVE_JS_FILES+=\"${SRC_DIR}/${i} \"\ndone\n\nV8_GENERATED_SOURCES_DIR=\"${CONFIGURATION_TEMP_DIR}/generated\"\nmkdir -p \"${V8_GENERATED_SOURCES_DIR}\"\n\nLIBRARIES_CC=\"${V8_GENERATED_SOURCES_DIR}/libraries.cc\"\nLIBRARIES_EMPTY_CC=\"${V8_GENERATED_SOURCES_DIR}/libraries-empty.cc\"\n\npython \"${V8ROOT}/tools/js2c.py\" \\\n \"${LIBRARIES_CC}.new\" \\\n \"${LIBRARIES_EMPTY_CC}.new\" \\\n ${NATIVE_JS_FILES}\n\n# Only use the new files if they're different from the existing files (if any),\n# preserving the existing files' timestamps when there are no changes. This\n# minimizes unnecessary build activity for a no-change build.\n\nif ! diff -q \"${LIBRARIES_CC}.new\" \"${LIBRARIES_CC}\" >& /dev/null ; then\n mv \"${LIBRARIES_CC}.new\" \"${LIBRARIES_CC}\"\nelse\n rm \"${LIBRARIES_CC}.new\"\nfi\n\nif ! diff -q \"${LIBRARIES_EMPTY_CC}.new\" \"${LIBRARIES_EMPTY_CC}\" >& /dev/null ; then\n mv \"${LIBRARIES_EMPTY_CC}.new\" \"${LIBRARIES_EMPTY_CC}\"\nelse\n rm \"${LIBRARIES_EMPTY_CC}.new\"\nfi\n";
+ shellScript = "set -ex\nJS_FILES=\"runtime.js\"\\\n\" v8natives.js\"\\\n\" array.js\"\\\n\" string.js\"\\\n\" uri.js\"\\\n\" math.js\"\\\n\" messages.js\"\\\n\" apinatives.js\"\\\n\" debug-delay.js\"\\\n\" mirror-delay.js\"\\\n\" date-delay.js\"\\\n\" regexp-delay.js\"\\\n\" macros.py\"\n\nV8ROOT=\"${SRCROOT}/..\"\n\nSRC_DIR=\"${V8ROOT}/src\"\n\nNATIVE_JS_FILES=\"\"\n\nfor i in ${JS_FILES} ; do\n NATIVE_JS_FILES+=\"${SRC_DIR}/${i} \"\ndone\n\nV8_GENERATED_SOURCES_DIR=\"${CONFIGURATION_TEMP_DIR}/generated\"\nmkdir -p \"${V8_GENERATED_SOURCES_DIR}\"\n\nLIBRARIES_CC=\"${V8_GENERATED_SOURCES_DIR}/libraries.cc\"\nLIBRARIES_EMPTY_CC=\"${V8_GENERATED_SOURCES_DIR}/libraries-empty.cc\"\n\npython \"${V8ROOT}/tools/js2c.py\" \\\n \"${LIBRARIES_CC}.new\" \\\n \"${LIBRARIES_EMPTY_CC}.new\" \\\n \"CORE\" \\\n ${NATIVE_JS_FILES}\n\n# Only use the new files if they're different from the existing files (if any),\n# preserving the existing files' timestamps when there are no changes. This\n# minimizes unnecessary build activity for a no-change build.\n\nif ! diff -q \"${LIBRARIES_CC}.new\" \"${LIBRARIES_CC}\" >& /dev/null ; then\n mv \"${LIBRARIES_CC}.new\" \"${LIBRARIES_CC}\"\nelse\n rm \"${LIBRARIES_CC}.new\"\nfi\n\nif ! diff -q \"${LIBRARIES_EMPTY_CC}.new\" \"${LIBRARIES_EMPTY_CC}\" >& /dev/null ; then\n mv \"${LIBRARIES_EMPTY_CC}.new\" \"${LIBRARIES_EMPTY_CC}\"\nelse\n rm \"${LIBRARIES_EMPTY_CC}.new\"\nfi\n";
};
/* End PBXShellScriptBuildPhase section */
diff --git a/tools/visual_studio/js2c.cmd b/tools/visual_studio/js2c.cmd
index 10df45e..b6a46a2 100644
--- a/tools/visual_studio/js2c.cmd
+++ b/tools/visual_studio/js2c.cmd
@@ -3,4 +3,4 @@
set TARGET_DIR=%2
set PYTHON="..\..\..\third_party\python_24\python.exe"
if not exist %PYTHON% set PYTHON=python.exe
-%PYTHON% ..\js2c.py %TARGET_DIR%\natives.cc %TARGET_DIR%\natives-empty.cc %SOURCE_DIR%\macros.py %SOURCE_DIR%\runtime.js %SOURCE_DIR%\v8natives.js %SOURCE_DIR%\array.js %SOURCE_DIR%\string.js %SOURCE_DIR%\uri.js %SOURCE_DIR%\math.js %SOURCE_DIR%\messages.js %SOURCE_DIR%\apinatives.js %SOURCE_DIR%\debug-delay.js %SOURCE_DIR%\mirror-delay.js %SOURCE_DIR%\date-delay.js %SOURCE_DIR%\regexp-delay.js
+%PYTHON% ..\js2c.py %TARGET_DIR%\natives.cc %TARGET_DIR%\natives-empty.cc CORE %SOURCE_DIR%\macros.py %SOURCE_DIR%\runtime.js %SOURCE_DIR%\v8natives.js %SOURCE_DIR%\array.js %SOURCE_DIR%\string.js %SOURCE_DIR%\uri.js %SOURCE_DIR%\math.js %SOURCE_DIR%\messages.js %SOURCE_DIR%\apinatives.js %SOURCE_DIR%\debug-delay.js %SOURCE_DIR%\mirror-delay.js %SOURCE_DIR%\date-delay.js %SOURCE_DIR%\regexp-delay.js