- Extended lazy loading to general objects, not just functions.

- Added lazily loaded JSON object.


git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@1784 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent a4d756a1
......@@ -123,6 +123,7 @@ debug-delay.js
mirror-delay.js
date-delay.js
regexp-delay.js
json-delay.js
'''.split()
......
......@@ -127,27 +127,20 @@ static void GenerateDictionaryLoad(MacroAssembler* masm,
}
// Helper function used to check that a value is either not a function
// or is loaded if it is a function.
static void GenerateCheckNonFunctionOrLoaded(MacroAssembler* masm,
Label* miss,
Register value,
Register scratch) {
// Helper function used to check that a value is either not an object
// or is loaded if it is an object.
static void GenerateCheckNonObjectOrLoaded(MacroAssembler* masm,
Label* miss,
Register value,
Register scratch) {
Label done;
// Check if the value is a Smi.
__ tst(value, Operand(kSmiTagMask));
__ b(eq, &done);
// Check if the value is a function.
__ ldr(scratch, FieldMemOperand(value, HeapObject::kMapOffset));
__ ldrb(scratch, FieldMemOperand(scratch, Map::kInstanceTypeOffset));
__ cmp(scratch, Operand(JS_FUNCTION_TYPE));
__ b(ne, &done);
// Check if the function has been loaded.
__ ldr(scratch,
FieldMemOperand(value, JSFunction::kSharedFunctionInfoOffset));
__ ldr(scratch,
FieldMemOperand(scratch, SharedFunctionInfo::kLazyLoadDataOffset));
__ cmp(scratch, Operand(Factory::undefined_value()));
// Check if the object has been loaded.
__ ldr(scratch, FieldMemOperand(value, JSObject::kMapOffset));
__ ldrb(scratch, FieldMemOperand(scratch, Map::kBitField2Offset));
__ tst(scratch, Operand(1 << Map::kNeedsLoading));
__ b(ne, miss);
__ bind(&done);
}
......@@ -284,9 +277,9 @@ static void GenerateNormalHelper(MacroAssembler* masm,
__ b(ne, miss);
// Check that the function has been loaded.
__ ldr(r0, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
__ ldr(r0, FieldMemOperand(r0, SharedFunctionInfo::kLazyLoadDataOffset));
__ cmp(r0, Operand(Factory::undefined_value()));
__ ldr(r0, FieldMemOperand(r1, JSObject::kMapOffset));
__ ldrb(r0, FieldMemOperand(r0, Map::kBitField2Offset));
__ tst(r0, Operand(1 << Map::kNeedsLoading));
__ b(ne, miss);
// Patch the receiver with the global proxy if necessary.
......@@ -470,7 +463,7 @@ void LoadIC::GenerateNormal(MacroAssembler* masm) {
__ bind(&probe);
GenerateDictionaryLoad(masm, &miss, r1, r0);
GenerateCheckNonFunctionOrLoaded(masm, &miss, r0, r1);
GenerateCheckNonObjectOrLoaded(masm, &miss, r0, r1);
__ Ret();
// Global object access: Check access rights.
......
......@@ -741,6 +741,19 @@ void Genesis::CreateRoots(v8::Handle<v8::ObjectTemplate> global_template,
global_context()->set_regexp_function(*regexp_fun);
}
{ // -- J S O N
Handle<String> name = Factory::NewStringFromAscii(CStrVector("JSON"));
Handle<JSFunction> cons = Factory::NewFunction(
name,
Factory::the_hole_value());
cons->SetInstancePrototype(global_context()->initial_object_prototype());
cons->SetInstanceClassName(*name);
Handle<JSObject> json_object = Factory::NewJSObject(cons, TENURED);
ASSERT(json_object->IsJSObject());
SetProperty(global, name, json_object, DONT_ENUM);
global_context()->set_json_object(*json_object);
}
{ // --- arguments_boilerplate_
// Make sure we can recognize argument objects at runtime.
// This is done by introducing an anonymous function with
......@@ -1068,6 +1081,10 @@ bool Genesis::InstallNatives() {
Natives::GetIndex("regexp"),
Top::global_context(),
Handle<Context>(Top::context()->runtime_context()));
SetupLazy(Handle<JSObject>(global_context()->json_object()),
Natives::GetIndex("json"),
Top::global_context(),
Handle<Context>(Top::context()->runtime_context()));
} else if (strlen(FLAG_natives_file) != 0) {
// Otherwise install natives from natives file if file exists and
......
......@@ -64,6 +64,7 @@ enum ContextLookupFlags {
V(OBJECT_FUNCTION_INDEX, JSFunction, object_function) \
V(ARRAY_FUNCTION_INDEX, JSFunction, array_function) \
V(DATE_FUNCTION_INDEX, JSFunction, date_function) \
V(JSON_OBJECT_INDEX, JSObject, json_object) \
V(REGEXP_FUNCTION_INDEX, JSFunction, regexp_function) \
V(INITIAL_OBJECT_PROTOTYPE_INDEX, JSObject, initial_object_prototype) \
V(CREATE_DATE_FUN_INDEX, JSFunction, create_date_fun) \
......@@ -186,6 +187,7 @@ class Context: public FixedArray {
OBJECT_FUNCTION_INDEX,
ARRAY_FUNCTION_INDEX,
DATE_FUNCTION_INDEX,
JSON_OBJECT_INDEX,
REGEXP_FUNCTION_INDEX,
CREATE_DATE_FUN_INDEX,
TO_NUMBER_FUN_INDEX,
......
......@@ -985,6 +985,25 @@ function DateToGMTString() {
}
function PadInt(n) {
// Format integers to have at least two digits.
return n < 10 ? '0' + n : n;
}
function DateToISOString() {
return this.getUTCFullYear() + '-' + PadInt(this.getUTCMonth() + 1) +
'-' + PadInt(this.getUTCDate()) + 'T' + PadInt(this.getUTCHours()) +
':' + PadInt(this.getUTCMinutes()) + ':' + PadInt(this.getUTCSeconds()) +
'Z';
}
function DateToJSON(key) {
return CheckJSONPrimitive(this.toISOString());
}
// -------------------------------------------------------------------
function SetupDate() {
......@@ -1044,7 +1063,9 @@ function SetupDate() {
"toGMTString", DateToGMTString,
"toUTCString", DateToUTCString,
"getYear", DateGetYear,
"setYear", DateSetYear
"setYear", DateSetYear,
"toISOString", DateToISOString,
"toJSON", DateToJSON
));
}
......
......@@ -627,9 +627,9 @@ OptimizedObjectForAddingMultipleProperties::
}
void LoadLazy(Handle<JSFunction> fun, bool* pending_exception) {
void LoadLazy(Handle<JSObject> obj, bool* pending_exception) {
HandleScope scope;
Handle<FixedArray> info(FixedArray::cast(fun->shared()->lazy_load_data()));
Handle<FixedArray> info(FixedArray::cast(obj->map()->constructor()));
int index = Smi::cast(info->get(0))->value();
ASSERT(index >= 0);
Handle<Context> compile_context(Context::cast(info->get(1)));
......@@ -674,27 +674,39 @@ void LoadLazy(Handle<JSFunction> fun, bool* pending_exception) {
// Reset the lazy load data before running the script to make sure
// not to get recursive lazy loading.
fun->shared()->set_lazy_load_data(Heap::undefined_value());
obj->map()->set_needs_loading(false);
obj->map()->set_constructor(info->get(3));
// Run the script.
Handle<JSFunction> script_fun(
Factory::NewFunctionFromBoilerplate(boilerplate, function_context));
Execution::Call(script_fun, receiver, 0, NULL, pending_exception);
// If lazy loading failed, restore the unloaded state of fun.
if (*pending_exception) fun->shared()->set_lazy_load_data(*info);
// If lazy loading failed, restore the unloaded state of obj.
if (*pending_exception) {
obj->map()->set_needs_loading(true);
obj->map()->set_constructor(*info);
}
}
void SetupLazy(Handle<JSFunction> fun,
void SetupLazy(Handle<JSObject> obj,
int index,
Handle<Context> compile_context,
Handle<Context> function_context) {
Handle<FixedArray> arr = Factory::NewFixedArray(3);
Handle<FixedArray> arr = Factory::NewFixedArray(4);
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
fun->shared()->set_lazy_load_data(*arr);
arr->set(3, obj->map()->constructor()); // Remember the constructor
Handle<Map> old_map(obj->map());
Handle<Map> new_map = Factory::CopyMap(old_map);
obj->set_map(*new_map);
new_map->set_needs_loading(true);
// Store the lazy loading info in the constructor field. We'll
// reestablish the constructor from the fixed array after loading.
new_map->set_constructor(*arr);
ASSERT(!obj->IsLoaded());
}
} } // namespace v8::internal
......@@ -301,11 +301,11 @@ bool CompileLazy(Handle<JSFunction> function, ClearExceptionFlag flag);
bool CompileLazyInLoop(Handle<JSFunction> function, ClearExceptionFlag flag);
// These deal with lazily loaded properties.
void SetupLazy(Handle<JSFunction> fun,
void SetupLazy(Handle<JSObject> obj,
int index,
Handle<Context> compile_context,
Handle<Context> function_context);
void LoadLazy(Handle<JSFunction> fun, bool* pending_exception);
void LoadLazy(Handle<JSObject> obj, bool* pending_exception);
class NoHandleAllocation BASE_EMBEDDED {
public:
......
......@@ -939,6 +939,7 @@ Object* Heap::AllocateMap(InstanceType instance_type, int instance_size) {
map->set_code_cache(empty_fixed_array());
map->set_unused_property_fields(0);
map->set_bit_field(0);
map->set_bit_field2(0);
return map;
}
......@@ -1409,7 +1410,6 @@ Object* Heap::AllocateSharedFunctionInfo(Object* name) {
share->set_formal_parameter_count(0);
share->set_instance_class_name(Object_symbol());
share->set_function_data(undefined_value());
share->set_lazy_load_data(undefined_value());
share->set_script(undefined_value());
share->set_start_position_and_type(0);
share->set_debug_info(undefined_value());
......
......@@ -123,23 +123,19 @@ static void GenerateDictionaryLoad(MacroAssembler* masm, Label* miss_label,
}
// Helper function used to check that a value is either not a function
// or is loaded if it is a function.
static void GenerateCheckNonFunctionOrLoaded(MacroAssembler* masm, Label* miss,
Register value, Register scratch) {
// Helper function used to check that a value is either not an object
// or is loaded if it is an object.
static void GenerateCheckNonObjectOrLoaded(MacroAssembler* masm, Label* miss,
Register value, Register scratch) {
Label done;
// Check if the value is a Smi.
__ test(value, Immediate(kSmiTagMask));
__ j(zero, &done, not_taken);
// Check if the value is a function.
__ CmpObjectType(value, JS_FUNCTION_TYPE, scratch);
__ j(not_equal, &done, taken);
// Check if the function has been loaded.
__ mov(scratch, FieldOperand(value, JSFunction::kSharedFunctionInfoOffset));
__ mov(scratch,
FieldOperand(scratch, SharedFunctionInfo::kLazyLoadDataOffset));
__ cmp(scratch, Factory::undefined_value());
__ j(not_equal, miss, not_taken);
// Check if the object has been loaded.
__ mov(scratch, FieldOperand(value, JSFunction::kMapOffset));
__ mov(scratch, FieldOperand(scratch, Map::kBitField2Offset));
__ test(scratch, Immediate(1 << Map::kNeedsLoading));
__ j(not_zero, miss, not_taken);
__ bind(&done);
}
......@@ -268,7 +264,7 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
__ j(not_zero, &slow, not_taken);
// Probe the dictionary leaving result in ecx.
GenerateDictionaryLoad(masm, &slow, ebx, ecx, edx, eax);
GenerateCheckNonFunctionOrLoaded(masm, &slow, ecx, edx);
GenerateCheckNonObjectOrLoaded(masm, &slow, ecx, edx);
__ mov(eax, Operand(ecx));
__ IncrementCounter(&Counters::keyed_load_generic_symbol, 1);
__ ret(0);
......@@ -493,10 +489,10 @@ static void GenerateNormalHelper(MacroAssembler* masm,
__ j(not_equal, miss, not_taken);
// Check that the function has been loaded.
__ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
__ mov(edx, FieldOperand(edx, SharedFunctionInfo::kLazyLoadDataOffset));
__ cmp(edx, Factory::undefined_value());
__ j(not_equal, miss, not_taken);
__ mov(edx, FieldOperand(edi, JSFunction::kMapOffset));
__ mov(edx, FieldOperand(edx, Map::kBitField2Offset));
__ test(edx, Immediate(1 << Map::kNeedsLoading));
__ j(not_zero, miss, not_taken);
// Patch the receiver with the global proxy if necessary.
if (is_global_object) {
......@@ -683,7 +679,7 @@ void LoadIC::GenerateNormal(MacroAssembler* masm) {
// Search the dictionary placing the result in eax.
__ bind(&probe);
GenerateDictionaryLoad(masm, &miss, edx, eax, ebx, ecx);
GenerateCheckNonFunctionOrLoaded(masm, &miss, eax, edx);
GenerateCheckNonObjectOrLoaded(masm, &miss, eax, edx);
__ ret(0);
// Global object access: Check access rights.
......
// Copyright 2009 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
var $JSON = global.JSON;
function IsValidJSON(s) {
// All empty whitespace is not valid.
if (/^\s*$/.test(s))
return false;
// This is taken from http://www.json.org/json2.js which is released to the
// public domain.
var backslashesRe = /\\["\\\/bfnrtu]/g;
var simpleValuesRe =
/"[^"\\\n\r\x00-\x1f\x7f-\x9f]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
var openBracketsRe = /(?:^|:|,)(?:[\s]*\[)+/g;
var remainderRe = /^[\],:{}\s]*$/;
return remainderRe.test(s.replace(backslashesRe, '@').
replace(simpleValuesRe, ']').
replace(openBracketsRe, ''));
}
function ParseJSONUnfiltered(text) {
var s = $String(text);
if (IsValidJSON(s)) {
try {
return global.eval('(' + s + ')');
} catch (e) {
// ignore exceptions
}
}
throw MakeSyntaxError('invalid_json', [s]);
}
function Revive(holder, name, reviver) {
var val = holder[name];
if (IS_OBJECT(val)) {
if (IS_ARRAY(val)) {
var length = val.length;
for (var i = 0; i < length; i++) {
var newElement = Revive(val, $String(i), reviver);
val[i] = newElement;
}
} else {
for (var p in val) {
if (ObjectHasOwnProperty.call(val, p)) {
var newElement = Revive(val, p, reviver);
if (IS_UNDEFINED(newElement)) {
delete val[p];
} else {
val[p] = newElement;
}
}
}
}
}
return reviver.call(holder, name, val);
}
function JSONParse(text, reviver) {
var unfiltered = ParseJSONUnfiltered(text);
if (IS_FUNCTION(reviver)) {
return Revive({'': unfiltered}, '', reviver);
} else {
return unfiltered;
}
}
var characterQuoteCache = {
'\"': '\\"',
'\\': '\\\\',
'/': '\\/',
'\b': '\\b',
'\f': '\\f',
'\n': '\\n',
'\r': '\\r',
'\t': '\\t',
'\x0B': '\\u000b'
};
function QuoteSingleJSONCharacter(c) {
if (c in characterQuoteCache)
return characterQuoteCache[c];
var charCode = c.charCodeAt(0);
var result;
if (charCode < 16) result = '\\u000';
else if (charCode < 256) result = '\\u00';
else if (charCode < 4096) result = '\\u0';
else result = '\\u';
result += charCode.toString(16);
characterQuoteCache[c] = result;
return result;
}
function QuoteJSONString(str) {
var quotable = /[\\\"\x00-\x1f\x80-\uffff]/g;
return '"' + str.replace(quotable, QuoteSingleJSONCharacter) + '"';
}
function StackContains(stack, val) {
var length = stack.length;
for (var i = 0; i < length; i++) {
if (stack[i] === val)
return true;
}
return false;
}
function SerializeArray(value, replacer, stack, indent, gap) {
if (StackContains(stack, value))
throw MakeTypeError('circular_structure', []);
stack.push(value);
var stepback = indent;
indent += gap;
var partial = [];
var len = value.length;
for (var i = 0; i < len; i++) {
var strP = JSONSerialize($String(i), value, replacer, stack,
indent, gap);
if (IS_UNDEFINED(strP))
strP = "null";
partial.push(strP);
}
var final;
if (gap == "") {
final = "[" + partial.join(",") + "]";
} else if (partial.length > 0) {
var separator = ",\n" + indent;
final = "[\n" + indent + partial.join(separator) + "\n" +
stepback + "]";
} else {
final = "[]";
}
stack.pop();
return final;
}
function SerializeObject(value, replacer, stack, indent, gap) {
if (StackContains(stack, value))
throw MakeTypeError('circular_structure', []);
stack.push(value);
var stepback = indent;
indent += gap;
var partial = [];
if (IS_ARRAY(replacer)) {
var length = replacer.length;
for (var i = 0; i < length; i++) {
if (ObjectHasOwnProperty.call(replacer, i)) {
var p = replacer[i];
var strP = JSONSerialize(p, value, replacer, stack, indent, gap);
if (!IS_UNDEFINED(strP)) {
var member = QuoteJSONString(p) + ":";
if (gap != "") member += " ";
member += strP;
partial.push(member);
}
}
}
} else {
for (var p in value) {
if (ObjectHasOwnProperty.call(value, p)) {
var strP = JSONSerialize(p, value, replacer, stack, indent, gap);
if (!IS_UNDEFINED(strP)) {
var member = QuoteJSONString(p) + ":";
if (gap != "") member += " ";
member += strP;
partial.push(member);
}
}
}
}
var final;
if (gap == "") {
final = "{" + partial.join(",") + "}";
} else if (partial.length > 0) {
var separator = ",\n" + indent;
final = "{\n" + indent + partial.join(separator) + "\n" +
stepback + "}";
} else {
final = "{}";
}
stack.pop();
return final;
}
function JSONSerialize(key, holder, replacer, stack, indent, gap) {
var value = holder[key];
if (IS_OBJECT(value) && value) {
var toJSON = value.toJSON;
if (IS_FUNCTION(toJSON))
value = toJSON.call(value, key);
}
if (IS_FUNCTION(replacer))
value = replacer.call(holder, key, value);
// Unwrap value if necessary
if (IS_OBJECT(value)) {
if (IS_NUMBER_WRAPPER(value)) {
value = $Number(value);
} else if (IS_STRING_WRAPPER(value)) {
value = $String(value);
}
}
switch (typeof value) {
case "string":
return QuoteJSONString(value);
case "object":
if (!value) {
return "null";
} else if (IS_ARRAY(value)) {
return SerializeArray(value, replacer, stack, indent, gap);
} else {
return SerializeObject(value, replacer, stack, indent, gap);
}
case "number":
return $isFinite(value) ? $String(value) : "null";
case "boolean":
return value ? "true" : "false";
}
}
function JSONStringify(value, replacer, space) {
var stack = [];
var indent = "";
if (IS_OBJECT(space)) {
// Unwrap 'space' if it is wrapped
if (IS_NUMBER_WRAPPER(space)) {
space = $Number(space);
} else if (IS_STRING_WRAPPER(space)) {
space = $String(space);
}
}
var gap;
if (IS_NUMBER(space)) {
space = $Math.min(space, 100);
gap = "";
for (var i = 0; i < space; i++)
gap += " ";
} else if (IS_STRING(space)) {
gap = space;
} else {
gap = "";
}
return JSONSerialize('', {'': value}, replacer, stack, indent, gap);
}
function SetupJSON() {
InstallFunctions($JSON, DONT_ENUM, $Array(
"parse", JSONParse,
"stringify", JSONStringify
));
}
SetupJSON();
......@@ -84,6 +84,8 @@ macro IS_BOOLEAN(arg) = (typeof(arg) === 'boolean');
macro IS_REGEXP(arg) = %HasRegExpClass(arg);
macro IS_ARRAY(arg) = %HasArrayClass(arg);
macro IS_DATE(arg) = %HasDateClass(arg);
macro IS_NUMBER_WRAPPER(arg) = %HasNumberClass(arg);
macro IS_STRING_WRAPPER(arg) = %HasStringClass(arg);
macro IS_ERROR(arg) = (%ClassOf(arg) === 'Error');
macro IS_SCRIPT(arg) = (%ClassOf(arg) === 'Script');
macro FLOOR(arg) = %Math_floor(arg);
......
......@@ -114,6 +114,9 @@ const kMessages = {
illegal_return: "Illegal return statement",
error_loading_debugger: "Error loading debugger %0",
no_input_to_regexp: "No input to %0",
result_not_primitive: "Result of %0 must be a primitive, was %1",
invalid_json: "String '%0' is not valid JSON",
circular_structure: "Converting circular structure to JSON"
};
......
......@@ -558,8 +558,6 @@ void SharedFunctionInfo::SharedFunctionInfoPrint() {
code()->ShortPrint();
PrintF("\n - source code = ");
GetSourceCode()->ShortPrint();
PrintF("\n - lazy load: %s",
lazy_load_data() == Heap::undefined_value() ? "no" : "yes");
// Script files are often large, hard to read.
// PrintF("\n - script =");
// script()->Print();
......@@ -579,7 +577,6 @@ void SharedFunctionInfo::SharedFunctionInfoVerify() {
VerifyObjectField(kCodeOffset);
VerifyObjectField(kInstanceClassNameOffset);
VerifyObjectField(kExternalReferenceDataOffset);
VerifyObjectField(kLazyLoadDataOffset);
VerifyObjectField(kScriptOffset);
VerifyObjectField(kDebugInfoOffset);
}
......
......@@ -1811,6 +1811,16 @@ void Map::set_bit_field(byte value) {
}
byte Map::bit_field2() {
return READ_BYTE_FIELD(this, kBitField2Offset);
}
void Map::set_bit_field2(byte value) {
WRITE_BYTE_FIELD(this, kBitField2Offset, value);
}
void Map::set_non_instance_prototype(bool value) {
if (value) {
set_bit_field(bit_field() | (1 << kHasNonInstancePrototype));
......@@ -2075,7 +2085,6 @@ ACCESSORS(SharedFunctionInfo, instance_class_name, Object,
kInstanceClassNameOffset)
ACCESSORS(SharedFunctionInfo, function_data, Object,
kExternalReferenceDataOffset)
ACCESSORS(SharedFunctionInfo, lazy_load_data, Object, kLazyLoadDataOffset)
ACCESSORS(SharedFunctionInfo, script, Object, kScriptOffset)
ACCESSORS(SharedFunctionInfo, debug_info, Object, kDebugInfoOffset)
ACCESSORS(SharedFunctionInfo, inferred_name, String, kInferredNameOffset)
......@@ -2141,8 +2150,8 @@ bool JSFunction::IsBoilerplate() {
}
bool JSFunction::IsLoaded() {
return shared()->lazy_load_data() == Heap::undefined_value();
bool JSObject::IsLoaded() {
return !map()->needs_loading();
}
......
......@@ -358,7 +358,7 @@ Object* JSObject::GetLazyProperty(Object* receiver,
Handle<Object> receiver_handle(receiver);
Handle<String> name_handle(name);
bool pending_exception;
LoadLazy(Handle<JSFunction>(JSFunction::cast(result->GetValue())),
LoadLazy(Handle<JSObject>(JSObject::cast(result->GetLazyValue())),
&pending_exception);
if (pending_exception) return Failure::Exception();
return this_handle->GetPropertyWithReceiver(*receiver_handle,
......@@ -377,7 +377,7 @@ Object* JSObject::SetLazyProperty(LookupResult* result,
Handle<String> name_handle(name);
Handle<Object> value_handle(value);
bool pending_exception;
LoadLazy(Handle<JSFunction>(JSFunction::cast(result->GetValue())),
LoadLazy(Handle<JSObject>(JSObject::cast(result->GetLazyValue())),
&pending_exception);
if (pending_exception) return Failure::Exception();
return this_handle->SetProperty(*name_handle, *value_handle, attributes);
......@@ -389,7 +389,7 @@ Object* JSObject::DeleteLazyProperty(LookupResult* result, String* name) {
Handle<JSObject> this_handle(this);
Handle<String> name_handle(name);
bool pending_exception;
LoadLazy(Handle<JSFunction>(JSFunction::cast(result->GetValue())),
LoadLazy(Handle<JSObject>(JSObject::cast(result->GetLazyValue())),
&pending_exception);
if (pending_exception) return Failure::Exception();
return this_handle->DeleteProperty(*name_handle);
......@@ -2715,6 +2715,7 @@ Object* Map::Copy() {
Map::cast(result)->set_inobject_properties(inobject_properties());
Map::cast(result)->set_unused_property_fields(unused_property_fields());
Map::cast(result)->set_bit_field(bit_field());
Map::cast(result)->set_bit_field2(bit_field2());
Map::cast(result)->ClearCodeCache();
return result;
}
......
......@@ -1242,6 +1242,9 @@ class JSObject: public HeapObject {
String* name,
PropertyAttributes* attributes);
// Tells whether this object needs to be loaded.
inline bool IsLoaded();
bool HasProperty(String* name) {
return GetPropertyAttribute(name) != ABSENT;
}
......@@ -2397,6 +2400,10 @@ class Map: public HeapObject {
inline byte bit_field();
inline void set_bit_field(byte value);
// Bit field 2.
inline byte bit_field2();
inline void set_bit_field2(byte value);
// Tells whether the object in the prototype property will be used
// for instances created from this function. If the prototype
// property is set to a value that is not a JSObject, the prototype
......@@ -2447,6 +2454,20 @@ class Map: public HeapObject {
return ((1 << kIsUndetectable) & bit_field()) != 0;
}
inline void set_needs_loading(bool value) {
if (value) {
set_bit_field2(bit_field2() | (1 << kNeedsLoading));
} else {
set_bit_field2(bit_field2() & ~(1 << kNeedsLoading));
}
}
// Does this object or function require a lazily loaded script to be
// run before being used?
inline bool needs_loading() {
return ((1 << kNeedsLoading) & bit_field2()) != 0;
}
// Tells whether the instance has a call-as-function handler.
inline void set_has_instance_call_handler() {
set_bit_field(bit_field() | (1 << kHasInstanceCallHandler));
......@@ -2550,7 +2571,7 @@ class Map: public HeapObject {
static const int kInstanceTypeOffset = kInstanceAttributesOffset + 0;
static const int kUnusedPropertyFieldsOffset = kInstanceAttributesOffset + 1;
static const int kBitFieldOffset = kInstanceAttributesOffset + 2;
// The byte at position 3 is not in use at the moment.
static const int kBitField2Offset = kInstanceAttributesOffset + 3;
// Bit positions for bit field.
static const int kUnused = 0; // To be used for marking recently used maps.
......@@ -2561,6 +2582,10 @@ class Map: public HeapObject {
static const int kIsUndetectable = 5;
static const int kHasInstanceCallHandler = 6;
static const int kIsAccessCheckNeeded = 7;
// Bit positions for but field 2
static const int kNeedsLoading = 0;
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(Map);
};
......@@ -2677,10 +2702,6 @@ class SharedFunctionInfo: public HeapObject {
// on objects.
DECL_ACCESSORS(function_data, Object)
// [lazy load data]: If the function has lazy loading, this field
// contains contexts and other data needed to load it.
DECL_ACCESSORS(lazy_load_data, Object)
// [script info]: Script from which the function originates.
DECL_ACCESSORS(script, Object)
......@@ -2754,9 +2775,7 @@ class SharedFunctionInfo: public HeapObject {
kExpectedNofPropertiesOffset + kIntSize;
static const int kExternalReferenceDataOffset =
kInstanceClassNameOffset + kPointerSize;
static const int kLazyLoadDataOffset =
kExternalReferenceDataOffset + kPointerSize;
static const int kScriptOffset = kLazyLoadDataOffset + kPointerSize;
static const int kScriptOffset = kExternalReferenceDataOffset + kPointerSize;
static const int kStartPositionAndTypeOffset = kScriptOffset + kPointerSize;
static const int kEndPositionOffset = kStartPositionAndTypeOffset + kIntSize;
static const int kFunctionTokenPositionOffset = kEndPositionOffset + kIntSize;
......@@ -2809,9 +2828,6 @@ class JSFunction: public JSObject {
// function.
inline bool IsBoilerplate();
// Tells whether this function needs to be loaded.
inline bool IsLoaded();
// [literals]: Fixed array holding the materialized literals.
//
// If the function contains object, regexp or array literals, the
......
......@@ -245,14 +245,25 @@ class LookupResult BASE_EMBEDDED {
// Tells whether the value needs to be loaded.
bool IsLoaded() {
if (lookup_type_ == DESCRIPTOR_TYPE || lookup_type_ == DICTIONARY_TYPE) {
Object* value = GetValue();
if (value->IsJSFunction()) {
return JSFunction::cast(value)->IsLoaded();
}
Object* target = GetLazyValue();
return !target->IsJSObject() || JSObject::cast(target)->IsLoaded();
}
return true;
}
Object* GetLazyValue() {
switch (type()) {
case FIELD:
return holder()->FastPropertyAt(GetFieldIndex());
case NORMAL:
return holder()->property_dictionary()->ValueAt(GetDictionaryEntry());
case CONSTANT_FUNCTION:
return GetConstantFunction();
default:
return Smi::FromInt(0);
}
}
Map* GetTransitionMap() {
ASSERT(lookup_type_ == DESCRIPTOR_TYPE);
ASSERT(type() == MAP_TRANSITION);
......
......@@ -812,6 +812,11 @@ ReplaceResultBuilder.prototype.generate = function() {
}
function StringToJSON(key) {
return CheckJSONPrimitive(this.valueOf());
}
// -------------------------------------------------------------------
function SetupString() {
......@@ -858,7 +863,8 @@ function SetupString() {
"small", StringSmall,
"strike", StringStrike,
"sub", StringSub,
"sup", StringSup
"sup", StringSup,
"toJSON", StringToJSON
));
}
......
......@@ -312,13 +312,19 @@ function BooleanValueOf() {
}
function BooleanToJSON(key) {
return CheckJSONPrimitive(this.valueOf());
}
// ----------------------------------------------------------------------------
function SetupBoolean() {
InstallFunctions($Boolean.prototype, DONT_ENUM, $Array(
"toString", BooleanToString,
"valueOf", BooleanValueOf
"valueOf", BooleanValueOf,
"toJSON", BooleanToJSON
));
}
......@@ -418,6 +424,18 @@ function NumberToPrecision(precision) {
}
function CheckJSONPrimitive(val) {
if (!IsPrimitive(val))
throw MakeTypeError('result_not_primitive', ['toJSON', val]);
return val;
}
function NumberToJSON(key) {
return CheckJSONPrimitive(this.valueOf());
}
// ----------------------------------------------------------------------------
function SetupNumber() {
......@@ -455,7 +473,8 @@ function SetupNumber() {
"valueOf", NumberValueOf,
"toFixed", NumberToFixed,
"toExponential", NumberToExponential,
"toPrecision", NumberToPrecision
"toPrecision", NumberToPrecision,
"toJSON", NumberToJSON
));
}
......
// Copyright 2009 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
function GenericToJSONChecks(Constructor, value, alternative) {
var n1 = new Constructor(value);
n1.valueOf = function () { return alternative; };
assertEquals(alternative, n1.toJSON());
var n2 = new Constructor(value);
n2.valueOf = null;
assertThrows(function () { n2.toJSON(); }, TypeError);
var n3 = new Constructor(value);
n3.valueOf = function () { return {}; };
assertThrows(function () { n3.toJSON(); }, TypeError, 'result_not_primitive');
var n4 = new Constructor(value);
n4.valueOf = function () {
assertEquals(0, arguments.length);
assertEquals(this, n4);
return null;
};
assertEquals(null, n4.toJSON());
}
// Number toJSON
assertEquals(3, (3).toJSON());
assertEquals(3, (3).toJSON(true));
assertEquals(4, (new Number(4)).toJSON());
GenericToJSONChecks(Number, 5, 6);
// Boolean toJSON
assertEquals(true, (true).toJSON());
assertEquals(true, (true).toJSON(false));
assertEquals(false, (false).toJSON());
assertEquals(true, (new Boolean(true)).toJSON());
GenericToJSONChecks(Boolean, true, false);
GenericToJSONChecks(Boolean, false, true);
// String toJSON
assertEquals("flot", "flot".toJSON());
assertEquals("flot", "flot".toJSON(3));
assertEquals("tolf", (new String("tolf")).toJSON());
GenericToJSONChecks(String, "x", "y");
// Date toJSON
assertEquals("1970-01-01T00:00:00Z", new Date(0).toJSON());
assertEquals("1979-01-11T08:00:00Z", new Date("1979-01-11 08:00 GMT").toJSON());
assertEquals("2005-05-05T05:05:05Z", new Date("2005-05-05 05:05:05 GMT").toJSON());
var n1 = new Date(10000);
n1.toISOString = function () { return "foo"; };
assertEquals("foo", n1.toJSON());
var n2 = new Date(10001);
n2.toISOString = null;
assertThrows(function () { n2.toJSON(); }, TypeError);
var n3 = new Date(10002);
n3.toISOString = function () { return {}; };
assertThrows(function () { n3.toJSON(); }, TypeError, "result_not_primitive");
var n4 = new Date(10003);
n4.toISOString = function () {
assertEquals(0, arguments.length);
assertEquals(this, n4);
return null;
};
assertEquals(null, n4.toJSON());
assertEquals(Object.prototype, JSON.__proto__);
assertEquals("[object JSON]", Object.prototype.toString.call(JSON));
// DontEnum
for (var p in this)
assertFalse(p == "JSON");
// Parse
assertEquals({}, JSON.parse("{}"));
assertEquals(null, JSON.parse("null"));
assertEquals(true, JSON.parse("true"));
assertEquals(false, JSON.parse("false"));
assertEquals("foo", JSON.parse('"foo"'));
assertEquals("f\no", JSON.parse('"f\\no"'));
assertEquals(1.1, JSON.parse("1.1"));
assertEquals(1, JSON.parse("1.0"));
assertEquals(0.0000000003, JSON.parse("3e-10"));
assertEquals([], JSON.parse("[]"));
assertEquals([1], JSON.parse("[1]"));
assertEquals([1, "2", true, null], JSON.parse('[1, "2", true, null]'));
function GetFilter(name) {
function Filter(key, value) {
return (key == name) ? undefined : value;
}
return Filter;
}
var pointJson = '{"x": 1, "y": 2}';
assertEquals({'x': 1, 'y': 2}, JSON.parse(pointJson));
assertEquals({'x': 1}, JSON.parse(pointJson, GetFilter('y')));
assertEquals({'y': 2}, JSON.parse(pointJson, GetFilter('x')));
assertEquals([1, 2, 3], JSON.parse("[1, 2, 3]"));
assertEquals([1, undefined, 3], JSON.parse("[1, 2, 3]", GetFilter(1)));
assertEquals([1, 2, undefined], JSON.parse("[1, 2, 3]", GetFilter(2)));
function DoubleNumbers(key, value) {
return (typeof value == 'number') ? 2 * value : value;
}
var deepObject = '{"a": {"b": 1, "c": 2}, "d": {"e": {"f": 3}}}';
assertEquals({"a": {"b": 1, "c": 2}, "d": {"e": {"f": 3}}},
JSON.parse(deepObject));
assertEquals({"a": {"b": 2, "c": 4}, "d": {"e": {"f": 6}}},
JSON.parse(deepObject, DoubleNumbers));
function TestInvalid(str) {
assertThrows(function () { JSON.parse(str); }, SyntaxError);
}
TestInvalid('"abc\x00def"');
TestInvalid('"abc\x10def"');
TestInvalid('"abc\x1fdef"');
TestInvalid("[1, 2");
TestInvalid('{"x": 3');
// Stringify
assertEquals("true", JSON.stringify(true));
assertEquals("false", JSON.stringify(false));
assertEquals("null", JSON.stringify(null));
assertEquals("false", JSON.stringify({toJSON: function () { return false; }}));
assertEquals("4", JSON.stringify(4));
assertEquals('"foo"', JSON.stringify("foo"));
assertEquals("null", JSON.stringify(Infinity));
assertEquals("null", JSON.stringify(-Infinity));
assertEquals("null", JSON.stringify(NaN));
assertEquals("4", JSON.stringify(new Number(4)));
assertEquals('"bar"', JSON.stringify(new String("bar")));
assertEquals('"foo\\u0000bar"', JSON.stringify("foo\0bar"));
assertEquals('"f\\"o\'o\\\\b\\ba\\fr\\nb\\ra\\tz"',
JSON.stringify("f\"o\'o\\b\ba\fr\nb\ra\tz"));
assertEquals("[1,2,3]", JSON.stringify([1, 2, 3]));
assertEquals("[\n 1,\n 2,\n 3\n]", JSON.stringify([1, 2, 3], null, 1));
assertEquals("[\n 1,\n 2,\n 3\n]", JSON.stringify([1, 2, 3], null, 2));
assertEquals("[\n 1,\n 2,\n 3\n]",
JSON.stringify([1, 2, 3], null, new Number(2)));
assertEquals("[\n^1,\n^2,\n^3\n]", JSON.stringify([1, 2, 3], null, "^"));
assertEquals("[\n^1,\n^2,\n^3\n]",
JSON.stringify([1, 2, 3], null, new String("^")));
assertEquals("[\n 1,\n 2,\n [\n 3,\n [\n 4\n ],\n 5\n ],\n 6,\n 7\n]",
JSON.stringify([1, 2, [3, [4], 5], 6, 7], null, 1));
assertEquals("[]", JSON.stringify([], null, 1));
assertEquals("[1,2,[3,[4],5],6,7]",
JSON.stringify([1, 2, [3, [4], 5], 6, 7], null));
assertEquals("[2,4,[6,[8],10],12,14]",
JSON.stringify([1, 2, [3, [4], 5], 6, 7], DoubleNumbers));
var circular = [1, 2, 3];
circular[2] = circular;
assertThrows(function () { JSON.stringify(circular); }, TypeError);
var singleton = [];
var multiOccurrence = [singleton, singleton, singleton];
assertEquals("[[],[],[]]", JSON.stringify(multiOccurrence));
assertEquals('{"x":5,"y":6}', JSON.stringify({x:5,y:6}));
assertEquals('{"x":5}', JSON.stringify({x:5,y:6}, ['x']));
assertEquals('{\n "a": "b",\n "c": "d"\n}',
JSON.stringify({a:"b",c:"d"}, null, 1));
assertEquals('{"y":6,"x":5}', JSON.stringify({x:5,y:6}, ['y', 'x']));
assertEquals(undefined, JSON.stringify(undefined));
assertEquals(undefined, JSON.stringify(function () { }));
......@@ -51,6 +51,25 @@ function fail(expected, found, name_opt) {
}
function deepObjectEquals(a, b) {
var aProps = [];
for (var key in a)
aProps.push(key);
var bProps = [];
for (var key in b)
bProps.push(key);
aProps.sort();
bProps.sort();
if (!deepEquals(aProps, bProps))
return false;
for (var i = 0; i < aProps.length; i++) {
if (!deepEquals(a[aProps[i]], b[aProps[i]]))
return false;
}
return true;
}
function deepEquals(a, b) {
if (a == b) return true;
if (typeof a == "number" && typeof b == "number" && isNaN(a) && isNaN(b)) {
......@@ -73,8 +92,9 @@ function deepEquals(a, b) {
}
}
return true;
} else {
return deepObjectEquals(a, b);
}
return false;
}
......@@ -130,12 +150,20 @@ function assertNotNull(value, name_opt) {
}
function assertThrows(code) {
function assertThrows(code, type_opt, cause_opt) {
var threwException = true;
try {
eval(code);
if (typeof code == 'function') {
code();
} else {
eval(code);
}
threwException = false;
} catch (e) {
if (typeof type_opt == 'function')
assertInstanceof(e, type_opt);
if (arguments.length >= 3)
assertEquals(e.type, cause_opt);
// Do nothing.
}
if (!threwException) assertTrue(false, "did not throw exception");
......
......@@ -3,4 +3,4 @@ set SOURCE_DIR=%1
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 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
%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 %SOURCE_DIR%\json-delay.js
......@@ -162,6 +162,10 @@
RelativePath="..\..\src\regexp-delay.js"
>
</File>
<File
RelativePath="..\..\src\json-delay.js"
>
</File>
<File
RelativePath="..\..\src\runtime.js"
>
......@@ -184,7 +188,7 @@
Name="VCCustomBuildTool"
Description="Processing js files..."
CommandLine=".\js2c.cmd ..\..\src &quot;$(IntDir)\DerivedSources&quot;"
AdditionalDependencies="..\..\src\macros.py;..\..\src\runtime.js;..\..\src\v8natives.js;..\..\src\array.js;..\..\src\string.js;..\..\src\uri.js;..\..\src\math.js;..\..\src\messages.js;..\..\src\apinatives.js;..\..\src\debug-delay.js;..\..\src\mirror-delay.js;..\..\src\date-delay.js;..\..\src\regexp-delay.js"
AdditionalDependencies="..\..\src\macros.py;..\..\src\runtime.js;..\..\src\v8natives.js;..\..\src\array.js;..\..\src\string.js;..\..\src\uri.js;..\..\src\math.js;..\..\src\messages.js;..\..\src\apinatives.js;..\..\src\debug-delay.js;..\..\src\mirror-delay.js;..\..\src\date-delay.js;..\..\src\regexp-delay.js;..\..\src\json-delay.js"
Outputs="$(IntDir)\DerivedSources\natives.cc;$(IntDir)\DerivedSources\natives-empty.cc"
/>
</FileConfiguration>
......@@ -195,7 +199,7 @@
Name="VCCustomBuildTool"
Description="Processing js files..."
CommandLine=".\js2c.cmd ..\..\src &quot;$(IntDir)\DerivedSources&quot;"
AdditionalDependencies="..\..\src\macros.py;..\..\src\runtime.js;..\..\src\v8natives.js;..\..\src\array.js;..\..\src\string.js;..\..\src\uri.js;..\..\src\math.js;..\..\src\messages.js;..\..\src\apinatives.js;..\..\src\debug-delay.js;..\..\src\mirror-delay.js;..\..\src\date-delay.js;..\..\src\regexp-delay.js"
AdditionalDependencies="..\..\src\macros.py;..\..\src\runtime.js;..\..\src\v8natives.js;..\..\src\array.js;..\..\src\string.js;..\..\src\uri.js;..\..\src\math.js;..\..\src\messages.js;..\..\src\apinatives.js;..\..\src\debug-delay.js;..\..\src\mirror-delay.js;..\..\src\date-delay.js;..\..\src\regexp-delay.js;..\..\src\json-delay.js"
Outputs="$(IntDir)\DerivedSources\natives.cc;$(IntDir)\DerivedSources\natives-empty.cc"
/>
</FileConfiguration>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment